diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /chart2/source/tools | |
parent | Initial commit. (diff) | |
download | libreoffice-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')
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: */ |