summaryrefslogtreecommitdiffstats
path: root/chart2/source/tools/DiagramHelper.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /chart2/source/tools/DiagramHelper.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'chart2/source/tools/DiagramHelper.cxx')
-rw-r--r--chart2/source/tools/DiagramHelper.cxx1685
1 files changed, 1685 insertions, 0 deletions
diff --git a/chart2/source/tools/DiagramHelper.cxx b/chart2/source/tools/DiagramHelper.cxx
new file mode 100644
index 000000000..213d1787d
--- /dev/null
+++ b/chart2/source/tools/DiagramHelper.cxx
@@ -0,0 +1,1685 @@
+/* -*- 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 <DataSeriesHelper.hxx>
+#include <AxisHelper.hxx>
+#include <ChartTypeHelper.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 <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/XTitled.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeTemplate.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.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 <unotools/saveopt.hxx>
+#include <rtl/math.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>
+
+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 Reference< XDiagram > & xDiagram,
+ const Reference< lang::XMultiServiceFactory > & 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
+ {
+ Reference< XChartTypeTemplate > xTempl(
+ xChartTypeManager->createInstance( aServiceNames[ i ] ), uno::UNO_QUERY_THROW );
+
+ if (xTempl.is() && xTempl->matchesTemplate(xDiagram, true))
+ {
+ aResult.first = xTempl;
+ aResult.second = aServiceNames[ i ];
+ bTemplateFound = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return aResult;
+}
+
+void DiagramHelper::setVertical(
+ const Reference< XDiagram > & xDiagram,
+ bool bVertical /* = true */ )
+{
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY );
+ if (!xCnt.is())
+ return;
+
+ const Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems();
+ uno::Any aValue;
+ aValue <<= bVertical;
+ for( uno::Reference< XCoordinateSystem > const & xCooSys : aCooSys )
+ {
+ Reference< beans::XPropertySet > xProp( xCooSys, uno::UNO_QUERY );
+ bool bChanged = false;
+ if (xProp.is())
+ {
+ bool bOldSwap = false;
+ if( !(xProp->getPropertyValue("SwapXAndYAxis") >>= bOldSwap)
+ || bVertical != bOldSwap )
+ bChanged = true;
+
+ if( bChanged )
+ xProp->setPropertyValue("SwapXAndYAxis", aValue);
+ }
+
+ if (!xCooSys.is())
+ continue;
+
+ 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)
+ {
+ Reference<chart2::XAxis> xAxis = xCooSys->getAxisByDimension(nDimIndex,nI);
+ if (!xAxis.is())
+ continue;
+
+ //adapt title rotation only when axis swapping has changed
+ if (!bChanged)
+ continue;
+
+ Reference< XTitled > xTitled( xAxis, uno::UNO_QUERY );
+ if (!xTitled.is())
+ continue;
+
+ Reference< beans::XPropertySet > xTitleProps( xTitled->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 uno::Reference< chart2::XDiagram > & xDiagram,
+ bool& rbFound, bool& rbAmbiguous )
+{
+ bool bValue = false;
+ rbFound = false;
+ rbAmbiguous = false;
+
+ Reference< XCoordinateSystemContainer > xCnt( xDiagram, uno::UNO_QUERY );
+ if (!xCnt.is())
+ return false;
+
+ const Sequence< Reference<XCoordinateSystem> > aCooSys = xCnt->getCoordinateSystems();
+
+ for (Reference<XCoordinateSystem> const & coords : aCooSys)
+ {
+ Reference<beans::XPropertySet> xProp(coords, uno::UNO_QUERY);
+ if (!xProp.is())
+ continue;
+
+ bool bCurrent = false;
+ if (xProp->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 Reference< XDiagram > & 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
+ uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+ if( !xCooSysContainer.is() )
+ return;
+ const uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ for( uno::Reference< XCoordinateSystem > const & xCooSys : aCooSysList )
+ {
+ //set correct percent stacking
+ const sal_Int32 nMaximumScaleIndex = xCooSys->getMaximumAxisIndexByDimension(1);
+ for(sal_Int32 nI=0; nI<=nMaximumScaleIndex; ++nI)
+ {
+ Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( 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
+ uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
+ if( !xChartTypeContainer.is() )
+ continue;
+ uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ if (!aChartTypeList.hasElements())
+ continue;
+
+ uno::Reference< XChartType > xChartType( aChartTypeList[0] );
+
+ //iterate through all series in this chart type
+ uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
+ OSL_ASSERT( xDataSeriesContainer.is());
+ if( !xDataSeriesContainer.is() )
+ continue;
+
+ const uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
+ for( uno::Reference< XDataSeries > const & dataSeries : aSeriesList )
+ {
+ Reference< beans::XPropertySet > xProp( dataSeries, uno::UNO_QUERY );
+ if(xProp.is())
+ xProp->setPropertyValue( "StackingDirection", aNewDirection );
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+}
+
+StackMode DiagramHelper::getStackMode( const Reference< XDiagram > & xDiagram, bool& rbFound, bool& rbAmbiguous )
+{
+ rbFound=false;
+ rbAmbiguous=false;
+
+ StackMode eGlobalStackMode = StackMode::NONE;
+
+ //iterate through all coordinate systems
+ uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+ if( !xCooSysContainer.is() )
+ return eGlobalStackMode;
+ const uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ for( uno::Reference< XCoordinateSystem > const & xCooSys : aCooSysList )
+ {
+ //iterate through all chart types in the current coordinate system
+ uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
+ if( !xChartTypeContainer.is() )
+ continue;
+ uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ for( sal_Int32 nT = 0; nT < aChartTypeList.getLength(); ++nT )
+ {
+ uno::Reference< XChartType > 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 Reference< XChartType > & xChartType,
+ bool& rbFound, bool& rbAmbiguous,
+ const Reference< XCoordinateSystem > & xCorrespondingCoordinateSystem )
+{
+ StackMode eStackMode = StackMode::NONE;
+ rbFound = false;
+ rbAmbiguous = false;
+
+ try
+ {
+ Reference< XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY_THROW );
+ Sequence< Reference< chart2::XDataSeries > > aSeries( xDSCnt->getDataSeries());
+
+ 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.getLength();
+ sal_Int32 i = (nSeriesCount == 1) ? 0: 1;
+ for( ; i<nSeriesCount; ++i )
+ {
+ rbFound = true;
+ Reference< beans::XPropertySet > xProp( aSeries[i], uno::UNO_QUERY_THROW );
+ chart2::StackingDirection eCurrentDirection = eCommonDirection;
+ // property is not MAYBEVOID
+ bool bSuccess = ( xProp->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]);
+
+ Reference< chart2::XAxis > xAxis(
+ xCorrespondingCoordinateSystem->getAxisByDimension( 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 Reference< XDiagram > & xDiagram )
+{
+ // -1: not yet set
+ sal_Int32 nResult = -1;
+
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
+ if( xCooSysCnt.is() )
+ {
+ const Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
+ xCooSysCnt->getCoordinateSystems());
+
+ for( Reference< XCoordinateSystem > const & xCooSys : aCooSysSeq )
+ {
+ if(xCooSys.is())
+ {
+ nResult = xCooSys->getDimension();
+ break;
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return nResult;
+}
+
+void DiagramHelper::setDimension(
+ const Reference< XDiagram > & 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:
+ Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ for( Reference<XCoordinateSystem> const & xOldCooSys : aCooSysList )
+ {
+ Reference< XCoordinateSystem > xNewCooSys;
+
+ Reference< XChartTypeContainer > xChartTypeContainer( xOldCooSys, uno::UNO_QUERY );
+ if( !xChartTypeContainer.is() )
+ continue;
+
+ const Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ for( Reference< XChartType > const & xChartType : aChartTypeList )
+ {
+ bIsSupportingOnlyDeepStackingFor3D = ChartTypeHelper::isSupportingOnlyDeepStackingFor3D( xChartType );
+ if(!xNewCooSys.is())
+ {
+ xNewCooSys = xChartType->createCoordinateSystem( nNewDimensionCount );
+ 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 Reference< XDiagram > & xDiagram,
+ const Reference< XCoordinateSystem > & xCooSysToReplace,
+ const Reference< XCoordinateSystem > & xReplacement )
+{
+ OSL_ASSERT( xDiagram.is());
+ if( ! xDiagram.is())
+ return;
+
+ // update the coordinate-system container
+ Reference< XCoordinateSystemContainer > xCont( xDiagram, uno::UNO_QUERY );
+ if( !xCont.is())
+ return;
+
+ try
+ {
+ Reference< chart2::data::XLabeledDataSequence > xCategories = DiagramHelper::getCategoriesFromDiagram( xDiagram );
+
+ // move chart types of xCooSysToReplace to xReplacement
+ Reference< XChartTypeContainer > xCTCntCooSys( xCooSysToReplace, uno::UNO_QUERY_THROW );
+ Reference< XChartTypeContainer > xCTCntReplacement( xReplacement, uno::UNO_QUERY_THROW );
+ xCTCntReplacement->setChartTypes( xCTCntCooSys->getChartTypes());
+
+ xCont->removeCoordinateSystem( xCooSysToReplace );
+ xCont->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 uno::Reference< chart2::XDiagram >& 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);
+ uno::Reference< chart2::XAxis > 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() )
+ {
+ uno::Reference< XAxis > 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;
+}
+
+uno::Reference< XAxis > DiagramHelper::getAttachedAxis(
+ const uno::Reference< XDataSeries >& xSeries,
+ const uno::Reference< XDiagram >& xDiagram )
+{
+ return AxisHelper::getAxis( 1, DiagramHelper::isSeriesAttachedToMainAxis( xSeries ), xDiagram );
+}
+
+uno::Reference< XChartType > DiagramHelper::getChartTypeOfSeries(
+ const uno::Reference< chart2::XDiagram >& xDiagram
+ , const uno::Reference< XDataSeries >& xGivenDataSeries )
+{
+ if( !xGivenDataSeries.is() )
+ return nullptr;
+ if(!xDiagram.is())
+ return nullptr;
+
+ //iterate through the model to find the given xSeries
+ //the found parent indicates the charttype
+
+ //iterate through all coordinate systems
+ uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+ if( !xCooSysContainer.is())
+ return nullptr;
+
+ const uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ for( uno::Reference< XCoordinateSystem > const & xCooSys : aCooSysList )
+ {
+ //iterate through all chart types in the current coordinate system
+ uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
+ OSL_ASSERT( xChartTypeContainer.is());
+ if( !xChartTypeContainer.is() )
+ continue;
+ const uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ for( uno::Reference< XChartType > const & xChartType : aChartTypeList )
+ {
+ //iterate through all series in this chart type
+ uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY );
+ OSL_ASSERT( xDataSeriesContainer.is());
+ if( !xDataSeriesContainer.is() )
+ continue;
+
+ const uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
+ for( uno::Reference< XDataSeries > const & dataSeries : aSeriesList )
+ {
+ if( xGivenDataSeries==dataSeries )
+ return xChartType;
+ }
+ }
+ }
+ return nullptr;
+}
+
+std::vector< Reference< XDataSeries > >
+ DiagramHelper::getDataSeriesFromDiagram(
+ const Reference< XDiagram > & xDiagram )
+{
+ std::vector< Reference< XDataSeries > > aResult;
+
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCooSysCnt(
+ xDiagram, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
+ xCooSysCnt->getCoordinateSystems());
+ for( Reference< XCoordinateSystem > const & coords : aCooSysSeq )
+ {
+ Reference< XChartTypeContainer > xCTCnt( coords, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
+ for( Reference< XChartType> const & chartType : aChartTypeSeq )
+ {
+ Reference< XDataSeriesContainer > xDSCnt( chartType, uno::UNO_QUERY_THROW );
+ Sequence< Reference< XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries() );
+ std::copy( aSeriesSeq.begin(), aSeriesSeq.end(),
+ std::back_inserter( aResult ));
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+
+ return aResult;
+}
+
+Sequence< Sequence< Reference< XDataSeries > > >
+ DiagramHelper::getDataSeriesGroups( const Reference< XDiagram > & xDiagram )
+{
+ vector< Sequence< Reference< XDataSeries > > > aResult;
+
+ //iterate through all coordinate systems
+ Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+ if( xCooSysContainer.is() )
+ {
+ const Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ for( Reference< XCoordinateSystem > const & coords : aCooSysList )
+ {
+ //iterate through all chart types in the current coordinate system
+ Reference< XChartTypeContainer > xChartTypeContainer( coords, uno::UNO_QUERY );
+ if( !xChartTypeContainer.is() )
+ continue;
+ const Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ for( Reference< XChartType > const & chartType : aChartTypeList )
+ {
+ Reference< XDataSeriesContainer > xDataSeriesContainer( chartType, uno::UNO_QUERY );
+ if( !xDataSeriesContainer.is() )
+ continue;
+ aResult.push_back( xDataSeriesContainer->getDataSeries() );
+ }
+ }
+ }
+ return comphelper::containerToSequence( aResult );
+}
+
+Reference< XChartType >
+ DiagramHelper::getChartTypeByIndex( const Reference< XDiagram >& xDiagram, sal_Int32 nIndex )
+{
+ Reference< XChartType > xChartType;
+
+ //iterate through all coordinate systems
+ Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+ if( ! xCooSysContainer.is())
+ return xChartType;
+
+ const Sequence< Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+ sal_Int32 nTypesSoFar = 0;
+ for( Reference< XCoordinateSystem > const & coords : aCooSysList )
+ {
+ Reference< XChartTypeContainer > xChartTypeContainer( coords, uno::UNO_QUERY );
+ if( !xChartTypeContainer.is() )
+ continue;
+ Sequence< Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ if( nIndex >= 0 && nIndex < (nTypesSoFar + aChartTypeList.getLength()) )
+ {
+ xChartType.set( aChartTypeList[nIndex - nTypesSoFar] );
+ break;
+ }
+ nTypesSoFar += aChartTypeList.getLength();
+ }
+
+ return xChartType;
+}
+
+namespace
+{
+
+std::vector< Reference< XAxis > > lcl_getAxisHoldingCategoriesFromDiagram(
+ const Reference< XDiagram > & xDiagram )
+{
+ std::vector< Reference< XAxis > > aRet;
+
+ // return first x-axis as fall-back
+ Reference< XAxis > xFallBack;
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCooSysCnt(
+ xDiagram, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
+ xCooSysCnt->getCoordinateSystems());
+ for( Reference< XCoordinateSystem > const & xCooSys : aCooSysSeq )
+ {
+ 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)
+ {
+ Reference< XAxis > xAxis = xCooSys->getAxisByDimension( 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.set( xAxis );
+ }
+ }
+ }
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2" );
+ }
+
+ if( aRet.empty() )
+ aRet.push_back(xFallBack);
+
+ return aRet;
+}
+
+} // anonymous namespace
+
+bool DiagramHelper::isCategoryDiagram(
+ const Reference< XDiagram >& xDiagram )
+{
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCooSysCnt(
+ xDiagram, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
+ xCooSysCnt->getCoordinateSystems());
+ for( Reference< XCoordinateSystem > const & xCooSys : aCooSysSeq )
+ {
+ 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)
+ {
+ Reference< XAxis > xAxis = xCooSys->getAxisByDimension( 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 Reference< chart2::data::XLabeledDataSequence >& xCategories,
+ const Reference< XDiagram >& xDiagram,
+ bool bSetAxisType /* = false */,
+ bool bCategoryAxis /* = true */ )
+{
+ std::vector< Reference< chart2::XAxis > > aCatAxes(
+ lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));
+
+ for (const Reference< chart2::XAxis >& 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 );
+ }
+ }
+}
+
+Reference< data::XLabeledDataSequence >
+ DiagramHelper::getCategoriesFromDiagram(
+ const Reference< XDiagram > & xDiagram )
+{
+ Reference< data::XLabeledDataSequence > xResult;
+
+ try
+ {
+ std::vector< Reference< chart2::XAxis > > aCatAxes(
+ lcl_getAxisHoldingCategoriesFromDiagram( xDiagram ));
+ //search for first categories
+ if (!aCatAxes.empty())
+ {
+ Reference< chart2::XAxis > xCatAxis(aCatAxes[0]);
+ if( xCatAxis.is())
+ {
+ ScaleData aScaleData( xCatAxis->getScaleData());
+ if( aScaleData.Categories.is() )
+ {
+ xResult.set( aScaleData.Categories );
+ uno::Reference<beans::XPropertySet> xProp(aScaleData.Categories->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 Reference< XChartType >& xChartType )
+{
+ if(!xChartType.is())
+ return;
+ OUString aMainSeq( xChartType->getRoleOfSequenceForSeriesLabel() );
+ Reference< XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY );
+ if( !xSeriesCnt.is() )
+ return;
+
+ const Sequence< Reference< XDataSeries > > aSeriesSeq( xSeriesCnt->getDataSeries() );
+ for( Reference< XDataSeries > const & dataSeries : aSeriesSeq )
+ {
+ Reference< data::XDataSource > xDataSource( dataSeries, uno::UNO_QUERY );
+ if( !xDataSource.is() )
+ continue;
+ Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
+ ::chart::DataSeriesHelper::getDataSequenceByRole( xDataSource, 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 Reference< XCoordinateSystem > & xCooSys )
+{
+ Sequence< OUString > aRet;
+
+ Reference< XChartTypeContainer > xTypeCntr( xCooSys, uno::UNO_QUERY );
+ if( xTypeCntr.is() )
+ {
+ const Sequence< Reference< XChartType > > aChartTypes( xTypeCntr->getChartTypes() );
+ for( Reference< XChartType > const & chartType : aChartTypes )
+ {
+ lcl_generateAutomaticCategoriesFromChartType( aRet, chartType );
+ if( aRet.hasElements() )
+ return aRet;
+ }
+ }
+ return aRet;
+}
+
+Sequence< OUString > DiagramHelper::getExplicitSimpleCategories(
+ ChartModel& rModel )
+{
+ uno::Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( rModel ) );
+ ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSys, rModel );
+ return aExplicitCategoriesProvider.getSimpleCategories();
+}
+
+namespace
+{
+void lcl_switchToDateCategories( const Reference< XChartDocument >& 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() );
+ double fTest = 0.0;
+ double fNan = 0.0;
+ ::rtl::math::setNan( & fNan );
+ sal_Int32 nN = aAnyCategories.getLength();
+ for( ; nN--; )
+ {
+ Sequence< Any >& rCat = aAnyCategories[nN];
+ if( rCat.getLength() > 1 )
+ rCat.realloc(1);
+ if( rCat.getLength() == 1 )
+ {
+ Any& rAny = rCat[0];
+ if( !(rAny>>=fTest) )
+ {
+ rAny <<= fNan;
+ }
+ }
+ }
+ xDataAccess->setAnyRowDescriptions( aAnyCategories );
+ }
+ //check the numberformat at the axis
+ Reference< beans::XPropertySet > xAxisProps( xAxis, uno::UNO_QUERY );
+ Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( xChartDoc, uno::UNO_QUERY );
+ if( xAxisProps.is() && xNumberFormatsSupplier.is() )
+ {
+ sal_Int32 nNumberFormat = -1;
+ xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormat;
+
+ Reference< util::XNumberFormats > xNumberFormats( xNumberFormatsSupplier->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 Reference< XChartDocument >& 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 Reference< XChartDocument >& xChartDoc )
+{
+ if(xChartDoc.is())
+ {
+ ControllerLockGuardUNO aCtrlLockGuard( xChartDoc );
+
+ Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartDoc ) );
+ if( xCooSys.is() )
+ {
+ Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) );
+ lcl_switchToDateCategories( xChartDoc, xAxis );
+ }
+ }
+}
+
+void DiagramHelper::switchToTextCategories( const Reference< XChartDocument >& xChartDoc )
+{
+ if(xChartDoc.is())
+ {
+ ControllerLockGuardUNO aCtrlLockGuard( xChartDoc );
+
+ Reference< chart2::XCoordinateSystem > xCooSys( ChartModelHelper::getFirstCoordinateSystem( xChartDoc ) );
+ if( xCooSys.is() )
+ {
+ Reference< XAxis > xAxis( xCooSys->getAxisByDimension(0,0) );
+ lcl_switchToTextCategories( xChartDoc, xAxis );
+ }
+ }
+}
+
+bool DiagramHelper::isSupportingDateAxis( const Reference< chart2::XDiagram >& 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, LANGUAGE_SYSTEM, 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;
+}
+
+Sequence< Reference< XChartType > >
+ DiagramHelper::getChartTypesFromDiagram(
+ const Reference< XDiagram > & xDiagram )
+{
+ std::vector< Reference< XChartType > > aResult;
+
+ if(xDiagram.is())
+ {
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCooSysCnt(
+ xDiagram, uno::UNO_QUERY_THROW );
+ const Sequence< Reference< XCoordinateSystem > > aCooSysSeq(
+ xCooSysCnt->getCoordinateSystems());
+ for( Reference< XCoordinateSystem > const & coords : aCooSysSeq )
+ {
+ Reference< XChartTypeContainer > xCTCnt( coords, uno::UNO_QUERY_THROW );
+ Sequence< Reference< XChartType > > aChartTypeSeq( xCTCnt->getChartTypes());
+ std::copy( aChartTypeSeq.begin(), aChartTypeSeq.end(),
+ std::back_inserter( aResult ));
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ }
+
+ return comphelper::containerToSequence( aResult );
+}
+
+bool DiagramHelper::areChartTypesCompatible( const Reference< ::chart2::XChartType >& xFirstType,
+ const Reference< ::chart2::XChartType >& 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 Reference< XDiagram >& xDiagram,
+ const Reference< XDataSeries >& xGivenDataSeries,
+ bool bForward,
+ bool bDoMove )
+{
+ bool bMovedOrMoveAllowed = false;
+
+ try
+ {
+ uno::Reference< XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY );
+
+ if( xGivenDataSeries.is() && xCooSysContainer.is() )
+ {
+ //find position of series.
+ bool bFound = false;
+ uno::Sequence< uno::Reference< XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() );
+
+ for( sal_Int32 nCS = 0; !bFound && nCS < aCooSysList.getLength(); ++nCS )
+ {
+ uno::Reference< XCoordinateSystem > xCooSys( aCooSysList[nCS] );
+
+ //iterate through all chart types in the current coordinate system
+ uno::Reference< XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY );
+ OSL_ASSERT( xChartTypeContainer.is());
+ if( !xChartTypeContainer.is() )
+ continue;
+ uno::Sequence< uno::Reference< XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() );
+ uno::Reference< XChartType > xFormerChartType;
+
+ for( sal_Int32 nT = 0; !bFound && nT < aChartTypeList.getLength(); ++nT )
+ {
+ uno::Reference< XChartType > xCurrentChartType( aChartTypeList[nT] );
+
+ //iterate through all series in this chart type
+ uno::Reference< XDataSeriesContainer > xDataSeriesContainer( xCurrentChartType, uno::UNO_QUERY );
+ OSL_ASSERT( xDataSeriesContainer.is());
+ if( !xDataSeriesContainer.is() )
+ continue;
+
+ uno::Sequence< uno::Reference< XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() );
+
+ for( sal_Int32 nS = 0; !bFound && nS < aSeriesList.getLength(); ++nS )
+ {
+
+ // We found the series we are interested in!
+ if( xGivenDataSeries==aSeriesList[nS] )
+ {
+ sal_Int32 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 && nNewSeriesIndex < aSeriesList.getLength() )
+ {
+ //move series in the same charttype
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ aSeriesList[ nOldSeriesIndex ] = aSeriesList[ nNewSeriesIndex ];
+ aSeriesList[ nNewSeriesIndex ] = xGivenDataSeries;
+ xDataSeriesContainer->setDataSeries( aSeriesList );
+ }
+ }
+ else if( nNewSeriesIndex<0 )
+ {
+ //exchange series with former charttype
+ if( xFormerChartType.is() && DiagramHelper::areChartTypesCompatible( xFormerChartType, xCurrentChartType ) )
+ {
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xFormerChartType, uno::UNO_QUERY );
+ if( xOtherDataSeriesContainer.is() )
+ {
+ uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() );
+ sal_Int32 nOtherSeriesIndex = aOtherSeriesList.getLength()-1;
+ if( nOtherSeriesIndex >= 0 && nOtherSeriesIndex < aOtherSeriesList.getLength() )
+ {
+ uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[nOtherSeriesIndex] );
+ aOtherSeriesList[nOtherSeriesIndex] = xGivenDataSeries;
+ xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList);
+
+ aSeriesList[nOldSeriesIndex]=xExchangeSeries;
+ xDataSeriesContainer->setDataSeries(aSeriesList);
+ }
+ }
+ }
+ }
+ }
+ else if( nT+1 < aChartTypeList.getLength() )
+ {
+ //exchange series with next charttype
+ uno::Reference< XChartType > xOtherChartType( aChartTypeList[nT+1] );
+ if( xOtherChartType.is() && DiagramHelper::areChartTypesCompatible( xOtherChartType, xCurrentChartType ) )
+ {
+ bMovedOrMoveAllowed = true;
+ if( bDoMove )
+ {
+ uno::Reference< XDataSeriesContainer > xOtherDataSeriesContainer( xOtherChartType, uno::UNO_QUERY );
+ if( xOtherDataSeriesContainer.is() )
+ {
+ uno::Sequence< uno::Reference< XDataSeries > > aOtherSeriesList( xOtherDataSeriesContainer->getDataSeries() );
+ if( aOtherSeriesList.hasElements() )
+ {
+ uno::Reference< XDataSeries > xExchangeSeries( aOtherSeriesList[0] );
+ aOtherSeriesList[0] = xGivenDataSeries;
+ xOtherDataSeriesContainer->setDataSeries(aOtherSeriesList);
+
+ aSeriesList[nOldSeriesIndex]=xExchangeSeries;
+ xDataSeriesContainer->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 Reference< XDiagram >& xDiagram,
+ const Reference< XDataSeries >& xGivenDataSeries,
+ bool bForward )
+{
+ const bool bDoMove = false;
+
+ bool bIsMoveable = lcl_moveSeriesOrCheckIfMoveIsAllowed(
+ xDiagram, xGivenDataSeries, bForward, bDoMove );
+
+ return bIsMoveable;
+}
+
+bool DiagramHelper::moveSeries( const Reference< XDiagram >& xDiagram, const Reference< XDataSeries >& xGivenDataSeries, bool bForward )
+{
+ const bool bDoMove = true;
+
+ bool bMoved = lcl_moveSeriesOrCheckIfMoveIsAllowed(
+ xDiagram, xGivenDataSeries, bForward, bDoMove );
+
+ return bMoved;
+}
+
+bool DiagramHelper::isSupportingFloorAndWall( const Reference<
+ chart2::XDiagram >& 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 Sequence< Reference< chart2::XChartType > > aTypes(
+ ::chart::DiagramHelper::getChartTypesFromDiagram( xDiagram ) );
+ for( Reference< chart2::XChartType > 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 css::uno::Reference< css::chart2::XDiagram >& xDiagram )
+{
+ uno::Reference< chart2::XChartType > 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 uno::Reference< chart2::XDiagram > & xDiagram,
+ bool& rbFound, bool& rbAmbiguous )
+{
+ sal_Int32 nCommonGeom( DataPointGeometry3D::CUBOID );
+ rbFound = false;
+ rbAmbiguous = false;
+
+ std::vector< Reference< chart2::XDataSeries > > aSeriesVec(
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram ));
+
+ if( aSeriesVec.empty())
+ rbAmbiguous = true;
+
+ for (auto const& series : aSeriesVec)
+ {
+ try
+ {
+ sal_Int32 nGeom = 0;
+ Reference< beans::XPropertySet > xProp(series, uno::UNO_QUERY_THROW);
+ if( xProp->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 Reference< chart2::XDiagram > & xDiagram,
+ sal_Int32 nNewGeometry )
+{
+ std::vector< Reference< chart2::XDataSeries > > aSeriesVec(
+ DiagramHelper::getDataSeriesFromDiagram( xDiagram ));
+
+ for (auto const& series : aSeriesVec)
+ {
+ DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints(
+ series, "Geometry3D", uno::Any( nNewGeometry ));
+ }
+}
+
+sal_Int32 DiagramHelper::getCorrectedMissingValueTreatment(
+ const Reference< chart2::XDiagram > & xDiagram,
+ const Reference< chart2::XChartType >& xChartType )
+{
+ sal_Int32 nResult = css::chart::MissingValueTreatment::LEAVE_GAP;
+ const uno::Sequence < sal_Int32 > aAvailableMissingValueTreatments(
+ ChartTypeHelper::getSupportedMissingValueTreatments( xChartType ) );
+
+ uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY );
+ if( xDiaProp.is() && (xDiaProp->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 uno::Reference<
+ chart2::XDiagram > & xDiagram )
+{
+ DiagramPositioningMode eMode = DiagramPositioningMode_AUTO;
+ uno::Reference< beans::XPropertySet > xDiaProps( xDiagram, uno::UNO_QUERY );
+ if( xDiaProps.is() )
+ {
+ RelativePosition aRelPos;
+ RelativeSize aRelSize;
+ if( (xDiaProps->getPropertyValue("RelativePosition") >>= aRelPos ) &&
+ (xDiaProps->getPropertyValue("RelativeSize") >>= aRelSize ) )
+ {
+ bool bPosSizeExcludeAxes=false;
+ xDiaProps->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 uno::Reference< frame::XModel >& xChartModel,
+ const awt::Rectangle& rPosRect /*100th mm*/ )
+{
+ ControllerLockGuardUNO aCtrlLockGuard( xChartModel );
+
+ bool bChanged = false;
+ awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );
+ uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY );
+ if( !xDiaProps.is() )
+ return bChanged;
+
+ RelativePosition aOldPos;
+ RelativeSize aOldSize;
+ xDiaProps->getPropertyValue("RelativePosition" ) >>= aOldPos;
+ xDiaProps->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;
+
+ xDiaProps->setPropertyValue( "RelativePosition", uno::Any(aNewPos) );
+ xDiaProps->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 uno::Reference< frame::XModel >& xChartModel )
+{
+ awt::Rectangle aRet(-1,-1,-1,-1);
+
+ uno::Reference< beans::XPropertySet > xDiaProps( ChartModelHelper::findDiagram( xChartModel ), uno::UNO_QUERY );
+ if( !xDiaProps.is() )
+ return aRet;
+
+ awt::Size aPageSize( ChartModelHelper::getPageSize(xChartModel) );
+
+ RelativePosition aRelPos;
+ RelativeSize aRelSize;
+ xDiaProps->getPropertyValue("RelativePosition" ) >>= aRelPos;
+ xDiaProps->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(SvtSaveOptions().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: */