summaryrefslogtreecommitdiffstats
path: root/chart2/source/tools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /chart2/source/tools
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chart2/source/tools')
-rw-r--r--chart2/source/tools/AxisHelper.cxx1151
-rw-r--r--chart2/source/tools/BaseGFXHelper.cxx236
-rw-r--r--chart2/source/tools/CachedDataSequence.cxx352
-rw-r--r--chart2/source/tools/CharacterProperties.cxx459
-rw-r--r--chart2/source/tools/ChartModelHelper.cxx250
-rw-r--r--chart2/source/tools/ChartTypeHelper.cxx724
-rw-r--r--chart2/source/tools/ChartViewHelper.cxx56
-rw-r--r--chart2/source/tools/ColorPerPointHelper.cxx78
-rw-r--r--chart2/source/tools/CommonConverters.cxx600
-rw-r--r--chart2/source/tools/ConfigColorScheme.cxx188
-rw-r--r--chart2/source/tools/ControllerLockGuard.cxx84
-rw-r--r--chart2/source/tools/DataSeriesHelper.cxx884
-rw-r--r--chart2/source/tools/DataSource.cxx88
-rw-r--r--chart2/source/tools/DataSourceHelper.cxx471
-rw-r--r--chart2/source/tools/DiagramHelper.cxx1571
-rw-r--r--chart2/source/tools/ErrorBar.cxx470
-rw-r--r--chart2/source/tools/ExplicitCategoriesProvider.cxx564
-rw-r--r--chart2/source/tools/ExponentialRegressionCurveCalculator.cxx221
-rw-r--r--chart2/source/tools/FillProperties.cxx200
-rw-r--r--chart2/source/tools/FormattedStringHelper.cxx63
-rw-r--r--chart2/source/tools/InternalData.cxx557
-rw-r--r--chart2/source/tools/InternalDataProvider.cxx1553
-rw-r--r--chart2/source/tools/LabeledDataSequence.cxx176
-rw-r--r--chart2/source/tools/LegendHelper.cxx122
-rw-r--r--chart2/source/tools/LifeTime.cxx441
-rw-r--r--chart2/source/tools/LinePropertiesHelper.cxx192
-rw-r--r--chart2/source/tools/LinearRegressionCurveCalculator.cxx69
-rw-r--r--chart2/source/tools/LogarithmicRegressionCurveCalculator.cxx186
-rw-r--r--chart2/source/tools/MeanValueRegressionCurveCalculator.cxx126
-rw-r--r--chart2/source/tools/MediaDescriptorHelper.cxx225
-rw-r--r--chart2/source/tools/ModifyListenerCallBack.cxx111
-rw-r--r--chart2/source/tools/ModifyListenerHelper.cxx74
-rw-r--r--chart2/source/tools/MovingAverageRegressionCurveCalculator.cxx165
-rw-r--r--chart2/source/tools/NameContainer.cxx128
-rw-r--r--chart2/source/tools/NumberFormatterWrapper.cxx149
-rw-r--r--chart2/source/tools/OPropertySet.cxx464
-rw-r--r--chart2/source/tools/ObjectIdentifier.cxx1379
-rw-r--r--chart2/source/tools/PolynomialRegressionCurveCalculator.cxx392
-rw-r--r--chart2/source/tools/PopupRequest.cxx31
-rw-r--r--chart2/source/tools/PotentialRegressionCurveCalculator.cxx188
-rw-r--r--chart2/source/tools/PropertyHelper.cxx296
-rw-r--r--chart2/source/tools/RangeHighlighter.cxx394
-rw-r--r--chart2/source/tools/ReferenceSizeProvider.cxx333
-rw-r--r--chart2/source/tools/RegressionCurveCalculator.cxx217
-rw-r--r--chart2/source/tools/RegressionCurveHelper.cxx920
-rw-r--r--chart2/source/tools/RegressionCurveModel.cxx541
-rw-r--r--chart2/source/tools/RegressionEquation.cxx294
-rw-r--r--chart2/source/tools/RegressionEquation.hxx118
-rw-r--r--chart2/source/tools/RelativePositionHelper.cxx381
-rw-r--r--chart2/source/tools/RelativeSizeHelper.cxx121
-rw-r--r--chart2/source/tools/ResId.cxx30
-rw-r--r--chart2/source/tools/Scaling.cxx267
-rw-r--r--chart2/source/tools/SceneProperties.cxx333
-rw-r--r--chart2/source/tools/StatisticsHelper.cxx363
-rw-r--r--chart2/source/tools/ThreeDHelper.cxx1457
-rw-r--r--chart2/source/tools/TitleHelper.cxx420
-rw-r--r--chart2/source/tools/UncachedDataSequence.cxx316
-rw-r--r--chart2/source/tools/UserDefinedProperties.cxx61
-rw-r--r--chart2/source/tools/WeakListenerAdapter.cxx46
-rw-r--r--chart2/source/tools/WrappedDefaultProperty.cxx77
-rw-r--r--chart2/source/tools/WrappedDirectStateProperty.cxx45
-rw-r--r--chart2/source/tools/WrappedIgnoreProperty.cxx113
-rw-r--r--chart2/source/tools/WrappedProperty.cxx125
-rw-r--r--chart2/source/tools/WrappedPropertySet.cxx445
-rw-r--r--chart2/source/tools/XMLRangeHelper.cxx393
65 files changed, 23544 insertions, 0 deletions
diff --git a/chart2/source/tools/AxisHelper.cxx b/chart2/source/tools/AxisHelper.cxx
new file mode 100644
index 000000000..a44fb567a
--- /dev/null
+++ b/chart2/source/tools/AxisHelper.cxx
@@ -0,0 +1,1151 @@
+/* -*- 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 <AxisHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ChartType.hxx>
+#include <Axis.hxx>
+#include <AxisIndexDefines.hxx>
+#include <DataSource.hxx>
+#include <LinePropertiesHelper.hxx>
+#include <servicenames_coosystems.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <Scaling.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <DataSourceHelper.hxx>
+#include <ReferenceSizeProvider.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/saveopt.hxx>
+
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cstddef>
+#include <map>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+Reference< chart2::XScaling > AxisHelper::createLinearScaling()
+{
+ return new LinearScaling( 1.0, 0.0 );
+}
+
+Reference< chart2::XScaling > AxisHelper::createLogarithmicScaling( double fBase )
+{
+ return new LogarithmicScaling( fBase );
+}
+
+ScaleData AxisHelper::createDefaultScale()
+{
+ ScaleData aScaleData;
+ aScaleData.AxisType = chart2::AxisType::REALNUMBER;
+ aScaleData.AutoDateAxis = true;
+ aScaleData.ShiftedCategoryPosition = false;
+ Sequence< SubIncrement > aSubIncrements{ SubIncrement() };
+ aScaleData.IncrementData.SubIncrements = aSubIncrements;
+ return aScaleData;
+}
+
+void AxisHelper::removeExplicitScaling( ScaleData& rScaleData )
+{
+ uno::Any aEmpty;
+ rScaleData.Minimum = rScaleData.Maximum = rScaleData.Origin = aEmpty;
+ rScaleData.Scaling = nullptr;
+ ScaleData aDefaultScale( createDefaultScale() );
+ rScaleData.IncrementData = aDefaultScale.IncrementData;
+ rScaleData.TimeIncrement = aDefaultScale.TimeIncrement;
+}
+
+bool AxisHelper::isLogarithmic( const Reference< XScaling >& xScaling )
+{
+ Reference< lang::XServiceName > xServiceName( xScaling, uno::UNO_QUERY );
+ return xServiceName.is()
+ && xServiceName->getServiceName() == "com.sun.star.chart2.LogarithmicScaling";
+}
+
+chart2::ScaleData AxisHelper::getDateCheckedScale( const rtl::Reference< Axis >& xAxis, ChartModel& rModel )
+{
+ ScaleData aScale = xAxis->getScaleData();
+ rtl::Reference< BaseCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
+ if( aScale.AutoDateAxis && aScale.AxisType == AxisType::CATEGORY )
+ {
+ sal_Int32 nDimensionIndex=0; sal_Int32 nAxisIndex=0;
+ AxisHelper::getIndicesForAxis(xAxis, xCooSys, nDimensionIndex, nAxisIndex );
+ bool bChartTypeAllowsDateAxis = ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( xCooSys, 0 ), nDimensionIndex );
+ if( bChartTypeAllowsDateAxis )
+ aScale.AxisType = AxisType::DATE;
+ }
+ if( aScale.AxisType == AxisType::DATE )
+ {
+ ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel );
+ if( !aExplicitCategoriesProvider.isDateAxis() )
+ aScale.AxisType = AxisType::CATEGORY;
+ }
+ return aScale;
+}
+
+void AxisHelper::checkDateAxis( chart2::ScaleData& rScale, ExplicitCategoriesProvider* pExplicitCategoriesProvider, bool bChartTypeAllowsDateAxis )
+{
+ if( rScale.AutoDateAxis && rScale.AxisType == AxisType::CATEGORY && bChartTypeAllowsDateAxis )
+ {
+ rScale.AxisType = AxisType::DATE;
+ removeExplicitScaling( rScale );
+ }
+ if( rScale.AxisType == AxisType::DATE && (!pExplicitCategoriesProvider || !pExplicitCategoriesProvider->isDateAxis()) )
+ {
+ rScale.AxisType = AxisType::CATEGORY;
+ removeExplicitScaling( rScale );
+ }
+}
+
+sal_Int32 AxisHelper::getExplicitNumberFormatKeyForAxis(
+ const Reference< chart2::XAxis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem
+ , const rtl::Reference<ChartModel>& xChartDoc
+ , bool bSearchForParallelAxisIfNothingIsFound )
+{
+ rtl::Reference< Axis > pAxis = dynamic_cast<Axis*>(xAxis.get());
+ assert(pAxis || !xAxis);
+ return getExplicitNumberFormatKeyForAxis(pAxis, xCorrespondingCoordinateSystem, xChartDoc, bSearchForParallelAxisIfNothingIsFound);
+}
+
+sal_Int32 AxisHelper::getExplicitNumberFormatKeyForAxis(
+ const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem
+ , const rtl::Reference<ChartModel>& xChartDoc
+ , bool bSearchForParallelAxisIfNothingIsFound )
+{
+ sal_Int32 nNumberFormatKey(0);
+ sal_Int32 nAxisIndex = 0;
+ sal_Int32 nDimensionIndex = 1;
+ AxisHelper::getIndicesForAxis( xAxis, xCorrespondingCoordinateSystem, nDimensionIndex, nAxisIndex );
+
+ if (!xAxis.is())
+ return 0;
+
+ bool bLinkToSource = true;
+ xAxis->getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource;
+ xAxis->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormatKey;
+
+ if (bLinkToSource)
+ {
+ bool bFormatSet = false;
+ //check whether we have a percent scale -> use percent format
+ if (xChartDoc)
+ {
+ ScaleData aData = AxisHelper::getDateCheckedScale( xAxis, *xChartDoc );
+ if( aData.AxisType==AxisType::PERCENT )
+ {
+ sal_Int32 nPercentFormat = DiagramHelper::getPercentNumberFormat( xChartDoc );
+ if( nPercentFormat != -1 )
+ {
+ nNumberFormatKey = nPercentFormat;
+ bFormatSet = true;
+ }
+ }
+ else if( aData.AxisType==AxisType::DATE )
+ {
+ if( aData.Categories.is() )
+ {
+ Reference< data::XDataSequence > xSeq( aData.Categories->getValues());
+ if( xSeq.is() && !( xChartDoc.is() && xChartDoc->hasInternalDataProvider()) )
+ nNumberFormatKey = xSeq->getNumberFormatKeyByIndex( -1 );
+ else
+ nNumberFormatKey = DiagramHelper::getDateNumberFormat( xChartDoc );
+ bFormatSet = true;
+ }
+ }
+ else if( xChartDoc.is() && xChartDoc->hasInternalDataProvider() && nDimensionIndex == 0 ) //maybe date axis
+ {
+ rtl::Reference< Diagram > xDiagram( xChartDoc->getFirstChartDiagram() );
+ if( DiagramHelper::isSupportingDateAxis( xDiagram ) )
+ {
+ nNumberFormatKey = DiagramHelper::getDateNumberFormat( xChartDoc );
+ }
+ else
+ {
+ rtl::Reference< DataSource > xSource = DataSourceHelper::getUsedData( *xChartDoc );
+ if( xSource.is() )
+ {
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aXValues(
+ DataSeriesHelper::getAllDataSequencesByRole( xSource->getDataSequences(), "values-x" ) );
+ if( aXValues.empty() )
+ {
+ uno::Reference< chart2::data::XLabeledDataSequence > xCategories( DiagramHelper::getCategoriesFromDiagram( xDiagram ) );
+ if( xCategories.is() )
+ {
+ Reference< data::XDataSequence > xSeq( xCategories->getValues());
+ if( xSeq.is() )
+ {
+ bool bHasValidDoubles = false;
+ double fTest=0.0;
+ Sequence< uno::Any > aCats( xSeq->getData() );
+ sal_Int32 nCount = aCats.getLength();
+ for( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ if( (aCats[i]>>=fTest) && !std::isnan(fTest) )
+ {
+ bHasValidDoubles=true;
+ break;
+ }
+ }
+ if( bHasValidDoubles )
+ nNumberFormatKey = DiagramHelper::getDateNumberFormat( xChartDoc );
+ }
+ }
+ }
+ }
+ }
+ bFormatSet = true;
+ }
+ }
+
+ if( !bFormatSet )
+ {
+ std::map< sal_Int32, sal_Int32 > aKeyMap;
+ bool bNumberFormatKeyFoundViaAttachedData = false;
+
+ try
+ {
+ OUString aRoleToMatch;
+ if( nDimensionIndex == 0 )
+ aRoleToMatch = "values-x";
+ const std::vector< rtl::Reference< ChartType > > & aChartTypes( xCorrespondingCoordinateSystem->getChartTypes2());
+ for( rtl::Reference< ChartType > const & chartType : aChartTypes )
+ {
+ if( nDimensionIndex != 0 )
+ aRoleToMatch = ChartTypeHelper::getRoleOfSequenceForYAxisNumberFormatDetection( chartType );
+ for( rtl::Reference< DataSeries > const & xDataSeries : chartType->getDataSeries2() )
+ {
+ if( nDimensionIndex == 1 )
+ {
+ //only take those series into account that are attached to this axis
+ sal_Int32 nAttachedAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries);
+ if( nAttachedAxisIndex != nAxisIndex )
+ continue;
+ }
+
+ Reference< data::XLabeledDataSequence > xLabeledSeq(
+ DataSeriesHelper::getDataSequenceByRole( xDataSeries, aRoleToMatch ) );
+
+ if( !xLabeledSeq.is() && nDimensionIndex==0 )
+ {
+ ScaleData aData = xAxis->getScaleData();
+ xLabeledSeq = aData.Categories;
+ }
+
+ if( xLabeledSeq.is() )
+ {
+ Reference< data::XDataSequence > xSeq( xLabeledSeq->getValues());
+ if( xSeq.is() )
+ {
+ sal_Int32 nKey = xSeq->getNumberFormatKeyByIndex( -1 );
+ // increase frequency
+ aKeyMap[ nKey ] ++;
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ if( ! aKeyMap.empty())
+ {
+ sal_Int32 nMaxFreq = 0;
+ // find most frequent key
+ for (auto const& elem : aKeyMap)
+ {
+ SAL_INFO(
+ "chart2.tools",
+ "NumberFormatKey " << elem.first << " appears "
+ << elem.second << " times");
+ // all values must at least be 1
+ if( elem.second > nMaxFreq )
+ {
+ nNumberFormatKey = elem.first;
+ bNumberFormatKeyFoundViaAttachedData = true;
+ nMaxFreq = elem.second;
+ }
+ }
+ }
+
+ if( bSearchForParallelAxisIfNothingIsFound )
+ {
+ //no format is set to this axis and no data is set to this axis
+ //--> try to obtain the format from the parallel y-axis
+ if( !bNumberFormatKeyFoundViaAttachedData && nDimensionIndex == 1 )
+ {
+ sal_Int32 nParallelAxisIndex = (nAxisIndex==1) ?0 :1;
+ rtl::Reference< Axis > xParallelAxis = AxisHelper::getAxis( 1, nParallelAxisIndex, xCorrespondingCoordinateSystem );
+ nNumberFormatKey = AxisHelper::getExplicitNumberFormatKeyForAxis(xParallelAxis, xCorrespondingCoordinateSystem, xChartDoc, false);
+ }
+ }
+ }
+ }
+
+ return nNumberFormatKey;
+}
+
+rtl::Reference< Axis > AxisHelper::createAxis(
+ sal_Int32 nDimensionIndex
+ , sal_Int32 nAxisIndex // 0==main or 1==secondary axis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , const Reference< uno::XComponentContext > & xContext
+ , ReferenceSizeProvider * pRefSizeProvider )
+{
+ if( !xContext.is() || !xCooSys.is() )
+ return nullptr;
+ if( nDimensionIndex >= xCooSys->getDimension() )
+ return nullptr;
+
+ rtl::Reference< Axis > xAxis = new Axis();
+
+ xCooSys->setAxisByDimension( nDimensionIndex, xAxis, nAxisIndex );
+
+ if( nAxisIndex>0 )//when inserting secondary axes copy some things from the main axis
+ {
+ css::chart::ChartAxisPosition eNewAxisPos( css::chart::ChartAxisPosition_END );
+
+ rtl::Reference< Axis > xMainAxis = xCooSys->getAxisByDimension2( nDimensionIndex, 0 );
+ if( xMainAxis.is() )
+ {
+ ScaleData aScale = xAxis->getScaleData();
+ ScaleData aMainScale = xMainAxis->getScaleData();
+
+ aScale.AxisType = aMainScale.AxisType;
+ aScale.AutoDateAxis = aMainScale.AutoDateAxis;
+ aScale.Categories = aMainScale.Categories;
+ aScale.Orientation = aMainScale.Orientation;
+ aScale.ShiftedCategoryPosition = aMainScale.ShiftedCategoryPosition;
+
+ xAxis->setScaleData( aScale );
+
+ //ensure that the second axis is not placed on the main axis
+ css::chart::ChartAxisPosition eMainAxisPos( css::chart::ChartAxisPosition_ZERO );
+ xMainAxis->getPropertyValue("CrossoverPosition") >>= eMainAxisPos;
+ if( eMainAxisPos == css::chart::ChartAxisPosition_END )
+ eNewAxisPos = css::chart::ChartAxisPosition_START;
+ }
+
+ xAxis->setPropertyValue("CrossoverPosition", uno::Any(eNewAxisPos) );
+ }
+
+ try
+ {
+ // set correct initial AutoScale
+ if( pRefSizeProvider )
+ pRefSizeProvider->setValuesAtPropertySet( xAxis );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xAxis;
+}
+
+rtl::Reference< Axis > AxisHelper::createAxis( sal_Int32 nDimensionIndex, bool bMainAxis
+ , const rtl::Reference< Diagram >& xDiagram
+ , const Reference< uno::XComponentContext >& xContext
+ , ReferenceSizeProvider * pRefSizeProvider )
+{
+ OSL_ENSURE( xContext.is(), "need a context to create an axis" );
+ if( !xContext.is() )
+ return nullptr;
+
+ sal_Int32 nAxisIndex = bMainAxis ? MAIN_AXIS_INDEX : SECONDARY_AXIS_INDEX;
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemByIndex( xDiagram, 0 );
+
+ // create axis
+ return AxisHelper::createAxis(
+ nDimensionIndex, nAxisIndex, xCooSys, xContext, pRefSizeProvider );
+}
+
+void AxisHelper::showAxis( sal_Int32 nDimensionIndex, bool bMainAxis
+ , const rtl::Reference< Diagram >& xDiagram
+ , const Reference< uno::XComponentContext >& xContext
+ , ReferenceSizeProvider * pRefSizeProvider )
+{
+ if( !xDiagram.is() )
+ return;
+
+ bool bNewAxisCreated = false;
+ rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, bMainAxis, xDiagram );
+ if( !xAxis.is() && xContext.is() )
+ {
+ // create axis
+ bNewAxisCreated = true;
+ xAxis = AxisHelper::createAxis( nDimensionIndex, bMainAxis, xDiagram, xContext, pRefSizeProvider );
+ }
+
+ OSL_ASSERT( xAxis.is());
+ if( !bNewAxisCreated ) //default is true already if created
+ AxisHelper::makeAxisVisible( xAxis );
+}
+
+void AxisHelper::showGrid( sal_Int32 nDimensionIndex, sal_Int32 nCooSysIndex, bool bMainGrid
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemByIndex( xDiagram, nCooSysIndex );
+ if(!xCooSys.is())
+ return;
+
+ rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, MAIN_AXIS_INDEX, xCooSys );
+ if(!xAxis.is())
+ {
+ //hhhh todo create axis without axis visibility
+ }
+ if(!xAxis.is())
+ return;
+
+ if( bMainGrid )
+ AxisHelper::makeGridVisible( xAxis->getGridProperties() );
+ else
+ {
+ const Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ for( auto const & i : aSubGrids )
+ AxisHelper::makeGridVisible( i );
+ }
+}
+
+void AxisHelper::makeAxisVisible( const rtl::Reference< Axis >& xAxis )
+{
+ if( xAxis.is() )
+ {
+ xAxis->setPropertyValue( "Show", uno::Any( true ) );
+ LinePropertiesHelper::SetLineVisible( xAxis );
+ xAxis->setPropertyValue( "DisplayLabels", uno::Any( true ) );
+ }
+}
+
+void AxisHelper::makeGridVisible( const Reference< beans::XPropertySet >& xGridProperties )
+{
+ if( xGridProperties.is() )
+ {
+ xGridProperties->setPropertyValue( "Show", uno::Any( true ) );
+ LinePropertiesHelper::SetLineVisible( xGridProperties );
+ }
+}
+
+void AxisHelper::hideAxis( sal_Int32 nDimensionIndex, bool bMainAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ AxisHelper::makeAxisInvisible( AxisHelper::getAxis( nDimensionIndex, bMainAxis, xDiagram ) );
+}
+
+void AxisHelper::makeAxisInvisible( const rtl::Reference< Axis >& xAxis )
+{
+ if( xAxis.is() )
+ {
+ xAxis->setPropertyValue( "Show", uno::Any( false ) );
+ }
+}
+
+void AxisHelper::hideAxisIfNoDataIsAttached( const rtl::Reference< Axis >& xAxis, const rtl::Reference< Diagram >& xDiagram )
+{
+ //axis is hidden if no data is attached anymore but data is available
+ bool bOtherSeriesAttachedToThisAxis = false;
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector = DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+ for (auto const& series : aSeriesVector)
+ {
+ rtl::Reference< Axis > xCurrentAxis = DiagramHelper::getAttachedAxis(series, xDiagram );
+ if( xCurrentAxis==xAxis )
+ {
+ bOtherSeriesAttachedToThisAxis = true;
+ break;
+ }
+ }
+ if(!bOtherSeriesAttachedToThisAxis && !aSeriesVector.empty() )
+ AxisHelper::makeAxisInvisible( xAxis );
+}
+
+void AxisHelper::hideGrid( sal_Int32 nDimensionIndex, sal_Int32 nCooSysIndex, bool bMainGrid
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemByIndex( xDiagram, nCooSysIndex );
+ if(!xCooSys.is())
+ return;
+
+ rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, MAIN_AXIS_INDEX, xCooSys );
+ if(!xAxis.is())
+ return;
+
+ if( bMainGrid )
+ AxisHelper::makeGridInvisible( xAxis->getGridProperties() );
+ else
+ {
+ const Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ for( auto const & i : aSubGrids)
+ AxisHelper::makeGridInvisible( i );
+ }
+}
+
+void AxisHelper::makeGridInvisible( const Reference< beans::XPropertySet >& xGridProperties )
+{
+ if( xGridProperties.is() )
+ {
+ xGridProperties->setPropertyValue( "Show", uno::Any( false ) );
+ }
+}
+
+bool AxisHelper::isGridShown( sal_Int32 nDimensionIndex, sal_Int32 nCooSysIndex, bool bMainGrid
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ bool bRet = false;
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemByIndex( xDiagram, nCooSysIndex );
+ if(!xCooSys.is())
+ return bRet;
+
+ rtl::Reference< Axis > xAxis = AxisHelper::getAxis( nDimensionIndex, MAIN_AXIS_INDEX, xCooSys );
+ if(!xAxis.is())
+ return bRet;
+
+ if( bMainGrid )
+ bRet = AxisHelper::isGridVisible( xAxis->getGridProperties() );
+ else
+ {
+ Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ if( aSubGrids.hasElements() )
+ bRet = AxisHelper::isGridVisible( aSubGrids[0] );
+ }
+
+ return bRet;
+}
+
+rtl::Reference< ::chart::BaseCoordinateSystem > AxisHelper::getCoordinateSystemByIndex(
+ const rtl::Reference< Diagram >& xDiagram, sal_Int32 nIndex )
+{
+ if(!xDiagram.is())
+ return nullptr;
+ auto & rCooSysList = xDiagram->getBaseCoordinateSystems();
+ if(0<=nIndex && o3tl::make_unsigned(nIndex) < rCooSysList.size())
+ return rCooSysList[nIndex];
+ return nullptr;
+}
+
+rtl::Reference< Axis > AxisHelper::getAxis( sal_Int32 nDimensionIndex, bool bMainAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ rtl::Reference< Axis > xRet;
+ try
+ {
+ rtl::Reference< BaseCoordinateSystem > xCooSys = AxisHelper::getCoordinateSystemByIndex( xDiagram, 0 );
+ xRet = AxisHelper::getAxis( nDimensionIndex, bMainAxis ? 0 : 1, xCooSys );
+ }
+ catch( const uno::Exception & )
+ {
+ }
+ return xRet;
+}
+
+rtl::Reference< Axis > AxisHelper::getAxis( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ rtl::Reference< Axis > xRet;
+ if(!xCooSys.is())
+ return xRet;
+
+ if(nDimensionIndex >= xCooSys->getDimension())
+ return xRet;
+
+ if(nAxisIndex > xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex))
+ return xRet;
+
+ assert(nAxisIndex >= 0);
+ assert(nDimensionIndex >= 0);
+ xRet = xCooSys->getAxisByDimension2( nDimensionIndex, nAxisIndex );
+ return xRet;
+}
+
+rtl::Reference< Axis > AxisHelper::getCrossingMainAxis( const Reference< chart2::XAxis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ rtl::Reference< Axis > pAxis = dynamic_cast<Axis*>(xAxis.get());
+ assert(pAxis || !xAxis);
+ return getCrossingMainAxis(pAxis, xCooSys);
+}
+
+rtl::Reference< Axis > AxisHelper::getCrossingMainAxis( const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ sal_Int32 nDimensionIndex = 0;
+ sal_Int32 nAxisIndex = 0;
+ AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex );
+ if( nDimensionIndex==2 )
+ {
+ nDimensionIndex=1;
+ bool bSwapXY = false;
+ if( (xCooSys->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXY) && bSwapXY )
+ nDimensionIndex=0;
+ }
+ else if( nDimensionIndex==1 )
+ nDimensionIndex=0;
+ else
+ nDimensionIndex=1;
+ return AxisHelper::getAxis( nDimensionIndex, 0, xCooSys );
+}
+
+rtl::Reference< Axis > AxisHelper::getParallelAxis( const Reference< XAxis >& xAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ try
+ {
+ sal_Int32 nCooSysIndex=-1;
+ sal_Int32 nDimensionIndex=-1;
+ sal_Int32 nAxisIndex=-1;
+ if( getIndicesForAxis( xAxis, xDiagram, nCooSysIndex, nDimensionIndex, nAxisIndex ) )
+ {
+ sal_Int32 nParallelAxisIndex = (nAxisIndex==1) ?0 :1;
+ return getAxis( nDimensionIndex, nParallelAxisIndex, getCoordinateSystemByIndex( xDiagram, nCooSysIndex ) );
+ }
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ return nullptr;
+}
+
+bool AxisHelper::isAxisShown( sal_Int32 nDimensionIndex, bool bMainAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ return AxisHelper::isAxisVisible( AxisHelper::getAxis( nDimensionIndex, bMainAxis, xDiagram ) );
+}
+
+bool AxisHelper::isAxisVisible( const Reference< XAxis >& xAxis )
+{
+ bool bRet = false;
+
+ Reference< beans::XPropertySet > xProps( xAxis, uno::UNO_QUERY );
+ if( xProps.is() )
+ {
+ xProps->getPropertyValue( "Show" ) >>= bRet;
+ bRet = bRet && ( LinePropertiesHelper::IsLineVisible( xProps )
+ || areAxisLabelsVisible( xProps ) );
+ }
+
+ return bRet;
+}
+
+bool AxisHelper::areAxisLabelsVisible( const Reference< beans::XPropertySet >& xAxisProperties )
+{
+ bool bRet = false;
+ if( xAxisProperties.is() )
+ {
+ xAxisProperties->getPropertyValue( "DisplayLabels" ) >>= bRet;
+ }
+ return bRet;
+}
+
+bool AxisHelper::isGridVisible( const Reference< beans::XPropertySet >& xGridproperties )
+{
+ bool bRet = false;
+
+ if( xGridproperties.is() )
+ {
+ xGridproperties->getPropertyValue( "Show" ) >>= bRet;
+ bRet = bRet && LinePropertiesHelper::IsLineVisible( xGridproperties );
+ }
+
+ return bRet;
+}
+
+Reference< beans::XPropertySet > AxisHelper::getGridProperties(
+ const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex, sal_Int32 nSubGridIndex )
+{
+ Reference< beans::XPropertySet > xRet;
+
+ rtl::Reference< Axis > xAxis( AxisHelper::getAxis( nDimensionIndex, nAxisIndex, xCooSys ) );
+ if( xAxis.is() )
+ {
+ if( nSubGridIndex<0 )
+ xRet.set( xAxis->getGridProperties() );
+ else
+ {
+ Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ if (nSubGridIndex < aSubGrids.getLength())
+ xRet.set( aSubGrids[nSubGridIndex] );
+ }
+ }
+
+ return xRet;
+}
+
+sal_Int32 AxisHelper::getDimensionIndexOfAxis(
+ const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ sal_Int32 nDimensionIndex = -1;
+ sal_Int32 nCooSysIndex = -1;
+ sal_Int32 nAxisIndex = -1;
+ AxisHelper::getIndicesForAxis( xAxis, xDiagram, nCooSysIndex , nDimensionIndex, nAxisIndex );
+ return nDimensionIndex;
+}
+
+bool AxisHelper::getIndicesForAxis(
+ const Reference< chart2::XAxis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , sal_Int32& rOutDimensionIndex, sal_Int32& rOutAxisIndex )
+{
+ rtl::Reference< Axis > pAxis = dynamic_cast<Axis*>(xAxis.get());
+ assert(pAxis || !xAxis);
+ return getIndicesForAxis(pAxis, xCooSys, rOutDimensionIndex, rOutAxisIndex);
+}
+
+bool AxisHelper::getIndicesForAxis(
+ const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , sal_Int32& rOutDimensionIndex, sal_Int32& rOutAxisIndex )
+{
+ //returns true if indices are found
+
+ rOutDimensionIndex = -1;
+ rOutAxisIndex = -1;
+
+ if( !xCooSys || !xAxis )
+ return false;
+
+ rtl::Reference< Axis > xCurrentAxis;
+ sal_Int32 nDimensionCount( xCooSys->getDimension() );
+ for( sal_Int32 nDimensionIndex = 0; nDimensionIndex < nDimensionCount; nDimensionIndex++ )
+ {
+ sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
+ for( sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; nAxisIndex++ )
+ {
+ xCurrentAxis = xCooSys->getAxisByDimension2(nDimensionIndex,nAxisIndex);
+ if( xCurrentAxis == xAxis )
+ {
+ rOutDimensionIndex = nDimensionIndex;
+ rOutAxisIndex = nAxisIndex;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool AxisHelper::getIndicesForAxis( const Reference< chart2::XAxis >& xAxis, const rtl::Reference< Diagram >& xDiagram
+ , sal_Int32& rOutCooSysIndex, sal_Int32& rOutDimensionIndex, sal_Int32& rOutAxisIndex )
+{
+ rtl::Reference< Axis > pAxis = dynamic_cast<Axis*>(xAxis.get());
+ assert(pAxis || !xAxis);
+ return getIndicesForAxis(pAxis, xDiagram, rOutCooSysIndex, rOutDimensionIndex, rOutAxisIndex);
+}
+
+bool AxisHelper::getIndicesForAxis( const rtl::Reference< Axis >& xAxis, const rtl::Reference< Diagram >& xDiagram
+ , sal_Int32& rOutCooSysIndex, sal_Int32& rOutDimensionIndex, sal_Int32& rOutAxisIndex )
+{
+ //returns true if indices are found
+
+ rOutCooSysIndex = -1;
+ rOutDimensionIndex = -1;
+ rOutAxisIndex = -1;
+
+ const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList = xDiagram->getBaseCoordinateSystems();
+ for( std::size_t nC=0; nC < aCooSysList.size(); ++nC )
+ {
+ if( AxisHelper::getIndicesForAxis( xAxis, aCooSysList[nC], rOutDimensionIndex, rOutAxisIndex ) )
+ {
+ rOutCooSysIndex = nC;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::vector< rtl::Reference< Axis > > AxisHelper::getAllAxesOfCoordinateSystem(
+ const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , bool bOnlyVisible /* = false */ )
+{
+ std::vector< rtl::Reference< Axis > > aAxisVector;
+
+ if(xCooSys.is())
+ {
+ sal_Int32 nMaxDimensionIndex = xCooSys->getDimension() -1;
+ if( nMaxDimensionIndex>=0 )
+ {
+ sal_Int32 nDimensionIndex = 0;
+ for(; nDimensionIndex<=nMaxDimensionIndex; ++nDimensionIndex)
+ {
+ const sal_Int32 nMaximumAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
+ for(sal_Int32 nAxisIndex=0; nAxisIndex<=nMaximumAxisIndex; ++nAxisIndex)
+ {
+ try
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2( nDimensionIndex, nAxisIndex );
+ if( xAxis.is() )
+ {
+ bool bAddAxis = true;
+ if( bOnlyVisible )
+ {
+ if( !(xAxis->getPropertyValue( "Show") >>= bAddAxis) )
+ bAddAxis = false;
+ }
+ if( bAddAxis )
+ aAxisVector.push_back( xAxis );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+ }
+ }
+
+ return aAxisVector;
+}
+
+std::vector< rtl::Reference< Axis > > AxisHelper::getAllAxesOfDiagram(
+ const rtl::Reference< Diagram >& xDiagram
+ , bool bOnlyVisible )
+{
+ std::vector< rtl::Reference< Axis > > aAxisVector;
+
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : xDiagram->getBaseCoordinateSystems() )
+ {
+ std::vector< rtl::Reference< Axis > > aAxesPerCooSys = AxisHelper::getAllAxesOfCoordinateSystem( coords, bOnlyVisible );
+ aAxisVector.insert( aAxisVector.end(), aAxesPerCooSys.begin(), aAxesPerCooSys.end() );
+ }
+
+ return aAxisVector;
+}
+
+Sequence< Reference< beans::XPropertySet > > AxisHelper::getAllGrids( const rtl::Reference< Diagram >& xDiagram )
+{
+ const std::vector< rtl::Reference< Axis > > aAllAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
+ std::vector< Reference< beans::XPropertySet > > aGridVector;
+
+ for( rtl::Reference< Axis > const & xAxis : aAllAxes )
+ {
+ Reference< beans::XPropertySet > xGridProperties( xAxis->getGridProperties() );
+ if( xGridProperties.is() )
+ aGridVector.push_back( xGridProperties );
+
+ const Sequence< Reference< beans::XPropertySet > > aSubGrids( xAxis->getSubGridProperties() );
+ for( Reference< beans::XPropertySet > const & xSubGrid : aSubGrids )
+ {
+ if( xSubGrid.is() )
+ aGridVector.push_back( xSubGrid );
+ }
+ }
+
+ return comphelper::containerToSequence( aGridVector );
+}
+
+void AxisHelper::getAxisOrGridPossibilities( Sequence< sal_Bool >& rPossibilityList
+ , const rtl::Reference< Diagram>& xDiagram, bool bAxis )
+{
+ rPossibilityList.realloc(6);
+ sal_Bool* pPossibilityList = rPossibilityList.getArray();
+
+ sal_Int32 nDimensionCount = DiagramHelper::getDimension( xDiagram );
+
+ //set possibilities:
+ sal_Int32 nIndex=0;
+ rtl::Reference< ChartType > xChartType = DiagramHelper::getChartTypeByIndex( xDiagram, 0 );
+ for(nIndex=0;nIndex<3;nIndex++)
+ pPossibilityList[nIndex]=ChartTypeHelper::isSupportingMainAxis(xChartType,nDimensionCount,nIndex);
+ for(nIndex=3;nIndex<6;nIndex++)
+ if( bAxis )
+ pPossibilityList[nIndex]=ChartTypeHelper::isSupportingSecondaryAxis(xChartType,nDimensionCount);
+ else
+ pPossibilityList[nIndex] = rPossibilityList[nIndex-3];
+}
+
+bool AxisHelper::isSecondaryYAxisNeeded( const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ if( !xCooSys.is() )
+ return false;
+
+ const std::vector< rtl::Reference< ChartType > > & aChartTypes( xCooSys->getChartTypes2() );
+ for( rtl::Reference< ChartType > const & chartType : aChartTypes )
+ {
+ const std::vector< rtl::Reference< DataSeries > > & aSeriesList = chartType->getDataSeries2();
+ for( sal_Int32 nS = aSeriesList.size(); nS-- ; )
+ {
+ sal_Int32 nAttachedAxisIndex = 0;
+ if( ( aSeriesList[nS]->getPropertyValue( "AttachedAxisIndex" ) >>= nAttachedAxisIndex ) &&
+ nAttachedAxisIndex>0 )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AxisHelper::shouldAxisBeDisplayed( const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ bool bRet = false;
+
+ if( xAxis.is() && xCooSys.is() )
+ {
+ sal_Int32 nDimensionIndex=-1;
+ sal_Int32 nAxisIndex=-1;
+ if( AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex ) )
+ {
+ sal_Int32 nDimensionCount = xCooSys->getDimension();
+ rtl::Reference< ChartType > xChartType( AxisHelper::getChartTypeByIndex( xCooSys, 0 ) );
+
+ bool bMainAxis = (nAxisIndex==MAIN_AXIS_INDEX);
+ if( bMainAxis )
+ bRet = ChartTypeHelper::isSupportingMainAxis(xChartType,nDimensionCount,nDimensionIndex);
+ else
+ bRet = ChartTypeHelper::isSupportingSecondaryAxis(xChartType,nDimensionCount);
+ }
+ }
+
+ return bRet;
+}
+
+void AxisHelper::getAxisOrGridExistence( Sequence< sal_Bool >& rExistenceList
+ , const rtl::Reference< Diagram>& xDiagram, bool bAxis )
+{
+ rExistenceList.realloc(6);
+ sal_Bool* pExistenceList = rExistenceList.getArray();
+
+ if(bAxis)
+ {
+ sal_Int32 nN;
+ for(nN=0;nN<3;nN++)
+ pExistenceList[nN] = AxisHelper::isAxisShown( nN, true, xDiagram );
+ for(nN=3;nN<6;nN++)
+ pExistenceList[nN] = AxisHelper::isAxisShown( nN%3, false, xDiagram );
+ }
+ else
+ {
+ sal_Int32 nN;
+
+ for(nN=0;nN<3;nN++)
+ pExistenceList[nN] = AxisHelper::isGridShown( nN, 0, true, xDiagram );
+ for(nN=3;nN<6;nN++)
+ pExistenceList[nN] = AxisHelper::isGridShown( nN%3, 0, false, xDiagram );
+ }
+}
+
+bool AxisHelper::changeVisibilityOfAxes( const rtl::Reference< Diagram >& xDiagram
+ , const Sequence< sal_Bool >& rOldExistenceList
+ , const Sequence< sal_Bool >& rNewExistenceList
+ , const Reference< uno::XComponentContext >& xContext
+ , ReferenceSizeProvider * pRefSizeProvider )
+{
+ bool bChanged = false;
+ for(sal_Int32 nN=0;nN<6;nN++)
+ {
+ if(rOldExistenceList[nN]!=rNewExistenceList[nN])
+ {
+ bChanged = true;
+ if(rNewExistenceList[nN])
+ {
+ AxisHelper::showAxis( nN%3, nN<3, xDiagram, xContext, pRefSizeProvider );
+ }
+ else
+ AxisHelper::hideAxis( nN%3, nN<3, xDiagram );
+ }
+ }
+ return bChanged;
+}
+
+bool AxisHelper::changeVisibilityOfGrids( const rtl::Reference< Diagram >& xDiagram
+ , const Sequence< sal_Bool >& rOldExistenceList
+ , const Sequence< sal_Bool >& rNewExistenceList )
+{
+ bool bChanged = false;
+ for(sal_Int32 nN=0;nN<6;nN++)
+ {
+ if(rOldExistenceList[nN]!=rNewExistenceList[nN])
+ {
+ bChanged = true;
+ if(rNewExistenceList[nN])
+ AxisHelper::showGrid( nN%3, 0, nN<3, xDiagram );
+ else
+ AxisHelper::hideGrid( nN%3, 0, nN<3, xDiagram );
+ }
+ }
+ return bChanged;
+}
+
+rtl::Reference< BaseCoordinateSystem > AxisHelper::getCoordinateSystemOfAxis(
+ const Reference< XAxis >& xAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ rtl::Reference< Axis > pAxis = dynamic_cast<Axis*>(xAxis.get());
+ assert(pAxis || !xAxis);
+ return getCoordinateSystemOfAxis(pAxis, xDiagram);
+}
+
+rtl::Reference< BaseCoordinateSystem > AxisHelper::getCoordinateSystemOfAxis(
+ const rtl::Reference< Axis >& xAxis
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ if (!xDiagram)
+ return nullptr;
+
+ rtl::Reference< BaseCoordinateSystem > xRet;
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ std::vector< rtl::Reference< Axis > > aAllAxis = AxisHelper::getAllAxesOfCoordinateSystem( xCooSys );
+
+ auto aFound = std::find( aAllAxis.begin(), aAllAxis.end(), xAxis );
+ if( aFound != aAllAxis.end())
+ {
+ xRet = xCooSys;
+ break;
+ }
+ }
+ return xRet;
+}
+
+rtl::Reference< ChartType > AxisHelper::getChartTypeByIndex( const rtl::Reference< BaseCoordinateSystem >& xCooSys, sal_Int32 nIndex )
+{
+ rtl::Reference< ChartType > xChartType;
+
+ if( xCooSys.is() )
+ {
+ const std::vector< rtl::Reference< ChartType > > aChartTypeList( xCooSys->getChartTypes2() );
+ if( nIndex >= 0 && o3tl::make_unsigned(nIndex) < aChartTypeList.size() )
+ xChartType = aChartTypeList[nIndex];
+ }
+
+ return xChartType;
+}
+
+void AxisHelper::setRTLAxisLayout( const rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ if( !xCooSys.is() )
+ return;
+
+ bool bCartesian = xCooSys->getViewServiceName() == CHART2_COOSYSTEM_CARTESIAN_VIEW_SERVICE_NAME;
+ if( !bCartesian )
+ return;
+
+ bool bVertical = false;
+ xCooSys->getPropertyValue( "SwapXAndYAxis" ) >>= bVertical;
+
+ sal_Int32 nHorizontalAxisDimension = bVertical ? 1 : 0;
+ sal_Int32 nVerticalAxisDimension = bVertical ? 0 : 1;
+
+ try
+ {
+ //reverse direction for horizontal main axis
+ rtl::Reference< Axis > xHorizontalMainAxis = AxisHelper::getAxis( nHorizontalAxisDimension, MAIN_AXIS_INDEX, xCooSys );
+ if( xHorizontalMainAxis.is() )
+ {
+ chart2::ScaleData aScale = xHorizontalMainAxis->getScaleData();
+ aScale.Orientation = chart2::AxisOrientation_REVERSE;
+ xHorizontalMainAxis->setScaleData(aScale);
+ }
+
+ //mathematical direction for vertical main axis
+ rtl::Reference< Axis > xVerticalMainAxis = AxisHelper::getAxis( nVerticalAxisDimension, MAIN_AXIS_INDEX, xCooSys );
+ if( xVerticalMainAxis.is() )
+ {
+ chart2::ScaleData aScale = xVerticalMainAxis->getScaleData();
+ aScale.Orientation = chart2::AxisOrientation_MATHEMATICAL;
+ xVerticalMainAxis->setScaleData(aScale);
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+
+ try
+ {
+ //reverse direction for horizontal secondary axis
+ rtl::Reference< Axis > xHorizontalSecondaryAxis = AxisHelper::getAxis( nHorizontalAxisDimension, SECONDARY_AXIS_INDEX, xCooSys );
+ if( xHorizontalSecondaryAxis.is() )
+ {
+ chart2::ScaleData aScale = xHorizontalSecondaryAxis->getScaleData();
+ aScale.Orientation = chart2::AxisOrientation_REVERSE;
+ xHorizontalSecondaryAxis->setScaleData(aScale);
+ }
+
+ //mathematical direction for vertical secondary axis
+ rtl::Reference< Axis > xVerticalSecondaryAxis = AxisHelper::getAxis( nVerticalAxisDimension, SECONDARY_AXIS_INDEX, xCooSys );
+ if( xVerticalSecondaryAxis.is() )
+ {
+ chart2::ScaleData aScale = xVerticalSecondaryAxis->getScaleData();
+ aScale.Orientation = chart2::AxisOrientation_MATHEMATICAL;
+ xVerticalSecondaryAxis->setScaleData(aScale);
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+rtl::Reference< ChartType > AxisHelper::getFirstChartTypeWithSeriesAttachedToAxisIndex( const rtl::Reference< Diagram >& xDiagram, const sal_Int32 nAttachedAxisIndex )
+{
+ rtl::Reference< ChartType > xChartType;
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector = DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+ for (auto const& series : aSeriesVector)
+ {
+ sal_Int32 nCurrentIndex = DataSeriesHelper::getAttachedAxisIndex(series);
+ if( nAttachedAxisIndex == nCurrentIndex )
+ {
+ xChartType = DiagramHelper::getChartTypeOfSeries(xDiagram, series);
+ if(xChartType.is())
+ break;
+ }
+ }
+ return xChartType;
+}
+
+bool AxisHelper::isAxisPositioningEnabled()
+{
+ const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(GetODFSaneDefaultVersion());
+ return nCurrentVersion >= SvtSaveOptions::ODFSVER_012;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/BaseGFXHelper.cxx b/chart2/source/tools/BaseGFXHelper.cxx
new file mode 100644
index 000000000..17bd4f5e1
--- /dev/null
+++ b/chart2/source/tools/BaseGFXHelper.cxx
@@ -0,0 +1,236 @@
+/* -*- 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 <BaseGFXHelper.hxx>
+#include <com/sun/star/drawing/PolyPolygonShape3D.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::drawing;
+using namespace ::basegfx;
+
+namespace chart::BaseGFXHelper
+{
+
+::basegfx::B3DRange getBoundVolume( const drawing::PolyPolygonShape3D& rPolyPoly )
+{
+ ::basegfx::B3DRange aRet;
+
+ bool bInited = false;
+ sal_Int32 nPolyCount = rPolyPoly.SequenceX.getLength();
+ for(sal_Int32 nPoly = 0; nPoly < nPolyCount; nPoly++)
+ {
+ sal_Int32 nPointCount = rPolyPoly.SequenceX[nPoly].getLength();
+ for( sal_Int32 nPoint = 0; nPoint < nPointCount; nPoint++)
+ {
+ if(!bInited)
+ {
+ aRet = ::basegfx::B3DRange(::basegfx::B3DTuple(
+ rPolyPoly.SequenceX[nPoly][nPoint]
+ , rPolyPoly.SequenceY[nPoly][nPoint]
+ , rPolyPoly.SequenceZ[nPoly][nPoint]));
+ bInited = true;
+ }
+ else
+ {
+ aRet.expand( ::basegfx::B3DTuple(
+ rPolyPoly.SequenceX[nPoly][nPoint]
+ , rPolyPoly.SequenceY[nPoly][nPoint]
+ , rPolyPoly.SequenceZ[nPoly][nPoint]));
+ }
+ }
+ }
+
+ return aRet;
+}
+
+::basegfx::B3DRange getBoundVolume( const std::vector<std::vector<css::drawing::Position3D>>& rPolyPoly )
+{
+ ::basegfx::B3DRange aRet;
+
+ bool bInited = false;
+ sal_Int32 nPolyCount = rPolyPoly.size();
+ for(sal_Int32 nPoly = 0; nPoly < nPolyCount; nPoly++)
+ {
+ sal_Int32 nPointCount = rPolyPoly[nPoly].size();
+ for( sal_Int32 nPoint = 0; nPoint < nPointCount; nPoint++)
+ {
+ if(!bInited)
+ {
+ aRet = ::basegfx::B3DRange(::basegfx::B3DTuple(
+ rPolyPoly[nPoly][nPoint].PositionX
+ , rPolyPoly[nPoly][nPoint].PositionY
+ , rPolyPoly[nPoly][nPoint].PositionZ));
+ bInited = true;
+ }
+ else
+ {
+ aRet.expand( ::basegfx::B3DTuple(
+ rPolyPoly[nPoly][nPoint].PositionX
+ , rPolyPoly[nPoly][nPoint].PositionY
+ , rPolyPoly[nPoly][nPoint].PositionZ));
+ }
+ }
+ }
+
+ return aRet;
+}
+
+B2IRectangle makeRectangle( const awt::Point& rPos, const awt::Size& rSize )
+{
+ return B2IRectangle(rPos.X,rPos.Y,rPos.X+rSize.Width,rPos.Y+rSize.Height);
+}
+
+B2IRectangle makeRectangle( const awt::Rectangle& rRect )
+{
+ return B2IRectangle(rRect.X, rRect.Y, rRect.X+rRect.Width, rRect.Y+rRect.Height);
+}
+
+awt::Point B2IRectangleToAWTPoint( const ::basegfx::B2IRectangle& rB2IRectangle )
+{
+ return awt::Point( rB2IRectangle.getMinX(), rB2IRectangle.getMinY() );
+}
+
+awt::Size B2IRectangleToAWTSize( const ::basegfx::B2IRectangle& rB2IRectangle )
+{
+ return awt::Size( static_cast< sal_Int32 >( rB2IRectangle.getWidth()),
+ static_cast< sal_Int32 >( rB2IRectangle.getHeight()));
+}
+
+awt::Rectangle toAwtRectangle(const basegfx::B2IRectangle& rRectangle)
+{
+ return awt::Rectangle(rRectangle.getMinX(), rRectangle.getMinY(),
+ rRectangle.getWidth(), rRectangle.getHeight());
+}
+
+B3DVector Direction3DToB3DVector( const Direction3D& rDirection )
+{
+ return B3DVector(
+ rDirection.DirectionX
+ , rDirection.DirectionY
+ , rDirection.DirectionZ
+ );
+}
+
+Direction3D B3DVectorToDirection3D( const B3DVector& rB3DVector )
+{
+ return Direction3D(
+ rB3DVector.getX()
+ , rB3DVector.getY()
+ , rB3DVector.getZ()
+ );
+}
+
+B3DVector Position3DToB3DVector( const Position3D& rPosition )
+{
+ return B3DVector(
+ rPosition.PositionX
+ , rPosition.PositionY
+ , rPosition.PositionZ
+ );
+}
+
+Position3D B3DVectorToPosition3D( const B3DVector& rB3DVector )
+{
+ return Position3D(
+ rB3DVector.getX()
+ , rB3DVector.getY()
+ , rB3DVector.getZ()
+ );
+}
+
+B3DHomMatrix HomogenMatrixToB3DHomMatrix( const HomogenMatrix & rHomogenMatrix )
+{
+ B3DHomMatrix aResult;
+
+ aResult.set( 0, 0, rHomogenMatrix.Line1.Column1 );
+ aResult.set( 0, 1, rHomogenMatrix.Line1.Column2 );
+ aResult.set( 0, 2, rHomogenMatrix.Line1.Column3 );
+ aResult.set( 0, 3, rHomogenMatrix.Line1.Column4 );
+
+ aResult.set( 1, 0, rHomogenMatrix.Line2.Column1 );
+ aResult.set( 1, 1, rHomogenMatrix.Line2.Column2 );
+ aResult.set( 1, 2, rHomogenMatrix.Line2.Column3 );
+ aResult.set( 1, 3, rHomogenMatrix.Line2.Column4 );
+
+ aResult.set( 2, 0, rHomogenMatrix.Line3.Column1 );
+ aResult.set( 2, 1, rHomogenMatrix.Line3.Column2 );
+ aResult.set( 2, 2, rHomogenMatrix.Line3.Column3 );
+ aResult.set( 2, 3, rHomogenMatrix.Line3.Column4 );
+
+ aResult.set( 3, 0, rHomogenMatrix.Line4.Column1 );
+ aResult.set( 3, 1, rHomogenMatrix.Line4.Column2 );
+ aResult.set( 3, 2, rHomogenMatrix.Line4.Column3 );
+ aResult.set( 3, 3, rHomogenMatrix.Line4.Column4 );
+
+ return aResult;
+}
+
+HomogenMatrix B3DHomMatrixToHomogenMatrix( const B3DHomMatrix & rB3DMatrix )
+{
+ HomogenMatrix aResult;
+
+ aResult.Line1.Column1 = rB3DMatrix.get( 0, 0 );
+ aResult.Line1.Column2 = rB3DMatrix.get( 0, 1 );
+ aResult.Line1.Column3 = rB3DMatrix.get( 0, 2 );
+ aResult.Line1.Column4 = rB3DMatrix.get( 0, 3 );
+
+ aResult.Line2.Column1 = rB3DMatrix.get( 1, 0 );
+ aResult.Line2.Column2 = rB3DMatrix.get( 1, 1 );
+ aResult.Line2.Column3 = rB3DMatrix.get( 1, 2 );
+ aResult.Line2.Column4 = rB3DMatrix.get( 1, 3 );
+
+ aResult.Line3.Column1 = rB3DMatrix.get( 2, 0 );
+ aResult.Line3.Column2 = rB3DMatrix.get( 2, 1 );
+ aResult.Line3.Column3 = rB3DMatrix.get( 2, 2 );
+ aResult.Line3.Column4 = rB3DMatrix.get( 2, 3 );
+
+ aResult.Line4.Column1 = rB3DMatrix.get( 3, 0 );
+ aResult.Line4.Column2 = rB3DMatrix.get( 3, 1 );
+ aResult.Line4.Column3 = rB3DMatrix.get( 3, 2 );
+ aResult.Line4.Column4 = rB3DMatrix.get( 3, 3 );
+
+ return aResult;
+}
+
+B3DTuple GetRotationFromMatrix( const B3DHomMatrix & rB3DMatrix )
+{
+ B3DTuple aScale, aTranslation, aRotation, aShearing;
+ rB3DMatrix.decompose( aScale, aTranslation, aRotation, aShearing );
+ return aRotation;
+}
+
+B3DTuple GetScaleFromMatrix( const B3DHomMatrix & rB3DMatrix )
+{
+ B3DTuple aScale, aTranslation, aRotation, aShearing;
+ rB3DMatrix.decompose( aScale, aTranslation, aRotation, aShearing );
+ return aScale;
+}
+
+void ReduceToRotationMatrix( ::basegfx::B3DHomMatrix & rB3DMatrix )
+{
+ B3DTuple aR( GetRotationFromMatrix( rB3DMatrix ) );
+ ::basegfx::B3DHomMatrix aRotationMatrix;
+ aRotationMatrix.rotate(aR.getX(),aR.getY(),aR.getZ());
+ rB3DMatrix = aRotationMatrix;
+}
+
+} // namespace chart::BaseGFXHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/CachedDataSequence.cxx b/chart2/source/tools/CachedDataSequence.cxx
new file mode 100644
index 000000000..440e9f736
--- /dev/null
+++ b/chart2/source/tools/CachedDataSequence.cxx
@@ -0,0 +1,352 @@
+/* -*- 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 <CachedDataSequence.hxx>
+#include <CommonFunctors.hxx>
+#include <ModifyListenerHelper.hxx>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <algorithm>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+using ::osl::MutexGuard;
+
+// necessary for MS compiler
+using ::comphelper::OPropertyContainer;
+using ::comphelper::OMutexAndBroadcastHelper;
+using ::comphelper::OPropertyArrayUsageHelper;
+using ::chart::impl::CachedDataSequence_Base;
+
+namespace
+{
+constexpr OUStringLiteral lcl_aServiceName = u"com.sun.star.comp.chart.CachedDataSequence";
+
+enum
+{
+// PROP_SOURCE_IDENTIFIER,
+ PROP_NUMBERFORMAT_KEY,
+ PROP_PROPOSED_ROLE
+};
+} // anonymous namespace
+
+namespace chart
+{
+
+CachedDataSequence::CachedDataSequence()
+ : OPropertyContainer( GetBroadcastHelper()),
+ CachedDataSequence_Base( GetMutex()),
+ m_eCurrentDataType( NUMERICAL ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+}
+CachedDataSequence::CachedDataSequence( const Reference< uno::XComponentContext > & /*xContext*/ )
+ : OPropertyContainer( GetBroadcastHelper()),
+ CachedDataSequence_Base( GetMutex()),
+ m_eCurrentDataType( MIXED ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+}
+
+CachedDataSequence::CachedDataSequence( const OUString & rSingleText )
+ : OPropertyContainer( GetBroadcastHelper()),
+ CachedDataSequence_Base( GetMutex()),
+ m_eCurrentDataType( TEXTUAL ),
+ m_aTextualSequence({rSingleText}),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+}
+
+CachedDataSequence::CachedDataSequence( const CachedDataSequence & rSource )
+ : OPropertyContainer( GetBroadcastHelper()),
+ CachedDataSequence_Base( GetMutex()),
+ m_nNumberFormatKey( rSource.m_nNumberFormatKey ),
+ m_sRole( rSource.m_sRole ),
+ m_eCurrentDataType( rSource.m_eCurrentDataType ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ switch( m_eCurrentDataType )
+ {
+ case TEXTUAL:
+ m_aTextualSequence = rSource.m_aTextualSequence;
+ break;
+ case NUMERICAL:
+ m_aNumericalSequence = rSource.m_aNumericalSequence;
+ break;
+ case MIXED:
+ m_aMixedSequence = rSource.m_aMixedSequence;
+ break;
+ }
+
+ registerProperties();
+}
+
+CachedDataSequence::~CachedDataSequence()
+{}
+
+void CachedDataSequence::registerProperties()
+{
+ registerProperty( "NumberFormatKey",
+ PROP_NUMBERFORMAT_KEY,
+ 0, // PropertyAttributes
+ & m_nNumberFormatKey,
+ cppu::UnoType<decltype(m_nNumberFormatKey)>::get() );
+
+ registerProperty( "Role",
+ PROP_PROPOSED_ROLE,
+ 0, // PropertyAttributes
+ & m_sRole,
+ cppu::UnoType<decltype(m_sRole)>::get() );
+}
+
+Sequence< double > CachedDataSequence::Impl_getNumericalData() const
+{
+ if( m_eCurrentDataType == NUMERICAL )
+ return m_aNumericalSequence;
+
+ sal_Int32 nSize = ( m_eCurrentDataType == TEXTUAL )
+ ? m_aTextualSequence.getLength()
+ : m_aMixedSequence.getLength();
+
+ Sequence< double > aResult( nSize );
+ double * pResultArray = aResult.getArray();
+
+ if( m_eCurrentDataType == TEXTUAL )
+ {
+ const OUString * pTextArray = m_aTextualSequence.getConstArray();
+ std::transform( pTextArray, pTextArray + nSize,
+ pResultArray,
+ CommonFunctors::OUStringToDouble() );
+ }
+ else
+ {
+ OSL_ASSERT( m_eCurrentDataType == MIXED );
+ const Any * pMixedArray = m_aMixedSequence.getConstArray();
+ std::transform( pMixedArray, pMixedArray + nSize,
+ pResultArray,
+ CommonFunctors::AnyToDouble() );
+ }
+ return aResult;
+}
+
+Sequence< OUString > CachedDataSequence::Impl_getTextualData() const
+{
+ if( m_eCurrentDataType == TEXTUAL )
+ return m_aTextualSequence;
+
+ sal_Int32 nSize = ( m_eCurrentDataType == NUMERICAL )
+ ? m_aNumericalSequence.getLength()
+ : m_aMixedSequence.getLength();
+
+ Sequence< OUString > aResult( nSize );
+ OUString * pResultArray = aResult.getArray();
+
+ if( m_eCurrentDataType == NUMERICAL )
+ {
+ const double * pTextArray = m_aNumericalSequence.getConstArray();
+ std::transform( pTextArray, pTextArray + nSize,
+ pResultArray,
+ CommonFunctors::DoubleToOUString() );
+ }
+ else
+ {
+ OSL_ASSERT( m_eCurrentDataType == MIXED );
+ const Any * pMixedArray = m_aMixedSequence.getConstArray();
+ std::transform( pMixedArray, pMixedArray + nSize,
+ pResultArray,
+ CommonFunctors::AnyToString() );
+ }
+
+ return aResult;
+}
+
+Sequence< Any > CachedDataSequence::Impl_getMixedData() const
+{
+ if( m_eCurrentDataType == MIXED )
+ return m_aMixedSequence;
+
+ sal_Int32 nSize = ( m_eCurrentDataType == NUMERICAL )
+ ? m_aNumericalSequence.getLength()
+ : m_aTextualSequence.getLength();
+
+ Sequence< Any > aResult( nSize );
+ Any * pResultArray = aResult.getArray();
+
+ if( m_eCurrentDataType == NUMERICAL )
+ {
+ const double * pTextArray = m_aNumericalSequence.getConstArray();
+ std::transform( pTextArray, pTextArray + nSize,
+ pResultArray,
+ CommonFunctors::makeAny< double >() );
+ }
+ else
+ {
+ OSL_ASSERT( m_eCurrentDataType == TEXTUAL );
+ const OUString * pMixedArray = m_aTextualSequence.getConstArray();
+ std::transform( pMixedArray, pMixedArray + nSize,
+ pResultArray,
+ CommonFunctors::makeAny< OUString >() );
+ }
+
+ return aResult;
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( CachedDataSequence, CachedDataSequence_Base, OPropertyContainer )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( CachedDataSequence, CachedDataSequence_Base, OPropertyContainer )
+
+// ____ XPropertySet ____
+Reference< beans::XPropertySetInfo > SAL_CALL CachedDataSequence::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+// ____ ::comphelper::OPropertySetHelper ____
+::cppu::IPropertyArrayHelper& CachedDataSequence::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+// ____ ::comphelper::OPropertyArrayHelper ____
+::cppu::IPropertyArrayHelper* CachedDataSequence::createArrayHelper() const
+{
+ Sequence< beans::Property > aProps;
+ // describes all properties which have been registered in the ctor
+ describeProperties( aProps );
+
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+OUString SAL_CALL CachedDataSequence::getImplementationName()
+{
+ return lcl_aServiceName;
+}
+
+sal_Bool SAL_CALL CachedDataSequence::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL CachedDataSequence::getSupportedServiceNames()
+{
+ return {
+ lcl_aServiceName,
+ "com.sun.star.chart2.data.DataSequence",
+ "com.sun.star.chart2.data.NumericalDataSequence",
+ "com.sun.star.chart2.data.TextualDataSequence"
+ };
+}
+
+// ________ XNumericalDataSequence ________
+Sequence< double > SAL_CALL CachedDataSequence::getNumericalData()
+{
+ MutexGuard aGuard( GetMutex() );
+
+ if( m_eCurrentDataType == NUMERICAL )
+ return m_aNumericalSequence;
+ else
+ return Impl_getNumericalData();
+}
+
+// ________ XTextualDataSequence ________
+Sequence< OUString > SAL_CALL CachedDataSequence::getTextualData()
+{
+ MutexGuard aGuard( GetMutex() );
+
+ if( m_eCurrentDataType == TEXTUAL )
+ return m_aTextualSequence;
+ else
+ return Impl_getTextualData();
+}
+
+// ________ XDataSequence ________
+Sequence< Any > SAL_CALL CachedDataSequence::getData()
+{
+ MutexGuard aGuard( GetMutex() );
+ return Impl_getMixedData();
+}
+
+OUString SAL_CALL CachedDataSequence::getSourceRangeRepresentation()
+{
+ return m_sRole;
+}
+
+Sequence< OUString > SAL_CALL CachedDataSequence::generateLabel( chart2::data::LabelOrigin /*eLabelOrigin*/ )
+{
+ // return empty label, as we have no range representations to determine something useful
+ return Sequence< OUString >();
+}
+
+::sal_Int32 SAL_CALL CachedDataSequence::getNumberFormatKeyByIndex( ::sal_Int32 /*nIndex*/ )
+{
+ return 0;
+}
+
+Reference< util::XCloneable > SAL_CALL CachedDataSequence::createClone()
+{
+ return new CachedDataSequence( *this );
+}
+
+void SAL_CALL CachedDataSequence::addModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL CachedDataSequence::removeModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+// lang::XInitialization:
+void SAL_CALL CachedDataSequence::initialize(const uno::Sequence< uno::Any > & _aArguments)
+{
+ ::comphelper::SequenceAsHashMap aMap(_aArguments);
+ m_aNumericalSequence = aMap.getUnpackedValueOrDefault( "DataSequence" ,m_aNumericalSequence);
+ if ( m_aNumericalSequence.hasElements() )
+ m_eCurrentDataType = NUMERICAL;
+ else
+ {
+ m_aTextualSequence = aMap.getUnpackedValueOrDefault( "DataSequence" ,m_aTextualSequence);
+ if ( m_aTextualSequence.hasElements() )
+ m_eCurrentDataType = TEXTUAL;
+ else
+ {
+ m_aMixedSequence = aMap.getUnpackedValueOrDefault( "DataSequence" ,m_aMixedSequence);
+ if ( m_aMixedSequence.hasElements() )
+ m_eCurrentDataType = MIXED;
+ }
+ }
+}
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart_CachedDataSequence_get_implementation(css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::CachedDataSequence(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/CharacterProperties.cxx b/chart2/source/tools/CharacterProperties.cxx
new file mode 100644
index 000000000..2923cc17b
--- /dev/null
+++ b/chart2/source/tools/CharacterProperties.cxx
@@ -0,0 +1,459 @@
+/* -*- 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 <CharacterProperties.hxx>
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/text/FontRelief.hpp>
+#include <com/sun/star/text/FontEmphasis.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <osl/diagnose.h>
+#include <unotools/lingucfg.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/outdev.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace chart
+{
+
+void CharacterProperties::AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ // CharacterProperties
+ rOutProperties.emplace_back( "CharFontName",
+ PROP_CHAR_FONT_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "CharFontStyleName",
+ PROP_CHAR_FONT_STYLE_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharFontFamily (see awt.FontFamily)
+ rOutProperties.emplace_back( "CharFontFamily",
+ PROP_CHAR_FONT_FAMILY,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontCharSet (see awt.CharSet)
+ rOutProperties.emplace_back( "CharFontCharSet",
+ PROP_CHAR_FONT_CHAR_SET,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontPitch (see awt.FontPitch)
+ rOutProperties.emplace_back( "CharFontPitch",
+ PROP_CHAR_FONT_PITCH,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharColor
+ rOutProperties.emplace_back( "CharColor",
+ PROP_CHAR_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharEscapement
+ rOutProperties.emplace_back( "CharEscapement",
+ PROP_CHAR_ESCAPEMENT,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharHeight
+ rOutProperties.emplace_back( "CharHeight",
+ PROP_CHAR_CHAR_HEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharUnderline (see awt.FontUnderline)
+ rOutProperties.emplace_back( "CharUnderline",
+ PROP_CHAR_UNDERLINE,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharUnderlineColor
+ rOutProperties.emplace_back( "CharUnderlineColor",
+ PROP_CHAR_UNDERLINE_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharUnderlineHasColor
+ rOutProperties.emplace_back( "CharUnderlineHasColor",
+ PROP_CHAR_UNDERLINE_HAS_COLOR,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharOverline (see awt.FontUnderline)
+ rOutProperties.emplace_back( "CharOverline",
+ PROP_CHAR_OVERLINE,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharOverlineColor
+ rOutProperties.emplace_back( "CharOverlineColor",
+ PROP_CHAR_OVERLINE_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharOverlineHasColor
+ rOutProperties.emplace_back( "CharOverlineHasColor",
+ PROP_CHAR_OVERLINE_HAS_COLOR,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharWeight (see awt.FontWeight)
+ rOutProperties.emplace_back( "CharWeight",
+ PROP_CHAR_WEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharPosture
+ rOutProperties.emplace_back( "CharPosture",
+ PROP_CHAR_POSTURE,
+ cppu::UnoType<awt::FontSlant>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "CharAutoKerning",
+ PROP_CHAR_AUTO_KERNING,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ rOutProperties.emplace_back( "CharKerning",
+ PROP_CHAR_KERNING,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharEscapementHeight
+ rOutProperties.emplace_back( "CharEscapementHeight",
+ PROP_CHAR_ESCAPEMENT_HEIGHT,
+ cppu::UnoType<sal_Int8>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharStrikeout (see awt.FontStrikeout)
+ rOutProperties.emplace_back( "CharStrikeout",
+ PROP_CHAR_STRIKE_OUT,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharWordMode
+ rOutProperties.emplace_back( "CharWordMode",
+ PROP_CHAR_WORD_MODE,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharLocale
+ rOutProperties.emplace_back( "CharLocale",
+ PROP_CHAR_LOCALE,
+ cppu::UnoType<lang::Locale>::get(),
+ //#i111967# no PropertyChangeEvent is fired on change so far
+ beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharShadowed
+ rOutProperties.emplace_back( "CharShadowed",
+ PROP_CHAR_SHADOWED,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharContoured
+ rOutProperties.emplace_back( "CharContoured",
+ PROP_CHAR_CONTOURED,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharRelief (see text.FontRelief)
+ rOutProperties.emplace_back( "CharRelief",
+ PROP_CHAR_RELIEF,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // CharEmphasize (see text.FontEmphasis)
+ rOutProperties.emplace_back( "CharEmphasis",
+ PROP_CHAR_EMPHASIS,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharacterPropertiesAsian
+
+ // CharFontNameAsian
+ rOutProperties.emplace_back( "CharFontNameAsian",
+ PROP_CHAR_ASIAN_FONT_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontStyleNameAsian
+ rOutProperties.emplace_back( "CharFontStyleNameAsian",
+ PROP_CHAR_ASIAN_FONT_STYLE_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharFontFamilyAsian (see awt.FontFamily)
+ rOutProperties.emplace_back( "CharFontFamilyAsian",
+ PROP_CHAR_ASIAN_FONT_FAMILY,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontCharSetAsian (see awt.CharSet)
+ rOutProperties.emplace_back( "CharFontCharSetAsian",
+ PROP_CHAR_ASIAN_CHAR_SET,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontPitchAsian (see awt.FontPitch)
+ rOutProperties.emplace_back( "CharFontPitchAsian",
+ PROP_CHAR_ASIAN_FONT_PITCH,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharHeightAsian
+ rOutProperties.emplace_back( "CharHeightAsian",
+ PROP_CHAR_ASIAN_CHAR_HEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharWeightAsian
+ rOutProperties.emplace_back( "CharWeightAsian",
+ PROP_CHAR_ASIAN_WEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharPostureAsian
+ rOutProperties.emplace_back( "CharPostureAsian",
+ PROP_CHAR_ASIAN_POSTURE,
+ cppu::UnoType<awt::FontSlant>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharLocaleAsian
+ rOutProperties.emplace_back( "CharLocaleAsian",
+ PROP_CHAR_ASIAN_LOCALE,
+ cppu::UnoType<lang::Locale>::get(),
+ //#i111967# no PropertyChangeEvent is fired on change so far
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // CharacterPropertiesComplex
+
+ // CharFontNameComplex
+ rOutProperties.emplace_back( "CharFontNameComplex",
+ PROP_CHAR_COMPLEX_FONT_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontStyleNameComplex
+ rOutProperties.emplace_back( "CharFontStyleNameComplex",
+ PROP_CHAR_COMPLEX_FONT_STYLE_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+ // CharFontFamilyComplex (see awt.FontFamily)
+ rOutProperties.emplace_back( "CharFontFamilyComplex",
+ PROP_CHAR_COMPLEX_FONT_FAMILY,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontCharSetComplex (see awt.CharSet)
+ rOutProperties.emplace_back( "CharFontCharSetComplex",
+ PROP_CHAR_COMPLEX_CHAR_SET,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharFontPitchComplex (see awt.FontPitch)
+ rOutProperties.emplace_back( "CharFontPitchComplex",
+ PROP_CHAR_COMPLEX_FONT_PITCH,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharHeightComplex
+ rOutProperties.emplace_back( "CharHeightComplex",
+ PROP_CHAR_COMPLEX_CHAR_HEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharWeightComplex
+ rOutProperties.emplace_back( "CharWeightComplex",
+ PROP_CHAR_COMPLEX_WEIGHT,
+ cppu::UnoType<float>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharPostureComplex
+ rOutProperties.emplace_back( "CharPostureComplex",
+ PROP_CHAR_COMPLEX_POSTURE,
+ cppu::UnoType<awt::FontSlant>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // CharLocaleComplex
+ rOutProperties.emplace_back( "CharLocaleComplex",
+ PROP_CHAR_COMPLEX_LOCALE,
+ cppu::UnoType<lang::Locale>::get(),
+ //#i111967# no PropertyChangeEvent is fired on change so far
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // Writing Mode left to right vs right to left
+ rOutProperties.emplace_back( "WritingMode",
+ PROP_WRITING_MODE,
+ cppu::UnoType<sal_Int16>::get(), /*css::text::WritingMode2*/
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ParaIsCharacterDistance",
+ PROP_PARA_IS_CHARACTER_DISTANCE,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+}
+
+void CharacterProperties::AddDefaultsToMap(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ const float fDefaultFontHeight = 13.0;
+
+ SvtLinguConfig aLinguConfig;
+ lang::Locale aDefaultLocale;
+ aLinguConfig.GetProperty(u"DefaultLocale") >>= aDefaultLocale;
+ lang::Locale aDefaultLocale_CJK;
+ aLinguConfig.GetProperty(u"DefaultLocale_CJK") >>= aDefaultLocale_CJK;
+ lang::Locale aDefaultLocale_CTL;
+ aLinguConfig.GetProperty(u"DefaultLocale_CTL") >>= aDefaultLocale_CTL;
+
+ using namespace ::com::sun::star::i18n::ScriptType;
+ LanguageType nLang;
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aDefaultLocale, false), LATIN);
+ vcl::Font aFont = OutputDevice::GetDefaultFont( DefaultFontType::LATIN_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne );
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aDefaultLocale_CJK, false), ASIAN);
+ vcl::Font aFontCJK = OutputDevice::GetDefaultFont( DefaultFontType::CJK_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne );
+ nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aDefaultLocale_CTL, false), COMPLEX);
+ vcl::Font aFontCTL = OutputDevice::GetDefaultFont( DefaultFontType::CTL_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne );
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_FONT_NAME, aFont.GetFamilyName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_FONT_STYLE_NAME, aFont.GetStyleName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_FONT_FAMILY, sal_Int16(aFont.GetFamilyType()) );//awt::FontFamily::SWISS
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_FONT_CHAR_SET, sal_Int16(aFont.GetCharSet()) );//use awt::CharSet::DONTKNOW instead of SYSTEM to avoid assertion issue 50249
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_FONT_PITCH, sal_Int16(aFont.GetPitch()) );//awt::FontPitch::VARIABLE
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_CHAR_COLOR, -1 ); //automatic color (COL_AUTO)
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_CHAR_HEIGHT, fDefaultFontHeight );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_UNDERLINE, awt::FontUnderline::NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_CHAR_UNDERLINE_COLOR, -1 ); //automatic color (COL_AUTO)
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_UNDERLINE_HAS_COLOR, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_OVERLINE, awt::FontUnderline::NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_CHAR_OVERLINE_COLOR, -1 ); //automatic color (COL_AUTO)
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_OVERLINE_HAS_COLOR, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_WEIGHT, awt::FontWeight::NORMAL );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_POSTURE, awt::FontSlant_NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_AUTO_KERNING, true );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, PROP_CHAR_KERNING, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, PROP_CHAR_STRIKE_OUT, awt::FontStrikeout::NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_WORD_MODE, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_LOCALE, aDefaultLocale );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_SHADOWED, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_CONTOURED, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_RELIEF, text::FontRelief::NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_EMPHASIS, text::FontEmphasis::NONE );
+
+ // Asian (com.sun.star.style.CharacterPropertiesAsian)
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_CHAR_HEIGHT, fDefaultFontHeight );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_WEIGHT, awt::FontWeight::NORMAL );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_POSTURE, awt::FontSlant_NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_LOCALE, aDefaultLocale_CJK );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_FONT_NAME, aFontCJK.GetFamilyName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_FONT_STYLE_NAME, aFontCJK.GetStyleName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_FONT_FAMILY, sal_Int16(aFontCJK.GetFamilyType()) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_CHAR_SET, sal_Int16(aFontCJK.GetCharSet()) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_ASIAN_FONT_PITCH, sal_Int16(aFontCJK.GetPitch()) );
+
+ // Complex Text Layout (com.sun.star.style.CharacterPropertiesComplex)
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_CHAR_HEIGHT, fDefaultFontHeight );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_WEIGHT, awt::FontWeight::NORMAL );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_POSTURE, awt::FontSlant_NONE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_LOCALE, aDefaultLocale_CTL );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_FONT_NAME, aFontCTL.GetFamilyName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_FONT_STYLE_NAME, aFontCTL.GetStyleName() );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_FONT_FAMILY, sal_Int16(aFontCTL.GetFamilyType()) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_CHAR_SET, sal_Int16(aFontCTL.GetCharSet()) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_CHAR_COMPLEX_FONT_PITCH, sal_Int16(aFontCTL.GetPitch()) );
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_WRITING_MODE, sal_Int16( css::text::WritingMode2::PAGE ) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_PARA_IS_CHARACTER_DISTANCE, true );
+}
+
+bool CharacterProperties::IsCharacterPropertyHandle( sal_Int32 nHandle )
+{
+ return ( FAST_PROPERTY_ID_START_CHAR_PROP <= nHandle &&
+ nHandle < CharacterProperties::FAST_PROPERTY_ID_END_CHAR_PROP );
+}
+
+awt::FontDescriptor CharacterProperties::createFontDescriptorFromPropertySet(
+ const uno::Reference< beans::XMultiPropertySet > & xMultiPropSet )
+{
+ awt::FontDescriptor aResult;
+ // Note: keep this sorted!
+ uno::Sequence< OUString > aPropNameSeq{
+ "CharFontCharSet", // CharSet
+ "CharFontFamily", // Family
+ "CharFontName", // Name
+ "CharFontPitch", // Pitch
+ "CharFontStyleName", // StyleName
+ "CharHeight", // Height
+ "CharPosture", // Slant
+ "CharStrikeout", // Strikeout
+ "CharUnderline", // Underline
+ "CharWeight", // Weight
+ "CharWordMode"}; // WordLineMode
+ uno::Sequence< uno::Any > aValues( xMultiPropSet->getPropertyValues( aPropNameSeq ));
+
+ sal_Int32 i=0;
+ // Note keep this sorted according to the list above (comments are the fieldnames)
+ aValues[ i++ ] >>= aResult.CharSet;
+ aValues[ i++ ] >>= aResult.Family;
+ aValues[ i++ ] >>= aResult.Name;
+ aValues[ i++ ] >>= aResult.Pitch;
+ aValues[ i++ ] >>= aResult.StyleName;
+ float fCharHeight = 0;
+ aValues[ i++ ] >>= fCharHeight;
+ aResult.Height = static_cast< sal_Int16 >( fCharHeight );
+ aValues[ i++ ] >>= aResult.Slant;
+ aValues[ i++ ] >>= aResult.Strikeout;
+ aValues[ i++ ] >>= aResult.Underline;
+ aValues[ i++ ] >>= aResult.Weight;
+ aValues[ i++ ] >>= aResult.WordLineMode;
+ OSL_ASSERT( i == aValues.getLength());
+
+ return aResult;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ChartModelHelper.cxx b/chart2/source/tools/ChartModelHelper.cxx
new file mode 100644
index 000000000..16e737b83
--- /dev/null
+++ b/chart2/source/tools/ChartModelHelper.cxx
@@ -0,0 +1,250 @@
+/* -*- 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 <ChartModelHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <DataSource.hxx>
+#include <DataSourceHelper.hxx>
+#include <ControllerLockGuard.hxx>
+#include <RangeHighlighter.hxx>
+#include <InternalDataProvider.hxx>
+#include <ChartModel.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <ChartType.hxx>
+#include <DataSeries.hxx>
+
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+uno::Reference< chart2::data::XRangeHighlighter > ChartModelHelper::createRangeHighlighter(
+ const rtl::Reference< ChartModel > & xSelectionSupplier )
+{
+ return new RangeHighlighter( xSelectionSupplier );
+}
+
+rtl::Reference< InternalDataProvider > ChartModelHelper::createInternalDataProvider(
+ const rtl::Reference<::chart::ChartModel>& xChartDoc, bool bConnectToModel )
+{
+ bool bDefaultDataInColumns(true);
+
+ // #i120559# Try to access the current state of "DataRowSource" for the
+ // chart data and use it as default for creating a new InternalDataProvider
+ if(xChartDoc.is())
+ {
+ // old XChartDocument interface
+ css::uno::Reference< css::chart::XChartDocument > xDoc(static_cast<cppu::OWeakObject*>(xChartDoc.get()), uno::UNO_QUERY);
+
+ if(xDoc.is())
+ {
+ css::uno::Reference< css::chart::XDiagram > aDiagram = xDoc->getDiagram();
+
+ if(aDiagram.is())
+ {
+ css::uno::Reference< css::beans::XPropertySet > xProp(aDiagram, uno::UNO_QUERY);
+
+ if(xProp.is())
+ {
+ css::chart::ChartDataRowSource aDataRowSource(css::chart::ChartDataRowSource_COLUMNS);
+
+ xProp->getPropertyValue( "DataRowSource" ) >>= aDataRowSource;
+
+ bDefaultDataInColumns = (aDataRowSource == css::chart::ChartDataRowSource_COLUMNS);
+ }
+ }
+ }
+ }
+
+ return new InternalDataProvider( xChartDoc, bConnectToModel, bDefaultDataInColumns );
+}
+
+rtl::Reference< Diagram > ChartModelHelper::findDiagram( const rtl::Reference<::chart::ChartModel>& xChartDoc )
+{
+ try
+ {
+ if( !xChartDoc )
+ return nullptr;
+ return xChartDoc->getFirstChartDiagram();
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return nullptr;
+}
+
+rtl::Reference< BaseCoordinateSystem > ChartModelHelper::getFirstCoordinateSystem( const rtl::Reference<::chart::ChartModel>& xModel )
+{
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xModel );
+ if( xDiagram.is() )
+ {
+ auto& rCooSysSeq( xDiagram->getBaseCoordinateSystems() );
+ if( !rCooSysSeq.empty() )
+ return rCooSysSeq[0];
+ }
+ return nullptr;
+}
+
+std::vector< rtl::Reference< DataSeries > > ChartModelHelper::getDataSeries(
+ const rtl::Reference<::chart::ChartModel> & xChartDoc )
+{
+ std::vector< rtl::Reference< DataSeries > > aResult;
+
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xChartDoc );
+ if( xDiagram.is())
+ aResult = DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+
+ return aResult;
+}
+
+rtl::Reference< ChartType > ChartModelHelper::getChartTypeOfSeries(
+ const rtl::Reference<::chart::ChartModel>& xModel
+ , const uno::Reference< XDataSeries >& xGivenDataSeries )
+{
+ return DiagramHelper::getChartTypeOfSeries( ChartModelHelper::findDiagram( xModel ), xGivenDataSeries );
+}
+
+rtl::Reference< ChartType > ChartModelHelper::getChartTypeOfSeries(
+ const rtl::Reference<::chart::ChartModel>& xModel
+ , const rtl::Reference< DataSeries >& xGivenDataSeries )
+{
+ return DiagramHelper::getChartTypeOfSeries( ChartModelHelper::findDiagram( xModel ), xGivenDataSeries );
+}
+
+awt::Size ChartModelHelper::getDefaultPageSize()
+{
+ return awt::Size( 16000, 9000 );
+}
+
+awt::Size ChartModelHelper::getPageSize( const rtl::Reference<::chart::ChartModel>& xModel )
+{
+ awt::Size aPageSize( ChartModelHelper::getDefaultPageSize() );
+ OSL_ENSURE(xModel.is(),"need xVisualObject for page size");
+ if( xModel.is() )
+ aPageSize = xModel->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
+ return aPageSize;
+}
+
+void ChartModelHelper::triggerRangeHighlighting( const rtl::Reference<::chart::ChartModel>& xModel )
+{
+ if( xModel.is() )
+ {
+ uno::Reference< view::XSelectionChangeListener > xSelectionChangeListener( xModel->getRangeHighlighter(), uno::UNO_QUERY );
+ //trigger selection of cell range
+ if( xSelectionChangeListener.is() )
+ {
+ lang::EventObject aEvent( xSelectionChangeListener );
+ xSelectionChangeListener->selectionChanged( aEvent );
+ }
+ }
+}
+
+bool ChartModelHelper::isIncludeHiddenCells( const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ bool bIncluded = true; // hidden cells are included by default.
+
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram(xChartModel) );
+ if (!xDiagram.is())
+ return bIncluded;
+
+ try
+ {
+ xDiagram->getPropertyValue("IncludeHiddenCells") >>= bIncluded;
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ }
+
+ return bIncluded;
+}
+
+bool ChartModelHelper::setIncludeHiddenCells( bool bIncludeHiddenCells, ChartModel& rModel )
+{
+ bool bChanged = false;
+ try
+ {
+ ControllerLockGuard aLockedControllers( rModel );
+
+ uno::Reference< beans::XPropertySet > xDiagramProperties( rModel.getFirstDiagram(), uno::UNO_QUERY );
+ if (xDiagramProperties.is())
+ {
+ bool bOldValue = bIncludeHiddenCells;
+ xDiagramProperties->getPropertyValue( "IncludeHiddenCells" ) >>= bOldValue;
+ if( bOldValue == bIncludeHiddenCells )
+ bChanged = true;
+
+ //set the property on all instances in all cases to get the different objects in sync!
+
+ uno::Any aNewValue(bIncludeHiddenCells);
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > xDataProviderProperties( rModel.getDataProvider(), uno::UNO_QUERY );
+ if( xDataProviderProperties.is() )
+ xDataProviderProperties->setPropertyValue("IncludeHiddenCells", aNewValue );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ //the property is optional!
+ }
+
+ try
+ {
+ rtl::Reference< DataSource > xUsedData = DataSourceHelper::getUsedData( rModel );
+ if( xUsedData.is() )
+ {
+ uno::Reference< beans::XPropertySet > xProp;
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aData( xUsedData->getDataSequences());
+ for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledData : aData )
+ {
+ xProp.set( uno::Reference< beans::XPropertySet >( labeledData->getValues(), uno::UNO_QUERY ) );
+ if(xProp.is())
+ xProp->setPropertyValue("IncludeHiddenCells", aNewValue );
+ xProp.set( uno::Reference< beans::XPropertySet >( labeledData->getLabel(), uno::UNO_QUERY ) );
+ if(xProp.is())
+ xProp->setPropertyValue("IncludeHiddenCells", aNewValue );
+ }
+ }
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ //the property is optional!
+ }
+
+ xDiagramProperties->setPropertyValue( "IncludeHiddenCells", aNewValue);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return bChanged;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ChartTypeHelper.cxx b/chart2/source/tools/ChartTypeHelper.cxx
new file mode 100644
index 000000000..7ea824d56
--- /dev/null
+++ b/chart2/source/tools/ChartTypeHelper.cxx
@@ -0,0 +1,724 @@
+/* -*- 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 <ChartTypeHelper.hxx>
+#include <ChartType.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <DiagramHelper.hxx>
+#include <servicenames_charttypes.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/XDataSeries.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+namespace chart
+{
+
+bool ChartTypeHelper::isSupportingAxisSideBySide(
+ const rtl::Reference< ::chart::ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ bool bResult = false;
+
+ if( xChartType.is() &&
+ nDimensionCount < 3 )
+ {
+ bool bFound=false;
+ bool bAmbiguous=false;
+ StackMode eStackMode = DiagramHelper::getStackModeFromChartType( xChartType, bFound, bAmbiguous, nullptr );
+ if( eStackMode == StackMode::NONE && !bAmbiguous )
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ bResult = ( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) );
+ }
+ }
+
+ return bResult;
+}
+
+bool ChartTypeHelper::isSupportingGeometryProperties( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //form tab only for 3D-bar and 3D-column charts.
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_BAR )
+ return true;
+ if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_COLUMN )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ChartTypeHelper::isSupportingStatisticProperties( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //3D charts, pie, net and stock do not support statistic properties
+
+ //@todo ask charttype itself (and series? --> stock chart?) --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ return false;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) ) //todo: BubbleChart support error bars and trend lines
+ return false;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingRegressionProperties( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ // note: old chart: only scatter chart
+ return isSupportingStatisticProperties( xChartType, nDimensionCount );
+}
+
+bool ChartTypeHelper::isSupportingAreaProperties( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //2D line charts, net and stock do not support area properties
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==2)
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingSymbolProperties( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //2D line charts, 2D scatter charts and 2D net charts do support symbols
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ return false;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE) )
+ return true;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
+ return true;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return true;
+ }
+ return false;
+}
+
+bool ChartTypeHelper::isSupportingMainAxis( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount, sal_Int32 nDimensionIndex )
+{
+ //pie charts do not support axis at all
+ //no 3rd axis for 2D charts
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+
+ if( nDimensionIndex == 2 )
+ return nDimensionCount == 3;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingSecondaryAxis( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //3D, pie and net charts do not support a secondary axis at all
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ return false;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ return false;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingOverlapAndGapWidthProperties(
+ const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //2D bar charts do support a this special properties
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ return false;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) )
+ return true;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) )
+ return true;
+ }
+ return false;
+}
+
+bool ChartTypeHelper::isSupportingBarConnectors(
+ const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ //2D bar charts with stacked series support this
+
+ //@todo ask charttype itself --> need model change first
+ if(xChartType.is())
+ {
+ if(nDimensionCount==3)
+ return false;
+
+ bool bFound=false;
+ bool bAmbiguous=false;
+ StackMode eStackMode = DiagramHelper::getStackModeFromChartType( xChartType, bFound, bAmbiguous, nullptr );
+ if( eStackMode != StackMode::YStacked || bAmbiguous )
+ return false;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) )
+ return true;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) )
+ return true; // note: old chart was false here
+ }
+ return false;
+}
+
+uno::Sequence < sal_Int32 > ChartTypeHelper::getSupportedLabelPlacements( const rtl::Reference< ChartType >& xChartType
+ , bool bSwapXAndY
+ , const uno::Reference< chart2::XDataSeries >& xSeries )
+{
+ uno::Sequence < sal_Int32 > aRet;
+ if( !xChartType.is() )
+ return aRet;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ {
+ bool bDonut = false;
+ xChartType->getPropertyValue( "UseRings") >>= bDonut;
+
+ if(!bDonut)
+ {
+ aRet.realloc(5);
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::AVOID_OVERLAP;
+ *pSeq++ = css::chart::DataLabelPlacement::OUTSIDE;
+ *pSeq++ = css::chart::DataLabelPlacement::INSIDE;
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ *pSeq++ = css::chart::DataLabelPlacement::CUSTOM;
+ }
+ else
+ {
+ aRet.realloc(1);
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ }
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE)
+ )
+ {
+ aRet.realloc(5);
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::TOP;
+ *pSeq++ = css::chart::DataLabelPlacement::BOTTOM;
+ *pSeq++ = css::chart::DataLabelPlacement::LEFT;
+ *pSeq++ = css::chart::DataLabelPlacement::RIGHT;
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) )
+ {
+
+ bool bStacked = false;
+ {
+ uno::Reference< beans::XPropertySet > xSeriesProp( xSeries, uno::UNO_QUERY );
+ chart2::StackingDirection eStacking = chart2::StackingDirection_NO_STACKING;
+ xSeriesProp->getPropertyValue( "StackingDirection" ) >>= eStacking;
+ bStacked = (eStacking == chart2::StackingDirection_Y_STACKING);
+ }
+
+ aRet.realloc( bStacked ? 3 : 6 );
+ sal_Int32* pSeq = aRet.getArray();
+ if(!bStacked)
+ {
+ if(bSwapXAndY)
+ {
+ *pSeq++ = css::chart::DataLabelPlacement::RIGHT;
+ *pSeq++ = css::chart::DataLabelPlacement::LEFT;
+ }
+ else
+ {
+ *pSeq++ = css::chart::DataLabelPlacement::TOP;
+ *pSeq++ = css::chart::DataLabelPlacement::BOTTOM;
+ }
+ }
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ if(!bStacked)
+ *pSeq++ = css::chart::DataLabelPlacement::OUTSIDE;
+ *pSeq++ = css::chart::DataLabelPlacement::INSIDE;
+ *pSeq++ = css::chart::DataLabelPlacement::NEAR_ORIGIN;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_AREA) )
+ {
+ bool bStacked = false;
+ {
+ uno::Reference<beans::XPropertySet> xSeriesProp(xSeries, uno::UNO_QUERY);
+ chart2::StackingDirection eStacking = chart2::StackingDirection_NO_STACKING;
+ xSeriesProp->getPropertyValue("StackingDirection") >>= eStacking;
+ bStacked = (eStacking == chart2::StackingDirection_Y_STACKING);
+ }
+
+ aRet.realloc(2);
+ sal_Int32* pSeq = aRet.getArray();
+ if (bStacked)
+ {
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ *pSeq++ = css::chart::DataLabelPlacement::TOP;
+ }
+ else
+ {
+ *pSeq++ = css::chart::DataLabelPlacement::TOP;
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ }
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ {
+ aRet.realloc(6);
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::OUTSIDE;
+ *pSeq++ = css::chart::DataLabelPlacement::TOP;
+ *pSeq++ = css::chart::DataLabelPlacement::BOTTOM;
+ *pSeq++ = css::chart::DataLabelPlacement::LEFT;
+ *pSeq++ = css::chart::DataLabelPlacement::RIGHT;
+ *pSeq++ = css::chart::DataLabelPlacement::CENTER;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ {
+ aRet.realloc(1);
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::OUTSIDE;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ {
+ aRet.realloc( 1 );
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::DataLabelPlacement::OUTSIDE;
+ }
+ else
+ {
+ OSL_FAIL( "unknown charttype" );
+ }
+
+ return aRet;
+}
+
+bool ChartTypeHelper::isSupportingRightAngledAxes( const rtl::Reference< ChartType >& xChartType )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingStartingAngle( const rtl::Reference< ChartType >& xChartType )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return true;
+ }
+ return false;
+}
+bool ChartTypeHelper::isSupportingBaseValue( const rtl::Reference< ChartType >& xChartType )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_AREA)
+ )
+ return true;
+ }
+ return false;
+}
+
+bool ChartTypeHelper::isSupportingAxisPositioning( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount, sal_Int32 nDimensionIndex )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ return false;
+ }
+ if( nDimensionCount==3 )
+ return nDimensionIndex<2;
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingDateAxis( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionIndex )
+{
+ if( nDimensionIndex!=0 )
+ return false;
+ if( xChartType.is() )
+ {
+ sal_Int32 nType = ChartTypeHelper::getAxisType( xChartType, nDimensionIndex );
+ if( nType != AxisType::CATEGORY )
+ return false;
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ return false;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingComplexCategory( const rtl::Reference< ChartType >& xChartType )
+{
+ if( xChartType.is() )
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ }
+ return true;
+}
+
+bool ChartTypeHelper::isSupportingCategoryPositioning( const rtl::Reference< ChartType >& xChartType, sal_Int32 nDimensionCount )
+{
+ if( xChartType.is() )
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_AREA) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK))
+ return true;
+ else if (nDimensionCount == 2 &&
+ (aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR)))
+ return true;
+ }
+ return false;
+}
+
+bool ChartTypeHelper::shiftCategoryPosAtXAxisPerDefault( const rtl::Reference< ChartType >& xChartType )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ return true;
+ }
+ return false;
+}
+
+bool ChartTypeHelper::noBordersForSimpleScheme( const rtl::Reference< ChartType >& xChartType )
+{
+ if(xChartType.is())
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 ChartTypeHelper::getDefaultDirectLightColor( bool bSimple, const rtl::Reference< ChartType >& xChartType )
+{
+ sal_Int32 nRet = static_cast< sal_Int32 >( 0x808080 ); // grey
+ if( xChartType .is() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ {
+ if( bSimple )
+ nRet = static_cast< sal_Int32 >( 0x333333 ); // grey80
+ else
+ nRet = static_cast< sal_Int32 >( 0xb3b3b3 ); // grey30
+ }
+ else if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_LINE
+ || aChartType == CHART2_SERVICE_NAME_CHARTTYPE_SCATTER )
+ nRet = static_cast< sal_Int32 >( 0x666666 ); // grey60
+ }
+ return nRet;
+}
+
+sal_Int32 ChartTypeHelper::getDefaultAmbientLightColor( bool bSimple, const rtl::Reference< ChartType >& xChartType )
+{
+ sal_Int32 nRet = static_cast< sal_Int32 >( 0x999999 ); // grey40
+ if( xChartType .is() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ {
+ if( bSimple )
+ nRet = static_cast< sal_Int32 >( 0xcccccc ); // grey20
+ else
+ nRet = static_cast< sal_Int32 >( 0x666666 ); // grey60
+ }
+ }
+ return nRet;
+}
+
+drawing::Direction3D ChartTypeHelper::getDefaultSimpleLightDirection( const rtl::Reference< ChartType >& xChartType )
+{
+ drawing::Direction3D aRet(0.0, 0.0, 1.0);
+ if( xChartType .is() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ aRet = drawing::Direction3D(0.0, 0.8, 0.5);
+ else if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_LINE
+ || aChartType == CHART2_SERVICE_NAME_CHARTTYPE_SCATTER )
+ aRet = drawing::Direction3D(0.9, 0.5, 0.05);
+ }
+ return aRet;
+}
+
+drawing::Direction3D ChartTypeHelper::getDefaultRealisticLightDirection( const rtl::Reference< ChartType >& xChartType )
+{
+ drawing::Direction3D aRet(0.0, 0.0, 1.0);
+ if( xChartType .is() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ aRet = drawing::Direction3D(0.6, 0.6, 0.6);
+ else if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_LINE
+ || aChartType == CHART2_SERVICE_NAME_CHARTTYPE_SCATTER )
+ aRet = drawing::Direction3D(0.9, 0.5, 0.05);
+ }
+ return aRet;
+}
+
+sal_Int32 ChartTypeHelper::getAxisType( const rtl::Reference<
+ ChartType >& xChartType, sal_Int32 nDimensionIndex )
+{
+ //returned is a constant from constant group css::chart2::AxisType
+
+ //@todo ask charttype itself --> need model change first
+ if(!xChartType.is())
+ return AxisType::CATEGORY;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if(nDimensionIndex==2)//z-axis
+ return AxisType::SERIES;
+ if(nDimensionIndex==1)//y-axis
+ return AxisType::REALNUMBER;
+ if(nDimensionIndex==0)//x-axis
+ {
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
+ return AxisType::REALNUMBER;
+ return AxisType::CATEGORY;
+ }
+ return AxisType::CATEGORY;
+}
+
+sal_Int32 ChartTypeHelper::getNumberOfDisplayedSeries(
+ const rtl::Reference< ChartType >& xChartType,
+ sal_Int32 nNumberOfSeries )
+{
+ if( xChartType.is() )
+ {
+ try
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ {
+ bool bDonut = false;
+ if( (xChartType->getPropertyValue( "UseRings") >>= bDonut)
+ && !bDonut )
+ {
+ return nNumberOfSeries>0 ? 1 : 0;
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ return nNumberOfSeries;
+}
+
+uno::Sequence < sal_Int32 > ChartTypeHelper::getSupportedMissingValueTreatments( const rtl::Reference< ChartType >& xChartType )
+{
+ uno::Sequence < sal_Int32 > aRet;
+ if( !xChartType.is() )
+ return aRet;
+
+ bool bFound=false;
+ bool bAmbiguous=false;
+ StackMode eStackMode = DiagramHelper::getStackModeFromChartType( xChartType, bFound, bAmbiguous, nullptr );
+ bool bStacked = bFound && (eStackMode == StackMode::YStacked);
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_COLUMN) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BAR) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
+ {
+ aRet.realloc( 2 );
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::MissingValueTreatment::LEAVE_GAP;
+ *pSeq++ = css::chart::MissingValueTreatment::USE_ZERO;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_AREA) )
+ {
+ aRet.realloc( bStacked ? 1 : 2 );
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::MissingValueTreatment::USE_ZERO;
+ if( !bStacked )
+ *pSeq++ = css::chart::MissingValueTreatment::CONTINUE;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_NET) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ {
+ aRet.realloc( bStacked ? 2 : 3 );
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::MissingValueTreatment::LEAVE_GAP;
+ *pSeq++ = css::chart::MissingValueTreatment::USE_ZERO;
+ if( !bStacked )
+ *pSeq++ = css::chart::MissingValueTreatment::CONTINUE;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) )
+ {
+ aRet.realloc( 3 );
+ sal_Int32* pSeq = aRet.getArray();
+ *pSeq++ = css::chart::MissingValueTreatment::CONTINUE;
+ *pSeq++ = css::chart::MissingValueTreatment::LEAVE_GAP;
+ *pSeq++ = css::chart::MissingValueTreatment::USE_ZERO;
+ }
+ else if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ {
+ aRet.realloc( 0 );
+ }
+ else
+ {
+ OSL_FAIL( "unknown charttype" );
+ }
+
+ return aRet;
+}
+
+bool ChartTypeHelper::isSeriesInFrontOfAxisLine( const rtl::Reference< ChartType >& xChartType )
+{
+ if( xChartType.is() )
+ {
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match( CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET ) )
+ return false;
+ }
+ return true;
+}
+
+OUString ChartTypeHelper::getRoleOfSequenceForYAxisNumberFormatDetection( const rtl::Reference< ChartType >& xChartType )
+{
+ OUString aRet( "values-y" );
+ if( !xChartType.is() )
+ return aRet;
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ aRet = xChartType->getRoleOfSequenceForSeriesLabel();
+ return aRet;
+}
+
+OUString ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( const rtl::Reference< ChartType >& xChartType )
+{
+ OUString aRet( "values-y" );
+ if( !xChartType.is() )
+ return aRet;
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK)
+ || aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) )
+ aRet = xChartType->getRoleOfSequenceForSeriesLabel();
+ return aRet;
+}
+
+bool ChartTypeHelper::isSupportingOnlyDeepStackingFor3D( const rtl::Reference< ChartType >& xChartType )
+{
+ bool bRet = false;
+ if( !xChartType.is() )
+ return bRet;
+
+ OUString aChartTypeName = xChartType->getChartType();
+ if( aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_LINE) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) ||
+ aChartTypeName.match(CHART2_SERVICE_NAME_CHARTTYPE_AREA) )
+ {
+ bRet = true;
+ }
+ return bRet;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ChartViewHelper.cxx b/chart2/source/tools/ChartViewHelper.cxx
new file mode 100644
index 000000000..8caaeddb1
--- /dev/null
+++ b/chart2/source/tools/ChartViewHelper.cxx
@@ -0,0 +1,56 @@
+/* -*- 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 <ChartViewHelper.hxx>
+#include <servicenames.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+void ChartViewHelper::setViewToDirtyState(const uno::Reference<frame::XModel>& xChartModel)
+{
+ try
+ {
+ uno::Reference<lang::XMultiServiceFactory> xFact(xChartModel, uno::UNO_QUERY);
+ if (xFact.is())
+ {
+ Reference<util::XModifyListener> xModifyListener(
+ xFact->createInstance(CHART_VIEW_SERVICE_NAME), uno::UNO_QUERY);
+ if (xModifyListener.is())
+ {
+ lang::EventObject aEvent(xChartModel);
+ xModifyListener->modified(aEvent);
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ColorPerPointHelper.cxx b/chart2/source/tools/ColorPerPointHelper.cxx
new file mode 100644
index 000000000..34aeb7866
--- /dev/null
+++ b/chart2/source/tools/ColorPerPointHelper.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 <ColorPerPointHelper.hxx>
+#include <com/sun/star/chart2/XDataSeries.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+
+#include <algorithm>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+bool ColorPerPointHelper::hasPointOwnColor(
+ const css::uno::Reference< css::beans::XPropertySet >& xDataSeriesProperties
+ , sal_Int32 nPointIndex
+ , const css::uno::Reference< css::beans::XPropertySet >& xDataPointProperties //may be NULL this is just for performance
+ )
+{
+ if( !xDataSeriesProperties.is() )
+ return false;
+
+ if( hasPointOwnProperties( xDataSeriesProperties, nPointIndex ))
+ {
+ uno::Reference< beans::XPropertyState > xPointState( xDataPointProperties, uno::UNO_QUERY );
+ if( !xPointState.is() )
+ {
+ uno::Reference< XDataSeries > xSeries( xDataSeriesProperties, uno::UNO_QUERY );
+ if(xSeries.is())
+ xPointState.set( xSeries->getDataPointByIndex( nPointIndex ), uno::UNO_QUERY );
+ }
+ if( !xPointState.is() )
+ return false;
+
+ return (xPointState->getPropertyState( "Color") != beans::PropertyState_DEFAULT_VALUE );
+ }
+
+ return false;
+}
+
+bool ColorPerPointHelper::hasPointOwnProperties(
+ const css::uno::Reference< css::beans::XPropertySet >& xSeriesProperties
+ , sal_Int32 nPointIndex )
+{
+ if( xSeriesProperties.is() )
+ {
+ uno::Sequence< sal_Int32 > aIndexList;
+ if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aIndexList )
+ {
+ const sal_Int32 * pBegIt = aIndexList.getConstArray();
+ const sal_Int32 * pEndIt = pBegIt + aIndexList.getLength();
+ return ( std::find( pBegIt, pEndIt, nPointIndex ) != pEndIt );
+ }
+ }
+
+ return false;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/CommonConverters.cxx b/chart2/source/tools/CommonConverters.cxx
new file mode 100644
index 000000000..6e47b5483
--- /dev/null
+++ b/chart2/source/tools/CommonConverters.cxx
@@ -0,0 +1,600 @@
+/* -*- 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 <CommonConverters.hxx>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/chart2/data/XDataSequence.hpp>
+#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
+#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <cstddef>
+#include <limits>
+
+namespace chart
+{
+
+using namespace ::com::sun::star;
+
+// diverse methods for class conversions; e.g. ::basegfx::B3DHomMatrix to HomogenMatrix
+
+drawing::HomogenMatrix B3DHomMatrixToHomogenMatrix( const ::basegfx::B3DHomMatrix& rM )
+{
+ drawing::HomogenMatrix aHM;
+ aHM.Line1.Column1 = rM.get(0, 0);
+ aHM.Line1.Column2 = rM.get(0, 1);
+ aHM.Line1.Column3 = rM.get(0, 2);
+ aHM.Line1.Column4 = rM.get(0, 3);
+ aHM.Line2.Column1 = rM.get(1, 0);
+ aHM.Line2.Column2 = rM.get(1, 1);
+ aHM.Line2.Column3 = rM.get(1, 2);
+ aHM.Line2.Column4 = rM.get(1, 3);
+ aHM.Line3.Column1 = rM.get(2, 0);
+ aHM.Line3.Column2 = rM.get(2, 1);
+ aHM.Line3.Column3 = rM.get(2, 2);
+ aHM.Line3.Column4 = rM.get(2, 3);
+ aHM.Line4.Column1 = rM.get(3, 0);
+ aHM.Line4.Column2 = rM.get(3, 1);
+ aHM.Line4.Column3 = rM.get(3, 2);
+ aHM.Line4.Column4 = rM.get(3, 3);
+ return aHM;
+}
+
+::basegfx::B3DHomMatrix HomogenMatrixToB3DHomMatrix( const drawing::HomogenMatrix& rHM )
+{
+ ::basegfx::B3DHomMatrix aM;
+ aM.set(0, 0, rHM.Line1.Column1);
+ aM.set(0, 1, rHM.Line1.Column2);
+ aM.set(0, 2, rHM.Line1.Column3);
+ aM.set(0, 3, rHM.Line1.Column4);
+ aM.set(1, 0, rHM.Line2.Column1);
+ aM.set(1, 1, rHM.Line2.Column2);
+ aM.set(1, 2, rHM.Line2.Column3);
+ aM.set(1, 3, rHM.Line2.Column4);
+ aM.set(2, 0, rHM.Line3.Column1);
+ aM.set(2, 1, rHM.Line3.Column2);
+ aM.set(2, 2, rHM.Line3.Column3);
+ aM.set(2, 3, rHM.Line3.Column4);
+ aM.set(3, 0, rHM.Line4.Column1);
+ aM.set(3, 1, rHM.Line4.Column2);
+ aM.set(3, 2, rHM.Line4.Column3);
+ aM.set(3, 3, rHM.Line4.Column4);
+ return aM;
+}
+
+::basegfx::B2DHomMatrix IgnoreZ( const ::basegfx::B3DHomMatrix& rM )
+{
+ ::basegfx::B2DHomMatrix aM;
+ aM.set(0, 0, rM.get(0, 0));
+ aM.set(0, 1, rM.get(0, 1));
+ aM.set(0, 2, rM.get(0, 3));
+ aM.set(1, 0, rM.get(1, 0));
+ aM.set(1, 1, rM.get(1, 1));
+ aM.set(1, 2, rM.get(1, 3));
+ aM.set(2, 0, rM.get(3, 0));
+ aM.set(2, 1, rM.get(3, 1));
+ aM.set(2, 2, rM.get(3, 3));
+ return aM;
+}
+
+drawing::HomogenMatrix3 B2DHomMatrixToHomogenMatrix3( const ::basegfx::B2DHomMatrix& rM )
+{
+ drawing::HomogenMatrix3 aHM;
+ aHM.Line1.Column1 = rM.get(0, 0);
+ aHM.Line1.Column2 = rM.get(0, 1);
+ aHM.Line1.Column3 = rM.get(0, 2);
+ aHM.Line2.Column1 = rM.get(1, 0);
+ aHM.Line2.Column2 = rM.get(1, 1);
+ aHM.Line2.Column3 = rM.get(1, 2);
+ aHM.Line3.Column1 = rM.get(2, 0);
+ aHM.Line3.Column2 = rM.get(2, 1);
+ aHM.Line3.Column3 = rM.get(2, 2);
+ return aHM;
+}
+
+::basegfx::B3DPoint Position3DToB3DPoint( const drawing::Position3D& rPosition )
+{
+ return ::basegfx::B3DPoint(
+ rPosition.PositionX ,
+ rPosition.PositionY ,
+ rPosition.PositionZ );
+}
+
+drawing::Direction3D B3DVectorToDirection3D( const ::basegfx::B3DVector& rVector)
+{
+ return drawing::Direction3D(
+ rVector.getX()
+ , rVector.getY()
+ , rVector.getZ()
+ );
+}
+
+drawing::Position3D B3DPointToPosition3D( const ::basegfx::B3DPoint& rPoint)
+{
+ return drawing::Position3D(
+ rPoint.getX()
+ , rPoint.getY()
+ , rPoint.getZ()
+ );
+}
+
+::basegfx::B3DVector Direction3DToB3DVector( const drawing::Direction3D& rDirection)
+{
+ return ::basegfx::B3DVector(
+ rDirection.DirectionX
+ , rDirection.DirectionY
+ , rDirection.DirectionZ
+ );
+}
+
+void AddPointToPoly( drawing::PolyPolygonShape3D& rPoly, const drawing::Position3D& rPos, sal_Int32 nPolygonIndex )
+{
+ if(nPolygonIndex<0)
+ {
+ OSL_FAIL( "The polygon index needs to be > 0");
+ nPolygonIndex=0;
+ }
+
+ //make sure that we have enough polygons
+ if(nPolygonIndex >= rPoly.SequenceX.getLength() )
+ {
+ rPoly.SequenceX.realloc(nPolygonIndex+1);
+ rPoly.SequenceY.realloc(nPolygonIndex+1);
+ rPoly.SequenceZ.realloc(nPolygonIndex+1);
+ }
+
+ drawing::DoubleSequence* pOuterSequenceX = &rPoly.SequenceX.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceY = &rPoly.SequenceY.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceZ = &rPoly.SequenceZ.getArray()[nPolygonIndex];
+
+ sal_Int32 nOldPointCount = pOuterSequenceX->getLength();
+
+ pOuterSequenceX->realloc(nOldPointCount+1);
+ pOuterSequenceY->realloc(nOldPointCount+1);
+ pOuterSequenceZ->realloc(nOldPointCount+1);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ pInnerSequenceX[nOldPointCount] = rPos.PositionX;
+ pInnerSequenceY[nOldPointCount] = rPos.PositionY;
+ pInnerSequenceZ[nOldPointCount] = rPos.PositionZ;
+}
+
+void AddPointToPoly( std::vector<std::vector<css::drawing::Position3D>>& rPoly, const drawing::Position3D& rPos, sal_Int32 nPolygonIndex )
+{
+ if(nPolygonIndex<0)
+ {
+ OSL_FAIL( "The polygon index needs to be > 0");
+ nPolygonIndex=0;
+ }
+
+ //make sure that we have enough polygons
+ if(o3tl::make_unsigned(nPolygonIndex) >= rPoly.size() )
+ {
+ rPoly.resize(nPolygonIndex+1);
+ }
+
+ std::vector<css::drawing::Position3D>* pOuterSequence = &rPoly[nPolygonIndex];
+ pOuterSequence->push_back(rPos);
+}
+
+drawing::Position3D getPointFromPoly( const drawing::PolyPolygonShape3D& rPolygon, sal_Int32 nPointIndex, sal_Int32 nPolyIndex )
+{
+ drawing::Position3D aRet(0.0,0.0,0.0);
+
+ if( nPolyIndex>=0 && nPolyIndex<rPolygon.SequenceX.getLength())
+ {
+ if(nPointIndex<rPolygon.SequenceX[nPolyIndex].getLength())
+ {
+ aRet.PositionX = rPolygon.SequenceX[nPolyIndex][nPointIndex];
+ aRet.PositionY = rPolygon.SequenceY[nPolyIndex][nPointIndex];
+ aRet.PositionZ = rPolygon.SequenceZ[nPolyIndex][nPointIndex];
+ }
+ else
+ {
+ OSL_FAIL("polygon was accessed with a wrong index");
+ }
+ }
+ else
+ {
+ OSL_FAIL("polygon was accessed with a wrong index");
+ }
+ return aRet;
+}
+
+drawing::Position3D getPointFromPoly( const std::vector<std::vector<css::drawing::Position3D>>& rPolygon, sal_Int32 nPointIndex, sal_Int32 nPolyIndex )
+{
+ drawing::Position3D aRet(0.0,0.0,0.0);
+
+ if( nPolyIndex>=0 && o3tl::make_unsigned(nPolyIndex)<rPolygon.size())
+ {
+ if(nPointIndex<static_cast<sal_Int32>(rPolygon[nPolyIndex].size()))
+ {
+ aRet = rPolygon[nPolyIndex][nPointIndex];
+ }
+ else
+ {
+ OSL_FAIL("polygon was accessed with a wrong index");
+ }
+ }
+ else
+ {
+ OSL_FAIL("polygon was accessed with a wrong index");
+ }
+ return aRet;
+}
+
+void addPolygon( std::vector<std::vector<css::drawing::Position3D>>& rRet, const std::vector<std::vector<css::drawing::Position3D>>& rAdd )
+{
+ sal_Int32 nAddOuterCount = rAdd.size();
+ sal_Int32 nOuterCount = rRet.size() + nAddOuterCount;
+ rRet.resize( nOuterCount );
+ auto pSequence = rRet.data();
+
+ sal_Int32 nIndex = 0;
+ sal_Int32 nOuter = nOuterCount - nAddOuterCount;
+ for( ; nOuter < nOuterCount; nOuter++ )
+ {
+ if( nIndex >= nAddOuterCount )
+ break;
+
+ pSequence[nOuter] = rAdd[nIndex];
+
+ nIndex++;
+ }
+}
+
+void appendPoly( std::vector<std::vector<css::drawing::Position3D>>& rRet, const std::vector<std::vector<css::drawing::Position3D>>& rAdd )
+{
+ std::size_t nOuterCount = std::max( rRet.size(), rAdd.size() );
+ rRet.resize(nOuterCount);
+ auto pSequence = rRet.data();
+
+ for( std::size_t nOuter=0;nOuter<nOuterCount;nOuter++ )
+ {
+ sal_Int32 nOldPointCount = rRet[nOuter].size();
+ sal_Int32 nAddPointCount = 0;
+ if(nOuter<rAdd.size())
+ nAddPointCount = rAdd[nOuter].size();
+ if(!nAddPointCount)
+ continue;
+
+ sal_Int32 nNewPointCount = nOldPointCount + nAddPointCount;
+
+ pSequence[nOuter].resize(nNewPointCount);
+ auto pSequence_nOuter = pSequence[nOuter].data();
+
+ sal_Int32 nPointTarget=nOldPointCount;
+ sal_Int32 nPointSource=nAddPointCount;
+ for( ; nPointSource-- ; nPointTarget++ )
+ {
+ pSequence_nOuter[nPointTarget] = rAdd[nOuter][nPointSource];
+ }
+ }
+}
+
+drawing::PolyPolygonShape3D BezierToPoly(
+ const drawing::PolyPolygonBezierCoords& rBezier )
+{
+ const drawing::PointSequenceSequence& rPointSequence = rBezier.Coordinates;
+
+ drawing::PolyPolygonShape3D aRet;
+ aRet.SequenceX.realloc( rPointSequence.getLength() );
+ auto pSequenceX = aRet.SequenceX.getArray();
+ aRet.SequenceY.realloc( rPointSequence.getLength() );
+ auto pSequenceY = aRet.SequenceY.getArray();
+ aRet.SequenceZ.realloc( rPointSequence.getLength() );
+ auto pSequenceZ = aRet.SequenceZ.getArray();
+
+ sal_Int32 nRealOuter = 0;
+ for(sal_Int32 nN = 0; nN < rPointSequence.getLength(); nN++)
+ {
+ sal_Int32 nInnerLength = rPointSequence[nN].getLength();
+ pSequenceX[nRealOuter].realloc( nInnerLength );
+ auto pSequenceX_nRealOuter = pSequenceX[nRealOuter].getArray();
+ pSequenceY[nRealOuter].realloc( nInnerLength );
+ auto pSequenceY_nRealOuter = pSequenceY[nRealOuter].getArray();
+ pSequenceZ[nRealOuter].realloc( nInnerLength );
+ auto pSequenceZ_nRealOuter = pSequenceZ[nRealOuter].getArray();
+
+ bool bHasOuterFlags = nN < rBezier.Flags.getLength();
+
+ sal_Int32 nRealInner = 0;
+ for( sal_Int32 nM = 0; nM < nInnerLength; nM++)
+ {
+ bool bHasInnerFlags = bHasOuterFlags && (nM < rBezier.Flags[nN].getLength());
+
+ if( !bHasInnerFlags || (rBezier.Flags[nN][nM] == drawing::PolygonFlags_NORMAL) )
+ {
+ pSequenceX_nRealOuter[nRealInner] = rPointSequence[nN][nM].X;
+ pSequenceY_nRealOuter[nRealInner] = rPointSequence[nN][nM].Y;
+ pSequenceZ_nRealOuter[nRealInner] = 0.0;
+ nRealInner++;
+ }
+ }
+
+ pSequenceX[nRealOuter].realloc( nRealInner );
+ pSequenceY[nRealOuter].realloc( nRealInner );
+ pSequenceZ[nRealOuter].realloc( nRealInner );
+
+ if( nRealInner>0 )
+ nRealOuter++;
+ }
+
+ aRet.SequenceX.realloc( nRealOuter );
+ aRet.SequenceY.realloc( nRealOuter );
+ aRet.SequenceZ.realloc( nRealOuter );
+
+ return aRet;
+}
+
+drawing::PointSequenceSequence PolyToPointSequence(
+ const drawing::PolyPolygonShape3D& rPolyPolygon )
+{
+ drawing::PointSequenceSequence aRet;
+ aRet.realloc( rPolyPolygon.SequenceX.getLength() );
+ auto pRet = aRet.getArray();
+
+ for(sal_Int32 nN = 0; nN < rPolyPolygon.SequenceX.getLength(); nN++)
+ {
+ sal_Int32 nInnerLength = rPolyPolygon.SequenceX[nN].getLength();
+ pRet[nN].realloc( nInnerLength );
+ auto pRet_nN = pRet[nN].getArray();
+ for( sal_Int32 nM = 0; nM < nInnerLength; nM++)
+ {
+ pRet_nN[nM].X = static_cast<sal_Int32>(rPolyPolygon.SequenceX[nN][nM]);
+ pRet_nN[nM].Y = static_cast<sal_Int32>(rPolyPolygon.SequenceY[nN][nM]);
+ }
+ }
+ return aRet;
+}
+
+drawing::PointSequenceSequence PolyToPointSequence(
+ const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon )
+{
+ drawing::PointSequenceSequence aRet;
+ aRet.realloc( rPolyPolygon.size() );
+ auto pRet = aRet.getArray();
+
+ for(std::size_t nN = 0; nN < rPolyPolygon.size(); nN++)
+ {
+ sal_Int32 nInnerLength = rPolyPolygon[nN].size();
+ pRet[nN].realloc( nInnerLength );
+ auto pRet_nN = pRet[nN].getArray();
+ for( sal_Int32 nM = 0; nM < nInnerLength; nM++)
+ {
+ pRet_nN[nM].X = static_cast<sal_Int32>(rPolyPolygon[nN][nM].PositionX);
+ pRet_nN[nM].Y = static_cast<sal_Int32>(rPolyPolygon[nN][nM].PositionY);
+ }
+ }
+ return aRet;
+}
+
+basegfx::B2DPolyPolygon PolyToB2DPolyPolygon(
+ const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon )
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ for(auto const & nN: rPolyPolygon)
+ {
+ basegfx::B2DPolygon aNewPolygon;
+ sal_Int32 nInnerLength = nN.size();
+ if(nInnerLength)
+ {
+ aNewPolygon.reserve(nInnerLength);
+ for( sal_Int32 nM = 0; nM < nInnerLength; nM++)
+ {
+ auto X = static_cast<sal_Int32>(nN[nM].PositionX);
+ auto Y = static_cast<sal_Int32>(nN[nM].PositionY);
+ aNewPolygon.append(basegfx::B2DPoint(X, Y));
+ }
+ // check for closed state flag
+ basegfx::utils::checkClosed(aNewPolygon);
+ }
+ aRetval.append(std::move(aNewPolygon));
+ }
+
+ return aRetval;
+}
+
+void appendPointSequence( drawing::PointSequenceSequence& rTarget
+ , const drawing::PointSequenceSequence& rAdd )
+{
+ sal_Int32 nAddCount = rAdd.getLength();
+ if(!nAddCount)
+ return;
+ sal_Int32 nOldCount = rTarget.getLength();
+
+ rTarget.realloc(nOldCount+nAddCount);
+ auto pTarget = rTarget.getArray();
+ for(sal_Int32 nS=0; nS<nAddCount; nS++ )
+ pTarget[nOldCount+nS]=rAdd[nS];
+}
+
+drawing::Position3D operator+( const drawing::Position3D& rPos
+ , const drawing::Direction3D& rDirection)
+{
+ return drawing::Position3D(
+ rPos.PositionX + rDirection.DirectionX
+ , rPos.PositionY + rDirection.DirectionY
+ , rPos.PositionZ + rDirection.DirectionZ
+ );
+}
+
+drawing::Direction3D operator-( const drawing::Position3D& rPos1
+ , const drawing::Position3D& rPos2)
+{
+ return drawing::Direction3D(
+ rPos1.PositionX - rPos2.PositionX
+ , rPos1.PositionY - rPos2.PositionY
+ , rPos1.PositionZ - rPos2.PositionZ
+ );
+}
+
+awt::Point Position3DToAWTPoint( const drawing::Position3D& rPos )
+{
+ awt::Point aRet;
+ aRet.X = static_cast<sal_Int32>(rPos.PositionX);
+ aRet.Y = static_cast<sal_Int32>(rPos.PositionY);
+ return aRet;
+}
+
+awt::Point ToPoint( const awt::Rectangle& rRectangle )
+{
+ return awt::Point( rRectangle.X, rRectangle.Y );
+}
+
+awt::Size ToSize( const awt::Rectangle& rRectangle )
+{
+ return awt::Size( rRectangle.Width, rRectangle.Height );
+}
+
+awt::Size Direction3DToAWTSize( const drawing::Direction3D& rDirection )
+{
+ awt::Size aRet;
+ aRet.Width = static_cast<sal_Int32>(rDirection.DirectionX);
+ aRet.Height = static_cast<sal_Int32>(rDirection.DirectionY);
+ return aRet;
+}
+
+drawing::Position3D SequenceToPosition3D( const uno::Sequence< double >& rSeq )
+{
+ OSL_ENSURE(rSeq.getLength()==3,"The sequence needs to have length 3 for conversion into vector");
+
+ drawing::Position3D aRet;
+ aRet.PositionX = rSeq.getLength()>0?rSeq[0]:0.0;
+ aRet.PositionY = rSeq.getLength()>1?rSeq[1]:0.0;
+ aRet.PositionZ = rSeq.getLength()>2?rSeq[2]:0.0;
+ return aRet;
+}
+
+using namespace ::com::sun::star::chart2;
+
+uno::Sequence< double > DataSequenceToDoubleSequence(
+ const uno::Reference< data::XDataSequence >& xDataSequence )
+{
+ uno::Sequence< double > aResult;
+ OSL_ASSERT( xDataSequence.is());
+ if(!xDataSequence.is())
+ return aResult;
+
+ uno::Reference< data::XNumericalDataSequence > xNumericalDataSequence( xDataSequence, uno::UNO_QUERY );
+ if( xNumericalDataSequence.is() )
+ {
+ aResult = xNumericalDataSequence->getNumericalData();
+ }
+ else
+ {
+ uno::Sequence< uno::Any > aValues = xDataSequence->getData();
+ aResult.realloc(aValues.getLength());
+ auto pResult = aResult.getArray();
+ for(sal_Int32 nN=aValues.getLength();nN--;)
+ {
+ if( !(aValues[nN] >>= pResult[nN]) )
+ pResult[nN] = std::numeric_limits<double>::quiet_NaN();
+ }
+ }
+
+ return aResult;
+}
+
+uno::Sequence< OUString > DataSequenceToStringSequence(
+ const uno::Reference< data::XDataSequence >& xDataSequence )
+{
+ uno::Sequence< OUString > aResult;
+ if(!xDataSequence.is())
+ return aResult;
+
+ uno::Reference< data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
+ if( xTextualDataSequence.is() )
+ {
+ aResult = xTextualDataSequence->getTextualData();
+ }
+ else
+ {
+ uno::Sequence< uno::Any > aValues = xDataSequence->getData();
+ aResult.realloc(aValues.getLength());
+ auto pResult = aResult.getArray();
+
+ for(sal_Int32 nN=aValues.getLength();nN--;)
+ aValues[nN] >>= pResult[nN];
+ }
+
+ return aResult;
+}
+
+bool hasDoubleValue( const uno::Any& rAny )
+{
+ bool bRet = false;
+ double fValue = 0.0;
+ if( rAny >>= fValue )
+ bRet = true;
+ return bRet;
+}
+
+bool hasLongOrShortValue( const uno::Any& rAny )
+{
+ bool bRet = false;
+ sal_Int32 n32 = 0;
+ if( rAny >>= n32 )
+ bRet = true;
+ else
+ {
+ sal_Int16 n16 = 0;
+ if( rAny >>= n16 )
+ bRet = true;
+ }
+ return bRet;
+}
+sal_Int16 getShortForLongAlso( const uno::Any& rAny )
+{
+ sal_Int16 nRet = 0;
+
+ if( !(rAny >>= nRet) )
+ {
+ sal_Int32 n32 = 0;
+ if( rAny >>= n32 )
+ nRet = static_cast<sal_Int16>(n32);
+ }
+ return nRet;
+}
+
+bool replaceParamterInString( OUString & rInOutResourceString,
+ const OUString & rParamToReplace,
+ std::u16string_view rReplaceWith )
+{
+ sal_Int32 nPos = rInOutResourceString.indexOf( rParamToReplace );
+ if( nPos == -1 )
+ return false;
+
+ rInOutResourceString = rInOutResourceString.replaceAt( nPos
+ , rParamToReplace.getLength(), rReplaceWith );
+ return true;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ConfigColorScheme.cxx b/chart2/source/tools/ConfigColorScheme.cxx
new file mode 100644
index 000000000..f6bbfe232
--- /dev/null
+++ b/chart2/source/tools/ConfigColorScheme.cxx
@@ -0,0 +1,188 @@
+/* -*- 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 <ConfigColorScheme.hxx>
+
+#include <unotools/configitem.hxx>
+#include <sal/macros.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <set>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace
+{
+
+constexpr OUStringLiteral aSeriesPropName = u"Series";
+
+} // anonymous namespace
+
+namespace chart
+{
+
+uno::Reference< chart2::XColorScheme > createConfigColorScheme( const uno::Reference< uno::XComponentContext > & xContext )
+{
+ return new ConfigColorScheme( xContext );
+}
+
+namespace impl
+{
+class ChartConfigItem : public ::utl::ConfigItem
+{
+public:
+ explicit ChartConfigItem( ConfigColorScheme & rListener );
+
+ void addPropertyNotification( const OUString & rPropertyName );
+ uno::Any getProperty( const OUString & aPropertyName );
+
+protected:
+ // ____ ::utl::ConfigItem ____
+ virtual void ImplCommit() override;
+ virtual void Notify( const Sequence< OUString > & aPropertyNames ) override;
+
+private:
+ ConfigColorScheme & m_rListener;
+ std::set< OUString > m_aPropertiesToNotify;
+};
+
+ChartConfigItem::ChartConfigItem( ConfigColorScheme & rListener ) :
+ ::utl::ConfigItem( "Office.Chart/DefaultColor" ),
+ m_rListener( rListener )
+{}
+
+void ChartConfigItem::Notify( const Sequence< OUString > & aPropertyNames )
+{
+ for( OUString const & s : aPropertyNames )
+ {
+ if( m_aPropertiesToNotify.find( s ) != m_aPropertiesToNotify.end())
+ m_rListener.notify( s );
+ }
+}
+
+void ChartConfigItem::ImplCommit()
+{}
+
+void ChartConfigItem::addPropertyNotification( const OUString & rPropertyName )
+{
+ m_aPropertiesToNotify.insert( rPropertyName );
+ EnableNotification( comphelper::containerToSequence( m_aPropertiesToNotify ));
+}
+
+uno::Any ChartConfigItem::getProperty( const OUString & aPropertyName )
+{
+ Sequence< uno::Any > aValues(
+ GetProperties( Sequence< OUString >( &aPropertyName, 1 )));
+ if( ! aValues.hasElements())
+ return uno::Any();
+ return aValues[0];
+}
+
+} // namespace impl
+
+// explicit
+ConfigColorScheme::ConfigColorScheme(
+ const Reference< uno::XComponentContext > & xContext ) :
+ m_xContext( xContext ),
+ m_nNumberOfColors( 0 ),
+ m_bNeedsUpdate( true )
+{
+}
+
+ConfigColorScheme::~ConfigColorScheme()
+{}
+
+void ConfigColorScheme::retrieveConfigColors()
+{
+ if( ! m_xContext.is())
+ return;
+
+ // create config item if necessary
+ if (!m_apChartConfigItem)
+ {
+ m_apChartConfigItem.reset(
+ new impl::ChartConfigItem( *this ));
+ m_apChartConfigItem->addPropertyNotification( aSeriesPropName );
+ }
+ assert(m_apChartConfigItem && "this can only be set at this point");
+
+ // retrieve colors
+ uno::Any aValue(
+ m_apChartConfigItem->getProperty( aSeriesPropName ));
+ if( aValue >>= m_aColorSequence )
+ m_nNumberOfColors = m_aColorSequence.getLength();
+ m_bNeedsUpdate = false;
+}
+
+// ____ XColorScheme ____
+::sal_Int32 SAL_CALL ConfigColorScheme::getColorByIndex( ::sal_Int32 nIndex )
+{
+ if( m_bNeedsUpdate )
+ retrieveConfigColors();
+
+ if( m_nNumberOfColors > 0 )
+ return static_cast< sal_Int32 >( m_aColorSequence[ nIndex % m_nNumberOfColors ] );
+
+ // fall-back: hard-coded standard colors
+ static const sal_Int32 nDefaultColors[] = {
+ 0x9999ff, 0x993366, 0xffffcc,
+ 0xccffff, 0x660066, 0xff8080,
+ 0x0066cc, 0xccccff, 0x000080,
+ 0xff00ff, 0x00ffff, 0xffff00
+ };
+
+ static const sal_Int32 nMaxDefaultColors = std::size( nDefaultColors );
+ return nDefaultColors[ nIndex % nMaxDefaultColors ];
+}
+
+void ConfigColorScheme::notify( std::u16string_view rPropertyName )
+{
+ if( rPropertyName == aSeriesPropName )
+ m_bNeedsUpdate = true;
+}
+
+OUString SAL_CALL ConfigColorScheme::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.ConfigDefaultColorScheme" ;
+}
+
+sal_Bool SAL_CALL ConfigColorScheme::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ConfigColorScheme::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.ColorScheme" };
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_ConfigDefaultColorScheme_get_implementation(css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::ConfigColorScheme(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ControllerLockGuard.cxx b/chart2/source/tools/ControllerLockGuard.cxx
new file mode 100644
index 000000000..9ae942a5a
--- /dev/null
+++ b/chart2/source/tools/ControllerLockGuard.cxx
@@ -0,0 +1,84 @@
+/* -*- 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 <ControllerLockGuard.hxx>
+#include <ChartModel.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+ControllerLockGuardUNO::ControllerLockGuardUNO( const rtl::Reference<::chart::ChartModel>& xModel ) :
+ mxModel( xModel )
+{
+ mxModel->lockControllers();
+}
+
+ControllerLockGuardUNO::~ControllerLockGuardUNO()
+{
+ mxModel->unlockControllers();
+}
+
+ControllerLockGuard::ControllerLockGuard( ChartModel& rModel ) :
+ mrModel( rModel )
+{
+ mrModel.lockControllers();
+}
+
+ControllerLockGuard::~ControllerLockGuard()
+{
+ mrModel.unlockControllers();
+}
+
+ControllerLockHelper::ControllerLockHelper( const rtl::Reference<::chart::ChartModel> & xModel ) :
+ m_xModel( xModel )
+{}
+
+ControllerLockHelper::~ControllerLockHelper()
+{}
+
+void ControllerLockHelper::lockControllers()
+{
+ if( m_xModel.is())
+ m_xModel->lockControllers();
+}
+
+void ControllerLockHelper::unlockControllers()
+{
+ if( m_xModel.is())
+ m_xModel->unlockControllers();
+}
+
+ControllerLockHelperGuard::ControllerLockHelperGuard( ControllerLockHelper & rHelper ) :
+ m_rHelper( rHelper )
+{
+ m_rHelper.lockControllers();
+}
+
+ControllerLockHelperGuard::~ControllerLockHelperGuard()
+{
+ m_rHelper.unlockControllers();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/DataSeriesHelper.cxx b/chart2/source/tools/DataSeriesHelper.cxx
new file mode 100644
index 000000000..a4b4dec45
--- /dev/null
+++ b/chart2/source/tools/DataSeriesHelper.cxx
@@ -0,0 +1,884 @@
+/* -*- 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 <DataSeriesHelper.hxx>
+#include <DataSeries.hxx>
+#include <DataSource.hxx>
+#include <ChartType.hxx>
+#include <unonames.hxx>
+#include <Diagram.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <Axis.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/data/LabelOrigin.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/SymbolStyle.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <iterator>
+#include <vector>
+#include <set>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace
+{
+
+class lcl_MatchesRole
+{
+public:
+ explicit lcl_MatchesRole( const OUString & aRole, bool bMatchPrefix ) :
+ m_aRole( aRole ),
+ m_bMatchPrefix( bMatchPrefix )
+ {}
+
+ bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
+ {
+ if(!xSeq.is())
+ return false;
+ Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
+ OUString aRole;
+
+ if( m_bMatchPrefix )
+ return ( xProp.is() &&
+ (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
+ aRole.match( m_aRole ));
+
+ return ( xProp.is() &&
+ (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
+ m_aRole == aRole );
+ }
+
+private:
+ OUString m_aRole;
+ bool m_bMatchPrefix;
+};
+
+Reference< chart2::data::XLabeledDataSequence > lcl_findLSequenceWithOnlyLabel(
+ const Reference< chart2::data::XDataSource > & xDataSource )
+{
+ Reference< chart2::data::XLabeledDataSequence > xResult;
+ const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
+
+ for( auto const & labeledData : aSequences )
+ {
+ OSL_ENSURE( labeledData.is(), "empty LabeledDataSequence" );
+ // no values are set but a label exists
+ if( labeledData.is() &&
+ ( ! labeledData->getValues().is() &&
+ labeledData->getLabel().is()))
+ {
+ xResult.set( labeledData );
+ break;
+ }
+ }
+
+ return xResult;
+}
+
+void lcl_getCooSysAndChartTypeOfSeries(
+ const rtl::Reference< ::chart::DataSeries > & xSeries,
+ const Reference< chart2::XDiagram > & xDiagram,
+ rtl::Reference< ::chart::BaseCoordinateSystem > & xOutCooSys,
+ rtl::Reference< ::chart::ChartType > & xOutChartType )
+{
+ if( !xDiagram.is())
+ return;
+ ::chart::Diagram* pDiagram = dynamic_cast<::chart::Diagram*>(xDiagram.get());
+
+ for( rtl::Reference< ::chart::BaseCoordinateSystem > const & coords : pDiagram->getBaseCoordinateSystems() )
+ {
+ for( rtl::Reference< ::chart::ChartType > const & chartType : coords->getChartTypes2() )
+ {
+ for( rtl::Reference< ::chart::DataSeries > const & dataSeries : chartType->getDataSeries2() )
+ {
+ if( dataSeries == xSeries )
+ {
+ xOutCooSys = coords;
+ xOutChartType = chartType;
+ }
+ }
+ }
+ }
+}
+
+void lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries, bool bInsert )
+{
+ try
+ {
+ Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
+ if( xSeriesProperties.is() )
+ {
+ DataPointLabel aLabelAtSeries;
+ xSeriesProperties->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabelAtSeries;
+ aLabelAtSeries.ShowNumber = bInsert;
+ if( !bInsert )
+ {
+ aLabelAtSeries.ShowNumberInPercent = false;
+ aLabelAtSeries.ShowCategoryName = false;
+ }
+ xSeriesProperties->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabelAtSeries));
+ uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
+ if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
+ {
+ for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
+ {
+ Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
+ if( xPointProp.is() )
+ {
+ DataPointLabel aLabel;
+ xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
+ aLabel.ShowNumber = bInsert;
+ if( !bInsert )
+ {
+ aLabel.ShowNumberInPercent = false;
+ aLabel.ShowCategoryName = false;
+ aLabel.ShowCustomLabel = false;
+ aLabel.ShowSeriesName = false;
+ }
+ xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
+ xPointProp->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any());
+ }
+ }
+ }
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+} // anonymous namespace
+
+namespace chart::DataSeriesHelper
+{
+
+OUString getRole( const uno::Reference< chart2::data::XLabeledDataSequence >& xLabeledDataSequence )
+{
+ OUString aRet;
+ if( xLabeledDataSequence.is() )
+ {
+ Reference< beans::XPropertySet > xProp( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
+ if( xProp.is() )
+ xProp->getPropertyValue( "Role" ) >>= aRet;
+ }
+ return aRet;
+}
+
+uno::Reference< chart2::data::XLabeledDataSequence >
+ getDataSequenceByRole(
+ const Reference< chart2::data::XDataSource > & xSource,
+ const OUString& aRole,
+ bool bMatchPrefix /* = false */ )
+{
+ uno::Reference< chart2::data::XLabeledDataSequence > aNoResult;
+ if( ! xSource.is())
+ return aNoResult;
+ const Sequence< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq( xSource->getDataSequences());
+ for (auto const & i : aLabeledSeq)
+ {
+ if (lcl_MatchesRole(aRole, bMatchPrefix)(i))
+ return i;
+ }
+
+ return aNoResult;
+}
+
+std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >
+ getAllDataSequencesByRole( const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aDataSequences,
+ const OUString& aRole )
+{
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aResultVec;
+ for (const auto & i : aDataSequences)
+ {
+ if (lcl_MatchesRole(aRole, /*bMatchPrefix*/true)(i))
+ aResultVec.push_back(i);
+ }
+ return aResultVec;
+}
+
+std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > >
+ getAllDataSequencesByRole( const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aDataSequences,
+ const OUString& aRole )
+{
+ std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > > aResultVec;
+ std::copy_if( aDataSequences.begin(), aDataSequences.end(),
+ std::back_inserter( aResultVec ),
+ lcl_MatchesRole(aRole, /*bMatchPrefix*/true) );
+ return aResultVec;
+}
+
+std::vector<uno::Reference<chart2::data::XLabeledDataSequence> >
+getAllDataSequences( const uno::Sequence<uno::Reference<chart2::XDataSeries> >& aSeries )
+{
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVec;
+
+ for( uno::Reference<chart2::XDataSeries> const & dataSeries : aSeries )
+ {
+ Reference< chart2::data::XDataSource > xSource( dataSeries, uno::UNO_QUERY );
+ if( xSource.is())
+ {
+ const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( xSource->getDataSequences());
+ for (const auto & i : aSeq)
+ {
+ aSeqVec.push_back(i);
+ }
+ }
+ }
+
+ return aSeqVec;
+}
+
+std::vector<uno::Reference<chart2::data::XLabeledDataSequence> >
+getAllDataSequences( const std::vector<rtl::Reference<DataSeries> >& aSeries )
+{
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVec;
+
+ for( rtl::Reference<DataSeries> const & dataSeries : aSeries )
+ {
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aSeq( dataSeries->getDataSequences2());
+ aSeqVec.insert( aSeqVec.end(), aSeq.begin(), aSeq.end() );
+ }
+
+ return aSeqVec;
+}
+
+rtl::Reference< DataSource >
+ getDataSource( const std::vector< rtl::Reference< DataSeries > > & aSeries )
+{
+ return new DataSource(getAllDataSequences(aSeries));
+}
+
+namespace
+{
+OUString lcl_getDataSequenceLabel( const Reference< chart2::data::XDataSequence > & xSequence )
+{
+ OUString aResult;
+
+ Reference< chart2::data::XTextualDataSequence > xTextSeq( xSequence, uno::UNO_QUERY );
+ if( xTextSeq.is())
+ {
+ Sequence< OUString > aSeq( xTextSeq->getTextualData());
+
+ const sal_Int32 nMax = aSeq.getLength() - 1;
+ OUStringBuffer aBuf;
+
+ for( sal_Int32 i = 0; i <= nMax; ++i )
+ {
+ aBuf.append( aSeq[i] );
+ if( i < nMax )
+ aBuf.append( ' ');
+ }
+ aResult = aBuf.makeStringAndClear();
+ }
+ else if( xSequence.is())
+ {
+ Sequence< uno::Any > aSeq( xSequence->getData());
+
+ const sal_Int32 nMax = aSeq.getLength() - 1;
+ OUString aVal;
+ OUStringBuffer aBuf;
+ double fNum = 0;
+
+ for( sal_Int32 i = 0; i <= nMax; ++i )
+ {
+ if( aSeq[i] >>= aVal )
+ {
+ aBuf.append( aVal );
+ if( i < nMax )
+ aBuf.append( ' ');
+ }
+ else if( aSeq[ i ] >>= fNum )
+ {
+ aBuf.append( fNum );
+ if( i < nMax )
+ aBuf.append( ' ');
+ }
+ }
+ aResult = aBuf.makeStringAndClear();
+ }
+
+ return aResult;
+}
+}
+
+OUString getLabelForLabeledDataSequence(
+ const Reference< chart2::data::XLabeledDataSequence > & xLabeledSeq )
+{
+ OUString aResult;
+ if( xLabeledSeq.is())
+ {
+ Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
+ if( xSeq.is() )
+ aResult = lcl_getDataSequenceLabel( xSeq );
+ if( !xSeq.is() || aResult.isEmpty() )
+ {
+ // no label set or label content is empty -> use auto-generated one
+ Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() );
+ if( xValueSeq.is() )
+ {
+ Sequence< OUString > aLabels( xValueSeq->generateLabel(
+ chart2::data::LabelOrigin_SHORT_SIDE ) );
+ // no labels returned is interpreted as: auto-generation not
+ // supported by sequence
+ if( aLabels.hasElements() )
+ aResult=aLabels[0];
+ else
+ {
+ //todo?: maybe use the index of the series as name
+ //but as the index may change it would be better to have such a name persistent
+ //what is not possible at the moment
+ //--> maybe use the identifier as part of the name ...
+ aResult = lcl_getDataSequenceLabel( xValueSeq );
+ }
+ }
+ }
+ }
+ return aResult;
+}
+
+OUString getDataSeriesLabel(
+ const rtl::Reference< DataSeries > & xSeries,
+ const OUString & rLabelSequenceRole )
+{
+ OUString aResult;
+
+ if( xSeries.is())
+ {
+ Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
+ ::chart::DataSeriesHelper::getDataSequenceByRole( xSeries, rLabelSequenceRole ));
+ if( xLabeledSeq.is())
+ aResult = getLabelForLabeledDataSequence( xLabeledSeq );
+ else
+ {
+ // special case: labeled data series with only a label and no values may
+ // serve as label
+ xLabeledSeq.set( lcl_findLSequenceWithOnlyLabel( xSeries ));
+ if( xLabeledSeq.is())
+ {
+ Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
+ if( xSeq.is())
+ aResult = lcl_getDataSequenceLabel( xSeq );
+ }
+ }
+
+ }
+
+ return aResult;
+}
+
+void setStackModeAtSeries(
+ const std::vector< rtl::Reference< DataSeries > > & aSeries,
+ const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem,
+ StackMode eStackMode )
+{
+ const uno::Any aPropValue(
+ ( (eStackMode == StackMode::YStacked) ||
+ (eStackMode == StackMode::YStackedPercent) )
+ ? chart2::StackingDirection_Y_STACKING
+ : (eStackMode == StackMode::ZStacked )
+ ? chart2::StackingDirection_Z_STACKING
+ : chart2::StackingDirection_NO_STACKING );
+
+ std::set< sal_Int32 > aAxisIndexSet;
+ for( rtl::Reference< DataSeries > const & dataSeries : aSeries )
+ {
+ try
+ {
+ if( dataSeries.is() )
+ {
+ dataSeries->setPropertyValue( "StackingDirection", aPropValue );
+
+ sal_Int32 nAxisIndex;
+ dataSeries->getPropertyValue( "AttachedAxisIndex" ) >>= nAxisIndex;
+ aAxisIndexSet.insert(nAxisIndex);
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ if( !(xCorrespondingCoordinateSystem.is() &&
+ 1 < xCorrespondingCoordinateSystem->getDimension()) )
+ return;
+
+ if( aAxisIndexSet.empty() )
+ {
+ aAxisIndexSet.insert(0);
+ }
+
+ for (auto const& axisIndex : aAxisIndexSet)
+ {
+ rtl::Reference< Axis > xAxis =
+ xCorrespondingCoordinateSystem->getAxisByDimension2(1, axisIndex);
+ if( xAxis.is())
+ {
+ bool bPercent = (eStackMode == StackMode::YStackedPercent);
+ chart2::ScaleData aScaleData = xAxis->getScaleData();
+
+ if( bPercent != (aScaleData.AxisType==chart2::AxisType::PERCENT) )
+ {
+ if( bPercent )
+ aScaleData.AxisType = chart2::AxisType::PERCENT;
+ else
+ aScaleData.AxisType = chart2::AxisType::REALNUMBER;
+ xAxis->setScaleData( aScaleData );
+ }
+ }
+ }
+}
+
+sal_Int32 getAttachedAxisIndex( const Reference< chart2::XDataSeries > & xSeries )
+{
+ sal_Int32 nRet = 0;
+ try
+ {
+ Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
+ if( xProp.is() )
+ {
+ xProp->getPropertyValue( "AttachedAxisIndex" ) >>= nRet;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return nRet;
+}
+
+sal_Int32 getNumberFormatKeyFromAxis(
+ const Reference< chart2::XDataSeries > & xSeries,
+ const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem,
+ sal_Int32 nDimensionIndex,
+ sal_Int32 nAxisIndex /* = -1 */ )
+{
+ sal_Int32 nResult = 0;
+ if( nAxisIndex == -1 )
+ nAxisIndex = getAttachedAxisIndex( xSeries );
+ try
+ {
+ rtl::Reference< Axis > xAxisProp =
+ xCorrespondingCoordinateSystem->getAxisByDimension2( nDimensionIndex, nAxisIndex );
+ if( xAxisProp.is())
+ xAxisProp->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nResult;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nResult;
+}
+
+rtl::Reference< ::chart::BaseCoordinateSystem > getCoordinateSystemOfSeries(
+ const Reference< chart2::XDataSeries > & xSeries,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ rtl::Reference< ::chart::BaseCoordinateSystem > xResult;
+ rtl::Reference< ::chart::ChartType > xDummy;
+ rtl::Reference< DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
+ assert(pSeries);
+ lcl_getCooSysAndChartTypeOfSeries( pSeries, xDiagram, xResult, xDummy );
+
+ return xResult;
+}
+
+rtl::Reference< ::chart::ChartType > getChartTypeOfSeries(
+ const Reference< chart2::XDataSeries > & xSeries,
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ rtl::Reference< ::chart::ChartType > xResult;
+ rtl::Reference< ::chart::BaseCoordinateSystem > xDummy;
+ rtl::Reference< DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
+ assert(pSeries);
+ lcl_getCooSysAndChartTypeOfSeries( pSeries, xDiagram, xDummy, xResult );
+
+ return xResult;
+}
+
+void deleteSeries(
+ const Reference< chart2::XDataSeries > & xSeries,
+ const rtl::Reference< ::chart::ChartType > & xChartType )
+{
+ try
+ {
+ rtl::Reference<DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
+ assert(pSeries);
+ std::vector< rtl::Reference< DataSeries > > aSeries = xChartType->getDataSeries2();
+ auto aIt = std::find( aSeries.begin(), aSeries.end(), pSeries );
+ if( aIt != aSeries.end())
+ {
+ aSeries.erase( aIt );
+ xChartType->setDataSeries( aSeries );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void switchSymbolsOnOrOff( const Reference< beans::XPropertySet > & xSeriesProperties,
+ bool bSymbolsOn, sal_Int32 nSeriesIndex )
+{
+ if( !xSeriesProperties.is() )
+ return;
+
+ chart2::Symbol aSymbProp;
+ if( xSeriesProperties->getPropertyValue( "Symbol") >>= aSymbProp )
+ {
+ if( !bSymbolsOn )
+ aSymbProp.Style = chart2::SymbolStyle_NONE;
+ else if( aSymbProp.Style == chart2::SymbolStyle_NONE )
+ {
+ aSymbProp.Style = chart2::SymbolStyle_STANDARD;
+ aSymbProp.StandardSymbol = nSeriesIndex;
+ }
+ xSeriesProperties->setPropertyValue( "Symbol", uno::Any( aSymbProp ));
+ }
+ //todo: check attributed data points
+}
+
+void switchLinesOnOrOff( const Reference< beans::XPropertySet > & xSeriesProperties, bool bLinesOn )
+{
+ if( !xSeriesProperties.is() )
+ return;
+
+ if( bLinesOn )
+ {
+ // keep line-styles that are not NONE
+ drawing::LineStyle eLineStyle;
+ if( (xSeriesProperties->getPropertyValue( "LineStyle") >>= eLineStyle ) &&
+ eLineStyle == drawing::LineStyle_NONE )
+ {
+ xSeriesProperties->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_SOLID ) );
+ }
+ }
+ else
+ xSeriesProperties->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_NONE ) );
+}
+
+void makeLinesThickOrThin( const Reference< beans::XPropertySet > & xSeriesProperties, bool bThick )
+{
+ if( !xSeriesProperties.is() )
+ return;
+
+ sal_Int32 nNewValue = bThick ? 80 : 0;
+ sal_Int32 nOldValue = 0;
+ if( (xSeriesProperties->getPropertyValue( "LineWidth") >>= nOldValue ) &&
+ nOldValue != nNewValue )
+ {
+ if( !(bThick && nOldValue>0))
+ xSeriesProperties->setPropertyValue( "LineWidth", uno::Any( nNewValue ) );
+ }
+}
+
+void setPropertyAlsoToAllAttributedDataPoints( const Reference< chart2::XDataSeries >& xSeries,
+ const OUString& rPropertyName, const uno::Any& rPropertyValue )
+{
+ rtl::Reference<DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
+ assert(!xSeries || pSeries);
+ setPropertyAlsoToAllAttributedDataPoints(pSeries, rPropertyName, rPropertyValue);
+}
+
+void setPropertyAlsoToAllAttributedDataPoints( const rtl::Reference< ::chart::DataSeries >& xSeries,
+ const OUString& rPropertyName, const uno::Any& rPropertyValue )
+{
+ if( !xSeries.is() )
+ return;
+
+ xSeries->setPropertyValue( rPropertyName, rPropertyValue );
+ uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
+ if( xSeries->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
+ {
+ for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
+ {
+ Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
+ if(!xPointProp.is())
+ continue;
+ xPointProp->setPropertyValue( rPropertyName, rPropertyValue );
+ if( rPropertyName == "LabelPlacement" )
+ xPointProp->setPropertyValue("CustomLabelPosition", uno::Any());
+ }
+ }
+}
+
+bool hasAttributedDataPointDifferentValue( const Reference< chart2::XDataSeries >& xSeries,
+ const OUString& rPropertyName, const uno::Any& rPropertyValue )
+{
+ Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
+ if( !xSeriesProperties.is() )
+ return false;
+
+ uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
+ if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
+ {
+ for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
+ {
+ Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
+ if(!xPointProp.is())
+ continue;
+ uno::Any aPointValue( xPointProp->getPropertyValue( rPropertyName ) );
+ if( rPropertyValue != aPointValue )
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace
+{
+
+bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
+{
+ if( !xDataSequence.is() )
+ return false;
+ uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY );
+ if( xProp.is() )
+ {
+ uno::Sequence< sal_Int32 > aHiddenValues;
+ try
+ {
+ xProp->getPropertyValue( "HiddenValues" ) >>= aHiddenValues;
+ if( !aHiddenValues.hasElements() )
+ return true;
+ }
+ catch( const uno::Exception& )
+ {
+ return true;
+ }
+ }
+ return xDataSequence->getData().hasElements();
+}
+
+}
+
+bool hasUnhiddenData( const uno::Reference< chart2::XDataSeries >& xSeries )
+{
+ uno::Reference< chart2::data::XDataSource > xDataSource( xSeries, uno::UNO_QUERY );
+
+ uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDataSequences = xDataSource->getDataSequences();
+
+ for(sal_Int32 nN = aDataSequences.getLength();nN--;)
+ {
+ if( !aDataSequences[nN].is() )
+ continue;
+ if( lcl_SequenceHasUnhiddenData( aDataSequences[nN]->getValues() ) )
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 translateIndexFromHiddenToFullSequence( sal_Int32 nIndex, const Reference< chart2::data::XDataSequence >& xDataSequence, bool bTranslate )
+{
+ if( !bTranslate )
+ return nIndex;
+
+ try
+ {
+ uno::Reference<beans::XPropertySet> xProp( xDataSequence, uno::UNO_QUERY );
+ if( xProp.is())
+ {
+ Sequence<sal_Int32> aHiddenIndicesSeq;
+ xProp->getPropertyValue( "HiddenValues" ) >>= aHiddenIndicesSeq;
+ if( aHiddenIndicesSeq.hasElements() )
+ {
+ auto aHiddenIndices( comphelper::sequenceToContainer<std::vector< sal_Int32 >>( aHiddenIndicesSeq ) );
+ std::sort( aHiddenIndices.begin(), aHiddenIndices.end() );
+
+ sal_Int32 nHiddenCount = static_cast<sal_Int32>(aHiddenIndices.size());
+ for( sal_Int32 nN = 0; nN < nHiddenCount; ++nN)
+ {
+ if( aHiddenIndices[nN] <= nIndex )
+ nIndex += 1;
+ else
+ break;
+ }
+ }
+ }
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ return nIndex;
+}
+
+bool hasDataLabelsAtSeries( const Reference< chart2::XDataSeries >& xSeries )
+{
+ bool bRet = false;
+ try
+ {
+ Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
+ if( xProp.is() )
+ {
+ DataPointLabel aLabel;
+ if( xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
+ bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName
+ || aLabel.ShowSeriesName;
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return bRet;
+}
+
+bool hasDataLabelsAtPoints( const Reference< chart2::XDataSeries >& xSeries )
+{
+ bool bRet = false;
+ try
+ {
+ Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
+ if( xSeriesProperties.is() )
+ {
+ uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
+ if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
+ {
+ for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
+ {
+ Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
+ if( xPointProp.is() )
+ {
+ DataPointLabel aLabel;
+ if( xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
+ bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
+ || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
+ || aLabel.ShowSeriesName;
+ if( bRet )
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return bRet;
+}
+
+bool hasDataLabelAtPoint( const Reference< chart2::XDataSeries >& xSeries, sal_Int32 nPointIndex )
+{
+ bool bRet = false;
+ try
+ {
+ Reference< beans::XPropertySet > xProp;
+ Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
+ if( xSeriesProperties.is() )
+ {
+ uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
+ if( xSeriesProperties->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList )
+ {
+ auto aIt = std::find( std::as_const(aAttributedDataPointIndexList).begin(), std::as_const(aAttributedDataPointIndexList).end(), nPointIndex );
+ if( aIt != std::as_const(aAttributedDataPointIndexList).end())
+ xProp = xSeries->getDataPointByIndex(nPointIndex);
+ else
+ xProp = xSeriesProperties;
+ }
+ if( xProp.is() )
+ {
+ DataPointLabel aLabel;
+ if( xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel )
+ bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent
+ || aLabel.ShowCategoryName || aLabel.ShowCustomLabel
+ || aLabel.ShowSeriesName;
+ }
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return bRet;
+}
+
+void insertDataLabelsToSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries )
+{
+ lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, true /*bInsert*/ );
+}
+
+void deleteDataLabelsFromSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries )
+{
+ lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, false /*bInsert*/ );
+}
+
+void insertDataLabelToPoint( const Reference< beans::XPropertySet >& xPointProp )
+{
+ try
+ {
+ if( xPointProp.is() )
+ {
+ DataPointLabel aLabel;
+ xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
+ aLabel.ShowNumber = true;
+ xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+void deleteDataLabelsFromPoint( const Reference< beans::XPropertySet >& xPointProp )
+{
+ try
+ {
+ if( xPointProp.is() )
+ {
+ DataPointLabel aLabel;
+ xPointProp->getPropertyValue(CHART_UNONAME_LABEL) >>= aLabel;
+ aLabel.ShowNumber = false;
+ aLabel.ShowNumberInPercent = false;
+ aLabel.ShowCategoryName = false;
+ aLabel.ShowCustomLabel = false;
+ aLabel.ShowSeriesName = false;
+ xPointProp->setPropertyValue(CHART_UNONAME_LABEL, uno::Any(aLabel));
+ xPointProp->setPropertyValue(CHART_UNONAME_CUSTOM_LABEL_FIELDS, uno::Any());
+ }
+ }
+ catch(const uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/DataSource.cxx b/chart2/source/tools/DataSource.cxx
new file mode 100644
index 000000000..c74687c14
--- /dev/null
+++ b/chart2/source/tools/DataSource.cxx
@@ -0,0 +1,88 @@
+/* -*- 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 <DataSource.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+
+using namespace ::com::sun::star;
+
+
+namespace chart
+{
+
+DataSource::DataSource()
+{}
+
+DataSource::DataSource(
+ const Sequence< Reference< chart2::data::XLabeledDataSequence > > & rSequences ) :
+ m_aDataSeq( rSequences )
+{}
+
+DataSource::DataSource(
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & rSequences )
+{
+ m_aDataSeq = comphelper::containerToSequence< Reference< chart2::data::XLabeledDataSequence > >(rSequences);
+}
+
+DataSource::~DataSource()
+{}
+
+// ____ XDataSource ____
+Sequence< Reference< chart2::data::XLabeledDataSequence > > SAL_CALL DataSource::getDataSequences()
+{
+ return m_aDataSeq;
+}
+
+// ____ XDataSink ____
+void SAL_CALL DataSource::setData( const Sequence< Reference< chart2::data::XLabeledDataSequence > >& aData )
+{
+ m_aDataSeq = aData;
+}
+
+OUString SAL_CALL DataSource::getImplementationName()
+{
+ return "com.sun.star.comp.chart.DataSource";
+}
+
+sal_Bool SAL_CALL DataSource::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DataSource::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.data.DataSource" };
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart_DataSource_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::DataSource);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/DataSourceHelper.cxx b/chart2/source/tools/DataSourceHelper.cxx
new file mode 100644
index 000000000..8e8dd2f3a
--- /dev/null
+++ b/chart2/source/tools/DataSourceHelper.cxx
@@ -0,0 +1,471 @@
+/* -*- 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 <DataSourceHelper.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <ChartTypeManager.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <DataSource.hxx>
+#include <ControllerLockGuard.hxx>
+#include <CachedDataSequence.hxx>
+#include <LabeledDataSequence.hxx>
+#include <unonames.hxx>
+
+#include <com/sun/star/chart2/data/XDataSource.hpp>
+#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
+
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <tools/diagnose_ex.h>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace
+{
+void lcl_addRanges( std::vector< OUString > & rOutResult,
+ const uno::Reference< data::XLabeledDataSequence > & xLabeledSeq )
+{
+ if( ! xLabeledSeq.is())
+ return;
+ uno::Reference< data::XDataSequence > xSeq( xLabeledSeq->getLabel());
+ if( xSeq.is())
+ rOutResult.push_back( xSeq->getSourceRangeRepresentation());
+ xSeq.set( xLabeledSeq->getValues());
+ if( xSeq.is())
+ rOutResult.push_back( xSeq->getSourceRangeRepresentation());
+}
+
+void lcl_addDataSourceRanges(
+ std::vector< OUString > & rOutResult,
+ const uno::Reference< data::XDataSource > & xDataSource )
+{
+ if( xDataSource.is() )
+ {
+ const auto aDataSequences(xDataSource->getDataSequences());
+ for (const auto& rDataSequence : aDataSequences)
+ lcl_addRanges(rOutResult, rDataSequence);
+ }
+}
+
+void lcl_addErrorBarRanges(
+ std::vector< OUString > & rOutResult,
+ const rtl::Reference< DataSeries > & xDataSeries )
+{
+ if( !xDataSeries.is())
+ return;
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > xErrorBarProp;
+ if( ( xDataSeries->getPropertyValue( CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp ) &&
+ xErrorBarProp.is())
+ {
+ sal_Int32 eStyle = css::chart::ErrorBarStyle::NONE;
+ if( ( xErrorBarProp->getPropertyValue( "ErrorBarStyle") >>= eStyle ) &&
+ eStyle == css::chart::ErrorBarStyle::FROM_DATA )
+ {
+ uno::Reference< data::XDataSource > xErrorBarDataSource( xErrorBarProp, uno::UNO_QUERY );
+ if( xErrorBarDataSource.is() )
+ lcl_addDataSourceRanges( rOutResult, xErrorBarDataSource );
+ }
+ }
+
+ if( ( xDataSeries->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp ) && xErrorBarProp.is())
+ {
+ sal_Int32 eStyle = css::chart::ErrorBarStyle::NONE;
+ if( ( xErrorBarProp->getPropertyValue("ErrorBarStyle") >>= eStyle ) &&
+ eStyle == css::chart::ErrorBarStyle::FROM_DATA )
+ {
+ uno::Reference< data::XDataSource > xErrorBarDataSource( xErrorBarProp, uno::UNO_QUERY );
+ if( xErrorBarDataSource.is() )
+ lcl_addDataSourceRanges( rOutResult, xErrorBarDataSource );
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+} // anonymous namespace
+
+Reference< chart2::data::XDataSequence > DataSourceHelper::createCachedDataSequence()
+{
+ return new ::chart::CachedDataSequence();
+}
+
+Reference< chart2::data::XDataSequence > DataSourceHelper::createCachedDataSequence( const OUString& rSingleText )
+{
+ return new ::chart::CachedDataSequence( rSingleText );
+}
+
+rtl::Reference< LabeledDataSequence > DataSourceHelper::createLabeledDataSequence(
+ const Reference< chart2::data::XDataSequence >& xValues ,
+ const Reference< chart2::data::XDataSequence >& xLabels )
+{
+ return new ::chart::LabeledDataSequence( xValues, xLabels );
+}
+
+rtl::Reference< LabeledDataSequence > DataSourceHelper::createLabeledDataSequence(
+ const Reference< chart2::data::XDataSequence >& xValues )
+{
+ return new ::chart::LabeledDataSequence( xValues );
+}
+
+rtl::Reference< LabeledDataSequence > DataSourceHelper::createLabeledDataSequence()
+{
+ return new ::chart::LabeledDataSequence;
+}
+
+uno::Sequence< beans::PropertyValue > DataSourceHelper::createArguments(
+ bool bUseColumns, bool bFirstCellAsLabel, bool bHasCategories )
+{
+ css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
+ if( bUseColumns )
+ eRowSource = css::chart::ChartDataRowSource_COLUMNS;
+
+ return
+ {
+ { "DataRowSource", -1, uno::Any( eRowSource), beans::PropertyState_DIRECT_VALUE },
+ { "FirstCellAsLabel", -1, uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE },
+ { "HasCategories", -1, uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE }
+ };
+}
+
+uno::Sequence< beans::PropertyValue > DataSourceHelper::createArguments(
+ const OUString & rRangeRepresentation,
+ const uno::Sequence< sal_Int32 >& rSequenceMapping,
+ bool bUseColumns, bool bFirstCellAsLabel, bool bHasCategories )
+{
+ uno::Sequence< beans::PropertyValue > aArguments( createArguments( bUseColumns, bFirstCellAsLabel, bHasCategories ));
+ aArguments.realloc( aArguments.getLength() + 1 );
+ aArguments.getArray()[aArguments.getLength() - 1] =
+ beans::PropertyValue( "CellRangeRepresentation"
+ , -1, uno::Any( rRangeRepresentation )
+ , beans::PropertyState_DIRECT_VALUE );
+ if( rSequenceMapping.hasElements() )
+ {
+ aArguments.realloc( aArguments.getLength() + 1 );
+ aArguments.getArray()[aArguments.getLength() - 1] =
+ beans::PropertyValue( "SequenceMapping"
+ , -1, uno::Any( rSequenceMapping )
+ , beans::PropertyState_DIRECT_VALUE );
+ }
+ return aArguments;
+}
+
+void DataSourceHelper::readArguments( const uno::Sequence< beans::PropertyValue >& rArguments
+ , OUString & rRangeRepresentation, uno::Sequence< sal_Int32 >& rSequenceMapping
+ , bool& bUseColumns, bool& bFirstCellAsLabel, bool& bHasCategories )
+{
+ for(const beans::PropertyValue& rProperty : rArguments)
+ {
+ if ( rProperty.Name == "DataRowSource" )
+ {
+ css::chart::ChartDataRowSource eRowSource;
+ if( rProperty.Value >>= eRowSource )
+ bUseColumns = (eRowSource==css::chart::ChartDataRowSource_COLUMNS);
+ }
+ else if ( rProperty.Name == "FirstCellAsLabel" )
+ {
+ rProperty.Value >>= bFirstCellAsLabel;
+ }
+ else if ( rProperty.Name == "HasCategories" )
+ {
+ rProperty.Value >>= bHasCategories;
+ }
+ else if ( rProperty.Name == "CellRangeRepresentation" )
+ {
+ rProperty.Value >>= rRangeRepresentation;
+ }
+ else if ( rProperty.Name == "SequenceMapping" )
+ {
+ rProperty.Value >>= rSequenceMapping;
+ }
+ }
+}
+
+rtl::Reference< DataSource > DataSourceHelper::pressUsedDataIntoRectangularFormat(
+ const rtl::Reference< ChartModel >& xChartDoc )
+{
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aResultVector;
+
+ //categories are always the first sequence
+ rtl::Reference< Diagram > xDiagram( xChartDoc->getFirstChartDiagram());
+
+ Reference< chart2::data::XLabeledDataSequence > xCategories( DiagramHelper::getCategoriesFromDiagram( xDiagram ) );
+ if( xCategories.is() )
+ aResultVector.push_back( xCategories );
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector = DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+ uno::Reference< chart2::data::XDataSource > xSeriesSource =
+ DataSeriesHelper::getDataSource( aSeriesVector );
+ const Sequence< Reference< chart2::data::XLabeledDataSequence > > aDataSequences( xSeriesSource->getDataSequences() );
+
+ //the first x-values is always the next sequence //todo ... other x-values get lost for old format
+ Reference< chart2::data::XLabeledDataSequence > xXValues(
+ DataSeriesHelper::getDataSequenceByRole( xSeriesSource, "values-x" ) );
+ if( xXValues.is() )
+ aResultVector.push_back( xXValues );
+
+ //add all other sequences now without x-values
+ for( Reference< chart2::data::XLabeledDataSequence > const & labeledData : aDataSequences )
+ {
+ OUString aRole = DataSeriesHelper::getRole(labeledData);
+ if( aRole != "values-x" )
+ aResultVector.push_back( labeledData );
+ }
+
+ return new DataSource( aResultVector );
+}
+
+uno::Sequence< OUString > DataSourceHelper::getUsedDataRanges(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ std::vector< OUString > aResult;
+
+ if( xDiagram.is())
+ {
+ uno::Reference< data::XLabeledDataSequence > xCategories( DiagramHelper::getCategoriesFromDiagram( xDiagram ) );
+ if( xCategories.is() )
+ lcl_addRanges( aResult, xCategories );
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector( DiagramHelper::getDataSeriesFromDiagram( xDiagram ) );
+ for (auto const& series : aSeriesVector)
+ {
+ lcl_addDataSourceRanges( aResult, series );
+ lcl_addErrorBarRanges( aResult, series );
+ }
+ }
+
+ return comphelper::containerToSequence( aResult );
+}
+
+uno::Sequence< OUString > DataSourceHelper::getUsedDataRanges( const rtl::Reference<::chart::ChartModel> & xChartModel )
+{
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xChartModel ) );
+ return getUsedDataRanges( xDiagram );
+}
+
+rtl::Reference< DataSource > DataSourceHelper::getUsedData(
+ ChartModel& rModel )
+{
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aResult;
+
+ rtl::Reference< Diagram > xDiagram = rModel.getFirstChartDiagram();
+ uno::Reference< chart2::data::XLabeledDataSequence > xCategories( DiagramHelper::getCategoriesFromDiagram( xDiagram ) );
+ if( xCategories.is() )
+ aResult.push_back( xCategories );
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector = ChartModelHelper::getDataSeries( &rModel );
+ for (auto const& series : aSeriesVector)
+ {
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aDataSequences( series->getDataSequences2() );
+ aResult.insert( aResult.end(), aDataSequences.begin(), aDataSequences.end() );
+ }
+
+ return new DataSource( aResult );
+}
+
+bool DataSourceHelper::detectRangeSegmentation(
+ const rtl::Reference<::chart::ChartModel>& xChartModel
+ , OUString& rOutRangeString
+ , css::uno::Sequence< sal_Int32 >& rSequenceMapping
+ , bool& rOutUseColumns
+ , bool& rOutFirstCellAsLabel
+ , bool& rOutHasCategories )
+{
+ bool bSomethingDetected = false;
+
+ if( !xChartModel.is() )
+ return bSomethingDetected;
+ uno::Reference< data::XDataProvider > xDataProvider( xChartModel->getDataProvider() );
+ if( !xDataProvider.is() )
+ return bSomethingDetected;
+
+ try
+ {
+ DataSourceHelper::readArguments(
+ xDataProvider->detectArguments( pressUsedDataIntoRectangularFormat( xChartModel ) ),
+ rOutRangeString, rSequenceMapping, rOutUseColumns, rOutFirstCellAsLabel, rOutHasCategories );
+ bSomethingDetected = !rOutRangeString.isEmpty();
+
+ uno::Reference< chart2::data::XLabeledDataSequence > xCategories(
+ DiagramHelper::getCategoriesFromDiagram( xChartModel->getFirstChartDiagram() ));
+ rOutHasCategories = xCategories.is();
+ }
+ catch( uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return bSomethingDetected;
+}
+
+bool DataSourceHelper::allArgumentsForRectRangeDetected(
+ const rtl::Reference<::chart::ChartModel>& xChartDocument )
+{
+ bool bHasDataRowSource = false;
+ bool bHasFirstCellAsLabel = false;
+ bool bHasCellRangeRepresentation = false;
+
+ uno::Reference< data::XDataProvider > xDataProvider( xChartDocument->getDataProvider() );
+ if( !xDataProvider.is() )
+ return false;
+
+ try
+ {
+ const uno::Sequence< beans::PropertyValue > aArguments(
+ xDataProvider->detectArguments( pressUsedDataIntoRectangularFormat( xChartDocument )));
+ for(const beans::PropertyValue& rProperty : aArguments)
+ {
+ if ( rProperty.Name == "DataRowSource" )
+ {
+ bHasDataRowSource =
+ (rProperty.Value.hasValue() && rProperty.Value.isExtractableTo(
+ cppu::UnoType<css::chart::ChartDataRowSource>::get()));
+ }
+ else if ( rProperty.Name == "FirstCellAsLabel" )
+ {
+ bHasFirstCellAsLabel =
+ (rProperty.Value.hasValue() && rProperty.Value.isExtractableTo(cppu::UnoType<bool>::get()));
+ }
+ else if ( rProperty.Name == "CellRangeRepresentation" )
+ {
+ OUString aRange;
+ bHasCellRangeRepresentation =
+ (rProperty.Value.hasValue() && (rProperty.Value >>= aRange) && !aRange.isEmpty());
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return (bHasCellRangeRepresentation && bHasDataRowSource && bHasFirstCellAsLabel);
+}
+
+void DataSourceHelper::setRangeSegmentation(
+ const rtl::Reference<::chart::ChartModel>& xChartModel
+ , const css::uno::Sequence< sal_Int32 >& rSequenceMapping
+ , bool bUseColumns , bool bFirstCellAsLabel, bool bUseCategories )
+{
+ uno::Reference< data::XDataProvider > xDataProvider( xChartModel->getDataProvider() );
+ if( !xDataProvider.is() )
+ return;
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xChartModel ) );
+ if( !xDiagram.is() )
+ return;
+ rtl::Reference< ::chart::ChartTypeManager > xChartTypeManager = xChartModel->getTypeManager();
+ if( !xChartTypeManager.is() )
+ return;
+
+ OUString aRangeString;
+ bool bDummy;
+ uno::Sequence< sal_Int32 > aDummy;
+ readArguments( xDataProvider->detectArguments( pressUsedDataIntoRectangularFormat( xChartModel )),
+ aRangeString, aDummy, bDummy, bDummy, bDummy );
+
+ uno::Sequence< beans::PropertyValue > aArguments(
+ createArguments( aRangeString, rSequenceMapping, bUseColumns, bFirstCellAsLabel, bUseCategories ) );
+
+ uno::Reference< chart2::data::XDataSource > xDataSource( xDataProvider->createDataSource(
+ aArguments ) );
+ if( !xDataSource.is() )
+ return;
+
+ ControllerLockGuardUNO aCtrlLockGuard( xChartModel );
+ xDiagram->setDiagramData( xDataSource, aArguments );
+}
+
+Sequence< OUString > DataSourceHelper::getRangesFromLabeledDataSequence(
+ const Reference< data::XLabeledDataSequence > & xLSeq )
+{
+ Sequence< OUString > aResult;
+ if( xLSeq.is())
+ {
+ Reference< data::XDataSequence > xLabel( xLSeq->getLabel());
+ Reference< data::XDataSequence > xValues( xLSeq->getValues());
+
+ if( xLabel.is())
+ {
+ if( xValues.is())
+ {
+ aResult = { xLabel->getSourceRangeRepresentation(),
+ xValues->getSourceRangeRepresentation() };
+ }
+ else
+ {
+ aResult = { xLabel->getSourceRangeRepresentation() };
+ }
+ }
+ else if( xValues.is())
+ {
+ aResult = { xValues->getSourceRangeRepresentation() };
+ }
+ }
+ return aResult;
+}
+
+OUString DataSourceHelper::getRangeFromValues(
+ const Reference< data::XLabeledDataSequence > & xLSeq )
+{
+ OUString aResult;
+ if( xLSeq.is() )
+ {
+ Reference< data::XDataSequence > xValues( xLSeq->getValues() );
+ if( xValues.is() )
+ aResult = xValues->getSourceRangeRepresentation();
+ }
+ return aResult;
+}
+
+Sequence< OUString > DataSourceHelper::getRangesFromDataSource( const Reference< data::XDataSource > & xSource )
+{
+ std::vector< OUString > aResult;
+ if( xSource.is())
+ {
+ const Sequence< Reference< data::XLabeledDataSequence > > aLSeqSeq( xSource->getDataSequences());
+ for( Reference< data::XLabeledDataSequence > const & labeledData : aLSeqSeq )
+ {
+ Reference< data::XDataSequence > xLabel( labeledData->getLabel());
+ Reference< data::XDataSequence > xValues( labeledData->getValues());
+
+ if( xLabel.is())
+ aResult.push_back( xLabel->getSourceRangeRepresentation());
+ if( xValues.is())
+ aResult.push_back( xValues->getSourceRangeRepresentation());
+ }
+ }
+ return comphelper::containerToSequence( aResult );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/DiagramHelper.cxx b/chart2/source/tools/DiagramHelper.cxx
new file mode 100644
index 000000000..04556b6ed
--- /dev/null
+++ b/chart2/source/tools/DiagramHelper.cxx
@@ -0,0 +1,1571 @@
+/* -*- 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 <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ChartTypeManager.hxx>
+#include <ChartTypeTemplate.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <servicenames_charttypes.hxx>
+#include <RelativePositionHelper.hxx>
+#include <ControllerLockGuard.hxx>
+#include <NumberFormatterWrapper.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/saveopt.hxx>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <cstddef>
+#include <limits>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using namespace ::std;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::chart2::XAnyDescriptionAccess;
+
+namespace chart
+{
+
+DiagramHelper::tTemplateWithServiceName
+ DiagramHelper::getTemplateForDiagram(
+ const rtl::Reference< Diagram > & xDiagram,
+ const rtl::Reference< ::chart::ChartTypeManager > & xChartTypeManager )
+{
+ DiagramHelper::tTemplateWithServiceName aResult;
+
+ if( ! (xChartTypeManager.is() && xDiagram.is()))
+ return aResult;
+
+ Sequence< OUString > aServiceNames( xChartTypeManager->getAvailableServiceNames());
+ const sal_Int32 nLength = aServiceNames.getLength();
+
+ bool bTemplateFound = false;
+
+ for( sal_Int32 i = 0; ! bTemplateFound && i < nLength; ++i )
+ {
+ try
+ {
+ rtl::Reference< ::chart::ChartTypeTemplate > xTempl =
+ xChartTypeManager->createTemplate( aServiceNames[ i ] );
+
+ if (xTempl.is() && xTempl->matchesTemplate2(xDiagram, true))
+ {
+ aResult.xChartTypeTemplate = xTempl;
+ aResult.sServiceName = aServiceNames[ i ];
+ bTemplateFound = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return aResult;
+}
+
+void DiagramHelper::setVertical(
+ const rtl::Reference< Diagram > & xDiagram,
+ bool bVertical /* = true */ )
+{
+ try
+ {
+ if (!xDiagram.is())
+ return;
+
+ uno::Any aValue;
+ aValue <<= bVertical;
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ bool bChanged = false;
+ bool bOldSwap = false;
+ if( !(xCooSys->getPropertyValue("SwapXAndYAxis") >>= bOldSwap)
+ || bVertical != bOldSwap )
+ bChanged = true;
+
+ if( bChanged )
+ xCooSys->setPropertyValue("SwapXAndYAxis", aValue);
+
+ const sal_Int32 nDimensionCount = xCooSys->getDimension();
+ sal_Int32 nDimIndex = 0;
+ for (nDimIndex=0; nDimIndex < nDimensionCount; ++nDimIndex)
+ {
+ const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nDimIndex);
+ for (sal_Int32 nI = 0; nI <= nMaximumScaleIndex; ++nI)
+ {
+ rtl::Reference<Axis> xAxis = xCooSys->getAxisByDimension2(nDimIndex,nI);
+ if (!xAxis.is())
+ continue;
+
+ //adapt title rotation only when axis swapping has changed
+ if (!bChanged)
+ continue;
+
+ Reference< beans::XPropertySet > xTitleProps( xAxis->getTitleObject(), uno::UNO_QUERY );
+ if (!xTitleProps.is())
+ continue;
+
+ double fAngleDegree = 0.0;
+ xTitleProps->getPropertyValue("TextRotation") >>= fAngleDegree;
+ if (fAngleDegree != 0.0 &&
+ !rtl::math::approxEqual(fAngleDegree, 90.0))
+ continue;
+
+ double fNewAngleDegree = 0.0;
+ if( !bVertical && nDimIndex == 1 )
+ fNewAngleDegree = 90.0;
+ else if( bVertical && nDimIndex == 0 )
+ fNewAngleDegree = 90.0;
+
+ xTitleProps->setPropertyValue("TextRotation", uno::Any(fNewAngleDegree));
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+bool DiagramHelper::getVertical( const rtl::Reference< Diagram > & xDiagram,
+ bool& rbFound, bool& rbAmbiguous )
+{
+ bool bValue = false;
+ rbFound = false;
+ rbAmbiguous = false;
+
+ if (!xDiagram.is())
+ return false;
+
+ for (rtl::Reference<BaseCoordinateSystem> const & coords : xDiagram->getBaseCoordinateSystems())
+ {
+ bool bCurrent = false;
+ if (coords->getPropertyValue("SwapXAndYAxis") >>= bCurrent)
+ {
+ if (!rbFound)
+ {
+ bValue = bCurrent;
+ rbFound = true;
+ }
+ else if (bCurrent != bValue)
+ {
+ // ambiguous -> choose always first found
+ rbAmbiguous = true;
+ }
+ }
+ }
+ return bValue;
+}
+
+void DiagramHelper::setStackMode(
+ const rtl::Reference< Diagram > & xDiagram,
+ StackMode eStackMode
+)
+{
+ try
+ {
+ bool bValueFound = false;
+ bool bIsAmbiguous = false;
+ StackMode eOldStackMode = DiagramHelper::getStackMode( xDiagram, bValueFound, bIsAmbiguous );
+
+ if( eStackMode == eOldStackMode && !bIsAmbiguous )
+ return;
+
+ StackingDirection eNewDirection = StackingDirection_NO_STACKING;
+ if( eStackMode == StackMode::YStacked || eStackMode == StackMode::YStackedPercent )
+ eNewDirection = StackingDirection_Y_STACKING;
+ else if( eStackMode == StackMode::ZStacked )
+ eNewDirection = StackingDirection_Z_STACKING;
+
+ uno::Any aNewDirection( eNewDirection );
+
+ bool bPercent = false;
+ if( eStackMode == StackMode::YStackedPercent )
+ bPercent = true;
+
+ //iterate through all coordinate systems
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ //set correct percent stacking
+ const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(1);
+ for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2( 1,nI );
+ if( xAxis.is())
+ {
+ chart2::ScaleData aScaleData = xAxis->getScaleData();
+ if( (aScaleData.AxisType==AxisType::PERCENT) != bPercent )
+ {
+ if( bPercent )
+ aScaleData.AxisType = AxisType::PERCENT;
+ else
+ aScaleData.AxisType = AxisType::REALNUMBER;
+ xAxis->setScaleData( aScaleData );
+ }
+ }
+ }
+ //iterate through all chart types in the current coordinate system
+ const std::vector< rtl::Reference< ChartType > > & aChartTypeList( xCooSys->getChartTypes2() );
+ if (aChartTypeList.empty())
+ continue;
+
+ rtl::Reference< ChartType > xChartType( aChartTypeList[0] );
+
+ //iterate through all series in this chart type
+ for( rtl::Reference< DataSeries > const & dataSeries : xChartType->getDataSeries2() )
+ {
+ dataSeries->setPropertyValue( "StackingDirection", aNewDirection );
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+StackMode DiagramHelper::getStackMode( const rtl::Reference< Diagram > & xDiagram, bool& rbFound, bool& rbAmbiguous )
+{
+ rbFound=false;
+ rbAmbiguous=false;
+
+ StackMode eGlobalStackMode = StackMode::NONE;
+
+ if( !xDiagram.is() )
+ return eGlobalStackMode;
+
+ //iterate through all coordinate systems
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ //iterate through all chart types in the current coordinate system
+ std::vector< rtl::Reference< ChartType > > aChartTypeList( xCooSys->getChartTypes2() );
+ for( std::size_t nT = 0; nT < aChartTypeList.size(); ++nT )
+ {
+ rtl::Reference< ChartType > xChartType( aChartTypeList[nT] );
+
+ StackMode eLocalStackMode = DiagramHelper::getStackModeFromChartType(
+ xChartType, rbFound, rbAmbiguous, xCooSys );
+
+ if( rbFound && eLocalStackMode != eGlobalStackMode && nT>0 )
+ {
+ rbAmbiguous = true;
+ return eGlobalStackMode;
+ }
+
+ eGlobalStackMode = eLocalStackMode;
+ }
+ }
+
+ return eGlobalStackMode;
+}
+
+StackMode DiagramHelper::getStackModeFromChartType(
+ const rtl::Reference< ChartType > & xChartType,
+ bool& rbFound, bool& rbAmbiguous,
+ const rtl::Reference< BaseCoordinateSystem > & xCorrespondingCoordinateSystem )
+{
+ StackMode eStackMode = StackMode::NONE;
+ rbFound = false;
+ rbAmbiguous = false;
+
+ try
+ {
+ const std::vector< rtl::Reference< DataSeries > > & aSeries = xChartType->getDataSeries2();
+
+ chart2::StackingDirection eCommonDirection = chart2::StackingDirection_NO_STACKING;
+ bool bDirectionInitialized = false;
+
+ // first series is irrelevant for stacking, start with second, unless
+ // there is only one series
+ const sal_Int32 nSeriesCount = aSeries.size();
+ sal_Int32 i = (nSeriesCount == 1) ? 0: 1;
+ for( ; i<nSeriesCount; ++i )
+ {
+ rbFound = true;
+ chart2::StackingDirection eCurrentDirection = eCommonDirection;
+ // property is not MAYBEVOID
+ bool bSuccess = ( aSeries[i]->getPropertyValue( "StackingDirection" ) >>= eCurrentDirection );
+ OSL_ASSERT( bSuccess );
+ if( ! bDirectionInitialized )
+ {
+ eCommonDirection = eCurrentDirection;
+ bDirectionInitialized = true;
+ }
+ else
+ {
+ if( eCommonDirection != eCurrentDirection )
+ {
+ rbAmbiguous = true;
+ break;
+ }
+ }
+ }
+
+ if( rbFound )
+ {
+ if( eCommonDirection == chart2::StackingDirection_Z_STACKING )
+ eStackMode = StackMode::ZStacked;
+ else if( eCommonDirection == chart2::StackingDirection_Y_STACKING )
+ {
+ eStackMode = StackMode::YStacked;
+
+ // percent stacking
+ if( xCorrespondingCoordinateSystem.is() )
+ {
+ if( 1 < xCorrespondingCoordinateSystem->getDimension() )
+ {
+ sal_Int32 nAxisIndex = 0;
+ if( nSeriesCount )
+ nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(aSeries[0]);
+
+ rtl::Reference< Axis > xAxis =
+ xCorrespondingCoordinateSystem->getAxisByDimension2( 1,nAxisIndex );
+ if( xAxis.is())
+ {
+ chart2::ScaleData aScaleData = xAxis->getScaleData();
+ if( aScaleData.AxisType==chart2::AxisType::PERCENT )
+ eStackMode = StackMode::YStackedPercent;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return eStackMode;
+}
+
+sal_Int32 DiagramHelper::getDimension( const rtl::Reference< Diagram > & xDiagram )
+{
+ // -1: not yet set
+ sal_Int32 nResult = -1;
+ if (!xDiagram)
+ return nResult;
+
+ try
+ {
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ if(xCooSys.is())
+ {
+ nResult = xCooSys->getDimension();
+ break;
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nResult;
+}
+
+void DiagramHelper::setDimension(
+ const rtl::Reference< Diagram > & xDiagram,
+ sal_Int32 nNewDimensionCount )
+{
+ if( ! xDiagram.is())
+ return;
+
+ if( DiagramHelper::getDimension( xDiagram ) == nNewDimensionCount )
+ return;
+
+ try
+ {
+ bool rbFound = false;
+ bool rbAmbiguous = true;
+ StackMode eStackMode = DiagramHelper::getStackMode( xDiagram, rbFound, rbAmbiguous );
+ bool bIsSupportingOnlyDeepStackingFor3D=false;
+
+ //change all coordinate systems:
+ auto aCoordSystems = xDiagram->getBaseCoordinateSystems();
+ for( rtl::Reference<BaseCoordinateSystem> const & xOldCooSys : aCoordSystems )
+ {
+ rtl::Reference< BaseCoordinateSystem > xNewCooSys;
+
+ const std::vector< rtl::Reference< ChartType > > aChartTypeList( xOldCooSys->getChartTypes2() );
+ for( rtl::Reference< ChartType > const & xChartType : aChartTypeList )
+ {
+ bIsSupportingOnlyDeepStackingFor3D = ChartTypeHelper::isSupportingOnlyDeepStackingFor3D( xChartType );
+ if(!xNewCooSys.is())
+ {
+ xNewCooSys = dynamic_cast<BaseCoordinateSystem*>(xChartType->createCoordinateSystem( nNewDimensionCount ).get());
+ assert(xNewCooSys);
+ break;
+ }
+ //@todo make sure that all following charttypes are also capable of the new dimension
+ //otherwise separate them in a different group
+ //BM: might be done in replaceCoordinateSystem()
+ }
+
+ // replace the old coordinate system at all places where it was used
+ DiagramHelper::replaceCoordinateSystem( xDiagram, xOldCooSys, xNewCooSys );
+ }
+
+ //correct stack mode if necessary
+ if( nNewDimensionCount==3 && eStackMode != StackMode::ZStacked && bIsSupportingOnlyDeepStackingFor3D )
+ DiagramHelper::setStackMode( xDiagram, StackMode::ZStacked );
+ else if( nNewDimensionCount==2 && eStackMode == StackMode::ZStacked )
+ DiagramHelper::setStackMode( xDiagram, StackMode::NONE );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void DiagramHelper::replaceCoordinateSystem(
+ const rtl::Reference< Diagram > & xDiagram,
+ const rtl::Reference< BaseCoordinateSystem > & xCooSysToReplace,
+ const rtl::Reference< BaseCoordinateSystem > & xReplacement )
+{
+ OSL_ASSERT( xDiagram.is());
+ if( ! xDiagram.is())
+ return;
+
+ // update the coordinate-system container
+
+ try
+ {
+ uno::Reference< chart2::data::XLabeledDataSequence > xCategories = DiagramHelper::getCategoriesFromDiagram( xDiagram );
+
+ // move chart types of xCooSysToReplace to xReplacement
+ xReplacement->setChartTypes( xCooSysToReplace->getChartTypes());
+
+ xDiagram->removeCoordinateSystem( xCooSysToReplace );
+ xDiagram->addCoordinateSystem( xReplacement );
+
+ if( xCategories.is() )
+ DiagramHelper::setCategoriesToDiagram( xCategories, xDiagram );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+bool DiagramHelper::isSeriesAttachedToMainAxis(
+ const uno::Reference< chart2::XDataSeries >& xDataSeries )
+{
+ sal_Int32 nAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries);
+ return (nAxisIndex==0);
+}
+
+bool DiagramHelper::attachSeriesToAxis( bool bAttachToMainAxis
+ , const uno::Reference< chart2::XDataSeries >& xDataSeries
+ , const rtl::Reference< Diagram >& xDiagram
+ , const uno::Reference< uno::XComponentContext > & xContext
+ , bool bAdaptAxes )
+{
+ bool bChanged = false;
+
+ //set property at axis
+ Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
+
+ sal_Int32 nNewAxisIndex = bAttachToMainAxis ? 0 : 1;
+ sal_Int32 nOldAxisIndex = DataSeriesHelper::getAttachedAxisIndex(xDataSeries);
+ rtl::Reference< Axis > xOldAxis = DiagramHelper::getAttachedAxis( xDataSeries, xDiagram );
+
+ if( nOldAxisIndex != nNewAxisIndex )
+ {
+ try
+ {
+ xProp->setPropertyValue( "AttachedAxisIndex", uno::Any( nNewAxisIndex ) );
+ bChanged = true;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ if( bChanged && xDiagram.is() )
+ {
+ rtl::Reference< Axis > xAxis = AxisHelper::getAxis( 1, bAttachToMainAxis, xDiagram );
+ if(!xAxis.is()) //create an axis if necessary
+ xAxis = AxisHelper::createAxis( 1, bAttachToMainAxis, xDiagram, xContext );
+ if( bAdaptAxes )
+ {
+ AxisHelper::makeAxisVisible( xAxis );
+ AxisHelper::hideAxisIfNoDataIsAttached( xOldAxis, xDiagram );
+ }
+ }
+
+ return bChanged;
+}
+
+rtl::Reference< Axis > DiagramHelper::getAttachedAxis(
+ const uno::Reference< XDataSeries >& xSeries,
+ const rtl::Reference< Diagram >& xDiagram )
+{
+ return AxisHelper::getAxis( 1, DiagramHelper::isSeriesAttachedToMainAxis( xSeries ), xDiagram );
+}
+
+rtl::Reference< Axis > DiagramHelper::getAttachedAxis(
+ const rtl::Reference< DataSeries >& xSeries,
+ const rtl::Reference< Diagram >& xDiagram )
+{
+ return AxisHelper::getAxis( 1, DiagramHelper::isSeriesAttachedToMainAxis( xSeries ), xDiagram );
+}
+
+rtl::Reference< ChartType > DiagramHelper::getChartTypeOfSeries(
+ const rtl::Reference< Diagram >& xDiagram
+ , const uno::Reference< XDataSeries >& xGivenDataSeries )
+{
+ if( !xGivenDataSeries.is() )
+ return nullptr;
+ if(!xDiagram.is())
+ return nullptr;
+ rtl::Reference pGivenDataSeries = dynamic_cast<DataSeries*>(xGivenDataSeries.get());
+ assert(pGivenDataSeries);
+
+ //iterate through the model to find the given xSeries
+ //the found parent indicates the charttype
+
+ //iterate through all coordinate systems
+
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ //iterate through all chart types in the current coordinate system
+ const std::vector< rtl::Reference< ChartType > > & aChartTypeList( xCooSys->getChartTypes2() );
+ for( rtl::Reference< ChartType > const & xChartType : aChartTypeList )
+ {
+ //iterate through all series in this chart type
+ for( rtl::Reference< DataSeries > const & dataSeries : xChartType->getDataSeries2() )
+ {
+ if( pGivenDataSeries==dataSeries )
+ return xChartType;
+ }
+ }
+ }
+ return nullptr;
+}
+
+std::vector< rtl::Reference< ::chart::DataSeries > >
+ DiagramHelper::getDataSeriesFromDiagram(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ std::vector< rtl::Reference< DataSeries > > aResult;
+ if (!xDiagram)
+ return aResult;
+
+ try
+ {
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : xDiagram->getBaseCoordinateSystems() )
+ {
+ for( rtl::Reference< ChartType> const & chartType : coords->getChartTypes2() )
+ {
+ const std::vector< rtl::Reference< DataSeries > > aSeriesSeq( chartType->getDataSeries2() );
+ aResult.insert( aResult.end(), aSeriesSeq.begin(), aSeriesSeq.end() );
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return aResult;
+}
+
+std::vector< std::vector< rtl::Reference< DataSeries > > >
+ DiagramHelper::getDataSeriesGroups( const rtl::Reference< Diagram > & xDiagram )
+{
+ if (!xDiagram)
+ return {};
+
+ vector< std::vector< rtl::Reference< DataSeries > > > aResult;
+
+ //iterate through all coordinate systems
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : xDiagram->getBaseCoordinateSystems() )
+ {
+ //iterate through all chart types in the current coordinate system
+ for( rtl::Reference< ChartType > const & chartType : coords->getChartTypes2() )
+ {
+ aResult.push_back( chartType->getDataSeries2() );
+ }
+ }
+ return aResult;
+}
+
+rtl::Reference< ChartType >
+ DiagramHelper::getChartTypeByIndex( const rtl::Reference< Diagram >& xDiagram, sal_Int32 nIndex )
+{
+ if (!xDiagram)
+ return nullptr;
+
+ rtl::Reference< ChartType > xChartType;
+
+ //iterate through all coordinate systems
+ sal_Int32 nTypesSoFar = 0;
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : xDiagram->getBaseCoordinateSystems() )
+ {
+ const std::vector< rtl::Reference< ChartType > > & aChartTypeList( coords->getChartTypes2() );
+ if( nIndex >= 0 && o3tl::make_unsigned(nIndex) < nTypesSoFar + aChartTypeList.size() )
+ {
+ xChartType = aChartTypeList[nIndex - nTypesSoFar];
+ break;
+ }
+ nTypesSoFar += aChartTypeList.size();
+ }
+
+ return xChartType;
+}
+
+namespace
+{
+
+std::vector< rtl::Reference< Axis > > lcl_getAxisHoldingCategoriesFromDiagram(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ std::vector< rtl::Reference< Axis > > aRet;
+
+ // return first x-axis as fall-back
+ rtl::Reference< Axis > xFallBack;
+ if (xDiagram.is()) try
+ {
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ OSL_ASSERT( xCooSys.is());
+ for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
+ {
+ const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
+ for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2( nN,nI );
+ OSL_ASSERT( xAxis.is());
+ if( xAxis.is())
+ {
+ ScaleData aScaleData = xAxis->getScaleData();
+ if( aScaleData.Categories.is() || (aScaleData.AxisType == AxisType::CATEGORY) )
+ {
+ aRet.push_back(xAxis);
+ }
+ if( (nN == 0) && !xFallBack.is())
+ xFallBack = xAxis;
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+
+ if( aRet.empty() )
+ aRet.push_back(xFallBack);
+
+ return aRet;
+}
+
+} // anonymous namespace
+
+bool DiagramHelper::isCategoryDiagram(
+ const rtl::Reference< Diagram >& xDiagram )
+{
+ try
+ {
+ for( rtl::Reference< BaseCoordinateSystem > const & xCooSys : xDiagram->getBaseCoordinateSystems() )
+ {
+ for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
+ {
+ const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
+ for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2( nN,nI );
+ OSL_ASSERT( xAxis.is());
+ if( xAxis.is())
+ {
+ ScaleData aScaleData = xAxis->getScaleData();
+ if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE )
+ return true;
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return false;
+}
+
+void DiagramHelper::setCategoriesToDiagram(
+ const uno::Reference< chart2::data::XLabeledDataSequence >& xCategories,
+ const rtl::Reference< Diagram >& xDiagram,
+ bool bSetAxisType /* = false */,
+ bool bCategoryAxis /* = true */ )
+{
+ std::vector< rtl::Reference< Axis > > aCatAxes(
+ lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));
+
+ for (const rtl::Reference< Axis >& xCatAxis : aCatAxes)
+ {
+ if( xCatAxis.is())
+ {
+ ScaleData aScaleData( xCatAxis->getScaleData());
+ aScaleData.Categories = xCategories;
+ if( bSetAxisType )
+ {
+ if( bCategoryAxis )
+ aScaleData.AxisType = AxisType::CATEGORY;
+ else if( aScaleData.AxisType == AxisType::CATEGORY || aScaleData.AxisType == AxisType::DATE )
+ aScaleData.AxisType = AxisType::REALNUMBER;
+ }
+ xCatAxis->setScaleData( aScaleData );
+ }
+ }
+}
+
+uno::Reference< chart2::data::XLabeledDataSequence >
+ DiagramHelper::getCategoriesFromDiagram(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ uno::Reference< chart2::data::XLabeledDataSequence > xResult;
+
+ try
+ {
+ std::vector< rtl::Reference< Axis > > aCatAxes(
+ lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));
+ //search for first categories
+ if (!aCatAxes.empty())
+ {
+ rtl::Reference< Axis > xCatAxis(aCatAxes[0]);
+ if( xCatAxis.is())
+ {
+ ScaleData aScaleData( xCatAxis->getScaleData());
+ if( aScaleData.Categories.is() )
+ {
+ xResult = aScaleData.Categories;
+ uno::Reference<beans::XPropertySet> xProp(xResult->getValues(), uno::UNO_QUERY );
+ if( xProp.is() )
+ {
+ try
+ {
+ xProp->setPropertyValue( "Role", uno::Any( OUString("categories") ) );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return xResult;
+}
+
+static void lcl_generateAutomaticCategoriesFromChartType(
+ Sequence< OUString >& rRet,
+ const rtl::Reference< ChartType >& xChartType )
+{
+ if(!xChartType.is())
+ return;
+ OUString aMainSeq( xChartType->getRoleOfSequenceForSeriesLabel() );
+
+ const std::vector< rtl::Reference< DataSeries > > & aSeriesSeq = xChartType->getDataSeries2();
+ for( rtl::Reference< DataSeries > const & dataSeries : aSeriesSeq )
+ {
+ uno::Reference< data::XLabeledDataSequence > xLabeledSeq =
+ ::chart::DataSeriesHelper::getDataSequenceByRole( dataSeries, aMainSeq );
+ if( !xLabeledSeq.is() )
+ continue;
+ Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() );
+ if( !xValueSeq.is() )
+ continue;
+ rRet = xValueSeq->generateLabel( chart2::data::LabelOrigin_LONG_SIDE );
+ if( rRet.hasElements() )
+ return;
+ }
+}
+
+Sequence< OUString > DiagramHelper::generateAutomaticCategoriesFromCooSys( const rtl::Reference< BaseCoordinateSystem > & xCooSys )
+{
+ Sequence< OUString > aRet;
+
+ if( xCooSys.is() )
+ {
+ const std::vector< rtl::Reference< ChartType > > & aChartTypes( xCooSys->getChartTypes2() );
+ for( rtl::Reference< ChartType > const & chartType : aChartTypes )
+ {
+ lcl_generateAutomaticCategoriesFromChartType( aRet, chartType );
+ if( aRet.hasElements() )
+ return aRet;
+ }
+ }
+ return aRet;
+}
+
+Sequence< OUString > DiagramHelper::getExplicitSimpleCategories(
+ ChartModel& rModel )
+{
+ rtl::Reference< BaseCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
+ ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel );
+ return aExplicitCategoriesProvider.getSimpleCategories();
+}
+
+namespace
+{
+void lcl_switchToDateCategories( const rtl::Reference< ChartModel >& xChartDoc, const Reference< XAxis >& xAxis )
+{
+ if( !xAxis.is() )
+ return;
+ if( !xChartDoc.is() )
+ return;
+
+ ScaleData aScale( xAxis->getScaleData() );
+ if( xChartDoc->hasInternalDataProvider() )
+ {
+ //remove all content the is not of type double and remove multiple level
+ Reference< XAnyDescriptionAccess > xDataAccess( xChartDoc->getDataProvider(), uno::UNO_QUERY );
+ if( xDataAccess.is() )
+ {
+ Sequence< Sequence< Any > > aAnyCategories( xDataAccess->getAnyRowDescriptions() );
+ auto aAnyCategoriesRange = asNonConstRange(aAnyCategories);
+ double fTest = 0.0;
+ sal_Int32 nN = aAnyCategories.getLength();
+ for( ; nN--; )
+ {
+ Sequence< Any >& rCat = aAnyCategoriesRange[nN];
+ if( rCat.getLength() > 1 )
+ rCat.realloc(1);
+ if( rCat.getLength() == 1 )
+ {
+ Any& rAny = rCat.getArray()[0];
+ if( !(rAny>>=fTest) )
+ {
+ rAny <<= std::numeric_limits<double>::quiet_NaN();
+ }
+ }
+ }
+ xDataAccess->setAnyRowDescriptions( aAnyCategories );
+ }
+ //check the numberformat at the axis
+ Reference< beans::XPropertySet > xAxisProps( xAxis, uno::UNO_QUERY );
+ if( xAxisProps.is() )
+ {
+ sal_Int32 nNumberFormat = -1;
+ xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormat;
+
+ Reference< util::XNumberFormats > xNumberFormats( xChartDoc->getNumberFormats() );
+ if( xNumberFormats.is() )
+ {
+ Reference< beans::XPropertySet > xKeyProps;
+ try
+ {
+ xKeyProps = xNumberFormats->getByKey( nNumberFormat );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ sal_Int32 nType = util::NumberFormat::UNDEFINED;
+ if( xKeyProps.is() )
+ xKeyProps->getPropertyValue( "Type" ) >>= nType;
+ if( !( nType & util::NumberFormat::DATE ) )
+ {
+ //set a date format to the axis
+ const LocaleDataWrapper& rLocaleDataWrapper = Application::GetSettings().GetLocaleDataWrapper();
+ Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE, rLocaleDataWrapper.getLanguageTag().getLocale(), true/*bCreate*/ );
+ if( aKeySeq.hasElements() )
+ {
+ xAxisProps->setPropertyValue(CHART_UNONAME_NUMFMT, uno::Any(aKeySeq[0]));
+ }
+ }
+ }
+ }
+ }
+ if( aScale.AxisType != chart2::AxisType::DATE )
+ AxisHelper::removeExplicitScaling( aScale );
+ aScale.AxisType = chart2::AxisType::DATE;
+ xAxis->setScaleData( aScale );
+}
+
+void lcl_switchToTextCategories( const rtl::Reference< ChartModel >& xChartDoc, const Reference< XAxis >& xAxis )
+{
+ if( !xAxis.is() )
+ return;
+ if( !xChartDoc.is() )
+ return;
+ ScaleData aScale( xAxis->getScaleData() );
+ if( aScale.AxisType != chart2::AxisType::CATEGORY )
+ AxisHelper::removeExplicitScaling( aScale );
+ //todo migrate dates to text?
+ aScale.AxisType = chart2::AxisType::CATEGORY;
+ aScale.AutoDateAxis = false;
+ xAxis->setScaleData( aScale );
+}
+
+}
+
+void DiagramHelper::switchToDateCategories( const rtl::Reference<::chart::ChartModel>& xChartDoc )
+{
+ if(xChartDoc.is())
+ {
+ ControllerLockGuardUNO aCtrlLockGuard( xChartDoc );
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = ChartModelHelper::getFirstCoordinateSystem( xChartDoc );
+ if( xCooSys.is() )
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2(0,0);
+ lcl_switchToDateCategories( xChartDoc, xAxis );
+ }
+ }
+}
+
+void DiagramHelper::switchToTextCategories( const rtl::Reference<::chart::ChartModel>& xChartDoc )
+{
+ if(xChartDoc.is())
+ {
+ ControllerLockGuardUNO aCtrlLockGuard( xChartDoc );
+
+ rtl::Reference< BaseCoordinateSystem > xCooSys = ChartModelHelper::getFirstCoordinateSystem( xChartDoc );
+ if( xCooSys.is() )
+ {
+ rtl::Reference< Axis > xAxis = xCooSys->getAxisByDimension2(0,0);
+ lcl_switchToTextCategories( xChartDoc, xAxis );
+ }
+ }
+}
+
+bool DiagramHelper::isSupportingDateAxis( const rtl::Reference< Diagram >& xDiagram )
+{
+ return ::chart::ChartTypeHelper::isSupportingDateAxis(
+ DiagramHelper::getChartTypeByIndex( xDiagram, 0 ), 0 );
+}
+
+bool DiagramHelper::isDateNumberFormat( sal_Int32 nNumberFormat, const Reference< util::XNumberFormats >& xNumberFormats )
+{
+ bool bIsDate = false;
+ if( !xNumberFormats.is() )
+ return bIsDate;
+
+ Reference< beans::XPropertySet > xKeyProps = xNumberFormats->getByKey( nNumberFormat );
+ if( xKeyProps.is() )
+ {
+ sal_Int32 nType = util::NumberFormat::UNDEFINED;
+ xKeyProps->getPropertyValue( "Type" ) >>= nType;
+ bIsDate = nType & util::NumberFormat::DATE;
+ }
+ return bIsDate;
+}
+
+sal_Int32 DiagramHelper::getDateNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier )
+{
+ sal_Int32 nRet=-1;
+
+ //try to get a date format with full year display
+ const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
+ NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
+ SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
+ if( pNumFormatter )
+ {
+ nRet = pNumFormatter->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, rLanguageTag.getLanguageType() );
+ }
+ else
+ {
+ Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() );
+ if( xNumberFormats.is() )
+ {
+ Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::DATE,
+ rLanguageTag.getLocale(), true/*bCreate */);
+ if( aKeySeq.hasElements() )
+ {
+ nRet = aKeySeq[0];
+ }
+ }
+ }
+ return nRet;
+}
+
+sal_Int32 DiagramHelper::getDateTimeInputNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier, double fNumber )
+{
+ sal_Int32 nRet = 0;
+
+ // Get the most detailed date/time format according to fNumber.
+ NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
+ SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
+ if (!pNumFormatter)
+ SAL_WARN("chart2", "DiagramHelper::getDateTimeInputNumberFormat - no SvNumberFormatter");
+ else
+ {
+ SvNumFormatType nType;
+ // Obtain best matching date, time or datetime format.
+ nRet = pNumFormatter->GuessDateTimeFormat( nType, fNumber, LANGUAGE_SYSTEM);
+ // Obtain the corresponding edit format.
+ nRet = pNumFormatter->GetEditFormat( fNumber, nRet, nType, nullptr);
+ }
+ return nRet;
+}
+
+sal_Int32 DiagramHelper::getPercentNumberFormat( const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier )
+{
+ sal_Int32 nRet=-1;
+ const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag();
+ NumberFormatterWrapper aNumberFormatterWrapper( xNumberFormatsSupplier );
+ SvNumberFormatter* pNumFormatter = aNumberFormatterWrapper.getSvNumberFormatter();
+ if( pNumFormatter )
+ {
+ nRet = pNumFormatter->GetFormatIndex( NF_PERCENT_INT, rLanguageTag.getLanguageType() );
+ }
+ else
+ {
+ Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->getNumberFormats() );
+ if( xNumberFormats.is() )
+ {
+ Sequence<sal_Int32> aKeySeq = xNumberFormats->queryKeys( util::NumberFormat::PERCENT,
+ rLanguageTag.getLocale(), true/*bCreate*/ );
+ if( aKeySeq.hasElements() )
+ {
+ // This *assumes* the sequence is sorted as in
+ // NfIndexTableOffset and the first format is the integer 0%
+ // format by chance... which usually is the case, but... anyway,
+ // we usually also have a number formatter so don't reach here.
+ nRet = aKeySeq[0];
+ }
+ }
+ }
+ return nRet;
+}
+
+std::vector< rtl::Reference< ChartType > >
+ DiagramHelper::getChartTypesFromDiagram(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ if(!xDiagram)
+ return {};
+
+ std::vector< rtl::Reference< ChartType > > aResult;
+ try
+ {
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : xDiagram->getBaseCoordinateSystems() )
+ {
+ const std::vector< rtl::Reference< ChartType > > & aChartTypeSeq( coords->getChartTypes2());
+ aResult.insert( aResult.end(), aChartTypeSeq.begin(), aChartTypeSeq.end() );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return aResult;
+}
+
+bool DiagramHelper::areChartTypesCompatible( const rtl::Reference< ChartType >& xFirstType,
+ const rtl::Reference< ChartType >& xSecondType )
+{
+ if( !xFirstType.is() || !xSecondType.is() )
+ return false;
+
+ auto aFirstRoles( comphelper::sequenceToContainer<std::vector< OUString >>( xFirstType->getSupportedMandatoryRoles() ) );
+ auto aSecondRoles( comphelper::sequenceToContainer<std::vector< OUString >>( xSecondType->getSupportedMandatoryRoles() ) );
+ std::sort( aFirstRoles.begin(), aFirstRoles.end() );
+ std::sort( aSecondRoles.begin(), aSecondRoles.end() );
+ return ( aFirstRoles == aSecondRoles );
+}
+
+namespace
+{
+ /**
+ * This method implements the logic of checking if a series can be moved
+ * forward/backward. Depending on the "bDoMove" parameter the series will
+ * be moved (bDoMove = true) or the function just will test if the
+ * series can be moved without doing the move (bDoMove = false).
+ *
+ * @param xDiagram
+ * Reference to the diagram that contains the series.
+ *
+ * @param xGivenDataSeries
+ * Reference to the series that should moved or tested for moving.
+ *
+ * @param bForward
+ * Direction in which the series should be moved or tested for moving.
+ *
+ * @param bDoMove
+ * Should this function really move the series (true) or just test if it is
+ * possible (false).
+ *
+ *
+ * @returns
+ * in case of bDoMove == true
+ * - True : if the move was done
+ * - False : the move failed
+ * in case of bDoMove == false
+ * - True : the series can be moved
+ * - False : the series can not be moved
+ *
+ */
+
+bool lcl_moveSeriesOrCheckIfMoveIsAllowed(
+ const rtl::Reference< Diagram >& xDiagram,
+ const rtl::Reference< DataSeries >& xGivenDataSeries,
+ bool bForward,
+ bool bDoMove )
+{
+ bool bMovedOrMoveAllowed = false;
+
+ try
+ {
+ if( xGivenDataSeries.is() && xDiagram.is() )
+ {
+ //find position of series.
+ bool bFound = false;
+ const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList( xDiagram->getBaseCoordinateSystems() );
+
+ for( std::size_t nCS = 0; !bFound && nCS < aCooSysList.size(); ++nCS )
+ {
+ const rtl::Reference< BaseCoordinateSystem > & xCooSys( aCooSysList[nCS] );
+
+ //iterate through all chart types in the current coordinate system
+ std::vector< rtl::Reference< ChartType > > aChartTypeList( xCooSys->getChartTypes2() );
+ rtl::Reference< ChartType > xFormerChartType;
+
+ for( std::size_t nT = 0; !bFound && nT < aChartTypeList.size(); ++nT )
+ {
+ rtl::Reference< ChartType > xCurrentChartType( aChartTypeList[nT] );
+
+ //iterate through all series in this chart type
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesList = xCurrentChartType->getDataSeries2();
+
+ for( std::size_t nS = 0; !bFound && nS < aSeriesList.size(); ++nS )
+ {
+
+ // We found the series we are interested in!
+ if( xGivenDataSeries==aSeriesList[nS] )
+ {
+ std::size_t nOldSeriesIndex = nS;
+ bFound = true;
+
+ try
+ {
+ sal_Int32 nNewSeriesIndex = nS;
+
+ // tdf#34517 Bringing forward means increasing, backwards means decreasing series position
+ if( !bForward )
+ nNewSeriesIndex--;
+ else
+ nNewSeriesIndex++;
+
+ if( nNewSeriesIndex >= 0 && o3tl::make_unsigned(nNewSeriesIndex) < aSeriesList.size() )
+ {
+ //move series in the same charttype
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ aSeriesList[ nOldSeriesIndex ] = aSeriesList[ nNewSeriesIndex ];
+ aSeriesList[ nNewSeriesIndex ] = xGivenDataSeries;
+ xCurrentChartType->setDataSeries( aSeriesList );
+ }
+ }
+ else if( nNewSeriesIndex<0 )
+ {
+ //exchange series with former charttype
+ if( xFormerChartType.is() && DiagramHelper::areChartTypesCompatible( xFormerChartType, xCurrentChartType ) )
+ {
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ std::vector< rtl::Reference< DataSeries > > aOtherSeriesList = xFormerChartType->getDataSeries2();
+ sal_Int32 nOtherSeriesIndex = aOtherSeriesList.size()-1;
+ if( nOtherSeriesIndex >= 0 && o3tl::make_unsigned(nOtherSeriesIndex) < aOtherSeriesList.size() )
+ {
+ rtl::Reference< DataSeries > xExchangeSeries( aOtherSeriesList[nOtherSeriesIndex] );
+ aOtherSeriesList[nOtherSeriesIndex] = xGivenDataSeries;
+ xFormerChartType->setDataSeries(aOtherSeriesList);
+
+ aSeriesList[nOldSeriesIndex]=xExchangeSeries;
+ xCurrentChartType->setDataSeries(aSeriesList);
+ }
+ }
+ }
+ }
+ else if( nT+1 < aChartTypeList.size() )
+ {
+ //exchange series with next charttype
+ rtl::Reference< ChartType > xOtherChartType( aChartTypeList[nT+1] );
+ if( xOtherChartType.is() && DiagramHelper::areChartTypesCompatible( xOtherChartType, xCurrentChartType ) )
+ {
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ std::vector< rtl::Reference< DataSeries > > aOtherSeriesList = xOtherChartType->getDataSeries2();
+ if( !aOtherSeriesList.empty() )
+ {
+ rtl::Reference< DataSeries > xExchangeSeries( aOtherSeriesList[0] );
+ aOtherSeriesList[0] = xGivenDataSeries;
+ xOtherChartType->setDataSeries(aOtherSeriesList);
+
+ aSeriesList[nOldSeriesIndex]=xExchangeSeries;
+ xCurrentChartType->setDataSeries(aSeriesList);
+ }
+ }
+ }
+ }
+ }
+ catch( const util::CloseVetoException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ }
+ }
+ xFormerChartType = xCurrentChartType;
+ }
+ }
+ }
+ }
+ catch( const util::CloseVetoException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ return bMovedOrMoveAllowed;
+}
+} // anonymous namespace
+
+bool DiagramHelper::isSeriesMoveable(
+ const rtl::Reference< Diagram >& xDiagram,
+ const Reference< XDataSeries >& xGivenDataSeries,
+ bool bForward )
+{
+ const bool bDoMove = false;
+
+ rtl::Reference pGivenDataSeries = dynamic_cast<DataSeries*>(xGivenDataSeries.get());
+ assert(pGivenDataSeries || !xGivenDataSeries);
+
+ bool bIsMoveable = lcl_moveSeriesOrCheckIfMoveIsAllowed(
+ xDiagram, pGivenDataSeries, bForward, bDoMove );
+
+ return bIsMoveable;
+}
+
+bool DiagramHelper::moveSeries( const rtl::Reference< Diagram >& xDiagram, const Reference< XDataSeries >& xGivenDataSeries, bool bForward )
+{
+ const bool bDoMove = true;
+
+ rtl::Reference pGivenDataSeries = dynamic_cast<DataSeries*>(xGivenDataSeries.get());
+ assert(pGivenDataSeries || !xGivenDataSeries);
+
+ bool bMoved = lcl_moveSeriesOrCheckIfMoveIsAllowed(
+ xDiagram, pGivenDataSeries, bForward, bDoMove );
+
+ return bMoved;
+}
+
+bool DiagramHelper::isSupportingFloorAndWall( const rtl::Reference< Diagram >& xDiagram )
+{
+ //pies and donuts currently do not support this because of wrong files from older versions
+ //todo: allow this in future again, if fileversion is available for OLE objects (metastream)
+ //thus the wrong bottom can be removed on import
+
+ const std::vector< rtl::Reference< ChartType > > aTypes(
+ ::chart::DiagramHelper::getChartTypesFromDiagram( xDiagram ) );
+ for( rtl::Reference< ChartType > const & xType : aTypes )
+ {
+ if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_PIE) )
+ return false;
+ if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_NET) )
+ return false;
+ if( xType.is() && xType->getChartType().match(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) )
+ return false;
+ }
+ return true;
+}
+
+bool DiagramHelper::isPieOrDonutChart( const rtl::Reference< Diagram >& xDiagram )
+{
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex(
+ xDiagram, 0 ) );
+
+ if( xChartType .is() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 DiagramHelper::getGeometry3D(
+ const rtl::Reference< Diagram > & xDiagram,
+ bool& rbFound, bool& rbAmbiguous )
+{
+ sal_Int32 nCommonGeom( DataPointGeometry3D::CUBOID );
+ rbFound = false;
+ rbAmbiguous = false;
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesVec =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+
+ if( aSeriesVec.empty())
+ rbAmbiguous = true;
+
+ for (auto const& series : aSeriesVec)
+ {
+ try
+ {
+ sal_Int32 nGeom = 0;
+ if( series->getPropertyValue( "Geometry3D") >>= nGeom )
+ {
+ if( ! rbFound )
+ {
+ // first series
+ nCommonGeom = nGeom;
+ rbFound = true;
+ }
+ // further series: compare for uniqueness
+ else if( nCommonGeom != nGeom )
+ {
+ rbAmbiguous = true;
+ break;
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return nCommonGeom;
+}
+
+void DiagramHelper::setGeometry3D(
+ const rtl::Reference< Diagram > & xDiagram,
+ sal_Int32 nNewGeometry )
+{
+ std::vector< rtl::Reference< DataSeries > > aSeriesVec =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+
+ for (auto const& series : aSeriesVec)
+ {
+ DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints(
+ series, "Geometry3D", uno::Any( nNewGeometry ));
+ }
+}
+
+sal_Int32 DiagramHelper::getCorrectedMissingValueTreatment(
+ const rtl::Reference< Diagram > & xDiagram,
+ const rtl::Reference< ChartType >& xChartType )
+{
+ sal_Int32 nResult = css::chart::MissingValueTreatment::LEAVE_GAP;
+ const uno::Sequence < sal_Int32 > aAvailableMissingValueTreatments(
+ ChartTypeHelper::getSupportedMissingValueTreatments( xChartType ) );
+
+ if( xDiagram.is() && (xDiagram->getPropertyValue( "MissingValueTreatment" ) >>= nResult) )
+ {
+ //ensure that the set value is supported by this charttype
+ for( sal_Int32 n : aAvailableMissingValueTreatments )
+ if( n == nResult )
+ return nResult; //ok
+ }
+
+ //otherwise use the first supported one
+ if( aAvailableMissingValueTreatments.hasElements() )
+ {
+ nResult = aAvailableMissingValueTreatments[0];
+ return nResult;
+ }
+
+ return nResult;
+}
+
+DiagramPositioningMode DiagramHelper::getDiagramPositioningMode( const rtl::Reference<
+ Diagram > & xDiagram )
+{
+ DiagramPositioningMode eMode = DiagramPositioningMode_AUTO;
+ if( xDiagram.is() )
+ {
+ RelativePosition aRelPos;
+ RelativeSize aRelSize;
+ if( (xDiagram->getPropertyValue("RelativePosition") >>= aRelPos ) &&
+ (xDiagram->getPropertyValue("RelativeSize") >>= aRelSize ) )
+ {
+ bool bPosSizeExcludeAxes=false;
+ xDiagram->getPropertyValue("PosSizeExcludeAxes") >>= bPosSizeExcludeAxes;
+ if( bPosSizeExcludeAxes )
+ eMode = DiagramPositioningMode_EXCLUDING;
+ else
+ eMode = DiagramPositioningMode_INCLUDING;
+ }
+ }
+ return eMode;
+}
+
+static void lcl_ensureRange0to1( double& rValue )
+{
+ if(rValue<0.0)
+ rValue=0.0;
+ if(rValue>1.0)
+ rValue=1.0;
+}
+
+bool DiagramHelper::setDiagramPositioning( const rtl::Reference<::chart::ChartModel>& xChartModel,
+ const awt::Rectangle& rPosRect /*100th mm*/ )
+{
+ ControllerLockGuardUNO aCtrlLockGuard( xChartModel );
+
+ bool bChanged = false;
+ awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xChartModel );
+ if( !xDiagram.is() )
+ return bChanged;
+
+ RelativePosition aOldPos;
+ RelativeSize aOldSize;
+ xDiagram->getPropertyValue("RelativePosition" ) >>= aOldPos;
+ xDiagram->getPropertyValue("RelativeSize" ) >>= aOldSize;
+
+ RelativePosition aNewPos;
+ aNewPos.Anchor = drawing::Alignment_TOP_LEFT;
+ aNewPos.Primary = double(rPosRect.X)/double(aPageSize.Width);
+ aNewPos.Secondary = double(rPosRect.Y)/double(aPageSize.Height);
+
+ chart2::RelativeSize aNewSize;
+ aNewSize.Primary = double(rPosRect.Width)/double(aPageSize.Width);
+ aNewSize.Secondary = double(rPosRect.Height)/double(aPageSize.Height);
+
+ lcl_ensureRange0to1( aNewPos.Primary );
+ lcl_ensureRange0to1( aNewPos.Secondary );
+ lcl_ensureRange0to1( aNewSize.Primary );
+ lcl_ensureRange0to1( aNewSize.Secondary );
+ if( (aNewPos.Primary + aNewSize.Primary) > 1.0 )
+ aNewPos.Primary = 1.0 - aNewSize.Primary;
+ if( (aNewPos.Secondary + aNewSize.Secondary) > 1.0 )
+ aNewPos.Secondary = 1.0 - aNewSize.Secondary;
+
+ xDiagram->setPropertyValue( "RelativePosition", uno::Any(aNewPos) );
+ xDiagram->setPropertyValue( "RelativeSize", uno::Any(aNewSize) );
+
+ bChanged = (aOldPos.Anchor!=aNewPos.Anchor) ||
+ (aOldPos.Primary!=aNewPos.Primary) ||
+ (aOldPos.Secondary!=aNewPos.Secondary) ||
+ (aOldSize.Primary!=aNewSize.Primary) ||
+ (aOldSize.Secondary!=aNewSize.Secondary);
+ return bChanged;
+}
+
+awt::Rectangle DiagramHelper::getDiagramRectangleFromModel( const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ awt::Rectangle aRet(-1,-1,-1,-1);
+
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xChartModel );
+ if( !xDiagram.is() )
+ return aRet;
+
+ awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );
+
+ RelativePosition aRelPos;
+ RelativeSize aRelSize;
+ xDiagram->getPropertyValue("RelativePosition" ) >>= aRelPos;
+ xDiagram->getPropertyValue("RelativeSize" ) >>= aRelSize;
+
+ awt::Size aAbsSize(
+ static_cast< sal_Int32 >( aRelSize.Primary * aPageSize.Width ),
+ static_cast< sal_Int32 >( aRelSize.Secondary * aPageSize.Height ));
+
+ awt::Point aAbsPos(
+ static_cast< sal_Int32 >( aRelPos.Primary * aPageSize.Width ),
+ static_cast< sal_Int32 >( aRelPos.Secondary * aPageSize.Height ));
+
+ awt::Point aAbsPosLeftTop = RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( aAbsPos, aAbsSize, aRelPos.Anchor );
+
+ aRet = awt::Rectangle(aAbsPosLeftTop.X, aAbsPosLeftTop.Y, aAbsSize.Width, aAbsSize.Height );
+
+ return aRet;
+}
+
+bool DiagramHelper::switchDiagramPositioningToExcludingPositioning(
+ ChartModel& rModel, bool bResetModifiedState, bool bConvertAlsoFromAutoPositioning )
+{
+ //return true if something was changed
+ const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(GetODFSaneDefaultVersion());
+ if (SvtSaveOptions::ODFSVER_012 < nCurrentODFVersion)
+ {
+ uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( rModel.getFirstDiagram(), uno::UNO_QUERY );
+ if( xDiagramPositioning.is() && ( bConvertAlsoFromAutoPositioning || !xDiagramPositioning->isAutomaticDiagramPositioning() )
+ && !xDiagramPositioning->isExcludingDiagramPositioning() )
+ {
+ ControllerLockGuard aCtrlLockGuard( rModel );
+ bool bModelWasModified = rModel.isModified();
+ xDiagramPositioning->setDiagramPositionExcludingAxes( xDiagramPositioning->calculateDiagramPositionExcludingAxes() );
+ if(bResetModifiedState && !bModelWasModified )
+ rModel.setModified(false);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ErrorBar.cxx b/chart2/source/tools/ErrorBar.cxx
new file mode 100644
index 000000000..f55b0637d
--- /dev/null
+++ b/chart2/source/tools/ErrorBar.cxx
@@ -0,0 +1,470 @@
+/* -*- 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 <ErrorBar.hxx>
+#include <EventListenerHelper.hxx>
+#include <CloneHelper.hxx>
+#include <ModifyListenerHelper.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include <com/sun/star/drawing/LineJoint.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+constexpr OUStringLiteral lcl_aServiceName = u"com.sun.star.comp.chart2.ErrorBar";
+
+bool lcl_isInternalData( const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq )
+{
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xLSeq, uno::UNO_QUERY );
+ return ( xServiceInfo.is() && xServiceInfo->getImplementationName() == "com.sun.star.comp.chart2.LabeledDataSequence" );
+}
+
+const SfxItemPropertySet* GetErrorBarPropertySet()
+{
+ static const SfxItemPropertyMapEntry aErrorBarPropertyMap_Impl[] =
+ {
+ {u"ShowPositiveError",0,cppu::UnoType<bool>::get(), 0, 0},
+ {u"ShowNegativeError",1,cppu::UnoType<bool>::get(), 0, 0},
+ {u"PositiveError",2,cppu::UnoType<double>::get(),0,0},
+ {u"NegativeError",3,cppu::UnoType<double>::get(), 0, 0},
+ {u"PercentageError",4,cppu::UnoType<double>::get(), 0, 0},
+ {u"ErrorBarStyle",5,cppu::UnoType<sal_Int32>::get(),0,0},
+ {u"ErrorBarRangePositive",6,cppu::UnoType<OUString>::get(),0,0}, // read-only for export
+ {u"ErrorBarRangeNegative",7,cppu::UnoType<OUString>::get(),0,0}, // read-only for export
+ {u"Weight",8,cppu::UnoType<double>::get(),0,0},
+ {u"LineStyle",9,cppu::UnoType<css::drawing::LineStyle>::get(),0,0},
+ {u"LineDash",10,cppu::UnoType<drawing::LineDash>::get(),0,0},
+ {u"LineWidth",11,cppu::UnoType<sal_Int32>::get(),0,0},
+ {u"LineColor",12,cppu::UnoType<css::util::Color>::get(),0,0},
+ {u"LineTransparence",13,cppu::UnoType<sal_Int16>::get(),0,0},
+ {u"LineJoint",14,cppu::UnoType<css::drawing::LineJoint>::get(),0,0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static SfxItemPropertySet aPropSet( aErrorBarPropertyMap_Impl );
+ return &aPropSet;
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+ErrorBar::ErrorBar() :
+ mnLineWidth(0),
+ meLineStyle(drawing::LineStyle_SOLID),
+ maLineColor(0),
+ mnLineTransparence(0),
+ meLineJoint(drawing::LineJoint_ROUND),
+ mbShowPositiveError(true),
+ mbShowNegativeError(true),
+ mfPositiveError(0),
+ mfNegativeError(0),
+ mfWeight(1),
+ meStyle(css::chart::ErrorBarStyle::NONE),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{}
+
+ErrorBar::ErrorBar( const ErrorBar & rOther ) :
+ impl::ErrorBar_Base(rOther),
+ maDashName(rOther.maDashName),
+ maLineDash(rOther.maLineDash),
+ mnLineWidth(rOther.mnLineWidth),
+ meLineStyle(rOther.meLineStyle),
+ maLineColor(rOther.maLineColor),
+ mnLineTransparence(rOther.mnLineTransparence),
+ meLineJoint(rOther.meLineJoint),
+ mbShowPositiveError(rOther.mbShowPositiveError),
+ mbShowNegativeError(rOther.mbShowNegativeError),
+ mfPositiveError(rOther.mfPositiveError),
+ mfNegativeError(rOther.mfNegativeError),
+ mfWeight(rOther.mfWeight),
+ meStyle(rOther.meStyle),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ if( ! rOther.m_aDataSequences.empty())
+ {
+ if( lcl_isInternalData( rOther.m_aDataSequences.front()))
+ CloneHelper::CloneRefVector< css::chart2::data::XLabeledDataSequence >(
+ rOther.m_aDataSequences, m_aDataSequences );
+ else
+ m_aDataSequences = rOther.m_aDataSequences;
+ ModifyListenerHelper::addListenerToAllElements( m_aDataSequences, m_xModifyEventForwarder );
+ }
+}
+
+ErrorBar::~ErrorBar()
+{}
+
+uno::Reference< util::XCloneable > SAL_CALL ErrorBar::createClone()
+{
+ return uno::Reference< util::XCloneable >( new ErrorBar( *this ));
+}
+
+// ____ XPropertySet ____
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ErrorBar::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef (
+ new SfxItemPropertySetInfo( GetErrorBarPropertySet()->getPropertyMap() ) );
+ return aRef;
+}
+
+void ErrorBar::setPropertyValue( const OUString& rPropName, const uno::Any& rAny )
+{
+ SolarMutexGuard aGuard;
+
+ if(rPropName == "ErrorBarStyle")
+ rAny >>= meStyle;
+ else if(rPropName == "PositiveError")
+ rAny >>= mfPositiveError;
+ else if(rPropName == "PercentageError")
+ {
+ rAny >>= mfPositiveError;
+ rAny >>= mfNegativeError;
+ }
+ else if(rPropName == "Weight")
+ {
+ rAny >>= mfWeight;
+ }
+ else if(rPropName == "NegativeError")
+ rAny >>= mfNegativeError;
+ else if(rPropName == "ShowPositiveError")
+ rAny >>= mbShowPositiveError;
+ else if(rPropName == "ShowNegativeError")
+ rAny >>= mbShowNegativeError;
+ else if(rPropName == "ErrorBarRangePositive" || rPropName == "ErrorBarRangeNegative")
+ throw beans::UnknownPropertyException("read-only property", static_cast< uno::XWeak*>(this));
+ else if(rPropName == "LineDashName")
+ rAny >>= maDashName;
+ else if(rPropName == "LineDash")
+ rAny >>= maLineDash;
+ else if(rPropName == "LineWidth")
+ rAny >>= mnLineWidth;
+ else if(rPropName == "LineStyle")
+ rAny >>= meLineStyle;
+ else if(rPropName == "LineColor")
+ rAny >>= maLineColor;
+ else if(rPropName == "LineTransparence")
+ rAny >>= mnLineTransparence;
+ else if(rPropName == "LineJoint")
+ rAny >>= meLineJoint;
+
+ m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
+}
+
+namespace {
+
+OUString getSourceRangeStrFromLabeledSequences( const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences, bool bPositive )
+{
+ OUString aDirection;
+ if(bPositive)
+ aDirection = "positive";
+ else
+ aDirection = "negative";
+
+ for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledData : aSequences )
+ {
+ try
+ {
+ if( labeledData.is())
+ {
+ uno::Reference< chart2::data::XDataSequence > xSequence( labeledData->getValues());
+ uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
+ OUString aRole;
+ if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) &&
+ aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
+ {
+ return xSequence->getSourceRangeRepresentation();
+ }
+ }
+ }
+ catch (uno::Exception const &)
+ {
+ // we can't be sure that this is 100% safe and we don't want to kill the export
+ // we should at least check why the exception is thrown
+ TOOLS_WARN_EXCEPTION("chart2", "unexpected exception");
+ }
+ catch (...)
+ {
+ // we can't be sure that this is 100% safe and we don't want to kill the export
+ // we should at least check why the exception is thrown
+ SAL_WARN("chart2", "unexpected exception! ");
+ }
+ }
+
+ return OUString();
+}
+
+}
+
+uno::Any ErrorBar::getPropertyValue(const OUString& rPropName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ if(rPropName == "ErrorBarStyle")
+ aRet <<= meStyle;
+ else if(rPropName == "PositiveError")
+ aRet <<= mfPositiveError;
+ else if(rPropName == "NegativeError")
+ aRet <<= mfNegativeError;
+ else if(rPropName == "PercentageError")
+ aRet <<= mfPositiveError;
+ else if(rPropName == "ShowPositiveError")
+ aRet <<= mbShowPositiveError;
+ else if(rPropName == "ShowNegativeError")
+ aRet <<= mbShowNegativeError;
+ else if(rPropName == "Weight")
+ aRet <<= mfWeight;
+ else if(rPropName == "ErrorBarRangePositive")
+ {
+ OUString aRange;
+ if(meStyle == css::chart::ErrorBarStyle::FROM_DATA)
+ {
+ uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences =
+ getDataSequences();
+
+ aRange = getSourceRangeStrFromLabeledSequences( aSequences, true );
+ }
+
+ aRet <<= aRange;
+ }
+ else if(rPropName == "ErrorBarRangeNegative")
+ {
+ OUString aRange;
+ if(meStyle == css::chart::ErrorBarStyle::FROM_DATA)
+ {
+ uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences =
+ getDataSequences();
+
+ aRange = getSourceRangeStrFromLabeledSequences( aSequences, false );
+ }
+
+ aRet <<= aRange;
+ }
+ else if(rPropName == "LineDashName")
+ aRet <<= maDashName;
+ else if(rPropName == "LineDash")
+ aRet <<= maLineDash;
+ else if(rPropName == "LineWidth")
+ aRet <<= mnLineWidth;
+ else if(rPropName == "LineStyle")
+ aRet <<= meLineStyle;
+ else if(rPropName == "LineColor")
+ aRet <<= maLineColor;
+ else if(rPropName == "LineTransparence")
+ aRet <<= mnLineTransparence;
+ else if(rPropName == "LineJoint")
+ aRet <<= meLineJoint;
+
+ SAL_WARN_IF(!aRet.hasValue(), "chart2", "asked for property value: " << rPropName);
+ return aRet;
+}
+
+beans::PropertyState ErrorBar::getPropertyState( const OUString& rPropName )
+{
+ if(rPropName == "ErrorBarStyle")
+ {
+ if(meStyle == css::chart::ErrorBarStyle::NONE)
+ return beans::PropertyState_DEFAULT_VALUE;
+ return beans::PropertyState_DIRECT_VALUE;
+ }
+ else if(rPropName == "PositiveError")
+ {
+ if(mbShowPositiveError)
+ {
+ switch(meStyle)
+ {
+ case css::chart::ErrorBarStyle::ABSOLUTE:
+ case css::chart::ErrorBarStyle::ERROR_MARGIN:
+ return beans::PropertyState_DIRECT_VALUE;
+ default:
+ break;
+ }
+ }
+ return beans::PropertyState_DEFAULT_VALUE;
+ }
+ else if(rPropName == "NegativeError")
+ {
+ if(mbShowNegativeError)
+ {
+ switch(meStyle)
+ {
+ case css::chart::ErrorBarStyle::ABSOLUTE:
+ case css::chart::ErrorBarStyle::ERROR_MARGIN:
+ return beans::PropertyState_DIRECT_VALUE;
+ default:
+ break;
+ }
+ }
+ return beans::PropertyState_DEFAULT_VALUE;
+ }
+ else if(rPropName == "PercentageError")
+ {
+ if(meStyle != css::chart::ErrorBarStyle::RELATIVE)
+ return beans::PropertyState_DEFAULT_VALUE;
+ return beans::PropertyState_DIRECT_VALUE;
+ }
+ else if(rPropName == "ShowPositiveError")
+ {
+ // this value should be never default
+ return beans::PropertyState_DIRECT_VALUE;
+ }
+ else if(rPropName == "ShowNegativeError")
+ {
+ // this value should be never default
+ return beans::PropertyState_DIRECT_VALUE;
+ }
+ else if(rPropName == "ErrorBarRangePositive")
+ {
+ if(meStyle == css::chart::ErrorBarStyle::FROM_DATA && mbShowPositiveError)
+ return beans::PropertyState_DIRECT_VALUE;
+ return beans::PropertyState_DEFAULT_VALUE;
+ }
+ else if(rPropName == "ErrorBarRangeNegative")
+ {
+ if(meStyle == css::chart::ErrorBarStyle::FROM_DATA && mbShowNegativeError)
+ return beans::PropertyState_DIRECT_VALUE;
+ return beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ return beans::PropertyState_DIRECT_VALUE;
+}
+
+uno::Sequence< beans::PropertyState > ErrorBar::getPropertyStates( const uno::Sequence< OUString >& rPropNames )
+{
+ uno::Sequence< beans::PropertyState > aRet( rPropNames.getLength() );
+ auto aRetRange = asNonConstRange(aRet);
+ for(sal_Int32 i = 0; i < rPropNames.getLength(); ++i)
+ {
+ aRetRange[i] = getPropertyState(rPropNames[i]);
+ }
+ return aRet;
+}
+
+void ErrorBar::setPropertyToDefault( const OUString& )
+{
+ //keep them unimplemented for now
+}
+
+uno::Any ErrorBar::getPropertyDefault( const OUString& )
+{
+ //keep them unimplemented for now
+ return uno::Any();
+}
+
+void ErrorBar::addPropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& )
+{
+}
+
+void ErrorBar::removePropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& )
+{
+}
+
+void ErrorBar::addVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& )
+{
+}
+
+void ErrorBar::removeVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& )
+{
+}
+
+// ____ XModifyBroadcaster ____
+void SAL_CALL ErrorBar::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL ErrorBar::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+// ____ XModifyListener ____
+void SAL_CALL ErrorBar::modified( const lang::EventObject& aEvent )
+{
+ m_xModifyEventForwarder->modified( aEvent );
+}
+
+// ____ XEventListener (base of XModifyListener) ____
+void SAL_CALL ErrorBar::disposing( const lang::EventObject& /* Source */ )
+{
+ // nothing
+}
+
+// ____ XDataSink ____
+void SAL_CALL ErrorBar::setData( const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aData )
+{
+ ModifyListenerHelper::removeListenerFromAllElements( m_aDataSequences, m_xModifyEventForwarder );
+ EventListenerHelper::removeListenerFromAllElements( m_aDataSequences, this );
+ m_aDataSequences = comphelper::sequenceToContainer<tDataSequenceContainer>( aData );
+ EventListenerHelper::addListenerToAllElements( m_aDataSequences, this );
+ ModifyListenerHelper::addListenerToAllElements( m_aDataSequences, m_xModifyEventForwarder );
+}
+
+// ____ XDataSource ____
+uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL ErrorBar::getDataSequences()
+{
+ return comphelper::containerToSequence( m_aDataSequences );
+}
+
+OUString SAL_CALL ErrorBar::getImplementationName()
+{
+ return lcl_aServiceName;
+}
+
+sal_Bool SAL_CALL ErrorBar::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ErrorBar::getSupportedServiceNames()
+{
+ return {
+ lcl_aServiceName,
+ "com.sun.star.chart2.ErrorBar"
+ };
+}
+
+// needed by MSC compiler
+using impl::ErrorBar_Base;
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_ErrorBar_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::ErrorBar);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ExplicitCategoriesProvider.cxx b/chart2/source/tools/ExplicitCategoriesProvider.cxx
new file mode 100644
index 000000000..2a4eee8a7
--- /dev/null
+++ b/chart2/source/tools/ExplicitCategoriesProvider.cxx
@@ -0,0 +1,564 @@
+/* -*- 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 <ExplicitCategoriesProvider.hxx>
+#include <DiagramHelper.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <DataSourceHelper.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <NumberFormatterWrapper.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <DataSeries.hxx>
+
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <limits>
+
+namespace chart
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using std::vector;
+
+ExplicitCategoriesProvider::ExplicitCategoriesProvider( const rtl::Reference< BaseCoordinateSystem >& xCooSysModel
+ , ChartModel& rModel )
+ : m_bDirty(true)
+ , m_xCooSysModel( xCooSysModel.get() )
+ , mrModel(rModel)
+ , m_bIsExplicitCategoriesInited(false)
+ , m_bIsDateAxis(false)
+ , m_bIsAutoDate(false)
+{
+ try
+ {
+ if( xCooSysModel.is() )
+ {
+ // TODO: handle different category names on the primary and secondary category axis.
+ rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
+ if( xAxis.is() )
+ {
+ ScaleData aScale( xAxis->getScaleData() );
+ m_xOriginalCategories = aScale.Categories;
+ m_bIsAutoDate = (aScale.AutoDateAxis && aScale.AxisType==chart2::AxisType::CATEGORY);
+ m_bIsDateAxis = (aScale.AxisType == chart2::AxisType::DATE || m_bIsAutoDate);
+ }
+ }
+
+ if( m_xOriginalCategories.is() )
+ {
+ uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() );
+
+ OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) );
+ if( xDataProvider.is() && !aCategoriesRange.isEmpty() )
+ {
+ const bool bFirstCellAsLabel = false;
+ const bool bHasCategories = false;
+ const uno::Sequence< sal_Int32 > aSequenceMapping;
+
+ uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource(
+ DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/
+ , bFirstCellAsLabel, bHasCategories ) ) );
+
+ uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource(
+ DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/
+ , bFirstCellAsLabel, bHasCategories ) ) );
+
+ if( xColumnCategoriesSource.is() && xRowCategoriesSource.is() )
+ {
+ Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences();
+ Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences();
+
+ sal_Int32 nColumnCount = aColumns.getLength();
+ sal_Int32 nRowCount = aRows.getLength();
+ if( nColumnCount>1 && nRowCount>1 )
+ {
+ //we have complex categories
+ //->split them in the direction of the first series
+ //detect whether the first series is a row or a column
+ bool bSeriesUsesColumns = true;
+ std::vector< rtl::Reference< DataSeries > > aSeries = ChartModelHelper::getDataSeries( &mrModel );
+ if( !aSeries.empty() )
+ {
+ rtl::Reference< DataSeries > xSeriesSource = aSeries.front();
+ OUString aStringDummy;
+ bool bDummy;
+ uno::Sequence< sal_Int32 > aSeqDummy;
+ DataSourceHelper::readArguments( xDataProvider->detectArguments( xSeriesSource),
+ aStringDummy, aSeqDummy, bSeriesUsesColumns, bDummy, bDummy );
+ }
+ if( bSeriesUsesColumns )
+ m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aColumns);
+ else
+ m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aRows);
+ }
+ }
+ }
+ if( m_aSplitCategoriesList.empty() )
+ {
+ m_aSplitCategoriesList = { m_xOriginalCategories };
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
+{
+}
+
+Reference< chart2::data::XDataSequence > ExplicitCategoriesProvider::getOriginalCategories()
+{
+ if( m_xOriginalCategories.is() )
+ return m_xOriginalCategories->getValues();
+ return nullptr;
+}
+
+bool ExplicitCategoriesProvider::hasComplexCategories() const
+{
+ return m_aSplitCategoriesList.size() > 1;
+}
+
+sal_Int32 ExplicitCategoriesProvider::getCategoryLevelCount() const
+{
+ sal_Int32 nCount = m_aSplitCategoriesList.size();
+ if(!nCount)
+ nCount = 1;
+ return nCount;
+}
+
+static std::vector<sal_Int32> lcl_getLimitingBorders( const std::vector< ComplexCategory >& rComplexCategories )
+{
+ std::vector<sal_Int32> aLimitingBorders;
+ sal_Int32 nBorderIndex = 0; /*border below the index*/
+ for (auto const& complexCategory : rComplexCategories)
+ {
+ nBorderIndex += complexCategory.Count;
+ aLimitingBorders.push_back(nBorderIndex);
+ }
+ return aLimitingBorders;
+}
+
+void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence< OUString >& rOutTexts, const uno::Sequence< uno::Any >& rInAnys, ChartModel& rModel )
+{
+ sal_Int32 nCount = rInAnys.getLength();
+ if(!nCount)
+ return;
+ rOutTexts.realloc(nCount);
+ auto pOutTexts = rOutTexts.getArray();
+
+ sal_Int32 nAxisNumberFormat = 0;
+ rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
+ if( xCooSysModel.is() )
+ {
+ rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
+ nAxisNumberFormat = AxisHelper::getExplicitNumberFormatKeyForAxis(
+ xAxis, xCooSysModel, &rModel, false );
+ }
+
+ Color nLabelColor;
+ bool bColorChanged = false;
+
+ NumberFormatterWrapper aNumberFormatterWrapper( rModel.getNumberFormatsSupplier() );
+
+ for(sal_Int32 nN=0;nN<nCount;nN++)
+ {
+ OUString aText;
+ uno::Any aAny = rInAnys[nN];
+ if( aAny.hasValue() )
+ {
+ double fDouble = 0;
+ if( aAny>>=fDouble )
+ {
+ if( !std::isnan(fDouble) )
+ aText = aNumberFormatterWrapper.getFormattedString(
+ nAxisNumberFormat, fDouble, nLabelColor, bColorChanged );
+ }
+ else
+ {
+ aAny>>=aText;
+ }
+ }
+ pOutTexts[nN] = aText;
+ }
+}
+
+SplitCategoriesProvider::~SplitCategoriesProvider()
+{
+}
+
+namespace {
+
+class SplitCategoriesProvider_ForLabeledDataSequences : public SplitCategoriesProvider
+{
+public:
+
+ explicit SplitCategoriesProvider_ForLabeledDataSequences(
+ const std::vector< Reference< data::XLabeledDataSequence> >& rSplitCategoriesList
+ , ChartModel& rModel )
+ : m_rSplitCategoriesList( rSplitCategoriesList )
+ , mrModel( rModel )
+ {}
+
+ virtual sal_Int32 getLevelCount() const override;
+ virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const override;
+
+private:
+ const std::vector< Reference< data::XLabeledDataSequence> >& m_rSplitCategoriesList;
+
+ ChartModel& mrModel;
+};
+
+}
+
+sal_Int32 SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
+{
+ return m_rSplitCategoriesList.size();
+}
+uno::Sequence< OUString > SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel ) const
+{
+ uno::Sequence< OUString > aRet;
+ Reference< data::XLabeledDataSequence > xLabeledDataSequence( m_rSplitCategoriesList[nLevel] );
+ if( xLabeledDataSequence.is() )
+ {
+ uno::Reference< data::XDataSequence > xDataSequence( xLabeledDataSequence->getValues() );
+ if( xDataSequence.is() )
+ ExplicitCategoriesProvider::convertCategoryAnysToText( aRet, xDataSequence->getData(), mrModel );
+ }
+ return aRet;
+}
+
+static std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(
+ const uno::Sequence< OUString >& rStrings
+ , const std::vector<sal_Int32>& rLimitingBorders, bool bCreateSingleCategories )
+{
+ std::vector< ComplexCategory > aResult;
+
+ sal_Int32 nMaxCount = rStrings.getLength();
+ OUString aPrevious;
+ sal_Int32 nCurrentCount=0;
+ for( sal_Int32 nN=0; nN<nMaxCount; nN++ )
+ {
+ const OUString& aCurrent = rStrings[nN];
+ if( bCreateSingleCategories || std::find( rLimitingBorders.begin(), rLimitingBorders.end(), nN ) != rLimitingBorders.end() )
+ {
+ aResult.emplace_back(aPrevious,nCurrentCount );
+ nCurrentCount=1;
+ aPrevious = aCurrent;
+ }
+ else
+ {
+ // Empty value is interpreted as a continuation of the previous
+ // category. Note that having the same value as the previous one
+ // does not equate to a continuation of the category.
+
+ if (aCurrent.isEmpty())
+ ++nCurrentCount;
+ else
+ {
+ aResult.emplace_back(aPrevious,nCurrentCount );
+ nCurrentCount=1;
+ aPrevious = aCurrent;
+ }
+ }
+ }
+ if( nCurrentCount )
+ aResult.emplace_back(aPrevious,nCurrentCount );
+
+ return aResult;
+}
+
+static sal_Int32 lcl_getCategoryCount( std::vector< ComplexCategory >& rComplexCategories )
+{
+ sal_Int32 nCount = 0;
+ for (auto const& complexCategory : rComplexCategories)
+ nCount+=complexCategory.Count;
+ return nCount;
+}
+
+static Sequence< OUString > lcl_getExplicitSimpleCategories(
+ const SplitCategoriesProvider& rSplitCategoriesProvider,
+ std::vector< std::vector< ComplexCategory > >& rComplexCats )
+{
+ Sequence< OUString > aRet;
+
+ rComplexCats.clear();
+ sal_Int32 nLCount = rSplitCategoriesProvider.getLevelCount();
+ for( sal_Int32 nL = 0; nL < nLCount; nL++ )
+ {
+ std::vector<sal_Int32> aLimitingBorders;
+ if(nL>0)
+ aLimitingBorders = lcl_getLimitingBorders( rComplexCats.back() );
+ rComplexCats.push_back( lcl_DataSequenceToComplexCategoryVector(
+ rSplitCategoriesProvider.getStringsForLevel(nL), aLimitingBorders, nL==(nLCount-1) ) );
+ }
+
+ //ensure that the category count is the same on each level
+ sal_Int32 nMaxCategoryCount = 0;
+ {
+ for (auto & complexCat : rComplexCats)
+ {
+ sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
+ nMaxCategoryCount = std::max( nCurrentCount, nMaxCategoryCount );
+ }
+ for (auto & complexCat : rComplexCats)
+ {
+ if ( !complexCat.empty() )
+ {
+ sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
+ if( nCurrentCount< nMaxCategoryCount )
+ {
+ ComplexCategory& rComplexCategory = complexCat.back();
+ rComplexCategory.Count += (nMaxCategoryCount-nCurrentCount);
+ }
+ }
+ }
+ }
+
+ //create a list with an element for every index
+ std::vector< std::vector< ComplexCategory > > aComplexCatsPerIndex;
+ for (auto const& complexCat : rComplexCats)
+ {
+ std::vector< ComplexCategory > aSingleLevel;
+ for (auto const& elem : complexCat)
+ {
+ sal_Int32 nCount = elem.Count;
+ while( nCount-- )
+ aSingleLevel.push_back(elem);
+ }
+ aComplexCatsPerIndex.push_back( aSingleLevel );
+ }
+
+ if(nMaxCategoryCount)
+ {
+ aRet.realloc(nMaxCategoryCount);
+ auto pRet = aRet.getArray();
+ for(sal_Int32 nN=0; nN<nMaxCategoryCount; nN++)
+ {
+ OUStringBuffer aText;
+ for (auto const& complexCatPerIndex : aComplexCatsPerIndex)
+ {
+ if ( o3tl::make_unsigned(nN) < complexCatPerIndex.size() )
+ {
+ OUString aAddText = complexCatPerIndex[nN].Text;
+ if( !aAddText.isEmpty() )
+ {
+ if(!aText.isEmpty())
+ aText.append(" ");
+ aText.append(aAddText);
+ }
+ }
+ }
+ pRet[nN]=aText.makeStringAndClear();
+ }
+ }
+ return aRet;
+}
+
+Sequence< OUString > ExplicitCategoriesProvider::getExplicitSimpleCategories(
+ const SplitCategoriesProvider& rSplitCategoriesProvider )
+{
+ vector< vector< ComplexCategory > > aComplexCats;
+ return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider, aComplexCats );
+}
+
+static bool lcl_fillDateCategories( const uno::Reference< data::XDataSequence >& xDataSequence, std::vector< double >& rDateCategories, bool bIsAutoDate, ChartModel& rModel )
+{
+ bool bOnlyDatesFound = true;
+ bool bAnyDataFound = false;
+
+ if( xDataSequence.is() )
+ {
+ uno::Sequence< uno::Any > aValues = xDataSequence->getData();
+ sal_Int32 nCount = aValues.getLength();
+ rDateCategories.reserve(nCount);
+ Reference< util::XNumberFormats > xNumberFormats( rModel.getNumberFormats() );
+
+ bool bOwnData = false;
+ bool bOwnDataAnddAxisHasAnyFormat = false;
+ bool bOwnDataAnddAxisHasDateFormat = false;
+ rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
+ if( xCooSysModel.is() )
+ {
+ if( rModel.hasInternalDataProvider() )
+ {
+ bOwnData = true;
+ rtl::Reference< Axis > xAxisProps = xCooSysModel->getAxisByDimension2(0,0);
+ sal_Int32 nAxisNumberFormat = 0;
+ if (xAxisProps.is() && (xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nAxisNumberFormat))
+ {
+ bOwnDataAnddAxisHasAnyFormat = true;
+ bOwnDataAnddAxisHasDateFormat = DiagramHelper::isDateNumberFormat( nAxisNumberFormat, xNumberFormats );
+ }
+ }
+ }
+
+ for(sal_Int32 nN=0;nN<nCount;nN++)
+ {
+ bool bIsDate = false;
+ if( bIsAutoDate )
+ {
+ if( bOwnData )
+ bIsDate = !bOwnDataAnddAxisHasAnyFormat || bOwnDataAnddAxisHasDateFormat;
+ else
+ bIsDate = DiagramHelper::isDateNumberFormat( xDataSequence->getNumberFormatKeyByIndex( nN ), xNumberFormats );
+ }
+ else
+ bIsDate = true;
+
+ bool bContainsEmptyString = false;
+ uno::Any aAny = aValues[nN];
+ if( aAny.hasValue() )
+ {
+ OUString aTest;
+ double fTest = 0;
+ bool bContainsNan = false;
+ if( (aAny>>=aTest) && aTest.isEmpty() ) //empty String
+ bContainsEmptyString = true;
+ else if( (aAny>>=fTest) && std::isnan(fTest) )
+ bContainsNan = true;
+
+ if( !bContainsEmptyString && !bContainsNan )
+ bAnyDataFound = true;
+ }
+ double aDate( 1.0 );
+ if( bIsDate && (aAny >>= aDate) )
+ rDateCategories.push_back( aDate );
+ else
+ {
+ if( aAny.hasValue() && !bContainsEmptyString )//empty string does not count as non date value!
+ bOnlyDatesFound=false;
+ rDateCategories.push_back( std::numeric_limits<double>::quiet_NaN() );
+ }
+ }
+ std::sort( rDateCategories.begin(), rDateCategories.end() );
+ }
+
+ return bAnyDataFound && bOnlyDatesFound;
+}
+
+void ExplicitCategoriesProvider::init()
+{
+ if( !m_bDirty )
+ return;
+
+ m_aComplexCats.clear();//not one per index
+ m_aDateCategories.clear();
+
+ if( m_xOriginalCategories.is() )
+ {
+ if( !hasComplexCategories() )
+ {
+ if(m_bIsDateAxis)
+ {
+ if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel.get(), 0 ), 0 ) )
+ m_bIsDateAxis = lcl_fillDateCategories( m_xOriginalCategories->getValues(), m_aDateCategories, m_bIsAutoDate, mrModel );
+ else
+ m_bIsDateAxis = false;
+ }
+ }
+ else
+ {
+ m_bIsDateAxis = false;
+ }
+ }
+ else
+ m_bIsDateAxis=false;
+ m_bDirty = false;
+}
+
+Sequence< OUString > const & ExplicitCategoriesProvider::getSimpleCategories()
+{
+ if( !m_bIsExplicitCategoriesInited )
+ {
+ init();
+ m_aExplicitCategories.realloc(0);
+ if( m_xOriginalCategories.is() )
+ {
+ if( !hasComplexCategories() )
+ {
+ uno::Reference< data::XDataSequence > xDataSequence( m_xOriginalCategories->getValues() );
+ if( xDataSequence.is() )
+ ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories, xDataSequence->getData(), mrModel );
+ }
+ else
+ {
+ m_aExplicitCategories = lcl_getExplicitSimpleCategories(
+ SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList, mrModel ), m_aComplexCats );
+ }
+ }
+ if(!m_aExplicitCategories.hasElements())
+ m_aExplicitCategories = DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel.get() );
+ m_bIsExplicitCategoriesInited = true;
+ }
+ return m_aExplicitCategories;
+}
+
+const std::vector<ComplexCategory>* ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel )
+{
+ init();
+ sal_Int32 nMaxIndex = m_aComplexCats.size()-1;
+ if (nLevel >= 0 && nLevel <= nMaxIndex)
+ return &m_aComplexCats[nMaxIndex-nLevel];
+ return nullptr;
+}
+
+OUString ExplicitCategoriesProvider::getCategoryByIndex(
+ const rtl::Reference< BaseCoordinateSystem >& xCooSysModel
+ , ChartModel& rModel
+ , sal_Int32 nIndex )
+{
+ if( xCooSysModel.is())
+ {
+ ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSysModel, rModel );
+ Sequence< OUString > aCategories( aExplicitCategoriesProvider.getSimpleCategories());
+ if( nIndex < aCategories.getLength())
+ return aCategories[ nIndex ];
+ }
+ return OUString();
+}
+
+bool ExplicitCategoriesProvider::isDateAxis()
+{
+ init();
+ return m_bIsDateAxis;
+}
+
+const std::vector< double >& ExplicitCategoriesProvider::getDateCategories()
+{
+ init();
+ return m_aDateCategories;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ExponentialRegressionCurveCalculator.cxx b/chart2/source/tools/ExponentialRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..9c41822d3
--- /dev/null
+++ b/chart2/source/tools/ExponentialRegressionCurveCalculator.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 <sal/config.h>
+
+#include <limits>
+#include <string_view>
+
+#include <ExponentialRegressionCurveCalculator.hxx>
+#include <RegressionCalculationHelper.hxx>
+#include <SpecialCharacters.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+ExponentialRegressionCurveCalculator::ExponentialRegressionCurveCalculator()
+ : m_fLogSlope(std::numeric_limits<double>::quiet_NaN())
+ , m_fLogIntercept(std::numeric_limits<double>::quiet_NaN())
+ , m_fSign(1.0)
+{
+}
+
+ExponentialRegressionCurveCalculator::~ExponentialRegressionCurveCalculator()
+{}
+
+// ____ XRegressionCurveCalculator ____
+void SAL_CALL ExponentialRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& aXValues,
+ const uno::Sequence< double >& aYValues )
+{
+ RegressionCalculationHelper::tDoubleVectorPair aValues(
+ RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValidAndYPositive()));
+ m_fSign = 1.0;
+
+ size_t nMax = aValues.first.size();
+ if( nMax <= 1 ) // at least 2 points
+ {
+ aValues = RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValidAndYNegative());
+ nMax = aValues.first.size();
+ if( nMax <= 1 )
+ {
+ m_fLogSlope = std::numeric_limits<double>::quiet_NaN();
+ m_fLogIntercept = std::numeric_limits<double>::quiet_NaN();
+ m_fCorrelationCoefficient = std::numeric_limits<double>::quiet_NaN();// actual it is coefficient of determination
+ return;
+ }
+ m_fSign = -1.0;
+ }
+
+ double fAverageX = 0.0, fAverageY = 0.0;
+ double fLogIntercept = ( mForceIntercept && (m_fSign * mInterceptValue)>0 ) ? log(m_fSign * mInterceptValue) : 0.0;
+ std::vector<double> yVector;
+ yVector.resize(nMax, 0.0);
+
+ size_t i = 0;
+ for( i = 0; i < nMax; ++i )
+ {
+ double yValue = log( m_fSign *aValues.second[i] );
+ if (mForceIntercept)
+ {
+ yValue -= fLogIntercept;
+ }
+ else
+ {
+ fAverageX += aValues.first[i];
+ fAverageY += yValue;
+ }
+ yVector[i] = yValue;
+ }
+
+ const double fN = static_cast< double >( nMax );
+ fAverageX /= fN;
+ fAverageY /= fN;
+
+ double fQx = 0.0, fQy = 0.0, fQxy = 0.0;
+ for( i = 0; i < nMax; ++i )
+ {
+ double fDeltaX = aValues.first[i] - fAverageX;
+ double fDeltaY = yVector[i] - fAverageY;
+
+ fQx += fDeltaX * fDeltaX;
+ fQy += fDeltaY * fDeltaY;
+ fQxy += fDeltaX * fDeltaY;
+ }
+
+ m_fLogSlope = fQxy / fQx;
+ m_fLogIntercept = mForceIntercept ? fLogIntercept : fAverageY - m_fLogSlope * fAverageX;
+ m_fCorrelationCoefficient = fQxy / sqrt( fQx * fQy );
+}
+
+double SAL_CALL ExponentialRegressionCurveCalculator::getCurveValue( double x )
+{
+ if( ! ( std::isnan( m_fLogSlope ) ||
+ std::isnan( m_fLogIntercept )))
+ {
+ return m_fSign * exp(m_fLogIntercept + x * m_fLogSlope);
+ }
+
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL ExponentialRegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const uno::Reference< chart2::XScaling >& xScalingX,
+ const uno::Reference< chart2::XScaling >& xScalingY,
+ sal_Bool bMaySkipPointsInCalculation )
+{
+ if( bMaySkipPointsInCalculation &&
+ isLinearScaling( xScalingX ) &&
+ isLogarithmicScaling( xScalingY ))
+ {
+ // optimize result
+ uno::Sequence< geometry::RealPoint2D > aResult{ { min, getCurveValue( min ) },
+ { max, getCurveValue( max ) } };
+
+ return aResult;
+ }
+
+ return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
+}
+
+OUString ExponentialRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey, sal_Int32* pFormulaMaxWidth /* = nullptr */ ) const
+{
+ double fIntercept = exp(m_fLogIntercept);
+ bool bHasSlope = !rtl::math::approxEqual( exp(m_fLogSlope), 1.0 );
+ bool bHasLogSlope = !rtl::math::approxEqual( fabs(m_fLogSlope), 1.0 );
+ bool bHasIntercept = !rtl::math::approxEqual( fIntercept, 1.0 ) && fIntercept != 0.0;
+
+ OUStringBuffer aBuf( mYName + " = " );
+ sal_Int32 nLineLength = aBuf.getLength();
+ sal_Int32 nValueLength=0;
+ if ( pFormulaMaxWidth && *pFormulaMaxWidth > 0 )
+ { // count characters different from coefficients
+ sal_Int32 nCharMin = nLineLength + 10 + mXName.getLength(); // 10 = "exp( ", " x )" + 2 extra characters
+ if ( m_fSign < 0.0 )
+ nCharMin += 2;
+ if ( fIntercept == 0.0 || ( !bHasSlope && m_fLogIntercept != 0.0 ) )
+ nCharMin += 3; // " + " special case where equation is written exp( a + b x )
+ if ( ( bHasIntercept || fIntercept == 0.0 || ( !bHasSlope && m_fLogIntercept != 0.0 ) ) &&
+ bHasLogSlope )
+ nValueLength = ( *pFormulaMaxWidth - nCharMin ) / 2;
+ else
+ nValueLength = *pFormulaMaxWidth - nCharMin;
+ if ( nValueLength <= 0 )
+ nValueLength = 1;
+ }
+ // temporary buffer
+ OUStringBuffer aTmpBuf("");
+ // if nValueLength not calculated then nullptr
+ sal_Int32* pValueLength = nValueLength ? &nValueLength : nullptr;
+ if ( m_fSign < 0.0 )
+ aTmpBuf.append( OUStringChar(aMinusSign) + " " );
+ if ( bHasIntercept )
+ {
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, fIntercept, pValueLength );
+ if ( aValueString != "1" ) // aValueString may be rounded to 1 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString + " " );
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+ aTmpBuf.truncate();
+ }
+ }
+ aTmpBuf.append( "exp( " );
+ if ( !bHasIntercept )
+ {
+ if ( fIntercept == 0.0 || // underflow, a true zero is impossible
+ ( !bHasSlope && m_fLogIntercept != 0.0 ) ) // show logarithmic output, if intercept and slope both are near one
+ { // otherwise drop output of intercept, which is 1 here
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, m_fLogIntercept, pValueLength );
+ if ( aValueString != "0" ) // aValueString may be rounded to 0 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString ).append( (m_fLogSlope < 0.0) ? std::u16string_view(u" ") : std::u16string_view(u" + ") );
+ }
+ }
+ }
+ if ( m_fLogSlope < 0.0 )
+ aTmpBuf.append( OUStringChar(aMinusSign) + " " );
+ if ( bHasLogSlope )
+ {
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fLogSlope), pValueLength );
+ if ( aValueString != "1" ) // aValueString may be rounded to 1 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString + " " );
+ }
+ }
+ aTmpBuf.append( mXName + " )");
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/FillProperties.cxx b/chart2/source/tools/FillProperties.cxx
new file mode 100644
index 000000000..d0c888d74
--- /dev/null
+++ b/chart2/source/tools/FillProperties.cxx
@@ -0,0 +1,200 @@
+/* -*- 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 <FillProperties.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/RectanglePoint.hpp>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace chart
+{
+
+namespace
+{
+
+void lcl_AddPropertiesToVector_without_BitmapProperties( std::vector< css::beans::Property > & rOutProperties )
+{
+ rOutProperties.emplace_back( "FillStyle",
+ FillProperties::PROP_FILL_STYLE,
+ cppu::UnoType<drawing::FillStyle>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillColor",
+ FillProperties::PROP_FILL_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID // "maybe auto"
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillTransparence",
+ FillProperties::PROP_FILL_TRANSPARENCE,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillTransparenceGradientName",
+ FillProperties::PROP_FILL_TRANSPARENCE_GRADIENT_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillGradientName",
+ FillProperties::PROP_FILL_GRADIENT_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillGradientStepCount",
+ FillProperties::PROP_FILL_GRADIENT_STEPCOUNT,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+
+ rOutProperties.emplace_back( "FillHatchName",
+ FillProperties::PROP_FILL_HATCH_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ //bitmap properties see lcl_AddPropertiesToVector_only_BitmapProperties()
+
+ rOutProperties.emplace_back( "FillBackground",
+ FillProperties::PROP_FILL_BACKGROUND,
+ cppu::UnoType<sal_Bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+}
+
+void lcl_AddPropertiesToVector_only_BitmapProperties( std::vector< css::beans::Property > & rOutProperties )
+{
+ rOutProperties.emplace_back( "FillBitmapName",
+ FillProperties::PROP_FILL_BITMAP_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapOffsetX",
+ FillProperties::PROP_FILL_BITMAP_OFFSETX,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapOffsetY",
+ FillProperties::PROP_FILL_BITMAP_OFFSETY,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapPositionOffsetX",
+ FillProperties::PROP_FILL_BITMAP_POSITION_OFFSETX,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapPositionOffsetY",
+ FillProperties::PROP_FILL_BITMAP_POSITION_OFFSETY,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapRectanglePoint",
+ FillProperties::PROP_FILL_BITMAP_RECTANGLEPOINT,
+ cppu::UnoType<drawing::RectanglePoint>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapLogicalSize",
+ FillProperties::PROP_FILL_BITMAP_LOGICALSIZE,
+ cppu::UnoType<sal_Bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapSizeX",
+ FillProperties::PROP_FILL_BITMAP_SIZEX,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapSizeY",
+ FillProperties::PROP_FILL_BITMAP_SIZEY,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "FillBitmapMode",
+ FillProperties::PROP_FILL_BITMAP_MODE,
+ cppu::UnoType<drawing::BitmapMode>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+}
+
+void lcl_AddDefaultsToMap_without_BitmapProperties(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, FillProperties::PROP_FILL_STYLE, drawing::FillStyle_SOLID );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, FillProperties::PROP_FILL_COLOR, 0xd9d9d9 ); // gray85
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, FillProperties::PROP_FILL_TRANSPARENCE, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, FillProperties::PROP_FILL_BACKGROUND, false );
+}
+
+void lcl_AddDefaultsToMap_only_BitmapProperties(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, FillProperties::PROP_FILL_BITMAP_OFFSETX, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, FillProperties::PROP_FILL_BITMAP_OFFSETY, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, FillProperties::PROP_FILL_BITMAP_POSITION_OFFSETX, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, FillProperties::PROP_FILL_BITMAP_POSITION_OFFSETY, 0 );
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, FillProperties::PROP_FILL_BITMAP_RECTANGLEPOINT, drawing::RectanglePoint_MIDDLE_MIDDLE );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, FillProperties::PROP_FILL_BITMAP_LOGICALSIZE, true );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, FillProperties::PROP_FILL_BITMAP_SIZEX, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, FillProperties::PROP_FILL_BITMAP_SIZEY, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, FillProperties::PROP_FILL_BITMAP_MODE, drawing::BitmapMode_REPEAT );
+}
+
+}//end anonymous namespace
+
+void FillProperties::AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ // Fill Properties see service drawing::FillProperties
+ lcl_AddPropertiesToVector_without_BitmapProperties( rOutProperties );
+ lcl_AddPropertiesToVector_only_BitmapProperties( rOutProperties );
+}
+
+void FillProperties::AddDefaultsToMap(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ lcl_AddDefaultsToMap_without_BitmapProperties( rOutMap );
+ lcl_AddDefaultsToMap_only_BitmapProperties( rOutMap );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/FormattedStringHelper.cxx b/chart2/source/tools/FormattedStringHelper.cxx
new file mode 100644
index 000000000..755dd532b
--- /dev/null
+++ b/chart2/source/tools/FormattedStringHelper.cxx
@@ -0,0 +1,63 @@
+/* -*- 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 <FormattedStringHelper.hxx>
+#include <com/sun/star/chart2/FormattedString.hpp>
+#include <tools/diagnose_ex.h>
+#include <comphelper/property.hxx>
+
+namespace chart
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+Sequence< Reference< chart2::XFormattedString2 > >
+ FormattedStringHelper::createFormattedStringSequence(
+ const Reference< uno::XComponentContext > & xContext
+ , const OUString & rString
+ , const Reference< beans::XPropertySet > & xTextProperties ) noexcept
+{
+ Reference< XFormattedString2 > xFormStr;
+ try
+ {
+ if( xContext.is() )
+ {
+ xFormStr = chart2::FormattedString::create(xContext);
+
+ xFormStr->setString( rString );
+
+ // set character properties
+ comphelper::copyProperties(
+ xTextProperties, Reference< beans::XPropertySet >( xFormStr, uno::UNO_QUERY_THROW ) );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return Sequence< Reference< XFormattedString2 > >( & xFormStr, 1 );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/InternalData.cxx b/chart2/source/tools/InternalData.cxx
new file mode 100644
index 000000000..b6fc1e090
--- /dev/null
+++ b/chart2/source/tools/InternalData.cxx
@@ -0,0 +1,557 @@
+/* -*- 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 <InternalData.hxx>
+#include <ResId.hxx>
+#include <strings.hrc>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#ifdef DEBUG_CHART2_TOOLS
+#define DEBUG_INTERNAL_DATA 1
+#endif
+
+#ifdef DEBUG_INTERNAL_DATA
+#include <svl/gridprinter.hxx>
+#endif
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+
+using ::com::sun::star::uno::Sequence;
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+namespace chart
+{
+
+namespace
+{
+struct lcl_NumberedStringGenerator
+{
+ lcl_NumberedStringGenerator( const OUString & rStub, const OUString & rWildcard ) :
+ m_aStub( rStub ),
+ m_nCounter( 0 ),
+ m_nStubStartIndex( rStub.indexOf( rWildcard )),
+ m_nWildcardLength( rWildcard.getLength())
+ {
+ }
+ vector< uno::Any > operator()()
+ {
+ return { uno::Any(m_aStub.replaceAt( m_nStubStartIndex, m_nWildcardLength, OUString::number( ++m_nCounter ))) };
+ }
+private:
+ OUString m_aStub;
+ sal_Int32 m_nCounter;
+ const sal_Int32 m_nStubStartIndex;
+ const sal_Int32 m_nWildcardLength;
+};
+
+template< typename T >
+ Sequence< T > lcl_ValarrayToSequence( const std::valarray< T > & rValarray )
+{
+#if defined __GLIBCXX__ && (!defined _GLIBCXX_RELEASE || _GLIBCXX_RELEASE < 12)
+ // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103022
+ if (!size(rValarray))
+ return Sequence<T>();
+#endif
+
+ return comphelper::containerToSequence(rValarray);
+}
+
+} // anonymous namespace
+
+InternalData::InternalData()
+ : m_nColumnCount( 0 )
+ , m_nRowCount( 0 )
+ , m_aRowLabels( 0 )
+ , m_aColumnLabels( 0 )
+{}
+
+const double fDefaultData[] = {
+ 9.10, 3.20, 4.54,
+ 2.40, 8.80, 9.65,
+ 3.10, 1.50, 3.70,
+ 4.30, 9.02, 6.20
+};
+
+void InternalData::createDefaultData()
+{
+ const sal_Int32 nRowCount = 4;
+ const sal_Int32 nColumnCount = 3;
+
+ m_nRowCount = nRowCount;
+ m_nColumnCount = nColumnCount;
+ const sal_Int32 nSize = nColumnCount * nRowCount;
+ // @todo: localize this!
+ const OUString aRowName(SchResId(STR_ROW_LABEL));
+ const OUString aColName(SchResId(STR_COLUMN_LABEL));
+
+ m_aData.resize( nSize );
+ for( sal_Int32 i=0; i<nSize; ++i )
+ m_aData[i] = fDefaultData[i];
+
+ m_aRowLabels.clear();
+ m_aRowLabels.reserve( m_nRowCount );
+ generate_n( back_inserter( m_aRowLabels ), m_nRowCount,
+ lcl_NumberedStringGenerator( aRowName, "%ROWNUMBER" ));
+
+ m_aColumnLabels.clear();
+ m_aColumnLabels.reserve( m_nColumnCount );
+ generate_n( back_inserter( m_aColumnLabels ), m_nColumnCount,
+ lcl_NumberedStringGenerator( aColName, "%COLUMNNUMBER" ));
+}
+
+void InternalData::setData( const Sequence< Sequence< double > >& rDataInRows )
+{
+ m_nRowCount = rDataInRows.getLength();
+ m_nColumnCount = (m_nRowCount ? rDataInRows[0].getLength() : 0);
+
+ if( m_aRowLabels.size() != static_cast< sal_uInt32 >( m_nRowCount ))
+ m_aRowLabels.resize( m_nRowCount );
+ if( m_aColumnLabels.size() != static_cast< sal_uInt32 >( m_nColumnCount ))
+ m_aColumnLabels.resize( m_nColumnCount );
+
+ m_aData.resize( m_nRowCount * m_nColumnCount );
+ // set all values to Nan
+ m_aData = std::numeric_limits<double>::quiet_NaN();
+
+ for( sal_Int32 nRow=0; nRow<m_nRowCount; ++nRow )
+ {
+ int nDataIdx = nRow*m_nColumnCount;
+ const sal_Int32 nMax = std::min( rDataInRows[nRow].getLength(), m_nColumnCount );
+ for( sal_Int32 nCol=0; nCol < nMax; ++nCol )
+ {
+ m_aData[nDataIdx] = rDataInRows[nRow][nCol];
+ nDataIdx += 1;
+ }
+ }
+}
+
+Sequence< Sequence< double > > InternalData::getData() const
+{
+ Sequence< Sequence< double > > aResult( m_nRowCount );
+ auto aResultRange = asNonConstRange(aResult);
+
+ for( sal_Int32 i=0; i<m_nRowCount; ++i )
+ aResultRange[i] = lcl_ValarrayToSequence< tDataType::value_type >(
+ m_aData[ std::slice( i*m_nColumnCount, m_nColumnCount, 1 ) ] );
+
+ return aResult;
+}
+
+Sequence< double > InternalData::getColumnValues( sal_Int32 nColumnIndex ) const
+{
+ if( nColumnIndex >= 0 && nColumnIndex < m_nColumnCount )
+ return lcl_ValarrayToSequence< tDataType::value_type >(
+ m_aData[ std::slice( nColumnIndex, m_nRowCount, m_nColumnCount ) ] );
+ return Sequence< double >();
+}
+Sequence< double > InternalData::getRowValues( sal_Int32 nRowIndex ) const
+{
+ if( nRowIndex >= 0 && nRowIndex < m_nRowCount )
+ return lcl_ValarrayToSequence< tDataType::value_type >(
+ m_aData[ std::slice( nRowIndex*m_nColumnCount, m_nColumnCount, 1 ) ] );
+ return Sequence< double >();
+}
+
+void InternalData::setColumnValues( sal_Int32 nColumnIndex, const vector< double > & rNewData )
+{
+ if( nColumnIndex < 0 )
+ return;
+ enlargeData( nColumnIndex + 1, rNewData.size() );
+
+ tDataType aSlice = m_aData[ std::slice( nColumnIndex, m_nRowCount, m_nColumnCount ) ];
+ for( vector< double >::size_type i = 0; i < rNewData.size(); ++i )
+ aSlice[i] = rNewData[i];
+ m_aData[ std::slice( nColumnIndex, m_nRowCount, m_nColumnCount ) ] = aSlice;
+}
+
+void InternalData::setRowValues( sal_Int32 nRowIndex, const vector< double > & rNewData )
+{
+ if( nRowIndex < 0 )
+ return;
+ enlargeData( rNewData.size(), nRowIndex+1 );
+
+ tDataType aSlice = m_aData[ std::slice( nRowIndex*m_nColumnCount, m_nColumnCount, 1 ) ];
+ for( vector< double >::size_type i = 0; i < rNewData.size(); ++i )
+ aSlice[i] = rNewData[i];
+ m_aData[ std::slice( nRowIndex*m_nColumnCount, m_nColumnCount, 1 ) ]= aSlice;
+}
+
+void InternalData::setComplexColumnLabel( sal_Int32 nColumnIndex, vector< uno::Any >&& rComplexLabel )
+{
+ if( nColumnIndex < 0 )
+ return;
+ if( o3tl::make_unsigned(nColumnIndex) >= m_aColumnLabels.size() )
+ {
+ m_aColumnLabels.resize(nColumnIndex+1);
+ enlargeData( nColumnIndex+1, 0 );
+ }
+ m_aColumnLabels[nColumnIndex] = std::move(rComplexLabel);
+
+ dump();
+}
+
+void InternalData::setComplexRowLabel( sal_Int32 nRowIndex, vector< uno::Any >&& rComplexLabel )
+{
+ if( nRowIndex < 0 )
+ return;
+ if( o3tl::make_unsigned(nRowIndex) >= m_aRowLabels.size() )
+ {
+ m_aRowLabels.resize(nRowIndex+1);
+ enlargeData( 0, nRowIndex+1 );
+ }
+ sal_Int32 nSize = static_cast<sal_Int32>( m_aRowLabels[nRowIndex].size() );
+ if( nSize >= 1 && !rComplexLabel.empty() )
+ {
+ m_aRowLabels[nRowIndex].resize(nSize+1);
+ m_aRowLabels[nRowIndex][nSize] = rComplexLabel[0];
+ }
+ else
+ {
+ m_aRowLabels[nRowIndex] = std::move(rComplexLabel);
+ }
+}
+
+vector< uno::Any > InternalData::getComplexColumnLabel( sal_Int32 nColumnIndex ) const
+{
+ if( nColumnIndex < static_cast< sal_Int32 >( m_aColumnLabels.size() ) )
+ return m_aColumnLabels[nColumnIndex];
+ else
+ return vector< uno::Any >();
+}
+vector< uno::Any > InternalData::getComplexRowLabel( sal_Int32 nRowIndex ) const
+{
+ if( nRowIndex < static_cast< sal_Int32 >( m_aRowLabels.size() ) )
+ return m_aRowLabels[nRowIndex];
+ else
+ return vector< uno::Any >();
+}
+
+void InternalData::swapRowWithNext( sal_Int32 nRowIndex )
+{
+ if( nRowIndex >= m_nRowCount - 1 )
+ return;
+
+ const sal_Int32 nMax = m_nColumnCount;
+ for( sal_Int32 nColIdx=0; nColIdx<nMax; ++nColIdx )
+ {
+ size_t nIndex1 = nColIdx + nRowIndex*m_nColumnCount;
+ size_t nIndex2 = nIndex1 + m_nColumnCount;
+ double fTemp = m_aData[nIndex1];
+ m_aData[nIndex1] = m_aData[nIndex2];
+ m_aData[nIndex2] = fTemp;
+ }
+
+ vector< uno::Any > aTemp( m_aRowLabels[nRowIndex] );
+ m_aRowLabels[nRowIndex] = m_aRowLabels[nRowIndex + 1];
+ m_aRowLabels[nRowIndex + 1] = aTemp;
+}
+
+void InternalData::swapColumnWithNext( sal_Int32 nColumnIndex )
+{
+ if( nColumnIndex >= m_nColumnCount - 1 )
+ return;
+
+ const sal_Int32 nMax = m_nRowCount;
+ for( sal_Int32 nRowIdx=0; nRowIdx<nMax; ++nRowIdx )
+ {
+ size_t nIndex1 = nColumnIndex + nRowIdx*m_nColumnCount;
+ size_t nIndex2 = nIndex1 + 1;
+ double fTemp = m_aData[nIndex1];
+ m_aData[nIndex1] = m_aData[nIndex2];
+ m_aData[nIndex2] = fTemp;
+ }
+
+ vector< uno::Any > aTemp( m_aColumnLabels[nColumnIndex] );
+ m_aColumnLabels[nColumnIndex] = m_aColumnLabels[nColumnIndex + 1];
+ m_aColumnLabels[nColumnIndex + 1] = aTemp;
+}
+
+bool InternalData::enlargeData( sal_Int32 nColumnCount, sal_Int32 nRowCount )
+{
+ sal_Int32 nNewColumnCount( std::max<sal_Int32>( m_nColumnCount, nColumnCount ) );
+ sal_Int32 nNewRowCount( std::max<sal_Int32>( m_nRowCount, nRowCount ) );
+ sal_Int32 nNewSize( nNewColumnCount*nNewRowCount );
+
+ bool bGrow = (nNewSize > m_nColumnCount*m_nRowCount);
+
+ if( bGrow )
+ {
+ tDataType aNewData( std::numeric_limits<double>::quiet_NaN(), nNewSize );
+ // copy old data
+ for( int nCol=0; nCol<m_nColumnCount; ++nCol )
+ static_cast< tDataType >(
+ aNewData[ std::slice( nCol, m_nRowCount, nNewColumnCount ) ] ) =
+ m_aData[ std::slice( nCol, m_nRowCount, m_nColumnCount ) ];
+
+ m_aData.resize( nNewSize );
+ m_aData = aNewData;
+ }
+ m_nColumnCount = nNewColumnCount;
+ m_nRowCount = nNewRowCount;
+ return bGrow;
+}
+
+void InternalData::insertColumn( sal_Int32 nAfterIndex )
+{
+ // note: -1 is allowed, as we insert after the given index
+ OSL_ASSERT( nAfterIndex < m_nColumnCount && nAfterIndex >= -1 );
+ if( nAfterIndex >= m_nColumnCount || nAfterIndex < -1 )
+ return;
+ sal_Int32 nNewColumnCount = m_nColumnCount + 1;
+ sal_Int32 nNewSize( nNewColumnCount * m_nRowCount );
+
+ tDataType aNewData( std::numeric_limits<double>::quiet_NaN(), nNewSize );
+
+ // copy old data
+ int nCol=0;
+ for( ; nCol<=nAfterIndex; ++nCol )
+ aNewData[ std::slice( nCol, m_nRowCount, nNewColumnCount ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( nCol, m_nRowCount, m_nColumnCount ) ] );
+ for( ++nCol; nCol<nNewColumnCount; ++nCol )
+ aNewData[ std::slice( nCol, m_nRowCount, nNewColumnCount ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( nCol - 1, m_nRowCount, m_nColumnCount ) ] );
+
+ m_nColumnCount = nNewColumnCount;
+ m_aData.resize( nNewSize );
+ m_aData = aNewData;
+
+ // labels
+ if( nAfterIndex < static_cast< sal_Int32 >( m_aColumnLabels.size()))
+ m_aColumnLabels.insert( m_aColumnLabels.begin() + (nAfterIndex + 1), vector< uno::Any >(1) );
+
+ dump();
+}
+
+sal_Int32 InternalData::appendColumn()
+{
+ insertColumn( getColumnCount() - 1 );
+ return getColumnCount() - 1;
+}
+
+sal_Int32 InternalData::appendRow()
+{
+ insertRow( getRowCount() - 1 );
+ return getRowCount() - 1;
+}
+
+sal_Int32 InternalData::getRowCount() const
+{
+ return m_nRowCount;
+}
+
+sal_Int32 InternalData::getColumnCount() const
+{
+ return m_nColumnCount;
+}
+
+void InternalData::insertRow( sal_Int32 nAfterIndex )
+{
+ // note: -1 is allowed, as we insert after the given index
+ OSL_ASSERT( nAfterIndex < m_nRowCount && nAfterIndex >= -1 );
+ if( nAfterIndex >= m_nRowCount || nAfterIndex < -1 )
+ return;
+ sal_Int32 nNewRowCount = m_nRowCount + 1;
+ sal_Int32 nNewSize( m_nColumnCount * nNewRowCount );
+
+ tDataType aNewData( std::numeric_limits<double>::quiet_NaN(), nNewSize );
+
+ // copy old data
+ sal_Int32 nIndex = nAfterIndex + 1;
+ aNewData[ std::slice( 0, nIndex * m_nColumnCount, 1 ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( 0, nIndex * m_nColumnCount, 1 ) ] );
+
+ if( nIndex < m_nRowCount )
+ {
+ sal_Int32 nRemainingCount = m_nColumnCount * (m_nRowCount - nIndex);
+ aNewData[ std::slice( (nIndex + 1) * m_nColumnCount, nRemainingCount, 1 ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( nIndex * m_nColumnCount, nRemainingCount, 1 ) ] );
+ }
+
+ m_nRowCount = nNewRowCount;
+ m_aData.resize( nNewSize );
+ m_aData = aNewData;
+
+ // labels
+ if( nAfterIndex < static_cast< sal_Int32 >( m_aRowLabels.size()))
+ m_aRowLabels.insert( m_aRowLabels.begin() + nIndex, vector< uno::Any > (1));
+
+ dump();
+}
+
+void InternalData::deleteColumn( sal_Int32 nAtIndex )
+{
+ OSL_ASSERT( nAtIndex < m_nColumnCount && nAtIndex >= 0 );
+ if( nAtIndex >= m_nColumnCount || m_nColumnCount < 1 || nAtIndex < 0 )
+ return;
+ sal_Int32 nNewColumnCount = m_nColumnCount - 1;
+ sal_Int32 nNewSize( nNewColumnCount * m_nRowCount );
+
+ tDataType aNewData( std::numeric_limits<double>::quiet_NaN(), nNewSize );
+
+ // copy old data
+ int nCol=0;
+ for( ; nCol<nAtIndex; ++nCol )
+ aNewData[ std::slice( nCol, m_nRowCount, nNewColumnCount ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( nCol, m_nRowCount, m_nColumnCount ) ] );
+ for( ; nCol<nNewColumnCount; ++nCol )
+ aNewData[ std::slice( nCol, m_nRowCount, nNewColumnCount ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( nCol + 1, m_nRowCount, m_nColumnCount ) ] );
+
+ m_nColumnCount = nNewColumnCount;
+ m_aData.resize( nNewSize );
+ m_aData = aNewData;
+
+ // labels
+ if( nAtIndex < static_cast< sal_Int32 >( m_aColumnLabels.size()))
+ m_aColumnLabels.erase( m_aColumnLabels.begin() + nAtIndex );
+
+ dump();
+}
+
+void InternalData::deleteRow( sal_Int32 nAtIndex )
+{
+ OSL_ASSERT( nAtIndex < m_nRowCount && nAtIndex >= 0 );
+ if( nAtIndex >= m_nRowCount || m_nRowCount < 1 || nAtIndex < 0 )
+ return;
+ sal_Int32 nNewRowCount = m_nRowCount - 1;
+ sal_Int32 nNewSize( m_nColumnCount * nNewRowCount );
+
+ tDataType aNewData( std::numeric_limits<double>::quiet_NaN(), nNewSize );
+
+ // copy old data
+ sal_Int32 nIndex = nAtIndex;
+ if( nIndex )
+ aNewData[ std::slice( 0, nIndex * m_nColumnCount, 1 ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( 0, nIndex * m_nColumnCount, 1 ) ] );
+
+ if( nIndex < nNewRowCount )
+ {
+ sal_Int32 nRemainingCount = m_nColumnCount * (nNewRowCount - nIndex);
+ aNewData[ std::slice( nIndex * m_nColumnCount, nRemainingCount, 1 ) ] =
+ static_cast< tDataType >(
+ m_aData[ std::slice( (nIndex + 1) * m_nColumnCount, nRemainingCount, 1 ) ] );
+ }
+
+ m_nRowCount = nNewRowCount;
+ m_aData.resize( nNewSize );
+ m_aData = aNewData;
+
+ // labels
+ if( nAtIndex < static_cast< sal_Int32 >( m_aRowLabels.size()))
+ m_aRowLabels.erase( m_aRowLabels.begin() + nAtIndex );
+
+ dump();
+}
+
+void InternalData::setComplexRowLabels( tVecVecAny&& rNewRowLabels )
+{
+ m_aRowLabels = std::move(rNewRowLabels);
+ sal_Int32 nNewRowCount = static_cast< sal_Int32 >( m_aRowLabels.size() );
+ if( nNewRowCount < m_nRowCount )
+ m_aRowLabels.resize( m_nRowCount );
+ else
+ enlargeData( 0, nNewRowCount );
+}
+
+const InternalData::tVecVecAny& InternalData::getComplexRowLabels() const
+{
+ return m_aRowLabels;
+}
+
+void InternalData::setComplexColumnLabels( tVecVecAny&& rNewColumnLabels )
+{
+ m_aColumnLabels = std::move(rNewColumnLabels);
+ sal_Int32 nNewColumnCount = static_cast< sal_Int32 >( m_aColumnLabels.size() );
+ if( nNewColumnCount < m_nColumnCount )
+ m_aColumnLabels.resize( m_nColumnCount );
+ else
+ enlargeData( nNewColumnCount, 0 );
+}
+
+const InternalData::tVecVecAny& InternalData::getComplexColumnLabels() const
+{
+ return m_aColumnLabels;
+}
+
+#ifdef DEBUG_INTERNAL_DATA
+void InternalData::dump() const
+{
+ // Header
+ if (!m_aColumnLabels.empty())
+ {
+ svl::GridPrinter aPrinter(m_aColumnLabels[0].size(), m_aColumnLabels.size(), true);
+ for (size_t nCol = 0; nCol < m_aColumnLabels.size(); ++nCol)
+ {
+ for (size_t nRow = 0; nRow < m_aColumnLabels[nCol].size(); ++nRow)
+ {
+ OUString aStr;
+ if (m_aColumnLabels[nCol].at(nRow) >>= aStr)
+ aPrinter.set(nRow, nCol, aStr);
+ }
+ }
+ aPrinter.print("Header");
+ }
+
+ if (!m_aRowLabels.empty())
+ {
+ svl::GridPrinter aPrinter(m_aRowLabels.size(), m_aRowLabels[0].size(), true);
+ for (size_t nRow = 0; nRow < m_aRowLabels.size(); ++nRow)
+ {
+ for (size_t nCol = 0; nCol < m_aRowLabels[nRow].size(); ++nCol)
+ {
+ OUString aStr;
+ if (m_aRowLabels[nRow].at(nCol) >>= aStr)
+ aPrinter.set(nRow, nCol, aStr);
+ }
+ }
+ aPrinter.print("Row labels");
+ }
+
+ svl::GridPrinter aPrinter(m_nRowCount, m_nColumnCount, true);
+
+ for (sal_Int32 nRow = 0; nRow < m_nRowCount; ++nRow)
+ {
+ tDataType aSlice( m_aData[ std::slice( nRow*m_nColumnCount, m_nColumnCount, 1 ) ] );
+ for (sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol)
+ aPrinter.set(nRow, nCol, OUString::number(aSlice[nCol]));
+ }
+
+ aPrinter.print("Column data");
+}
+#else
+void InternalData::dump() const {}
+#endif
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/InternalDataProvider.cxx b/chart2/source/tools/InternalDataProvider.cxx
new file mode 100644
index 000000000..0c95286e9
--- /dev/null
+++ b/chart2/source/tools/InternalDataProvider.cxx
@@ -0,0 +1,1553 @@
+/* -*- 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 <cstddef>
+#include <iterator>
+
+#include <InternalDataProvider.hxx>
+#include <LabeledDataSequence.hxx>
+#include <DataSource.hxx>
+#include <XMLRangeHelper.hxx>
+#include <CommonFunctors.hxx>
+#include <UncachedDataSequence.hxx>
+#include <DataSourceHelper.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <ExplicitCategoriesProvider.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <DataSeries.hxx>
+
+#include <com/sun/star/chart2/data/XDataSequence.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/property.hxx>
+#include <o3tl/string_view.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <limits>
+#include <vector>
+#include <algorithm>
+
+namespace com::sun::star::chart2 { class XChartDocument; }
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+namespace
+{
+
+constexpr OUStringLiteral lcl_aCategoriesRangeName = u"categories";
+const char lcl_aCategoriesLevelRangeNamePrefix[] = "categoriesL "; //L <-> level
+const char lcl_aCategoriesPointRangeNamePrefix[] = "categoriesP "; //P <-> point
+constexpr OUStringLiteral lcl_aCategoriesRoleName = u"categories";
+const char lcl_aLabelRangePrefix[] = "label ";
+constexpr OUStringLiteral lcl_aCompleteRange = u"all";
+
+typedef std::multimap< OUString, uno::WeakReference< chart2::data::XDataSequence > >
+ lcl_tSequenceMap;
+
+std::vector< OUString > lcl_AnyToStringSequence( const std::vector< uno::Any >& aAnySeq )
+{
+ std::vector< OUString > aResult;
+ aResult.resize( aAnySeq.size() );
+ int i = 0;
+ for (const uno::Any& aAny : aAnySeq)
+ aResult[i++] = CommonFunctors::AnyToString()(aAny);
+ return aResult;
+}
+
+std::vector< uno::Any > lcl_StringToAnyVector( const css::uno::Sequence< OUString >& aStringSeq )
+{
+ std::vector< uno::Any > aResult;
+ aResult.resize( aStringSeq.getLength() );
+ int i = 0;
+ for (const OUString& aStr : aStringSeq)
+ aResult[i++] = CommonFunctors::makeAny<OUString>()(aStr);
+ return aResult;
+}
+
+struct lcl_setModified
+{
+ void operator() ( const lcl_tSequenceMap::value_type & rMapEntry )
+ {
+ // convert weak reference to reference
+ Reference< chart2::data::XDataSequence > xSeq( rMapEntry.second );
+ if( xSeq.is())
+ {
+ Reference< util::XModifiable > xMod( xSeq, uno::UNO_QUERY );
+ if( xMod.is())
+ xMod->setModified( true );
+ }
+ }
+};
+
+struct lcl_internalizeSeries
+{
+ lcl_internalizeSeries( InternalData & rInternalData,
+ InternalDataProvider & rProvider,
+ bool bConnectToModel, bool bDataInColumns ) :
+ m_rInternalData( rInternalData ),
+ m_rProvider( rProvider ),
+ m_bConnectToModel( bConnectToModel ),
+ m_bDataInColumns( bDataInColumns )
+ {}
+ void operator() ( const rtl::Reference< DataSeries > & xSeries )
+ {
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aOldSeriesData = xSeries->getDataSequences2();
+ std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aNewSeriesData( aOldSeriesData.size() );
+ for( std::size_t i=0; i<aOldSeriesData.size(); ++i )
+ {
+ sal_Int32 nNewIndex( m_bDataInColumns ? m_rInternalData.appendColumn() : m_rInternalData.appendRow() );
+ OUString aIdentifier( OUString::number( nNewIndex ));
+ //@todo: deal also with genericXDataSequence
+ Reference< chart2::data::XNumericalDataSequence > xValues( aOldSeriesData[i]->getValues(), uno::UNO_QUERY );
+ Reference< chart2::data::XTextualDataSequence > xLabel( aOldSeriesData[i]->getLabel(), uno::UNO_QUERY );
+ Reference< chart2::data::XDataSequence > xNewValues;
+
+ if( xValues.is() )
+ {
+ auto aValues( comphelper::sequenceToContainer<std::vector< double >>( xValues->getNumericalData()));
+ if( m_bDataInColumns )
+ m_rInternalData.setColumnValues( nNewIndex, aValues );
+ else
+ m_rInternalData.setRowValues( nNewIndex, aValues );
+ if( m_bConnectToModel )
+ {
+ xNewValues.set( m_rProvider.createDataSequenceByRangeRepresentation( aIdentifier ));
+ comphelper::copyProperties(
+ Reference< beans::XPropertySet >( xValues, uno::UNO_QUERY ),
+ Reference< beans::XPropertySet >( xNewValues, uno::UNO_QUERY ));
+ }
+ }
+
+ if( xLabel.is() )
+ {
+ if( m_bDataInColumns )
+ m_rInternalData.setComplexColumnLabel( nNewIndex, lcl_StringToAnyVector( xLabel->getTextualData() ) );
+ else
+ m_rInternalData.setComplexRowLabel( nNewIndex, lcl_StringToAnyVector( xLabel->getTextualData() ) );
+ if( m_bConnectToModel )
+ {
+ Reference< chart2::data::XDataSequence > xNewLabel(
+ m_rProvider.createDataSequenceByRangeRepresentation( lcl_aLabelRangePrefix + aIdentifier ));
+ comphelper::copyProperties(
+ Reference< beans::XPropertySet >( xLabel, uno::UNO_QUERY ),
+ Reference< beans::XPropertySet >( xNewLabel, uno::UNO_QUERY ));
+ aNewSeriesData[i].set( new LabeledDataSequence( xNewValues, xNewLabel ) );
+ }
+ }
+ else
+ {
+ if( m_bConnectToModel )
+ aNewSeriesData[i].set( new LabeledDataSequence( xNewValues ) );
+ }
+ }
+ if( m_bConnectToModel )
+ xSeries->setData( aNewSeriesData );
+ }
+
+private:
+ InternalData & m_rInternalData;
+ InternalDataProvider & m_rProvider;
+ bool m_bConnectToModel;
+ bool m_bDataInColumns;
+};
+
+struct lcl_copyFromLevel
+{
+public:
+
+ explicit lcl_copyFromLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ uno::Any operator() ( const vector< uno::Any >& rVector )
+ {
+ uno::Any aRet;
+ if( m_nLevel < static_cast< sal_Int32 >(rVector.size()) )
+ aRet = rVector[m_nLevel];
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_getStringFromLevelVector
+{
+public:
+
+ explicit lcl_getStringFromLevelVector( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ OUString operator() ( const vector< uno::Any >& rVector )
+ {
+ OUString aString;
+ if( m_nLevel < static_cast< sal_Int32 >(rVector.size()) )
+ aString = CommonFunctors::AnyToString()(rVector[m_nLevel]);
+ return aString;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_setAnyAtLevel
+{
+public:
+
+ explicit lcl_setAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ vector< uno::Any > operator() ( const vector< uno::Any >& rVector, const uno::Any& rNewValue )
+ {
+ vector< uno::Any > aRet( rVector );
+ if( m_nLevel >= static_cast< sal_Int32 >(aRet.size()) )
+ aRet.resize( m_nLevel+1 );
+ aRet[ m_nLevel ]=rNewValue;
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_setAnyAtLevelFromStringSequence
+{
+public:
+
+ explicit lcl_setAnyAtLevelFromStringSequence( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ vector< uno::Any > operator() ( const vector< uno::Any >& rVector, const OUString& rNewValue )
+ {
+ vector< uno::Any > aRet( rVector );
+ if( m_nLevel >= static_cast< sal_Int32 >(aRet.size()) )
+ aRet.resize( m_nLevel+1 );
+ aRet[ m_nLevel ] <<= rNewValue;
+ return aRet;
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_insertAnyAtLevel
+{
+public:
+
+ explicit lcl_insertAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ void operator() ( vector< uno::Any >& rVector )
+ {
+ if( m_nLevel >= static_cast< sal_Int32 >(rVector.size()) )
+ {
+ rVector.resize( m_nLevel + 1 );
+ }
+ else
+ {
+ rVector.insert( rVector.begin() + m_nLevel, uno::Any() );
+ }
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+struct lcl_removeAnyAtLevel
+{
+public:
+
+ explicit lcl_removeAnyAtLevel( sal_Int32 nLevel ) : m_nLevel( nLevel )
+ {}
+
+ void operator() ( vector< uno::Any >& rVector )
+ {
+ if( m_nLevel < static_cast<sal_Int32>(rVector.size()) )
+ {
+ rVector.erase(rVector.begin() + m_nLevel);
+ }
+ }
+
+private:
+ sal_Int32 m_nLevel;
+};
+
+} // anonymous namespace
+
+InternalDataProvider::InternalDataProvider()
+ : m_bDataInColumns( true )
+{}
+
+InternalDataProvider::InternalDataProvider(
+ const rtl::Reference< ChartModel > & xModel,
+ bool bConnectToModel,
+ bool bDefaultDataInColumns)
+: m_bDataInColumns( bDefaultDataInColumns )
+{
+ if (!xModel.is())
+ return;
+ try
+ {
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xModel ) );
+ if( xDiagram.is())
+ {
+ //data in columns?
+ {
+ OUString aRangeString;
+ bool bFirstCellAsLabel = true;
+ bool bHasCategories = true;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ const bool bSomethingDetected(
+ DataSourceHelper::detectRangeSegmentation(
+ xModel, aRangeString, aSequenceMapping, m_bDataInColumns, bFirstCellAsLabel, bHasCategories ));
+
+ // #i120559# if no data was available, restore default
+ if(!bSomethingDetected && m_bDataInColumns != bDefaultDataInColumns)
+ {
+ m_bDataInColumns = bDefaultDataInColumns;
+ }
+ }
+
+ // categories
+ {
+ vector< vector< uno::Any > > aNewCategories;//inner count is level
+ {
+ ExplicitCategoriesProvider aExplicitCategoriesProvider(ChartModelHelper::getFirstCoordinateSystem(xModel), *xModel);
+
+ const std::vector< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() );
+ sal_Int32 nLevelCount = rSplitCategoriesList.size();
+ for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
+ {
+ Reference< chart2::data::XLabeledDataSequence > xLDS( rSplitCategoriesList[nL] );
+ if( !xLDS.is() )
+ continue;
+ Sequence< uno::Any > aDataSeq;
+ Reference< chart2::data::XDataSequence > xSeq( xLDS->getValues() );
+ if( xSeq.is() )
+ aDataSeq = xSeq->getData();
+ sal_Int32 nLength = aDataSeq.getLength();
+ sal_Int32 nCatLength = static_cast< sal_Int32 >(aNewCategories.size());
+ if( nCatLength < nLength )
+ aNewCategories.resize( nLength );
+ else if( nLength < nCatLength )
+ aDataSeq.realloc( nCatLength );
+ transform( aNewCategories.begin(), aNewCategories.end(), aDataSeq.getConstArray(),
+ aNewCategories.begin(), lcl_setAnyAtLevel(nL) );
+ }
+ if( !nLevelCount )
+ {
+ Sequence< OUString > aSimplecategories = aExplicitCategoriesProvider.getSimpleCategories();
+ sal_Int32 nLength = aSimplecategories.getLength();
+ aNewCategories.reserve( nLength );
+ for( sal_Int32 nN=0; nN<nLength; nN++)
+ {
+ aNewCategories.push_back( { uno::Any(aSimplecategories[nN]) } );
+ }
+ }
+ }
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aNewCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aNewCategories) );
+ if( bConnectToModel )
+ DiagramHelper::setCategoriesToDiagram(
+ rtl::Reference< LabeledDataSequence >(new LabeledDataSequence(
+ createDataSequenceByRangeRepresentation( lcl_aCategoriesRangeName ))),
+ xDiagram );
+ }
+
+ // data series
+ std::vector< rtl::Reference< DataSeries > > aSeriesVector( ChartModelHelper::getDataSeries( xModel ));
+ lcl_internalizeSeries ftor( m_aInternalData, *this, bConnectToModel, m_bDataInColumns );
+ for( const auto& rxScreen : aSeriesVector )
+ ftor( rxScreen );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+// copy-CTOR
+InternalDataProvider::InternalDataProvider( const InternalDataProvider & rOther ) :
+ impl::InternalDataProvider_Base(rOther),
+ m_aSequenceMap( rOther.m_aSequenceMap ),
+ m_aInternalData( rOther.m_aInternalData ),
+ m_bDataInColumns( rOther.m_bDataInColumns )
+{}
+
+InternalDataProvider::~InternalDataProvider()
+{}
+
+void InternalDataProvider::addDataSequenceToMap(
+ const OUString & rRangeRepresentation,
+ const Reference< chart2::data::XDataSequence > & xSequence )
+{
+ m_aSequenceMap.emplace(
+ rRangeRepresentation,
+ uno::WeakReference< chart2::data::XDataSequence >( xSequence ));
+}
+
+void InternalDataProvider::deleteMapReferences( const OUString & rRangeRepresentation )
+{
+ // set sequence to deleted by setting its range to an empty string
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( rRangeRepresentation ));
+ for( tSequenceMap::iterator aIt( aRange.first ); aIt != aRange.second; ++aIt )
+ {
+ Reference< chart2::data::XDataSequence > xSeq( aIt->second );
+ if( xSeq.is())
+ {
+ Reference< container::XNamed > xNamed( xSeq, uno::UNO_QUERY );
+ if( xNamed.is())
+ xNamed->setName( OUString());
+ }
+ }
+ // remove from map
+ m_aSequenceMap.erase( aRange.first, aRange.second );
+}
+
+void InternalDataProvider::adaptMapReferences(
+ const OUString & rOldRangeRepresentation,
+ const OUString & rNewRangeRepresentation )
+{
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( rOldRangeRepresentation ));
+ tSequenceMap aNewElements;
+ for( tSequenceMap::iterator aIt( aRange.first ); aIt != aRange.second; ++aIt )
+ {
+ Reference< chart2::data::XDataSequence > xSeq( aIt->second );
+ if( xSeq.is())
+ {
+ Reference< container::XNamed > xNamed( xSeq, uno::UNO_QUERY );
+ if( xNamed.is())
+ xNamed->setName( rNewRangeRepresentation );
+ }
+ aNewElements.emplace( rNewRangeRepresentation, aIt->second );
+ }
+ // erase map values for old index
+ m_aSequenceMap.erase( aRange.first, aRange.second );
+ // add new entries for values with new index
+ m_aSequenceMap.insert( aNewElements.begin(), aNewElements.end() );
+}
+
+void InternalDataProvider::increaseMapReferences(
+ sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ for( sal_Int32 nIndex = nEnd - 1; nIndex >= nBegin; --nIndex )
+ {
+ adaptMapReferences( OUString::number( nIndex ),
+ OUString::number( nIndex + 1 ));
+ adaptMapReferences( lcl_aLabelRangePrefix + OUString::number( nIndex ),
+ lcl_aLabelRangePrefix + OUString::number( nIndex + 1 ));
+ }
+}
+
+void InternalDataProvider::decreaseMapReferences(
+ sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ for( sal_Int32 nIndex = nBegin; nIndex < nEnd; ++nIndex )
+ {
+ adaptMapReferences( OUString::number( nIndex ),
+ OUString::number( nIndex - 1 ));
+ adaptMapReferences( lcl_aLabelRangePrefix + OUString::number( nIndex ),
+ lcl_aLabelRangePrefix + OUString::number( nIndex - 1 ));
+ }
+}
+
+rtl::Reference< UncachedDataSequence > InternalDataProvider::createDataSequenceAndAddToMap(
+ const OUString & rRangeRepresentation )
+{
+ rtl::Reference<UncachedDataSequence> xSeq = createDataSequenceFromArray(rRangeRepresentation, u"", u"");
+ if (xSeq.is())
+ return nullptr;
+
+ xSeq.set(new UncachedDataSequence(this, rRangeRepresentation));
+ addDataSequenceToMap(rRangeRepresentation, xSeq);
+ return xSeq;
+}
+
+rtl::Reference<UncachedDataSequence>
+InternalDataProvider::createDataSequenceFromArray( const OUString& rArrayStr, std::u16string_view rRole, std::u16string_view rRoleQualifier )
+{
+ if (rArrayStr.indexOf('{') != 0 || rArrayStr[rArrayStr.getLength()-1] != '}')
+ {
+ // Not an array string.
+ return nullptr;
+ }
+
+ bool bAllNumeric = true;
+ rtl::Reference<UncachedDataSequence> xSeq;
+
+ const sal_Unicode* p = rArrayStr.getStr();
+ const sal_Unicode* pEnd = p + rArrayStr.getLength();
+ const sal_Unicode* pElem = nullptr;
+ OUString aElem;
+
+ std::vector<OUString> aRawElems;
+ ++p; // Skip the first '{'.
+ --pEnd; // Skip the last '}'.
+ bool bInQuote = false;
+ for (; p != pEnd; ++p)
+ {
+ // Skip next "" within the title text: it's an escaped double quotation mark.
+ if (bInQuote && *p == '"' && *(p + 1) == '"')
+ {
+ if (!pElem)
+ pElem = p;
+ ++p;
+ }
+ else if (*p == '"')
+ {
+ bInQuote = !bInQuote;
+ if (bInQuote)
+ {
+ // Opening quote.
+ pElem = nullptr;
+ }
+ else
+ {
+ // Closing quote.
+ if (pElem)
+ aElem = OUString(pElem, p-pElem);
+ // Non empty string
+ if (!aElem.isEmpty())
+ bAllNumeric = false;
+ // Restore also escaped double quotation marks
+ aRawElems.push_back(aElem.replaceAll("\"\"", "\""));
+ pElem = nullptr;
+ aElem.clear();
+
+ ++p; // Skip '"'.
+ if (p == pEnd)
+ break;
+ }
+ }
+ else if (*p == ';' && !bInQuote)
+ {
+ // element separator.
+ if (pElem)
+ aElem = OUString(pElem, p-pElem);
+ aRawElems.push_back(aElem);
+ pElem = nullptr;
+ aElem.clear();
+ }
+ else if (!pElem)
+ pElem = p;
+ }
+
+ if (pElem)
+ {
+ aElem = OUString(pElem, p-pElem);
+ aRawElems.push_back(aElem);
+ }
+
+ if (rRole == u"values-y" || rRole == u"values-first" || rRole == u"values-last" ||
+ rRole == u"values-min" || rRole == u"values-max" || rRole == u"values-size" ||
+ rRole == u"error-bars-y-positive" || rRole == u"error-bars-y-negative")
+ {
+ // Column values. Append a new data column and populate it.
+
+ std::vector<double> aValues;
+ aValues.reserve(aRawElems.size());
+ for (const OUString & aRawElem : aRawElems)
+ {
+ if (aRawElem.isEmpty())
+ aValues.push_back(NAN);
+ else
+ aValues.push_back(aRawElem.toDouble());
+ }
+ sal_Int32 n = m_aInternalData.appendColumn();
+
+ m_aInternalData.setColumnValues(n, aValues);
+
+ OUString aRangeRep = OUString::number(n);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ else if (rRole == u"values-x")
+ {
+ std::vector<double> aValues;
+ aValues.reserve(aRawElems.size());
+ if (bAllNumeric)
+ {
+ for (const OUString & aRawElem : aRawElems)
+ {
+ if (!aRawElem.isEmpty())
+ aValues.push_back(aRawElem.toDouble());
+ else
+ aValues.push_back(NAN);
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < aRawElems.size(); ++i)
+ aValues.push_back(i+1);
+ }
+
+ sal_Int32 n = m_aInternalData.appendColumn();
+ m_aInternalData.setColumnValues(n, aValues);
+
+ OUString aRangeRep = OUString::number(n);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ else if (rRole == u"categories")
+ {
+ // Category labels.
+
+ // Store date categories as numbers.
+ bool bStoreNumeric = rRoleQualifier == u"date";
+ double fValue;
+ for (size_t i = 0; i < aRawElems.size(); ++i)
+ {
+ if (bStoreNumeric)
+ {
+ bool bGetDouble = bAllNumeric && !aRawElems[i].isEmpty();
+ fValue = bGetDouble ? aRawElems[i].toDouble() :
+ std::numeric_limits<double>::quiet_NaN();
+ }
+ std::vector<uno::Any> aLabels(1,
+ bStoreNumeric ? uno::Any(fValue) : uno::Any(aRawElems[i]));
+ m_aInternalData.setComplexRowLabel(i, std::move(aLabels));
+ }
+
+ xSeq.set(new UncachedDataSequence(this, lcl_aCategoriesRangeName));
+ addDataSequenceToMap(lcl_aCategoriesRangeName, xSeq);
+ }
+ else if (rRole == u"label")
+ {
+ // Data series label. There should be only one element. This always
+ // goes to the last data column.
+ sal_Int32 nColSize = m_aInternalData.getColumnCount();
+ if (!aRawElems.empty() && nColSize)
+ {
+ // Do not overwrite an existing label (attempted by series with no data values)
+ if (!m_aInternalData.getComplexColumnLabel(nColSize-1)[0].hasValue())
+ {
+ std::vector<uno::Any> aLabels(1, uno::Any(aRawElems[0]));
+ m_aInternalData.setComplexColumnLabel(nColSize-1, std::move(aLabels));
+ }
+
+ OUString aRangeRep = lcl_aLabelRangePrefix + OUString::number(nColSize-1);
+ xSeq.set(new UncachedDataSequence(this, aRangeRep));
+ addDataSequenceToMap(aRangeRep, xSeq);
+ }
+ }
+
+ return xSeq;
+}
+
+Reference< chart2::data::XDataSequence > InternalDataProvider::createDataSequenceAndAddToMap(
+ const OUString & rRangeRepresentation,
+ const OUString & rRole )
+{
+ rtl::Reference< UncachedDataSequence > xSeq =
+ new UncachedDataSequence( this, rRangeRepresentation, rRole );
+ addDataSequenceToMap( rRangeRepresentation, xSeq );
+ return xSeq;
+}
+
+// ____ XDataProvider ____
+sal_Bool SAL_CALL InternalDataProvider::createDataSourcePossible( const Sequence< beans::PropertyValue >& /* aArguments */ )
+{
+ return true;
+}
+
+namespace
+{
+
+sal_Int32 lcl_getInnerLevelCount( const vector< vector< uno::Any > >& rLabels )
+{
+ sal_Int32 nCount = 1;//minimum is 1!
+ for (auto const& elemLabel : rLabels)
+ {
+ nCount = std::max<sal_Int32>( elemLabel.size(), nCount );
+ }
+ return nCount;
+}
+
+}//end anonymous namespace
+
+Reference< chart2::data::XDataSource > SAL_CALL InternalDataProvider::createDataSource(
+ const Sequence< beans::PropertyValue >& aArguments )
+{
+ OUString aRangeRepresentation;
+ bool bUseColumns = true;
+ bool bFirstCellAsLabel = true;
+ bool bHasCategories = true;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ DataSourceHelper::readArguments( aArguments, aRangeRepresentation, aSequenceMapping, bUseColumns, bFirstCellAsLabel, bHasCategories );
+
+ if( aRangeRepresentation == lcl_aCategoriesRangeName )
+ {
+ //return split complex categories if we have any:
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aComplexCategories;
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ if( bUseColumns==m_bDataInColumns )
+ {
+ sal_Int32 nLevelCount = lcl_getInnerLevelCount( aCategories );
+ for( sal_Int32 nL=0; nL<nLevelCount; nL++ )
+ aComplexCategories.push_back( new LabeledDataSequence(
+ new UncachedDataSequence( this
+ , lcl_aCategoriesLevelRangeNamePrefix + OUString::number( nL )
+ , lcl_aCategoriesRoleName ) ) );
+ }
+ else
+ {
+ sal_Int32 nPointCount = m_bDataInColumns ? m_aInternalData.getRowCount() : m_aInternalData.getColumnCount();
+ for( sal_Int32 nP=0; nP<nPointCount; nP++ )
+ aComplexCategories.push_back( new LabeledDataSequence(
+ new UncachedDataSequence( this
+ , lcl_aCategoriesPointRangeNamePrefix + OUString::number( nP )
+ , lcl_aCategoriesRoleName ) ) );
+ }
+ //don't add the created sequences to the map as they are used temporarily only ...
+ return new DataSource( comphelper::containerToSequence(aComplexCategories) );
+ }
+
+ OSL_ASSERT( aRangeRepresentation == lcl_aCompleteRange );
+
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aResultLSeqVec;
+
+ // categories
+ if( bHasCategories )
+ aResultLSeqVec.push_back(
+ new LabeledDataSequence( createDataSequenceAndAddToMap( lcl_aCategoriesRangeName, lcl_aCategoriesRoleName ) ) );
+
+ // data with labels
+ std::vector< Reference< chart2::data::XLabeledDataSequence > > aDataVec;
+ const sal_Int32 nCount = (bUseColumns ? m_aInternalData.getColumnCount() : m_aInternalData.getRowCount());
+ aDataVec.reserve(nCount);
+ for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx)
+ {
+ aDataVec.push_back(
+ new LabeledDataSequence(
+ createDataSequenceAndAddToMap( OUString::number( nIdx )),
+ createDataSequenceAndAddToMap( lcl_aLabelRangePrefix + OUString::number( nIdx ))));
+ }
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+ m_bDataInColumns = bUseColumns;
+
+ //reorder labeled sequences according to aSequenceMapping; ignore categories
+ for( sal_Int32 nNewIndex = 0; nNewIndex < aSequenceMapping.getLength(); nNewIndex++ )
+ {
+ std::vector< LabeledDataSequence* >::size_type nOldIndex = aSequenceMapping[nNewIndex];
+ if( nOldIndex < aDataVec.size() )
+ {
+ if( aDataVec[nOldIndex].is() )
+ {
+ aResultLSeqVec.push_back( aDataVec[nOldIndex] );
+ aDataVec[nOldIndex] = nullptr;
+ }
+ }
+ }
+
+ //add left over data sequences to result
+ for (auto const& elem : aDataVec)
+ {
+ if( elem.is() )
+ aResultLSeqVec.push_back(elem);
+ }
+
+ return new DataSource( comphelper::containerToSequence(aResultLSeqVec) );
+}
+
+Sequence< beans::PropertyValue > SAL_CALL InternalDataProvider::detectArguments(
+ const Reference< chart2::data::XDataSource >& /* xDataSource */ )
+{
+ Sequence< beans::PropertyValue > aArguments{
+ beans::PropertyValue(
+ "CellRangeRepresentation", -1, uno::Any( OUString(lcl_aCompleteRange) ),
+ beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "DataRowSource", -1, uno::Any(
+ m_bDataInColumns
+ ? css::chart::ChartDataRowSource_COLUMNS
+ : css::chart::ChartDataRowSource_ROWS ),
+ beans::PropertyState_DIRECT_VALUE ),
+ // internal data always contains labels and categories
+ beans::PropertyValue(
+ "FirstCellAsLabel", -1, uno::Any( true ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "HasCategories", -1, uno::Any( true ), beans::PropertyState_DIRECT_VALUE )
+ };
+ // #i85913# Sequence Mapping is not needed for internal data, as it is
+ // applied to the data when the data source is created.
+
+ return aArguments;
+}
+
+sal_Bool SAL_CALL InternalDataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& /* aRangeRepresentation */ )
+{
+ return true;
+}
+
+Reference< chart2::data::XDataSequence > SAL_CALL InternalDataProvider::createDataSequenceByRangeRepresentation(
+ const OUString& aRangeRepresentation )
+{
+ if( aRangeRepresentation.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRangeRepresentation == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+
+ // categories
+ return createDataSequenceAndAddToMap( lcl_aCategoriesRangeName, lcl_aCategoriesRoleName );
+ }
+ else if( aRangeRepresentation.match( lcl_aLabelRangePrefix ))
+ {
+ // label
+ sal_Int32 nIndex = o3tl::toInt32(aRangeRepresentation.subView( strlen(lcl_aLabelRangePrefix)));
+ return createDataSequenceAndAddToMap( lcl_aLabelRangePrefix + OUString::number( nIndex ));
+ }
+ else if ( aRangeRepresentation == "last" )
+ {
+ sal_Int32 nIndex = (m_bDataInColumns
+ ? m_aInternalData.getColumnCount()
+ : m_aInternalData.getRowCount()) - 1;
+ return createDataSequenceAndAddToMap( OUString::number( nIndex ));
+ }
+ else if( !aRangeRepresentation.isEmpty())
+ {
+ // data
+ return createDataSequenceAndAddToMap( aRangeRepresentation );
+ }
+
+ return Reference< chart2::data::XDataSequence >();
+}
+
+Reference<chart2::data::XDataSequence> SAL_CALL
+InternalDataProvider::createDataSequenceByValueArray(
+ const OUString& aRole, const OUString& aRangeRepresentation, const OUString& aRoleQualifier )
+{
+ return createDataSequenceFromArray(aRangeRepresentation, aRole, aRoleQualifier);
+}
+
+Reference< sheet::XRangeSelection > SAL_CALL InternalDataProvider::getRangeSelection()
+{
+ // there is no range selection component
+ return Reference< sheet::XRangeSelection >();
+}
+
+// ____ XInternalDataProvider ____
+sal_Bool SAL_CALL InternalDataProvider::hasDataByRangeRepresentation( const OUString& aRange )
+{
+ bool bResult = false;
+
+ if( aRange.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRange == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+ bResult = true;
+ }
+ else if( aRange.match( lcl_aLabelRangePrefix ))
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ bResult = (nIndex < (m_bDataInColumns ? m_aInternalData.getColumnCount(): m_aInternalData.getRowCount()));
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ bResult = (nIndex < (m_bDataInColumns ? m_aInternalData.getColumnCount(): m_aInternalData.getRowCount()));
+ }
+
+ return bResult;
+}
+
+Sequence< uno::Any > SAL_CALL InternalDataProvider::getDataByRangeRepresentation( const OUString& aRange )
+{
+ Sequence< uno::Any > aResult;
+
+ if( aRange.match( lcl_aLabelRangePrefix ) )
+ {
+ auto nIndex = o3tl::toUInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ vector< uno::Any > aComplexLabel = m_bDataInColumns
+ ? m_aInternalData.getComplexColumnLabel( nIndex )
+ : m_aInternalData.getComplexRowLabel( nIndex );
+ if( !aComplexLabel.empty() )
+ aResult = comphelper::containerToSequence(aComplexLabel);
+ }
+ else if( aRange.match( lcl_aCategoriesPointRangeNamePrefix ) )
+ {
+ auto nPointIndex = o3tl::toUInt32(aRange.subView( strlen(lcl_aCategoriesPointRangeNamePrefix) ));
+ vector< uno::Any > aComplexCategory = m_bDataInColumns
+ ? m_aInternalData.getComplexRowLabel( nPointIndex )
+ : m_aInternalData.getComplexColumnLabel( nPointIndex );
+ if( !aComplexCategory.empty() )
+ aResult = comphelper::containerToSequence(aComplexCategory);
+ }
+ else if( aRange.match( lcl_aCategoriesLevelRangeNamePrefix ) )
+ {
+ sal_Int32 nLevel = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix) ));
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ if( nLevel < lcl_getInnerLevelCount( aCategories ) )
+ {
+ aResult.realloc( aCategories.size() );
+ transform( aCategories.begin(), aCategories.end(),
+ aResult.getArray(), lcl_copyFromLevel(nLevel) );
+ }
+ }
+ else if( aRange == lcl_aCategoriesRangeName )
+ {
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ sal_Int32 nLevelCount = lcl_getInnerLevelCount( aCategories );
+ if( nLevelCount == 1 )
+ {
+ aResult = getDataByRangeRepresentation( lcl_aCategoriesLevelRangeNamePrefix + OUString::number( 0 ) );
+ }
+ else
+ {
+ // Maybe this 'else' part and the functions is not necessary anymore.
+ const Sequence< OUString > aLabels = m_bDataInColumns ? getRowDescriptions() : getColumnDescriptions();
+ aResult.realloc( aLabels.getLength() );
+ transform( aLabels.begin(), aLabels.end(),
+ aResult.getArray(), CommonFunctors::makeAny< OUString >() );
+ }
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ if( nIndex >= 0 )
+ {
+ const Sequence< double > aData = m_bDataInColumns
+ ? m_aInternalData.getColumnValues(nIndex)
+ : m_aInternalData.getRowValues(nIndex);
+ if( aData.hasElements() )
+ {
+ aResult.realloc( aData.getLength());
+ transform( aData.begin(), aData.end(),
+ aResult.getArray(), CommonFunctors::makeAny< double >());
+ }
+ }
+ }
+
+ return aResult;
+}
+
+void SAL_CALL InternalDataProvider::setDataByRangeRepresentation(
+ const OUString& aRange, const Sequence< uno::Any >& aNewData )
+{
+ auto aNewVector( comphelper::sequenceToContainer<vector< uno::Any >>(aNewData) );
+ if( aRange.match( lcl_aLabelRangePrefix ) )
+ {
+ sal_uInt32 nIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aLabelRangePrefix)));
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexColumnLabel( nIndex, std::move(aNewVector) );
+ else
+ m_aInternalData.setComplexRowLabel( nIndex, std::move(aNewVector) );
+ }
+ else if( aRange.match( lcl_aCategoriesPointRangeNamePrefix ) )
+ {
+ sal_Int32 nPointIndex = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix)));
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabel( nPointIndex, std::move(aNewVector) );
+ else
+ m_aInternalData.setComplexColumnLabel( nPointIndex, std::move(aNewVector) );
+ }
+ else if( aRange.match( lcl_aCategoriesLevelRangeNamePrefix ) )
+ {
+ sal_Int32 nLevel = o3tl::toInt32(aRange.subView( strlen(lcl_aCategoriesLevelRangeNamePrefix)));
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+
+ //ensure equal length
+ if( aNewVector.size() > aComplexCategories.size() )
+ aComplexCategories.resize( aNewVector.size() );
+ else if( aNewVector.size() < aComplexCategories.size() )
+ aNewVector.resize( aComplexCategories.size() );
+
+ transform( aComplexCategories.begin(), aComplexCategories.end(), aNewVector.begin(),
+ aComplexCategories.begin(), lcl_setAnyAtLevel(nLevel) );
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+ }
+ else if( aRange == lcl_aCategoriesRangeName )
+ {
+ vector< vector< uno::Any > > aComplexCategories;
+ aComplexCategories.resize( aNewVector.size() );
+ transform( aComplexCategories.begin(), aComplexCategories.end(), aNewVector.begin(),
+ aComplexCategories.begin(), lcl_setAnyAtLevel(0) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+ }
+ else
+ {
+ sal_Int32 nIndex = aRange.toInt32();
+ if( nIndex>=0 )
+ {
+ vector< double > aNewDataVec;
+ transform( aNewData.begin(), aNewData.end(),
+ back_inserter( aNewDataVec ), CommonFunctors::AnyToDouble());
+ if( m_bDataInColumns )
+ m_aInternalData.setColumnValues( nIndex, aNewDataVec );
+ else
+ m_aInternalData.setRowValues( nIndex, aNewDataVec );
+ }
+ }
+}
+
+void SAL_CALL InternalDataProvider::insertSequence( ::sal_Int32 nAfterIndex )
+{
+ if( m_bDataInColumns )
+ {
+ increaseMapReferences( nAfterIndex + 1, m_aInternalData.getColumnCount());
+ m_aInternalData.insertColumn( nAfterIndex );
+ }
+ else
+ {
+ increaseMapReferences( nAfterIndex + 1, m_aInternalData.getRowCount());
+ m_aInternalData.insertRow( nAfterIndex );
+ }
+}
+
+void SAL_CALL InternalDataProvider::deleteSequence( ::sal_Int32 nAtIndex )
+{
+ deleteMapReferences( OUString::number( nAtIndex ));
+ deleteMapReferences( lcl_aLabelRangePrefix + OUString::number( nAtIndex ));
+ if( m_bDataInColumns )
+ {
+ decreaseMapReferences( nAtIndex + 1, m_aInternalData.getColumnCount());
+ m_aInternalData.deleteColumn( nAtIndex );
+ }
+ else
+ {
+ decreaseMapReferences( nAtIndex + 1, m_aInternalData.getRowCount());
+ m_aInternalData.deleteRow( nAtIndex );
+ }
+}
+
+void SAL_CALL InternalDataProvider::appendSequence()
+{
+ if( m_bDataInColumns )
+ m_aInternalData.appendColumn();
+ else
+ m_aInternalData.appendRow();
+}
+
+void SAL_CALL InternalDataProvider::insertComplexCategoryLevel( sal_Int32 nLevel )
+{
+ OSL_ENSURE( nLevel> 0, "you can only insert category levels > 0" );//the first categories level cannot be deleted, check the calling code for error
+ if( nLevel>0 )
+ {
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+ std::for_each( aComplexCategories.begin(), aComplexCategories.end(), lcl_insertAnyAtLevel(nLevel) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+ }
+}
+void SAL_CALL InternalDataProvider::deleteComplexCategoryLevel( sal_Int32 nLevel )
+{
+ OSL_ENSURE( nLevel>0, "you can only delete category levels > 0" );//the first categories level cannot be deleted, check the calling code for error
+ if( nLevel>0 )
+ {
+ vector< vector< uno::Any > > aComplexCategories = m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels();
+ std::for_each( aComplexCategories.begin(), aComplexCategories.end(), lcl_removeAnyAtLevel(nLevel) );
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aComplexCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexCategories) );
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+ }
+}
+
+void SAL_CALL InternalDataProvider::insertDataPointForAllSequences( ::sal_Int32 nAfterIndex )
+{
+ sal_Int32 nMaxRep = 0;
+ if( m_bDataInColumns )
+ {
+ m_aInternalData.insertRow( nAfterIndex );
+ nMaxRep = m_aInternalData.getColumnCount();
+ }
+ else
+ {
+ m_aInternalData.insertColumn( nAfterIndex );
+ nMaxRep = m_aInternalData.getRowCount();
+ }
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::deleteDataPointForAllSequences( ::sal_Int32 nAtIndex )
+{
+ sal_Int32 nMaxRep = 0;
+ if( m_bDataInColumns )
+ {
+ m_aInternalData.deleteRow( nAtIndex );
+ nMaxRep = m_aInternalData.getColumnCount();
+ }
+ else
+ {
+ m_aInternalData.deleteColumn( nAtIndex );
+ nMaxRep = m_aInternalData.getRowCount();
+ }
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::swapDataPointWithNextOneForAllSequences( ::sal_Int32 nAtIndex )
+{
+ if( m_bDataInColumns )
+ m_aInternalData.swapRowWithNext( nAtIndex );
+ else
+ m_aInternalData.swapColumnWithNext( nAtIndex );
+ sal_Int32 nMaxRep = (m_bDataInColumns
+ ? m_aInternalData.getColumnCount()
+ : m_aInternalData.getRowCount());
+
+ // notify change to all affected ranges
+ tSequenceMap::const_iterator aBegin( m_aSequenceMap.lower_bound( "0"));
+ tSequenceMap::const_iterator aEnd( m_aSequenceMap.upper_bound( OUString::number( nMaxRep )));
+ std::for_each( aBegin, aEnd, lcl_setModified());
+
+ tSequenceMapRange aRange( m_aSequenceMap.equal_range( lcl_aCategoriesRangeName ));
+ std::for_each( aRange.first, aRange.second, lcl_setModified());
+}
+
+void SAL_CALL InternalDataProvider::registerDataSequenceForChanges( const Reference< chart2::data::XDataSequence >& xSeq )
+{
+ if( xSeq.is())
+ addDataSequenceToMap( xSeq->getSourceRangeRepresentation(), xSeq );
+}
+
+// ____ XRangeXMLConversion ____
+OUString SAL_CALL InternalDataProvider::convertRangeToXML( const OUString& aRangeRepresentation )
+{
+ XMLRangeHelper::CellRange aRange;
+ aRange.aTableName = "local-table";
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+ if( aRangeRepresentation.match( lcl_aCategoriesRangeName ))
+ {
+ OSL_ASSERT( aRangeRepresentation == lcl_aCategoriesRangeName );//it is not expected nor implemented that only parts of the categories are really requested
+ aRange.aUpperLeft.bIsEmpty = false;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 1;
+ aRange.aUpperLeft.nRow = 0;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ }
+ }
+ else if( aRangeRepresentation.match( lcl_aLabelRangePrefix ))
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aRangeRepresentation.subView( strlen(lcl_aLabelRangePrefix)));
+ aRange.aUpperLeft.bIsEmpty = false;
+ aRange.aLowerRight.bIsEmpty = true;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = nIndex + 1;
+ aRange.aUpperLeft.nRow = 0;
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = nIndex + 1;
+ }
+ }
+ else if( aRangeRepresentation == lcl_aCompleteRange )
+ {
+ aRange.aUpperLeft.bIsEmpty = false;
+ aRange.aLowerRight.bIsEmpty = false;
+ aRange.aUpperLeft.nColumn = 0;
+ aRange.aUpperLeft.nRow = 0;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ sal_Int32 nIndex = aRangeRepresentation.toInt32();
+ aRange.aUpperLeft.bIsEmpty = false;
+ if( m_bDataInColumns )
+ {
+ aRange.aUpperLeft.nColumn = nIndex + 1;
+ aRange.aUpperLeft.nRow = 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nRow = m_aInternalData.getRowCount();
+ }
+ else
+ {
+ aRange.aUpperLeft.nColumn = 1;
+ aRange.aUpperLeft.nRow = nIndex + 1;
+ aRange.aLowerRight = aRange.aUpperLeft;
+ aRange.aLowerRight.nColumn = m_aInternalData.getColumnCount();
+ }
+ }
+
+ return XMLRangeHelper::getXMLStringFromCellRange( aRange );
+}
+
+OUString SAL_CALL InternalDataProvider::convertRangeFromXML( const OUString& aXMLRange )
+{
+ // Handle non-standards-conforming table:cell-range-address="PivotChart", see
+ // <https://bugs.documentfoundation.org/show_bug.cgi?id=112783> "PIVOT CHARTS: Save produces
+ // invalid file because of invalid cell address":
+ if (aXMLRange == "PivotChart") {
+ return "";
+ }
+
+ static const OUStringLiteral aPivotTableID(u"PT@");
+ if (aXMLRange.startsWith(aPivotTableID))
+ return aXMLRange.copy(aPivotTableID.getLength());
+
+ XMLRangeHelper::CellRange aRange( XMLRangeHelper::getCellRangeFromXMLString( aXMLRange ));
+ if( aRange.aUpperLeft.bIsEmpty )
+ {
+ OSL_ENSURE( aRange.aLowerRight.bIsEmpty, "Weird Range" );
+ return OUString();
+ }
+
+ // "all"
+ if( !aRange.aLowerRight.bIsEmpty &&
+ ( aRange.aUpperLeft.nColumn != aRange.aLowerRight.nColumn ) &&
+ ( aRange.aUpperLeft.nRow != aRange.aLowerRight.nRow ) )
+ return lcl_aCompleteRange;
+
+ // attention: this data provider has the limitation that it stores
+ // internally if data comes from columns or rows. It is intended for
+ // creating only one used data source.
+ // @todo: add this information in the range representation strings
+
+ // data in columns
+ if( m_bDataInColumns )
+ {
+ if( aRange.aUpperLeft.nColumn == 0 )
+ return lcl_aCategoriesRangeName;
+ if( aRange.aUpperLeft.nRow == 0 )
+ return lcl_aLabelRangePrefix + OUString::number( aRange.aUpperLeft.nColumn - 1 );
+
+ return OUString::number( aRange.aUpperLeft.nColumn - 1 );
+ }
+
+ // data in rows
+ if( aRange.aUpperLeft.nRow == 0 )
+ return lcl_aCategoriesRangeName;
+ if( aRange.aUpperLeft.nColumn == 0 )
+ return lcl_aLabelRangePrefix + OUString::number( aRange.aUpperLeft.nRow - 1 );
+
+ return OUString::number( aRange.aUpperLeft.nRow - 1 );
+}
+
+namespace
+{
+
+template< class Type >
+Sequence< Sequence< Type > > lcl_convertVectorVectorToSequenceSequence( const vector< vector< Type > >& rIn )
+{
+ Sequence< Sequence< Type > > aRet;
+ sal_Int32 nOuterCount = rIn.size();
+ if( nOuterCount )
+ {
+ aRet.realloc(nOuterCount);
+ auto pRet = aRet.getArray();
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ pRet[nN]= comphelper::containerToSequence( rIn[nN] );
+ }
+ return aRet;
+}
+
+template< class Type >
+vector< vector< Type > > lcl_convertSequenceSequenceToVectorVector( const Sequence< Sequence< Type > >& rIn )
+{
+ vector< vector< Type > > aRet;
+ sal_Int32 nOuterCount = rIn.getLength();
+ if( nOuterCount )
+ {
+ aRet.resize(nOuterCount);
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ aRet[nN]= comphelper::sequenceToContainer<vector< Type >>( rIn[nN] );
+ }
+ return aRet;
+}
+
+std::vector< Sequence< OUString > > lcl_convertComplexAnyVectorToStringSequence( const vector< vector< uno::Any > >& rIn )
+{
+ std::vector< Sequence< OUString > > aRet;
+ sal_Int32 nOuterCount = rIn.size();
+ if( nOuterCount )
+ {
+ aRet.resize(nOuterCount);
+ for( sal_Int32 nN=0; nN<nOuterCount; nN++)
+ aRet[nN] = comphelper::containerToSequence(lcl_AnyToStringSequence( rIn[nN] ));
+ }
+ return aRet;
+}
+
+vector< vector< uno::Any > > lcl_convertComplexStringSequenceToAnyVector( const Sequence< Sequence< OUString > >& rIn )
+{
+ vector< vector< uno::Any > > aRet;
+ sal_Int32 nOuterCount = rIn.getLength();
+ aRet.reserve(nOuterCount);
+ for (sal_Int32 nN = 0; nN < nOuterCount; nN++)
+ aRet.push_back( lcl_StringToAnyVector( rIn[nN] ) );
+ return aRet;
+}
+
+class SplitCategoriesProvider_ForComplexDescriptions : public SplitCategoriesProvider
+{
+public:
+
+ explicit SplitCategoriesProvider_ForComplexDescriptions( const std::vector< std::vector< uno::Any > >& rComplexDescriptions )
+ : m_rComplexDescriptions( rComplexDescriptions )
+ {}
+
+ virtual sal_Int32 getLevelCount() const override;
+ virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const override;
+
+private:
+ const std::vector< std::vector< uno::Any > >& m_rComplexDescriptions;
+};
+
+sal_Int32 SplitCategoriesProvider_ForComplexDescriptions::getLevelCount() const
+{
+ return lcl_getInnerLevelCount( m_rComplexDescriptions );
+}
+uno::Sequence< OUString > SplitCategoriesProvider_ForComplexDescriptions::getStringsForLevel( sal_Int32 nLevel ) const
+{
+ uno::Sequence< OUString > aResult;
+ if( nLevel < lcl_getInnerLevelCount( m_rComplexDescriptions ) )
+ {
+ aResult.realloc( m_rComplexDescriptions.size() );
+ transform( m_rComplexDescriptions.begin(), m_rComplexDescriptions.end(),
+ aResult.getArray(), lcl_getStringFromLevelVector(nLevel) );
+ }
+ return aResult;
+}
+
+}//anonymous namespace
+
+// ____ XDateCategories ____
+Sequence< double > SAL_CALL InternalDataProvider::getDateCategories()
+{
+ const vector< vector< uno::Any > > & aCategories( m_bDataInColumns ? m_aInternalData.getComplexRowLabels() : m_aInternalData.getComplexColumnLabels());
+ sal_Int32 nCount = aCategories.size();
+ Sequence< double > aDoubles( nCount );
+ auto aDoublesRange = asNonConstRange(aDoubles);
+ sal_Int32 nN=0;
+ for (auto const& category : aCategories)
+ {
+ double fValue;
+ if( category.empty() || !(category[0]>>=fValue) )
+ fValue = std::numeric_limits<double>::quiet_NaN();
+ aDoublesRange[nN++]=fValue;
+ }
+ return aDoubles;
+}
+
+void SAL_CALL InternalDataProvider::setDateCategories( const Sequence< double >& rDates )
+{
+ sal_Int32 nCount = rDates.getLength();
+ vector< vector< uno::Any > > aNewCategories;
+ aNewCategories.reserve(nCount);
+ vector< uno::Any > aSingleLabel(1);
+
+ for(sal_Int32 nN=0; nN<nCount; ++nN )
+ {
+ aSingleLabel[0] <<= rDates[nN];
+ aNewCategories.push_back(aSingleLabel);
+ }
+
+ if( m_bDataInColumns )
+ m_aInternalData.setComplexRowLabels( std::move(aNewCategories) );
+ else
+ m_aInternalData.setComplexColumnLabels( std::move(aNewCategories) );
+}
+
+// ____ XAnyDescriptionAccess ____
+Sequence< Sequence< uno::Any > > SAL_CALL InternalDataProvider::getAnyRowDescriptions()
+{
+ return lcl_convertVectorVectorToSequenceSequence( m_aInternalData.getComplexRowLabels() );
+}
+void SAL_CALL InternalDataProvider::setAnyRowDescriptions( const Sequence< Sequence< uno::Any > >& aRowDescriptions )
+{
+ m_aInternalData.setComplexRowLabels( lcl_convertSequenceSequenceToVectorVector( aRowDescriptions ) );
+}
+Sequence< Sequence< uno::Any > > SAL_CALL InternalDataProvider::getAnyColumnDescriptions()
+{
+ return lcl_convertVectorVectorToSequenceSequence( m_aInternalData.getComplexColumnLabels() );
+}
+void SAL_CALL InternalDataProvider::setAnyColumnDescriptions( const Sequence< Sequence< uno::Any > >& aColumnDescriptions )
+{
+ m_aInternalData.setComplexColumnLabels( lcl_convertSequenceSequenceToVectorVector( aColumnDescriptions ) );
+}
+
+// ____ XComplexDescriptionAccess ____
+Sequence< Sequence< OUString > > SAL_CALL InternalDataProvider::getComplexRowDescriptions()
+{
+ return comphelper::containerToSequence(lcl_convertComplexAnyVectorToStringSequence( m_aInternalData.getComplexRowLabels() ));
+}
+void SAL_CALL InternalDataProvider::setComplexRowDescriptions( const Sequence< Sequence< OUString > >& aRowDescriptions )
+{
+ m_aInternalData.setComplexRowLabels( lcl_convertComplexStringSequenceToAnyVector(aRowDescriptions) );
+}
+Sequence< Sequence< OUString > > SAL_CALL InternalDataProvider::getComplexColumnDescriptions()
+{
+ return comphelper::containerToSequence(lcl_convertComplexAnyVectorToStringSequence( m_aInternalData.getComplexColumnLabels() ));
+}
+void SAL_CALL InternalDataProvider::setComplexColumnDescriptions( const Sequence< Sequence< OUString > >& aColumnDescriptions )
+{
+ m_aInternalData.setComplexColumnLabels( lcl_convertComplexStringSequenceToAnyVector(aColumnDescriptions) );
+}
+
+// ____ XChartDataArray ____
+Sequence< Sequence< double > > SAL_CALL InternalDataProvider::getData()
+{
+ return m_aInternalData.getData();
+}
+
+void SAL_CALL InternalDataProvider::setData( const Sequence< Sequence< double > >& rDataInRows )
+{
+ return m_aInternalData.setData( rDataInRows );
+}
+
+void SAL_CALL InternalDataProvider::setRowDescriptions( const Sequence< OUString >& aRowDescriptions )
+{
+ vector< vector< uno::Any > > aComplexDescriptions( aRowDescriptions.getLength() );
+ transform( aComplexDescriptions.begin(), aComplexDescriptions.end(), aRowDescriptions.getConstArray(),
+ aComplexDescriptions.begin(), lcl_setAnyAtLevelFromStringSequence(0) );
+ m_aInternalData.setComplexRowLabels( std::move(aComplexDescriptions) );
+}
+
+void SAL_CALL InternalDataProvider::setColumnDescriptions( const Sequence< OUString >& aColumnDescriptions )
+{
+ vector< vector< uno::Any > > aComplexDescriptions( aColumnDescriptions.getLength() );
+ transform( aComplexDescriptions.begin(), aComplexDescriptions.end(), aColumnDescriptions.getConstArray(),
+ aComplexDescriptions.begin(), lcl_setAnyAtLevelFromStringSequence(0) );
+ m_aInternalData.setComplexColumnLabels( std::move(aComplexDescriptions) );
+}
+
+Sequence< OUString > SAL_CALL InternalDataProvider::getRowDescriptions()
+{
+ const vector< vector< uno::Any > > & aComplexLabels( m_aInternalData.getComplexRowLabels() );
+ SplitCategoriesProvider_ForComplexDescriptions aProvider( aComplexLabels );
+ return ExplicitCategoriesProvider::getExplicitSimpleCategories( aProvider );
+}
+
+Sequence< OUString > SAL_CALL InternalDataProvider::getColumnDescriptions()
+{
+ const vector< vector< uno::Any > > & aComplexLabels( m_aInternalData.getComplexColumnLabels() );
+ SplitCategoriesProvider_ForComplexDescriptions aProvider( aComplexLabels );
+ return ExplicitCategoriesProvider::getExplicitSimpleCategories( aProvider );
+}
+
+// ____ XChartData (base of XChartDataArray) ____
+void SAL_CALL InternalDataProvider::addChartDataChangeEventListener(
+ const Reference< css::chart::XChartDataChangeEventListener >& )
+{
+}
+
+void SAL_CALL InternalDataProvider::removeChartDataChangeEventListener(
+ const Reference< css::chart::XChartDataChangeEventListener >& )
+{
+}
+
+double SAL_CALL InternalDataProvider::getNotANumber()
+{
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+sal_Bool SAL_CALL InternalDataProvider::isNotANumber( double nNumber )
+{
+ return std::isnan( nNumber )
+ || std::isinf( nNumber );
+}
+// lang::XInitialization:
+void SAL_CALL InternalDataProvider::initialize(const uno::Sequence< uno::Any > & _aArguments)
+{
+ comphelper::SequenceAsHashMap aArgs(_aArguments);
+ if ( aArgs.getUnpackedValueOrDefault( "CreateDefaultData", false ) )
+ m_aInternalData.createDefaultData();
+}
+
+// ____ XCloneable ____
+Reference< util::XCloneable > SAL_CALL InternalDataProvider::createClone()
+{
+ return Reference< util::XCloneable >( new InternalDataProvider( *this ));
+}
+
+OUString SAL_CALL InternalDataProvider::getImplementationName()
+{
+ // note: in xmloff this name is used to indicate usage of own data
+ return "com.sun.star.comp.chart.InternalDataProvider";
+}
+
+sal_Bool SAL_CALL InternalDataProvider::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL InternalDataProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.data.DataProvider" };
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart_InternalDataProvider_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::InternalDataProvider);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LabeledDataSequence.cxx b/chart2/source/tools/LabeledDataSequence.cxx
new file mode 100644
index 000000000..9557f05ae
--- /dev/null
+++ b/chart2/source/tools/LabeledDataSequence.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 <LabeledDataSequence.hxx>
+#include <ModifyListenerHelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+LabeledDataSequence::LabeledDataSequence() :
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{}
+
+LabeledDataSequence::LabeledDataSequence(
+ const uno::Reference< chart2::data::XDataSequence > & rValues ) :
+ m_xData( rValues ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ ModifyListenerHelper::addListener( m_xData, m_xModifyEventForwarder );
+}
+
+LabeledDataSequence::LabeledDataSequence(
+ const uno::Reference< chart2::data::XDataSequence > & rValues,
+ const uno::Reference< chart2::data::XDataSequence > & rLabel ) :
+ m_xData( rValues ),
+ m_xLabel( rLabel ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ ModifyListenerHelper::addListener( m_xData, m_xModifyEventForwarder );
+ ModifyListenerHelper::addListener( m_xLabel, m_xModifyEventForwarder );
+}
+
+LabeledDataSequence::LabeledDataSequence( const LabeledDataSequence& rSource ) :
+ impl::LabeledDataSequence_Base(),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ uno::Reference< chart2::data::XDataSequence > xNewValues( rSource.m_xData );
+ uno::Reference< chart2::data::XDataSequence > xNewLabel( rSource.m_xLabel );
+
+ uno::Reference< util::XCloneable > xLabelCloneable( rSource.m_xLabel, uno::UNO_QUERY );
+ if( xLabelCloneable.is())
+ xNewLabel.set( xLabelCloneable->createClone(), uno::UNO_QUERY );
+
+ uno::Reference< util::XCloneable > xValuesCloneable( rSource.m_xData, uno::UNO_QUERY );
+ if( xValuesCloneable.is())
+ xNewValues.set( xValuesCloneable->createClone(), uno::UNO_QUERY );
+
+ m_xData = xNewValues;
+ m_xLabel = xNewLabel;
+
+ ModifyListenerHelper::addListener( m_xData, m_xModifyEventForwarder );
+ ModifyListenerHelper::addListener( m_xLabel, m_xModifyEventForwarder );
+}
+
+LabeledDataSequence::~LabeledDataSequence()
+{
+ if( m_xModifyEventForwarder.is())
+ {
+ if( m_xData.is())
+ ModifyListenerHelper::removeListener( m_xData, m_xModifyEventForwarder );
+ if( m_xLabel.is())
+ ModifyListenerHelper::removeListener( m_xLabel, m_xModifyEventForwarder );
+ }
+}
+
+// ____ XLabeledDataSequence ____
+uno::Reference< chart2::data::XDataSequence > SAL_CALL LabeledDataSequence::getValues()
+{
+ return m_xData;
+}
+
+void SAL_CALL LabeledDataSequence::setValues(
+ const uno::Reference< chart2::data::XDataSequence >& xSequence )
+{
+ if( m_xData != xSequence )
+ {
+ ModifyListenerHelper::removeListener( m_xData, m_xModifyEventForwarder );
+ m_xData = xSequence;
+ ModifyListenerHelper::addListener( m_xData, m_xModifyEventForwarder );
+ }
+}
+
+uno::Reference< chart2::data::XDataSequence > SAL_CALL LabeledDataSequence::getLabel()
+{
+ return m_xLabel;
+}
+
+void SAL_CALL LabeledDataSequence::setLabel(
+ const uno::Reference< chart2::data::XDataSequence >& xSequence )
+{
+ if( m_xLabel != xSequence )
+ {
+ ModifyListenerHelper::removeListener( m_xLabel, m_xModifyEventForwarder );
+ m_xLabel = xSequence;
+ ModifyListenerHelper::addListener( m_xLabel, m_xModifyEventForwarder );
+ }
+}
+
+// ____ XCloneable ____
+uno::Reference< util::XCloneable > SAL_CALL LabeledDataSequence::createClone()
+{
+ uno::Reference< chart2::data::XDataSequence > xNewValues( m_xData );
+ uno::Reference< chart2::data::XDataSequence > xNewLabel( m_xLabel );
+
+ uno::Reference< util::XCloneable > xLabelCloneable( m_xLabel, uno::UNO_QUERY );
+ if( xLabelCloneable.is())
+ xNewLabel.set( xLabelCloneable->createClone(), uno::UNO_QUERY );
+
+ uno::Reference< util::XCloneable > xValuesCloneable( m_xData, uno::UNO_QUERY );
+ if( xValuesCloneable.is())
+ xNewValues.set( xValuesCloneable->createClone(), uno::UNO_QUERY );
+
+ return uno::Reference< util::XCloneable >(
+ new LabeledDataSequence( xNewValues, xNewLabel ) );
+}
+
+// ____ XModifyBroadcaster ____
+void SAL_CALL LabeledDataSequence::addModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL LabeledDataSequence::removeModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+OUString SAL_CALL LabeledDataSequence::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.LabeledDataSequence";
+}
+
+sal_Bool SAL_CALL LabeledDataSequence::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LabeledDataSequence::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.data.LabeledDataSequence" };
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_LabeledDataSequence_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::LabeledDataSequence );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LegendHelper.cxx b/chart2/source/tools/LegendHelper.cxx
new file mode 100644
index 000000000..5bb48f752
--- /dev/null
+++ b/chart2/source/tools/LegendHelper.cxx
@@ -0,0 +1,122 @@
+/* -*- 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 <LegendHelper.hxx>
+#include <Legend.hxx>
+#include <ChartModel.hxx>
+#include <Diagram.hxx>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+rtl::Reference< Legend > LegendHelper::showLegend( ChartModel& rModel
+ , const uno::Reference< uno::XComponentContext >& xContext )
+{
+ rtl::Reference< Legend > xLegend = LegendHelper::getLegend( rModel, xContext, true );
+ if( xLegend.is())
+ {
+ xLegend->setPropertyValue( "Show", uno::Any(true) );
+
+ chart2::RelativePosition aRelativePosition;
+ if( !(xLegend->getPropertyValue( "RelativePosition") >>= aRelativePosition) )
+ {
+ chart2::LegendPosition ePos = chart2::LegendPosition_LINE_END;
+ if( !(xLegend->getPropertyValue( "AnchorPosition") >>= ePos ) )
+ xLegend->setPropertyValue( "AnchorPosition", uno::Any( ePos ));
+
+ css::chart::ChartLegendExpansion eExpansion =
+ ( ePos == chart2::LegendPosition_LINE_END ||
+ ePos == chart2::LegendPosition_LINE_START )
+ ? css::chart::ChartLegendExpansion_HIGH
+ : css::chart::ChartLegendExpansion_WIDE;
+ if( !(xLegend->getPropertyValue( "Expansion") >>= eExpansion ) )
+ xLegend->setPropertyValue( "Expansion", uno::Any( eExpansion ));
+
+ xLegend->setPropertyValue( "RelativePosition", uno::Any());
+ }
+
+ }
+ return xLegend;
+}
+
+void LegendHelper::hideLegend( ChartModel& rModel )
+{
+ rtl::Reference< Legend > xLegend = LegendHelper::getLegend( rModel, nullptr );
+ if( xLegend.is())
+ {
+ xLegend->setPropertyValue( "Show", uno::Any(false) );
+ }
+}
+
+rtl::Reference< Legend > LegendHelper::getLegend(
+ ChartModel& rModel
+ , const uno::Reference< uno::XComponentContext >& xContext
+ , bool bCreate )
+{
+ rtl::Reference< Legend > xResult;
+
+ try
+ {
+ rtl::Reference< Diagram > xDia( rModel.getFirstChartDiagram());
+ if( xDia.is() )
+ {
+ xResult = xDia->getLegend2();
+ if( bCreate && !xResult.is() && xContext.is() )
+ {
+ xResult = new Legend();
+ xDia->setLegend( xResult );
+ }
+ }
+ else if(bCreate)
+ {
+ OSL_FAIL("need diagram for creation of legend");
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return xResult;
+}
+
+bool LegendHelper::hasLegend( const rtl::Reference< Diagram > & xDiagram )
+{
+ bool bReturn = false;
+ if( xDiagram.is())
+ {
+ uno::Reference< beans::XPropertySet > xLegendProp( xDiagram->getLegend(), uno::UNO_QUERY );
+ if( xLegendProp.is())
+ xLegendProp->getPropertyValue( "Show") >>= bReturn;
+ }
+
+ return bReturn;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LifeTime.cxx b/chart2/source/tools/LifeTime.cxx
new file mode 100644
index 000000000..7a84f4f64
--- /dev/null
+++ b/chart2/source/tools/LifeTime.cxx
@@ -0,0 +1,441 @@
+/* -*- 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 <LifeTime.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+
+namespace apphelper
+{
+
+LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent )
+ : m_aListenerContainer( m_aAccessMutex )
+ , m_pComponent(pComponent)
+{
+ m_bDisposed = false;
+ m_bInDispose = false;
+ m_nAccessCount = 0;
+ m_nLongLastingCallCount = 0;
+ m_aNoAccessCountCondition.set();
+ m_aNoLongLastingCallCountCondition.set();
+}
+
+LifeTimeManager::~LifeTimeManager()
+{
+}
+
+bool LifeTimeManager::impl_isDisposed( bool bAssert )
+{
+ if( m_bDisposed || m_bInDispose )
+ {
+ if( bAssert )
+ {
+ OSL_FAIL( "This component is already disposed " );
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LifeTimeManager::impl_canStartApiCall()
+{
+ if( impl_isDisposed() )
+ return false; //behave passive if already disposed
+
+ //mutex is acquired
+ return true;
+}
+
+void LifeTimeManager::impl_registerApiCall(bool bLongLastingCall)
+{
+ //only allowed if not disposed
+ //do not acquire the mutex here because it will be acquired already
+ m_nAccessCount++;
+ if(m_nAccessCount==1)
+ //@todo? is it ok to wake some threads here while we have acquired the mutex?
+ m_aNoAccessCountCondition.reset();
+
+ if(bLongLastingCall)
+ m_nLongLastingCallCount++;
+ if(m_nLongLastingCallCount==1)
+ m_aNoLongLastingCallCountCondition.reset();
+}
+
+void LifeTimeManager::impl_unregisterApiCall(bool bLongLastingCall)
+{
+ //Mutex needs to be acquired exactly once
+ //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
+
+ OSL_ENSURE( m_nAccessCount>0, "access count mismatch" );
+ m_nAccessCount--;
+ if(bLongLastingCall)
+ m_nLongLastingCallCount--;
+ if( m_nLongLastingCallCount==0 )
+ {
+ m_aNoLongLastingCallCountCondition.set();
+ }
+ if( m_nAccessCount== 0)
+ {
+ m_aNoAccessCountCondition.set();
+ impl_apiCallCountReachedNull();
+
+ }
+}
+
+bool LifeTimeManager::dispose()
+{
+ //hold no mutex
+ {
+ osl::MutexGuard aGuard( m_aAccessMutex );
+
+ if( m_bDisposed || m_bInDispose )
+ {
+ SAL_WARN("chart2", "This component is already disposed " );
+ return false; //behave passive if already disposed
+ }
+
+ m_bInDispose = true;
+ //adding any listener is not allowed anymore
+ //new calls will not be accepted
+ //still running calls have the freedom to finish their work without crash
+ }
+ //no mutex is acquired
+
+ //--do the disposing of listeners after calling this method
+ {
+ uno::Reference< lang::XComponent > xComponent(m_pComponent);
+ if(xComponent.is())
+ {
+ // notify XCLoseListeners
+ lang::EventObject aEvent( xComponent );
+ m_aListenerContainer.disposeAndClear( aEvent );
+ }
+ }
+
+ //no mutex is acquired
+ {
+ osl::MutexGuard aGuard( m_aAccessMutex );
+ OSL_ENSURE( !m_bDisposed, "dispose was called already" );
+ m_bDisposed = true;
+ }
+ //no mutex is acquired
+
+ //wait until all still running calls have finished
+ //the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
+ m_aNoAccessCountCondition.wait();
+
+ //we are the only ones working on our data now
+
+ return true;
+ //--release all resources and references after calling this method successful
+}
+
+CloseableLifeTimeManager::CloseableLifeTimeManager( css::util::XCloseable* pCloseable
+ , css::lang::XComponent* pComponent )
+ : LifeTimeManager( pComponent )
+ , m_pCloseable(pCloseable)
+{
+ m_bClosed = false;
+ m_bInTryClose = false;
+ m_bOwnership = false;
+ m_aEndTryClosingCondition.set();
+}
+
+CloseableLifeTimeManager::~CloseableLifeTimeManager()
+{
+}
+
+bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert )
+{
+ if( impl_isDisposed( bAssert ) )
+ return true;
+
+ if( m_bClosed )
+ {
+ if( bAssert )
+ {
+ OSL_FAIL( "This object is already closed" );
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CloseableLifeTimeManager::g_close_startTryClose(bool bDeliverOwnership)
+{
+ //no mutex is allowed to be acquired
+ {
+ osl::MutexGuard aGuard( m_aAccessMutex );
+ if( impl_isDisposedOrClosed(false) )
+ return false;
+
+ //Mutex needs to be acquired exactly once; will be released inbetween
+ if( !impl_canStartApiCall() )
+ return false;
+ //mutex is acquired
+
+ //not closed already -> we try to close again
+ m_bInTryClose = true;
+ m_aEndTryClosingCondition.reset();
+
+ impl_registerApiCall(false);
+ }
+
+ //no mutex is acquired
+
+ //only remove listener calls will be worked on until end of tryclose
+ //all other new calls will wait till end of try close // @todo? is that really ok
+
+ //?? still running calls have the freedom to finish their work without crash
+
+ try
+ {
+ uno::Reference< util::XCloseable > xCloseable(m_pCloseable);
+ if(xCloseable.is())
+ {
+ //--call queryClosing on all registered close listeners
+ ::comphelper::OInterfaceContainerHelper2* pIC = m_aListenerContainer.getContainer(
+ cppu::UnoType<util::XCloseListener>::get());
+ if( pIC )
+ {
+ lang::EventObject aEvent( xCloseable );
+ ::comphelper::OInterfaceIteratorHelper2 aIt( *pIC );
+ while( aIt.hasMoreElements() )
+ {
+ static_cast< util::XCloseListener* >( aIt.next() )
+ ->queryClosing( aEvent, bDeliverOwnership );
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ //no mutex is acquired
+ g_close_endTryClose(bDeliverOwnership);
+ throw;
+ }
+ return true;
+}
+
+void CloseableLifeTimeManager::g_close_endTryClose(bool bDeliverOwnership )
+{
+ //this method is called, if the try to close was not successful
+ osl::MutexGuard aGuard( m_aAccessMutex );
+ impl_setOwnership( bDeliverOwnership, false );
+
+ m_bInTryClose = false;
+ m_aEndTryClosingCondition.set();
+
+ //Mutex needs to be acquired exactly once
+ //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
+ impl_unregisterApiCall(false);
+}
+
+void CloseableLifeTimeManager::g_close_isNeedToCancelLongLastingCalls( bool bDeliverOwnership, util::CloseVetoException const & ex )
+{
+ //this method is called when no closelistener has had a veto during queryclosing
+ //the method returns false, if nothing stands against closing anymore
+ //it returns true, if some longlasting calls are running, which might be cancelled
+ //it throws the given exception, if long calls are running but not cancelable
+
+ osl::MutexGuard aGuard( m_aAccessMutex );
+ //this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
+ if( !m_nLongLastingCallCount )
+ return;
+
+ impl_setOwnership( bDeliverOwnership, true );
+
+ m_bInTryClose = false;
+ m_aEndTryClosingCondition.set();
+
+ //Mutex needs to be acquired exactly once
+ //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
+ impl_unregisterApiCall(false);
+
+ throw ex;
+}
+
+void CloseableLifeTimeManager::g_close_endTryClose_doClose()
+{
+ //this method is called, if the try to close was successful
+ osl::MutexGuard aGuard( m_aAccessMutex );
+
+ m_bInTryClose = false;
+ m_aEndTryClosingCondition.set();
+
+ //Mutex needs to be acquired exactly once
+ //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
+ impl_unregisterApiCall(false);
+ impl_doClose();
+}
+
+void CloseableLifeTimeManager::impl_setOwnership( bool bDeliverOwnership, bool bMyVeto )
+{
+ m_bOwnership = bDeliverOwnership && bMyVeto;
+}
+
+void CloseableLifeTimeManager::impl_apiCallCountReachedNull()
+{
+ //Mutex needs to be acquired exactly once
+ //mutex will be released inbetween in impl_doClose()
+ if( m_pCloseable && m_bOwnership )
+ impl_doClose();
+}
+
+void CloseableLifeTimeManager::impl_doClose()
+{
+ //Mutex needs to be acquired exactly once before calling impl_doClose()
+
+ if(m_bClosed)
+ return; //behave as passive as possible, if disposed or closed already
+ if( m_bDisposed || m_bInDispose )
+ return; //behave as passive as possible, if disposed or closed already
+
+ m_bClosed = true;
+
+ NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex );
+ //mutex is not acquired, mutex will be reacquired at the end of this method automatically
+
+ uno::Reference< util::XCloseable > xCloseable;
+ try
+ {
+ xCloseable.set(m_pCloseable);
+ if(xCloseable.is())
+ {
+ //--call notifyClosing on all registered close listeners
+ ::comphelper::OInterfaceContainerHelper2* pIC = m_aListenerContainer.getContainer(
+ cppu::UnoType<util::XCloseListener>::get());
+ if( pIC )
+ {
+ lang::EventObject aEvent( xCloseable );
+ ::comphelper::OInterfaceIteratorHelper2 aIt( *pIC );
+ while( aIt.hasMoreElements() )
+ {
+ static_cast< util::XCloseListener* >( aIt.next() )->notifyClosing( aEvent );
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ if(xCloseable.is())
+ {
+ uno::Reference< lang::XComponent > xComponent( xCloseable, uno::UNO_QUERY );
+ if(xComponent.is())
+ {
+ OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
+ xComponent->dispose();
+ }
+ }
+ //mutex will be reacquired in destructor of aNegativeGuard
+}
+
+void CloseableLifeTimeManager::g_addCloseListener( const uno::Reference< util::XCloseListener > & xListener )
+{
+ osl::MutexGuard aGuard( m_aAccessMutex );
+ //Mutex needs to be acquired exactly once; will be released inbetween
+ if( !impl_canStartApiCall() )
+ return;
+ //mutex is acquired
+
+ m_aListenerContainer.addInterface( cppu::UnoType<util::XCloseListener>::get(),xListener );
+ m_bOwnership = false;
+}
+
+bool CloseableLifeTimeManager::impl_canStartApiCall()
+{
+ //Mutex needs to be acquired exactly once before calling this method
+ //the mutex will be released inbetween and reacquired
+
+ if( impl_isDisposed() )
+ return false; //behave passive if already disposed
+ if( m_bClosed )
+ return false; //behave passive if closing is already done
+
+ //during try-close most calls need to wait for the decision
+ while( m_bInTryClose )
+ {
+ //if someone tries to close this object at the moment
+ //we need to wait for his end because the result of the preceding call
+ //is relevant for our behaviour here
+
+ m_aAccessMutex.release();
+ m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
+ m_aAccessMutex.acquire();
+ if( m_bDisposed || m_bInDispose || m_bClosed )
+ return false; //return if closed already
+ }
+ //mutex is acquired
+ return true;
+}
+
+bool LifeTimeGuard::startApiCall(bool bLongLastingCall)
+{
+ //Mutex needs to be acquired exactly once; will be released inbetween
+ //mutex is required due to constructor of LifeTimeGuard
+
+ OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
+ if(m_bCallRegistered)
+ return false;
+
+ //Mutex needs to be acquired exactly once; will be released inbetween
+ if( !m_rManager.impl_canStartApiCall() )
+ return false;
+ //mutex is acquired
+
+ m_bCallRegistered = true;
+ m_bLongLastingCallRegistered = bLongLastingCall;
+ m_rManager.impl_registerApiCall(bLongLastingCall);
+ return true;
+}
+
+LifeTimeGuard::~LifeTimeGuard()
+{
+ try
+ {
+ //do acquire the mutex if it was cleared before
+ osl::MutexGuard g(m_rManager.m_aAccessMutex);
+ if(m_bCallRegistered)
+ {
+ //Mutex needs to be acquired exactly once
+ //mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
+ m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered);
+ }
+ }
+ catch( uno::Exception& ex )
+ {
+ //@todo ? allow a uno::RuntimeException from dispose to travel through??
+ ex.Context.is(); //to avoid compilation warnings
+ }
+}
+
+}//end namespace apphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LinePropertiesHelper.cxx b/chart2/source/tools/LinePropertiesHelper.cxx
new file mode 100644
index 000000000..7e6d28860
--- /dev/null
+++ b/chart2/source/tools/LinePropertiesHelper.cxx
@@ -0,0 +1,192 @@
+/* -*- 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 <LinePropertiesHelper.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/LineCap.hpp>
+#include <com/sun/star/drawing/LineJoint.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace chart
+{
+
+void LinePropertiesHelper::AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ // Line Properties see service drawing::LineProperties
+ rOutProperties.emplace_back( "LineStyle",
+ PROP_LINE_STYLE,
+ cppu::UnoType<drawing::LineStyle>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "LineDash",
+ PROP_LINE_DASH,
+ cppu::UnoType<drawing::LineDash>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+
+//not in service description
+ rOutProperties.emplace_back( "LineDashName",
+ PROP_LINE_DASH_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT
+ | beans::PropertyAttribute::MAYBEVOID );
+
+ rOutProperties.emplace_back( "LineColor",
+ PROP_LINE_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "LineTransparence",
+ PROP_LINE_TRANSPARENCE,
+ cppu::UnoType<sal_Int16>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "LineWidth",
+ PROP_LINE_WIDTH,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "LineJoint",
+ PROP_LINE_JOINT,
+ cppu::UnoType<drawing::LineJoint>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "LineCap",
+ PROP_LINE_CAP,
+ cppu::UnoType<drawing::LineCap>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+}
+
+void LinePropertiesHelper::AddDefaultsToMap(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_LINE_STYLE, drawing::LineStyle_SOLID );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_LINE_WIDTH, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_LINE_COLOR, 0x000000 ); // black
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int16 >( rOutMap, PROP_LINE_TRANSPARENCE, 0 );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_LINE_JOINT, drawing::LineJoint_ROUND );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_LINE_CAP, drawing::LineCap_BUTT );
+}
+
+bool LinePropertiesHelper::IsLineVisible( const css::uno::Reference<
+ css::beans::XPropertySet >& xLineProperties )
+{
+ bool bRet = false;
+ try
+ {
+ if( xLineProperties.is() )
+ {
+ drawing::LineStyle aLineStyle(drawing::LineStyle_SOLID);
+ xLineProperties->getPropertyValue( "LineStyle" ) >>= aLineStyle;
+ if( aLineStyle != drawing::LineStyle_NONE )
+ {
+ sal_Int16 nLineTransparence=0;
+ xLineProperties->getPropertyValue( "LineTransparence" ) >>= nLineTransparence;
+ if(nLineTransparence!=100)
+ {
+ bRet = true;
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return bRet;
+}
+
+void LinePropertiesHelper::SetLineVisible( const css::uno::Reference<
+ css::beans::XPropertySet >& xLineProperties )
+{
+ try
+ {
+ if( xLineProperties.is() )
+ {
+ drawing::LineStyle aLineStyle(drawing::LineStyle_SOLID);
+ xLineProperties->getPropertyValue( "LineStyle" ) >>= aLineStyle;
+ if( aLineStyle == drawing::LineStyle_NONE )
+ xLineProperties->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_SOLID ) );
+
+ sal_Int16 nLineTransparence=0;
+ xLineProperties->getPropertyValue( "LineTransparence" ) >>= nLineTransparence;
+ if(nLineTransparence==100)
+ xLineProperties->setPropertyValue( "LineTransparence", uno::Any( sal_Int16(0) ) );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void LinePropertiesHelper::SetLineInvisible( const css::uno::Reference<
+ css::beans::XPropertySet >& xLineProperties )
+{
+ try
+ {
+ if( xLineProperties.is() )
+ {
+ drawing::LineStyle aLineStyle(drawing::LineStyle_SOLID);
+ xLineProperties->getPropertyValue( "LineStyle" ) >>= aLineStyle;
+ if( aLineStyle != drawing::LineStyle_NONE )
+ xLineProperties->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_NONE ) );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void LinePropertiesHelper::SetLineColor( const css::uno::Reference<
+ css::beans::XPropertySet >& xLineProperties, sal_Int32 nColor )
+{
+ try
+ {
+ if( xLineProperties.is() )
+ {
+ xLineProperties->setPropertyValue( "LineColor", uno::Any( nColor ) );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LinearRegressionCurveCalculator.cxx b/chart2/source/tools/LinearRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..ee836555b
--- /dev/null
+++ b/chart2/source/tools/LinearRegressionCurveCalculator.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 <LinearRegressionCurveCalculator.hxx>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+LinearRegressionCurveCalculator::LinearRegressionCurveCalculator()
+{}
+
+LinearRegressionCurveCalculator::~LinearRegressionCurveCalculator()
+{}
+
+void LinearRegressionCurveCalculator::setRegressionProperties(
+ sal_Int32 /*aDegree*/,
+ sal_Bool aForceIntercept,
+ double aInterceptValue,
+ sal_Int32 aPeriod,
+ sal_Int32 /*nMovingType*/)
+{
+ PolynomialRegressionCurveCalculator::setRegressionProperties(
+ 1,
+ aForceIntercept,
+ aInterceptValue,
+ aPeriod,
+ 0);
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL LinearRegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const uno::Reference< chart2::XScaling >& xScalingX,
+ const uno::Reference< chart2::XScaling >& xScalingY,
+ sal_Bool bMaySkipPointsInCalculation )
+{
+ if( bMaySkipPointsInCalculation &&
+ isLinearScaling( xScalingX ) &&
+ isLinearScaling( xScalingY ))
+ {
+ // optimize result
+ uno::Sequence< geometry::RealPoint2D > aResult{ { min, getCurveValue( min ) },
+ { max, getCurveValue( max ) } };
+
+ return aResult;
+ }
+ return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/LogarithmicRegressionCurveCalculator.cxx b/chart2/source/tools/LogarithmicRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..ffd1808df
--- /dev/null
+++ b/chart2/source/tools/LogarithmicRegressionCurveCalculator.cxx
@@ -0,0 +1,186 @@
+/* -*- 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 <LogarithmicRegressionCurveCalculator.hxx>
+#include <RegressionCalculationHelper.hxx>
+#include <SpecialCharacters.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+LogarithmicRegressionCurveCalculator::LogarithmicRegressionCurveCalculator() :
+ m_fSlope( std::numeric_limits<double>::quiet_NaN() ),
+ m_fIntercept( std::numeric_limits<double>::quiet_NaN() )
+{
+}
+
+LogarithmicRegressionCurveCalculator::~LogarithmicRegressionCurveCalculator()
+{}
+
+// ____ XRegressionCurve ____
+void SAL_CALL LogarithmicRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& aXValues,
+ const uno::Sequence< double >& aYValues )
+{
+ RegressionCalculationHelper::tDoubleVectorPair aValues(
+ RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValidAndXPositive()));
+
+ const size_t nMax = aValues.first.size();
+ if( nMax <= 1 ) // at least 2 points
+ {
+ m_fSlope = std::numeric_limits<double>::quiet_NaN();
+ m_fIntercept = std::numeric_limits<double>::quiet_NaN();
+ m_fCorrelationCoefficient = std::numeric_limits<double>::quiet_NaN();
+ return;
+ }
+
+ double fAverageX = 0.0, fAverageY = 0.0;
+ size_t i = 0;
+ for( i = 0; i < nMax; ++i )
+ {
+ fAverageX += log( aValues.first[i] );
+ fAverageY += aValues.second[i];
+ }
+
+ const double fN = static_cast< double >( nMax );
+ fAverageX /= fN;
+ fAverageY /= fN;
+
+ double fQx = 0.0, fQy = 0.0, fQxy = 0.0;
+ for( i = 0; i < nMax; ++i )
+ {
+ double fDeltaX = log( aValues.first[i] ) - fAverageX;
+ double fDeltaY = aValues.second[i] - fAverageY;
+
+ fQx += fDeltaX * fDeltaX;
+ fQy += fDeltaY * fDeltaY;
+ fQxy += fDeltaX * fDeltaY;
+ }
+
+ m_fSlope = fQxy / fQx;
+ m_fIntercept = fAverageY - m_fSlope * fAverageX;
+ m_fCorrelationCoefficient = fQxy / sqrt( fQx * fQy );
+}
+
+double SAL_CALL LogarithmicRegressionCurveCalculator::getCurveValue( double x )
+{
+ if( ! ( std::isnan( m_fSlope ) ||
+ std::isnan( m_fIntercept )))
+ {
+ return m_fSlope * log( x ) + m_fIntercept;
+ }
+
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL LogarithmicRegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const uno::Reference< chart2::XScaling >& xScalingX,
+ const uno::Reference< chart2::XScaling >& xScalingY,
+ sal_Bool bMaySkipPointsInCalculation )
+{
+ if( bMaySkipPointsInCalculation &&
+ isLogarithmicScaling( xScalingX ) &&
+ isLinearScaling( xScalingY ))
+ {
+ // optimize result
+ uno::Sequence< geometry::RealPoint2D > aResult{ { min, getCurveValue( min ) },
+ { max, getCurveValue( max ) } };
+
+ return aResult;
+ }
+ return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
+}
+
+OUString LogarithmicRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey, sal_Int32* pFormulaMaxWidth /* = nullptr */ ) const
+{
+ bool bHasSlope = !rtl::math::approxEqual( fabs( m_fSlope ), 1.0 );
+ OUStringBuffer aBuf( mYName + " = " );
+ sal_Int32 nLineLength = aBuf.getLength();
+ sal_Int32 nValueLength=0;
+ if ( pFormulaMaxWidth && *pFormulaMaxWidth > 0 ) // count nValueLength
+ {
+ sal_Int32 nCharMin = nLineLength + 6 + mXName.getLength(); // 6 = "ln(x)" + 2 extra characters
+ if( m_fSlope < 0.0 )
+ nCharMin += 2; // "- "
+ if( m_fSlope != 0.0 && m_fIntercept != 0.0 )
+ {
+ nCharMin += 3; // " + "
+ if ( bHasSlope )
+ nValueLength = (*pFormulaMaxWidth - nCharMin) / 2;
+ }
+ if ( nValueLength == 0 ) // not yet calculated
+ nValueLength = *pFormulaMaxWidth - nCharMin;
+ if ( nValueLength <= 0 )
+ nValueLength = 1;
+ }
+
+ // temporary buffer
+ OUStringBuffer aTmpBuf("");
+ // if nValueLength not calculated then nullptr
+ sal_Int32* pValueLength = nValueLength ? &nValueLength : nullptr;
+ if( m_fSlope != 0.0 ) // add slope value
+ {
+ if( m_fSlope < 0.0 )
+ {
+ aTmpBuf.append( OUStringChar(aMinusSign) + " " );
+ }
+ if( bHasSlope )
+ {
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fSlope), pValueLength );
+ if ( aValueString != "1" ) // aValueString may be rounded to 1 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString + " " );
+ }
+ }
+ aTmpBuf.append( "ln(" + mXName + ") " );
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+ aTmpBuf.truncate();
+
+ if( m_fIntercept > 0.0 )
+ aTmpBuf.append( "+ " );
+ }
+ // add intercept value
+ if( m_fIntercept < 0.0 )
+ aTmpBuf.append( OUStringChar(aMinusSign) + " " );
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fIntercept), pValueLength );
+ if ( aValueString != "0" ) // aValueString may be rounded to 0 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString );
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+ }
+
+ if ( std::u16string_view(aBuf) == OUStringConcatenation(mYName + " = ") )
+ aBuf.append( "0" );
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/MeanValueRegressionCurveCalculator.cxx b/chart2/source/tools/MeanValueRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..aefd3f8f0
--- /dev/null
+++ b/chart2/source/tools/MeanValueRegressionCurveCalculator.cxx
@@ -0,0 +1,126 @@
+/* -*- 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 <MeanValueRegressionCurveCalculator.hxx>
+
+#include <osl/diagnose.h>
+
+#include <cmath>
+#include <limits>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+MeanValueRegressionCurveCalculator::MeanValueRegressionCurveCalculator() :
+ m_fMeanValue( std::numeric_limits<double>::quiet_NaN() )
+{
+}
+
+MeanValueRegressionCurveCalculator::~MeanValueRegressionCurveCalculator()
+{}
+
+// ____ XRegressionCurveCalculator ____
+void SAL_CALL MeanValueRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& /*aXValues*/,
+ const uno::Sequence< double >& aYValues )
+{
+ const sal_Int32 nDataLength = aYValues.getLength();
+ sal_Int32 nMax = nDataLength;
+ double fSumY = 0.0;
+ const double * pY = aYValues.getConstArray();
+
+ for( sal_Int32 i = 0; i < nDataLength; ++i )
+ {
+ if( std::isnan( pY[i] ) ||
+ std::isinf( pY[i] ))
+ --nMax;
+ else
+ fSumY += pY[i];
+ }
+
+ m_fCorrelationCoefficient = 0.0;
+
+ if( nMax == 0 )
+ {
+ m_fMeanValue = std::numeric_limits<double>::quiet_NaN();
+ }
+ else
+ {
+ m_fMeanValue = fSumY / static_cast< double >( nMax );
+
+ // correlation coefficient: standard deviation
+ if( nMax > 1 )
+ {
+ double fErrorSum = 0.0;
+ for( sal_Int32 i = 0; i < nDataLength; ++i )
+ {
+ if( !std::isnan( pY[i] ) &&
+ !std::isinf( pY[i] ))
+ {
+ double v = m_fMeanValue - pY[i];
+ fErrorSum += (v*v);
+ }
+ }
+ OSL_ASSERT( fErrorSum >= 0.0 );
+ m_fCorrelationCoefficient = sqrt( fErrorSum / (nMax - 1 ));
+ }
+ }
+}
+
+double SAL_CALL MeanValueRegressionCurveCalculator::getCurveValue( double /*x*/ )
+{
+ return m_fMeanValue;
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL MeanValueRegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const uno::Reference< chart2::XScaling >& xScalingX,
+ const uno::Reference< chart2::XScaling >& xScalingY,
+ sal_Bool bMaySkipPointsInCalculation )
+{
+ if( bMaySkipPointsInCalculation )
+ {
+ // optimize result
+ uno::Sequence< geometry::RealPoint2D > aResult{ { min, m_fMeanValue },
+ { max, m_fMeanValue } };
+
+ return aResult;
+ }
+ return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
+}
+
+OUString MeanValueRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey, sal_Int32* pFormulaLength /* = nullptr */ ) const
+{
+ OUString aBuf(mYName + " = ");
+ if ( pFormulaLength )
+ {
+ *pFormulaLength -= aBuf.getLength();
+ if ( *pFormulaLength <= 0 )
+ return "###";
+ }
+ return ( aBuf + getFormattedString( xNumFormatter, nNumberFormatKey, m_fMeanValue, pFormulaLength ) );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/MediaDescriptorHelper.cxx b/chart2/source/tools/MediaDescriptorHelper.cxx
new file mode 100644
index 000000000..bd217b0fb
--- /dev/null
+++ b/chart2/source/tools/MediaDescriptorHelper.cxx
@@ -0,0 +1,225 @@
+/* -*- 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 <MediaDescriptorHelper.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+using namespace ::com::sun::star;
+
+namespace apphelper
+{
+
+MediaDescriptorHelper::MediaDescriptorHelper( const uno::Sequence<
+ beans::PropertyValue > & rMediaDescriptor )
+ : m_aModelProperties(rMediaDescriptor.getLength())
+{
+ auto aModelPropertiesRange = asNonConstRange(m_aModelProperties);
+ css::uno::Sequence< css::beans::PropertyValue >
+ aRegularProperties(rMediaDescriptor.getLength()); //these are the properties which are described in service com.sun.star.document.MediaDescriptor and not marked as deprecated
+ auto aRegularPropertiesRange = asNonConstRange(aRegularProperties);
+ impl_init();
+ sal_Int32 nRegularCount = 0;
+ sal_Int32 nModelCount = 0;
+
+ auto addRegularProp = [&aRegularPropertiesRange, &nRegularCount](const beans::PropertyValue& rRegularProp)
+ {
+ aRegularPropertiesRange[nRegularCount] = rRegularProp;
+ ++nRegularCount;
+ };
+ auto addModelProp = [&aModelPropertiesRange, &nModelCount, &addRegularProp](const beans::PropertyValue& rModelProp)
+ {
+ addRegularProp(rModelProp);
+ aModelPropertiesRange[nModelCount] = rModelProp;
+ ++nModelCount;
+ };
+
+ //read given rMediaDescriptor and store in internal structures:
+ for( const beans::PropertyValue& rProp : rMediaDescriptor)
+ {
+ if (rProp.Name == "AsTemplate")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Author")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "CharacterSet")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Comment")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "ComponentData")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "FilterData")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "FilterName")
+ {
+ ISSET_FilterName = rProp.Value >>= FilterName;
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "FilterOptions")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "FrameName")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Hidden")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "HierarchicalDocumentName")
+ {
+ rProp.Value >>= HierarchicalDocumentName;
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "OutputStream")
+ {
+ ISSET_OutputStream = rProp.Value >>= OutputStream;
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "InputStream")
+ {
+ ISSET_InputStream = rProp.Value >>= InputStream;
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "InteractionHandler")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "JumpMark")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "MediaType")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "OpenNewView")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "Overwrite")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Password")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "PosSize")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "PostData")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "Preview")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "ReadOnly")
+ {
+ rProp.Value >>= ReadOnly;
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "Referer")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "SetEmbedded")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "Silent")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "StatusIndicator")
+ {
+ addRegularProp(rProp);
+ }
+ else if (rProp.Name == "Storage")
+ {
+ ISSET_Storage = rProp.Value >>= Storage;
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Stream")
+ {
+ ISSET_Stream = rProp.Value >>= Stream;
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Unpacked")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "URL")
+ {
+ ISSET_URL = rProp.Value >>= URL;
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "Version")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "ViewData")
+ {
+ addModelProp(rProp);
+ }
+ else if (rProp.Name == "ViewId")
+ {
+ addModelProp(rProp);
+ }
+ }
+
+ aRegularProperties.realloc(nRegularCount);
+ m_aModelProperties.realloc(nModelCount);
+}
+
+void MediaDescriptorHelper::impl_init()
+{
+ ISSET_FilterName = false;
+
+ ISSET_OutputStream = false;
+ ISSET_InputStream = false;
+
+ ReadOnly = false;
+ ISSET_URL = false;
+
+ ISSET_Storage = false;
+ ISSET_Stream = false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ModifyListenerCallBack.cxx b/chart2/source/tools/ModifyListenerCallBack.cxx
new file mode 100644
index 000000000..037ee7a78
--- /dev/null
+++ b/chart2/source/tools/ModifyListenerCallBack.cxx
@@ -0,0 +1,111 @@
+/* -*- 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 <ModifyListenerCallBack.hxx>
+#include <comphelper/compbase.hxx>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace chart {
+
+typedef comphelper::WeakComponentImplHelper< css::util::XModifyListener >
+ ModifyListenerCallBack_Base;
+
+class ModifyListenerCallBack_impl
+ : public ModifyListenerCallBack_Base
+{
+public:
+ explicit ModifyListenerCallBack_impl( const Link<void*,void>& rCallBack );
+
+ void startListening( const Reference< util::XModifyBroadcaster >& xBroadcaster );
+ void stopListening();
+
+ //XModifyListener
+ virtual void SAL_CALL modified( const lang::EventObject& aEvent ) override;
+
+ //XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
+
+ using ::comphelper::WeakComponentImplHelperBase::disposing;
+
+private:
+ Link<void*,void> m_aLink;//will be called on modify
+ Reference< util::XModifyBroadcaster > m_xBroadcaster;//broadcaster to listen at
+};
+
+ModifyListenerCallBack_impl::ModifyListenerCallBack_impl( const Link<void*,void>& rCallBack )
+ : m_aLink( rCallBack )
+{
+}
+
+//XModifyListener
+void SAL_CALL ModifyListenerCallBack_impl::modified( const lang::EventObject& /*aEvent*/ )
+{
+ m_aLink.Call(nullptr);
+}
+
+//XEventListener
+void SAL_CALL ModifyListenerCallBack_impl::disposing( const lang::EventObject& /*Source*/ )
+{
+ m_xBroadcaster.clear();
+}
+
+void ModifyListenerCallBack_impl::startListening( const ::com::sun::star::uno::Reference< ::com::sun::star::util::XModifyBroadcaster >& xBroadcaster )
+{
+ if( m_xBroadcaster == xBroadcaster )
+ return;
+
+ stopListening();
+ m_xBroadcaster = xBroadcaster;
+ if( m_xBroadcaster.is() )
+ m_xBroadcaster->addModifyListener( this );
+}
+void ModifyListenerCallBack_impl::stopListening()
+{
+ if( m_xBroadcaster.is() )
+ {
+ m_xBroadcaster->removeModifyListener( this );
+ m_xBroadcaster.clear();
+ }
+}
+
+ModifyListenerCallBack::ModifyListenerCallBack( const Link<void*,void>& rCallBack )
+ : pModifyListener_impl( new ModifyListenerCallBack_impl(rCallBack) )
+ , m_xModifyListener( pModifyListener_impl )
+{
+}
+
+ModifyListenerCallBack::~ModifyListenerCallBack()
+{
+ stopListening();
+}
+
+void ModifyListenerCallBack::startListening( const ::com::sun::star::uno::Reference< ::com::sun::star::util::XModifyBroadcaster >& xBroadcaster )
+{
+ pModifyListener_impl->startListening( xBroadcaster );
+}
+void ModifyListenerCallBack::stopListening()
+{
+ pModifyListener_impl->stopListening();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ModifyListenerHelper.cxx b/chart2/source/tools/ModifyListenerHelper.cxx
new file mode 100644
index 000000000..507e79fa3
--- /dev/null
+++ b/chart2/source/tools/ModifyListenerHelper.cxx
@@ -0,0 +1,74 @@
+/* -*- 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 <ModifyListenerHelper.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+ModifyEventForwarder::ModifyEventForwarder()
+{
+}
+
+// ____ XModifyBroadcaster ____
+void SAL_CALL ModifyEventForwarder::addModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ m_aModifyListeners.addInterface( aGuard, aListener );
+}
+
+void SAL_CALL ModifyEventForwarder::removeModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ m_aModifyListeners.removeInterface( aGuard, aListener );
+}
+
+// ____ XModifyListener ____
+void SAL_CALL ModifyEventForwarder::modified( const lang::EventObject& aEvent )
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( m_aModifyListeners.getLength(aGuard) == 0 )
+ return;
+
+ m_aModifyListeners.notifyEach( aGuard, &util::XModifyListener::modified, aEvent );
+}
+
+// ____ XEventListener (base of XModifyListener) ____
+void SAL_CALL ModifyEventForwarder::disposing( const lang::EventObject& /* Source */ )
+{
+ // nothing
+}
+
+// ____ WeakComponentImplHelperBase ____
+void ModifyEventForwarder::disposing(std::unique_lock<std::mutex>& rGuard)
+{
+ // dispose was called at this
+ m_aModifyListeners.disposeAndClear( rGuard, lang::EventObject( static_cast<cppu::OWeakObject*>(this) ) );
+}
+
+} // namespace chart::ModifyListenerHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/MovingAverageRegressionCurveCalculator.cxx b/chart2/source/tools/MovingAverageRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..6726d9d78
--- /dev/null
+++ b/chart2/source/tools/MovingAverageRegressionCurveCalculator.cxx
@@ -0,0 +1,165 @@
+/* -*- 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 <MovingAverageRegressionCurveCalculator.hxx>
+#include <RegressionCalculationHelper.hxx>
+#include <ResId.hxx>
+#include <strings.hrc>
+
+#include <algorithm>
+#include <limits>
+
+#include <com/sun/star/chart2/MovingAverageType.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+namespace chart
+{
+
+MovingAverageRegressionCurveCalculator::MovingAverageRegressionCurveCalculator()
+{}
+
+MovingAverageRegressionCurveCalculator::~MovingAverageRegressionCurveCalculator()
+{}
+
+// ____ XRegressionCurveCalculator ____
+void SAL_CALL MovingAverageRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& aXValues,
+ const uno::Sequence< double >& aYValues )
+{
+ m_fCorrelationCoefficient = std::numeric_limits<double>::quiet_NaN();
+
+ RegressionCalculationHelper::tDoubleVectorPair aValues(
+ RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValid()));
+
+ aYList.clear();
+ aXList.clear();
+
+ // For formulas, see
+ // https://docs.oasis-open.org/office/OpenDocument/v1.3/cs02/part3-schema/OpenDocument-v1.3-cs02-part3-schema.html#property-chart_regression-moving-type
+
+ switch (mnMovingType)
+ {
+ case MovingAverageType::Central:
+ {
+
+ calculateValuesCentral(std::move(aValues));
+ break;
+ }
+
+ case MovingAverageType::AveragedAbscissa:
+ {
+ calculateValues(std::move(aValues), true);
+ break;
+ }
+ case MovingAverageType::Prior:
+ default:
+ {
+ calculateValues(std::move(aValues), false);
+ break;
+ }
+ }
+}
+
+void MovingAverageRegressionCurveCalculator::calculateValuesCentral(
+ RegressionCalculationHelper::tDoubleVectorPair aValues)
+{
+ const size_t aSize = aValues.first.size();
+ if (aSize == 0)
+ return;
+ for (size_t i = mPeriod - 1; i < aSize; ++i)
+ {
+ double yAvg = 0.0;
+
+ for (sal_Int32 j = 0; j < mPeriod; j++)
+ {
+ yAvg += aValues.second[i - j];
+ }
+ yAvg /= mPeriod;
+ aYList.push_back(yAvg);
+ }
+ sal_Int32 nPeriodLocal = (mPeriod % 2 == 0) ? (mPeriod / 2) : ((mPeriod - 1) / 2);
+ for (size_t i = nPeriodLocal; i < aSize - 1; ++i)
+ {
+ double x = aValues.first[i];
+ aXList.push_back(x);
+ }
+}
+
+void MovingAverageRegressionCurveCalculator::calculateValues(
+ RegressionCalculationHelper::tDoubleVectorPair aValues, bool bUseXAvg)
+{
+ const size_t aSize = aValues.first.size();
+ for (size_t i = mPeriod - 1; i < aSize; ++i)
+ {
+ double xAvg = 0.0;
+ double yAvg = 0.0;
+
+ for (sal_Int32 j = 0; j < mPeriod; j++)
+ {
+ xAvg += aValues.first[i - j];
+ yAvg += aValues.second[i - j];
+ }
+ yAvg /= mPeriod;
+ xAvg /= mPeriod;
+
+ aYList.push_back(yAvg);
+ if (bUseXAvg)
+ {
+ aXList.push_back(xAvg);
+ }
+ else
+ {
+ double x = aValues.first[i];
+ aXList.push_back(x);
+ }
+ }
+}
+
+double SAL_CALL MovingAverageRegressionCurveCalculator::getCurveValue( double /*x*/ )
+{
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL MovingAverageRegressionCurveCalculator::getCurveValues(
+ double /*min*/, double /*max*/, sal_Int32 /*nPointCount*/,
+ const uno::Reference< chart2::XScaling >& /*xScalingX*/,
+ const uno::Reference< chart2::XScaling >& /*xScalingY*/,
+ sal_Bool /*bMaySkipPointsInCalculation*/ )
+{
+ size_t nSize = std::min(aXList.size(), aYList.size());
+ uno::Sequence< geometry::RealPoint2D > aResult( nSize );
+ std::transform(aXList.begin(), aXList.begin() + nSize, aYList.begin(), aResult.getArray(),
+ [](const auto& x, const auto& y) { return geometry::RealPoint2D(x, y); });
+ return aResult;
+}
+
+OUString MovingAverageRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& /*xNumFormatter*/,
+ sal_Int32 /*nNumberFormatKey*/, sal_Int32* /*pFormulaLength = nullptr */ ) const
+{
+ return SchResId( STR_OBJECT_MOVING_AVERAGE_WITH_PARAMETERS );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/NameContainer.cxx b/chart2/source/tools/NameContainer.cxx
new file mode 100644
index 000000000..23e0a2821
--- /dev/null
+++ b/chart2/source/tools/NameContainer.cxx
@@ -0,0 +1,128 @@
+/* -*- 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 <NameContainer.hxx>
+
+#include <com/sun/star/uno/Any.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+namespace chart
+{
+
+
+NameContainer::NameContainer()
+{
+}
+
+NameContainer::NameContainer( const NameContainer & rOther )
+ : impl::NameContainer_Base(rOther)
+ , m_aMap( rOther.m_aMap )
+{
+}
+
+NameContainer::~NameContainer()
+{
+}
+
+//XServiceInfo
+OUString SAL_CALL NameContainer::getImplementationName()
+{
+ return "com.sun.star.comp.chart.XMLNameSpaceMap";
+}
+
+sal_Bool SAL_CALL NameContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL NameContainer::getSupportedServiceNames()
+{
+ return { "com.sun.star.xml.NamespaceMap" };
+}
+
+// XNameContainer
+void SAL_CALL NameContainer::insertByName( const OUString& rName, const Any& rElement )
+{
+ if( m_aMap.find( rName ) != m_aMap.end() )
+ throw container::ElementExistException();
+ m_aMap.emplace( rName, rElement );
+}
+
+void SAL_CALL NameContainer::removeByName( const OUString& Name )
+{
+ tContentMap::iterator aIt( m_aMap.find( Name ));
+ if( aIt == m_aMap.end())
+ throw container::NoSuchElementException();
+ m_aMap.erase( aIt );
+}
+
+// XNameReplace
+void SAL_CALL NameContainer::replaceByName( const OUString& rName, const Any& rElement )
+{
+ tContentMap::iterator aIt( m_aMap.find( rName ));
+ if( aIt == m_aMap.end() )
+ throw container::NoSuchElementException();
+ aIt->second = rElement;
+}
+
+// XNameAccess
+Any SAL_CALL NameContainer::getByName( const OUString& rName )
+{
+ tContentMap::iterator aIter( m_aMap.find( rName ) );
+ if( aIter == m_aMap.end() )
+ throw container::NoSuchElementException();
+ return aIter->second;
+}
+
+Sequence< OUString > SAL_CALL NameContainer::getElementNames()
+{
+ return comphelper::mapKeysToSequence(m_aMap);
+}
+
+sal_Bool SAL_CALL NameContainer::hasByName( const OUString& rName )
+{
+ return ( m_aMap.find( rName ) != m_aMap.end() );
+}
+
+// XElementAccess
+sal_Bool SAL_CALL NameContainer::hasElements()
+{
+ return ! m_aMap.empty();
+}
+
+uno::Type SAL_CALL NameContainer::getElementType()
+{
+ return ::cppu::UnoType<OUString>::get();
+}
+
+// XCloneable
+uno::Reference< util::XCloneable > SAL_CALL NameContainer::createClone()
+{
+ return uno::Reference< util::XCloneable >( new NameContainer( *this ));
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/NumberFormatterWrapper.cxx b/chart2/source/tools/NumberFormatterWrapper.cxx
new file mode 100644
index 000000000..e00df1fae
--- /dev/null
+++ b/chart2/source/tools/NumberFormatterWrapper.cxx
@@ -0,0 +1,149 @@
+/* -*- 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 <NumberFormatterWrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/numuno.hxx>
+#include <tools/color.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+
+FixedNumberFormatter::FixedNumberFormatter(
+ const uno::Reference< util::XNumberFormatsSupplier >& xSupplier
+ , sal_Int32 nNumberFormatKey )
+ : m_aNumberFormatterWrapper(xSupplier)
+ , m_nNumberFormatKey( nNumberFormatKey )
+{
+}
+
+FixedNumberFormatter::~FixedNumberFormatter()
+{
+}
+
+OUString FixedNumberFormatter::getFormattedString( double fValue, Color& rLabelColor, bool& rbColorChanged ) const
+{
+ return m_aNumberFormatterWrapper.getFormattedString(
+ m_nNumberFormatKey, fValue, rLabelColor, rbColorChanged );
+}
+
+NumberFormatterWrapper::NumberFormatterWrapper( const uno::Reference< util::XNumberFormatsSupplier >& xSupplier )
+ : m_xNumberFormatsSupplier(xSupplier)
+ , m_pNumberFormatter(nullptr)
+
+{
+ uno::Reference<beans::XPropertySet> xProp(m_xNumberFormatsSupplier,uno::UNO_QUERY);
+ OUString sNullDate( "NullDate" );
+ if ( xProp.is() && xProp->getPropertySetInfo()->hasPropertyByName(sNullDate) )
+ m_aNullDate = xProp->getPropertyValue(sNullDate);
+ SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xSupplier );
+ if( pSupplierObj )
+ m_pNumberFormatter = pSupplierObj->GetNumberFormatter();
+ SAL_WARN_IF(!m_pNumberFormatter,"chart2.tools","need a numberformatter");
+}
+
+NumberFormatterWrapper::~NumberFormatterWrapper()
+{
+}
+
+namespace
+{
+ bool getDate(const css::uno::Any& rAny, util::Date& rDate)
+ {
+ if (rAny >>= rDate)
+ return true;
+ util::DateTime aUtilDateTime;
+ if (rAny >>= aUtilDateTime)
+ {
+ rDate.Day = aUtilDateTime.Day;
+ rDate.Month = aUtilDateTime.Month;
+ rDate.Year = aUtilDateTime.Year;
+ return true;
+ }
+ SAL_WARN("chart2.tools", "neither a util::Date nor a util::DateTime");
+ return false;
+ }
+}
+
+Date NumberFormatterWrapper::getNullDate() const
+{
+ Date aRet(30,12,1899);
+
+ util::Date aUtilDate;
+ if (m_aNullDate.hasValue() && getDate(m_aNullDate, aUtilDate))
+ {
+ aRet = Date(aUtilDate.Day,aUtilDate.Month,aUtilDate.Year);
+ }
+ else if( m_pNumberFormatter )
+ {
+ aRet = m_pNumberFormatter->GetNullDate();
+ }
+ return aRet;
+}
+
+OUString NumberFormatterWrapper::getFormattedString( sal_Int32 nNumberFormatKey, double fValue,
+ Color& rLabelColor, bool& rbColorChanged ) const
+{
+ OUString aText;
+ const Color* pTextColor = nullptr;
+ if( !m_pNumberFormatter )
+ {
+ OSL_FAIL("Need a NumberFormatter");
+ return aText;
+ }
+ // i99104 handle null date correctly
+ sal_Int16 nYear = 1899;
+ sal_uInt16 nDay = 30,nMonth = 12;
+ if ( m_aNullDate.hasValue() )
+ {
+ const Date& rDate = m_pNumberFormatter->GetNullDate();
+ nYear = rDate.GetYear();
+ nMonth = rDate.GetMonth();
+ nDay = rDate.GetDay();
+ util::Date aNewNullDate;
+ if (getDate(m_aNullDate, aNewNullDate))
+ m_pNumberFormatter->ChangeNullDate(aNewNullDate.Day,aNewNullDate.Month,aNewNullDate.Year);
+ }
+ // tdf#130969: use UNLIMITED_PRECISION in case of GENERAL Number Format
+ if( m_pNumberFormatter->GetStandardPrec() != SvNumberFormatter::UNLIMITED_PRECISION )
+ m_pNumberFormatter->ChangeStandardPrec(SvNumberFormatter::UNLIMITED_PRECISION);
+ m_pNumberFormatter->GetOutputString(fValue, nNumberFormatKey, aText, &pTextColor);
+ if ( m_aNullDate.hasValue() )
+ {
+ m_pNumberFormatter->ChangeNullDate(nDay,nMonth,nYear);
+ }
+
+ if(pTextColor)
+ {
+ rbColorChanged = true;
+ rLabelColor = *pTextColor;
+ }
+ else
+ rbColorChanged = false;
+
+ return aText;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/OPropertySet.cxx b/chart2/source/tools/OPropertySet.cxx
new file mode 100644
index 000000000..bcf92741f
--- /dev/null
+++ b/chart2/source/tools/OPropertySet.cxx
@@ -0,0 +1,464 @@
+/* -*- 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 <OPropertySet.hxx>
+#include <CloneHelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/style/XStyle.hpp>
+
+#include <algorithm>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+using ::osl::MutexGuard;
+
+// needed for MS compiler
+using ::cppu::OBroadcastHelper;
+using ::cppu::OPropertySetHelper;
+
+namespace property
+{
+
+OPropertySet::OPropertySet( ::osl::Mutex & par_rMutex ) :
+ OBroadcastHelper( par_rMutex ),
+ // the following causes a warning; there seems to be no way to avoid it
+ OPropertySetHelper( static_cast< OBroadcastHelper & >( *this )),
+ m_rMutex( par_rMutex ),
+ m_bSetNewValuesExplicitlyEvenIfTheyEqualDefault(false)
+{
+}
+
+OPropertySet::OPropertySet( const OPropertySet & rOther, ::osl::Mutex & par_rMutex ) :
+ OBroadcastHelper( par_rMutex ),
+ // the following causes a warning; there seems to be no way to avoid it
+ OPropertySetHelper( static_cast< OBroadcastHelper & >( *this )),
+ m_rMutex( par_rMutex ),
+ m_bSetNewValuesExplicitlyEvenIfTheyEqualDefault(false)
+{
+ MutexGuard aGuard( m_rMutex );
+
+ m_aProperties = rOther.m_aProperties;
+
+ // clone interface properties
+ for(auto& rProp : m_aProperties)
+ {
+ if( rProp.second.hasValue() &&
+ rProp.second.getValueType().getTypeClass() == uno::TypeClass_INTERFACE )
+ {
+ Reference< util::XCloneable > xCloneable;
+ if( rProp.second >>= xCloneable )
+ rProp.second <<= xCloneable->createClone();
+ }
+ }
+
+ m_xStyle.set( ::chart::CloneHelper::CreateRefClone< style::XStyle >()( rOther.m_xStyle ));
+}
+
+void OPropertySet::SetNewValuesExplicitlyEvenIfTheyEqualDefault()
+{
+ m_bSetNewValuesExplicitlyEvenIfTheyEqualDefault = true;
+}
+
+OPropertySet::~OPropertySet()
+{}
+
+Any SAL_CALL OPropertySet::queryInterface( const uno::Type& aType )
+{
+ return ::cppu::queryInterface(
+ aType,
+ static_cast< lang::XTypeProvider * >( this ),
+ static_cast< beans::XPropertySet * >( this ),
+ static_cast< beans::XMultiPropertySet * >( this ),
+ static_cast< beans::XFastPropertySet * >( this ),
+ static_cast< beans::XPropertyState * >( this ),
+ static_cast< beans::XMultiPropertyStates * >( this ),
+ static_cast< style::XStyleSupplier * >( this ) );
+}
+
+// ____ XTypeProvider ____
+Sequence< uno::Type > SAL_CALL
+ OPropertySet::getTypes()
+{
+ static const Sequence< uno::Type > aTypeList{
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XFastPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<style::XStyleSupplier>::get() };
+
+ return aTypeList;
+}
+
+Sequence< sal_Int8 > SAL_CALL
+ OPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// ____ XPropertyState ____
+beans::PropertyState SAL_CALL
+ OPropertySet::getPropertyState( const OUString& PropertyName )
+{
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+
+ return GetPropertyStateByHandle(
+ rPH.getHandleByName( PropertyName ));
+}
+
+Sequence< beans::PropertyState > SAL_CALL
+ OPropertySet::getPropertyStates( const Sequence< OUString >& aPropertyName )
+{
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ aPropertyName.getLength() ]);
+ rPH.fillHandles( pHandles.get(), aPropertyName );
+
+ std::vector< sal_Int32 > aHandles( pHandles.get(), pHandles.get() + aPropertyName.getLength());
+ pHandles.reset();
+
+ return GetPropertyStatesByHandle( aHandles );
+}
+
+void SAL_CALL
+ OPropertySet::setPropertyToDefault( const OUString& PropertyName )
+{
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+
+ SetPropertyToDefault( rPH.getHandleByName( PropertyName ));
+ firePropertyChangeEvent();
+}
+
+Any SAL_CALL
+ OPropertySet::getPropertyDefault( const OUString& aPropertyName )
+{
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+
+ Any any;
+ GetDefaultValue( rPH.getHandleByName( aPropertyName ), any );
+ return any;
+}
+
+// ____ XMultiPropertyStates ____
+
+// Note: getPropertyStates() is already implemented in XPropertyState with the
+// same signature
+
+void SAL_CALL
+ OPropertySet::setAllPropertiesToDefault()
+{
+ SetAllPropertiesToDefault();
+ firePropertyChangeEvent();
+}
+
+void SAL_CALL
+ OPropertySet::setPropertiesToDefault( const Sequence< OUString >& aPropertyNames )
+{
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ aPropertyNames.getLength() ]);
+ rPH.fillHandles( pHandles.get(), aPropertyNames );
+
+ std::vector< sal_Int32 > aHandles( pHandles.get(), pHandles.get() + aPropertyNames.getLength());
+ pHandles.reset();
+
+ SetPropertiesToDefault( aHandles );
+}
+
+Sequence< Any > SAL_CALL
+ OPropertySet::getPropertyDefaults( const Sequence< OUString >& aPropertyNames )
+{
+ ::cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+ const sal_Int32 nElements = aPropertyNames.getLength();
+
+ Sequence< Any > aResult( nElements );
+ Any * pResultArray = aResult.getArray();
+ sal_Int32 nI = 0;
+
+ for( ; nI < nElements; ++nI )
+ {
+ GetDefaultValue(
+ rPH.getHandleByName( aPropertyNames[ nI ] ),
+ pResultArray[ nI ]);
+ }
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL OPropertySet::convertFastPropertyValue
+ ( Any & rConvertedValue,
+ Any & rOldValue,
+ sal_Int32 nHandle,
+ const Any& rValue )
+{
+ getFastPropertyValue( rOldValue, nHandle );
+ //accept longs also for short values
+ {
+ sal_Int16 nValue;
+ if( (rOldValue>>=nValue) && !(rValue>>=nValue) )
+ {
+ sal_Int32 n32Value = 0;
+ if( rValue>>=n32Value )
+ {
+ rConvertedValue <<= static_cast<sal_Int16>(n32Value);
+ return true;
+ }
+
+ sal_Int64 n64Value = 0;
+ if( rValue>>=n64Value )
+ {
+ rConvertedValue <<= static_cast<sal_Int16>(n64Value);
+ return true;
+ }
+ }
+ }
+ rConvertedValue = rValue;
+ if( !m_bSetNewValuesExplicitlyEvenIfTheyEqualDefault && rOldValue == rConvertedValue )
+ return false;//no change necessary
+ return true;
+}
+
+void SAL_CALL OPropertySet::setFastPropertyValue_NoBroadcast
+ ( sal_Int32 nHandle,
+ const Any& rValue )
+{
+#if OSL_DEBUG_LEVEL > 0
+ if( rValue.hasValue())
+ {
+ cppu::IPropertyArrayHelper & rPH = getInfoHelper();
+ OUString aName;
+ rPH.fillPropertyMembersByHandle( &aName, nullptr, nHandle );
+ OSL_ENSURE( rValue.isExtractableTo( rPH.getPropertyByName( aName ).Type ),
+ "Property type is wrong" );
+ }
+#endif
+
+ Any aDefault;
+ try
+ {
+ GetDefaultValue( nHandle, aDefault );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ aDefault.clear();
+ }
+ SetPropertyValueByHandle( nHandle, rValue );
+ if( !m_bSetNewValuesExplicitlyEvenIfTheyEqualDefault && aDefault.hasValue() && aDefault == rValue ) //#i98893# don't export defaults to file
+ SetPropertyToDefault( nHandle );
+ else
+ SetPropertyValueByHandle( nHandle, rValue );
+}
+
+void SAL_CALL OPropertySet::getFastPropertyValue
+ ( Any& rValue,
+ sal_Int32 nHandle ) const
+{
+ if( GetPropertyValueByHandle( rValue, nHandle ))
+ return;
+
+ // property was not set -> try style
+ uno::Reference< beans::XFastPropertySet > xStylePropSet( m_xStyle, uno::UNO_QUERY );
+ if( xStylePropSet.is() )
+ {
+#ifdef DBG_UTIL
+ {
+ // check if the handle of the style points to the same property
+ // name as the handle in this property set
+ uno::Reference< beans::XPropertySet > xPropSet( xStylePropSet, uno::UNO_QUERY );
+ if( xPropSet.is())
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo();
+ if( xInfo.is() )
+ {
+ // for some reason the virtual method getInfoHelper() is
+ // not const
+ ::cppu::IPropertyArrayHelper & rPH =
+ const_cast< OPropertySet * >( this )->getInfoHelper();
+
+ // find the Property with Handle nHandle in Style
+ Sequence< beans::Property > aProps( xInfo->getProperties() );
+ sal_Int32 nI = aProps.getLength() - 1;
+ while( ( nI >= 0 ) && nHandle != aProps[ nI ].Handle )
+ --nI;
+
+ if( nI >= 0 ) // => nHandle == aProps[nI].Handle
+ {
+ // check whether the handle in this property set is
+ // the same as the one in the style
+ beans::Property aProp( rPH.getPropertyByName( aProps[ nI ].Name ) );
+ OSL_ENSURE( nHandle == aProp.Handle,
+ "HandleCheck: Handles for same property differ!" );
+
+ if( nHandle == aProp.Handle )
+ {
+ OSL_ENSURE( aProp.Type == aProps[nI].Type,
+ "HandleCheck: Types differ!" );
+ OSL_ENSURE( aProp.Attributes == aProps[nI].Attributes,
+ "HandleCheck: Attributes differ!" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "HandleCheck: Handle not found in Style" );
+ }
+ }
+ else
+ OSL_FAIL( "HandleCheck: Invalid XPropertySetInfo returned" );
+ }
+ else
+ OSL_FAIL( "HandleCheck: XPropertySet not supported" );
+ }
+#endif
+ rValue = xStylePropSet->getFastPropertyValue( nHandle );
+ }
+ else
+ {
+ // there is no style (or the style does not support XFastPropertySet)
+ // => take the default value
+ try
+ {
+ GetDefaultValue( nHandle, rValue );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ rValue.clear();
+ }
+ }
+}
+
+void OPropertySet::firePropertyChangeEvent()
+{
+ // nothing in base class
+}
+
+// ____ XStyleSupplier ____
+Reference< style::XStyle > SAL_CALL OPropertySet::getStyle()
+{
+ return m_xStyle;
+}
+
+void SAL_CALL OPropertySet::setStyle( const Reference< style::XStyle >& xStyle )
+{
+ if( ! SetStyle( xStyle ))
+ throw lang::IllegalArgumentException(
+ "Empty Style",
+ static_cast< beans::XPropertySet * >( this ),
+ 0 );
+}
+
+// ____ XMultiPropertySet ____
+void SAL_CALL OPropertySet::setPropertyValues(
+ const Sequence< OUString >& PropertyNames, const Sequence< Any >& Values )
+{
+ ::cppu::OPropertySetHelper::setPropertyValues( PropertyNames, Values );
+
+ firePropertyChangeEvent();
+}
+
+// ____ XFastPropertySet ____
+void SAL_CALL OPropertySet::setFastPropertyValue( sal_Int32 nHandle, const Any& rValue )
+{
+ ::cppu::OPropertySetHelper::setFastPropertyValue( nHandle, rValue );
+
+ firePropertyChangeEvent();
+}
+
+beans::PropertyState OPropertySet::GetPropertyStateByHandle( sal_Int32 nHandle ) const
+{
+ if( m_aProperties.end() == m_aProperties.find( nHandle ))
+ return beans::PropertyState_DEFAULT_VALUE;
+ return beans::PropertyState_DIRECT_VALUE;
+}
+
+Sequence< beans::PropertyState > OPropertySet::GetPropertyStatesByHandle(
+ const std::vector< sal_Int32 > & aHandles ) const
+{
+ Sequence< beans::PropertyState > aResult( aHandles.size());
+
+ std::transform( aHandles.begin(), aHandles.end(),
+ aResult.getArray(),
+ [this](sal_Int32 nHandle) { return GetPropertyStateByHandle(nHandle); });
+
+ return aResult;
+}
+
+void OPropertySet::SetPropertyToDefault( sal_Int32 nHandle )
+{
+ tPropertyMap::iterator aFoundIter( m_aProperties.find( nHandle ) );
+
+ if( m_aProperties.end() != aFoundIter )
+ {
+ m_aProperties.erase( aFoundIter );
+ }
+}
+
+void OPropertySet::SetPropertiesToDefault(
+ const std::vector< sal_Int32 > & aHandles )
+{
+ for(auto nHandle : aHandles)
+ m_aProperties.erase(nHandle);
+}
+
+void OPropertySet::SetAllPropertiesToDefault()
+{
+ m_aProperties.clear();
+}
+
+bool OPropertySet::GetPropertyValueByHandle(
+ Any & rValue,
+ sal_Int32 nHandle ) const
+{
+ bool bResult = false;
+
+ tPropertyMap::const_iterator aFoundIter( m_aProperties.find( nHandle ) );
+
+ if( m_aProperties.end() != aFoundIter )
+ {
+ rValue = (*aFoundIter).second;
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+void OPropertySet::SetPropertyValueByHandle(
+ sal_Int32 nHandle, const Any & rValue )
+{
+ m_aProperties[ nHandle ] = rValue;
+}
+
+bool OPropertySet::SetStyle( const Reference< style::XStyle > & xStyle )
+{
+ if( ! xStyle.is())
+ return false;
+
+ m_xStyle = xStyle;
+ return true;
+}
+
+
+} // namespace property
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ObjectIdentifier.cxx b/chart2/source/tools/ObjectIdentifier.cxx
new file mode 100644
index 000000000..8bc65422c
--- /dev/null
+++ b/chart2/source/tools/ObjectIdentifier.cxx
@@ -0,0 +1,1379 @@
+/* -*- 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 <cstddef>
+#include <map>
+
+#include <ObjectIdentifier.hxx>
+#include <TitleHelper.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <ChartType.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <servicenames_charttypes.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <unonames.hxx>
+#include <BaseCoordinateSystem.hxx>
+#include <DataSeries.hxx>
+#include <RegressionCurveModel.hxx>
+
+#include <com/sun/star/chart2/XAxis.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace com::sun::star::drawing { class XShape; }
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+
+const sal_Unicode m_aMultiClick[] = u"MultiClick";
+const sal_Unicode m_aDragMethodEquals[] = u"DragMethod=";
+const sal_Unicode m_aDragParameterEquals[] = u"DragParameter=";
+const sal_Unicode m_aProtocol[] = u"CID/";
+const OUString m_aPieSegmentDragMethodServiceName("PieSegmentDragging");
+
+namespace
+{
+
+OUString lcl_createClassificationStringForType( ObjectType eObjectType
+ , std::u16string_view rDragMethodServiceName
+ , std::u16string_view rDragParameterString
+ )
+{
+ OUStringBuffer aRet;
+ switch( eObjectType )
+ {
+ //these object types are all selected only after their parents was selected before
+ case OBJECTTYPE_LEGEND_ENTRY: //parent is intended to be OBJECTTYPE_LEGEND
+ case OBJECTTYPE_DATA_POINT: //parent is intended to be OBJECTTYPE_DATA_SERIES
+ case OBJECTTYPE_DATA_LABEL: //parent is intended to be OBJECTTYPE_DATA_LABELS
+ case OBJECTTYPE_DATA_ERRORS_X: //parent is intended to be OBJECTTYPE_DATA_ERRORS
+ case OBJECTTYPE_DATA_ERRORS_Y: //parent is intended to be OBJECTTYPE_DATA_ERRORS
+ case OBJECTTYPE_DATA_ERRORS_Z: //parent is intended to be OBJECTTYPE_DATA_ERRORS
+ aRet=m_aMultiClick;
+ break;
+ default:
+ break;//empty string
+ }
+ if( !rDragMethodServiceName.empty() )
+ {
+ if( !aRet.isEmpty() )
+ aRet.append(":");
+ aRet.append( m_aDragMethodEquals );
+ aRet.append( rDragMethodServiceName );
+
+ if( !rDragParameterString.empty() )
+ {
+ if( !aRet.isEmpty() )
+ aRet.append(":");
+ aRet.append( m_aDragParameterEquals );
+ aRet.append( rDragParameterString );
+ }
+ }
+ return aRet.makeStringAndClear();
+}
+
+typedef std::map< TitleHelper::eTitleType, OUString > tTitleMap;
+const tTitleMap& lcl_getTitleMap()
+{
+ //maps the title type to the ParentParticle for that title
+ static tTitleMap s_aTitleMap{
+ {TitleHelper::MAIN_TITLE, ""},
+ {TitleHelper::SUB_TITLE, "D=0"},
+ {TitleHelper::X_AXIS_TITLE, "D=0:CS=0:Axis=0,0"},
+ {TitleHelper::Y_AXIS_TITLE, "D=0:CS=0:Axis=1,0"},
+ {TitleHelper::Z_AXIS_TITLE, "D=0:CS=0:Axis=2,0"},
+ {TitleHelper::SECONDARY_X_AXIS_TITLE, "D=0:CS=0:Axis=0,1"},
+ {TitleHelper::SECONDARY_Y_AXIS_TITLE, "D=0:CS=0:Axis=1,1"}};
+ return s_aTitleMap;
+}
+
+OUString lcl_getTitleParentParticle( TitleHelper::eTitleType aTitleType )
+{
+ OUString aRet;
+
+ const tTitleMap& rMap = lcl_getTitleMap();
+ tTitleMap::const_iterator aIt( rMap.find( aTitleType ) );
+ if( aIt != rMap.end())
+ aRet = (*aIt).second;
+
+ return aRet;
+}
+
+rtl::Reference<ChartType> lcl_getFirstStockChartType( const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xChartModel ) );
+ if(!xDiagram.is())
+ return nullptr;
+
+ //iterate through all coordinate systems
+
+ const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList( xDiagram->getBaseCoordinateSystems() );
+ for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysList )
+ {
+ //iterate through all chart types in the current coordinate system
+ for( rtl::Reference< ChartType > const & xChartType : coords->getChartTypes2() )
+ {
+ OUString aChartType = xChartType->getChartType();
+ if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) )
+ return xChartType;
+ }
+ }
+ return nullptr;
+}
+
+OUString lcl_getIndexStringAfterString( const OUString& rString, const OUString& rSearchString )
+{
+ OUStringBuffer aRet;
+
+ sal_Int32 nIndexStart = rString.lastIndexOf( rSearchString );
+ if( nIndexStart != -1 )
+ {
+ nIndexStart += rSearchString.getLength();
+ sal_Int32 nIndexEnd = rString.getLength();
+ sal_Int32 nNextColon = rString.indexOf( ':', nIndexStart );
+ if( nNextColon != -1 )
+ nIndexEnd = nNextColon;
+ aRet = rString.subView(nIndexStart,nIndexEnd-nIndexStart);
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+sal_Int32 lcl_StringToIndex( std::u16string_view rIndexString )
+{
+ sal_Int32 nRet = -1;
+ if( !rIndexString.empty() )
+ {
+ nRet = o3tl::toInt32(rIndexString);
+ if( nRet < -1 )
+ nRet = -1;
+ }
+ return nRet;
+}
+
+void lcl_parseCooSysIndices( sal_Int32& rnDiagram, sal_Int32& rnCooSys, const OUString& rString )
+{
+ rnDiagram = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, "D=" ) );
+ rnCooSys = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, "CS=" ) );
+}
+
+void lcl_parseAxisIndices( sal_Int32& rnDimensionIndex, sal_Int32& rnAxisIndex, const OUString& rString )
+{
+ OUString aAxisIndexString = lcl_getIndexStringAfterString( rString, ":Axis=" );
+ sal_Int32 nCharacterIndex=0;
+ rnDimensionIndex = lcl_StringToIndex( o3tl::getToken(aAxisIndexString, 0, ',', nCharacterIndex ) );
+ rnAxisIndex = lcl_StringToIndex( o3tl::getToken(aAxisIndexString, 0, ',', nCharacterIndex ) );
+}
+
+void lcl_parseGridIndices( sal_Int32& rnSubGridIndex, const OUString& rString )
+{
+ rnSubGridIndex = -1;
+ rnSubGridIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, ":SubGrid=" ) );
+}
+
+void lcl_parseSeriesIndices( sal_Int32& rnChartTypeIndex, sal_Int32& rnSeriesIndex, sal_Int32& rnPointIndex, const OUString& rString )
+{
+ rnChartTypeIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, "CT=" ) );
+ rnSeriesIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, "Series=" ) );
+ rnPointIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rString, "Point=" ) );
+}
+
+void lcl_getDiagramAndCooSys( const OUString& rObjectCID
+ , const rtl::Reference<::chart::ChartModel>& xChartModel
+ , rtl::Reference< Diagram >& xDiagram
+ , rtl::Reference< BaseCoordinateSystem >& xCooSys )
+{
+ sal_Int32 nDiagramIndex = -1;
+ sal_Int32 nCooSysIndex = -1;
+ lcl_parseCooSysIndices( nDiagramIndex, nCooSysIndex, rObjectCID );
+ xDiagram = ChartModelHelper::findDiagram( xChartModel );//todo use nDiagramIndex when more than one diagram is possible in future
+ if( !xDiagram.is() )
+ return;
+
+ if( nCooSysIndex > -1 )
+ {
+ const std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysList( xDiagram->getBaseCoordinateSystems() );
+ if( o3tl::make_unsigned(nCooSysIndex) < aCooSysList.size() )
+ xCooSys = aCooSysList[nCooSysIndex];
+ }
+}
+
+} //anonymous namespace
+
+ObjectIdentifier::ObjectIdentifier()
+{
+}
+
+ObjectIdentifier::ObjectIdentifier( const OUString& rObjectCID )
+ :m_aObjectCID( rObjectCID )
+{
+}
+
+ObjectIdentifier::ObjectIdentifier( const Reference< drawing::XShape >& rxShape )
+ : m_xAdditionalShape( rxShape )
+{
+}
+
+ObjectIdentifier::ObjectIdentifier( const Any& rAny )
+{
+ const uno::Type& rType = rAny.getValueType();
+ if ( rType == cppu::UnoType<OUString>::get() )
+ {
+ rAny >>= m_aObjectCID;
+ }
+ else if ( rType == cppu::UnoType< drawing::XShape >::get() )
+ {
+ rAny >>= m_xAdditionalShape;
+ }
+}
+
+bool ObjectIdentifier::operator==( const ObjectIdentifier& rOID ) const
+{
+ return areIdenticalObjects( m_aObjectCID, rOID.m_aObjectCID ) &&
+ ( m_xAdditionalShape == rOID.m_xAdditionalShape );
+}
+
+bool ObjectIdentifier::operator!=( const ObjectIdentifier& rOID ) const
+{
+ return !operator==( rOID );
+}
+
+bool ObjectIdentifier::operator<( const ObjectIdentifier& rOID ) const
+{
+ bool bReturn = false;
+ if ( !(m_aObjectCID.isEmpty() || rOID.m_aObjectCID.isEmpty()) )
+ {
+ bReturn = ( m_aObjectCID.compareTo( rOID.m_aObjectCID ) < 0 );
+ }
+ else if ( !m_aObjectCID.isEmpty() )
+ {
+ bReturn = true;
+ }
+ else if ( !rOID.m_aObjectCID.isEmpty() )
+ {
+ bReturn = false;
+ }
+ else if ( m_xAdditionalShape.is() && rOID.m_xAdditionalShape.is() )
+ {
+ bReturn = ( m_xAdditionalShape < rOID.m_xAdditionalShape );
+ }
+ return bReturn;
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForObject(
+ const Reference< uno::XInterface >& xObject
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ OUString aRet;
+
+ enum ObjectType eObjectType = OBJECTTYPE_UNKNOWN;
+ const std::u16string_view aObjectID;
+ OUString aParentParticle;
+ const std::u16string_view aDragMethodServiceName;
+ const std::u16string_view aDragParameterString;
+
+ try
+ {
+ //title
+ Reference< XTitle > xTitle( xObject, uno::UNO_QUERY );
+ if( xTitle.is() )
+ {
+ TitleHelper::eTitleType aTitleType;
+ if( TitleHelper::getTitleType( aTitleType, xTitle, xChartModel ) )
+ {
+ eObjectType = OBJECTTYPE_TITLE;
+ aParentParticle = lcl_getTitleParentParticle( aTitleType );
+ aRet = ObjectIdentifier::createClassifiedIdentifierWithParent(
+ eObjectType, aObjectID, aParentParticle, aDragMethodServiceName, aDragParameterString );
+ }
+ return aRet;
+
+ }
+
+ //axis
+ Reference< XAxis > xAxis( xObject, uno::UNO_QUERY );
+ if( xAxis.is() )
+ {
+ rtl::Reference< BaseCoordinateSystem > xCooSys( AxisHelper::getCoordinateSystemOfAxis( xAxis, ChartModelHelper::findDiagram( xChartModel ) ) );
+ OUString aCooSysParticle( createParticleForCoordinateSystem( xCooSys, xChartModel ) );
+ sal_Int32 nDimensionIndex=-1;
+ sal_Int32 nAxisIndex=-1;
+ AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex );
+ OUString aAxisParticle( createParticleForAxis( nDimensionIndex, nAxisIndex ) );
+ return createClassifiedIdentifierForParticles( aCooSysParticle, aAxisParticle );
+ }
+
+ //legend
+ Reference< XLegend > xLegend( xObject, uno::UNO_QUERY );
+ if( xLegend.is() )
+ {
+ return createClassifiedIdentifierForParticle( createParticleForLegend( xChartModel ) );
+ }
+
+ //diagram
+ Reference< XDiagram > xDiagram( xObject, uno::UNO_QUERY );
+ if( xDiagram.is() )
+ {
+ return createClassifiedIdentifierForParticle( createParticleForDiagram() );
+ }
+
+ //todo
+ //XDataSeries
+ //CooSys
+ //charttype
+ //datapoint?
+ //Gridproperties
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ if( eObjectType != OBJECTTYPE_UNKNOWN )
+ {
+ aRet = ObjectIdentifier::createClassifiedIdentifierWithParent(
+ eObjectType, aObjectID, aParentParticle, aDragMethodServiceName, aDragParameterString );
+ }
+ else
+ {
+ OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");
+ }
+
+ return aRet;
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForObject(
+ const rtl::Reference< Legend >& xLegend
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ try
+ {
+ if( xLegend.is() )
+ {
+ return createClassifiedIdentifierForParticle( createParticleForLegend( xChartModel ) );
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");
+
+ return OUString();
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForObject(
+ const rtl::Reference<::chart::Axis>& xAxis
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ try
+ {
+ //axis
+ if( xAxis.is() )
+ {
+ rtl::Reference< BaseCoordinateSystem > xCooSys( AxisHelper::getCoordinateSystemOfAxis( xAxis, ChartModelHelper::findDiagram( xChartModel ) ) );
+ OUString aCooSysParticle( createParticleForCoordinateSystem( xCooSys, xChartModel ) );
+ sal_Int32 nDimensionIndex=-1;
+ sal_Int32 nAxisIndex=-1;
+ AxisHelper::getIndicesForAxis( xAxis, xCooSys, nDimensionIndex, nAxisIndex );
+ OUString aAxisParticle( createParticleForAxis( nDimensionIndex, nAxisIndex ) );
+ return createClassifiedIdentifierForParticles( aCooSysParticle, aAxisParticle );
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ OSL_FAIL("give object could not be identified in createClassifiedIdentifierForObject");
+
+ return OUString();
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForParticle(
+ std::u16string_view rParticle )
+{
+ return ObjectIdentifier::createClassifiedIdentifierForParticles( rParticle, u"" );
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForParticles(
+ std::u16string_view rParentParticle
+ , std::u16string_view rChildParticle
+ , std::u16string_view rDragMethodServiceName
+ , std::u16string_view rDragParameterString )
+{
+ ObjectType eObjectType( ObjectIdentifier::getObjectType( rChildParticle ) );
+ if( eObjectType == OBJECTTYPE_UNKNOWN )
+ eObjectType = ObjectIdentifier::getObjectType( rParentParticle );
+
+ OUStringBuffer aRet( m_aProtocol );
+ aRet.append( lcl_createClassificationStringForType( eObjectType, rDragMethodServiceName, rDragParameterString ));
+ if(o3tl::make_unsigned(aRet.getLength()) >= std::size(m_aProtocol))
+ aRet.append("/");
+
+ if(!rParentParticle.empty())
+ {
+ aRet.append(rParentParticle);
+ if( !rChildParticle.empty() )
+ aRet.append(":");
+ }
+ aRet.append(rChildParticle);
+
+ return aRet.makeStringAndClear();
+}
+
+OUString ObjectIdentifier::createParticleForDiagram()
+{
+ //TODO: if more than one diagram is implemented, add the correct diagram index here
+ return "D=0";
+}
+
+OUString ObjectIdentifier::createParticleForCoordinateSystem(
+ const rtl::Reference< BaseCoordinateSystem >& xCooSys
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ OUString aRet;
+
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xChartModel ) );
+ if( xDiagram.is() )
+ {
+ std::size_t nCooSysIndex = 0;
+ const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysList( xDiagram->getBaseCoordinateSystems() );
+ for( ; nCooSysIndex < aCooSysList.size(); ++nCooSysIndex )
+ {
+ if( xCooSys == aCooSysList[nCooSysIndex] )
+ {
+ aRet = ObjectIdentifier::createParticleForDiagram() + ":CS=" + OUString::number( nCooSysIndex );
+ break;
+ }
+ }
+ }
+
+ return aRet;
+}
+
+OUString ObjectIdentifier::createParticleForAxis(
+ sal_Int32 nDimensionIndex
+ , sal_Int32 nAxisIndex )
+{
+ return "Axis=" +
+ OUString::number( nDimensionIndex ) +
+ "," +
+ OUString::number( nAxisIndex );
+}
+
+OUString ObjectIdentifier::createParticleForGrid(
+ sal_Int32 nDimensionIndex
+ , sal_Int32 nAxisIndex )
+{
+ OUString aRet = "Axis=" + OUString::number( nDimensionIndex )
+ + "," + OUString::number( nAxisIndex ) + ":Grid=0";
+
+ return aRet;
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierForGrid(
+ const Reference< XAxis >& xAxis
+ , const rtl::Reference<::chart::ChartModel>& xChartModel
+ , sal_Int32 nSubGridIndex )
+{
+ //-1: main grid, 0: first subgrid etc
+
+ OUString aAxisCID( createClassifiedIdentifierForObject( xAxis, xChartModel ) );
+ OUString aGridCID( addChildParticle( aAxisCID
+ , createChildParticleWithIndex( OBJECTTYPE_GRID, 0 ) ) );
+ if( nSubGridIndex >= 0 )
+ {
+ aGridCID = addChildParticle( aGridCID
+ , createChildParticleWithIndex( OBJECTTYPE_SUBGRID, 0 ) );
+ }
+ return aGridCID;
+}
+
+OUString ObjectIdentifier::createParticleForSeries(
+ sal_Int32 nDiagramIndex, sal_Int32 nCooSysIndex
+ , sal_Int32 nChartTypeIndex, sal_Int32 nSeriesIndex )
+{
+ return
+ "D=" + OUString::number( nDiagramIndex ) +
+ ":CS=" + OUString::number( nCooSysIndex ) +
+ ":CT=" + OUString::number( nChartTypeIndex ) +
+ ":" + getStringForType( OBJECTTYPE_DATA_SERIES ) + "=" +
+ OUString::number( nSeriesIndex );
+}
+
+
+OUString ObjectIdentifier::createParticleForLegend(
+ const rtl::Reference<::chart::ChartModel>& )
+{
+ //todo: if more than one diagram is implemented, find the correct diagram which is owner of the given legend
+
+ return ObjectIdentifier::createParticleForDiagram() + ":" + getStringForType( OBJECTTYPE_LEGEND ) + "=";
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifier(
+ enum ObjectType eObjectType //e.g. OBJECTTYPE_DATA_SERIES
+ , std::u16string_view rParticleID )//e.g. SeriesID
+{
+ return createClassifiedIdentifierWithParent(
+ eObjectType, rParticleID, u"" );
+}
+
+OUString ObjectIdentifier::createClassifiedIdentifierWithParent(
+ enum ObjectType eObjectType //e.g. OBJECTTYPE_DATA_POINT or OBJECTTYPE_GRID
+ , std::u16string_view rParticleID //e.g. Point Index or SubGrid Index
+ , std::u16string_view rParentPartical //e.g. "Series=SeriesID" or "Grid=GridId"
+ , std::u16string_view rDragMethodServiceName
+ , std::u16string_view rDragParameterString
+ )
+ //, bool bIsMultiClickObject ) //e.g. true
+{
+ //e.g. "MultiClick/Series=2:Point=34"
+
+ OUStringBuffer aRet( m_aProtocol );
+ aRet.append( lcl_createClassificationStringForType( eObjectType, rDragMethodServiceName, rDragParameterString ));
+ if(o3tl::make_unsigned(aRet.getLength()) >= std::size(m_aProtocol))
+ aRet.append("/");
+ aRet.append(rParentPartical);
+ if(!rParentPartical.empty())
+ aRet.append(":");
+
+ aRet.append(getStringForType( eObjectType ));
+ aRet.append("=");
+ aRet.append(rParticleID);
+
+ return aRet.makeStringAndClear();
+}
+
+const OUString& ObjectIdentifier::getPieSegmentDragMethodServiceName()
+{
+ return m_aPieSegmentDragMethodServiceName;
+}
+
+OUString ObjectIdentifier::createPieSegmentDragParameterString(
+ sal_Int32 nOffsetPercent
+ , const awt::Point& rMinimumPosition
+ , const awt::Point& rMaximumPosition )
+{
+ OUString aRet = OUString::number( nOffsetPercent )
+ + "," + OUString::number( rMinimumPosition.X )
+ + "," + OUString::number( rMinimumPosition.Y )
+ + "," + OUString::number( rMaximumPosition.X )
+ + "," + OUString::number( rMaximumPosition.Y );
+ return aRet;
+}
+
+bool ObjectIdentifier::parsePieSegmentDragParameterString(
+ std::u16string_view rDragParameterString
+ , sal_Int32& rOffsetPercent
+ , awt::Point& rMinimumPosition
+ , awt::Point& rMaximumPosition )
+{
+ sal_Int32 nCharacterIndex = 0;
+
+ std::u16string_view aValueString( o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex ) );
+ rOffsetPercent = o3tl::toInt32(aValueString);
+ if( nCharacterIndex < 0 )
+ return false;
+
+ aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
+ rMinimumPosition.X = o3tl::toInt32(aValueString);
+ if( nCharacterIndex < 0 )
+ return false;
+
+ aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
+ rMinimumPosition.Y = o3tl::toInt32(aValueString);
+ if( nCharacterIndex < 0 )
+ return false;
+
+ aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
+ rMaximumPosition.X = o3tl::toInt32(aValueString);
+ if( nCharacterIndex < 0 )
+ return false;
+
+ aValueString = o3tl::getToken(rDragParameterString, 0, ',', nCharacterIndex );
+ rMaximumPosition.Y = o3tl::toInt32(aValueString);
+ return nCharacterIndex >= 0;
+}
+
+std::u16string_view ObjectIdentifier::getDragMethodServiceName( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( m_aDragMethodEquals );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+
+std::u16string_view ObjectIdentifier::getDragParameterString( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( m_aDragParameterEquals );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+
+bool ObjectIdentifier::isDragableObject( std::u16string_view rClassifiedIdentifier )
+{
+ bool bReturn = false;
+ ObjectType eObjectType = ObjectIdentifier::getObjectType( rClassifiedIdentifier );
+ switch( eObjectType )
+ {
+ case OBJECTTYPE_TITLE:
+ case OBJECTTYPE_LEGEND:
+ case OBJECTTYPE_DIAGRAM:
+ case OBJECTTYPE_DATA_LABEL:
+ case OBJECTTYPE_DATA_CURVE_EQUATION:
+ //case OBJECTTYPE_DIAGRAM_WALL:
+ bReturn = true;
+ break;
+ default:
+ std::u16string_view aDragMethodServiceName( ObjectIdentifier::getDragMethodServiceName( rClassifiedIdentifier ) );
+ bReturn = !aDragMethodServiceName.empty();
+ break;
+ }
+ return bReturn;
+}
+
+bool ObjectIdentifier::isDragableObject() const
+{
+ bool bReturn = false;
+ if ( isAutoGeneratedObject() )
+ {
+ bReturn = isDragableObject( m_aObjectCID );
+ }
+ else if ( isAdditionalShape() )
+ {
+ bReturn = true;
+ }
+ return bReturn;
+}
+
+bool ObjectIdentifier::isRotateableObject( std::u16string_view rClassifiedIdentifier )
+{
+ bool bReturn = false;
+ ObjectType eObjectType = ObjectIdentifier::getObjectType( rClassifiedIdentifier );
+ switch( eObjectType )
+ {
+ case OBJECTTYPE_DIAGRAM:
+ //case OBJECTTYPE_DIAGRAM_WALL:
+ bReturn = true;
+ break;
+ default:
+ bReturn = false;
+ break;
+ }
+ return bReturn;
+}
+
+bool ObjectIdentifier::isMultiClickObject( std::u16string_view rClassifiedIdentifier )
+{
+ //the name of a shape is it's ClassifiedIdentifier
+
+ //a MultiClickObject is an object that is selectable by more than one click only ;
+ //before a MultiClickObject can be selected it is necessary that a named parent group object
+ //was selected before;
+
+ //!!!!! by definition the name of a MultiClickObject starts with "CID/MultiClick:"
+ bool bRet = o3tl::starts_with(rClassifiedIdentifier.substr( std::size(m_aProtocol)-1 ), m_aMultiClick);
+ return bRet;
+}
+
+bool ObjectIdentifier::areSiblings( const OUString& rCID1, const OUString& rCID2 )
+{
+ bool bRet=false;
+ sal_Int32 nLastSign1 = rCID1.lastIndexOf( '=' );
+ sal_Int32 nLastSign2 = rCID2.lastIndexOf( '=' );
+ if( nLastSign1 == rCID1.indexOf( '=' ) )//CID cannot be sibling if only one "=" occurs
+ bRet=false;
+ else if( nLastSign2 == rCID2.indexOf( '=' ) )//CID cannot be sibling if only one "=" occurs
+ bRet=false;
+ else if( ObjectIdentifier::areIdenticalObjects( rCID1, rCID2 ) )
+ bRet=false;
+ else
+ {
+ std::u16string_view aParent1( ObjectIdentifier::getFullParentParticle( rCID1 ) );
+ if( !aParent1.empty() )
+ {
+ std::u16string_view aParent2( ObjectIdentifier::getFullParentParticle( rCID2 ) );
+ bRet=aParent1 == aParent2;
+ }
+ //legend entries are special:
+ if(!bRet)
+ {
+ if( getObjectType(rCID1) == OBJECTTYPE_LEGEND_ENTRY
+ && getObjectType(rCID2) == OBJECTTYPE_LEGEND_ENTRY )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool ObjectIdentifier::areIdenticalObjects( const OUString& rCID1, const OUString& rCID2 )
+{
+ if( rCID1 == rCID2 )
+ return true;
+ //draggable pie or donut segments need special treatment, as their CIDs do change with offset
+ {
+ if( rCID1.indexOf( m_aPieSegmentDragMethodServiceName ) < 0
+ || rCID2.indexOf( m_aPieSegmentDragMethodServiceName ) < 0 )
+ return false;
+
+ OUString aID1( ObjectIdentifier::getObjectID( rCID1 ) );
+ OUString aID2( ObjectIdentifier::getObjectID( rCID2 ) );
+ if( !aID1.isEmpty() && aID1 == aID2 )
+ return true;
+ }
+ return false;
+}
+
+OUString ObjectIdentifier::getStringForType( ObjectType eObjectType )
+{
+ OUString aRet;
+ switch( eObjectType )
+ {
+ case OBJECTTYPE_PAGE:
+ aRet="Page";
+ break;
+ case OBJECTTYPE_TITLE:
+ aRet="Title";
+ break;
+ case OBJECTTYPE_LEGEND:
+ aRet="Legend";
+ break;
+ case OBJECTTYPE_LEGEND_ENTRY:
+ aRet="LegendEntry";
+ break;
+ case OBJECTTYPE_DIAGRAM:
+ aRet="D";
+ break;
+ case OBJECTTYPE_DIAGRAM_WALL:
+ aRet="DiagramWall";
+ break;
+ case OBJECTTYPE_DIAGRAM_FLOOR:
+ aRet="DiagramFloor";
+ break;
+ case OBJECTTYPE_AXIS:
+ aRet="Axis";
+ break;
+ case OBJECTTYPE_AXIS_UNITLABEL:
+ aRet="AxisUnitLabel";
+ break;
+ case OBJECTTYPE_GRID:
+ aRet="Grid";
+ break;
+ case OBJECTTYPE_SUBGRID:
+ aRet="SubGrid";
+ break;
+ case OBJECTTYPE_DATA_SERIES:
+ aRet="Series";
+ break;
+ case OBJECTTYPE_DATA_POINT:
+ aRet="Point";
+ break;
+ case OBJECTTYPE_DATA_LABELS:
+ aRet="DataLabels";
+ break;
+ case OBJECTTYPE_DATA_LABEL:
+ aRet="DataLabel";
+ break;
+ case OBJECTTYPE_DATA_ERRORS_X:
+ aRet="ErrorsX";
+ break;
+ case OBJECTTYPE_DATA_ERRORS_Y:
+ aRet="ErrorsY";
+ break;
+ case OBJECTTYPE_DATA_ERRORS_Z:
+ aRet="ErrorsZ";
+ break;
+ case OBJECTTYPE_DATA_CURVE:
+ aRet="Curve";
+ break;
+ case OBJECTTYPE_DATA_CURVE_EQUATION:
+ aRet="Equation";
+ break;
+ case OBJECTTYPE_DATA_AVERAGE_LINE:
+ aRet="Average";
+ break;
+ case OBJECTTYPE_DATA_STOCK_RANGE:
+ aRet="StockRange";
+ break;
+ case OBJECTTYPE_DATA_STOCK_LOSS:
+ aRet="StockLoss";
+ break;
+ case OBJECTTYPE_DATA_STOCK_GAIN:
+ aRet="StockGain";
+ break;
+ default: //OBJECTTYPE_UNKNOWN
+ ;
+ }
+ return aRet;
+}
+
+ObjectType ObjectIdentifier::getObjectType( std::u16string_view aCID )
+{
+ ObjectType eRet;
+ size_t nLastSign = aCID.rfind( ':' );//last sign before the type string
+ if(nLastSign == std::u16string_view::npos)
+ nLastSign = aCID.rfind( '/' );
+ if(nLastSign == std::u16string_view::npos)
+ {
+ size_t nEndIndex = aCID.rfind( '=' );
+ if(nEndIndex == std::u16string_view::npos)
+ return OBJECTTYPE_UNKNOWN;
+ nLastSign = 0;
+ }
+ if( nLastSign>0 )
+ nLastSign++;
+
+ aCID = aCID.substr(nLastSign);
+ if( o3tl::starts_with(aCID, u"Page") )
+ eRet = OBJECTTYPE_PAGE;
+ else if( o3tl::starts_with(aCID, u"Title") )
+ eRet = OBJECTTYPE_TITLE;
+ else if( o3tl::starts_with(aCID, u"LegendEntry") )
+ eRet = OBJECTTYPE_LEGEND_ENTRY;
+ else if( o3tl::starts_with(aCID, u"Legend") )
+ eRet = OBJECTTYPE_LEGEND;
+ else if( o3tl::starts_with(aCID, u"DiagramWall") )
+ eRet = OBJECTTYPE_DIAGRAM_WALL;
+ else if( o3tl::starts_with(aCID, u"DiagramFloor") )
+ eRet = OBJECTTYPE_DIAGRAM_FLOOR;
+ else if( o3tl::starts_with(aCID, u"D=") )
+ eRet = OBJECTTYPE_DIAGRAM;
+ else if( o3tl::starts_with(aCID, u"AxisUnitLabel") )
+ eRet = OBJECTTYPE_AXIS_UNITLABEL;
+ else if( o3tl::starts_with(aCID, u"Axis") )
+ eRet = OBJECTTYPE_AXIS;
+ else if( o3tl::starts_with(aCID, u"Grid") )
+ eRet = OBJECTTYPE_GRID;
+ else if( o3tl::starts_with(aCID, u"SubGrid") )
+ eRet = OBJECTTYPE_SUBGRID;
+ else if( o3tl::starts_with(aCID, u"Series") )
+ eRet = OBJECTTYPE_DATA_SERIES;
+ else if( o3tl::starts_with(aCID, u"Point") )
+ eRet = OBJECTTYPE_DATA_POINT;
+ else if( o3tl::starts_with(aCID, u"DataLabels") )
+ eRet = OBJECTTYPE_DATA_LABELS;
+ else if( o3tl::starts_with(aCID, u"DataLabel") )
+ eRet = OBJECTTYPE_DATA_LABEL;
+ else if( o3tl::starts_with(aCID, u"ErrorsX") )
+ eRet = OBJECTTYPE_DATA_ERRORS_X;
+ else if( o3tl::starts_with(aCID, u"ErrorsY") )
+ eRet = OBJECTTYPE_DATA_ERRORS_Y;
+ else if( o3tl::starts_with(aCID, u"ErrorsZ") )
+ eRet = OBJECTTYPE_DATA_ERRORS_Z;
+ else if( o3tl::starts_with(aCID, u"Curve") )
+ eRet = OBJECTTYPE_DATA_CURVE;
+ else if( o3tl::starts_with(aCID, u"Equation") )
+ eRet = OBJECTTYPE_DATA_CURVE_EQUATION;
+ else if( o3tl::starts_with(aCID, u"Average") )
+ eRet = OBJECTTYPE_DATA_AVERAGE_LINE;
+ else if( o3tl::starts_with(aCID, u"StockRange") )
+ eRet = OBJECTTYPE_DATA_STOCK_RANGE;
+ else if( o3tl::starts_with(aCID, u"StockLoss") )
+ eRet = OBJECTTYPE_DATA_STOCK_LOSS;
+ else if( o3tl::starts_with(aCID, u"StockGain") )
+ eRet = OBJECTTYPE_DATA_STOCK_GAIN;
+ else
+ eRet = OBJECTTYPE_UNKNOWN;
+
+ return eRet;
+}
+
+ObjectType ObjectIdentifier::getObjectType() const
+{
+ ObjectType eObjectType( OBJECTTYPE_UNKNOWN );
+ if ( isAutoGeneratedObject() )
+ {
+ eObjectType = getObjectType( m_aObjectCID );
+ }
+ else if ( isAdditionalShape() )
+ {
+ eObjectType = OBJECTTYPE_SHAPE;
+ }
+ return eObjectType;
+}
+
+OUString ObjectIdentifier::createDataCurveCID(
+ std::u16string_view rSeriesParticle
+ , sal_Int32 nCurveIndex
+ , bool bAverageLine )
+{
+ OUString aParticleID( OUString::number( nCurveIndex ) );
+ ObjectType eType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE;
+ return createClassifiedIdentifierWithParent( eType, aParticleID, rSeriesParticle );
+}
+
+OUString ObjectIdentifier::createDataCurveEquationCID(
+ std::u16string_view rSeriesParticle
+ , sal_Int32 nCurveIndex )
+{
+ OUString aParticleID( OUString::number( nCurveIndex ) );
+ return createClassifiedIdentifierWithParent( OBJECTTYPE_DATA_CURVE_EQUATION, aParticleID, rSeriesParticle );
+}
+
+OUString ObjectIdentifier::addChildParticle( std::u16string_view rParticle, std::u16string_view rChildParticle )
+{
+ OUStringBuffer aRet(rParticle);
+
+ if( !aRet.isEmpty() && !rChildParticle.empty() )
+ aRet.append(":");
+ if( !rChildParticle.empty() )
+ aRet.append(rChildParticle);
+
+ return aRet.makeStringAndClear();
+}
+
+OUString ObjectIdentifier::createChildParticleWithIndex( ObjectType eObjectType, sal_Int32 nIndex )
+{
+ OUStringBuffer aRet( getStringForType( eObjectType ) );
+ if( !aRet.isEmpty() )
+ {
+ aRet.append("=");
+ aRet.append(nIndex);
+ }
+ return aRet.makeStringAndClear();
+}
+
+sal_Int32 ObjectIdentifier::getIndexFromParticleOrCID( const OUString& rParticleOrCID )
+{
+ const OUString aIndexString = lcl_getIndexStringAfterString( rParticleOrCID, "=" );
+ return lcl_StringToIndex( o3tl::getToken(aIndexString, 0, ',' ) );
+}
+
+OUString ObjectIdentifier::createSeriesSubObjectStub( ObjectType eSubObjectType
+ , std::u16string_view rSeriesParticle
+ , std::u16string_view rDragMethodServiceName
+ , std::u16string_view rDragParameterString )
+{
+ OUString aChildParticle = getStringForType( eSubObjectType ) + "=";
+
+ return createClassifiedIdentifierForParticles(
+ rSeriesParticle, aChildParticle
+ , rDragMethodServiceName, rDragParameterString );
+}
+
+OUString ObjectIdentifier::createPointCID( std::u16string_view rPointCID_Stub, sal_Int32 nIndex )
+{
+ return rPointCID_Stub + OUString::number( nIndex );
+}
+
+std::u16string_view ObjectIdentifier::getParticleID( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+ size_t nLast = rCID.rfind('=');
+ if(nLast != std::u16string_view::npos)
+ aRet = rCID.substr(++nLast);
+ return aRet;
+}
+
+std::u16string_view ObjectIdentifier::getFullParentParticle( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nStartPos = rCID.rfind('/');
+ if( nStartPos != std::u16string_view::npos )
+ {
+ nStartPos++;
+ size_t nEndPos = rCID.rfind(':');
+ if( nEndPos != std::u16string_view::npos && nStartPos < nEndPos )
+ {
+ aRet = rCID.substr(nStartPos,nEndPos-nStartPos);
+ }
+ }
+
+ return aRet;
+}
+
+OUString ObjectIdentifier::getObjectID( const OUString& rCID )
+{
+ OUString aRet;
+
+ sal_Int32 nStartPos = rCID.lastIndexOf('/');
+ if( nStartPos>=0 )
+ {
+ nStartPos++;
+ sal_Int32 nEndPos = rCID.getLength();
+ aRet = rCID.copy(nStartPos,nEndPos-nStartPos);
+ }
+
+ return aRet;
+}
+
+bool ObjectIdentifier::isCID( std::u16string_view rName )
+{
+ return !rName.empty() && o3tl::starts_with( rName, m_aProtocol );
+}
+
+Reference< beans::XPropertySet > ObjectIdentifier::getObjectPropertySet(
+ const OUString& rObjectCID
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ //return the model object that is indicated by rObjectCID
+ if(rObjectCID.isEmpty())
+ return nullptr;
+ if(!xChartModel.is())
+ return nullptr;
+
+ Reference< beans::XPropertySet > xObjectProperties;
+ try
+ {
+ ObjectType eObjectType = ObjectIdentifier::getObjectType( rObjectCID );
+ std::u16string_view aParticleID = ObjectIdentifier::getParticleID( rObjectCID );
+
+ rtl::Reference< Diagram > xDiagram;
+ rtl::Reference< BaseCoordinateSystem > xCooSys;
+ lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );
+
+ switch(eObjectType)
+ {
+ case OBJECTTYPE_PAGE:
+ {
+ xObjectProperties.set( xChartModel->getPageBackground() );
+ }
+ break;
+ case OBJECTTYPE_TITLE:
+ {
+ TitleHelper::eTitleType aTitleType = getTitleTypeForCID( rObjectCID );
+ Reference< XTitle > xTitle( TitleHelper::getTitle( aTitleType, xChartModel ) );
+ xObjectProperties.set( xTitle, uno::UNO_QUERY );
+ }
+ break;
+ case OBJECTTYPE_LEGEND:
+ {
+ if( xDiagram.is() )
+ xObjectProperties.set( xDiagram->getLegend(), uno::UNO_QUERY );
+ }
+ break;
+ case OBJECTTYPE_LEGEND_ENTRY:
+ break;
+ case OBJECTTYPE_DIAGRAM:
+ {
+ xObjectProperties = xDiagram;
+ }
+ break;
+ case OBJECTTYPE_DIAGRAM_WALL:
+ {
+ if( xDiagram.is() )
+ xObjectProperties.set( xDiagram->getWall() );
+ }
+ break;
+ case OBJECTTYPE_DIAGRAM_FLOOR:
+ {
+ if( xDiagram.is() )
+ xObjectProperties.set( xDiagram->getFloor() );
+ }
+ break;
+ case OBJECTTYPE_AXIS:
+ {
+ sal_Int32 nDimensionIndex = -1;
+ sal_Int32 nAxisIndex = -1;
+ lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );
+
+ rtl::Reference< Axis > xAxis =
+ AxisHelper::getAxis( nDimensionIndex, nAxisIndex, xCooSys );
+ if( xAxis.is() )
+ xObjectProperties = xAxis;
+ }
+ break;
+ case OBJECTTYPE_AXIS_UNITLABEL:
+ break;
+ case OBJECTTYPE_GRID:
+ case OBJECTTYPE_SUBGRID:
+ {
+ sal_Int32 nDimensionIndex = -1;
+ sal_Int32 nAxisIndex = -1;
+ lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );
+
+ sal_Int32 nSubGridIndex = -1;
+ lcl_parseGridIndices( nSubGridIndex, rObjectCID );
+
+ xObjectProperties.set( AxisHelper::getGridProperties( xCooSys , nDimensionIndex, nAxisIndex, nSubGridIndex ) );
+ }
+ break;
+ case OBJECTTYPE_DATA_LABELS:
+ case OBJECTTYPE_DATA_SERIES:
+ {
+ rtl::Reference< DataSeries > xSeries( ObjectIdentifier::getDataSeriesForCID(
+ rObjectCID, xChartModel ) );
+ if( xSeries.is() )
+ xObjectProperties = xSeries;
+
+ break;
+ }
+ case OBJECTTYPE_DATA_LABEL:
+ case OBJECTTYPE_DATA_POINT:
+ {
+ rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID(
+ rObjectCID, xChartModel );
+ if(xSeries.is())
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aParticleID);
+ xObjectProperties = xSeries->getDataPointByIndex( nIndex );
+ }
+ break;
+ }
+ case OBJECTTYPE_DATA_ERRORS_X:
+ case OBJECTTYPE_DATA_ERRORS_Y:
+ case OBJECTTYPE_DATA_ERRORS_Z:
+ {
+ rtl::Reference< DataSeries > xSeries = ObjectIdentifier::getDataSeriesForCID(
+ rObjectCID, xChartModel );
+ if(xSeries.is())
+ {
+ Reference< beans::XPropertySet > xErrorBarProp;
+ OUString errorBar;
+
+ if ( eObjectType == OBJECTTYPE_DATA_ERRORS_X)
+ errorBar = CHART_UNONAME_ERRORBAR_X;
+ else if (eObjectType == OBJECTTYPE_DATA_ERRORS_Y)
+ errorBar = CHART_UNONAME_ERRORBAR_Y;
+ else
+ errorBar = "ErrorBarZ";
+
+ xSeries->getPropertyValue( errorBar ) >>= xErrorBarProp;
+ xObjectProperties = xErrorBarProp;
+ }
+ break;
+ }
+ case OBJECTTYPE_DATA_AVERAGE_LINE:
+ case OBJECTTYPE_DATA_CURVE:
+ case OBJECTTYPE_DATA_CURVE_EQUATION:
+ {
+ rtl::Reference< DataSeries > xRegressionContainer = ObjectIdentifier::getDataSeriesForCID(
+ rObjectCID, xChartModel );
+ if(xRegressionContainer.is())
+ {
+ sal_Int32 nIndex = o3tl::toInt32(aParticleID);
+ const std::vector< rtl::Reference< RegressionCurveModel > > & aCurveList =
+ xRegressionContainer->getRegressionCurves2();
+ if( nIndex >= 0 && o3tl::make_unsigned(nIndex) < aCurveList.size() )
+ {
+ if( eObjectType == OBJECTTYPE_DATA_CURVE_EQUATION )
+ xObjectProperties = aCurveList[nIndex]->getEquationProperties();
+ else
+ xObjectProperties = aCurveList[nIndex];
+ }
+ }
+ break;
+ }
+ case OBJECTTYPE_DATA_STOCK_RANGE:
+ break;
+ case OBJECTTYPE_DATA_STOCK_LOSS:
+ {
+ rtl::Reference<ChartType> xChartType( lcl_getFirstStockChartType( xChartModel ) );
+ if(xChartType.is())
+ xChartType->getPropertyValue( "BlackDay" ) >>= xObjectProperties;
+ }
+ break;
+ case OBJECTTYPE_DATA_STOCK_GAIN:
+ {
+ rtl::Reference<ChartType> xChartType( lcl_getFirstStockChartType( xChartModel ) );
+ if(xChartType.is())
+ xChartType->getPropertyValue( "WhiteDay" ) >>= xObjectProperties;
+ }
+ break;
+ default: //OBJECTTYPE_UNKNOWN
+ break;
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return xObjectProperties;
+}
+
+rtl::Reference< Axis > ObjectIdentifier::getAxisForCID(
+ const OUString& rObjectCID
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ rtl::Reference< Diagram > xDiagram;
+ rtl::Reference< BaseCoordinateSystem > xCooSys;
+ lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );
+
+ sal_Int32 nDimensionIndex = -1;
+ sal_Int32 nAxisIndex = -1;
+ lcl_parseAxisIndices( nDimensionIndex, nAxisIndex, rObjectCID );
+
+ return AxisHelper::getAxis( nDimensionIndex, nAxisIndex, xCooSys );
+}
+
+rtl::Reference< DataSeries > ObjectIdentifier::getDataSeriesForCID(
+ const OUString& rObjectCID
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ rtl::Reference< Diagram > xDiagram;
+ rtl::Reference< BaseCoordinateSystem > xCooSys;
+ lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );
+
+ sal_Int32 nChartTypeIndex = -1;
+ sal_Int32 nSeriesIndex = -1;
+ sal_Int32 nPointIndex = -1;
+ lcl_parseSeriesIndices( nChartTypeIndex, nSeriesIndex, nPointIndex, rObjectCID );
+
+ rtl::Reference< DataSeries > xSeries;
+ rtl::Reference< ChartType > xDataSeriesContainer( DiagramHelper::getChartTypeByIndex( xDiagram, nChartTypeIndex ) );
+ if( xDataSeriesContainer.is() )
+ {
+ const std::vector< rtl::Reference< DataSeries > > & aDataSeriesSeq( xDataSeriesContainer->getDataSeries2() );
+ if( nSeriesIndex >= 0 && o3tl::make_unsigned(nSeriesIndex) < aDataSeriesSeq.size() )
+ xSeries = aDataSeriesSeq[nSeriesIndex];
+ }
+
+ return xSeries;
+}
+
+rtl::Reference< Diagram > ObjectIdentifier::getDiagramForCID(
+ const OUString& rObjectCID
+ , const rtl::Reference<::chart::ChartModel>& xChartModel )
+{
+ rtl::Reference< Diagram > xDiagram;
+ rtl::Reference< BaseCoordinateSystem > xCooSys;
+ lcl_getDiagramAndCooSys( rObjectCID, xChartModel, xDiagram, xCooSys );
+
+ return xDiagram;
+}
+
+TitleHelper::eTitleType ObjectIdentifier::getTitleTypeForCID( std::u16string_view rCID )
+{
+ TitleHelper::eTitleType eRet( TitleHelper::MAIN_TITLE );
+
+ std::u16string_view aParentParticle = ObjectIdentifier::getFullParentParticle( rCID );
+ const tTitleMap& rMap = lcl_getTitleMap();
+ tTitleMap::const_iterator aIt = std::find_if(rMap.begin(), rMap.end(),
+ [&aParentParticle](tTitleMap::const_reference rEntry) { return aParentParticle == rEntry.second; });
+ if (aIt != rMap.end())
+ eRet = (*aIt).first;
+
+ return eRet;
+}
+
+OUString ObjectIdentifier::getSeriesParticleFromCID( const OUString& rCID )
+{
+ sal_Int32 nDiagramIndex = -1;
+ sal_Int32 nCooSysIndex = -1;
+ lcl_parseCooSysIndices( nDiagramIndex, nCooSysIndex, rCID );
+
+ sal_Int32 nChartTypeIndex = -1;
+ sal_Int32 nSeriesIndex = -1;
+ sal_Int32 nPointIndex = -1;
+ lcl_parseSeriesIndices( nChartTypeIndex, nSeriesIndex, nPointIndex, rCID );
+
+ return ObjectIdentifier::createParticleForSeries( nDiagramIndex, nCooSysIndex, nChartTypeIndex, nSeriesIndex );
+}
+
+OUString ObjectIdentifier::getMovedSeriesCID( const OUString& rObjectCID, bool bForward )
+{
+ sal_Int32 nDiagramIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, "CID/D=" ) );
+ sal_Int32 nCooSysIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, "CS=" ) );
+ sal_Int32 nChartTypeIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, "CT=" ) );
+ sal_Int32 nSeriesIndex = lcl_StringToIndex( lcl_getIndexStringAfterString( rObjectCID, "Series=" ) );
+
+ if( bForward )
+ nSeriesIndex--;
+ else
+ nSeriesIndex++;
+
+ OUString aRet = ObjectIdentifier::createParticleForSeries( nDiagramIndex, nCooSysIndex, nChartTypeIndex, nSeriesIndex );
+ return ObjectIdentifier::createClassifiedIdentifierForParticle( aRet );
+}
+
+bool ObjectIdentifier::isValid() const
+{
+ return ( isAutoGeneratedObject() || isAdditionalShape() );
+}
+
+bool ObjectIdentifier::isAutoGeneratedObject() const
+{
+ return ( !m_aObjectCID.isEmpty() );
+}
+
+bool ObjectIdentifier::isAdditionalShape() const
+{
+ return m_xAdditionalShape.is();
+}
+
+Any ObjectIdentifier::getAny() const
+{
+ Any aAny;
+ if ( isAutoGeneratedObject() )
+ {
+ aAny <<= getObjectCID();
+ }
+ else if ( isAdditionalShape() )
+ {
+ aAny <<= getAdditionalShape();
+ }
+ return aAny;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx b/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..3d7f1c076
--- /dev/null
+++ b/chart2/source/tools/PolynomialRegressionCurveCalculator.cxx
@@ -0,0 +1,392 @@
+/* -*- 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 <PolynomialRegressionCurveCalculator.hxx>
+#include <RegressionCalculationHelper.hxx>
+
+#include <cmath>
+#include <limits>
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <SpecialCharacters.hxx>
+
+using namespace com::sun::star;
+
+namespace chart
+{
+
+static double lcl_GetDotProduct(std::vector<double>& aVec1, std::vector<double>& aVec2)
+{
+ double fResult = 0.0;
+ assert(aVec1.size() == aVec2.size());
+ for (size_t i = 0; i < aVec1.size(); ++i)
+ fResult += aVec1[i] * aVec2[i];
+ return fResult;
+}
+
+PolynomialRegressionCurveCalculator::PolynomialRegressionCurveCalculator()
+{}
+
+PolynomialRegressionCurveCalculator::~PolynomialRegressionCurveCalculator()
+{}
+
+void PolynomialRegressionCurveCalculator::computeCorrelationCoefficient(
+ RegressionCalculationHelper::tDoubleVectorPair& rValues,
+ const sal_Int32 aNoValues,
+ double yAverage )
+{
+ double aSumError = 0.0;
+ double aSumTotal = 0.0;
+ double aSumYpred2 = 0.0;
+
+ for( sal_Int32 i = 0; i < aNoValues; i++ )
+ {
+ double xValue = rValues.first[i];
+ double yActual = rValues.second[i];
+ double yPredicted = getCurveValue( xValue );
+ aSumTotal += (yActual - yAverage) * (yActual - yAverage);
+ aSumError += (yActual - yPredicted) * (yActual - yPredicted);
+ if(mForceIntercept)
+ aSumYpred2 += (yPredicted - mInterceptValue) * (yPredicted - mInterceptValue);
+ }
+
+ double aRSquared = 0.0;
+ if(mForceIntercept)
+ {
+ if (auto const div = aSumError + aSumYpred2)
+ {
+ aRSquared = aSumYpred2 / div;
+ }
+ }
+ else if (aSumTotal != 0.0)
+ {
+ aRSquared = 1.0 - (aSumError / aSumTotal);
+ }
+
+ if (aRSquared > 0.0)
+ m_fCorrelationCoefficient = std::sqrt(aRSquared);
+ else
+ m_fCorrelationCoefficient = 0.0;
+}
+
+// ____ XRegressionCurveCalculator ____
+void SAL_CALL PolynomialRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& aXValues,
+ const uno::Sequence< double >& aYValues )
+{
+ m_fCorrelationCoefficient = std::numeric_limits<double>::quiet_NaN();
+
+ RegressionCalculationHelper::tDoubleVectorPair aValues(
+ RegressionCalculationHelper::cleanup( aXValues, aYValues, RegressionCalculationHelper::isValid()));
+
+ const sal_Int32 aNoValues = aValues.first.size();
+
+ const sal_Int32 aNoPowers = mForceIntercept ? mDegree : mDegree + 1;
+
+ mCoefficients.clear();
+ mCoefficients.resize(aNoPowers, 0.0);
+
+ double yAverage = 0.0;
+
+ std::vector<double> yVector;
+ yVector.resize(aNoValues, 0.0);
+
+ for(sal_Int32 i = 0; i < aNoValues; i++)
+ {
+ double yValue = aValues.second[i];
+ if (mForceIntercept)
+ yValue -= mInterceptValue;
+ yVector[i] = yValue;
+ yAverage += yValue;
+ }
+ if (aNoValues != 0)
+ {
+ yAverage /= aNoValues;
+ }
+
+ // Special case for single variable regression like in LINEST
+ // implementation in Calc.
+ if (mDegree == 1)
+ {
+ std::vector<double> xVector;
+ xVector.resize(aNoValues, 0.0);
+ double xAverage = 0.0;
+
+ for(sal_Int32 i = 0; i < aNoValues; ++i)
+ {
+ double xValue = aValues.first[i];
+ xVector[i] = xValue;
+ xAverage += xValue;
+ }
+ if (aNoValues != 0)
+ {
+ xAverage /= aNoValues;
+ }
+
+ if (!mForceIntercept)
+ {
+ for (sal_Int32 i = 0; i < aNoValues; ++i)
+ {
+ xVector[i] -= xAverage;
+ yVector[i] -= yAverage;
+ }
+ }
+ double fSumXY = lcl_GetDotProduct(xVector, yVector);
+ double fSumX2 = lcl_GetDotProduct(xVector, xVector);
+
+ double fSlope = fSumXY / fSumX2;
+
+ if (!mForceIntercept)
+ {
+ mInterceptValue = ::rtl::math::approxSub(yAverage, fSlope * xAverage);
+ mCoefficients[0] = mInterceptValue;
+ mCoefficients[1] = fSlope;
+ }
+ else
+ {
+ mCoefficients[0] = fSlope;
+ mCoefficients.insert(mCoefficients.begin(), mInterceptValue);
+ }
+
+ computeCorrelationCoefficient(aValues, aNoValues, yAverage);
+ return;
+ }
+
+ std::vector<double> aQRTransposed;
+ aQRTransposed.resize(aNoValues * aNoPowers, 0.0);
+
+ for(sal_Int32 j = 0; j < aNoPowers; j++)
+ {
+ sal_Int32 aPower = mForceIntercept ? j+1 : j;
+ sal_Int32 aColumnIndex = j * aNoValues;
+ for(sal_Int32 i = 0; i < aNoValues; i++)
+ {
+ double xValue = aValues.first[i];
+ aQRTransposed[i + aColumnIndex] = std::pow(xValue, static_cast<int>(aPower));
+ }
+ }
+
+ // QR decomposition - based on org.apache.commons.math.linear.QRDecomposition from apache commons math (ASF)
+ sal_Int32 aMinorSize = std::min(aNoValues, aNoPowers);
+
+ std::vector<double> aDiagonal;
+ aDiagonal.resize(aMinorSize, 0.0);
+
+ // Calculate Householder reflectors
+ for (sal_Int32 aMinor = 0; aMinor < aMinorSize; aMinor++)
+ {
+ double aNormSqr = 0.0;
+ for (sal_Int32 x = aMinor; x < aNoValues; x++)
+ {
+ double c = aQRTransposed[x + aMinor * aNoValues];
+ aNormSqr += c * c;
+ }
+
+ double a;
+
+ if (aQRTransposed[aMinor + aMinor * aNoValues] > 0.0)
+ a = -std::sqrt(aNormSqr);
+ else
+ a = std::sqrt(aNormSqr);
+
+ aDiagonal[aMinor] = a;
+
+ if (a != 0.0)
+ {
+ aQRTransposed[aMinor + aMinor * aNoValues] -= a;
+
+ for (sal_Int32 aColumn = aMinor + 1; aColumn < aNoPowers; aColumn++)
+ {
+ double alpha = 0.0;
+ for (sal_Int32 aRow = aMinor; aRow < aNoValues; aRow++)
+ {
+ alpha -= aQRTransposed[aRow + aColumn * aNoValues] * aQRTransposed[aRow + aMinor * aNoValues];
+ }
+ alpha /= a * aQRTransposed[aMinor + aMinor * aNoValues];
+
+ for (sal_Int32 aRow = aMinor; aRow < aNoValues; aRow++)
+ {
+ aQRTransposed[aRow + aColumn * aNoValues] -= alpha * aQRTransposed[aRow + aMinor * aNoValues];
+ }
+ }
+ }
+ }
+
+ // Solve the linear equation
+ for (sal_Int32 aMinor = 0; aMinor < aMinorSize; aMinor++)
+ {
+ double aDotProduct = 0;
+
+ for (sal_Int32 aRow = aMinor; aRow < aNoValues; aRow++)
+ {
+ aDotProduct += yVector[aRow] * aQRTransposed[aRow + aMinor * aNoValues];
+ }
+ aDotProduct /= aDiagonal[aMinor] * aQRTransposed[aMinor + aMinor * aNoValues];
+
+ for (sal_Int32 aRow = aMinor; aRow < aNoValues; aRow++)
+ {
+ yVector[aRow] += aDotProduct * aQRTransposed[aRow + aMinor * aNoValues];
+ }
+
+ }
+
+ for (sal_Int32 aRow = aDiagonal.size() - 1; aRow >= 0; aRow--)
+ {
+ yVector[aRow] /= aDiagonal[aRow];
+ double yRow = yVector[aRow];
+ mCoefficients[aRow] = yRow;
+
+ for (sal_Int32 i = 0; i < aRow; i++)
+ {
+ yVector[i] -= yRow * aQRTransposed[i + aRow * aNoValues];
+ }
+ }
+
+ if(mForceIntercept)
+ {
+ mCoefficients.insert(mCoefficients.begin(), mInterceptValue);
+ }
+
+ // Calculate correlation coefficient
+ computeCorrelationCoefficient(aValues, aNoValues, yAverage);
+}
+
+double SAL_CALL PolynomialRegressionCurveCalculator::getCurveValue( double x )
+{
+ if (mCoefficients.empty())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ sal_Int32 aNoCoefficients = static_cast<sal_Int32>(mCoefficients.size());
+
+ // Horner's method
+ double fResult = 0.0;
+ for (sal_Int32 i = aNoCoefficients - 1; i >= 0; i--)
+ {
+ fResult = mCoefficients[i] + (x * fResult);
+ }
+ return fResult;
+}
+
+OUString PolynomialRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey, sal_Int32* pFormulaMaxWidth /* = nullptr */ ) const
+{
+ OUStringBuffer aBuf( mYName + " = " );
+
+ sal_Int32 nValueLength=0;
+ sal_Int32 aLastIndex = mCoefficients.size() - 1;
+
+ if ( pFormulaMaxWidth && *pFormulaMaxWidth > 0 )
+ {
+ sal_Int32 nCharMin = aBuf.getLength(); // count characters different from coefficients
+ double nCoefficients = aLastIndex + 1.0; // number of coefficients
+ for (sal_Int32 i = aLastIndex; i >= 0; i--)
+ {
+ double aValue = mCoefficients[i];
+ if ( aValue == 0.0 )
+ { // do not count coefficient if it is 0
+ nCoefficients --;
+ continue;
+ }
+ if ( rtl::math::approxEqual( fabs( aValue ) , 1.0 ) )
+ { // do not count coefficient if it is 1
+ nCoefficients --;
+ if ( i == 0 ) // intercept = 1
+ nCharMin ++;
+ }
+ if ( i != aLastIndex )
+ nCharMin += 3; // " + "
+ if ( i > 0 )
+ {
+ nCharMin += mXName.getLength() + 1; // " x"
+ if ( i > 1 )
+ nCharMin +=1; // "^i"
+ if ( i >= 10 )
+ nCharMin ++; // 2 digits for i
+ }
+ }
+ nValueLength = ( *pFormulaMaxWidth - nCharMin ) / nCoefficients;
+ if ( nValueLength <= 0 )
+ nValueLength = 1;
+ }
+
+ bool bFindValue = false;
+ sal_Int32 nLineLength = aBuf.getLength();
+ for (sal_Int32 i = aLastIndex; i >= 0; i--)
+ {
+ double aValue = mCoefficients[i];
+ OUStringBuffer aTmpBuf(""); // temporary buffer
+ if (aValue == 0.0)
+ {
+ continue;
+ }
+ else if (aValue < 0.0)
+ {
+ if ( bFindValue ) // if it is not the first aValue
+ aTmpBuf.append( " " );
+ aTmpBuf.append( OUStringChar(aMinusSign) + " ");
+ aValue = - aValue;
+ }
+ else
+ {
+ if ( bFindValue ) // if it is not the first aValue
+ aTmpBuf.append( " + " );
+ }
+ bFindValue = true;
+
+ // if nValueLength not calculated then nullptr
+ sal_Int32* pValueLength = nValueLength ? &nValueLength : nullptr;
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, aValue, pValueLength );
+ if ( i == 0 || aValueString != "1" ) // aValueString may be rounded to 1 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString );
+ if ( i > 0 ) // insert blank between coefficient and x
+ aTmpBuf.append( " " );
+ }
+
+ if(i > 0)
+ {
+ aTmpBuf.append( mXName );
+ if (i > 1)
+ {
+ if (i < 10) // simple case if only one digit
+ aTmpBuf.append( aSuperscriptFigures[ i ] );
+ else
+ {
+ OUString aValueOfi = OUString::number( i );
+ for ( sal_Int32 n = 0; n < aValueOfi.getLength() ; n++ )
+ {
+ sal_Int32 nIndex = aValueOfi[n] - u'0';
+ aTmpBuf.append( aSuperscriptFigures[ nIndex ] );
+ }
+ }
+ }
+ }
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+ }
+ if ( std::u16string_view(aBuf) == OUStringConcatenation( mYName + " = ") )
+ aBuf.append( "0" );
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/PopupRequest.cxx b/chart2/source/tools/PopupRequest.cxx
new file mode 100644
index 000000000..70dd65b78
--- /dev/null
+++ b/chart2/source/tools/PopupRequest.cxx
@@ -0,0 +1,31 @@
+/* -*- 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 <PopupRequest.hxx>
+
+using namespace css;
+
+namespace chart
+{
+PopupRequest::PopupRequest() {}
+
+PopupRequest::~PopupRequest() {}
+
+// ____ XRequestCallback ____
+
+void SAL_CALL PopupRequest::addCallback(const uno::Reference<awt::XCallback>& xCallback,
+ const uno::Any& /*aData*/)
+{
+ m_xCallback = xCallback;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/PotentialRegressionCurveCalculator.cxx b/chart2/source/tools/PotentialRegressionCurveCalculator.cxx
new file mode 100644
index 000000000..01aa5b254
--- /dev/null
+++ b/chart2/source/tools/PotentialRegressionCurveCalculator.cxx
@@ -0,0 +1,188 @@
+/* -*- 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 <PotentialRegressionCurveCalculator.hxx>
+#include <RegressionCalculationHelper.hxx>
+#include <SpecialCharacters.hxx>
+
+#include <limits>
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+PotentialRegressionCurveCalculator::PotentialRegressionCurveCalculator()
+ : m_fSlope(std::numeric_limits<double>::quiet_NaN())
+ , m_fIntercept(std::numeric_limits<double>::quiet_NaN())
+ , m_fSign(1.0)
+{
+}
+
+PotentialRegressionCurveCalculator::~PotentialRegressionCurveCalculator()
+{}
+
+// ____ XRegressionCurveCalculator ____
+void SAL_CALL PotentialRegressionCurveCalculator::recalculateRegression(
+ const uno::Sequence< double >& aXValues,
+ const uno::Sequence< double >& aYValues )
+{
+ RegressionCalculationHelper::tDoubleVectorPair aValues(
+ RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValidAndBothPositive()));
+ m_fSign = 1.0;
+
+ size_t nMax = aValues.first.size();
+ if( nMax <= 1 ) // at least 2 points
+ {
+ aValues = RegressionCalculationHelper::cleanup(
+ aXValues, aYValues,
+ RegressionCalculationHelper::isValidAndXPositiveAndYNegative());
+ nMax = aValues.first.size();
+ if( nMax <= 1 )
+ {
+ m_fSlope = std::numeric_limits<double>::quiet_NaN();
+ m_fIntercept = std::numeric_limits<double>::quiet_NaN();
+ m_fCorrelationCoefficient = std::numeric_limits<double>::quiet_NaN();
+ return;
+ }
+ m_fSign = -1.0;
+ }
+
+ double fAverageX = 0.0, fAverageY = 0.0;
+ size_t i = 0;
+ for( i = 0; i < nMax; ++i )
+ {
+ fAverageX += log( aValues.first[i] );
+ fAverageY += log( m_fSign * aValues.second[i] );
+ }
+
+ const double fN = static_cast< double >( nMax );
+ fAverageX /= fN;
+ fAverageY /= fN;
+
+ double fQx = 0.0, fQy = 0.0, fQxy = 0.0;
+ for( i = 0; i < nMax; ++i )
+ {
+ double fDeltaX = log( aValues.first[i] ) - fAverageX;
+ double fDeltaY = log( m_fSign * aValues.second[i] ) - fAverageY;
+
+ fQx += fDeltaX * fDeltaX;
+ fQy += fDeltaY * fDeltaY;
+ fQxy += fDeltaX * fDeltaY;
+ }
+
+ m_fSlope = fQxy / fQx;
+ m_fIntercept = fAverageY - m_fSlope * fAverageX;
+ m_fCorrelationCoefficient = fQxy / sqrt( fQx * fQy );
+
+ m_fIntercept = m_fSign * exp( m_fIntercept );
+}
+
+double SAL_CALL PotentialRegressionCurveCalculator::getCurveValue( double x )
+{
+ if( ! ( std::isnan( m_fSlope ) ||
+ std::isnan( m_fIntercept )))
+ {
+ return m_fIntercept * pow( x, m_fSlope );
+ }
+
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+uno::Sequence< geometry::RealPoint2D > SAL_CALL PotentialRegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const uno::Reference< chart2::XScaling >& xScalingX,
+ const uno::Reference< chart2::XScaling >& xScalingY,
+ sal_Bool bMaySkipPointsInCalculation )
+{
+ if( bMaySkipPointsInCalculation &&
+ isLogarithmicScaling( xScalingX ) &&
+ isLogarithmicScaling( xScalingY ))
+ {
+ // optimize result
+ uno::Sequence< geometry::RealPoint2D > aResult{ { min, getCurveValue( min ) },
+ { max, getCurveValue( max ) } };
+
+ return aResult;
+ }
+ return RegressionCurveCalculator::getCurveValues( min, max, nPointCount, xScalingX, xScalingY, bMaySkipPointsInCalculation );
+}
+
+OUString PotentialRegressionCurveCalculator::ImplGetRepresentation(
+ const uno::Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey, sal_Int32* pFormulaMaxWidth /* = nullptr */ ) const
+{
+ bool bHasIntercept = !rtl::math::approxEqual( fabs(m_fIntercept), 1.0 );
+ OUStringBuffer aBuf( mYName + " = " );
+ sal_Int32 nLineLength = aBuf.getLength();
+ sal_Int32 nValueLength=0;
+ if ( pFormulaMaxWidth && *pFormulaMaxWidth > 0 ) // count nValueLength
+ {
+ sal_Int32 nCharMin = nLineLength + mXName.getLength() + 3; // 3 = "^" + 2 extra characters
+ if ( m_fIntercept != 0.0 && m_fSlope != 0.0 )
+ {
+ if ( m_fIntercept < 0.0 )
+ nCharMin += 2; // "- "
+ if ( bHasIntercept )
+ nValueLength = (*pFormulaMaxWidth - nCharMin) / 2;
+ }
+ if ( nValueLength == 0 ) // not yet calculated
+ nValueLength = *pFormulaMaxWidth - nCharMin;
+ if ( nValueLength <= 0 )
+ nValueLength = 1;
+ }
+
+ if( m_fIntercept == 0.0 )
+ {
+ aBuf.append( '0' );
+ }
+ else
+ {
+ // temporary buffer
+ OUStringBuffer aTmpBuf("");
+ // if nValueLength not calculated then nullptr
+ sal_Int32* pValueLength = nValueLength ? &nValueLength : nullptr;
+ if ( m_fIntercept < 0.0 ) // add intercept value
+ aTmpBuf.append( OUStringChar(aMinusSign) + " " );
+ if( bHasIntercept )
+ {
+ OUString aValueString = getFormattedString( xNumFormatter, nNumberFormatKey, fabs(m_fIntercept), pValueLength );
+ if ( aValueString != "1" ) // aValueString may be rounded to 1 if nValueLength is small
+ {
+ aTmpBuf.append( aValueString + " " );
+ }
+ }
+ if( m_fSlope != 0.0 ) // add slope value
+ {
+ aTmpBuf.append( mXName + "^" );
+ aTmpBuf.append( getFormattedString( xNumFormatter, nNumberFormatKey, m_fSlope, pValueLength ));
+ }
+ addStringToEquation( aBuf, nLineLength, aTmpBuf, pFormulaMaxWidth );
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/PropertyHelper.cxx b/chart2/source/tools/PropertyHelper.cxx
new file mode 100644
index 000000000..9f34ba1c2
--- /dev/null
+++ b/chart2/source/tools/PropertyHelper.cxx
@@ -0,0 +1,296 @@
+/* -*- 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 <PropertyHelper.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <iterator>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+
+namespace
+{
+struct lcl_EqualsElement
+{
+ explicit lcl_EqualsElement( const Any & rValue, const Reference< container::XNameAccess > & xAccess )
+ : m_aValue( rValue ), m_xAccess( xAccess )
+ {
+ OSL_ASSERT( m_xAccess.is());
+ }
+
+ bool operator() ( const OUString & rName )
+ {
+ try
+ {
+ return (m_xAccess->getByName( rName ) == m_aValue);
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return false;
+ }
+
+private:
+ Any m_aValue;
+ Reference< container::XNameAccess > m_xAccess;
+};
+
+struct lcl_StringMatches
+{
+ explicit lcl_StringMatches( const OUString & rCmpStr ) :
+ m_aCmpStr( rCmpStr )
+ {}
+
+ bool operator() ( std::u16string_view rStr )
+ {
+ return o3tl::starts_with( rStr, m_aCmpStr );
+ }
+
+private:
+ OUString m_aCmpStr;
+};
+
+struct lcl_OUStringRestToInt32
+{
+ explicit lcl_OUStringRestToInt32( sal_Int32 nPrefixLength ) :
+ m_nPrefixLength( nPrefixLength )
+ {}
+ sal_Int32 operator() ( const OUString & rStr )
+ {
+ if( m_nPrefixLength > rStr.getLength() )
+ return 0;
+ return o3tl::toInt32(rStr.subView( m_nPrefixLength ));
+ }
+private:
+ sal_Int32 m_nPrefixLength;
+};
+
+/** adds a fill gradient, fill hatch, fill bitmap, fill transparency gradient,
+ line dash or line marker to the corresponding name container with a unique
+ name.
+
+ @param rPrefix
+ The prefix used for automated name generation.
+
+ @param rPreferredName
+ If this string is not empty it is used as name if it is unique in the
+ table. Otherwise a new name is generated using pPrefix.
+
+ @return the new name under which the property was stored in the table
+*/
+OUString lcl_addNamedPropertyUniqueNameToTable(
+ const Any & rValue,
+ const Reference< container::XNameContainer > & xNameContainer,
+ const OUString & rPrefix,
+ const OUString & rPreferredName )
+{
+ if( ! xNameContainer.is() ||
+ ! rValue.hasValue() ||
+ ( rValue.getValueType() != xNameContainer->getElementType()))
+ return rPreferredName;
+
+ try
+ {
+ Reference< container::XNameAccess > xNameAccess( xNameContainer, uno::UNO_QUERY_THROW );
+ const uno::Sequence<OUString> aElementNames = xNameAccess->getElementNames();
+ auto it = std::find_if( aElementNames.begin(), aElementNames.end(), lcl_EqualsElement( rValue, xNameAccess ));
+
+ // element found => return name
+ if( it != aElementNames.end())
+ return *it;
+
+ // element not found in container
+ OUString aUniqueName;
+
+ // check if preferred name is already used
+ if( !rPreferredName.isEmpty())
+ {
+ auto aIt = std::find( aElementNames.begin(), aElementNames.end(), rPreferredName );
+ if( aIt == aElementNames.end())
+ aUniqueName = rPreferredName;
+ }
+
+ if( aUniqueName.isEmpty())
+ {
+ auto aNames( comphelper::sequenceToContainer<std::vector< OUString >>( aElementNames ));
+ // create a unique id using the prefix plus a number
+ std::vector< sal_Int32 > aNumbers;
+ std::vector< OUString >::iterator aNonConstIt(
+ std::partition( aNames.begin(), aNames.end(), lcl_StringMatches( rPrefix )));
+ std::transform( aNames.begin(), aNonConstIt,
+ back_inserter( aNumbers ),
+ lcl_OUStringRestToInt32( rPrefix.getLength() ));
+ std::vector< sal_Int32 >::const_iterator aMaxIt(
+ std::max_element( aNumbers.begin(), aNumbers.end()));
+
+ sal_Int32 nIndex = 1;
+ if( aMaxIt != aNumbers.end())
+ nIndex = (*aMaxIt) + 1;
+
+ aUniqueName = rPrefix + OUString::number( nIndex );
+ }
+
+ OSL_ASSERT( !aUniqueName.isEmpty());
+ xNameContainer->insertByName( aUniqueName, rValue );
+ return aUniqueName;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return rPreferredName;
+}
+
+} // anonymous namespace
+
+namespace chart::PropertyHelper
+{
+
+OUString addLineDashUniqueNameToTable(
+ const Any & rValue,
+ const Reference< lang::XMultiServiceFactory > & xFact,
+ const OUString & rPreferredName )
+{
+ if( xFact.is())
+ {
+ Reference< container::XNameContainer > xNameCnt(
+ xFact->createInstance( "com.sun.star.drawing.DashTable"),
+ uno::UNO_QUERY );
+ if( xNameCnt.is())
+ return lcl_addNamedPropertyUniqueNameToTable(
+ rValue, xNameCnt, "ChartDash ", rPreferredName );
+ }
+ return OUString();
+}
+
+OUString addGradientUniqueNameToTable(
+ const Any & rValue,
+ const Reference< lang::XMultiServiceFactory > & xFact,
+ const OUString & rPreferredName )
+{
+ if( xFact.is())
+ {
+ Reference< container::XNameContainer > xNameCnt(
+ xFact->createInstance( "com.sun.star.drawing.GradientTable"),
+ uno::UNO_QUERY );
+ if( xNameCnt.is())
+ return lcl_addNamedPropertyUniqueNameToTable(
+ rValue, xNameCnt, "ChartGradient ", rPreferredName );
+ }
+ return OUString();
+}
+
+OUString addTransparencyGradientUniqueNameToTable(
+ const Any & rValue,
+ const Reference< lang::XMultiServiceFactory > & xFact,
+ const OUString & rPreferredName )
+{
+ if( xFact.is())
+ {
+ Reference< container::XNameContainer > xNameCnt(
+ xFact->createInstance( "com.sun.star.drawing.TransparencyGradientTable"),
+ uno::UNO_QUERY );
+ if( xNameCnt.is())
+ return lcl_addNamedPropertyUniqueNameToTable(
+ rValue, xNameCnt, "ChartTransparencyGradient ", rPreferredName );
+ }
+ return OUString();
+}
+
+OUString addHatchUniqueNameToTable(
+ const Any & rValue,
+ const Reference< lang::XMultiServiceFactory > & xFact,
+ const OUString & rPreferredName )
+{
+ if( xFact.is())
+ {
+ Reference< container::XNameContainer > xNameCnt(
+ xFact->createInstance( "com.sun.star.drawing.HatchTable"),
+ uno::UNO_QUERY );
+ if( xNameCnt.is())
+ return lcl_addNamedPropertyUniqueNameToTable(
+ rValue, xNameCnt, "ChartHatch ", rPreferredName );
+ }
+ return OUString();
+}
+
+OUString addBitmapUniqueNameToTable(
+ const Any & rValue,
+ const Reference< lang::XMultiServiceFactory > & xFact,
+ const OUString & rPreferredName )
+{
+ if( xFact.is())
+ {
+ Reference< container::XNameContainer > xNameCnt(
+ xFact->createInstance( "com.sun.star.drawing.BitmapTable"),
+ uno::UNO_QUERY );
+ if( xNameCnt.is())
+ return lcl_addNamedPropertyUniqueNameToTable(
+ rValue, xNameCnt, "ChartBitmap ", rPreferredName );
+ }
+ return OUString();
+}
+
+void setPropertyValueAny( tPropertyValueMap & rOutMap, tPropertyValueMapKey key, const uno::Any & rAny )
+{
+ tPropertyValueMap::iterator aIt( rOutMap.find( key ));
+ if( aIt == rOutMap.end())
+ rOutMap.emplace( key, rAny );
+ else
+ (*aIt).second = rAny;
+}
+
+template<>
+ void setPropertyValue< css::uno::Any >( tPropertyValueMap & rOutMap, tPropertyValueMapKey key, const css::uno::Any & rAny )
+{
+ setPropertyValueAny( rOutMap, key, rAny );
+}
+
+void setPropertyValueDefaultAny( tPropertyValueMap & rOutMap, tPropertyValueMapKey key, const uno::Any & rAny )
+{
+ OSL_ENSURE( rOutMap.end() == rOutMap.find( key ), "Default already exists for property" );
+ setPropertyValue( rOutMap, key, rAny );
+}
+
+template<>
+ void setPropertyValueDefault< css::uno::Any >( tPropertyValueMap & rOutMap, tPropertyValueMapKey key, const css::uno::Any & rAny )
+{
+ setPropertyValueDefaultAny( rOutMap, key, rAny );
+}
+
+void setEmptyPropertyValueDefault( tPropertyValueMap & rOutMap, tPropertyValueMapKey key )
+{
+ setPropertyValueDefault( rOutMap, key, uno::Any());
+}
+
+} // namespace chart::PropertyHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RangeHighlighter.cxx b/chart2/source/tools/RangeHighlighter.cxx
new file mode 100644
index 000000000..d59400050
--- /dev/null
+++ b/chart2/source/tools/RangeHighlighter.cxx
@@ -0,0 +1,394 @@
+/* -*- 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 <RangeHighlighter.hxx>
+#include <WeakListenerAdapter.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <DataSourceHelper.hxx>
+#include <ObjectIdentifier.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <Diagram.hxx>
+
+#include <com/sun/star/chart2/ScaleData.hpp>
+#include <com/sun/star/chart2/XAxis.hpp>
+#include <com/sun/star/chart2/XDataSeries.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/color.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace
+{
+
+const Color defaultPreferredColor = COL_LIGHTBLUE;
+
+void lcl_fillRanges(
+ Sequence< chart2::data::HighlightedRange > & rOutRanges,
+ const Sequence< OUString >& aRangeStrings,
+ Color nPreferredColor,
+ sal_Int32 nIndex = -1 )
+{
+ rOutRanges.realloc( aRangeStrings.getLength());
+ auto pOutRanges = rOutRanges.getArray();
+ for( sal_Int32 i=0; i<aRangeStrings.getLength(); ++i )
+ {
+ pOutRanges[i].RangeRepresentation = aRangeStrings[i];
+ pOutRanges[i].PreferredColor = sal_Int32(nPreferredColor);
+ pOutRanges[i].AllowMerginigWithOtherRanges = false;
+ pOutRanges[i].Index = nIndex;
+ }
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+RangeHighlighter::RangeHighlighter(
+ const rtl::Reference< ChartModel > & xChartModel ) :
+ m_xSelectionSupplier(xChartModel->getCurrentController(), uno::UNO_QUERY),
+ m_xChartModel( xChartModel ),
+ m_nAddedListenerCount( 0 ),
+ m_bIncludeHiddenCells(true)
+{
+}
+
+RangeHighlighter::~RangeHighlighter()
+{}
+
+// ____ XRangeHighlighter ____
+Sequence< chart2::data::HighlightedRange > SAL_CALL RangeHighlighter::getSelectedRanges()
+{
+ return m_aSelectedRanges;
+}
+
+void RangeHighlighter::determineRanges()
+{
+ m_aSelectedRanges.realloc( 0 );
+ if( !m_xChartModel.is())
+ return;
+ if( !m_xSelectionSupplier.is())
+ return;
+
+ try
+ {
+ m_bIncludeHiddenCells = ChartModelHelper::isIncludeHiddenCells( m_xChartModel );
+
+ uno::Any aSelection( m_xSelectionSupplier->getSelection());
+ const uno::Type& rType = aSelection.getValueType();
+
+ if ( rType == cppu::UnoType<OUString>::get() )
+ {
+ // @todo??: maybe getSelection() should return a model object rather than a CID
+
+ OUString aCID;
+ aSelection >>= aCID;
+ if ( !aCID.isEmpty() )
+ {
+ ObjectType eObjectType = ObjectIdentifier::getObjectType( aCID );
+ sal_Int32 nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aCID );
+ rtl::Reference< DataSeries > xDataSeries( ObjectIdentifier::getDataSeriesForCID( aCID, m_xChartModel ) );
+ if( eObjectType == OBJECTTYPE_LEGEND_ENTRY )
+ {
+ OUString aParentParticel( ObjectIdentifier::getFullParentParticle( aCID ) );
+ ObjectType eParentObjectType = ObjectIdentifier::getObjectType( aParentParticel );
+ eObjectType = eParentObjectType;
+ if( eObjectType == OBJECTTYPE_DATA_POINT )
+ nIndex = ObjectIdentifier::getIndexFromParticleOrCID( aParentParticel );
+ }
+
+ if( eObjectType == OBJECTTYPE_DATA_POINT || eObjectType == OBJECTTYPE_DATA_LABEL )
+ {
+ // Data Point
+ fillRangesForDataPoint( xDataSeries, nIndex );
+ return;
+ }
+ else if( eObjectType == OBJECTTYPE_DATA_ERRORS_X ||
+ eObjectType == OBJECTTYPE_DATA_ERRORS_Y ||
+ eObjectType == OBJECTTYPE_DATA_ERRORS_Z )
+ {
+ // select error bar ranges, or data series, if the style is
+ // not set to FROM_DATA
+ fillRangesForErrorBars( ObjectIdentifier::getObjectPropertySet( aCID, m_xChartModel ), xDataSeries );
+ return;
+ }
+ else if( xDataSeries.is() )
+ {
+ // Data Series
+ fillRangesForDataSeries( xDataSeries );
+ return;
+ }
+ else if( eObjectType == OBJECTTYPE_AXIS )
+ {
+ // Axis (Categories)
+ Reference< chart2::XAxis > xAxis( ObjectIdentifier::getObjectPropertySet( aCID, m_xChartModel ), uno::UNO_QUERY );
+ if( xAxis.is())
+ {
+ fillRangesForCategories( xAxis );
+ return;
+ }
+ }
+ else if( eObjectType == OBJECTTYPE_PAGE
+ || eObjectType == OBJECTTYPE_DIAGRAM
+ || eObjectType == OBJECTTYPE_DIAGRAM_WALL
+ || eObjectType == OBJECTTYPE_DIAGRAM_FLOOR
+ )
+ {
+ // Diagram
+ rtl::Reference< ::chart::Diagram > xDia( ObjectIdentifier::getDiagramForCID( aCID, m_xChartModel ) );
+ if( xDia.is())
+ {
+ fillRangesForDiagram( xDia );
+ return;
+ }
+ }
+ }
+ }
+ else if ( rType == cppu::UnoType< drawing::XShape >::get() )
+ {
+ // #i12587# support for shapes in chart
+ Reference< drawing::XShape > xShape;
+ aSelection >>= xShape;
+ if ( xShape.is() )
+ {
+ return;
+ }
+ }
+ else
+ {
+ //if nothing is selected select all ranges
+ fillRangesForDiagram( m_xChartModel->getFirstChartDiagram() );
+ return;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void RangeHighlighter::fillRangesForDiagram( const rtl::Reference< Diagram > & xDiagram )
+{
+ Sequence< OUString > aSelectedRanges( DataSourceHelper::getUsedDataRanges( xDiagram ));
+ m_aSelectedRanges.realloc( aSelectedRanges.getLength());
+ auto pSelectedRanges = m_aSelectedRanges.getArray();
+ // @todo: merge ranges
+ for( sal_Int32 i=0; i<aSelectedRanges.getLength(); ++i )
+ {
+ pSelectedRanges[i].RangeRepresentation = aSelectedRanges[i];
+ pSelectedRanges[i].Index = -1;
+ pSelectedRanges[i].PreferredColor = sal_Int32(defaultPreferredColor);
+ pSelectedRanges[i].AllowMerginigWithOtherRanges = true;
+ }
+}
+
+void RangeHighlighter::fillRangesForDataSeries( const uno::Reference< chart2::XDataSeries > & xSeries )
+{
+ Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
+ if( xSource.is())
+ {
+ lcl_fillRanges( m_aSelectedRanges,
+ ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
+ defaultPreferredColor );
+ }
+}
+
+void RangeHighlighter::fillRangesForErrorBars(
+ const uno::Reference< beans::XPropertySet > & xErrorBar,
+ const uno::Reference< chart2::XDataSeries > & xSeries )
+{
+ // only show error bar ranges, if the style is set to FROM_DATA
+ bool bUsesRangesAsErrorBars = false;
+ try
+ {
+ sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
+ bUsesRangesAsErrorBars =
+ ( xErrorBar.is() &&
+ (xErrorBar->getPropertyValue( "ErrorBarStyle") >>= nStyle) &&
+ nStyle == css::chart::ErrorBarStyle::FROM_DATA );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ if( bUsesRangesAsErrorBars )
+ {
+ Reference< chart2::data::XDataSource > xSource( xErrorBar, uno::UNO_QUERY );
+ if( xSource.is())
+ {
+ lcl_fillRanges( m_aSelectedRanges,
+ ::chart::DataSourceHelper::getRangesFromDataSource( xSource ),
+ defaultPreferredColor );
+ }
+ }
+ else
+ {
+ fillRangesForDataSeries( xSeries );
+ }
+}
+
+void RangeHighlighter::fillRangesForCategories( const Reference< chart2::XAxis > & xAxis )
+{
+ if( ! xAxis.is())
+ return;
+ chart2::ScaleData aData( xAxis->getScaleData());
+ lcl_fillRanges( m_aSelectedRanges,
+ DataSourceHelper::getRangesFromLabeledDataSequence( aData.Categories ),
+ defaultPreferredColor );
+}
+
+void RangeHighlighter::fillRangesForDataPoint( const rtl::Reference< DataSeries > & xDataSeries, sal_Int32 nIndex )
+{
+ if( !xDataSeries.is())
+ return;
+
+ Color nPreferredColor = defaultPreferredColor;
+ std::vector< chart2::data::HighlightedRange > aHilightedRanges;
+ const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aLSeqSeq( xDataSeries->getDataSequences2());
+ for( uno::Reference< chart2::data::XLabeledDataSequence > const & labelDataSeq : aLSeqSeq )
+ {
+ Reference< chart2::data::XDataSequence > xLabel( labelDataSeq->getLabel());
+ Reference< chart2::data::XDataSequence > xValues( labelDataSeq->getValues());
+
+ if( xLabel.is())
+ aHilightedRanges.emplace_back(
+ xLabel->getSourceRangeRepresentation(),
+ -1,
+ sal_Int32(nPreferredColor),
+ false );
+
+ sal_Int32 nUnhiddenIndex = DataSeriesHelper::translateIndexFromHiddenToFullSequence( nIndex, xValues, !m_bIncludeHiddenCells );
+ if( xValues.is())
+ aHilightedRanges.emplace_back(
+ xValues->getSourceRangeRepresentation(),
+ nUnhiddenIndex,
+ sal_Int32(nPreferredColor),
+ false );
+ }
+ m_aSelectedRanges = comphelper::containerToSequence( aHilightedRanges );
+}
+
+void SAL_CALL RangeHighlighter::addSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
+{
+ if(!xListener.is())
+ return;
+
+ if( m_nAddedListenerCount == 0 )
+ startListening();
+ std::unique_lock g(m_aMutex);
+ maSelectionChangeListeners.addInterface( g, xListener);
+ ++m_nAddedListenerCount;
+
+ //bring the new listener up to the current state
+ lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
+ xListener->selectionChanged( aEvent );
+}
+
+void SAL_CALL RangeHighlighter::removeSelectionChangeListener( const Reference< view::XSelectionChangeListener >& xListener )
+{
+ std::unique_lock g(m_aMutex);
+ maSelectionChangeListeners.removeInterface( g, xListener );
+ --m_nAddedListenerCount;
+ if( m_nAddedListenerCount == 0 )
+ stopListening();
+}
+
+// ____ XSelectionChangeListener ____
+void SAL_CALL RangeHighlighter::selectionChanged( const lang::EventObject& /*aEvent*/ )
+{
+ determineRanges();
+
+ // determine ranges of selected view objects
+ // if changed, fire an event
+ fireSelectionEvent();
+}
+
+void RangeHighlighter::fireSelectionEvent()
+{
+ std::unique_lock g(m_aMutex);
+ if( maSelectionChangeListeners.getLength(g) )
+ {
+ lang::EventObject aEvent( static_cast< lang::XComponent* >( this ) );
+ maSelectionChangeListeners.forEach(g,
+ [&aEvent](const css::uno::Reference<view::XSelectionChangeListener>& xListener)
+ {
+ xListener->selectionChanged(aEvent);
+ }
+ );
+ }
+}
+
+void SAL_CALL RangeHighlighter::disposing( const lang::EventObject& Source )
+{
+ if( Source.Source == m_xSelectionSupplier )
+ {
+ m_xSelectionSupplier.clear();
+ m_aSelectedRanges.realloc( 0 );
+ fireSelectionEvent();
+ }
+}
+
+void RangeHighlighter::startListening()
+{
+ if( m_xSelectionSupplier.is())
+ {
+ if( ! m_xListener.is())
+ {
+ m_xListener.set( new WeakSelectionChangeListenerAdapter( this ));
+ determineRanges();
+ }
+ m_xSelectionSupplier->addSelectionChangeListener( m_xListener );
+ }
+}
+
+void RangeHighlighter::stopListening()
+{
+ if( m_xSelectionSupplier.is() && m_xListener.is())
+ {
+ m_xSelectionSupplier->removeSelectionChangeListener( m_xListener );
+ m_xListener.clear();
+ }
+}
+
+// ____ WeakComponentImplHelperBase ____
+// is called when dispose() is called at this component
+void RangeHighlighter::disposing(std::unique_lock<std::mutex>&)
+{
+ // @todo: remove listener. Currently the controller shows an assertion
+ // because it is already disposed
+// stopListening();
+ m_xListener.clear();
+ m_xSelectionSupplier.clear();
+ m_nAddedListenerCount = 0;
+ m_aSelectedRanges.realloc( 0 );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ReferenceSizeProvider.cxx b/chart2/source/tools/ReferenceSizeProvider.cxx
new file mode 100644
index 000000000..dd099edcf
--- /dev/null
+++ b/chart2/source/tools/ReferenceSizeProvider.cxx
@@ -0,0 +1,333 @@
+/* -*- 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 <ReferenceSizeProvider.hxx>
+#include <RelativeSizeHelper.hxx>
+#include <ChartModelHelper.hxx>
+#include <ChartModel.hxx>
+#include <DataSeries.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+ReferenceSizeProvider::ReferenceSizeProvider(
+ awt::Size aPageSize,
+ const rtl::Reference<::chart::ChartModel> & xChartDoc ) :
+ m_aPageSize( aPageSize ),
+ m_xChartDoc( xChartDoc ),
+ m_bUseAutoScale( getAutoResizeState( xChartDoc ) == AUTO_RESIZE_YES )
+{}
+
+void ReferenceSizeProvider::impl_setValuesAtTitled(
+ const Reference< XTitled > & xTitled )
+{
+ if( xTitled.is())
+ {
+ Reference< XTitle > xTitle( xTitled->getTitleObject());
+ if( xTitle.is())
+ setValuesAtTitle( xTitle );
+ }
+}
+
+void ReferenceSizeProvider::setValuesAtTitle(
+ const Reference< XTitle > & xTitle )
+{
+ try
+ {
+ Reference< beans::XPropertySet > xTitleProp( xTitle, uno::UNO_QUERY_THROW );
+ awt::Size aOldRefSize;
+ bool bHasOldRefSize(
+ xTitleProp->getPropertyValue( "ReferencePageSize") >>= aOldRefSize );
+
+ // set from auto-resize on to off -> adapt font sizes at XFormattedStrings
+ if( bHasOldRefSize && ! useAutoScale())
+ {
+ const uno::Sequence< uno::Reference< XFormattedString > > aStrSeq(
+ xTitle->getText());
+ for( uno::Reference< XFormattedString > const & formattedStr : aStrSeq )
+ {
+ RelativeSizeHelper::adaptFontSizes(
+ Reference< beans::XPropertySet >( formattedStr, uno::UNO_QUERY ),
+ aOldRefSize, getPageSize());
+ }
+ }
+
+ setValuesAtPropertySet( xTitleProp, /* bAdaptFontSizes = */ false );
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void ReferenceSizeProvider::setValuesAtAllDataSeries()
+{
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDoc ));
+
+ // DataSeries/Points
+ std::vector< rtl::Reference< DataSeries > > aSeries =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+
+ for (auto const& elem : aSeries)
+ {
+ // data points
+ Sequence< sal_Int32 > aPointIndexes;
+ try
+ {
+ if( elem->getPropertyValue( "AttributedDataPoints") >>= aPointIndexes )
+ {
+ for( sal_Int32 idx : std::as_const(aPointIndexes) )
+ setValuesAtPropertySet(
+ elem->getDataPointByIndex( idx ) );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ //it is important to correct the datapoint properties first as they do reference the series properties
+ setValuesAtPropertySet( elem );
+ }
+}
+
+void ReferenceSizeProvider::setValuesAtPropertySet(
+ const Reference< beans::XPropertySet > & xProp,
+ bool bAdaptFontSizes /* = true */ )
+{
+ if( ! xProp.is())
+ return;
+
+ static constexpr OUStringLiteral aRefSizeName = u"ReferencePageSize";
+
+ try
+ {
+ awt::Size aRefSize( getPageSize() );
+ awt::Size aOldRefSize;
+ bool bHasOldRefSize( xProp->getPropertyValue( aRefSizeName ) >>= aOldRefSize );
+
+ if( useAutoScale())
+ {
+ if( ! bHasOldRefSize )
+ xProp->setPropertyValue( aRefSizeName, uno::Any( aRefSize ));
+ }
+ else
+ {
+ if( bHasOldRefSize )
+ {
+ xProp->setPropertyValue( aRefSizeName, uno::Any());
+
+ // adapt font sizes
+ if( bAdaptFontSizes )
+ RelativeSizeHelper::adaptFontSizes( xProp, aOldRefSize, aRefSize );
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void ReferenceSizeProvider::getAutoResizeFromPropSet(
+ const Reference< beans::XPropertySet > & xProp,
+ ReferenceSizeProvider::AutoResizeState & rInOutState )
+{
+ AutoResizeState eSingleState = AUTO_RESIZE_UNKNOWN;
+
+ if( xProp.is())
+ {
+ try
+ {
+ if( xProp->getPropertyValue( "ReferencePageSize" ).hasValue())
+ eSingleState = AUTO_RESIZE_YES;
+ else
+ eSingleState = AUTO_RESIZE_NO;
+ }
+ catch (const uno::Exception&)
+ {
+ // unknown property -> state stays unknown
+ }
+ }
+
+ // current state unknown => nothing changes. Otherwise if current state
+ // differs from state so far, we have an ambiguity
+ if( rInOutState == AUTO_RESIZE_UNKNOWN )
+ {
+ rInOutState = eSingleState;
+ }
+ else if( eSingleState != AUTO_RESIZE_UNKNOWN &&
+ eSingleState != rInOutState )
+ {
+ rInOutState = AUTO_RESIZE_AMBIGUOUS;
+ }
+}
+
+void ReferenceSizeProvider::impl_getAutoResizeFromTitled(
+ const Reference< XTitled > & xTitled,
+ ReferenceSizeProvider::AutoResizeState & rInOutState )
+{
+ if( xTitled.is())
+ {
+ Reference< beans::XPropertySet > xProp( xTitled->getTitleObject(), uno::UNO_QUERY );
+ if( xProp.is())
+ getAutoResizeFromPropSet( xProp, rInOutState );
+ }
+}
+
+/** Retrieves the state auto-resize from all objects that support this
+ feature. If all objects return the same state, AUTO_RESIZE_YES or
+ AUTO_RESIZE_NO is returned.
+
+ If no object supporting the feature is found, AUTO_RESIZE_UNKNOWN is
+ returned. If there are multiple objects, some with state YES and some
+ with state NO, AUTO_RESIZE_AMBIGUOUS is returned.
+*/
+ReferenceSizeProvider::AutoResizeState ReferenceSizeProvider::getAutoResizeState(
+ const rtl::Reference<::chart::ChartModel> & xChartDoc )
+{
+ AutoResizeState eResult = AUTO_RESIZE_UNKNOWN;
+
+ // Main Title
+ if( xChartDoc.is())
+ impl_getAutoResizeFromTitled( xChartDoc, eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+
+ // diagram is needed by the rest of the objects
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( xChartDoc );
+ if( ! xDiagram.is())
+ return eResult;
+
+ // Sub Title
+ if( xDiagram.is())
+ impl_getAutoResizeFromTitled( xDiagram, eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+
+ // Legend
+ Reference< beans::XPropertySet > xLegendProp( xDiagram->getLegend(), uno::UNO_QUERY );
+ if( xLegendProp.is())
+ getAutoResizeFromPropSet( xLegendProp, eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+
+ // Axes (incl. Axis Titles)
+ const std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
+ for( rtl::Reference< Axis > const & axis : aAxes )
+ {
+ getAutoResizeFromPropSet( axis, eResult );
+ impl_getAutoResizeFromTitled( axis, eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+ }
+
+ // DataSeries/Points
+ std::vector< rtl::Reference< DataSeries > > aSeries =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+
+ for (auto const& elem : aSeries)
+ {
+ getAutoResizeFromPropSet( elem, eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+
+ // data points
+ Sequence< sal_Int32 > aPointIndexes;
+ try
+ {
+ if( elem->getPropertyValue( "AttributedDataPoints") >>= aPointIndexes )
+ {
+ for( sal_Int32 idx : std::as_const(aPointIndexes) )
+ {
+ getAutoResizeFromPropSet(
+ elem->getDataPointByIndex( idx ), eResult );
+ if( eResult == AUTO_RESIZE_AMBIGUOUS )
+ return eResult;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return eResult;
+}
+
+void ReferenceSizeProvider::toggleAutoResizeState()
+{
+ setAutoResizeState( m_bUseAutoScale ? AUTO_RESIZE_NO : AUTO_RESIZE_YES );
+}
+
+/** sets the auto-resize at all objects that support this feature for text.
+ eNewState must be either AUTO_RESIZE_YES or AUTO_RESIZE_NO
+*/
+void ReferenceSizeProvider::setAutoResizeState( ReferenceSizeProvider::AutoResizeState eNewState )
+{
+ m_bUseAutoScale = (eNewState == AUTO_RESIZE_YES);
+
+ // Main Title
+ impl_setValuesAtTitled( m_xChartDoc );
+
+ // diagram is needed by the rest of the objects
+ rtl::Reference< Diagram > xDiagram = ChartModelHelper::findDiagram( m_xChartDoc );
+ if( ! xDiagram.is())
+ return;
+
+ // Sub Title
+ impl_setValuesAtTitled( xDiagram );
+
+ // Legend
+ Reference< beans::XPropertySet > xLegendProp( xDiagram->getLegend(), uno::UNO_QUERY );
+ if( xLegendProp.is())
+ setValuesAtPropertySet( xLegendProp );
+
+ // Axes (incl. Axis Titles)
+ const std::vector< rtl::Reference< Axis > > aAxes = AxisHelper::getAllAxesOfDiagram( xDiagram );
+ for( rtl::Reference< Axis > const & axis : aAxes )
+ {
+ setValuesAtPropertySet( axis );
+ impl_setValuesAtTitled( axis );
+ }
+
+ // DataSeries/Points
+ setValuesAtAllDataSeries();
+
+ // recalculate new state (in case it stays unknown or is ambiguous
+ m_bUseAutoScale = (getAutoResizeState( m_xChartDoc ) == AUTO_RESIZE_YES);
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RegressionCurveCalculator.cxx b/chart2/source/tools/RegressionCurveCalculator.cxx
new file mode 100644
index 000000000..fd2ca4329
--- /dev/null
+++ b/chart2/source/tools/RegressionCurveCalculator.cxx
@@ -0,0 +1,217 @@
+/* -*- 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 <RegressionCurveCalculator.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <rtl/math.hxx>
+
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+
+#include <comphelper/numbers.hxx>
+#include <comphelper/extract.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+namespace chart
+{
+
+RegressionCurveCalculator::RegressionCurveCalculator()
+ : m_fCorrelationCoefficient(std::numeric_limits<double>::quiet_NaN())
+ , mDegree(2)
+ , mForceIntercept(false)
+ , mInterceptValue(std::numeric_limits<double>::quiet_NaN())
+ , mPeriod(2)
+ , mXName("x")
+ , mYName("f(x)")
+ , mnMovingType(0)
+{
+}
+
+RegressionCurveCalculator::~RegressionCurveCalculator()
+{}
+
+bool RegressionCurveCalculator::isLinearScaling(
+ const Reference< chart2::XScaling > & xScaling )
+{
+ // no scaling means linear
+ if( !xScaling.is())
+ return true;
+ uno::Reference< lang::XServiceName > xServiceName( xScaling, uno::UNO_QUERY );
+ return xServiceName.is() && xServiceName->getServiceName() == "com.sun.star.chart2.LinearScaling";
+}
+
+bool RegressionCurveCalculator::isLogarithmicScaling(
+ const Reference< chart2::XScaling > & xScaling )
+{
+ uno::Reference< lang::XServiceName > xServiceName( xScaling, uno::UNO_QUERY );
+ return xServiceName.is() && xServiceName->getServiceName() == "com.sun.star.chart2.LogarithmicScaling";
+}
+
+void RegressionCurveCalculator::setRegressionProperties(
+ sal_Int32 aDegree,
+ sal_Bool aForceIntercept,
+ double aInterceptValue,
+ sal_Int32 aPeriod,
+ sal_Int32 nMovingType )
+{
+ mDegree = aDegree;
+ mForceIntercept = aForceIntercept;
+ mInterceptValue = aInterceptValue;
+ mPeriod = aPeriod;
+ mnMovingType = nMovingType;
+}
+
+OUString RegressionCurveCalculator::getFormattedString(
+ const Reference< util::XNumberFormatter >& xNumFormatter,
+ sal_Int32 nNumberFormatKey,
+ double fNumber, const sal_Int32* pStringLength /* = nullptr */ )
+{
+ if ( pStringLength && *pStringLength <= 0 )
+ return "###";
+ OUString aResult;
+
+ if( xNumFormatter.is() )
+ {
+ bool bStandard = ::cppu::any2bool( ::comphelper::getNumberFormatProperty( xNumFormatter, nNumberFormatKey, "StandardFormat" ) );
+ if( pStringLength && bStandard )
+ { // round fNumber to *pStringLength characters
+ const sal_Int32 nMinDigit = 6; // minimum significant digits for General format
+ sal_Int32 nSignificantDigit = ( *pStringLength <= nMinDigit ? nMinDigit : *pStringLength );
+ aResult = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_G1, nSignificantDigit, '.', true );
+ // count characters different from significant digits (decimal separator, scientific notation)
+ sal_Int32 nExtraChar = aResult.getLength() - *pStringLength;
+ if ( nExtraChar > 0 && *pStringLength > nMinDigit )
+ {
+ nSignificantDigit = *pStringLength - nExtraChar;
+ if ( nSignificantDigit < nMinDigit )
+ nSignificantDigit = nMinDigit;
+ aResult = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_G1, nSignificantDigit, '.', true );
+ }
+ fNumber = ::rtl::math::stringToDouble( aResult, '.', ',' );
+ }
+ aResult = xNumFormatter->convertNumberToString( nNumberFormatKey, fNumber );
+ }
+ else
+ {
+ sal_Int32 nStringLength = 4; // default length
+ if ( pStringLength )
+ nStringLength = *pStringLength;
+ aResult = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_G1, nStringLength, '.', true );
+ }
+ return aResult;
+}
+
+Sequence< geometry::RealPoint2D > SAL_CALL RegressionCurveCalculator::getCurveValues(
+ double min, double max, ::sal_Int32 nPointCount,
+ const Reference< chart2::XScaling >& xScalingX,
+ const Reference< chart2::XScaling >& /* xScalingY */,
+ sal_Bool /* bMaySkipPointsInCalculation */ )
+{
+ if( nPointCount < 2 )
+ throw lang::IllegalArgumentException("too few points", static_cast<cppu::OWeakObject*>(this), 2);
+
+ // determine if scaling and inverse scaling for x-values work
+ bool bDoXScaling( xScalingX.is());
+ uno::Reference< chart2::XScaling > xInverseScaling;
+ if( bDoXScaling )
+ xInverseScaling.set( xScalingX->getInverseScaling());
+ bDoXScaling = bDoXScaling && xInverseScaling.is();
+
+ Sequence< geometry::RealPoint2D > aResult( nPointCount );
+ auto pResult = aResult.getArray();
+
+ double fMin( min );
+ double fFact = (max - min) / double(nPointCount-1);
+
+ if( bDoXScaling )
+ {
+ fMin = xScalingX->doScaling( min );
+ fFact = (xScalingX->doScaling( max ) - fMin) / double(nPointCount-1);
+ }
+
+ for(sal_Int32 nP=0; nP<nPointCount; nP++)
+ {
+ double x = fMin + nP * fFact;
+ if( bDoXScaling )
+ x = xInverseScaling->doScaling( x );
+ pResult[nP].X = x;
+ pResult[nP].Y = getCurveValue( x );
+ }
+
+ return aResult;
+}
+
+double SAL_CALL RegressionCurveCalculator::getCorrelationCoefficient()
+{
+ return m_fCorrelationCoefficient;
+}
+
+OUString SAL_CALL RegressionCurveCalculator::getRepresentation()
+{
+ return ImplGetRepresentation( Reference< util::XNumberFormatter >(), 0 );
+}
+
+OUString SAL_CALL RegressionCurveCalculator::getFormattedRepresentation(
+ const Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier,
+ sal_Int32 nNumberFormatKey, sal_Int32 nFormulaLength )
+{
+ // create and prepare a number formatter
+ if( !xNumFmtSupplier.is())
+ return getRepresentation();
+ Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext(), uno::UNO_SET_THROW );
+ Reference< util::XNumberFormatter > xNumFormatter( util::NumberFormatter::create(xContext), uno::UNO_QUERY_THROW );
+ xNumFormatter->attachNumberFormatsSupplier( xNumFmtSupplier );
+
+ if ( nFormulaLength > 0 )
+ return ImplGetRepresentation( xNumFormatter, nNumberFormatKey, &nFormulaLength );
+ return ImplGetRepresentation( xNumFormatter, nNumberFormatKey );
+}
+
+void RegressionCurveCalculator::addStringToEquation(
+ OUStringBuffer& aStrEquation, sal_Int32& nLineLength, OUStringBuffer const & aAddString, const sal_Int32* pMaxWidth)
+{
+ if ( pMaxWidth && ( nLineLength + aAddString.getLength() > *pMaxWidth ) )
+ { // wrap line
+ aStrEquation.append( "\n " ); // start new line with a blank
+ nLineLength = 1;
+ }
+ aStrEquation.append( aAddString );
+ nLineLength += aAddString.getLength();
+}
+
+void SAL_CALL RegressionCurveCalculator::setXYNames( const OUString& aXName, const OUString& aYName )
+{
+ if ( aXName.isEmpty() )
+ mXName = OUString ("x");
+ else
+ mXName = aXName;
+ if ( aYName.isEmpty() )
+ mYName = OUString ("f(x)");
+ else
+ mYName = aYName;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RegressionCurveHelper.cxx b/chart2/source/tools/RegressionCurveHelper.cxx
new file mode 100644
index 000000000..60ac5ebd3
--- /dev/null
+++ b/chart2/source/tools/RegressionCurveHelper.cxx
@@ -0,0 +1,920 @@
+/* -*- 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 <cstddef>
+
+#include <RegressionCurveHelper.hxx>
+#include <MeanValueRegressionCurveCalculator.hxx>
+#include <LinearRegressionCurveCalculator.hxx>
+#include <PolynomialRegressionCurveCalculator.hxx>
+#include <MovingAverageRegressionCurveCalculator.hxx>
+#include <LogarithmicRegressionCurveCalculator.hxx>
+#include <ExponentialRegressionCurveCalculator.hxx>
+#include <PotentialRegressionCurveCalculator.hxx>
+#include <CommonConverters.hxx>
+#include <RegressionCurveModel.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ChartType.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <DataSeries.hxx>
+#include <ResId.hxx>
+#include <strings.hrc>
+#include <DiagramHelper.hxx>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/XRegressionCurveCalculator.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XDataSeries.hpp>
+#include <com/sun/star/chart2/data/XDataSource.hpp>
+#include <o3tl/safeint.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/property.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::lang::XServiceName;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Exception;
+
+namespace
+{
+OUString lcl_getServiceNameForType(SvxChartRegress eType)
+{
+ OUString aServiceName;
+ switch( eType )
+ {
+ case SvxChartRegress::Linear:
+ aServiceName = "com.sun.star.chart2.LinearRegressionCurve";
+ break;
+ case SvxChartRegress::Log:
+ aServiceName = "com.sun.star.chart2.LogarithmicRegressionCurve";
+ break;
+ case SvxChartRegress::Exp:
+ aServiceName = "com.sun.star.chart2.ExponentialRegressionCurve";
+ break;
+ case SvxChartRegress::Power:
+ aServiceName = "com.sun.star.chart2.PotentialRegressionCurve";
+ break;
+ case SvxChartRegress::Polynomial:
+ aServiceName = "com.sun.star.chart2.PolynomialRegressionCurve";
+ break;
+ case SvxChartRegress::MovingAverage:
+ aServiceName = "com.sun.star.chart2.MovingAverageRegressionCurve";
+ break;
+ default:
+ OSL_FAIL("unknown regression curve type - use linear instead");
+ aServiceName = "com.sun.star.chart2.LinearRegressionCurve";
+ break;
+ }
+ return aServiceName;
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::createMeanValueLine()
+{
+ return new MeanValueRegressionCurve;
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::createRegressionCurveByServiceName(
+ std::u16string_view aServiceName )
+{
+ rtl::Reference< RegressionCurveModel > xResult;
+
+ // todo: use factory methods with service name
+ if( aServiceName == u"com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ xResult.set( new LinearRegressionCurve );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ xResult.set( new LogarithmicRegressionCurve );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ xResult.set( new ExponentialRegressionCurve );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ xResult.set( new PotentialRegressionCurve );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ xResult.set( new PolynomialRegressionCurve );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ xResult.set( new MovingAverageRegressionCurve );
+ }
+
+ return xResult;
+}
+
+Reference< XRegressionCurveCalculator > RegressionCurveHelper::createRegressionCurveCalculatorByServiceName(
+ std::u16string_view aServiceName )
+{
+ Reference< XRegressionCurveCalculator > xResult;
+
+ // todo: use factory methods with service name
+ if( aServiceName == u"com.sun.star.chart2.MeanValueRegressionCurve" )
+ {
+ xResult.set( new MeanValueRegressionCurveCalculator() );
+ }
+ if( aServiceName == u"com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ xResult.set( new LinearRegressionCurveCalculator() );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ xResult.set( new LogarithmicRegressionCurveCalculator() );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ xResult.set( new ExponentialRegressionCurveCalculator() );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ xResult.set( new PotentialRegressionCurveCalculator() );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ xResult.set( new PolynomialRegressionCurveCalculator() );
+ }
+ else if( aServiceName == u"com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ xResult.set( new MovingAverageRegressionCurveCalculator() );
+ }
+
+ return xResult;
+}
+
+void RegressionCurveHelper::initializeCurveCalculator(
+ const Reference< XRegressionCurveCalculator > & xOutCurveCalculator,
+ const Reference< data::XDataSource > & xSource,
+ bool bUseXValuesIfAvailable /* = true */ )
+{
+ if( ! (xOutCurveCalculator.is() &&
+ xSource.is() ))
+ return;
+
+ Sequence< double > aXValues, aYValues;
+ bool bXValuesFound = false, bYValuesFound = false;
+
+ Sequence< Reference< data::XLabeledDataSequence > > aDataSeqs( xSource->getDataSequences());
+ sal_Int32 i = 0;
+ for( i=0;
+ ! (bXValuesFound && bYValuesFound) && i<aDataSeqs.getLength();
+ ++i )
+ {
+ try
+ {
+ Reference< data::XDataSequence > xSeq( aDataSeqs[i]->getValues());
+ Reference< XPropertySet > xProp( xSeq, uno::UNO_QUERY_THROW );
+ OUString aRole;
+ if( xProp->getPropertyValue( "Role" ) >>= aRole )
+ {
+ if( bUseXValuesIfAvailable && !bXValuesFound && aRole == "values-x" )
+ {
+ aXValues = DataSequenceToDoubleSequence( xSeq );
+ bXValuesFound = true;
+ }
+ else if( !bYValuesFound && aRole == "values-y" )
+ {
+ aYValues = DataSequenceToDoubleSequence( xSeq );
+ bYValuesFound = true;
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ if( ! bXValuesFound &&
+ bYValuesFound )
+ {
+ // initialize with 1, 2, ...
+ //first category (index 0) matches with real number 1.0
+ aXValues.realloc( aYValues.getLength());
+ auto pXValues = aXValues.getArray();
+ for( i=0; i<aXValues.getLength(); ++i )
+ pXValues[i] = i+1;
+ bXValuesFound = true;
+ }
+
+ if( bXValuesFound && bYValuesFound &&
+ aXValues.hasElements() &&
+ aYValues.hasElements() )
+ xOutCurveCalculator->recalculateRegression( aXValues, aYValues );
+}
+
+void RegressionCurveHelper::initializeCurveCalculator(
+ const Reference< XRegressionCurveCalculator > & xOutCurveCalculator,
+ const Reference< XDataSeries > & xSeries,
+ const rtl::Reference<::chart::ChartModel> & xModel )
+{
+ sal_Int32 nAxisType = ChartTypeHelper::getAxisType(
+ ChartModelHelper::getChartTypeOfSeries( xModel, xSeries ), 0 ); // x-axis
+
+ initializeCurveCalculator( xOutCurveCalculator,
+ uno::Reference< data::XDataSource >( xSeries, uno::UNO_QUERY ),
+ (nAxisType == AxisType::REALNUMBER) );
+}
+
+bool RegressionCurveHelper::hasMeanValueLine(
+ const uno::Reference< XRegressionCurveContainer > & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return false;
+
+ try
+ {
+ const uno::Sequence< uno::Reference< XRegressionCurve > > aCurves(
+ xRegCnt->getRegressionCurves());
+ for( uno::Reference< XRegressionCurve > const & curve : aCurves )
+ {
+ if( isMeanValueLine( curve ))
+ return true;
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return false;
+}
+
+bool RegressionCurveHelper::hasMeanValueLine(
+ const rtl::Reference< DataSeries > & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return false;
+
+ try
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( isMeanValueLine( curve ))
+ return true;
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return false;
+}
+
+bool RegressionCurveHelper::isMeanValueLine(
+ const uno::Reference< chart2::XRegressionCurve > & xRegCurve )
+{
+ uno::Reference< XServiceName > xServName( xRegCurve, uno::UNO_QUERY );
+ return xServName.is() &&
+ xServName->getServiceName() ==
+ "com.sun.star.chart2.MeanValueRegressionCurve";
+}
+
+bool RegressionCurveHelper::isMeanValueLine(
+ const rtl::Reference< RegressionCurveModel > & xRegCurve )
+{
+ return xRegCurve.is() &&
+ xRegCurve->getServiceName() ==
+ "com.sun.star.chart2.MeanValueRegressionCurve";
+}
+
+rtl::Reference< RegressionCurveModel >
+ RegressionCurveHelper::getMeanValueLine(
+ const uno::Reference< chart2::XRegressionCurveContainer > & xRegCnt )
+{
+ if( xRegCnt.is())
+ {
+ try
+ {
+ const uno::Sequence< uno::Reference< XRegressionCurve > > aCurves(
+ xRegCnt->getRegressionCurves());
+ for( uno::Reference< XRegressionCurve > const & curve : aCurves )
+ {
+ if( isMeanValueLine( curve ))
+ return dynamic_cast<RegressionCurveModel*>(curve.get());
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return nullptr;
+}
+
+rtl::Reference< RegressionCurveModel >
+ RegressionCurveHelper::getMeanValueLine(
+ const rtl::Reference< DataSeries > & xRegCnt )
+{
+ if( xRegCnt.is())
+ {
+ try
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( isMeanValueLine( curve ))
+ return curve;
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return nullptr;
+}
+
+void RegressionCurveHelper::addMeanValueLine(
+ uno::Reference< XRegressionCurveContainer > const & xRegCnt,
+ const uno::Reference< XPropertySet > & xSeriesProp )
+{
+ if( !xRegCnt.is() ||
+ ::chart::RegressionCurveHelper::hasMeanValueLine( xRegCnt ) )
+ return;
+
+ // todo: use a valid context
+ uno::Reference< XRegressionCurve > xCurve( createMeanValueLine() );
+ xRegCnt->addRegressionCurve( xCurve );
+
+ if( xSeriesProp.is())
+ {
+ uno::Reference< XPropertySet > xProp( xCurve, uno::UNO_QUERY );
+ if( xProp.is())
+ {
+ xProp->setPropertyValue( "LineColor",
+ xSeriesProp->getPropertyValue( "Color"));
+ }
+ }
+}
+
+void RegressionCurveHelper::addMeanValueLine(
+ rtl::Reference< DataSeries > const & xRegCnt,
+ const uno::Reference< XPropertySet > & xSeriesProp )
+{
+ if( !xRegCnt.is() ||
+ ::chart::RegressionCurveHelper::hasMeanValueLine( xRegCnt ) )
+ return;
+
+ // todo: use a valid context
+ rtl::Reference< RegressionCurveModel > xCurve( createMeanValueLine() );
+ xRegCnt->addRegressionCurve( xCurve );
+
+ if( xSeriesProp.is())
+ {
+ xCurve->setPropertyValue( "LineColor",
+ xSeriesProp->getPropertyValue( "Color"));
+ }
+}
+
+void RegressionCurveHelper::removeMeanValueLine(
+ Reference< XRegressionCurveContainer > const & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return;
+
+ try
+ {
+ const Sequence< Reference< XRegressionCurve > > aCurves(
+ xRegCnt->getRegressionCurves());
+ for( Reference< XRegressionCurve > const & curve : aCurves )
+ {
+ if( isMeanValueLine( curve ))
+ {
+ xRegCnt->removeRegressionCurve( curve );
+ // attention: the iterator i has become invalid now
+
+ // note: assume that there is only one mean-value curve
+ // to remove multiple mean-value curves remove the break
+ break;
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void RegressionCurveHelper::removeMeanValueLine(
+ rtl::Reference< DataSeries > const & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return;
+
+ try
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( isMeanValueLine( curve ))
+ {
+ xRegCnt->removeRegressionCurve( curve );
+ // attention: the iterator i has become invalid now
+
+ // note: assume that there is only one mean-value curve
+ // to remove multiple mean-value curves remove the break
+ break;
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::addRegressionCurve(
+ SvxChartRegress eType,
+ uno::Reference< XRegressionCurveContainer > const & xRegressionCurveContainer,
+ const uno::Reference< beans::XPropertySet >& xPropertySource,
+ const uno::Reference< beans::XPropertySet >& xEquationProperties )
+{
+ rtl::Reference< RegressionCurveModel > xCurve;
+
+ if( !xRegressionCurveContainer.is() )
+ return xCurve;
+
+ if( eType == SvxChartRegress::NONE )
+ {
+ OSL_FAIL("don't create a regression curve of type none");
+ return xCurve;
+ }
+
+ OUString aServiceName( lcl_getServiceNameForType( eType ));
+ if( !aServiceName.isEmpty())
+ {
+ // todo: use a valid context
+ xCurve = createRegressionCurveByServiceName( aServiceName );
+
+ if( xEquationProperties.is())
+ xCurve->setEquationProperties( xEquationProperties );
+
+ if( xPropertySource.is())
+ comphelper::copyProperties( xPropertySource, xCurve );
+ else
+ {
+ uno::Reference< XPropertySet > xSeriesProp( xRegressionCurveContainer, uno::UNO_QUERY );
+ if( xSeriesProp.is())
+ {
+ xCurve->setPropertyValue( "LineColor",
+ xSeriesProp->getPropertyValue( "Color"));
+ }
+ }
+ }
+ xRegressionCurveContainer->addRegressionCurve( xCurve );
+
+ return xCurve;
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::addRegressionCurve(
+ SvxChartRegress eType,
+ rtl::Reference< DataSeries > const & xRegressionCurveContainer,
+ const uno::Reference< beans::XPropertySet >& xPropertySource,
+ const uno::Reference< beans::XPropertySet >& xEquationProperties )
+{
+ rtl::Reference< RegressionCurveModel > xCurve;
+
+ if( !xRegressionCurveContainer.is() )
+ return xCurve;
+
+ if( eType == SvxChartRegress::NONE )
+ {
+ OSL_FAIL("don't create a regression curve of type none");
+ return xCurve;
+ }
+
+ OUString aServiceName( lcl_getServiceNameForType( eType ));
+ if( !aServiceName.isEmpty())
+ {
+ // todo: use a valid context
+ xCurve = createRegressionCurveByServiceName( aServiceName );
+
+ if( xEquationProperties.is())
+ xCurve->setEquationProperties( xEquationProperties );
+
+ if( xPropertySource.is())
+ comphelper::copyProperties( xPropertySource, xCurve );
+ else
+ {
+ xCurve->setPropertyValue( "LineColor",
+ xRegressionCurveContainer->getPropertyValue( "Color"));
+ }
+ }
+ xRegressionCurveContainer->addRegressionCurve( xCurve );
+
+ return xCurve;
+}
+
+/** removes all regression curves that are not of type mean value
+ and returns true, if anything was removed
+ */
+bool RegressionCurveHelper::removeAllExceptMeanValueLine(
+ rtl::Reference< DataSeries > const & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return false;
+
+ bool bRemovedSomething = false;
+ try
+ {
+ std::vector< rtl::Reference< RegressionCurveModel > > aCurvesToDelete;
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( ! isMeanValueLine( curve ))
+ {
+ aCurvesToDelete.push_back( curve );
+ }
+ }
+
+ for (auto const& curveToDelete : aCurvesToDelete)
+ {
+ xRegCnt->removeRegressionCurve(curveToDelete);
+ bRemovedSomething = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return bRemovedSomething;
+}
+
+void RegressionCurveHelper::removeEquations(
+ rtl::Reference< DataSeries > const & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return;
+
+ try
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( !isMeanValueLine( curve ) )
+ {
+ uno::Reference< beans::XPropertySet > xEqProp( curve->getEquationProperties() ) ;
+ if( xEqProp.is())
+ {
+ xEqProp->setPropertyValue( "ShowEquation", uno::Any( false ));
+ xEqProp->setPropertyValue( "XName", uno::Any( OUString("x") ));
+ xEqProp->setPropertyValue( "YName", uno::Any( OUString("f(x) ") ));
+ xEqProp->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( false ));
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::changeRegressionCurveType(
+ SvxChartRegress eType,
+ uno::Reference< XRegressionCurveContainer > const & xRegressionCurveContainer,
+ uno::Reference< XRegressionCurve > const & xRegressionCurve )
+{
+ xRegressionCurveContainer->removeRegressionCurve( xRegressionCurve );
+ return RegressionCurveHelper::addRegressionCurve(
+ eType,
+ xRegressionCurveContainer,
+ uno::Reference< beans::XPropertySet >( xRegressionCurve, uno::UNO_QUERY ),
+ xRegressionCurve->getEquationProperties());
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::getFirstCurveNotMeanValueLine(
+ const Reference< XRegressionCurveContainer > & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return nullptr;
+
+ try
+ {
+ const uno::Sequence< uno::Reference< chart2::XRegressionCurve > > aCurves(
+ xRegCnt->getRegressionCurves());
+ for( uno::Reference< chart2::XRegressionCurve > const & curve : aCurves )
+ {
+ if( ! isMeanValueLine( curve ))
+ {
+ return dynamic_cast<RegressionCurveModel*>(curve.get());
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nullptr;
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::getFirstCurveNotMeanValueLine(
+ const rtl::Reference< DataSeries > & xRegCnt )
+{
+ if( !xRegCnt.is())
+ return nullptr;
+
+ try
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : xRegCnt->getRegressionCurves2() )
+ {
+ if( ! isMeanValueLine( curve ))
+ {
+ return curve;
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nullptr;
+}
+
+rtl::Reference< RegressionCurveModel > RegressionCurveHelper::getRegressionCurveAtIndex(
+ const rtl::Reference< DataSeries >& xCurveContainer,
+ sal_Int32 aIndex )
+{
+ if( !xCurveContainer.is())
+ return nullptr;
+
+ try
+ {
+ const std::vector< rtl::Reference< RegressionCurveModel > > aCurves(xCurveContainer->getRegressionCurves2());
+ if(0 <= aIndex && o3tl::make_unsigned(aIndex) < aCurves.size())
+ {
+ if(!isMeanValueLine(aCurves[aIndex]))
+ return aCurves[aIndex];
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nullptr;
+}
+
+SvxChartRegress RegressionCurveHelper::getRegressionType(
+ const Reference< XRegressionCurve > & xCurve )
+{
+ SvxChartRegress eResult = SvxChartRegress::Unknown;
+
+ try
+ {
+ Reference< lang::XServiceName > xServName( xCurve, uno::UNO_QUERY );
+ if( xServName.is())
+ {
+ OUString aServiceName( xServName->getServiceName() );
+
+ if( aServiceName == "com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ eResult = SvxChartRegress::Linear;
+ }
+ else if( aServiceName == "com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ eResult = SvxChartRegress::Log;
+ }
+ else if( aServiceName == "com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ eResult = SvxChartRegress::Exp;
+ }
+ else if( aServiceName == "com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ eResult = SvxChartRegress::Power;
+ }
+ else if( aServiceName == "com.sun.star.chart2.MeanValueRegressionCurve" )
+ {
+ eResult = SvxChartRegress::MeanValue;
+ }
+ else if( aServiceName == "com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ eResult = SvxChartRegress::Polynomial;
+ }
+ else if( aServiceName == "com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ eResult = SvxChartRegress::MovingAverage;
+ }
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+
+ return eResult;
+}
+
+SvxChartRegress RegressionCurveHelper::getFirstRegressTypeNotMeanValueLine(
+ const Reference< XRegressionCurveContainer > & xRegCnt )
+{
+ SvxChartRegress eResult = SvxChartRegress::NONE;
+
+ if( xRegCnt.is())
+ {
+ const Sequence< Reference< XRegressionCurve > > aCurves(
+ xRegCnt->getRegressionCurves());
+ for( Reference< XRegressionCurve > const & curve : aCurves )
+ {
+ SvxChartRegress eType = getRegressionType( curve );
+ if( eType != SvxChartRegress::MeanValue &&
+ eType != SvxChartRegress::Unknown )
+ {
+ eResult = eType;
+ break;
+ }
+ }
+ }
+
+ return eResult;
+}
+
+OUString RegressionCurveHelper::getUINameForRegressionCurve( const Reference< XRegressionCurve >& xRegressionCurve )
+{
+ OUString aResult = getRegressionCurveSpecificName(xRegressionCurve);
+ if (aResult.isEmpty())
+ {
+ aResult = getRegressionCurveGenericName(xRegressionCurve);
+ if (!aResult.isEmpty())
+ {
+ aResult += " (%SERIESNAME)";
+ }
+ }
+ return aResult;
+}
+
+OUString RegressionCurveHelper::getRegressionCurveGenericName(const Reference< XRegressionCurve >& xRegressionCurve)
+{
+ OUString aResult;
+ if(!xRegressionCurve.is())
+ return aResult;
+
+ Reference< lang::XServiceName > xServiceName( xRegressionCurve, uno::UNO_QUERY );
+ if(!xServiceName.is())
+ return aResult;
+
+ OUString aServiceName(xServiceName->getServiceName());
+
+ if( aServiceName == "com.sun.star.chart2.MeanValueRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_MEAN);
+ }
+ else if( aServiceName == "com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_LINEAR);
+ }
+ else if( aServiceName == "com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_LOG);
+ }
+ else if( aServiceName == "com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_EXP);
+ }
+ else if( aServiceName == "com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_POWER);
+ }
+ else if( aServiceName == "com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_POLYNOMIAL);
+ }
+ else if( aServiceName == "com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ aResult = SchResId(STR_REGRESSION_MOVING_AVERAGE);
+ }
+ return aResult;
+}
+
+OUString RegressionCurveHelper::getRegressionCurveSpecificName(const Reference< XRegressionCurve >& xRegressionCurve)
+{
+ OUString aResult;
+
+ if(!xRegressionCurve.is())
+ return aResult;
+
+ Reference<XPropertySet> xProperties( xRegressionCurve, uno::UNO_QUERY );
+ if(!xProperties.is())
+ return aResult;
+
+ xProperties->getPropertyValue("CurveName") >>= aResult;
+
+ return aResult;
+}
+
+OUString RegressionCurveHelper::getRegressionCurveName( const Reference< XRegressionCurve >& xRegressionCurve )
+{
+ OUString aResult = getRegressionCurveSpecificName(xRegressionCurve);
+ if (aResult.isEmpty())
+ return getRegressionCurveGenericName(xRegressionCurve);
+ return aResult;
+}
+
+std::vector< rtl::Reference< RegressionCurveModel > >
+ RegressionCurveHelper::getAllRegressionCurvesNotMeanValueLine(
+ const rtl::Reference< Diagram > & xDiagram )
+{
+ std::vector< rtl::Reference< RegressionCurveModel > > aResult;
+ std::vector< rtl::Reference< DataSeries > > aSeries( DiagramHelper::getDataSeriesFromDiagram( xDiagram ));
+ for (auto const& elem : aSeries)
+ {
+ for( rtl::Reference< RegressionCurveModel > const & curve : elem->getRegressionCurves2() )
+ {
+ if( ! isMeanValueLine( curve ))
+ aResult.push_back( curve );
+ }
+ }
+
+ return aResult;
+}
+
+void RegressionCurveHelper::resetEquationPosition(
+ const Reference< chart2::XRegressionCurve > & xCurve )
+{
+ if( !xCurve.is())
+ return;
+
+ try
+ {
+ static const OUStringLiteral aPosPropertyName( u"RelativePosition" );
+ Reference< beans::XPropertySet > xEqProp( xCurve->getEquationProperties()); // since m233: , uno::UNO_SET_THROW );
+ if( xEqProp->getPropertyValue( aPosPropertyName ).hasValue())
+ xEqProp->setPropertyValue( aPosPropertyName, uno::Any());
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+}
+
+sal_Int32 RegressionCurveHelper::getRegressionCurveIndex(
+ const rtl::Reference< DataSeries >& xContainer,
+ const rtl::Reference< RegressionCurveModel >& xCurve )
+{
+ if( xContainer.is())
+ {
+ const std::vector< rtl::Reference< RegressionCurveModel > > & aCurves(
+ xContainer->getRegressionCurves2());
+
+ for( std::size_t i = 0; i < aCurves.size(); ++i )
+ {
+ if( xCurve == aCurves[i] )
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool RegressionCurveHelper::hasEquation( const Reference< chart2::XRegressionCurve > & xCurve )
+{
+ bool bHasEquation = false;
+ if( xCurve.is())
+ {
+ uno::Reference< beans::XPropertySet > xEquationProp( xCurve->getEquationProperties());
+ if( xEquationProp.is())
+ {
+ bool bShowEquation = false;
+ bool bShowCoefficient = false;
+ xEquationProp->getPropertyValue( "ShowEquation") >>= bShowEquation;
+ xEquationProp->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowCoefficient;
+ bHasEquation = bShowEquation || bShowCoefficient;
+ }
+ }
+ return bHasEquation;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RegressionCurveModel.cxx b/chart2/source/tools/RegressionCurveModel.cxx
new file mode 100644
index 000000000..890913e34
--- /dev/null
+++ b/chart2/source/tools/RegressionCurveModel.cxx
@@ -0,0 +1,541 @@
+/* -*- 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 <RegressionCurveModel.hxx>
+#include <LinePropertiesHelper.hxx>
+#include <RegressionCurveHelper.hxx>
+#include "RegressionEquation.hxx"
+#include <CloneHelper.hxx>
+#include <PropertyHelper.hxx>
+#include <ModifyListenerHelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace
+{
+enum
+{
+ PROPERTY_DEGREE,
+ PROPERTY_PERIOD,
+ PROPERTY_EXTRAPOLATE_FORWARD,
+ PROPERTY_EXTRAPOLATE_BACKWARD,
+ PROPERTY_FORCE_INTERCEPT,
+ PROPERTY_INTERCEPT_VALUE,
+ PROPERTY_CURVE_NAME,
+ PROPERTY_MOVING_AVERAGE_TYPE
+};
+
+void lcl_AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ rOutProperties.emplace_back( "PolynomialDegree",
+ PROPERTY_DEGREE,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "MovingAveragePeriod",
+ PROPERTY_PERIOD,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "MovingAverageType",
+ PROPERTY_MOVING_AVERAGE_TYPE,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ExtrapolateForward",
+ PROPERTY_EXTRAPOLATE_FORWARD,
+ cppu::UnoType<double>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ExtrapolateBackward",
+ PROPERTY_EXTRAPOLATE_BACKWARD,
+ cppu::UnoType<double>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ForceIntercept",
+ PROPERTY_FORCE_INTERCEPT,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "InterceptValue",
+ PROPERTY_INTERCEPT_VALUE,
+ cppu::UnoType<double>::get(),
+ beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "CurveName",
+ PROPERTY_CURVE_NAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+}
+
+::chart::tPropertyValueMap GetStaticXXXDefaults()
+{
+ static ::chart::tPropertyValueMap aStaticDefaults =
+ [](){
+ ::chart::tPropertyValueMap aTmp;
+ ::chart::LinePropertiesHelper::AddDefaultsToMap( aTmp );
+ return aTmp;
+ }();
+ return aStaticDefaults;
+};
+
+::cppu::OPropertyArrayHelper& GetStaticRegressionCurveInfoHelper()
+{
+ static ::cppu::OPropertyArrayHelper aPropHelper =
+ [](){
+ std::vector< css::beans::Property > aProperties;
+ lcl_AddPropertiesToVector( aProperties );
+ ::chart::LinePropertiesHelper::AddPropertiesToVector( aProperties );
+
+ std::sort( aProperties.begin(), aProperties.end(),
+ ::chart::PropertyNameLess() );
+
+ return comphelper::containerToSequence( aProperties );
+ }();
+ return aPropHelper;
+};
+
+uno::Reference< beans::XPropertySetInfo >& GetStaticRegressionCurveInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xPropertySetInfo(
+ ::cppu::OPropertySetHelper::createPropertySetInfo(GetStaticRegressionCurveInfoHelper() ) );
+ return xPropertySetInfo;
+};
+
+} // anonymous namespace
+
+namespace chart
+{
+
+RegressionCurveModel::RegressionCurveModel( tCurveType eCurveType ) :
+ ::property::OPropertySet( m_aMutex ),
+ m_eRegressionCurveType( eCurveType ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() ),
+ m_xEquationProperties( new RegressionEquation )
+{
+ // set 0 line width (default) hard, so that it is always written to XML,
+ // because the old implementation uses different defaults
+ setFastPropertyValue_NoBroadcast(
+ LinePropertiesHelper::PROP_LINE_WIDTH, uno::Any( sal_Int32( 0 )));
+ ModifyListenerHelper::addListener( m_xEquationProperties, m_xModifyEventForwarder );
+}
+
+RegressionCurveModel::RegressionCurveModel( const RegressionCurveModel & rOther ) :
+ impl::RegressionCurveModel_Base(rOther),
+ ::property::OPropertySet( rOther, m_aMutex ),
+ m_eRegressionCurveType( rOther.m_eRegressionCurveType ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ m_xEquationProperties.set( CloneHelper::CreateRefClone< beans::XPropertySet >()( rOther.m_xEquationProperties ));
+ ModifyListenerHelper::addListener( m_xEquationProperties, m_xModifyEventForwarder );
+}
+
+RegressionCurveModel::~RegressionCurveModel()
+{}
+
+// ____ XRegressionCurve ____
+uno::Reference< chart2::XRegressionCurveCalculator > SAL_CALL
+ RegressionCurveModel::getCalculator()
+{
+ return RegressionCurveHelper::createRegressionCurveCalculatorByServiceName( getServiceName());
+}
+
+uno::Reference< beans::XPropertySet > SAL_CALL RegressionCurveModel::getEquationProperties()
+{
+ return m_xEquationProperties;
+}
+
+void SAL_CALL RegressionCurveModel::setEquationProperties( const uno::Reference< beans::XPropertySet >& xEquationProperties )
+{
+ if( xEquationProperties.is())
+ {
+ if( m_xEquationProperties.is())
+ ModifyListenerHelper::removeListener( m_xEquationProperties, m_xModifyEventForwarder );
+
+ m_xEquationProperties.set( xEquationProperties );
+ ModifyListenerHelper::addListener( m_xEquationProperties, m_xModifyEventForwarder );
+ fireModifyEvent();
+ }
+}
+
+// ____ XServiceName ____
+OUString SAL_CALL RegressionCurveModel::getServiceName()
+{
+ switch( m_eRegressionCurveType )
+ {
+ case CURVE_TYPE_MEAN_VALUE:
+ return "com.sun.star.chart2.MeanValueRegressionCurve";
+ case CURVE_TYPE_LINEAR:
+ return "com.sun.star.chart2.LinearRegressionCurve";
+ case CURVE_TYPE_LOGARITHM:
+ return "com.sun.star.chart2.LogarithmicRegressionCurve";
+ case CURVE_TYPE_EXPONENTIAL:
+ return "com.sun.star.chart2.ExponentialRegressionCurve";
+ case CURVE_TYPE_POWER:
+ return "com.sun.star.chart2.PotentialRegressionCurve";
+ case CURVE_TYPE_POLYNOMIAL:
+ return "com.sun.star.chart2.PolynomialRegressionCurve";
+ case CURVE_TYPE_MOVING_AVERAGE:
+ return "com.sun.star.chart2.MovingAverageRegressionCurve";
+ }
+
+ return OUString();
+}
+
+// ____ XModifyBroadcaster ____
+void SAL_CALL RegressionCurveModel::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL RegressionCurveModel::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+// ____ XModifyListener ____
+void SAL_CALL RegressionCurveModel::modified( const lang::EventObject& aEvent )
+{
+ m_xModifyEventForwarder->modified( aEvent );
+}
+
+// ____ XEventListener (base of XModifyListener) ____
+void SAL_CALL RegressionCurveModel::disposing( const lang::EventObject& /* Source */ )
+{
+ // nothing
+}
+
+// ____ OPropertySet ____
+void RegressionCurveModel::firePropertyChangeEvent()
+{
+ fireModifyEvent();
+}
+
+void RegressionCurveModel::fireModifyEvent()
+{
+ m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
+}
+
+// ____ OPropertySet ____
+void RegressionCurveModel::GetDefaultValue( sal_Int32 nHandle, uno::Any& rAny ) const
+{
+ const tPropertyValueMap& rStaticDefaults = GetStaticXXXDefaults();
+ tPropertyValueMap::const_iterator aFound( rStaticDefaults.find( nHandle ) );
+ if( aFound == rStaticDefaults.end() )
+ rAny.clear();
+ else
+ rAny = (*aFound).second;
+}
+
+::cppu::IPropertyArrayHelper & SAL_CALL RegressionCurveModel::getInfoHelper()
+{
+ return GetStaticRegressionCurveInfoHelper();
+}
+
+// ____ XPropertySet ____
+uno::Reference< beans::XPropertySetInfo > SAL_CALL RegressionCurveModel::getPropertySetInfo()
+{
+ return GetStaticRegressionCurveInfo();
+}
+
+// needed by MSC compiler
+using impl::RegressionCurveModel_Base;
+
+IMPLEMENT_FORWARD_XINTERFACE2( RegressionCurveModel, RegressionCurveModel_Base, OPropertySet )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( RegressionCurveModel, RegressionCurveModel_Base, OPropertySet )
+
+// implementations
+
+MeanValueRegressionCurve::MeanValueRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_MEAN_VALUE )
+{}
+MeanValueRegressionCurve::MeanValueRegressionCurve(
+ const MeanValueRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+MeanValueRegressionCurve::~MeanValueRegressionCurve()
+{}
+
+OUString SAL_CALL MeanValueRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.MeanValueRegressionCurve";
+}
+
+sal_Bool SAL_CALL MeanValueRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MeanValueRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.MeanValueRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL MeanValueRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new MeanValueRegressionCurve( *this ));
+}
+
+LinearRegressionCurve::LinearRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_LINEAR )
+{}
+LinearRegressionCurve::LinearRegressionCurve(
+ const LinearRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+LinearRegressionCurve::~LinearRegressionCurve()
+{}
+
+OUString SAL_CALL LinearRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.LinearRegressionCurve";
+}
+
+sal_Bool SAL_CALL LinearRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LinearRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.LinearRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL LinearRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new LinearRegressionCurve( *this ));
+}
+
+LogarithmicRegressionCurve::LogarithmicRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_LOGARITHM )
+{}
+LogarithmicRegressionCurve::LogarithmicRegressionCurve(
+ const LogarithmicRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+LogarithmicRegressionCurve::~LogarithmicRegressionCurve()
+{}
+
+OUString SAL_CALL LogarithmicRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.LogarithmicRegressionCurve";
+}
+
+sal_Bool SAL_CALL LogarithmicRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LogarithmicRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.LogarithmicRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL LogarithmicRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new LogarithmicRegressionCurve( *this ));
+}
+
+ExponentialRegressionCurve::ExponentialRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_EXPONENTIAL )
+{}
+ExponentialRegressionCurve::ExponentialRegressionCurve(
+ const ExponentialRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+ExponentialRegressionCurve::~ExponentialRegressionCurve()
+{}
+
+OUString SAL_CALL ExponentialRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.ExponentialRegressionCurve";
+}
+
+sal_Bool SAL_CALL ExponentialRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ExponentialRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.ExponentialRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL ExponentialRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new ExponentialRegressionCurve( *this ));
+}
+
+PotentialRegressionCurve::PotentialRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_POWER )
+{}
+PotentialRegressionCurve::PotentialRegressionCurve(
+ const PotentialRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+PotentialRegressionCurve::~PotentialRegressionCurve()
+{}
+
+OUString SAL_CALL PotentialRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.PotentialRegressionCurve";
+}
+
+sal_Bool SAL_CALL PotentialRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL PotentialRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.PotentialRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL PotentialRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new PotentialRegressionCurve( *this ));
+}
+
+PolynomialRegressionCurve::PolynomialRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_POLYNOMIAL )
+{}
+PolynomialRegressionCurve::PolynomialRegressionCurve(
+ const PolynomialRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+PolynomialRegressionCurve::~PolynomialRegressionCurve()
+{}
+
+OUString SAL_CALL PolynomialRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.PolynomialRegressionCurve";
+}
+
+sal_Bool SAL_CALL PolynomialRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL PolynomialRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.PolynomialRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL PolynomialRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new PolynomialRegressionCurve( *this ));
+}
+
+MovingAverageRegressionCurve::MovingAverageRegressionCurve()
+ : RegressionCurveModel( RegressionCurveModel::CURVE_TYPE_MOVING_AVERAGE )
+{}
+MovingAverageRegressionCurve::MovingAverageRegressionCurve(
+ const MovingAverageRegressionCurve & rOther ) :
+ RegressionCurveModel( rOther )
+{}
+MovingAverageRegressionCurve::~MovingAverageRegressionCurve()
+{}
+
+OUString SAL_CALL MovingAverageRegressionCurve::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.MovingAverageRegressionCurve";
+}
+
+sal_Bool SAL_CALL MovingAverageRegressionCurve::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MovingAverageRegressionCurve::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionCurve", "com.sun.star.chart2.MovingAverageRegressionCurve" };
+}
+
+uno::Reference< util::XCloneable > SAL_CALL MovingAverageRegressionCurve::createClone()
+{
+ return uno::Reference< util::XCloneable >( new MovingAverageRegressionCurve( *this ));
+}
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_ExponentialRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::ExponentialRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_LinearRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::LinearRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_LogarithmicRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::LogarithmicRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_MeanValueRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::MeanValueRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_PotentialRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::PotentialRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_PolynomialRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::PolynomialRegressionCurve );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_MovingAverageRegressionCurve_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::MovingAverageRegressionCurve );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RegressionEquation.cxx b/chart2/source/tools/RegressionEquation.cxx
new file mode 100644
index 000000000..d421b09d5
--- /dev/null
+++ b/chart2/source/tools/RegressionEquation.cxx
@@ -0,0 +1,294 @@
+/* -*- 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 "RegressionEquation.hxx"
+#include <LinePropertiesHelper.hxx>
+#include <FillProperties.hxx>
+#include <UserDefinedProperties.hxx>
+#include <CharacterProperties.hxx>
+#include <PropertyHelper.hxx>
+#include <ModifyListenerHelper.hxx>
+#include <unonames.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/awt/Size.hpp>
+
+#include <algorithm>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::beans::Property;
+using ::osl::MutexGuard;
+
+namespace
+{
+
+enum
+{
+ PROP_EQUATION_SHOW,
+ PROP_EQUATION_XNAME,
+ PROP_EQUATION_YNAME,
+ PROP_EQUATION_SHOW_CORRELATION_COEFF,
+ PROP_EQUATION_REF_PAGE_SIZE,
+ PROP_EQUATION_REL_POS,
+ PROP_EQUATION_NUMBER_FORMAT
+};
+
+void lcl_AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ rOutProperties.emplace_back( "ShowEquation",
+ PROP_EQUATION_SHOW,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "XName",
+ PROP_EQUATION_XNAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "YName",
+ PROP_EQUATION_YNAME,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ShowCorrelationCoefficient",
+ PROP_EQUATION_SHOW_CORRELATION_COEFF,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ rOutProperties.emplace_back( "ReferencePageSize",
+ PROP_EQUATION_REF_PAGE_SIZE,
+ cppu::UnoType<awt::Size>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+
+ rOutProperties.emplace_back( "RelativePosition",
+ PROP_EQUATION_REL_POS,
+ cppu::UnoType<chart2::RelativePosition>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+
+ rOutProperties.emplace_back( CHART_UNONAME_NUMFMT,
+ PROP_EQUATION_NUMBER_FORMAT,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+}
+
+::chart::tPropertyValueMap& GetStaticRegressionEquationDefaults()
+{
+ static ::chart::tPropertyValueMap aStaticDefaults =
+ [](){
+ ::chart::tPropertyValueMap aOutMap;
+ ::chart::LinePropertiesHelper::AddDefaultsToMap( aOutMap );
+ ::chart::FillProperties::AddDefaultsToMap( aOutMap );
+ ::chart::CharacterProperties::AddDefaultsToMap( aOutMap );
+
+ ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_EQUATION_SHOW, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_EQUATION_XNAME, OUString("x") );
+ ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_EQUATION_YNAME, OUString("f(x)") );
+ ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_EQUATION_SHOW_CORRELATION_COEFF, false );
+ //::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_EQUATION_SEPARATOR, OUString( '\n' ));
+
+ // override other defaults
+ ::chart::PropertyHelper::setPropertyValue( aOutMap, ::chart::FillProperties::PROP_FILL_STYLE, drawing::FillStyle_NONE );
+ ::chart::PropertyHelper::setPropertyValue( aOutMap, ::chart::LinePropertiesHelper::PROP_LINE_STYLE, drawing::LineStyle_NONE );
+
+ float fDefaultCharHeight = 10.0;
+ ::chart::PropertyHelper::setPropertyValue( aOutMap, ::chart::CharacterProperties::PROP_CHAR_CHAR_HEIGHT, fDefaultCharHeight );
+ ::chart::PropertyHelper::setPropertyValue( aOutMap, ::chart::CharacterProperties::PROP_CHAR_ASIAN_CHAR_HEIGHT, fDefaultCharHeight );
+ ::chart::PropertyHelper::setPropertyValue( aOutMap, ::chart::CharacterProperties::PROP_CHAR_COMPLEX_CHAR_HEIGHT, fDefaultCharHeight );
+ return aOutMap;
+ }();
+ return aStaticDefaults;
+};
+
+::cppu::OPropertyArrayHelper& GetStaticRegressionEquationInfoHelper()
+{
+ static ::cppu::OPropertyArrayHelper aPropHelper =
+ [](){
+ std::vector< css::beans::Property > aProperties;
+ lcl_AddPropertiesToVector( aProperties );
+ ::chart::LinePropertiesHelper::AddPropertiesToVector( aProperties );
+ ::chart::FillProperties::AddPropertiesToVector( aProperties );
+ ::chart::CharacterProperties::AddPropertiesToVector( aProperties );
+ ::chart::UserDefinedProperties::AddPropertiesToVector( aProperties );
+
+ std::sort( aProperties.begin(), aProperties.end(),
+ ::chart::PropertyNameLess() );
+
+ return comphelper::containerToSequence( aProperties );
+ }();
+ return aPropHelper;
+};
+
+const uno::Reference< beans::XPropertySetInfo > & GetStaticRegressionEquationInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xPropertySetInfo(
+ ::cppu::OPropertySetHelper::createPropertySetInfo(GetStaticRegressionEquationInfoHelper()) );
+ return xPropertySetInfo;
+};
+
+} // anonymous namespace
+
+namespace chart
+{
+
+RegressionEquation::RegressionEquation() :
+ ::property::OPropertySet( m_aMutex ),
+ m_xModifyEventForwarder( new ModifyEventForwarder())
+{}
+
+RegressionEquation::RegressionEquation( const RegressionEquation & rOther ) :
+ impl::RegressionEquation_Base(rOther),
+ ::property::OPropertySet( rOther, m_aMutex ),
+ m_xModifyEventForwarder( new ModifyEventForwarder())
+{}
+
+RegressionEquation::~RegressionEquation()
+{}
+
+// ____ XCloneable ____
+uno::Reference< util::XCloneable > SAL_CALL RegressionEquation::createClone()
+{
+ return uno::Reference< util::XCloneable >( new RegressionEquation( *this ));
+}
+
+// ____ OPropertySet ____
+void RegressionEquation::GetDefaultValue( sal_Int32 nHandle, uno::Any& rAny ) const
+{
+ const tPropertyValueMap& rStaticDefaults = GetStaticRegressionEquationDefaults();
+ tPropertyValueMap::const_iterator aFound( rStaticDefaults.find( nHandle ) );
+ if( aFound == rStaticDefaults.end() )
+ rAny.clear();
+ else
+ rAny = (*aFound).second;
+}
+
+::cppu::IPropertyArrayHelper & SAL_CALL RegressionEquation::getInfoHelper()
+{
+ return GetStaticRegressionEquationInfoHelper();
+}
+
+// ____ XPropertySet ____
+Reference< beans::XPropertySetInfo > SAL_CALL RegressionEquation::getPropertySetInfo()
+{
+ return GetStaticRegressionEquationInfo();
+}
+
+// ____ XModifyBroadcaster ____
+void SAL_CALL RegressionEquation::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL RegressionEquation::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+// ____ XModifyListener ____
+void SAL_CALL RegressionEquation::modified( const lang::EventObject& aEvent )
+{
+ m_xModifyEventForwarder->modified( aEvent );
+}
+
+// ____ XEventListener (base of XModifyListener) ____
+void SAL_CALL RegressionEquation::disposing( const lang::EventObject& /* Source */ )
+{
+ // nothing
+}
+
+// ____ OPropertySet ____
+void RegressionEquation::firePropertyChangeEvent()
+{
+ fireModifyEvent();
+}
+
+void RegressionEquation::fireModifyEvent()
+{
+ m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
+}
+
+// ____ XTitle ____
+uno::Sequence< uno::Reference< chart2::XFormattedString > > SAL_CALL RegressionEquation::getText()
+{
+ MutexGuard aGuard( m_aMutex );
+ return m_aStrings;
+}
+
+void SAL_CALL RegressionEquation::setText( const uno::Sequence< uno::Reference< chart2::XFormattedString > >& Strings )
+{
+ MutexGuard aGuard( m_aMutex );
+ ModifyListenerHelper::removeListenerFromAllElements(
+ comphelper::sequenceToContainer<std::vector<uno::Reference< chart2::XFormattedString > > >( m_aStrings ),
+ m_xModifyEventForwarder );
+ m_aStrings = Strings;
+ ModifyListenerHelper::addListenerToAllElements(
+ comphelper::sequenceToContainer<std::vector<uno::Reference< chart2::XFormattedString > > >( m_aStrings ),
+ m_xModifyEventForwarder );
+ fireModifyEvent();
+}
+
+OUString SAL_CALL RegressionEquation::getImplementationName()
+{
+ return "com.sun.star.comp.chart2.RegressionEquation";
+}
+
+sal_Bool SAL_CALL RegressionEquation::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL RegressionEquation::getSupportedServiceNames()
+{
+ return { "com.sun.star.chart2.RegressionEquation",
+ "com.sun.star.beans.PropertySet",
+ "com.sun.star.drawing.FillProperties",
+ "com.sun.star.drawing.LineProperties",
+ "com.sun.star.style.CharacterProperties" };
+}
+
+using impl::RegressionEquation_Base;
+
+IMPLEMENT_FORWARD_XINTERFACE2( RegressionEquation, RegressionEquation_Base, ::property::OPropertySet )
+
+} // namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_chart2_RegressionEquation_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ::chart::RegressionEquation);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RegressionEquation.hxx b/chart2/source/tools/RegressionEquation.hxx
new file mode 100644
index 000000000..9c26a6ad9
--- /dev/null
+++ b/chart2/source/tools/RegressionEquation.hxx
@@ -0,0 +1,118 @@
+/* -*- 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/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/chart2/XTitle.hpp>
+
+#include <OPropertySet.hxx>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/uno3.hxx>
+#include <ModifyListenerHelper.hxx>
+
+namespace chart
+{
+
+namespace impl
+{
+typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::util::XCloneable,
+ css::util::XModifyBroadcaster,
+ css::util::XModifyListener,
+ css::chart2::XTitle >
+ RegressionEquation_Base;
+}
+
+class RegressionEquation final :
+ public cppu::BaseMutex,
+ public impl::RegressionEquation_Base,
+ public ::property::OPropertySet
+{
+public:
+ explicit RegressionEquation();
+ virtual ~RegressionEquation() override;
+
+ 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;
+
+ /// merge XInterface implementations
+ DECLARE_XINTERFACE()
+
+private:
+ explicit RegressionEquation( const RegressionEquation & rOther );
+
+ // ____ OPropertySet ____
+ virtual void GetDefaultValue( sal_Int32 nHandle, css::uno::Any& rAny ) const override;
+
+ virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override;
+
+ // ____ XPropertySet ____
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+
+ // ____ XCloneable ____
+ virtual css::uno::Reference< css::util::XCloneable > SAL_CALL createClone() 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;
+
+ // ____ XEventListener (base of XModifyListener) ____
+ virtual void SAL_CALL disposing(
+ const css::lang::EventObject& Source ) override;
+
+ // ____ XTitle ____
+ virtual css::uno::Sequence<
+ css::uno::Reference< css::chart2::XFormattedString > > SAL_CALL getText() override;
+ virtual void SAL_CALL setText( const css::uno::Sequence<
+ css::uno::Reference<
+ css::chart2::XFormattedString > >& Strings ) override;
+
+ using ::cppu::OPropertySetHelper::disposing;
+
+ // ____ OPropertySet ____
+ virtual void firePropertyChangeEvent() override;
+
+ void fireModifyEvent();
+
+ css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > > m_aStrings;
+
+ rtl::Reference<ModifyEventForwarder> m_xModifyEventForwarder;
+};
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RelativePositionHelper.cxx b/chart2/source/tools/RelativePositionHelper.cxx
new file mode 100644
index 000000000..260888e90
--- /dev/null
+++ b/chart2/source/tools/RelativePositionHelper.cxx
@@ -0,0 +1,381 @@
+/* -*- 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 <RelativePositionHelper.hxx>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace chart
+{
+
+chart2::RelativePosition RelativePositionHelper::getReanchoredPosition(
+ const chart2::RelativePosition & rPosition,
+ const chart2::RelativeSize & rObjectSize,
+ drawing::Alignment aNewAnchor )
+{
+ chart2::RelativePosition aResult( rPosition );
+ if( rPosition.Anchor != aNewAnchor )
+ {
+ sal_Int32 nShiftHalfWidths = 0;
+ sal_Int32 nShiftHalfHeights = 0;
+
+ // normalize to top-left
+ switch( rPosition.Anchor )
+ {
+ case drawing::Alignment_TOP_LEFT:
+ break;
+ case drawing::Alignment_LEFT:
+ nShiftHalfHeights -= 1;
+ break;
+ case drawing::Alignment_BOTTOM_LEFT:
+ nShiftHalfHeights -= 2;
+ break;
+ case drawing::Alignment_TOP:
+ nShiftHalfWidths -= 1;
+ break;
+ case drawing::Alignment_CENTER:
+ nShiftHalfWidths -= 1;
+ nShiftHalfHeights -= 1;
+ break;
+ case drawing::Alignment_BOTTOM:
+ nShiftHalfWidths -= 1;
+ nShiftHalfHeights -= 2;
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ nShiftHalfWidths -= 2;
+ break;
+ case drawing::Alignment_RIGHT:
+ nShiftHalfWidths -= 2;
+ nShiftHalfHeights -= 1;
+ break;
+ case drawing::Alignment_BOTTOM_RIGHT:
+ nShiftHalfWidths -= 2;
+ nShiftHalfHeights -= 2;
+ break;
+ case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
+ break;
+ }
+
+ // transform
+ switch( aNewAnchor )
+ {
+ case drawing::Alignment_TOP_LEFT:
+ break;
+ case drawing::Alignment_LEFT:
+ nShiftHalfHeights += 1;
+ break;
+ case drawing::Alignment_BOTTOM_LEFT:
+ nShiftHalfHeights += 2;
+ break;
+ case drawing::Alignment_TOP:
+ nShiftHalfWidths += 1;
+ break;
+ case drawing::Alignment_CENTER:
+ nShiftHalfWidths += 1;
+ nShiftHalfHeights += 1;
+ break;
+ case drawing::Alignment_BOTTOM:
+ nShiftHalfWidths += 1;
+ nShiftHalfHeights += 2;
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ nShiftHalfWidths += 2;
+ break;
+ case drawing::Alignment_RIGHT:
+ nShiftHalfWidths += 2;
+ nShiftHalfHeights += 1;
+ break;
+ case drawing::Alignment_BOTTOM_RIGHT:
+ nShiftHalfWidths += 2;
+ nShiftHalfHeights += 2;
+ break;
+ case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
+ break;
+ }
+
+ if( nShiftHalfWidths != 0 )
+ aResult.Primary += (rObjectSize.Primary / 2.0) * nShiftHalfWidths;
+ if( nShiftHalfHeights != 0 )
+ aResult.Secondary += (rObjectSize.Secondary / 2.0) * nShiftHalfHeights;
+ }
+
+ return aResult;
+}
+
+awt::Point RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
+ awt::Point aPoint
+ , awt::Size aObjectSize
+ , drawing::Alignment aAnchor )
+{
+ awt::Point aResult( aPoint );
+
+ double fXDelta = 0.0;
+ double fYDelta = 0.0;
+
+ // adapt x-value
+ switch( aAnchor )
+ {
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_BOTTOM:
+ fXDelta -= static_cast< double >( aObjectSize.Width ) / 2.0;
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ case drawing::Alignment_RIGHT:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ fXDelta -= aObjectSize.Width;
+ break;
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_BOTTOM_LEFT:
+ default:
+ // nothing to do
+ break;
+ }
+
+ // adapt y-value
+ switch( aAnchor )
+ {
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_RIGHT:
+ fYDelta -= static_cast< double >( aObjectSize.Height ) / 2.0;
+ break;
+ case drawing::Alignment_BOTTOM_LEFT:
+ case drawing::Alignment_BOTTOM:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ fYDelta -= aObjectSize.Height;
+ break;
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_TOP_RIGHT:
+ default:
+ // nothing to do
+ break;
+ }
+
+ aResult.X += static_cast< sal_Int32 >( ::rtl::math::round( fXDelta ));
+ aResult.Y += static_cast< sal_Int32 >( ::rtl::math::round( fYDelta ));
+
+ return aResult;
+}
+
+awt::Point RelativePositionHelper::getCenterOfAnchoredObject(
+ awt::Point aPoint
+ , awt::Size aUnrotatedObjectSize
+ , drawing::Alignment aAnchor
+ , double fAnglePi )
+{
+ awt::Point aResult( aPoint );
+
+ double fXDelta = 0.0;
+ double fYDelta = 0.0;
+
+ // adapt x-value
+ switch( aAnchor )
+ {
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_BOTTOM:
+ // nothing to do
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ case drawing::Alignment_RIGHT:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ fXDelta -= aUnrotatedObjectSize.Width/2;
+ break;
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_BOTTOM_LEFT:
+ default:
+ fXDelta += aUnrotatedObjectSize.Width/2;
+ break;
+ }
+
+ // adapt y-value
+ switch( aAnchor )
+ {
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_RIGHT:
+ // nothing to do
+ break;
+ case drawing::Alignment_BOTTOM_LEFT:
+ case drawing::Alignment_BOTTOM:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ fYDelta -= aUnrotatedObjectSize.Height/2;
+ break;
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_TOP_RIGHT:
+ fYDelta += aUnrotatedObjectSize.Height/2;
+ break;
+ default:
+ // nothing to do
+ break;
+ }
+
+ //take rotation into account:
+ aResult.X += static_cast< sal_Int32 >(
+ ::rtl::math::round( fXDelta * std::cos( fAnglePi ) + fYDelta * std::sin( fAnglePi ) ) );
+ aResult.Y += static_cast< sal_Int32 >(
+ ::rtl::math::round( - fXDelta * std::sin( fAnglePi ) + fYDelta * std::cos( fAnglePi ) ) );
+
+ return aResult;
+}
+
+bool RelativePositionHelper::centerGrow(
+ chart2::RelativePosition & rInOutPosition,
+ chart2::RelativeSize & rInOutSize,
+ double fAmountX, double fAmountY )
+{
+ chart2::RelativePosition aPos( rInOutPosition );
+ chart2::RelativeSize aSize( rInOutSize );
+ const double fPosCheckThreshold = 0.02;
+ const double fSizeCheckThreshold = 0.1;
+
+ // grow/shrink, back to relative
+ aSize.Primary += fAmountX;
+ aSize.Secondary += fAmountY;
+
+ double fShiftAmountX = fAmountX / 2.0;
+ double fShiftAmountY = fAmountY / 2.0;
+
+ // shift X
+ switch( rInOutPosition.Anchor )
+ {
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_BOTTOM_LEFT:
+ aPos.Primary -= fShiftAmountX;
+ break;
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_BOTTOM:
+ // nothing
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ case drawing::Alignment_RIGHT:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ aPos.Primary += fShiftAmountX;
+ break;
+ case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
+ break;
+ }
+
+ // shift Y
+ switch( rInOutPosition.Anchor )
+ {
+ case drawing::Alignment_TOP:
+ case drawing::Alignment_TOP_LEFT:
+ case drawing::Alignment_TOP_RIGHT:
+ aPos.Secondary -= fShiftAmountY;
+ break;
+ case drawing::Alignment_CENTER:
+ case drawing::Alignment_LEFT:
+ case drawing::Alignment_RIGHT:
+ // nothing
+ break;
+ case drawing::Alignment_BOTTOM:
+ case drawing::Alignment_BOTTOM_LEFT:
+ case drawing::Alignment_BOTTOM_RIGHT:
+ aPos.Secondary += fShiftAmountY;
+ break;
+ case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
+ break;
+ }
+
+ // anchor must not be changed
+ OSL_ASSERT( rInOutPosition.Anchor == aPos.Anchor );
+
+ if( rInOutPosition.Primary == aPos.Primary &&
+ rInOutPosition.Secondary == aPos.Secondary &&
+ rInOutSize.Primary == aSize.Primary &&
+ rInOutSize.Secondary == aSize.Secondary )
+ return false;
+
+ // Note: this somewhat complicated check allows the output being
+ // out-of-bounds if the input was also out-of-bounds, and the change is
+ // for "advantage". E.g., you have a chart that laps out on the left
+ // side. If you shrink it, this should be possible, also if it still
+ // laps out on the left side afterwards. But you shouldn't be able to
+ // grow it then.
+
+ chart2::RelativePosition aUpperLeft(
+ RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_TOP_LEFT ));
+ chart2::RelativePosition aLowerRight(
+ RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_BOTTOM_RIGHT ));
+
+ // Do not grow, if this leads to corners being off-screen
+ if( fAmountX > 0.0 &&
+ ( (aUpperLeft.Primary < fPosCheckThreshold) ||
+ (aLowerRight.Primary > (1.0 - fPosCheckThreshold)) ))
+ return false;
+ if( fAmountY > 0.0 &&
+ ( (aUpperLeft.Secondary < fPosCheckThreshold) ||
+ (aLowerRight.Secondary > (1.0 - fPosCheckThreshold)) ))
+ return false;
+
+ // Do not shrink, if this leads to a size too small
+ if( fAmountX < 0.0 &&
+ ( aSize.Primary < fSizeCheckThreshold ))
+ return false;
+ if( fAmountY < 0.0 &&
+ ( aSize.Secondary < fSizeCheckThreshold ))
+ return false;
+
+ rInOutPosition = aPos;
+ rInOutSize = aSize;
+ return true;
+}
+
+bool RelativePositionHelper::moveObject(
+ chart2::RelativePosition & rInOutPosition,
+ const chart2::RelativeSize & rObjectSize,
+ double fAmountX, double fAmountY )
+{
+ chart2::RelativePosition aPos( rInOutPosition );
+ aPos.Primary += fAmountX;
+ aPos.Secondary += fAmountY;
+ const double fPosCheckThreshold = 0.02;
+
+ chart2::RelativePosition aUpperLeft(
+ RelativePositionHelper::getReanchoredPosition( aPos, rObjectSize, drawing::Alignment_TOP_LEFT ));
+ chart2::RelativePosition aLowerRight( aUpperLeft );
+ aLowerRight.Primary += rObjectSize.Primary;
+ aLowerRight.Secondary += rObjectSize.Secondary;
+
+ const double fFarEdgeThreshold = 1.0 - fPosCheckThreshold;
+ if( ( fAmountX > 0.0 && (aLowerRight.Primary > fFarEdgeThreshold)) ||
+ ( fAmountX < 0.0 && (aUpperLeft.Primary < fPosCheckThreshold)) ||
+ ( fAmountY > 0.0 && (aLowerRight.Secondary > fFarEdgeThreshold)) ||
+ ( fAmountY < 0.0 && (aUpperLeft.Secondary < fPosCheckThreshold)) )
+ return false;
+
+ rInOutPosition = aPos;
+ return true;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/RelativeSizeHelper.cxx b/chart2/source/tools/RelativeSizeHelper.cxx
new file mode 100644
index 000000000..c1bdc28fc
--- /dev/null
+++ b/chart2/source/tools/RelativeSizeHelper.cxx
@@ -0,0 +1,121 @@
+/* -*- 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 <RelativeSizeHelper.hxx>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/diagnose_ex.h>
+#include <svx/unoshape.hxx>
+#include <vector>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::std;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+
+namespace chart
+{
+
+double RelativeSizeHelper::calculate(
+ double fValue,
+ const awt::Size & rOldReferenceSize,
+ const awt::Size & rNewReferenceSize )
+{
+ if( rOldReferenceSize.Width <= 0 ||
+ rOldReferenceSize.Height <= 0 )
+ return fValue;
+
+ return min(
+ static_cast< double >( rNewReferenceSize.Width ) / static_cast< double >( rOldReferenceSize.Width ),
+ static_cast< double >( rNewReferenceSize.Height ) / static_cast< double >( rOldReferenceSize.Height ))
+ * fValue;
+}
+
+void RelativeSizeHelper::adaptFontSizes(
+ SvxShapeText& xTargetProperties,
+ const awt::Size & rOldReferenceSize,
+ const awt::Size & rNewReferenceSize )
+{
+ float fFontHeight = 0;
+
+ vector< OUString > aProperties;
+ aProperties.emplace_back("CharHeight" );
+ aProperties.emplace_back("CharHeightAsian" );
+ aProperties.emplace_back("CharHeightComplex" );
+
+ for (auto const& property : aProperties)
+ {
+ try
+ {
+ if( xTargetProperties.SvxShape::getPropertyValue(property) >>= fFontHeight )
+ {
+ xTargetProperties.SvxShape::setPropertyValue(
+ property,
+ Any( static_cast< float >(
+ calculate( fFontHeight, rOldReferenceSize, rNewReferenceSize ))));
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+}
+
+void RelativeSizeHelper::adaptFontSizes(
+ const Reference< XPropertySet > & xTargetProperties,
+ const awt::Size & rOldReferenceSize,
+ const awt::Size & rNewReferenceSize )
+{
+ if( ! xTargetProperties.is())
+ return;
+
+ float fFontHeight = 0;
+
+ vector< OUString > aProperties;
+ aProperties.emplace_back("CharHeight" );
+ aProperties.emplace_back("CharHeightAsian" );
+ aProperties.emplace_back("CharHeightComplex" );
+
+ for (auto const& property : aProperties)
+ {
+ try
+ {
+ if( xTargetProperties->getPropertyValue(property) >>= fFontHeight )
+ {
+ xTargetProperties->setPropertyValue(
+ property,
+ Any( static_cast< float >(
+ calculate( fFontHeight, rOldReferenceSize, rNewReferenceSize ))));
+ }
+ }
+ catch( const Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ResId.cxx b/chart2/source/tools/ResId.cxx
new file mode 100644
index 000000000..aaa1d840c
--- /dev/null
+++ b/chart2/source/tools/ResId.cxx
@@ -0,0 +1,30 @@
+/* -*- 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 <ResId.hxx>
+
+namespace chart
+{
+ OUString SchResId(TranslateId aId)
+ {
+ return Translate::get(aId, Translate::Create("chart"));
+ }
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/Scaling.cxx b/chart2/source/tools/Scaling.cxx
new file mode 100644
index 000000000..b49dab6d6
--- /dev/null
+++ b/chart2/source/tools/Scaling.cxx
@@ -0,0 +1,267 @@
+/* -*- 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 <Scaling.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cmath>
+#include <limits>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace
+{
+
+constexpr OUStringLiteral lcl_aServiceName_Logarithmic = u"com.sun.star.chart2.LogarithmicScaling";
+constexpr OUStringLiteral lcl_aServiceName_Exponential = u"com.sun.star.chart2.ExponentialScaling";
+constexpr OUStringLiteral lcl_aServiceName_Linear = u"com.sun.star.chart2.LinearScaling";
+constexpr OUStringLiteral lcl_aServiceName_Power = u"com.sun.star.chart2.PowerScaling";
+
+}
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+LogarithmicScaling::LogarithmicScaling() :
+ m_fBase( 10.0 ),
+ m_fLogOfBase( log( 10.0 ) )
+{
+}
+
+LogarithmicScaling::LogarithmicScaling( double fBase ) :
+ m_fBase( fBase ),
+ m_fLogOfBase( log( fBase ) )
+{
+}
+
+LogarithmicScaling::~LogarithmicScaling()
+{
+}
+
+double SAL_CALL LogarithmicScaling::doScaling( double value )
+{
+ if( std::isnan( value ) || std::isinf( value ) )
+ return std::numeric_limits<double>::quiet_NaN();
+ return std::log( value ) / m_fLogOfBase;
+}
+
+uno::Reference< XScaling > SAL_CALL LogarithmicScaling::getInverseScaling()
+{
+ return new ExponentialScaling( m_fBase );
+}
+
+OUString SAL_CALL LogarithmicScaling::getServiceName()
+{
+ return lcl_aServiceName_Logarithmic;
+}
+
+OUString SAL_CALL LogarithmicScaling::getImplementationName()
+{
+ return lcl_aServiceName_Logarithmic;
+}
+
+sal_Bool SAL_CALL LogarithmicScaling::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LogarithmicScaling::getSupportedServiceNames()
+{
+ return { lcl_aServiceName_Logarithmic };
+}
+
+ExponentialScaling::ExponentialScaling() :
+ m_fBase( 10.0 )
+{
+}
+
+ExponentialScaling::ExponentialScaling( double fBase ) :
+ m_fBase( fBase )
+{
+}
+
+ExponentialScaling::~ExponentialScaling()
+{
+}
+
+double SAL_CALL ExponentialScaling::doScaling( double value )
+{
+ if( std::isnan( value ) || std::isinf( value ) )
+ return std::numeric_limits<double>::quiet_NaN();
+ return std::pow( m_fBase, value );
+}
+
+uno::Reference< XScaling > SAL_CALL ExponentialScaling::getInverseScaling()
+{
+ return new LogarithmicScaling( m_fBase );
+}
+
+OUString SAL_CALL ExponentialScaling::getServiceName()
+{
+ return lcl_aServiceName_Exponential;
+}
+
+OUString SAL_CALL ExponentialScaling::getImplementationName()
+{
+ return lcl_aServiceName_Exponential;
+}
+
+sal_Bool SAL_CALL ExponentialScaling::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ExponentialScaling::getSupportedServiceNames()
+{
+ return { lcl_aServiceName_Exponential };
+}
+
+LinearScaling::LinearScaling() :
+ m_fSlope( 1.0 ),
+ m_fOffset( 0.0 )
+{}
+
+LinearScaling::LinearScaling( double fSlope, double fOffset ) :
+ m_fSlope( fSlope ),
+ m_fOffset( fOffset )
+{}
+
+LinearScaling::~LinearScaling()
+{}
+
+double SAL_CALL LinearScaling::doScaling( double value )
+{
+ if( std::isnan( value ) || std::isinf( value ) )
+ return std::numeric_limits<double>::quiet_NaN();
+ return m_fOffset + m_fSlope * value;
+}
+
+uno::Reference< XScaling > SAL_CALL
+ LinearScaling::getInverseScaling()
+{
+ // ToDo: ApproxEqual ?
+ if( m_fSlope == 0 )
+ throw uno::RuntimeException("Divide by zero exception");
+
+ return new LinearScaling( 1.0 / m_fSlope, m_fOffset / m_fSlope );
+}
+
+OUString SAL_CALL LinearScaling::getServiceName()
+{
+ return lcl_aServiceName_Linear;
+}
+
+OUString SAL_CALL LinearScaling::getImplementationName()
+{
+ return lcl_aServiceName_Linear ;
+}
+
+sal_Bool SAL_CALL LinearScaling::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL LinearScaling::getSupportedServiceNames()
+{
+ return { lcl_aServiceName_Linear };
+}
+
+PowerScaling::PowerScaling() :
+ m_fExponent( 10.0 )
+{}
+
+PowerScaling::PowerScaling( double fExponent ) :
+ m_fExponent( fExponent )
+{}
+
+PowerScaling::~PowerScaling()
+{}
+
+double SAL_CALL PowerScaling::doScaling( double value )
+{
+ if( std::isnan( value ) || std::isinf( value ) )
+ return std::numeric_limits<double>::quiet_NaN();
+ return std::pow( value, m_fExponent );
+}
+
+uno::Reference< XScaling > SAL_CALL
+ PowerScaling::getInverseScaling()
+{
+ // ToDo: ApproxEqual ?
+ if( m_fExponent == 0 )
+ throw uno::RuntimeException("Divide by zero exception");
+
+ return new PowerScaling( 1.0 / m_fExponent );
+}
+
+ OUString SAL_CALL
+PowerScaling::getServiceName()
+{
+ return lcl_aServiceName_Power;
+}
+
+OUString SAL_CALL PowerScaling::getImplementationName()
+{
+ return lcl_aServiceName_Power;
+}
+
+sal_Bool SAL_CALL PowerScaling::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL PowerScaling::getSupportedServiceNames()
+{
+ return { lcl_aServiceName_Power };
+}
+
+} //namespace chart
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_chart2_LinearScaling_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new chart::LinearScaling );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_chart2_ExponentialScaling_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new chart::ExponentialScaling );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_chart2_LogarithmicScaling_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new chart::LogarithmicScaling );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_chart2_PowerScaling_get_implementation(css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new chart::PowerScaling );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/SceneProperties.cxx b/chart2/source/tools/SceneProperties.cxx
new file mode 100644
index 000000000..a98bfef2d
--- /dev/null
+++ b/chart2/source/tools/SceneProperties.cxx
@@ -0,0 +1,333 @@
+/* -*- 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 <SceneProperties.hxx>
+#include <ChartType.hxx>
+#include <ChartTypeHelper.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/drawing/HomogenMatrix.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/CameraGeometry.hpp>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace chart
+{
+
+void SceneProperties::AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ // transformation matrix
+ rOutProperties.emplace_back( "D3DTransformMatrix",
+ PROP_SCENE_TRANSF_MATRIX,
+ cppu::UnoType<drawing::HomogenMatrix>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // distance: deprecated ( this is not used by the chart view; it's only here for compatibility with old chart )
+ rOutProperties.emplace_back( "D3DSceneDistance",
+ PROP_SCENE_DISTANCE,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // focalLength: deprecated ( this is not used by the chart view; it's only here for compatibility with old chart )
+ rOutProperties.emplace_back( "D3DSceneFocalLength",
+ PROP_SCENE_FOCAL_LENGTH,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // shadowSlant
+ rOutProperties.emplace_back( "D3DSceneShadowSlant",
+ PROP_SCENE_SHADOW_SLANT,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // shadeMode
+ rOutProperties.emplace_back( "D3DSceneShadeMode",
+ PROP_SCENE_SHADE_MODE,
+ cppu::UnoType<drawing::ShadeMode>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // ambientColor
+ rOutProperties.emplace_back( "D3DSceneAmbientColor",
+ PROP_SCENE_AMBIENT_COLOR,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // lightingMode
+ rOutProperties.emplace_back( "D3DSceneTwoSidedLighting",
+ PROP_SCENE_TWO_SIDED_LIGHTING,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // camera geometry
+ rOutProperties.emplace_back( "D3DCameraGeometry",
+ PROP_SCENE_CAMERA_GEOMETRY,
+ cppu::UnoType<drawing::CameraGeometry>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // perspective
+ rOutProperties.emplace_back( "D3DScenePerspective",
+ PROP_SCENE_PERSPECTIVE,
+ cppu::UnoType<drawing::ProjectionMode>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+
+ // Light Sources
+ // light source 1
+ rOutProperties.emplace_back( "D3DSceneLightColor1",
+ PROP_SCENE_LIGHT_COLOR_1,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection1",
+ PROP_SCENE_LIGHT_DIRECTION_1,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn1",
+ PROP_SCENE_LIGHT_ON_1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 2
+ rOutProperties.emplace_back( "D3DSceneLightColor2",
+ PROP_SCENE_LIGHT_COLOR_2,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection2",
+ PROP_SCENE_LIGHT_DIRECTION_2,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn2",
+ PROP_SCENE_LIGHT_ON_2,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 3
+ rOutProperties.emplace_back( "D3DSceneLightColor3",
+ PROP_SCENE_LIGHT_COLOR_3,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection3",
+ PROP_SCENE_LIGHT_DIRECTION_3,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn3",
+ PROP_SCENE_LIGHT_ON_3,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 4
+ rOutProperties.emplace_back( "D3DSceneLightColor4",
+ PROP_SCENE_LIGHT_COLOR_4,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection4",
+ PROP_SCENE_LIGHT_DIRECTION_4,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn4",
+ PROP_SCENE_LIGHT_ON_4,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 5
+ rOutProperties.emplace_back( "D3DSceneLightColor5",
+ PROP_SCENE_LIGHT_COLOR_5,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection5",
+ PROP_SCENE_LIGHT_DIRECTION_5,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn5",
+ PROP_SCENE_LIGHT_ON_5,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 6
+ rOutProperties.emplace_back( "D3DSceneLightColor6",
+ PROP_SCENE_LIGHT_COLOR_6,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection6",
+ PROP_SCENE_LIGHT_DIRECTION_6,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn6",
+ PROP_SCENE_LIGHT_ON_6,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 7
+ rOutProperties.emplace_back( "D3DSceneLightColor7",
+ PROP_SCENE_LIGHT_COLOR_7,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection7",
+ PROP_SCENE_LIGHT_DIRECTION_7,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn7",
+ PROP_SCENE_LIGHT_ON_7,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ // light source 8
+ rOutProperties.emplace_back( "D3DSceneLightColor8",
+ PROP_SCENE_LIGHT_COLOR_8,
+ cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightDirection8",
+ PROP_SCENE_LIGHT_DIRECTION_8,
+ cppu::UnoType<drawing::Direction3D>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+ rOutProperties.emplace_back( "D3DSceneLightOn8",
+ PROP_SCENE_LIGHT_ON_8,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID
+ | beans::PropertyAttribute::MAYBEDEFAULT );
+}
+
+void SceneProperties::AddDefaultsToMap(
+ ::chart::tPropertyValueMap & rOutMap )
+{
+ // Identity Matrix
+ drawing::HomogenMatrix aMtx;
+ aMtx.Line1.Column1 = aMtx.Line2.Column2 =
+ aMtx.Line3.Column3 = aMtx.Line4.Column4 = 1.0;
+ aMtx.Line1.Column2 = aMtx.Line1.Column3 = aMtx.Line1.Column4 =
+ aMtx.Line2.Column1 = aMtx.Line2.Column3 = aMtx.Line2.Column4 =
+ aMtx.Line3.Column1 = aMtx.Line3.Column2 = aMtx.Line3.Column4 =
+ aMtx.Line4.Column1 = aMtx.Line4.Column2 = aMtx.Line4.Column3 = 0.0;
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_TRANSF_MATRIX, aMtx );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_SCENE_DISTANCE, 4200 );
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( rOutMap, PROP_SCENE_FOCAL_LENGTH, 8000 );
+
+// PROP_SCENE_SHADOW_SLANT;
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_SHADE_MODE, drawing::ShadeMode_SMOOTH );
+
+ ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >(
+ rOutMap, PROP_SCENE_AMBIENT_COLOR, ChartTypeHelper::getDefaultAmbientLightColor(false,nullptr));
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_TWO_SIDED_LIGHTING, true );
+
+ drawing::Position3D vrp( 0.0, 0.0, 1.0 );
+ drawing::Direction3D vpn( 0.0, 0.0, 1.0 );
+ drawing::Direction3D vup( 0.0, 1.0, 0.0 );
+ drawing::CameraGeometry aDefaultCameraGeometry( vrp, vpn, vup );
+
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_CAMERA_GEOMETRY, aDefaultCameraGeometry );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_PERSPECTIVE, drawing::ProjectionMode_PERSPECTIVE );
+
+ // Light Sources
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_1, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_2, true );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_3, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_4, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_5, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_6, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_7, false );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_ON_8, false );
+
+ uno::Any aDefaultLightDirection( drawing::Direction3D( 0.0, 0.0, 1.0 ) );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_1, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_2, ChartTypeHelper::getDefaultSimpleLightDirection(nullptr));
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_3, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_4, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_5, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_6, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_7, aDefaultLightDirection );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_DIRECTION_8, aDefaultLightDirection );
+
+ uno::Any aDefaultLightColor;
+ aDefaultLightColor <<= ChartTypeHelper::getDefaultDirectLightColor(false,nullptr);
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_1, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_2, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_3, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_4, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_5, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_6, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_7, aDefaultLightColor );
+ ::chart::PropertyHelper::setPropertyValueDefault( rOutMap, PROP_SCENE_LIGHT_COLOR_8, aDefaultLightColor );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/StatisticsHelper.cxx b/chart2/source/tools/StatisticsHelper.cxx
new file mode 100644
index 000000000..78c8b8dda
--- /dev/null
+++ b/chart2/source/tools/StatisticsHelper.cxx
@@ -0,0 +1,363 @@
+/* -*- 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 <StatisticsHelper.hxx>
+#include <DataSeriesHelper.hxx>
+#include <ErrorBar.hxx>
+#include <unonames.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/chart2/XDataSeries.hpp>
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataSink.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <tools/diagnose_ex.h>
+
+#include <cmath>
+#include <limits>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using namespace ::com::sun::star;
+
+namespace
+{
+
+double lcl_getVariance( const Sequence< double > & rData, sal_Int32 & rOutValidCount )
+{
+ const sal_Int32 nCount = rData.getLength();
+ rOutValidCount = nCount;
+
+ double fSum = 0.0;
+ double fQuadSum = 0.0;
+
+ for( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ const double fData = rData[i];
+ if( std::isnan( fData ))
+ --rOutValidCount;
+ else
+ {
+ fSum += fData;
+ fQuadSum += fData * fData;
+ }
+ }
+
+ if( rOutValidCount == 0 )
+ return std::numeric_limits<double>::quiet_NaN();
+
+ const double fN = static_cast< double >( rOutValidCount );
+ return (fQuadSum - fSum*fSum/fN) / fN;
+}
+
+uno::Reference< chart2::data::XLabeledDataSequence > lcl_getErrorBarLabeledSequence(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ bool bPositiveValue, bool bYError,
+ OUString & rOutRoleNameUsed )
+{
+ OUStringBuffer aRole( "error-bars-");
+ if( bYError )
+ aRole.append( 'y');
+ else
+ aRole.append( 'x');
+
+ OUString aPlainRole = aRole.makeStringAndClear();
+ aRole.append( aPlainRole );
+ aRole.append( '-' );
+
+ if( bPositiveValue )
+ aRole.append( "positive" );
+ else
+ aRole.append( "negative" );
+
+ OUString aLongRole = aRole.makeStringAndClear();
+ uno::Reference< chart2::data::XLabeledDataSequence > xLSeq =
+ ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aLongRole );
+ // try role without "-negative" or "-positive" postfix
+ if( xLSeq.is())
+ rOutRoleNameUsed = aLongRole;
+ else
+ {
+ xLSeq = ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, aPlainRole );
+ if( xLSeq.is())
+ rOutRoleNameUsed = aPlainRole;
+ else
+ rOutRoleNameUsed = aLongRole;
+ }
+
+ return xLSeq;
+}
+
+void lcl_setRole(
+ const Reference< chart2::data::XDataSequence > & xNewSequence,
+ const OUString & rRole )
+{
+ Reference< beans::XPropertySet > xSeqProp( xNewSequence, uno::UNO_QUERY );
+ if( xSeqProp.is())
+ xSeqProp->setPropertyValue( "Role", uno::Any( rRole ));
+}
+
+void lcl_addSequenceToDataSource(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ const Reference< chart2::data::XDataSequence > & xNewSequence,
+ const OUString & rRole )
+{
+ Reference< chart2::data::XDataSink > xSink( xDataSource, uno::UNO_QUERY );
+ Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ if( ! xSink.is() )
+ return;
+
+ Reference< chart2::data::XLabeledDataSequence > xLSeq( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW );
+
+ lcl_setRole( xNewSequence, rRole );
+ xLSeq->setValues( xNewSequence );
+ Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences(
+ xDataSource->getDataSequences());
+ aSequences.realloc( aSequences.getLength() + 1 );
+ auto pSequences = aSequences.getArray();
+ pSequences[ aSequences.getLength() - 1 ] = xLSeq;
+ xSink->setData( aSequences );
+}
+
+void lcl_setXMLRangePropertyAtDataSequence(
+ const Reference< chart2::data::XDataSequence > & xDataSequence,
+ const OUString & rXMLRange )
+{
+ try
+ {
+ static const OUStringLiteral aXMLRangePropName( u"CachedXMLRange");
+ Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
+ Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
+ if( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ))
+ xProp->setPropertyValue( aXMLRangePropName, uno::Any( rXMLRange ));
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+} // anonymous namespace
+
+namespace chart
+{
+
+double StatisticsHelper::getVariance(
+ const Sequence< double > & rData )
+{
+ sal_Int32 nValCount;
+ return lcl_getVariance( rData, nValCount );
+}
+
+double StatisticsHelper::getStandardDeviation( const Sequence< double > & rData )
+{
+ double fResult = getVariance( rData );
+ if( ! std::isnan( fResult ))
+ fResult = sqrt( fResult );
+
+ return fResult;
+}
+
+double StatisticsHelper::getStandardError( const Sequence< double > & rData )
+{
+ sal_Int32 nValCount;
+ double fVar = lcl_getVariance( rData, nValCount );
+
+ if( nValCount == 0 || std::isnan( fVar ))
+ return std::numeric_limits<double>::quiet_NaN();
+ // standard-deviation / sqrt(n)
+ return sqrt( fVar ) / sqrt( double(nValCount) );
+}
+
+uno::Reference< chart2::data::XLabeledDataSequence > StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ bool bPositiveValue,
+ bool bYError /* = true */ )
+{
+ uno::Reference< chart2::data::XLabeledDataSequence > xResult;
+ if( !xDataSource.is())
+ return xResult;
+
+ OUString aRole;
+ uno::Reference< chart2::data::XLabeledDataSequence > xLSeq =
+ lcl_getErrorBarLabeledSequence( xDataSource, bPositiveValue, bYError, aRole );
+ if( xLSeq.is())
+ xResult = xLSeq;
+
+ return xResult;
+}
+
+Reference< chart2::data::XDataSequence > StatisticsHelper::getErrorDataSequenceFromDataSource(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ bool bPositiveValue,
+ bool bYError /* = true */ )
+{
+ uno::Reference< chart2::data::XLabeledDataSequence > xLSeq =
+ StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
+ xDataSource, bPositiveValue,
+ bYError );
+ if( !xLSeq.is())
+ return Reference< chart2::data::XDataSequence >();
+
+ return xLSeq->getValues();
+}
+
+double StatisticsHelper::getErrorFromDataSource(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ sal_Int32 nIndex,
+ bool bPositiveValue,
+ bool bYError /* = true */ )
+{
+ double fResult = std::numeric_limits<double>::quiet_NaN();
+
+ Reference< chart2::data::XDataSequence > xValues(
+ StatisticsHelper::getErrorDataSequenceFromDataSource( xDataSource, bPositiveValue, bYError ));
+
+ Reference< chart2::data::XNumericalDataSequence > xNumValues( xValues, uno::UNO_QUERY );
+ if( xNumValues.is())
+ {
+ Sequence< double > aData( xNumValues->getNumericalData());
+ if( nIndex < aData.getLength())
+ fResult = aData[nIndex];
+ }
+ else if( xValues.is())
+ {
+ Sequence< uno::Any > aData( xValues->getData());
+ if( nIndex < aData.getLength())
+ aData[nIndex] >>= fResult;
+ }
+
+ return fResult;
+}
+
+void StatisticsHelper::setErrorDataSequence(
+ const Reference< chart2::data::XDataSource > & xDataSource,
+ const Reference< chart2::data::XDataProvider > & xDataProvider,
+ const OUString & rNewRange,
+ bool bPositiveValue,
+ bool bYError /* = true */,
+ OUString const * pXMLRange /* = 0 */ )
+{
+ Reference< chart2::data::XDataSink > xDataSink( xDataSource, uno::UNO_QUERY );
+ if( ! ( xDataSink.is() && xDataProvider.is()))
+ return;
+
+ OUString aRole;
+ Reference< chart2::data::XLabeledDataSequence > xLSeq(
+ lcl_getErrorBarLabeledSequence( xDataSource, bPositiveValue, bYError, aRole ));
+ Reference< chart2::data::XDataSequence > xNewSequence(
+ xDataProvider->createDataSequenceByRangeRepresentation( rNewRange ));
+ if( xNewSequence.is())
+ {
+ if( pXMLRange )
+ lcl_setXMLRangePropertyAtDataSequence( xNewSequence, *pXMLRange );
+ if( xLSeq.is())
+ {
+ lcl_setRole( xNewSequence, aRole );
+ xLSeq->setValues( xNewSequence );
+ }
+ else
+ lcl_addSequenceToDataSource( xDataSource, xNewSequence, aRole );
+ }
+}
+
+Reference< beans::XPropertySet > StatisticsHelper::addErrorBars(
+ const Reference< chart2::XDataSeries > & xDataSeries,
+ sal_Int32 nStyle,
+ bool bYError /* = true */ )
+{
+ Reference< beans::XPropertySet > xErrorBar;
+ Reference< beans::XPropertySet > xSeriesProp( xDataSeries, uno::UNO_QUERY );
+ if( !xSeriesProp.is())
+ return xErrorBar;
+
+ const OUString aPropName(
+ bYError ? OUString(CHART_UNONAME_ERRORBAR_Y) : OUString(CHART_UNONAME_ERRORBAR_X));
+ if( !( xSeriesProp->getPropertyValue( aPropName ) >>= xErrorBar ) ||
+ !xErrorBar.is())
+ {
+ xErrorBar.set( new ErrorBar );
+ }
+
+ OSL_ASSERT( xErrorBar.is());
+ if( xErrorBar.is())
+ {
+ xErrorBar->setPropertyValue( "ErrorBarStyle", uno::Any( nStyle ));
+ }
+
+ xSeriesProp->setPropertyValue( aPropName, uno::Any( xErrorBar ));
+
+ return xErrorBar;
+}
+
+Reference< beans::XPropertySet > StatisticsHelper::getErrorBars(
+ const Reference< chart2::XDataSeries > & xDataSeries,
+ bool bYError /* = true */ )
+{
+ Reference< beans::XPropertySet > xSeriesProp( xDataSeries, uno::UNO_QUERY );
+ Reference< beans::XPropertySet > xErrorBar;
+ const OUString aPropName(
+ bYError ? OUString(CHART_UNONAME_ERRORBAR_Y) : OUString(CHART_UNONAME_ERRORBAR_X));
+
+ if ( xSeriesProp.is())
+ xSeriesProp->getPropertyValue( aPropName ) >>= xErrorBar;
+
+ return xErrorBar;
+}
+
+bool StatisticsHelper::hasErrorBars(
+ const Reference< chart2::XDataSeries > & xDataSeries,
+ bool bYError /* = true */ )
+{
+ Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
+ sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
+
+ return ( xErrorBar.is() &&
+ ( xErrorBar->getPropertyValue( "ErrorBarStyle") >>= nStyle ) &&
+ nStyle != css::chart::ErrorBarStyle::NONE );
+}
+
+void StatisticsHelper::removeErrorBars(
+ const Reference< chart2::XDataSeries > & xDataSeries,
+ bool bYError /* = true */ )
+{
+ Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
+ if ( xErrorBar.is())
+ xErrorBar->setPropertyValue( "ErrorBarStyle", uno::Any(
+ css::chart::ErrorBarStyle::NONE ));
+}
+
+bool StatisticsHelper::usesErrorBarRanges(
+ const Reference< chart2::XDataSeries > & xDataSeries,
+ bool bYError /* = true */ )
+{
+ Reference< beans::XPropertySet > xErrorBar( getErrorBars( xDataSeries, bYError ));
+ sal_Int32 nStyle = css::chart::ErrorBarStyle::NONE;
+
+ return ( xErrorBar.is() &&
+ ( xErrorBar->getPropertyValue( "ErrorBarStyle") >>= nStyle ) &&
+ nStyle == css::chart::ErrorBarStyle::FROM_DATA );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/ThreeDHelper.cxx b/chart2/source/tools/ThreeDHelper.cxx
new file mode 100644
index 000000000..06ce49b2e
--- /dev/null
+++ b/chart2/source/tools/ThreeDHelper.cxx
@@ -0,0 +1,1457 @@
+/* -*- 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 <ThreeDHelper.hxx>
+#include <Diagram.hxx>
+#include <DiagramHelper.hxx>
+#include <ChartTypeHelper.hxx>
+#include <ChartType.hxx>
+#include <BaseGFXHelper.hxx>
+#include <DataSeries.hxx>
+#include <DataSeriesHelper.hxx>
+#include <defines.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <rtl/math.hxx>
+
+namespace chart
+{
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::uno::Reference;
+using ::rtl::math::cos;
+using ::rtl::math::sin;
+using ::rtl::math::tan;
+
+namespace
+{
+
+bool lcl_isRightAngledAxesSetAndSupported( const rtl::Reference< Diagram >& xDiagram )
+{
+ if( xDiagram.is() )
+ {
+ bool bRightAngledAxes = false;
+ xDiagram->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
+ if(bRightAngledAxes)
+ {
+ if( ChartTypeHelper::isSupportingRightAngledAxes(
+ DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void lcl_RotateLightSource( const Reference< beans::XPropertySet >& xSceneProperties
+ , const OUString& rLightSourceDirection
+ , const OUString& rLightSourceOn
+ , const ::basegfx::B3DHomMatrix& rRotationMatrix )
+{
+ if( !xSceneProperties.is() )
+ return;
+
+ bool bLightOn = false;
+ if( !(xSceneProperties->getPropertyValue( rLightSourceOn ) >>= bLightOn) )
+ return;
+
+ if( bLightOn )
+ {
+ drawing::Direction3D aLight;
+ if( xSceneProperties->getPropertyValue( rLightSourceDirection ) >>= aLight )
+ {
+ ::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aLight ) );
+ aLightVector = rRotationMatrix*aLightVector;
+
+ xSceneProperties->setPropertyValue( rLightSourceDirection
+ , uno::Any( BaseGFXHelper::B3DVectorToDirection3D( aLightVector ) ) );
+ }
+ }
+}
+
+void lcl_rotateLights( const ::basegfx::B3DHomMatrix& rLightRottion, const Reference< beans::XPropertySet >& xSceneProperties )
+{
+ if(!xSceneProperties.is())
+ return;
+
+ ::basegfx::B3DHomMatrix aLightRottion( rLightRottion );
+ BaseGFXHelper::ReduceToRotationMatrix( aLightRottion );
+
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection1", "D3DSceneLightOn1", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection2", "D3DSceneLightOn2", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection3", "D3DSceneLightOn3", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection4", "D3DSceneLightOn4", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection5", "D3DSceneLightOn5", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection6", "D3DSceneLightOn6", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection7", "D3DSceneLightOn7", aLightRottion );
+ lcl_RotateLightSource( xSceneProperties, "D3DSceneLightDirection8", "D3DSceneLightOn8", aLightRottion );
+}
+
+::basegfx::B3DHomMatrix lcl_getInverseRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
+{
+ ::basegfx::B3DHomMatrix aInverseRotation;
+ double fXAngleRad=0.0;
+ double fYAngleRad=0.0;
+ double fZAngleRad=0.0;
+ ThreeDHelper::getRotationAngleFromDiagram(
+ xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
+ aInverseRotation.rotate( 0.0, 0.0, -fZAngleRad );
+ aInverseRotation.rotate( 0.0, -fYAngleRad, 0.0 );
+ aInverseRotation.rotate( -fXAngleRad, 0.0, 0.0 );
+ return aInverseRotation;
+}
+
+::basegfx::B3DHomMatrix lcl_getCompleteRotationMatrix( const Reference< beans::XPropertySet >& xSceneProperties )
+{
+ ::basegfx::B3DHomMatrix aCompleteRotation;
+ double fXAngleRad=0.0;
+ double fYAngleRad=0.0;
+ double fZAngleRad=0.0;
+ ThreeDHelper::getRotationAngleFromDiagram(
+ xSceneProperties, fXAngleRad, fYAngleRad, fZAngleRad );
+ aCompleteRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
+ return aCompleteRotation;
+}
+
+bool lcl_isEqual( const drawing::Direction3D& rA, const drawing::Direction3D& rB )
+{
+ return ::rtl::math::approxEqual(rA.DirectionX, rB.DirectionX)
+ && ::rtl::math::approxEqual(rA.DirectionY, rB.DirectionY)
+ && ::rtl::math::approxEqual(rA.DirectionZ, rB.DirectionZ);
+}
+
+bool lcl_isLightScheme( const rtl::Reference< Diagram >& xDiagram, bool bRealistic )
+{
+ if(!xDiagram.is())
+ return false;
+
+ bool bIsOn = false;
+ xDiagram->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_2 ) >>= bIsOn;
+ if(!bIsOn)
+ return false;
+
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+
+ sal_Int32 nColor = 0;
+ xDiagram->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTCOLOR_2 ) >>= nColor;
+ if( nColor != ::chart::ChartTypeHelper::getDefaultDirectLightColor( !bRealistic, xChartType ) )
+ return false;
+
+ sal_Int32 nAmbientColor = 0;
+ xDiagram->getPropertyValue( UNO_NAME_3D_SCENE_AMBIENTCOLOR ) >>= nAmbientColor;
+ if( nAmbientColor != ::chart::ChartTypeHelper::getDefaultAmbientLightColor( !bRealistic, xChartType ) )
+ return false;
+
+ drawing::Direction3D aDirection(0,0,0);
+ xDiagram->getPropertyValue( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2 ) >>= aDirection;
+
+ drawing::Direction3D aDefaultDirection( bRealistic
+ ? ChartTypeHelper::getDefaultRealisticLightDirection(xChartType)
+ : ChartTypeHelper::getDefaultSimpleLightDirection(xChartType) );
+
+ //rotate default light direction when right angled axes are off but supported
+ {
+ bool bRightAngledAxes = false;
+ xDiagram->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
+ if(!bRightAngledAxes)
+ {
+ if( ChartTypeHelper::isSupportingRightAngledAxes(
+ DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
+ {
+ ::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagram ) );
+ BaseGFXHelper::ReduceToRotationMatrix( aRotation );
+ ::basegfx::B3DVector aLightVector( BaseGFXHelper::Direction3DToB3DVector( aDefaultDirection ) );
+ aLightVector = aRotation*aLightVector;
+ aDefaultDirection = BaseGFXHelper::B3DVectorToDirection3D( aLightVector );
+ }
+ }
+ }
+
+ return lcl_isEqual( aDirection, aDefaultDirection );
+}
+
+bool lcl_isRealisticLightScheme( const rtl::Reference< Diagram >& xDiagram )
+{
+ return lcl_isLightScheme( xDiagram, true /*bRealistic*/ );
+}
+bool lcl_isSimpleLightScheme( const rtl::Reference< Diagram >& xDiagram )
+{
+ return lcl_isLightScheme( xDiagram, false /*bRealistic*/ );
+}
+void lcl_setLightsForScheme( const rtl::Reference< Diagram >& xDiagram, const ThreeDLookScheme& rScheme )
+{
+ if(!xDiagram.is())
+ return;
+ if( rScheme == ThreeDLookScheme::ThreeDLookScheme_Unknown)
+ return;
+
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_2, uno::Any( true ) );
+
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+ uno::Any aADirection( rScheme == ThreeDLookScheme::ThreeDLookScheme_Simple
+ ? ChartTypeHelper::getDefaultSimpleLightDirection(xChartType)
+ : ChartTypeHelper::getDefaultRealisticLightDirection(xChartType) );
+
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTDIRECTION_2, aADirection );
+ //rotate light direction when right angled axes are off but supported
+ {
+ bool bRightAngledAxes = false;
+ xDiagram->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
+ if(!bRightAngledAxes)
+ {
+ if( ChartTypeHelper::isSupportingRightAngledAxes( xChartType ) )
+ {
+ ::basegfx::B3DHomMatrix aRotation( lcl_getCompleteRotationMatrix( xDiagram ) );
+ BaseGFXHelper::ReduceToRotationMatrix( aRotation );
+ lcl_RotateLightSource( xDiagram, "D3DSceneLightDirection2", "D3DSceneLightOn2", aRotation );
+ }
+ }
+ }
+
+ sal_Int32 nColor = ::chart::ChartTypeHelper::getDefaultDirectLightColor(
+ rScheme == ThreeDLookScheme::ThreeDLookScheme_Simple, xChartType);
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTCOLOR_2, uno::Any( nColor ) );
+
+ sal_Int32 nAmbientColor = ::chart::ChartTypeHelper::getDefaultAmbientLightColor(
+ rScheme == ThreeDLookScheme::ThreeDLookScheme_Simple, xChartType);
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_AMBIENTCOLOR, uno::Any( nAmbientColor ) );
+}
+
+bool lcl_isRealisticScheme( drawing::ShadeMode aShadeMode
+ , sal_Int32 nRoundedEdges
+ , sal_Int32 nObjectLines )
+{
+ if(aShadeMode!=drawing::ShadeMode_SMOOTH)
+ return false;
+ if(nRoundedEdges!=5)
+ return false;
+ if(nObjectLines!=0)
+ return false;
+ return true;
+}
+
+bool lcl_isSimpleScheme( drawing::ShadeMode aShadeMode
+ , sal_Int32 nRoundedEdges
+ , sal_Int32 nObjectLines
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ if(aShadeMode!=drawing::ShadeMode_FLAT)
+ return false;
+ if(nRoundedEdges!=0)
+ return false;
+ if(nObjectLines==0)
+ {
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+ return ChartTypeHelper::noBordersForSimpleScheme( xChartType );
+ }
+ if(nObjectLines!=1)
+ return false;
+ return true;
+}
+
+void lcl_setRealisticScheme( drawing::ShadeMode& rShadeMode
+ , sal_Int32& rnRoundedEdges
+ , sal_Int32& rnObjectLines )
+{
+ rShadeMode = drawing::ShadeMode_SMOOTH;
+ rnRoundedEdges = 5;
+ rnObjectLines = 0;
+}
+
+void lcl_setSimpleScheme( drawing::ShadeMode& rShadeMode
+ , sal_Int32& rnRoundedEdges
+ , sal_Int32& rnObjectLines
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ rShadeMode = drawing::ShadeMode_FLAT;
+ rnRoundedEdges = 0;
+
+ rtl::Reference< ChartType > xChartType( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) );
+ rnObjectLines = ChartTypeHelper::noBordersForSimpleScheme( xChartType ) ? 0 : 1;
+}
+
+} //end anonymous namespace
+
+drawing::CameraGeometry ThreeDHelper::getDefaultCameraGeometry( bool bPie )
+{
+ // ViewReferencePoint (Point on the View plane)
+ drawing::Position3D vrp(17634.6218373783, 10271.4823817647, 24594.8639082739);
+ // ViewPlaneNormal (Normal to the View Plane)
+ drawing::Direction3D vpn(0.416199821709347, 0.173649045905254, 0.892537795986984);
+ // ViewUpVector (determines the v-axis direction on the view plane as
+ // projection of VUP parallel to VPN onto th view pane)
+ drawing::Direction3D vup(-0.0733876362771618, 0.984807599917971, -0.157379306090273);
+
+ if( bPie )
+ {
+ vrp = drawing::Position3D( 0.0, 0.0, 87591.2408759124 );//--> 5 percent perspective
+ vpn = drawing::Direction3D( 0.0, 0.0, 1.0 );
+ vup = drawing::Direction3D( 0.0, 1.0, 0.0 );
+ }
+
+ return drawing::CameraGeometry( vrp, vpn, vup );
+}
+
+namespace
+{
+::basegfx::B3DHomMatrix lcl_getCameraMatrix( const uno::Reference< beans::XPropertySet >& xSceneProperties )
+{
+ drawing::HomogenMatrix aCameraMatrix;
+
+ drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
+ if( xSceneProperties.is() )
+ xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
+
+ ::basegfx::B3DVector aVPN( BaseGFXHelper::Direction3DToB3DVector( aCG.vpn ) );
+ ::basegfx::B3DVector aVUP( BaseGFXHelper::Direction3DToB3DVector( aCG.vup ) );
+
+ //normalize vectors:
+ aVPN.normalize();
+ aVUP.normalize();
+
+ ::basegfx::B3DVector aCross = ::basegfx::cross( aVUP, aVPN );
+
+ //first line is VUP x VPN
+ aCameraMatrix.Line1.Column1 = aCross[0];
+ aCameraMatrix.Line1.Column2 = aCross[1];
+ aCameraMatrix.Line1.Column3 = aCross[2];
+ aCameraMatrix.Line1.Column4 = 0.0;
+
+ //second line is VUP
+ aCameraMatrix.Line2.Column1 = aVUP[0];
+ aCameraMatrix.Line2.Column2 = aVUP[1];
+ aCameraMatrix.Line2.Column3 = aVUP[2];
+ aCameraMatrix.Line2.Column4 = 0.0;
+
+ //third line is VPN
+ aCameraMatrix.Line3.Column1 = aVPN[0];
+ aCameraMatrix.Line3.Column2 = aVPN[1];
+ aCameraMatrix.Line3.Column3 = aVPN[2];
+ aCameraMatrix.Line3.Column4 = 0.0;
+
+ //fourth line is 0 0 0 1
+ aCameraMatrix.Line4.Column1 = 0.0;
+ aCameraMatrix.Line4.Column2 = 0.0;
+ aCameraMatrix.Line4.Column3 = 0.0;
+ aCameraMatrix.Line4.Column4 = 1.0;
+
+ return BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aCameraMatrix );
+}
+
+double lcl_shiftAngleToIntervalMinusPiToPi( double fAngleRad )
+{
+ //valid range: ]-Pi,Pi]
+ while( fAngleRad<=-M_PI )
+ fAngleRad+=(2*M_PI);
+ while( fAngleRad>M_PI )
+ fAngleRad-=(2*M_PI);
+ return fAngleRad;
+}
+
+void lcl_ensureIntervalMinus1To1( double& rSinOrCos )
+{
+ if (rSinOrCos < -1.0)
+ rSinOrCos = -1.0;
+ else if (rSinOrCos > 1.0)
+ rSinOrCos = 1.0;
+}
+
+bool lcl_isSinZero( double fAngleRad )
+{
+ return ::basegfx::fTools::equalZero( sin(fAngleRad), 0.0000001 );
+}
+bool lcl_isCosZero( double fAngleRad )
+{
+ return ::basegfx::fTools::equalZero( cos(fAngleRad), 0.0000001 );
+}
+
+}
+
+void ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
+ sal_Int32 nElevationDeg, sal_Int32 nRotationDeg,
+ double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad)
+{
+ // for a description of the algorithm see issue 72994
+ //https://bz.apache.org/ooo/show_bug.cgi?id=72994
+ //https://bz.apache.org/ooo/attachment.cgi?id=50608
+
+ nElevationDeg = NormAngle360(nElevationDeg);
+ nRotationDeg = NormAngle360(nRotationDeg);
+
+ double& x = rfXAngleRad;
+ double& y = rfYAngleRad;
+ double& z = rfZAngleRad;
+
+ double E = basegfx::deg2rad(nElevationDeg); //elevation in Rad
+ double R = basegfx::deg2rad(nRotationDeg); //rotation in Rad
+
+ if( (nRotationDeg == 0 || nRotationDeg == 180 )
+ && ( nElevationDeg == 90 || nElevationDeg == 270 ) )
+ {
+ //sR==0 && cE==0
+ z = 0.0;
+ //element 23
+ double f23 = cos(R)*sin(E);
+ if(f23>0)
+ x = M_PI_2;
+ else
+ x = -M_PI_2;
+ y = R;
+ }
+ else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
+ && ( nElevationDeg == 90 || nElevationDeg == 270 ) )
+ {
+ //cR==0 && cE==0
+ z = M_PI_2;
+ if( sin(R)>0 )
+ x = M_PI_2;
+ else
+ x = -M_PI_2;
+
+ if( (sin(R)*sin(E))>0 )
+ y = 0.0;
+ else
+ y = M_PI;
+ }
+ else if( (nRotationDeg == 0 || nRotationDeg == 180 )
+ && ( nElevationDeg == 0 || nElevationDeg == 180 ) )
+ {
+ //sR==0 && sE==0
+ z = 0.0;
+ y = R;
+ x = E;
+ }
+ else if( ( nRotationDeg == 90 || nRotationDeg == 270 )
+ && ( nElevationDeg == 0 || nElevationDeg == 180 ) )
+ {
+ //cR==0 && sE==0
+ z = 0.0;
+
+ if( (sin(R)/cos(E))>0 )
+ y = M_PI_2;
+ else
+ y = -M_PI_2;
+
+ if( (cos(E))>0 )
+ x = 0;
+ else
+ x = M_PI;
+ }
+ else if ( nElevationDeg == 0 || nElevationDeg == 180 )
+ {
+ //sR!=0 cR!=0 sE==0
+ z = 0.0;
+ x = E;
+ y = R;
+ //use element 13 for sign
+ if((cos(x)*sin(y)*sin(R))<0.0)
+ y *= -1.0;
+ }
+ else if ( nElevationDeg == 90 || nElevationDeg == 270 )
+ {
+ //sR!=0 cR!=0 cE==0
+ //element 12 + 22 --> y=0 or M_PI and x=+-M_PI/2
+ //-->element 13/23:
+ z = atan(sin(R)/(cos(R)*sin(E)));
+ //use element 13 for sign for x
+ if( (sin(R)*sin(z))>0.0 )
+ x = M_PI_2;
+ else
+ x = -M_PI_2;
+ //use element 21 for y
+ if( (sin(R)*sin(E)*sin(z))>0.0)
+ y = 0.0;
+ else
+ y = M_PI;
+ }
+ else if ( nRotationDeg == 0 || nRotationDeg == 180 )
+ {
+ //sE!=0 cE!=0 sR==0
+ z = 0.0;
+ x = E;
+ y = R;
+ double f23 = cos(R)*sin(E);
+ if( (f23 * sin(x)) < 0.0 )
+ x *= -1.0; //todo ??
+ }
+ else if (nRotationDeg == 90 || nRotationDeg == 270)
+ {
+ //sE!=0 cE!=0 cR==0
+ //z = +- M_PI/2;
+ //x = +- M_PI/2;
+ z = M_PI_2;
+ x = M_PI_2;
+ double sR = sin(R);
+ if( sR<0.0 )
+ x *= -1.0; //different signs for x and z
+
+ //use element 21:
+ double cy = sR*sin(E)/sin(z);
+ lcl_ensureIntervalMinus1To1(cy);
+ y = acos(cy);
+
+ //use element 22 for sign:
+ if( (sin(x)*sin(y)*sin(z)*cos(E))<0.0)
+ y *= -1.0;
+ }
+ else
+ {
+ z = atan(tan(R) * sin(E));
+ if(cos(z)==0.0)
+ {
+ OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
+ return;
+ }
+ double cy = cos(R)/cos(z);
+ lcl_ensureIntervalMinus1To1(cy);
+ y = acos(cy);
+
+ //element 12 in 23
+ double fDenominator = cos(z)*(1.0-pow(sin(y),2));
+ if(fDenominator==0.0)
+ {
+ OSL_FAIL("calculation error in ThreeDHelper::convertElevationRotationDegToXYZAngleRad");
+ return;
+ }
+ double sx = cos(R)*sin(E)/fDenominator;
+ lcl_ensureIntervalMinus1To1(sx);
+ x = asin( sx );
+
+ //use element 13 for sign:
+ double f13a = cos(x)*cos(z)*sin(y);
+ double f13b = sin(R)-sx*sin(z);
+ if( (f13b*f13a)<0.0 )
+ {
+ //change x or y
+ //use element 22 for further investigations:
+ //try
+ y *= -1;
+ double f22a = cos(x)*cos(z);
+ double f22b = cos(E)-(sx*sin(y)*sin(z));
+ if( (f22a*f22b)<0.0 )
+ {
+ y *= -1;
+ x=(M_PI-x);
+ }
+ }
+ else
+ {
+ //change nothing or both
+ //use element 22 for further investigations:
+ double f22a = cos(x)*cos(z);
+ double f22b = cos(E)-(sx*sin(y)*sin(z));
+ if( (f22a*f22b)<0.0 )
+ {
+ y *= -1;
+ x=(M_PI-x);
+ }
+ }
+ }
+}
+
+void ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
+ sal_Int32& rnElevationDeg, sal_Int32& rnRotationDeg,
+ double fXRad, double fYRad, double fZRad)
+{
+ // for a description of the algorithm see issue 72994
+ //https://bz.apache.org/ooo/show_bug.cgi?id=72994
+ //https://bz.apache.org/ooo/attachment.cgi?id=50608
+
+ double R = 0.0; //Rotation in Rad
+ double E = 0.0; //Elevation in Rad
+
+ double& x = fXRad;
+ double& y = fYRad;
+ double& z = fZRad;
+
+ double f11 = cos(y)*cos(z);
+
+ if( lcl_isSinZero(y) )
+ {
+ //siny == 0
+
+ if( lcl_isCosZero(x) )
+ {
+ //siny == 0 && cosx == 0
+
+ if( lcl_isSinZero(z) )
+ {
+ //siny == 0 && cosx == 0 && sinz == 0
+ //example: x=+-90 y=0oder180 z=0(oder180)
+
+ //element 13+11
+ if( f11 > 0 )
+ R = 0.0;
+ else
+ R = M_PI;
+
+ //element 23
+ double f23 = cos(z)*sin(x) / cos(R);
+ if( f23 > 0 )
+ E = M_PI_2;
+ else
+ E = -M_PI_2;
+ }
+ else if( lcl_isCosZero(z) )
+ {
+ //siny == 0 && cosx == 0 && cosz == 0
+ //example: x=+-90 y=0oder180 z=+-90
+
+ double f13 = sin(x)*sin(z);
+ //element 13+11
+ if( f13 > 0 )
+ R = M_PI_2;
+ else
+ R = -M_PI_2;
+
+ //element 21
+ double f21 = cos(y)*sin(z) / sin(R);
+ if( f21 > 0 )
+ E = M_PI_2;
+ else
+ E = -M_PI_2;
+ }
+ else
+ {
+ //siny == 0 && cosx == 0 && cosz != 0 && sinz != 0
+ //element 11 && 13
+ double f13 = sin(x)*sin(z);
+ R = atan( f13/f11 );
+
+ if(f11<0)
+ R+=M_PI;
+
+ //element 23
+ double f23 = cos(z)*sin(x);
+ if( f23/cos(R) > 0 )
+ E = M_PI_2;
+ else
+ E = -M_PI_2;
+ }
+ }
+ else if( lcl_isSinZero(x) )
+ {
+ //sinY==0 sinX==0
+ //element 13+11
+ if( f11 > 0 )
+ R = 0.0;
+ else
+ R = M_PI;
+
+ double f22 = cos(x)*cos(z);
+ if( f22 > 0 )
+ E = 0.0;
+ else
+ E = M_PI;
+ }
+ else if( lcl_isSinZero(z) )
+ {
+ //sinY==0 sinZ==0 sinx!=0 cosx!=0
+ //element 13+11
+ if( f11 > 0 )
+ R = 0.0;
+ else
+ R = M_PI;
+
+ //element 22 && 23
+ double f22 = cos(x)*cos(z);
+ double f23 = cos(z)*sin(x);
+ E = atan( f23/(f22*cos(R)) );
+ if( (f22*cos(E))<0 )
+ E+=M_PI;
+ }
+ else if( lcl_isCosZero(z) )
+ {
+ //sinY == 0 && cosZ == 0 && cosx != 0 && sinx != 0
+ double f13 = sin(x)*sin(z);
+ //element 13+11
+ if( f13 > 0 )
+ R = M_PI_2;
+ else
+ R = -M_PI_2;
+
+ //element 21+22
+ double f21 = cos(y)*sin(z);
+ if( f21/sin(R) > 0 )
+ E = M_PI_2;
+ else
+ E = -M_PI_2;
+ }
+ else
+ {
+ //sinY == 0 && all other !=0
+ double f13 = sin(x)*sin(z);
+ R = atan( f13/f11 );
+ if( (f11*cos(R))<0.0 )
+ R+=M_PI;
+
+ double f22 = cos(x)*cos(z);
+ if( !lcl_isCosZero(R) )
+ E = atan( cos(z)*sin(x) /( f22*cos(R) ) );
+ else
+ E = atan( cos(y)*sin(z) /( f22*sin(R) ) );
+ if( (f22*cos(E))<0 )
+ E+=M_PI;
+ }
+ }
+ else if( lcl_isCosZero(y) )
+ {
+ //cosY==0
+
+ double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
+ if( f13 >= 0 )
+ R = M_PI_2;
+ else
+ R = -M_PI_2;
+
+ double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
+ if( f22 >= 0 )
+ E = 0.0;
+ else
+ E = M_PI;
+ }
+ else if( lcl_isSinZero(x) )
+ {
+ //cosY!=0 sinY!=0 sinX=0
+ if( lcl_isSinZero(z) )
+ {
+ //cosY!=0 sinY!=0 sinX=0 sinZ=0
+ double f13 = cos(x)*cos(z)*sin(y);
+ R = atan( f13/f11 );
+ //R = asin(f13);
+ if( f11<0 )
+ R+=M_PI;
+
+ double f22 = cos(x)*cos(z);
+ if( f22>0 )
+ E = 0.0;
+ else
+ E = M_PI;
+ }
+ else if( lcl_isCosZero(z) )
+ {
+ //cosY!=0 sinY!=0 sinX=0 cosZ=0
+ R = x;
+ E = y;//or -y
+ //use 23 for 'signs'
+ double f23 = -1.0*cos(x)*sin(y)*sin(z);
+ if( (f23*cos(R)*sin(E))<0.0 )
+ {
+ //change R or E
+ E = -y;
+ }
+ }
+ else
+ {
+ //cosY!=0 sinY!=0 sinX=0 sinZ!=0 cosZ!=0
+ double f13 = cos(x)*cos(z)*sin(y);
+ R = atan( f13/f11 );
+
+ if( f11<0 )
+ R+=M_PI;
+
+ double f21 = cos(y)*sin(z);
+ double f22 = cos(x)*cos(z);
+ E = atan(f21/(f22*sin(R)) );
+
+ if( (f22*cos(E))<0.0 )
+ E+=M_PI;
+ }
+ }
+ else if( lcl_isCosZero(x) )
+ {
+ //cosY!=0 sinY!=0 cosX=0
+
+ if( lcl_isSinZero(z) )
+ {
+ //cosY!=0 sinY!=0 cosX=0 sinZ=0
+ R=0;//13 -> R=0 or M_PI
+ if( f11<0.0 )
+ R=M_PI;
+ E=M_PI_2;//22 -> E=+-M_PI/2
+ //use element 11 and 23 for sign
+ double f23 = cos(z)*sin(x);
+ if( (f11*f23*sin(E))<0.0 )
+ E=-M_PI_2;
+ }
+ else if( lcl_isCosZero(z) )
+ {
+ //cosY!=0 sinY!=0 cosX=0 cosZ=0
+ //element 11 & 13:
+ if( (sin(x)*sin(z))>0.0 )
+ R=M_PI_2;
+ else
+ R=-M_PI_2;
+ //element 22:
+ E=acos( sin(x)*sin(y)*sin(z));
+ //use element 21 for sign:
+ if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
+ E*=-1.0;
+ }
+ else
+ {
+ //cosY!=0 sinY!=0 cosX=0 sinZ!=0 cosZ!=0
+ //element 13/11
+ R = atan( sin(x)*sin(z)/(cos(y)*cos(z)) );
+ //use 13 for 'sign'
+ if( (sin(x)*sin(z))<0.0 )
+ R += M_PI;
+ //element 22
+ E = acos(sin(x)*sin(y)*sin(z) );
+ //use 21 for sign
+ if( (cos(y)*sin(z)*sin(R)*sin(E))<0.0 )
+ E*=-1.0;
+ }
+ }
+ else if( lcl_isSinZero(z) )
+ {
+ //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ=0
+ //element 11
+ R=y;
+ //use element 13 for sign
+ if( (cos(x)*cos(z)*sin(y)*sin(R))<0.0 )
+ R*=-1.0;
+ //element 22
+ E = acos( cos(x)*cos(z) );
+ //use element 23 for sign
+ if( (cos(z)*sin(x)*cos(R)*sin(E))<0.0 )
+ E*=-1.0;
+ }
+ else if( lcl_isCosZero(z) )
+ {
+ //cosY!=0 sinY!=0 sinX!=0 cosX!=0 cosZ=0
+ //element 21/23
+ R=atan(-cos(y)/(cos(x)*sin(y)));
+ //use element 13 for 'sign'
+ if( (sin(x)*sin(z)*sin(R))<0.0 )
+ R+=M_PI;
+ //element 21/22
+ E=atan( cos(y)*sin(z)/(sin(R)*sin(x)*sin(y)*sin(z)) );
+ //use element 23 for 'sign'
+ if( (-cos(x)*sin(y)*sin(z)*cos(R)*sin(E))<0.0 )
+ E+=M_PI;
+ }
+ else
+ {
+ //cosY!=0 sinY!=0 sinX!=0 cosX!=0 sinZ!=0 cosZ!=0
+ //13/11:
+ double f13 = sin(x)*sin(z)+cos(x)*cos(z)*sin(y);
+ R = atan( f13/ f11 );
+ if(f11<0.0)
+ R+=M_PI;
+ double f22 = cos(x)*cos(z)+sin(x)*sin(y)*sin(z);
+ double f23 = cos(x)*sin(y)*sin(z)-cos(z)*sin(x);
+ //23/22:
+ E = atan( -1.0*f23/(f22*cos(R)) );
+ if(f22<0.0)
+ E+=M_PI;
+ }
+
+ rnElevationDeg = basegfx::fround(basegfx::rad2deg(E));
+ rnRotationDeg = basegfx::fround(basegfx::rad2deg(R));
+}
+
+double ThreeDHelper::getValueClippedToRange( double fAngle, const double& fPositivLimit )
+{
+ if( fAngle<-1*fPositivLimit )
+ fAngle=-1*fPositivLimit;
+ else if( fAngle>fPositivLimit )
+ fAngle=fPositivLimit;
+ return fAngle;
+}
+
+void ThreeDHelper::adaptRadAnglesForRightAngledAxes( double& rfXAngleRad, double& rfYAngleRad )
+{
+ rfXAngleRad = ThreeDHelper::getValueClippedToRange(rfXAngleRad, basegfx::deg2rad(ThreeDHelper::getXDegreeAngleLimitForRightAngledAxes()) );
+ rfYAngleRad = ThreeDHelper::getValueClippedToRange(rfYAngleRad, basegfx::deg2rad(ThreeDHelper::getYDegreeAngleLimitForRightAngledAxes()) );
+}
+
+void ThreeDHelper::getRotationAngleFromDiagram(
+ const Reference< beans::XPropertySet >& xSceneProperties, double& rfXAngleRad, double& rfYAngleRad, double& rfZAngleRad )
+{
+ //takes the camera and the transformation matrix into account
+
+ rfXAngleRad = rfYAngleRad = rfZAngleRad = 0.0;
+
+ if( !xSceneProperties.is() )
+ return;
+
+ //get camera rotation
+ ::basegfx::B3DHomMatrix aFixCameraRotationMatrix( lcl_getCameraMatrix( xSceneProperties ) );
+ BaseGFXHelper::ReduceToRotationMatrix( aFixCameraRotationMatrix );
+
+ //get scene rotation
+ ::basegfx::B3DHomMatrix aSceneRotation;
+ {
+ drawing::HomogenMatrix aHomMatrix;
+ if( xSceneProperties->getPropertyValue( "D3DTransformMatrix") >>= aHomMatrix )
+ {
+ aSceneRotation = BaseGFXHelper::HomogenMatrixToB3DHomMatrix( aHomMatrix );
+ BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
+ }
+ }
+
+ ::basegfx::B3DHomMatrix aResultRotation = aFixCameraRotationMatrix * aSceneRotation;
+ ::basegfx::B3DTuple aRotation( BaseGFXHelper::GetRotationFromMatrix( aResultRotation ) );
+
+ rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getX());
+ rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getY());
+ rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(aRotation.getZ());
+
+ if(rfZAngleRad<-M_PI_2 || rfZAngleRad>M_PI_2)
+ {
+ rfZAngleRad-=M_PI;
+ rfXAngleRad-=M_PI;
+ rfYAngleRad=(M_PI-rfYAngleRad);
+
+ rfXAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfXAngleRad);
+ rfYAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfYAngleRad);
+ rfZAngleRad = lcl_shiftAngleToIntervalMinusPiToPi(rfZAngleRad);
+ }
+}
+
+void ThreeDHelper::switchRightAngledAxes( const Reference< beans::XPropertySet >& xSceneProperties, bool bRightAngledAxes )
+{
+ try
+ {
+ if( xSceneProperties.is() )
+ {
+ bool bOldRightAngledAxes = false;
+ xSceneProperties->getPropertyValue( "RightAngledAxes") >>= bOldRightAngledAxes;
+ if( bOldRightAngledAxes!=bRightAngledAxes)
+ {
+ xSceneProperties->setPropertyValue( "RightAngledAxes", uno::Any( bRightAngledAxes ));
+ if(bRightAngledAxes)
+ {
+ ::basegfx::B3DHomMatrix aInverseRotation( lcl_getInverseRotationMatrix( xSceneProperties ) );
+ lcl_rotateLights( aInverseRotation, xSceneProperties );
+ }
+ else
+ {
+ ::basegfx::B3DHomMatrix aCompleteRotation( lcl_getCompleteRotationMatrix( xSceneProperties ) );
+ lcl_rotateLights( aCompleteRotation, xSceneProperties );
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void ThreeDHelper::setRotationAngleToDiagram(
+ const rtl::Reference< Diagram >& xDiagram
+ , double fXAngleRad, double fYAngleRad, double fZAngleRad )
+{
+ //the rotation of the camera is not touched but taken into account
+ //the rotation difference is applied to the transformation matrix
+
+ //the light sources will be adapted also
+
+ if( !xDiagram.is() )
+ return;
+
+ try
+ {
+ //remind old rotation for adaptation of light directions
+ ::basegfx::B3DHomMatrix aInverseOldRotation( lcl_getInverseRotationMatrix( xDiagram ) );
+
+ ::basegfx::B3DHomMatrix aInverseCameraRotation;
+ {
+ ::basegfx::B3DTuple aR( BaseGFXHelper::GetRotationFromMatrix(
+ lcl_getCameraMatrix( xDiagram ) ) );
+ aInverseCameraRotation.rotate( 0.0, 0.0, -aR.getZ() );
+ aInverseCameraRotation.rotate( 0.0, -aR.getY(), 0.0 );
+ aInverseCameraRotation.rotate( -aR.getX(), 0.0, 0.0 );
+ }
+
+ ::basegfx::B3DHomMatrix aCumulatedRotation;
+ aCumulatedRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
+
+ //calculate new scene matrix
+ ::basegfx::B3DHomMatrix aSceneRotation = aInverseCameraRotation*aCumulatedRotation;
+ BaseGFXHelper::ReduceToRotationMatrix( aSceneRotation );
+
+ //set new rotation to transformation matrix
+ xDiagram->setPropertyValue(
+ "D3DTransformMatrix", uno::Any( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
+
+ //rotate lights if RightAngledAxes are not set or not supported
+ bool bRightAngledAxes = false;
+ xDiagram->getPropertyValue( "RightAngledAxes") >>= bRightAngledAxes;
+ if(!bRightAngledAxes || !ChartTypeHelper::isSupportingRightAngledAxes(
+ DiagramHelper::getChartTypeByIndex( xDiagram, 0 ) ) )
+ {
+ ::basegfx::B3DHomMatrix aNewRotation;
+ aNewRotation.rotate( fXAngleRad, fYAngleRad, fZAngleRad );
+ lcl_rotateLights( aNewRotation*aInverseOldRotation, xDiagram );
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+void ThreeDHelper::getRotationFromDiagram( const rtl::Reference< Diagram >& xSceneProperties
+ , sal_Int32& rnHorizontalAngleDegree, sal_Int32& rnVerticalAngleDegree )
+{
+ double fXAngle, fYAngle, fZAngle;
+ ThreeDHelper::getRotationAngleFromDiagram( xSceneProperties, fXAngle, fYAngle, fZAngle );
+
+ if( !lcl_isRightAngledAxesSetAndSupported( xSceneProperties ) )
+ {
+ ThreeDHelper::convertXYZAngleRadToElevationRotationDeg(
+ rnHorizontalAngleDegree, rnVerticalAngleDegree, fXAngle, fYAngle, fZAngle);
+ rnVerticalAngleDegree*=-1;
+ }
+ else
+ {
+ rnHorizontalAngleDegree = basegfx::fround(basegfx::rad2deg(fXAngle));
+ rnVerticalAngleDegree = basegfx::fround(-1.0 * basegfx::rad2deg(fYAngle));
+ // nZRotation = basegfx::fround(-1.0 * basegfx::rad2deg(fZAngle));
+ }
+
+ rnHorizontalAngleDegree = NormAngle180(rnHorizontalAngleDegree);
+ rnVerticalAngleDegree = NormAngle180(rnVerticalAngleDegree);
+}
+
+void ThreeDHelper::setRotationToDiagram( const rtl::Reference< Diagram >& xDiagram
+ , sal_Int32 nHorizontalAngleDegree, sal_Int32 nVerticalYAngleDegree )
+{
+ //todo: x and y is not equal to horz and vert in case of RightAngledAxes==false
+ double fXAngle = basegfx::deg2rad(nHorizontalAngleDegree);
+ double fYAngle = basegfx::deg2rad(-1 * nVerticalYAngleDegree);
+ double fZAngle = 0.0;
+
+ if( !lcl_isRightAngledAxesSetAndSupported( xDiagram ) )
+ ThreeDHelper::convertElevationRotationDegToXYZAngleRad(
+ nHorizontalAngleDegree, -1*nVerticalYAngleDegree, fXAngle, fYAngle, fZAngle );
+
+ ThreeDHelper::setRotationAngleToDiagram( xDiagram, fXAngle, fYAngle, fZAngle );
+}
+
+void ThreeDHelper::getCameraDistanceRange( double& rfMinimumDistance, double& rfMaximumDistance )
+{
+ rfMinimumDistance = 3.0/4.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
+ rfMaximumDistance = 20.0*FIXED_SIZE_FOR_3D_CHART_VOLUME;//empiric value
+}
+
+void ThreeDHelper::ensureCameraDistanceRange( double& rfCameraDistance )
+{
+ double fMin, fMax;
+ getCameraDistanceRange( fMin, fMax );
+ if( rfCameraDistance < fMin )
+ rfCameraDistance = fMin;
+ if( rfCameraDistance > fMax )
+ rfCameraDistance = fMax;
+}
+
+double ThreeDHelper::getCameraDistance(
+ const Reference< beans::XPropertySet >& xSceneProperties )
+{
+ double fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
+
+ if( !xSceneProperties.is() )
+ return fCameraDistance;
+
+ try
+ {
+ drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
+ xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
+ ::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
+ fCameraDistance = aVRP.getLength();
+
+ ensureCameraDistanceRange( fCameraDistance );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return fCameraDistance;
+}
+
+void ThreeDHelper::setCameraDistance(
+ const Reference< beans::XPropertySet >& xSceneProperties, double fCameraDistance )
+{
+ if( !xSceneProperties.is() )
+ return;
+
+ try
+ {
+ if( fCameraDistance <= 0 )
+ fCameraDistance = FIXED_SIZE_FOR_3D_CHART_VOLUME;
+
+ drawing::CameraGeometry aCG( ThreeDHelper::getDefaultCameraGeometry() );
+ xSceneProperties->getPropertyValue( "D3DCameraGeometry" ) >>= aCG;
+ ::basegfx::B3DVector aVRP( BaseGFXHelper::Position3DToB3DVector( aCG.vrp ) );
+ if( ::basegfx::fTools::equalZero( aVRP.getLength() ) )
+ aVRP = ::basegfx::B3DVector(0,0,1);
+ aVRP.setLength(fCameraDistance);
+ aCG.vrp = BaseGFXHelper::B3DVectorToPosition3D( aVRP );
+
+ xSceneProperties->setPropertyValue( "D3DCameraGeometry", uno::Any( aCG ));
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+double ThreeDHelper::CameraDistanceToPerspective( double fCameraDistance )
+{
+ double fMin, fMax;
+ ThreeDHelper::getCameraDistanceRange( fMin, fMax );
+ //fMax <-> 0; fMin <->100
+ //a/x + b = y
+ double a = 100.0*fMax*fMin/(fMax-fMin);
+ double b = -a/fMax;
+
+ double fRet = a/fCameraDistance + b;
+
+ return fRet;
+}
+
+double ThreeDHelper::PerspectiveToCameraDistance( double fPerspective )
+{
+ double fMin, fMax;
+ ThreeDHelper::getCameraDistanceRange( fMin, fMax );
+ //fMax <-> 0; fMin <->100
+ //a/x + b = y
+ double a = 100.0*fMax*fMin/(fMax-fMin);
+ double b = -a/fMax;
+
+ double fRet = a/(fPerspective - b);
+
+ return fRet;
+}
+
+ThreeDLookScheme ThreeDHelper::detectScheme( const rtl::Reference< Diagram >& xDiagram )
+{
+ ThreeDLookScheme aScheme = ThreeDLookScheme::ThreeDLookScheme_Unknown;
+
+ sal_Int32 nRoundedEdges;
+ sal_Int32 nObjectLines;
+ ThreeDHelper::getRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
+
+ //get shade mode and light settings:
+ drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
+ try
+ {
+ if( xDiagram.is() )
+ xDiagram->getPropertyValue( "D3DSceneShadeMode" )>>= aShadeMode;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ if( lcl_isSimpleScheme( aShadeMode, nRoundedEdges, nObjectLines, xDiagram ) )
+ {
+ if( lcl_isSimpleLightScheme(xDiagram) )
+ aScheme = ThreeDLookScheme::ThreeDLookScheme_Simple;
+ }
+ else if( lcl_isRealisticScheme( aShadeMode, nRoundedEdges, nObjectLines ) )
+ {
+ if( lcl_isRealisticLightScheme(xDiagram) )
+ aScheme = ThreeDLookScheme::ThreeDLookScheme_Realistic;
+ }
+
+ return aScheme;
+}
+
+void ThreeDHelper::setScheme( const rtl::Reference< Diagram >& xDiagram, ThreeDLookScheme aScheme )
+{
+ if( aScheme == ThreeDLookScheme::ThreeDLookScheme_Unknown )
+ return;
+
+ drawing::ShadeMode aShadeMode;
+ sal_Int32 nRoundedEdges;
+ sal_Int32 nObjectLines;
+
+ if( aScheme == ThreeDLookScheme::ThreeDLookScheme_Simple )
+ lcl_setSimpleScheme(aShadeMode,nRoundedEdges,nObjectLines,xDiagram);
+ else
+ lcl_setRealisticScheme(aShadeMode,nRoundedEdges,nObjectLines);
+
+ try
+ {
+ ThreeDHelper::setRoundedEdgesAndObjectLines( xDiagram, nRoundedEdges, nObjectLines );
+
+ if( xDiagram.is() )
+ {
+ drawing::ShadeMode aOldShadeMode;
+ if( ! ( (xDiagram->getPropertyValue( "D3DSceneShadeMode" )>>=aOldShadeMode) &&
+ aOldShadeMode == aShadeMode ))
+ {
+ xDiagram->setPropertyValue( "D3DSceneShadeMode", uno::Any( aShadeMode ));
+ }
+ }
+
+ lcl_setLightsForScheme( xDiagram, aScheme );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+}
+
+void ThreeDHelper::set3DSettingsToDefault( const rtl::Reference< Diagram >& xDiagram )
+{
+ if(xDiagram.is())
+ {
+ xDiagram->setPropertyToDefault( "D3DSceneDistance");
+ xDiagram->setPropertyToDefault( "D3DSceneFocalLength");
+ }
+ ThreeDHelper::setDefaultRotation( xDiagram );
+ ThreeDHelper::setDefaultIllumination( xDiagram );
+}
+
+void ThreeDHelper::setDefaultRotation( const uno::Reference< beans::XPropertySet >& xSceneProperties, bool bPieOrDonut )
+{
+ if( !xSceneProperties.is() )
+ return;
+
+ drawing::CameraGeometry aCameraGeo( ThreeDHelper::getDefaultCameraGeometry( bPieOrDonut ) );
+ xSceneProperties->setPropertyValue( "D3DCameraGeometry", uno::Any( aCameraGeo ));
+
+ ::basegfx::B3DHomMatrix aSceneRotation;
+ if( bPieOrDonut )
+ aSceneRotation.rotate( -M_PI/3.0, 0, 0 );
+ xSceneProperties->setPropertyValue( "D3DTransformMatrix",
+ uno::Any( BaseGFXHelper::B3DHomMatrixToHomogenMatrix( aSceneRotation )));
+}
+
+void ThreeDHelper::setDefaultRotation( const rtl::Reference< Diagram >& xDiagram )
+{
+ bool bPieOrDonut( DiagramHelper::isPieOrDonutChart( xDiagram ) );
+ ThreeDHelper::setDefaultRotation( xDiagram, bPieOrDonut );
+}
+
+void ThreeDHelper::setDefaultIllumination( const rtl::Reference<::chart::Diagram>& xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ drawing::ShadeMode aShadeMode( drawing::ShadeMode_SMOOTH );
+ try
+ {
+ xDiagram->getPropertyValue( "D3DSceneShadeMode" )>>= aShadeMode;
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_1, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_3, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_4, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_5, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_6, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_7, uno::Any( false ) );
+ xDiagram->setPropertyValue( UNO_NAME_3D_SCENE_LIGHTON_8, uno::Any( false ) );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ ThreeDLookScheme aScheme = (aShadeMode == drawing::ShadeMode_FLAT)
+ ? ThreeDLookScheme::ThreeDLookScheme_Simple
+ : ThreeDLookScheme::ThreeDLookScheme_Realistic;
+ lcl_setLightsForScheme( xDiagram, aScheme );
+}
+
+void ThreeDHelper::getRoundedEdgesAndObjectLines(
+ const rtl::Reference< Diagram > & xDiagram
+ , sal_Int32& rnRoundedEdges, sal_Int32& rnObjectLines )
+{
+ rnRoundedEdges = -1;
+ rnObjectLines = -1;
+ try
+ {
+ bool bDifferentRoundedEdges = false;
+ bool bDifferentObjectLines = false;
+
+ drawing::LineStyle aLineStyle( drawing::LineStyle_SOLID );
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesList =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+ sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
+
+ OUString aPercentDiagonalPropertyName( "PercentDiagonal" );
+ OUString aBorderStylePropertyName( "BorderStyle" );
+
+ for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
+ {
+ rtl::Reference< DataSeries > xSeries( aSeriesList[nS] );
+ if(!nS)
+ {
+ rnRoundedEdges = 0;
+ try
+ {
+ sal_Int16 nPercentDiagonal = 0;
+
+ xSeries->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
+ rnRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
+
+ if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
+ , aPercentDiagonalPropertyName, uno::Any(nPercentDiagonal) ) )
+ bDifferentRoundedEdges = true;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ bDifferentRoundedEdges = true;
+ }
+ try
+ {
+ xSeries->getPropertyValue( aBorderStylePropertyName ) >>= aLineStyle;
+
+ if( DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
+ , aBorderStylePropertyName, uno::Any(aLineStyle) ) )
+ bDifferentObjectLines = true;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ bDifferentObjectLines = true;
+ }
+ }
+ else
+ {
+ if( !bDifferentRoundedEdges )
+ {
+ sal_Int16 nPercentDiagonal = 0;
+ xSeries->getPropertyValue( aPercentDiagonalPropertyName ) >>= nPercentDiagonal;
+ sal_Int32 nCurrentRoundedEdges = static_cast< sal_Int32 >( nPercentDiagonal );
+ if(nCurrentRoundedEdges!=rnRoundedEdges
+ || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
+ , aPercentDiagonalPropertyName, uno::Any( static_cast< sal_Int16 >(rnRoundedEdges) ) ) )
+ {
+ bDifferentRoundedEdges = true;
+ }
+ }
+
+ if( !bDifferentObjectLines )
+ {
+ drawing::LineStyle aCurrentLineStyle;
+ xSeries->getPropertyValue( aBorderStylePropertyName ) >>= aCurrentLineStyle;
+ if(aCurrentLineStyle!=aLineStyle
+ || DataSeriesHelper::hasAttributedDataPointDifferentValue( xSeries
+ , aBorderStylePropertyName, uno::Any(aLineStyle) ) )
+ bDifferentObjectLines = true;
+ }
+ }
+ if( bDifferentRoundedEdges && bDifferentObjectLines )
+ break;
+ }
+
+ //set rnObjectLines
+ rnObjectLines = 0;
+ if( bDifferentObjectLines )
+ rnObjectLines = -1;
+ else if( aLineStyle == drawing::LineStyle_SOLID )
+ rnObjectLines = 1;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+void ThreeDHelper::setRoundedEdgesAndObjectLines(
+ const rtl::Reference< Diagram > & xDiagram
+ , sal_Int32 nRoundedEdges, sal_Int32 nObjectLines )
+{
+ if( (nRoundedEdges<0||nRoundedEdges>100) && nObjectLines!=0 && nObjectLines!=1 )
+ return;
+
+ drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
+ if(nObjectLines==1)
+ aLineStyle = drawing::LineStyle_SOLID;
+
+ uno::Any aALineStyle( aLineStyle);
+ uno::Any aARoundedEdges( static_cast< sal_Int16 >( nRoundedEdges ));
+
+ std::vector< rtl::Reference< DataSeries > > aSeriesList =
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram );
+ sal_Int32 nSeriesCount = static_cast<sal_Int32>( aSeriesList.size() );
+ for( sal_Int32 nS = 0; nS < nSeriesCount; ++nS )
+ {
+ rtl::Reference< DataSeries > xSeries( aSeriesList[nS] );
+
+ if( nRoundedEdges>=0 && nRoundedEdges<=100 )
+ DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, "PercentDiagonal", aARoundedEdges );
+
+ if( nObjectLines==0 || nObjectLines==1 )
+ DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, "BorderStyle", aALineStyle );
+ }
+}
+
+CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardLeftWall( const rtl::Reference< ::chart::Diagram >& xDiagram )
+{
+ CuboidPlanePosition eRet(CuboidPlanePosition_Left);
+
+ double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
+ ThreeDHelper::getRotationAngleFromDiagram( xDiagram, fXAngleRad, fYAngleRad, fZAngleRad );
+ if( lcl_isRightAngledAxesSetAndSupported( xDiagram ) )
+ {
+ ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
+ }
+ if( sin(fYAngleRad)>0.0 )
+ eRet = CuboidPlanePosition_Right;
+ return eRet;
+}
+
+CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBackWall( const rtl::Reference< Diagram >& xDiagram )
+{
+ CuboidPlanePosition eRet(CuboidPlanePosition_Back);
+
+ double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
+ ThreeDHelper::getRotationAngleFromDiagram( xDiagram, fXAngleRad, fYAngleRad, fZAngleRad );
+ if( lcl_isRightAngledAxesSetAndSupported( xDiagram ) )
+ {
+ ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
+ }
+ if( cos(fXAngleRad)*cos(fYAngleRad)<0.0 )
+ eRet = CuboidPlanePosition_Front;
+ return eRet;
+}
+
+CuboidPlanePosition ThreeDHelper::getAutomaticCuboidPlanePositionForStandardBottom( const rtl::Reference< Diagram >& xDiagram )
+{
+ CuboidPlanePosition eRet(CuboidPlanePosition_Bottom);
+
+ double fXAngleRad=0.0; double fYAngleRad=0.0; double fZAngleRad=0.0;
+ ThreeDHelper::getRotationAngleFromDiagram( xDiagram, fXAngleRad, fYAngleRad, fZAngleRad );
+ if( lcl_isRightAngledAxesSetAndSupported( xDiagram ) )
+ {
+ ThreeDHelper::adaptRadAnglesForRightAngledAxes( fXAngleRad, fYAngleRad );
+ }
+ if( sin(fXAngleRad)*cos(fYAngleRad)<0.0 )
+ eRet = CuboidPlanePosition_Top;
+ return eRet;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/TitleHelper.cxx b/chart2/source/tools/TitleHelper.cxx
new file mode 100644
index 000000000..943adace7
--- /dev/null
+++ b/chart2/source/tools/TitleHelper.cxx
@@ -0,0 +1,420 @@
+/* -*- 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 <TitleHelper.hxx>
+#include <Title.hxx>
+#include <ChartModel.hxx>
+#include <ChartModelHelper.hxx>
+#include <Axis.hxx>
+#include <AxisHelper.hxx>
+#include <DiagramHelper.hxx>
+#include <Diagram.hxx>
+#include <ReferenceSizeProvider.hxx>
+#include <com/sun/star/chart2/FormattedString.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+namespace chart
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::chart2;
+using ::com::sun::star::uno::Reference;
+
+namespace {
+
+uno::Reference< XTitled > lcl_getTitleParentFromDiagram(
+ TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ uno::Reference< XTitled > xResult;
+
+ if( nTitleIndex == TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION ||
+ nTitleIndex == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
+ {
+ bool bDummy = false;
+ bool bIsVertical = DiagramHelper::getVertical( xDiagram, bDummy, bDummy );
+
+ if( nTitleIndex == TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION )
+ nTitleIndex = bIsVertical ? TitleHelper::X_AXIS_TITLE : TitleHelper::Y_AXIS_TITLE;
+ else
+ nTitleIndex = bIsVertical ? TitleHelper::Y_AXIS_TITLE : TitleHelper::X_AXIS_TITLE;
+ }
+
+ switch( nTitleIndex )
+ {
+ case TitleHelper::SUB_TITLE:
+ if( xDiagram.is())
+ xResult = xDiagram;
+ break;
+ case TitleHelper::X_AXIS_TITLE:
+ if( xDiagram.is())
+ xResult = AxisHelper::getAxis( 0, true, xDiagram );
+ break;
+ case TitleHelper::Y_AXIS_TITLE:
+ if( xDiagram.is())
+ xResult = AxisHelper::getAxis( 1, true, xDiagram );
+ break;
+ case TitleHelper::Z_AXIS_TITLE:
+ if( xDiagram.is())
+ xResult = AxisHelper::getAxis( 2, true, xDiagram );
+ break;
+ case TitleHelper::SECONDARY_X_AXIS_TITLE:
+ if( xDiagram.is())
+ xResult = AxisHelper::getAxis( 0, false, xDiagram );
+ break;
+ case TitleHelper::SECONDARY_Y_AXIS_TITLE:
+ if( xDiagram.is())
+ xResult = AxisHelper::getAxis( 1, false, xDiagram );
+ break;
+
+ case TitleHelper::MAIN_TITLE:
+ default:
+ OSL_FAIL( "Unsupported Title-Type requested" );
+ break;
+ }
+
+ return xResult;
+}
+
+uno::Reference< XTitled > lcl_getTitleParent( TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference< Diagram >& xDiagram )
+{
+ uno::Reference< XTitled > xResult;
+ switch( nTitleIndex )
+ {
+ case TitleHelper::MAIN_TITLE:
+ SAL_WARN("chart2", "should not be reached");
+ break;
+ case TitleHelper::SUB_TITLE:
+ case TitleHelper::X_AXIS_TITLE:
+ case TitleHelper::Y_AXIS_TITLE:
+ case TitleHelper::Z_AXIS_TITLE:
+ case TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION:
+ case TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION:
+ case TitleHelper::SECONDARY_X_AXIS_TITLE:
+ case TitleHelper::SECONDARY_Y_AXIS_TITLE:
+ xResult.set( lcl_getTitleParentFromDiagram( nTitleIndex, xDiagram ));
+ break;
+ default:
+ OSL_FAIL( "Unsupported Title-Type requested" );
+ break;
+ }
+
+ return xResult;
+}
+
+uno::Reference< XTitled > lcl_getTitleParent( TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference<::chart::ChartModel>& xModel )
+{
+ if(nTitleIndex == TitleHelper::MAIN_TITLE)
+ {
+ return xModel;
+ }
+
+ rtl::Reference< Diagram > xDiagram;
+
+ if( xModel.is())
+ xDiagram = xModel->getFirstChartDiagram();
+
+ return lcl_getTitleParent( nTitleIndex, xDiagram );
+}
+
+}
+
+uno::Reference< XTitle > TitleHelper::getTitle( TitleHelper::eTitleType nTitleIndex
+ , ChartModel& rModel )
+{
+ if(nTitleIndex == TitleHelper::MAIN_TITLE)
+ return rModel.getTitleObject();
+
+ rtl::Reference< Diagram > xDiagram = rModel.getFirstChartDiagram();
+ uno::Reference< XTitled > xTitled( lcl_getTitleParent( nTitleIndex, xDiagram ) );
+ if( xTitled.is())
+ return xTitled->getTitleObject();
+ return nullptr;
+}
+
+uno::Reference< XTitle > TitleHelper::getTitle( TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference<ChartModel>& xModel )
+{
+ uno::Reference< XTitled > xTitled;
+ if(nTitleIndex == TitleHelper::MAIN_TITLE)
+ {
+ xTitled = xModel;
+ }
+ else
+ {
+ rtl::Reference< Diagram > xDiagram;
+ if( xModel.is())
+ xDiagram = xModel->getFirstChartDiagram();
+ xTitled = lcl_getTitleParent( nTitleIndex, xDiagram );
+ }
+ if( xTitled.is())
+ return xTitled->getTitleObject();
+ return nullptr;
+}
+
+uno::Reference< XTitle > TitleHelper::createOrShowTitle(
+ TitleHelper::eTitleType eTitleType
+ , const OUString& rTitleText
+ , const rtl::Reference<ChartModel>& xModel
+ , const uno::Reference< uno::XComponentContext > & xContext )
+{
+ uno::Reference< chart2::XTitle > xTitled( TitleHelper::getTitle( eTitleType, xModel ) );
+ if( xTitled.is())
+ {
+ css::uno::Reference<css::beans::XPropertySet> xProps(xTitled, css::uno::UNO_QUERY_THROW);
+ xProps->setPropertyValue("Visible",css::uno::Any(true));
+ return xTitled;
+ }
+ else
+ {
+ return createTitle(eTitleType, rTitleText, xModel, xContext, nullptr/*pRefSizeProvider*/);
+ }
+}
+
+uno::Reference< XTitle > TitleHelper::createTitle(
+ TitleHelper::eTitleType eTitleType
+ , const OUString& rTitleText
+ , const rtl::Reference<ChartModel>& xModel
+ , const uno::Reference< uno::XComponentContext > & xContext
+ , ReferenceSizeProvider * pRefSizeProvider )
+{
+ rtl::Reference< ::chart::Title > xTitle;
+ uno::Reference< XTitled > xTitled( lcl_getTitleParent( eTitleType, xModel ) );
+
+ if( !xTitled.is() )
+ {
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xModel ) );
+ rtl::Reference< Axis > xAxis;
+ switch( eTitleType )
+ {
+ case TitleHelper::SECONDARY_X_AXIS_TITLE:
+ xAxis = AxisHelper::createAxis( 0, false, xDiagram, xContext );
+ break;
+ case TitleHelper::SECONDARY_Y_AXIS_TITLE:
+ xAxis = AxisHelper::createAxis( 1, false, xDiagram, xContext );
+ break;
+ default:
+ break;
+ }
+ if( xAxis.is() )
+ {
+ xAxis->setPropertyValue( "Show", uno::Any( false ) );
+ xTitled = lcl_getTitleParent( eTitleType, xModel );
+ }
+ }
+
+ if(xTitled.is())
+ {
+ rtl::Reference< Diagram > xDiagram( ChartModelHelper::findDiagram( xModel ) );
+
+ xTitle = new ::chart::Title();
+
+ // default char height (main: 13.0 == default)
+ float fDefaultCharHeightSub = 11.0;
+ float fDefaultCharHeightAxis = 9.0;
+ switch( eTitleType )
+ {
+ case TitleHelper::SUB_TITLE:
+ TitleHelper::setCompleteString(
+ rTitleText, xTitle, xContext, & fDefaultCharHeightSub );
+ break;
+ case TitleHelper::X_AXIS_TITLE:
+ case TitleHelper::Y_AXIS_TITLE:
+ case TitleHelper::Z_AXIS_TITLE:
+ case TitleHelper::TITLE_AT_STANDARD_X_AXIS_POSITION:
+ case TitleHelper::TITLE_AT_STANDARD_Y_AXIS_POSITION:
+ case TitleHelper::SECONDARY_X_AXIS_TITLE:
+ case TitleHelper::SECONDARY_Y_AXIS_TITLE:
+ TitleHelper::setCompleteString(
+ rTitleText, xTitle, xContext, & fDefaultCharHeightAxis );
+ break;
+ default:
+ TitleHelper::setCompleteString( rTitleText, xTitle, xContext );
+ break;
+ }
+
+ // set/clear autoscale
+ if( pRefSizeProvider )
+ pRefSizeProvider->setValuesAtTitle( xTitle );
+
+ xTitled->setTitleObject( xTitle );
+
+ //default rotation 90 degree for y axis title in normal coordinatesystems or for x axis title for swapped coordinatesystems
+ if( eTitleType == TitleHelper::X_AXIS_TITLE ||
+ eTitleType == TitleHelper::Y_AXIS_TITLE ||
+ eTitleType == TitleHelper::SECONDARY_X_AXIS_TITLE ||
+ eTitleType == TitleHelper::SECONDARY_Y_AXIS_TITLE )
+
+ {
+ try
+ {
+ bool bDummy = false;
+ bool bIsVertical = DiagramHelper::getVertical( xDiagram, bDummy, bDummy );
+
+ if( (!bIsVertical && eTitleType == TitleHelper::Y_AXIS_TITLE)
+ || (bIsVertical && eTitleType == TitleHelper::X_AXIS_TITLE)
+ || (!bIsVertical && eTitleType == TitleHelper::SECONDARY_Y_AXIS_TITLE)
+ || (bIsVertical && eTitleType == TitleHelper::SECONDARY_X_AXIS_TITLE) )
+ {
+ xTitle->setPropertyValue( "TextRotation", uno::Any( 90.0 ));
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+ return xTitle;
+
+}
+
+OUString TitleHelper::getCompleteString( const uno::Reference< XTitle >& xTitle )
+{
+ if(!xTitle.is())
+ return OUString();
+ OUStringBuffer aRet;
+ const uno::Sequence< uno::Reference< XFormattedString > > aStringList = xTitle->getText();
+ for( uno::Reference< XFormattedString > const & formattedStr : aStringList )
+ aRet.append( formattedStr->getString() );
+ return aRet.makeStringAndClear();
+}
+
+void TitleHelper::setCompleteString( const OUString& rNewText
+ , const uno::Reference< XTitle >& xTitle
+ , const uno::Reference< uno::XComponentContext > & xContext
+ , const float * pDefaultCharHeight /* = 0 */ )
+{
+ //the format of the first old text portion will be maintained if there is any
+ if(!xTitle.is())
+ return;
+
+ OUString aNewText = rNewText;
+
+ bool bStacked = false;
+ uno::Reference< beans::XPropertySet > xTitleProperties( xTitle, uno::UNO_QUERY );
+ if( xTitleProperties.is() )
+ xTitleProperties->getPropertyValue( "StackCharacters" ) >>= bStacked;
+
+ if( bStacked )
+ {
+ //#i99841# remove linebreaks that were added for vertical stacking
+ OUStringBuffer aUnstackedStr;
+ OUStringBuffer aSource(rNewText);
+
+ bool bBreakIgnored = false;
+ sal_Int32 nLen = rNewText.getLength();
+ for( sal_Int32 nPos = 0; nPos < nLen; ++nPos )
+ {
+ sal_Unicode aChar = aSource[nPos];
+ if( aChar != '\n' )
+ {
+ aUnstackedStr.append( aChar );
+ bBreakIgnored = false;
+ }
+ else if( aChar == '\n' && bBreakIgnored )
+ aUnstackedStr.append( aChar );
+ else
+ bBreakIgnored = true;
+ }
+ aNewText = aUnstackedStr.makeStringAndClear();
+ }
+
+ uno::Sequence< uno::Reference< XFormattedString > > aNewStringList;
+
+ uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = xTitle->getText();
+ if( aOldStringList.hasElements() )
+ {
+ aNewStringList = { aOldStringList[0] };
+ aNewStringList[0]->setString( aNewText );
+ }
+ else
+ {
+ uno::Reference< chart2::XFormattedString2 > xFormattedString =
+ chart2::FormattedString::create( xContext );
+
+ xFormattedString->setString( aNewText );
+ aNewStringList = { xFormattedString };
+ if( pDefaultCharHeight != nullptr )
+ {
+ try
+ {
+ uno::Any aFontSize( *pDefaultCharHeight );
+ xFormattedString->setPropertyValue( "CharHeight", aFontSize );
+ xFormattedString->setPropertyValue( "CharHeightAsian", aFontSize );
+ xFormattedString->setPropertyValue( "CharHeightComplex", aFontSize );
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+ xTitle->setText( aNewStringList );
+}
+
+void TitleHelper::removeTitle( TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference<ChartModel>& xModel )
+{
+ uno::Reference< XTitled > xTitled( lcl_getTitleParent( nTitleIndex, xModel ) );
+ if( xTitled.is())
+ {
+ xTitled->setTitleObject(nullptr);
+ }
+}
+
+bool TitleHelper::getTitleType( eTitleType& rType
+ , const css::uno::Reference< css::chart2::XTitle >& xTitle
+ , const rtl::Reference<ChartModel>& xModel )
+{
+ if( !xTitle.is() || !xModel.is() )
+ return false;
+
+ Reference< chart2::XTitle > xCurrentTitle;
+ for( sal_Int32 nTitleType = TITLE_BEGIN; nTitleType < NORMAL_TITLE_END; nTitleType++ )
+ {
+ xCurrentTitle = TitleHelper::getTitle( static_cast<eTitleType>(nTitleType), xModel );
+ if( xCurrentTitle == xTitle )
+ {
+ rType = static_cast<eTitleType>(nTitleType);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TitleHelper::hideTitle( TitleHelper::eTitleType nTitleIndex
+ , const rtl::Reference<ChartModel>& xModel)
+{
+ uno::Reference< chart2::XTitle > xTitled( TitleHelper::getTitle( nTitleIndex, xModel ) );
+ if( xTitled.is())
+ {
+ css::uno::Reference<css::beans::XPropertySet> xProps(xTitled, css::uno::UNO_QUERY_THROW);
+ xProps->setPropertyValue("Visible",css::uno::Any(false));
+ }
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/UncachedDataSequence.cxx b/chart2/source/tools/UncachedDataSequence.cxx
new file mode 100644
index 000000000..b07fc11ad
--- /dev/null
+++ b/chart2/source/tools/UncachedDataSequence.cxx
@@ -0,0 +1,316 @@
+/* -*- 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 <UncachedDataSequence.hxx>
+#include <CommonFunctors.hxx>
+#include <ModifyListenerHelper.hxx>
+#include <InternalDataProvider.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <algorithm>
+#include <strings.hrc>
+#include <ResId.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+using ::osl::MutexGuard;
+
+// necessary for MS compiler
+using ::comphelper::OPropertyContainer;
+using ::chart::impl::UncachedDataSequence_Base;
+
+namespace
+{
+constexpr OUStringLiteral lcl_aServiceName = u"com.sun.star.comp.chart.UncachedDataSequence";
+
+enum
+{
+ PROP_NUMBERFORMAT_KEY,
+ PROP_PROPOSED_ROLE,
+ PROP_XML_RANGE
+};
+} // anonymous namespace
+
+namespace chart
+{
+
+UncachedDataSequence::UncachedDataSequence(
+ const rtl::Reference< InternalDataProvider > & xIntDataProv,
+ const OUString & rRangeRepresentation )
+ : OPropertyContainer( GetBroadcastHelper()),
+ UncachedDataSequence_Base( GetMutex()),
+ m_nNumberFormatKey(0),
+ m_xDataProvider( xIntDataProv ),
+ m_aSourceRepresentation( rRangeRepresentation ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+}
+
+UncachedDataSequence::UncachedDataSequence(
+ const rtl::Reference< InternalDataProvider > & xIntDataProv,
+ const OUString & rRangeRepresentation,
+ const OUString & rRole )
+ : OPropertyContainer( GetBroadcastHelper()),
+ UncachedDataSequence_Base( GetMutex()),
+ m_nNumberFormatKey(0),
+ m_xDataProvider( xIntDataProv ),
+ m_aSourceRepresentation( rRangeRepresentation ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+ setFastPropertyValue_NoBroadcast( PROP_PROPOSED_ROLE, uno::Any( rRole ));
+}
+
+UncachedDataSequence::UncachedDataSequence( const UncachedDataSequence & rSource )
+ : OPropertyContainer( GetBroadcastHelper()),
+ UncachedDataSequence_Base( GetMutex()),
+ m_nNumberFormatKey( rSource.m_nNumberFormatKey ),
+ m_sRole( rSource.m_sRole ),
+ m_xDataProvider( rSource.m_xDataProvider ),
+ m_aSourceRepresentation( rSource.m_aSourceRepresentation ),
+ m_xModifyEventForwarder( new ModifyEventForwarder() )
+{
+ registerProperties();
+}
+
+UncachedDataSequence::~UncachedDataSequence()
+{}
+
+void UncachedDataSequence::registerProperties()
+{
+ registerProperty( "NumberFormatKey",
+ PROP_NUMBERFORMAT_KEY,
+ 0, // PropertyAttributes
+ & m_nNumberFormatKey,
+ cppu::UnoType<decltype(m_nNumberFormatKey)>::get() );
+
+ registerProperty( "Role",
+ PROP_PROPOSED_ROLE,
+ 0, // PropertyAttributes
+ & m_sRole,
+ cppu::UnoType<decltype(m_sRole)>::get() );
+
+ registerProperty( "CachedXMLRange",
+ PROP_XML_RANGE,
+ 0, // PropertyAttributes
+ & m_aXMLRange,
+ cppu::UnoType<decltype(m_aXMLRange)>::get() );
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( UncachedDataSequence, UncachedDataSequence_Base, OPropertyContainer )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( UncachedDataSequence, UncachedDataSequence_Base, OPropertyContainer )
+
+// ____ XPropertySet ____
+Reference< beans::XPropertySetInfo > SAL_CALL UncachedDataSequence::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() );
+}
+
+// ____ ::comphelper::OPropertySetHelper ____
+::cppu::IPropertyArrayHelper& UncachedDataSequence::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+// ____ ::comphelper::OPropertyArrayHelper ____
+::cppu::IPropertyArrayHelper* UncachedDataSequence::createArrayHelper() const
+{
+ Sequence< beans::Property > aProps;
+ // describes all properties which have been registered in the ctor
+ describeProperties( aProps );
+
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+OUString SAL_CALL UncachedDataSequence::getImplementationName()
+{
+ return lcl_aServiceName;
+}
+
+sal_Bool SAL_CALL UncachedDataSequence::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL UncachedDataSequence::getSupportedServiceNames()
+{
+ return {
+ lcl_aServiceName,
+ "com.sun.star.chart2.data.DataSequence",
+ "com.sun.star.chart2.data.NumericalDataSequence",
+ "com.sun.star.chart2.data.TextualDataSequence"
+ };
+}
+
+// ________ XNumericalDataSequence ________
+Sequence< double > SAL_CALL UncachedDataSequence::getNumericalData()
+{
+ Sequence< double > aResult;
+ MutexGuard aGuard( GetMutex() );
+ if( m_xDataProvider.is())
+ {
+ const Sequence< uno::Any > aValues( m_xDataProvider->getDataByRangeRepresentation( m_aSourceRepresentation ));
+ aResult.realloc( aValues.getLength());
+ std::transform( aValues.begin(), aValues.end(),
+ aResult.getArray(), CommonFunctors::AnyToDouble());
+ }
+ return aResult;
+}
+
+// ________ XTextualDataSequence ________
+Sequence< OUString > SAL_CALL UncachedDataSequence::getTextualData()
+{
+ Sequence< OUString > aResult;
+ MutexGuard aGuard( GetMutex() );
+ if( m_xDataProvider.is())
+ {
+ const Sequence< uno::Any > aValues( m_xDataProvider->getDataByRangeRepresentation( m_aSourceRepresentation ));
+ aResult.realloc( aValues.getLength());
+ std::transform( aValues.begin(), aValues.end(),
+ aResult.getArray(), CommonFunctors::AnyToString());
+ }
+ return aResult;
+}
+
+// ________ XDataSequence ________
+Sequence< Any > SAL_CALL UncachedDataSequence::getData()
+{
+ MutexGuard aGuard( GetMutex() );
+ if( m_xDataProvider.is())
+ return m_xDataProvider->getDataByRangeRepresentation( m_aSourceRepresentation );
+ return Sequence< Any >();
+}
+
+OUString SAL_CALL UncachedDataSequence::getSourceRangeRepresentation()
+{
+ return getName();
+}
+
+Sequence< OUString > SAL_CALL UncachedDataSequence::generateLabel( chart2::data::LabelOrigin )
+{
+ // auto-generated label
+ sal_Int32 nSeries = m_aSourceRepresentation.toInt32() + 1;
+ OUString aResString(::chart::SchResId(STR_DATA_UNNAMED_SERIES_WITH_INDEX));
+ static const OUStringLiteral aReplacementStr(u"%NUMBER");
+ sal_Int32 nIndex = aResString.indexOf(aReplacementStr);
+ OUString aName;
+ if( nIndex != -1 )
+ aName = aResString.replaceAt(nIndex, aReplacementStr.getLength(), OUString::number(nSeries));
+ return Sequence< OUString >( &aName, 1 );
+}
+
+::sal_Int32 SAL_CALL UncachedDataSequence::getNumberFormatKeyByIndex( ::sal_Int32 )
+{
+ return m_nNumberFormatKey;
+}
+
+// ____ XIndexReplace ____
+void SAL_CALL UncachedDataSequence::replaceByIndex( ::sal_Int32 Index, const uno::Any& Element )
+{
+ MutexGuard aGuard( GetMutex() );
+ Sequence< Any > aData( getData());
+ if( Index < aData.getLength() &&
+ m_xDataProvider.is() )
+ {
+ aData.getArray()[Index] = Element;
+ m_xDataProvider->setDataByRangeRepresentation( m_aSourceRepresentation, aData );
+ fireModifyEvent();
+ }
+}
+
+// ____ XIndexAccess (base of XIndexReplace) ____
+::sal_Int32 SAL_CALL UncachedDataSequence::getCount()
+{
+ OSL_FAIL( "Implement!" );
+ return 0;
+}
+
+uno::Any SAL_CALL UncachedDataSequence::getByIndex( ::sal_Int32 )
+{
+ OSL_FAIL( "Implement!" );
+ return uno::Any();
+}
+
+// ____ XElementAccess (base of XIndexAccess) ____
+uno::Type SAL_CALL UncachedDataSequence::getElementType()
+{
+ return cppu::UnoType<uno::Any>::get();
+}
+
+sal_Bool SAL_CALL UncachedDataSequence::hasElements()
+{
+ if( ! m_xDataProvider.is())
+ return false;
+ return m_xDataProvider->hasDataByRangeRepresentation( m_aSourceRepresentation );
+}
+
+// ____ XNamed ____
+OUString SAL_CALL UncachedDataSequence::getName()
+{
+ return m_aSourceRepresentation;
+}
+
+void SAL_CALL UncachedDataSequence::setName( const OUString& aName )
+{
+ m_aSourceRepresentation = aName;
+ fireModifyEvent();
+}
+
+Reference< util::XCloneable > SAL_CALL UncachedDataSequence::createClone()
+{
+ return new UncachedDataSequence( *this );
+}
+
+// ____ XModifiable ____
+sal_Bool SAL_CALL UncachedDataSequence::isModified()
+{
+ return false;
+}
+
+void SAL_CALL UncachedDataSequence::setModified( sal_Bool bModified )
+{
+ if( bModified )
+ fireModifyEvent();
+}
+
+// ____ XModifyBroadcaster (base of XModifiable) ____
+void SAL_CALL UncachedDataSequence::addModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->addModifyListener( aListener );
+}
+
+void SAL_CALL UncachedDataSequence::removeModifyListener( const Reference< util::XModifyListener >& aListener )
+{
+ m_xModifyEventForwarder->removeModifyListener( aListener );
+}
+
+void UncachedDataSequence::fireModifyEvent()
+{
+ // @todo: currently never called, as data changes are not yet reported by
+ // the data provider
+ m_xModifyEventForwarder->modified( lang::EventObject( static_cast< uno::XWeak* >( this )));
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/UserDefinedProperties.cxx b/chart2/source/tools/UserDefinedProperties.cxx
new file mode 100644
index 000000000..f610ae0f7
--- /dev/null
+++ b/chart2/source/tools/UserDefinedProperties.cxx
@@ -0,0 +1,61 @@
+/* -*- 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 <UserDefinedProperties.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <cppu/unotype.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::beans::Property;
+
+namespace chart
+{
+
+void UserDefinedProperties::AddPropertiesToVector(
+ std::vector< Property > & rOutProperties )
+{
+ rOutProperties.emplace_back( "ChartUserDefinedAttributes",
+ PROP_XML_USERDEF_CHART,
+ cppu::UnoType<container::XNameContainer>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+ rOutProperties.emplace_back( "TextUserDefinedAttributes",
+ PROP_XML_USERDEF_TEXT,
+ cppu::UnoType<container::XNameContainer>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+ rOutProperties.emplace_back( "ParaUserDefinedAttributes",
+ PROP_XML_USERDEF_PARA,
+ cppu::UnoType<container::XNameContainer>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+ // UserDefinedAttributesSupplier
+ rOutProperties.emplace_back( "UserDefinedAttributes",
+ PROP_XML_USERDEF,
+ cppu::UnoType<container::XNameContainer>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::MAYBEVOID );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WeakListenerAdapter.cxx b/chart2/source/tools/WeakListenerAdapter.cxx
new file mode 100644
index 000000000..16b3d8150
--- /dev/null
+++ b/chart2/source/tools/WeakListenerAdapter.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 <WeakListenerAdapter.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+WeakSelectionChangeListenerAdapter::WeakSelectionChangeListenerAdapter(
+ const Reference< view::XSelectionChangeListener > & xListener ) :
+ WeakListenerAdapter< css::view::XSelectionChangeListener >( xListener )
+{}
+
+WeakSelectionChangeListenerAdapter::~WeakSelectionChangeListenerAdapter()
+{}
+
+void SAL_CALL WeakSelectionChangeListenerAdapter::selectionChanged( const lang::EventObject& aEvent )
+{
+ Reference< view::XSelectionChangeListener > xSelChgListener( getListener() );
+ if( xSelChgListener.is())
+ xSelChgListener->selectionChanged( aEvent );
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WrappedDefaultProperty.cxx b/chart2/source/tools/WrappedDefaultProperty.cxx
new file mode 100644
index 000000000..74d2b4b0a
--- /dev/null
+++ b/chart2/source/tools/WrappedDefaultProperty.cxx
@@ -0,0 +1,77 @@
+/* -*- 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 <WrappedDefaultProperty.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+WrappedDefaultProperty::WrappedDefaultProperty(
+ const OUString& rOuterName, const OUString& rInnerName,
+ const uno::Any& rNewOuterDefault ) :
+ WrappedProperty( rOuterName, rInnerName ),
+ m_aOuterDefaultValue( rNewOuterDefault )
+{}
+
+WrappedDefaultProperty::~WrappedDefaultProperty()
+{}
+
+void WrappedDefaultProperty::setPropertyToDefault(
+ const Reference< beans::XPropertyState >& xInnerPropertyState ) const
+{
+ Reference< beans::XPropertySet > xInnerPropSet( xInnerPropertyState, uno::UNO_QUERY );
+ if( xInnerPropSet.is())
+ setPropertyValue( m_aOuterDefaultValue, xInnerPropSet );
+}
+
+uno::Any WrappedDefaultProperty::getPropertyDefault(
+ const Reference< beans::XPropertyState >& /* xInnerPropertyState */ ) const
+{
+ return m_aOuterDefaultValue;
+}
+
+beans::PropertyState WrappedDefaultProperty::getPropertyState(
+ const Reference< beans::XPropertyState >& xInnerPropertyState ) const
+{
+ beans::PropertyState aState = beans::PropertyState_DIRECT_VALUE;
+ try
+ {
+ Reference< beans::XPropertySet > xInnerProp( xInnerPropertyState, uno::UNO_QUERY_THROW );
+ uno::Any aValue = getPropertyValue( xInnerProp );
+ if( m_aOuterDefaultValue == convertInnerToOuterValue( aValue ))
+ aState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return aState;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WrappedDirectStateProperty.cxx b/chart2/source/tools/WrappedDirectStateProperty.cxx
new file mode 100644
index 000000000..ff74fac8c
--- /dev/null
+++ b/chart2/source/tools/WrappedDirectStateProperty.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 <WrappedDirectStateProperty.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+WrappedDirectStateProperty::WrappedDirectStateProperty(
+ const OUString& rOuterName, const OUString& rInnerName ) :
+ WrappedProperty( rOuterName, rInnerName )
+{}
+
+WrappedDirectStateProperty::~WrappedDirectStateProperty()
+{}
+
+beans::PropertyState WrappedDirectStateProperty::getPropertyState(
+ const Reference< beans::XPropertyState >& /* xInnerPropertyState */ ) const
+{
+ return beans::PropertyState_DIRECT_VALUE;
+}
+
+} // namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WrappedIgnoreProperty.cxx b/chart2/source/tools/WrappedIgnoreProperty.cxx
new file mode 100644
index 000000000..4e62dca5b
--- /dev/null
+++ b/chart2/source/tools/WrappedIgnoreProperty.cxx
@@ -0,0 +1,113 @@
+/* -*- 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 <WrappedIgnoreProperty.hxx>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineJoint.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/RectanglePoint.hpp>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+WrappedIgnoreProperty::WrappedIgnoreProperty( const OUString& rOuterName, const Any& rDefaultValue )
+ : WrappedProperty( rOuterName, OUString() )
+ , m_aDefaultValue( rDefaultValue )
+ , m_aCurrentValue( rDefaultValue )
+{
+}
+WrappedIgnoreProperty::~WrappedIgnoreProperty()
+{
+}
+
+void WrappedIgnoreProperty::setPropertyValue( const Any& rOuterValue, const Reference< beans::XPropertySet >& /* xInnerPropertySet */ ) const
+{
+ m_aCurrentValue = rOuterValue;
+}
+
+Any WrappedIgnoreProperty::getPropertyValue( const Reference< beans::XPropertySet >& /* xInnerPropertySet */ ) const
+{
+ return m_aCurrentValue;
+}
+
+void WrappedIgnoreProperty::setPropertyToDefault( const Reference< beans::XPropertyState >& /* xInnerPropertyState */ ) const
+{
+ m_aCurrentValue = m_aDefaultValue;
+}
+
+Any WrappedIgnoreProperty::getPropertyDefault( const Reference< beans::XPropertyState >& /* xInnerPropertyState */ ) const
+{
+ return m_aDefaultValue;
+}
+
+beans::PropertyState WrappedIgnoreProperty::getPropertyState( const Reference< beans::XPropertyState >& /* xInnerPropertyState */ ) const
+{
+ return ( m_aCurrentValue == m_aDefaultValue
+ ? beans::PropertyState_DEFAULT_VALUE
+ : beans::PropertyState_DIRECT_VALUE );
+}
+
+void WrappedIgnoreProperties::addIgnoreLineProperties( std::vector< std::unique_ptr<WrappedProperty> >& rList )
+{
+ rList.emplace_back( new WrappedIgnoreProperty( "LineStyle", uno::Any( drawing::LineStyle_SOLID ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "LineDashName", uno::Any( OUString() ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "LineColor", uno::Any( sal_Int32(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "LineTransparence", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "LineWidth", uno::Any( sal_Int32(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "LineJoint", uno::Any( drawing::LineJoint_ROUND ) ) );
+}
+
+void WrappedIgnoreProperties::addIgnoreFillProperties( std::vector< std::unique_ptr<WrappedProperty> >& rList )
+{
+ addIgnoreFillProperties_without_BitmapProperties( rList );
+ addIgnoreFillProperties_only_BitmapProperties( rList );
+}
+
+void WrappedIgnoreProperties::addIgnoreFillProperties_without_BitmapProperties( std::vector< std::unique_ptr<WrappedProperty> >& rList )
+{
+ rList.emplace_back( new WrappedIgnoreProperty( "FillStyle", uno::Any( drawing::FillStyle_SOLID ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillColor", uno::Any( sal_Int32(-1) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillTransparence", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillTransparenceGradientName", uno::Any( OUString() ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillGradientName", uno::Any( OUString() ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillHatchName", uno::Any( OUString() ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBackground", uno::Any( false ) ) );
+}
+
+void WrappedIgnoreProperties::addIgnoreFillProperties_only_BitmapProperties( std::vector< std::unique_ptr<WrappedProperty> >& rList )
+{
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapOffsetX", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapOffsetY", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapPositionOffsetX", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapPositionOffsetY", uno::Any( sal_Int16(0) ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapRectanglePoint", uno::Any( drawing::RectanglePoint_LEFT_TOP ) ) );
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapLogicalSize", uno::Any( false ) ) );//todo correct default?
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapSizeX", uno::Any( sal_Int32(10) ) ) );//todo which default?
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapSizeY", uno::Any( sal_Int32(10) ) ) );//todo which default?
+ rList.emplace_back( new WrappedIgnoreProperty( "FillBitmapMode", uno::Any( drawing::BitmapMode_REPEAT ) ) );
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WrappedProperty.cxx b/chart2/source/tools/WrappedProperty.cxx
new file mode 100644
index 000000000..ee28aba86
--- /dev/null
+++ b/chart2/source/tools/WrappedProperty.cxx
@@ -0,0 +1,125 @@
+/* -*- 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 <WrappedProperty.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+WrappedProperty::WrappedProperty( const OUString& rOuterName, const OUString& rInnerName)
+ : m_aOuterName( rOuterName )
+ , m_aInnerName( rInnerName )
+{
+}
+WrappedProperty::~WrappedProperty()
+{
+}
+
+OUString WrappedProperty::getInnerName() const
+{
+ return m_aInnerName;
+}
+
+Any WrappedProperty::convertInnerToOuterValue( const Any& rInnerValue ) const
+{
+ return rInnerValue;
+}
+Any WrappedProperty::convertOuterToInnerValue( const Any& rOuterValue ) const
+{
+ return rOuterValue;
+}
+
+void WrappedProperty::setPropertyValue( const Any& rOuterValue, const Reference< beans::XPropertySet >& xInnerPropertySet ) const
+{
+ if(xInnerPropertySet.is())
+ xInnerPropertySet->setPropertyValue( getInnerName(), convertOuterToInnerValue( rOuterValue ) );
+}
+
+Any WrappedProperty::getPropertyValue( const Reference< beans::XPropertySet >& xInnerPropertySet ) const
+{
+ Any aRet;
+ if( xInnerPropertySet.is() )
+ {
+ aRet = xInnerPropertySet->getPropertyValue( getInnerName() );
+ aRet = convertInnerToOuterValue( aRet );
+ }
+ return aRet;
+}
+
+void WrappedProperty::setPropertyToDefault( const Reference< beans::XPropertyState >& xInnerPropertyState ) const
+{
+ if( xInnerPropertyState.is() && !getInnerName().isEmpty() )
+ xInnerPropertyState->setPropertyToDefault(getInnerName());
+ else
+ {
+ Reference< beans::XPropertySet > xInnerProp( xInnerPropertyState, uno::UNO_QUERY );
+ setPropertyValue( getPropertyDefault( xInnerPropertyState ), xInnerProp );
+ }
+}
+
+Any WrappedProperty::getPropertyDefault( const Reference< beans::XPropertyState >& xInnerPropertyState ) const
+{
+ Any aRet;
+ if( xInnerPropertyState.is() )
+ {
+ aRet = xInnerPropertyState->getPropertyDefault( getInnerName() );
+ aRet = convertInnerToOuterValue( aRet );
+ }
+ return aRet;
+}
+
+beans::PropertyState WrappedProperty::getPropertyState( const Reference< beans::XPropertyState >& xInnerPropertyState ) const
+{
+ beans::PropertyState aState = beans::PropertyState_DIRECT_VALUE;
+ OUString aInnerName( getInnerName() );
+ if( xInnerPropertyState.is() && !aInnerName.isEmpty() )
+ aState = xInnerPropertyState->getPropertyState( aInnerName );
+ else
+ {
+ try
+ {
+ Reference< beans::XPropertySet > xInnerProp( xInnerPropertyState, uno::UNO_QUERY );
+ uno::Any aValue = getPropertyValue( xInnerProp );
+ if( !aValue.hasValue() )
+ aState = beans::PropertyState_DEFAULT_VALUE;
+ else
+ {
+ uno::Any aDefault = getPropertyDefault( xInnerPropertyState );
+ if( aValue == aDefault )
+ aState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ return aState;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/WrappedPropertySet.cxx b/chart2/source/tools/WrappedPropertySet.cxx
new file mode 100644
index 000000000..ead7f6d05
--- /dev/null
+++ b/chart2/source/tools/WrappedPropertySet.cxx
@@ -0,0 +1,445 @@
+/* -*- 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 <WrappedPropertySet.hxx>
+#include <cppuhelper/propshlp.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+namespace chart
+{
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+WrappedPropertySet::WrappedPropertySet()
+{
+}
+WrappedPropertySet::~WrappedPropertySet()
+{
+ clearWrappedPropertySet();
+}
+
+Reference< beans::XPropertyState > WrappedPropertySet::getInnerPropertyState()
+{
+ return Reference< beans::XPropertyState >( getInnerPropertySet(), uno::UNO_QUERY );
+}
+
+void WrappedPropertySet::clearWrappedPropertySet()
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );//do not use different mutex than is already used for static property sequence
+
+ m_pPropertyArrayHelper.reset();
+ m_pWrappedPropertyMap.reset();
+
+ m_xInfo = nullptr;
+}
+
+//XPropertySet
+Reference< beans::XPropertySetInfo > SAL_CALL WrappedPropertySet::getPropertySetInfo( )
+{
+ Reference< beans::XPropertySetInfo > xInfo = m_xInfo;
+ if( !xInfo.is() )
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );//do not use different mutex than is already used for static property sequence
+ xInfo = m_xInfo;
+ if( !xInfo.is() )
+ {
+ xInfo = ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_xInfo = xInfo;
+ }
+ }
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return m_xInfo;
+}
+
+void SAL_CALL WrappedPropertySet::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ try
+ {
+ sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( nHandle );
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( pWrappedProperty )
+ pWrappedProperty->setPropertyValue( rValue, xInnerPropertySet );
+ else if( xInnerPropertySet.is() )
+ xInnerPropertySet->setPropertyValue( rPropertyName, rValue );
+ else
+ {
+ SAL_WARN("chart2.tools", "found no inner property set to map to");
+ }
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ throw;
+ }
+ catch( const beans::PropertyVetoException& )
+ {
+ throw;
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ throw;
+ }
+ catch( const lang::WrappedTargetException& )
+ {
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( const uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ TOOLS_WARN_EXCEPTION( "chart2", "invalid exception caught in WrappedPropertySet::setPropertyValue");
+ throw lang::WrappedTargetException( ex.Message, nullptr, anyEx );
+ }
+}
+Any SAL_CALL WrappedPropertySet::getPropertyValue( const OUString& rPropertyName )
+{
+ Any aRet;
+
+ try
+ {
+ sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( nHandle );
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( pWrappedProperty )
+ aRet = pWrappedProperty->getPropertyValue( xInnerPropertySet );
+ else if( xInnerPropertySet.is() )
+ aRet = xInnerPropertySet->getPropertyValue( rPropertyName );
+ else
+ {
+ SAL_WARN("chart2.tools", "found no inner property set to map to");
+ }
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ throw;
+ }
+ catch( const lang::WrappedTargetException& )
+ {
+ throw;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( const uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ TOOLS_WARN_EXCEPTION( "chart2", "invalid exception caught in WrappedPropertySet::setPropertyValue");
+ throw lang::WrappedTargetException( ex.Message, nullptr, anyEx );
+ }
+
+ return aRet;
+}
+
+void SAL_CALL WrappedPropertySet::addPropertyChangeListener( const OUString& rPropertyName, const Reference< beans::XPropertyChangeListener >& xListener )
+{
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( xInnerPropertySet.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ xInnerPropertySet->addPropertyChangeListener( pWrappedProperty->getInnerName(), xListener );
+ else
+ xInnerPropertySet->addPropertyChangeListener( rPropertyName, xListener );
+ }
+}
+void SAL_CALL WrappedPropertySet::removePropertyChangeListener( const OUString& rPropertyName, const Reference< beans::XPropertyChangeListener >& aListener )
+{
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( xInnerPropertySet.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ xInnerPropertySet->removePropertyChangeListener( pWrappedProperty->getInnerName(), aListener );
+ else
+ xInnerPropertySet->removePropertyChangeListener( rPropertyName, aListener );
+ }
+}
+void SAL_CALL WrappedPropertySet::addVetoableChangeListener( const OUString& rPropertyName, const Reference< beans::XVetoableChangeListener >& aListener )
+{
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( xInnerPropertySet.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ xInnerPropertySet->addVetoableChangeListener( pWrappedProperty->getInnerName(), aListener );
+ else
+ xInnerPropertySet->addVetoableChangeListener( rPropertyName, aListener );
+ }
+}
+void SAL_CALL WrappedPropertySet::removeVetoableChangeListener( const OUString& rPropertyName, const Reference< beans::XVetoableChangeListener >& aListener )
+{
+ Reference< beans::XPropertySet > xInnerPropertySet( getInnerPropertySet() );
+ if( xInnerPropertySet.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ xInnerPropertySet->removeVetoableChangeListener( pWrappedProperty->getInnerName(), aListener );
+ else
+ xInnerPropertySet->removeVetoableChangeListener( rPropertyName, aListener );
+ }
+}
+
+//XMultiPropertySet
+void SAL_CALL WrappedPropertySet::setPropertyValues( const Sequence< OUString >& rNameSeq, const Sequence< Any >& rValueSeq )
+{
+ bool bUnknownProperty = false;
+ sal_Int32 nMinCount = std::min( rValueSeq.getLength(), rNameSeq.getLength() );
+ for(sal_Int32 nN=0; nN<nMinCount; nN++)
+ {
+ OUString aPropertyName( rNameSeq[nN] );
+ try
+ {
+ setPropertyValue( aPropertyName, rValueSeq[nN] );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ bUnknownProperty = true;
+ }
+ }
+ //todo: store unknown properties elsewhere
+ OSL_ENSURE(!bUnknownProperty,"unknown property");
+// if( bUnknownProperty )
+// throw beans::UnknownPropertyException();
+}
+Sequence< Any > SAL_CALL WrappedPropertySet::getPropertyValues( const Sequence< OUString >& rNameSeq )
+{
+ Sequence< Any > aRetSeq;
+ if( rNameSeq.hasElements() )
+ {
+ aRetSeq.realloc( rNameSeq.getLength() );
+ auto pRetSeq = aRetSeq.getArray();
+ for(sal_Int32 nN=0; nN<rNameSeq.getLength(); nN++)
+ {
+ try
+ {
+ OUString aPropertyName( rNameSeq[nN] );
+ pRetSeq[nN] = getPropertyValue( aPropertyName );
+ }
+ catch( const beans::UnknownPropertyException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ catch( const lang::WrappedTargetException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+ }
+ return aRetSeq;
+}
+void SAL_CALL WrappedPropertySet::addPropertiesChangeListener( const Sequence< OUString >& /* rNameSeq */, const Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented yet");
+ //todo
+}
+void SAL_CALL WrappedPropertySet::removePropertiesChangeListener( const Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented yet");
+ //todo
+}
+void SAL_CALL WrappedPropertySet::firePropertiesChangeEvent( const Sequence< OUString >& /* rNameSeq */, const Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented yet");
+ //todo
+}
+
+//XPropertyState
+beans::PropertyState SAL_CALL WrappedPropertySet::getPropertyState( const OUString& rPropertyName )
+{
+ beans::PropertyState aState( beans::PropertyState_DIRECT_VALUE );
+
+ Reference< beans::XPropertyState > xInnerPropertyState( getInnerPropertyState() );
+ if( xInnerPropertyState.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ aState = pWrappedProperty->getPropertyState( xInnerPropertyState );
+ else
+ aState = xInnerPropertyState->getPropertyState( rPropertyName );
+ }
+ return aState;
+}
+
+const WrappedProperty* WrappedPropertySet::getWrappedProperty( const OUString& rOuterName )
+{
+ sal_Int32 nHandle = getInfoHelper().getHandleByName( rOuterName );
+ return getWrappedProperty( nHandle );
+}
+
+const WrappedProperty* WrappedPropertySet::getWrappedProperty( sal_Int32 nHandle )
+{
+ tWrappedPropertyMap::const_iterator aFound( getWrappedPropertyMap().find( nHandle ) );
+ if( aFound != getWrappedPropertyMap().end() )
+ return (*aFound).second.get();
+ return nullptr;
+}
+
+Sequence< beans::PropertyState > SAL_CALL WrappedPropertySet::getPropertyStates( const Sequence< OUString >& rNameSeq )
+{
+ Sequence< beans::PropertyState > aRetSeq;
+ if( rNameSeq.hasElements() )
+ {
+ aRetSeq.realloc( rNameSeq.getLength() );
+ auto pRetSeq = aRetSeq.getArray();
+ for(sal_Int32 nN=0; nN<rNameSeq.getLength(); nN++)
+ {
+ OUString aPropertyName( rNameSeq[nN] );
+ pRetSeq[nN] = getPropertyState( aPropertyName );
+ }
+ }
+ return aRetSeq;
+}
+
+void SAL_CALL WrappedPropertySet::setPropertyToDefault( const OUString& rPropertyName )
+{
+ Reference< beans::XPropertyState > xInnerPropertyState( getInnerPropertyState() );
+ if( xInnerPropertyState.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ pWrappedProperty->setPropertyToDefault( xInnerPropertyState );
+ else
+ xInnerPropertyState->setPropertyToDefault( rPropertyName );
+ }
+}
+Any SAL_CALL WrappedPropertySet::getPropertyDefault( const OUString& rPropertyName )
+{
+ Any aRet;
+ Reference< beans::XPropertyState > xInnerPropertyState( getInnerPropertyState() );
+ if( xInnerPropertyState.is() )
+ {
+ const WrappedProperty* pWrappedProperty = getWrappedProperty( rPropertyName );
+ if( pWrappedProperty )
+ aRet = pWrappedProperty->getPropertyDefault(xInnerPropertyState);
+ else
+ aRet = xInnerPropertyState->getPropertyDefault( rPropertyName );
+ }
+ return aRet;
+}
+
+//XMultiPropertyStates
+void SAL_CALL WrappedPropertySet::setAllPropertiesToDefault( )
+{
+ const Sequence< beans::Property >& rPropSeq = getPropertySequence();
+ for(beans::Property const & prop : rPropSeq)
+ {
+ setPropertyToDefault( prop.Name );
+ }
+}
+void SAL_CALL WrappedPropertySet::setPropertiesToDefault( const Sequence< OUString >& rNameSeq )
+{
+ for(OUString const & s : rNameSeq)
+ {
+ setPropertyToDefault( s );
+ }
+}
+Sequence< Any > SAL_CALL WrappedPropertySet::getPropertyDefaults( const Sequence< OUString >& rNameSeq )
+{
+ Sequence< Any > aRetSeq;
+ if( rNameSeq.hasElements() )
+ {
+ aRetSeq.realloc( rNameSeq.getLength() );
+ auto pRetSeq = aRetSeq.getArray();
+ for(sal_Int32 nN=0; nN<rNameSeq.getLength(); nN++)
+ {
+ OUString aPropertyName( rNameSeq[nN] );
+ pRetSeq[nN] = getPropertyDefault( aPropertyName );
+ }
+ }
+ return aRetSeq;
+}
+
+::cppu::IPropertyArrayHelper& WrappedPropertySet::getInfoHelper()
+{
+ ::cppu::OPropertyArrayHelper* p = m_pPropertyArrayHelper.get();
+ if(!p)
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );//do not use different mutex than is already used for static property sequence
+ p = m_pPropertyArrayHelper.get();
+ if(!p)
+ {
+ p = new ::cppu::OPropertyArrayHelper( getPropertySequence(), true );
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_pPropertyArrayHelper.reset(p);
+ }
+ }
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *m_pPropertyArrayHelper;
+}
+
+tWrappedPropertyMap& WrappedPropertySet::getWrappedPropertyMap()
+{
+ tWrappedPropertyMap* p = m_pWrappedPropertyMap.get();
+ if(!p)
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );//do not use different mutex than is already used for static property sequence
+ p = m_pWrappedPropertyMap.get();
+ if(!p)
+ {
+ std::vector< std::unique_ptr<WrappedProperty> > aPropList( createWrappedProperties() );
+ p = new tWrappedPropertyMap;
+
+ for (auto & elem : aPropList)
+ {
+ sal_Int32 nHandle = getInfoHelper().getHandleByName( elem->getOuterName() );
+
+ if( nHandle == -1 )
+ {
+ OSL_FAIL( "missing property in property list" );
+ }
+ else if( p->find( nHandle ) != p->end() )
+ {
+ //duplicate Wrapped property
+ OSL_FAIL( "duplicate Wrapped property" );
+ }
+ else
+ (*p)[ nHandle ] = std::move(elem);
+ }
+
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_pWrappedPropertyMap.reset(p);
+ }
+ }
+ else
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *m_pWrappedPropertyMap;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/source/tools/XMLRangeHelper.cxx b/chart2/source/tools/XMLRangeHelper.cxx
new file mode 100644
index 000000000..196a03965
--- /dev/null
+++ b/chart2/source/tools/XMLRangeHelper.cxx
@@ -0,0 +1,393 @@
+/* -*- 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 <XMLRangeHelper.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <algorithm>
+
+namespace
+{
+/** unary function that escapes backslashes and single quotes in a sal_Unicode
+ array (which you can get from an OUString with getStr()) and puts the result
+ into the OUStringBuffer given in the CTOR
+ */
+class lcl_Escape
+{
+public:
+ explicit lcl_Escape( OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
+ void operator() ( sal_Unicode aChar )
+ {
+ static const sal_Unicode s_aQuote( '\'' );
+ static const sal_Unicode s_aBackslash( '\\' );
+
+ if( aChar == s_aQuote ||
+ aChar == s_aBackslash )
+ m_aResultBuffer.append( s_aBackslash );
+ m_aResultBuffer.append( aChar );
+ }
+
+private:
+ OUStringBuffer & m_aResultBuffer;
+};
+
+/** unary function that removes backslash escapes in a sal_Unicode array (which
+ you can get from an OUString with getStr()) and puts the result into the
+ OUStringBuffer given in the CTOR
+ */
+class lcl_UnEscape
+{
+public:
+ explicit lcl_UnEscape( OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
+ void operator() ( sal_Unicode aChar )
+ {
+ static const sal_Unicode s_aBackslash( '\\' );
+
+ if( aChar != s_aBackslash )
+ m_aResultBuffer.append( aChar );
+ }
+
+private:
+ OUStringBuffer & m_aResultBuffer;
+};
+
+void lcl_getXMLStringForCell( const ::chart::XMLRangeHelper::Cell & rCell, OUStringBuffer * output )
+{
+ OSL_ASSERT(output != nullptr);
+
+ if( rCell.empty())
+ return;
+
+ sal_Int32 nCol = rCell.nColumn;
+ output->append( '.' );
+ if( ! rCell.bRelativeColumn )
+ output->append( '$' );
+
+ // get A, B, C, ..., AA, AB, ... representation of column number
+ if( nCol < 26 )
+ output->append( static_cast<sal_Unicode>('A' + nCol) );
+ else if( nCol < 702 )
+ {
+ output->append( static_cast<sal_Unicode>('A' + nCol / 26 - 1 ));
+ output->append( static_cast<sal_Unicode>('A' + nCol % 26) );
+ }
+ else // works for nCol <= 18,278
+ {
+ output->append( static_cast<sal_Unicode>('A' + nCol / 702 - 1 ));
+ output->append( static_cast<sal_Unicode>('A' + (nCol % 702) / 26 ));
+ output->append( static_cast<sal_Unicode>('A' + nCol % 26) );
+ }
+
+ // write row number as number
+ if( ! rCell.bRelativeRow )
+ output->append( '$' );
+ output->append( rCell.nRow + sal_Int32(1) );
+}
+
+void lcl_getSingleCellAddressFromXMLString(
+ std::u16string_view rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ ::chart::XMLRangeHelper::Cell & rOutCell )
+{
+ // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
+ static const sal_Unicode aDollar( '$' );
+ static const sal_Unicode aLetterA( 'A' );
+
+ OUString aCellStr = OUString(rXMLString.substr( nStartPos, nEndPos - nStartPos + 1 )).toAsciiUpperCase();
+ const sal_Unicode* pStrArray = aCellStr.getStr();
+ sal_Int32 nLength = aCellStr.getLength();
+ sal_Int32 i = nLength - 1, nColumn = 0;
+
+ // parse number for row
+ while( rtl::isAsciiDigit( pStrArray[ i ] ) && i >= 0 )
+ i--;
+ rOutCell.nRow = (o3tl::toInt32(aCellStr.subView( i + 1 ))) - 1;
+ // a dollar in XML means absolute (whereas in UI it means relative)
+ if( pStrArray[ i ] == aDollar )
+ {
+ i--;
+ rOutCell.bRelativeRow = false;
+ }
+ else
+ rOutCell.bRelativeRow = true;
+
+ // parse rest for column
+ sal_Int32 nPower = 1;
+ while( rtl::isAsciiAlpha( pStrArray[ i ] ))
+ {
+ nColumn += (pStrArray[ i ] - aLetterA + 1) * nPower;
+ i--;
+ nPower *= 26;
+ }
+ rOutCell.nColumn = nColumn - 1;
+
+ rOutCell.bRelativeColumn = true;
+ if( i >= 0 &&
+ pStrArray[ i ] == aDollar )
+ rOutCell.bRelativeColumn = false;
+ rOutCell.bIsEmpty = false;
+}
+
+bool lcl_getCellAddressFromXMLString(
+ const OUString& rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ ::chart::XMLRangeHelper::Cell & rOutCell,
+ OUString& rOutTableName )
+{
+ static const sal_Unicode aDot( '.' );
+ static const sal_Unicode aQuote( '\'' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ sal_Int32 nNextDelimiterPos = nStartPos;
+
+ sal_Int32 nDelimiterPos = nStartPos;
+ bool bInQuotation = false;
+ // parse table name
+ while( nDelimiterPos < nEndPos &&
+ ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nDelimiterPos ] == aBackslash )
+ ++nDelimiterPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nDelimiterPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nDelimiterPos;
+ }
+
+ if( nDelimiterPos == -1 )
+ return false;
+
+ if( nDelimiterPos > nStartPos && nDelimiterPos < nEndPos )
+ {
+ // there is a table name before the address
+
+ OUStringBuffer aTableNameBuffer;
+ const sal_Unicode * pTableName = rXMLString.getStr();
+
+ // remove escapes from table name
+ std::for_each( pTableName + nStartPos,
+ pTableName + nDelimiterPos,
+ lcl_UnEscape( aTableNameBuffer ));
+
+ // unquote quoted table name
+ const sal_Unicode * pBuf = aTableNameBuffer.getStr();
+ if( pBuf[ 0 ] == aQuote &&
+ pBuf[ aTableNameBuffer.getLength() - 1 ] == aQuote )
+ {
+ OUString aName = aTableNameBuffer.makeStringAndClear();
+ rOutTableName = aName.copy( 1, aName.getLength() - 2 );
+ }
+ else
+ rOutTableName = aTableNameBuffer.makeStringAndClear();
+ }
+ else
+ nDelimiterPos = nStartPos;
+
+ for( sal_Int32 i = 0;
+ nNextDelimiterPos < nEndPos;
+ nDelimiterPos = nNextDelimiterPos, i++ )
+ {
+ nNextDelimiterPos = rXMLString.indexOf( aDot, nDelimiterPos + 1 );
+ if( nNextDelimiterPos == -1 ||
+ nNextDelimiterPos > nEndPos )
+ nNextDelimiterPos = nEndPos + 1;
+
+ if( i==0 )
+ // only take first cell
+ lcl_getSingleCellAddressFromXMLString(
+ rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
+ }
+
+ return true;
+}
+
+bool lcl_getCellRangeAddressFromXMLString(
+ const OUString& rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ ::chart::XMLRangeHelper::CellRange & rOutRange )
+{
+ bool bResult = true;
+ static const sal_Unicode aColon( ':' );
+ static const sal_Unicode aQuote( '\'' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ sal_Int32 nDelimiterPos = nStartPos;
+ bool bInQuotation = false;
+ // parse table name
+ while( nDelimiterPos < nEndPos &&
+ ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nDelimiterPos ] == aBackslash )
+ ++nDelimiterPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nDelimiterPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nDelimiterPos;
+ }
+
+ if( nDelimiterPos == nEndPos )
+ {
+ // only one cell
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
+ rOutRange.aUpperLeft,
+ rOutRange.aTableName );
+ if( rOutRange.aTableName.isEmpty() )
+ bResult = false;
+ }
+ else
+ {
+ // range (separated by a colon)
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nDelimiterPos - 1,
+ rOutRange.aUpperLeft,
+ rOutRange.aTableName );
+ if( rOutRange.aTableName.isEmpty() )
+ bResult = false;
+
+ OUString sTableSecondName;
+ if( bResult )
+ {
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
+ rOutRange.aLowerRight,
+ sTableSecondName );
+ }
+ if( bResult &&
+ !sTableSecondName.isEmpty() &&
+ sTableSecondName != rOutRange.aTableName )
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+} // anonymous namespace
+
+namespace chart::XMLRangeHelper
+{
+
+CellRange getCellRangeFromXMLString( const OUString & rXMLString )
+{
+ static const sal_Unicode aSpace( ' ' );
+ static const sal_Unicode aQuote( '\'' );
+// static const sal_Unicode aDoubleQuote( '\"' );
+ static const sal_Unicode aDollar( '$' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ const sal_Int32 nLength = rXMLString.getLength();
+
+ // reset
+ CellRange aResult;
+
+ // iterate over different ranges
+ for( sal_Int32 nStartPos = 0, nEndPos = nStartPos;
+ nEndPos < nLength;
+ nStartPos = ++nEndPos )
+ {
+ // find start point of next range
+
+ // ignore leading '$'
+ if( rXMLString[ nEndPos ] == aDollar)
+ nEndPos++;
+
+ bool bInQuotation = false;
+ // parse range
+ while( nEndPos < nLength &&
+ ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nEndPos ] == aBackslash )
+ ++nEndPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nEndPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nEndPos;
+ }
+
+ if( ! lcl_getCellRangeAddressFromXMLString(
+ rXMLString,
+ nStartPos, nEndPos - 1,
+ aResult ))
+ {
+ // if an error occurred, bail out
+ return CellRange();
+ }
+ }
+
+ return aResult;
+}
+
+OUString getXMLStringFromCellRange( const CellRange & rRange )
+{
+ static const sal_Unicode aSpace( ' ' );
+ static const sal_Unicode aQuote( '\'' );
+
+ OUStringBuffer aBuffer;
+
+ if( !rRange.aTableName.isEmpty())
+ {
+ bool bNeedsEscaping = ( rRange.aTableName.indexOf( aQuote ) > -1 );
+ bool bNeedsQuoting = bNeedsEscaping || ( rRange.aTableName.indexOf( aSpace ) > -1 );
+
+ // quote table name if it contains spaces or quotes
+ if( bNeedsQuoting )
+ {
+ // leading quote
+ aBuffer.append( aQuote );
+
+ // escape existing quotes
+ if( bNeedsEscaping )
+ {
+ const sal_Unicode * pTableNameBeg = rRange.aTableName.getStr();
+
+ // append the quoted string at the buffer
+ std::for_each( pTableNameBeg,
+ pTableNameBeg + rRange.aTableName.getLength(),
+ lcl_Escape( aBuffer ) );
+ }
+ else
+ aBuffer.append( rRange.aTableName );
+
+ // final quote
+ aBuffer.append( aQuote );
+ }
+ else
+ aBuffer.append( rRange.aTableName );
+ }
+ lcl_getXMLStringForCell( rRange.aUpperLeft, &aBuffer );
+
+ if( ! rRange.aLowerRight.empty())
+ {
+ // we have a range (not a single cell)
+ aBuffer.append( u':');
+ lcl_getXMLStringForCell( rRange.aLowerRight, &aBuffer );
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+} // namespace chart::XMLRangeHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */