diff options
Diffstat (limited to 'xmloff/source/chart/SchXMLSeries2Context.cxx')
-rw-r--r-- | xmloff/source/chart/SchXMLSeries2Context.cxx | 1308 |
1 files changed, 1308 insertions, 0 deletions
diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx new file mode 100644 index 0000000000..cf4e33e769 --- /dev/null +++ b/xmloff/source/chart/SchXMLSeries2Context.cxx @@ -0,0 +1,1308 @@ +/* -*- 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 "SchXMLSeries2Context.hxx" +#include "SchXMLPlotAreaContext.hxx" +#include "SchXMLRegressionCurveObjectContext.hxx" +#include "SchXMLPropertyMappingContext.hxx" +#include "SchXMLTools.hxx" + +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XRegressionCurve.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> + +#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp> +#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp> +#include <com/sun/star/chart2/DataPointCustomLabelField.hpp> + +#include <com/sun/star/chart/ChartAxisAssign.hpp> +#include <com/sun/star/chart/ChartSymbolType.hpp> +#include <com/sun/star/chart/ChartDataCaption.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/chart/ChartLegendPosition.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> + +#include <comphelper/processfactory.hxx> + +#include <sal/log.hxx> +#include <utility> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/SchXMLSeriesHelper.hxx> +#include <SchXMLImport.hxx> +#include <xmloff/prstylei.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <algorithm> // std::find_if + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +class SchXMLDomain2Context : public SvXMLImportContext +{ +private: + ::std::vector< OUString > & mrAddresses; + +public: + SchXMLDomain2Context( SvXMLImport& rImport, + ::std::vector< OUString > & rAddresses ); + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +SchXMLDomain2Context::SchXMLDomain2Context( + SvXMLImport& rImport, + ::std::vector< OUString > & rAddresses ) : + SvXMLImportContext( rImport ), + mrAddresses( rAddresses ) +{ +} + +void SchXMLDomain2Context::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if (aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) ) + mrAddresses.push_back( aIter.toString() ); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } +} + +void lcl_setAutomaticSymbolSize( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport ) +{ + awt::Size aSymbolSize(140,140);//old default for standard sized charts 7cm height + + uno::Reference< chart::XChartDocument > xChartDoc( rImport.GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is() ) + { + double fScale = 1; + uno::Reference< beans::XPropertySet > xLegendProp( xChartDoc->getLegend(), uno::UNO_QUERY ); + chart::ChartLegendPosition aLegendPosition = chart::ChartLegendPosition_NONE; + if( xLegendProp.is() && (xLegendProp->getPropertyValue("Alignment") >>= aLegendPosition) + && chart::ChartLegendPosition_NONE != aLegendPosition ) + { + + double fFontHeight = 6.0; + if( xLegendProp->getPropertyValue("CharHeight") >>= fFontHeight ) + fScale = 0.75*fFontHeight/6.0; + } + else + { + uno::Reference< embed::XVisualObject > xVisualObject( rImport.GetModel(), uno::UNO_QUERY ); + if( xVisualObject.is() ) + { + awt::Size aPageSize( xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); + fScale = aPageSize.Height/7000.0; + } + } + if( fScale>0 ) + { + aSymbolSize.Height = static_cast<sal_Int32>( fScale * aSymbolSize.Height ); + aSymbolSize.Width = aSymbolSize.Height; + } + } + xSeriesOrPointProp->setPropertyValue("SymbolSize",uno::Any( aSymbolSize )); +} + +void lcl_setSymbolSizeIfNeeded( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport ) +{ + if( !xSeriesOrPointProp.is() ) + return; + + sal_Int32 nSymbolType = chart::ChartSymbolType::NONE; + if( !(xSeriesOrPointProp.is() && ( xSeriesOrPointProp->getPropertyValue("SymbolType") >>= nSymbolType)) ) + return; + + if(chart::ChartSymbolType::NONE!=nSymbolType) + { + if( chart::ChartSymbolType::BITMAPURL==nSymbolType ) + { + //set special size for graphics to indicate to use the bitmap size itself + xSeriesOrPointProp->setPropertyValue("SymbolSize",uno::Any( awt::Size(-1,-1) )); + } + else + { + lcl_setAutomaticSymbolSize( xSeriesOrPointProp, rImport ); + } + } +} + +void lcl_resetSymbolSizeForPointsIfNecessary( const uno::Reference< beans::XPropertySet >& xPointProp, const SvXMLImport& rImport + , const XMLPropStyleContext * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize", pPropStyleContext, pStylesCtxt ) ); + if( !aASymbolSize.hasValue() ) + lcl_setSymbolSizeIfNeeded( xPointProp, rImport ); +} + +void lcl_setLinkNumberFormatToSourceIfNeeded( const uno::Reference< beans::XPropertySet >& xPointProp + , const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + uno::Any aAny( SchXMLTools::getPropertyFromContext(u"LinkNumberFormatToSource", pPropStyleContext, pStylesCtxt) ); + if( aAny.hasValue() ) + return; + + if( !xPointProp.is() ) + return; + + bool bLinkToSource = false; + if( xPointProp.is() && (xPointProp->getPropertyValue("LinkNumberFormatToSource") >>= bLinkToSource) ) + { + if( bLinkToSource ) + { + xPointProp->setPropertyValue("LinkNumberFormatToSource", uno::Any(false)); + } + } +} + +void lcl_insertErrorBarLSequencesToMap( + tSchXMLLSequencesPerIndex & rInOutMap, + const uno::Reference< beans::XPropertySet > & xSeriesProp ) +{ + Reference< chart2::data::XDataSource > xErrorBarSource; + if( ( xSeriesProp->getPropertyValue( "ErrorBarY" ) >>= xErrorBarSource ) && + xErrorBarSource.is() ) + { + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSequences( + xErrorBarSource->getDataSequences()); + for( const auto& rLSequence : aLSequences ) + { + // use "0" as data index. This is ok, as it is not used for error bars + rInOutMap.emplace( + tSchXMLIndexWithPart( 0, SCH_XML_PART_ERROR_BARS ), rLSequence ); + } + } +} + +Reference< chart2::data::XLabeledDataSequence2 > lcl_createAndAddSequenceToSeries( const OUString& rRole + , const OUString& rRange + , const Reference< chart2::XChartDocument >& xChartDoc + , const Reference< chart2::XDataSeries >& xSeries ) +{ + Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq; + + Reference< chart2::data::XDataSource > xSeriesSource( xSeries,uno::UNO_QUERY ); + Reference< chart2::data::XDataSink > xSeriesSink( xSeries, uno::UNO_QUERY ); + + if( !(!rRange.isEmpty() && xChartDoc.is() && xSeriesSource.is() && xSeriesSink.is()) ) + return xLabeledSeq; + + // create a new sequence + xLabeledSeq = SchXMLTools::GetNewLabeledDataSequence(); + + // set values at the new sequence + Reference< chart2::data::XDataSequence > xSeq = SchXMLTools::CreateDataSequence( rRange, xChartDoc ); + Reference< beans::XPropertySet > xSeqProp( xSeq, uno::UNO_QUERY ); + if( xSeqProp.is()) + xSeqProp->setPropertyValue("Role", uno::Any( rRole)); + xLabeledSeq->setValues( xSeq ); + + // add new sequence to data series / push to front to have the correct sequence order if charttype is changed afterwards + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aOldSeq( xSeriesSource->getDataSequences()); + sal_Int32 nOldCount = aOldSeq.getLength(); + Sequence< Reference< chart2::data::XLabeledDataSequence > > aNewSeq( nOldCount + 1 ); + auto pNewSeq = aNewSeq.getArray(); + pNewSeq[0].set(xLabeledSeq, uno::UNO_QUERY_THROW); + std::copy(aOldSeq.begin(), aOldSeq.end(), std::next(pNewSeq)); + xSeriesSink->setData( aNewSeq ); + + return xLabeledSeq; +} + +XMLPropStyleContext* lcl_GetStylePropContext( + const SvXMLStylesContext* pStylesCtxt, + const SvXMLStyleContext*& rpStyle, + OUString const & rStyleName ) +{ + rpStyle = pStylesCtxt->FindStyleChildContext( SchXMLImportHelper::GetChartFamilyID(), rStyleName ); + XMLPropStyleContext* pPropStyleContext = + const_cast< XMLPropStyleContext* >(dynamic_cast< const XMLPropStyleContext* >( rpStyle )); + return pPropStyleContext; +} + +} // anonymous namespace + +SchXMLSeries2Context::SchXMLSeries2Context( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const Reference< chart2::XChartDocument > & xNewDoc, + std::vector< SchXMLAxis >& rAxes, + ::std::vector< DataRowPointStyle >& rStyleVector, + ::std::vector< RegressionStyle >& rRegressionStyleVector, + sal_Int32 nSeriesIndex, + bool bStockHasVolume, + GlobalSeriesImportInfo& rGlobalSeriesImportInfo, + const OUString & aGlobalChartTypeName, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + bool& rGlobalChartTypeUsedBySeries, + const awt::Size & rChartSize ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mxNewDoc( xNewDoc ), + mrAxes( rAxes ), + mrStyleVector( rStyleVector ), + mrRegressionStyleVector( rRegressionStyleVector ), + mnSeriesIndex( nSeriesIndex ), + mnDataPointIndex( 0 ), + m_bStockHasVolume( bStockHasVolume ), + m_rGlobalSeriesImportInfo(rGlobalSeriesImportInfo), + mpAttachedAxis( nullptr ), + mnAttachedAxis( 0 ), + maGlobalChartTypeName( aGlobalChartTypeName ), + maSeriesChartTypeName( aGlobalChartTypeName ), + m_bHasDomainContext(false), + mrLSequencesPerIndex( rLSequencesPerIndex ), + mrGlobalChartTypeUsedBySeries( rGlobalChartTypeUsedBySeries ), + mbSymbolSizeIsMissingInFile(false), + maChartSize( rChartSize ), + // A series manages the DataRowPointStyle-struct of a data-label child element. + mDataLabel(DataRowPointStyle::DATA_LABEL_SERIES, OUString{}) +{ + if( aGlobalChartTypeName == "com.sun.star.chart2.DonutChartType" ) + { + maSeriesChartTypeName = "com.sun.star.chart2.PieChartType"; + maGlobalChartTypeName = maSeriesChartTypeName; + } +} + +SchXMLSeries2Context::~SchXMLSeries2Context() +{ + SAL_WARN_IF( !maPostponedSequences.empty(), "xmloff.chart", "maPostponedSequences is NULL"); +} + +void SchXMLSeries2Context::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // parse attributes + mnAttachedAxis = 1; + + bool bHasRange = false; + OUString aSeriesLabelRange; + OUString aSeriesLabelString; + bool bHideLegend = false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + OUString aValue = aIter.toString(); + switch(aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_VALUES_CELL_RANGE_ADDRESS): + m_aSeriesRange = aValue; + bHasRange = true; + break; + case XML_ELEMENT(CHART, XML_LABEL_CELL_ADDRESS): + aSeriesLabelRange = aValue; + break; + case XML_ELEMENT(LO_EXT, XML_LABEL_STRING): + aSeriesLabelString = aValue; + break; + case XML_ELEMENT(CHART, XML_ATTACHED_AXIS): + { + sal_Int32 nNumOfAxes = mrAxes.size(); + for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ ) + { + if( aValue == mrAxes[ nCurrent ].aName && + mrAxes[ nCurrent ].eDimension == SCH_XML_AXIS_Y ) + { + mpAttachedAxis = &( mrAxes[ nCurrent ] ); + } + } + } + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + msAutoStyleName = aValue; + break; + case XML_ELEMENT(CHART, XML_CLASS): + { + OUString aClassName; + sal_uInt16 nClassPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrValueQName( + aValue, &aClassName ); + if( XML_NAMESPACE_CHART == nClassPrefix ) + maSeriesChartTypeName = SchXMLTools::GetChartTypeByClassName( aClassName, false /* bUseOldNames */ ); + + if( maSeriesChartTypeName.isEmpty()) + maSeriesChartTypeName = aClassName; + } + break; + case XML_ELEMENT(LO_EXT, XML_HIDE_LEGEND): + bHideLegend = aValue.toBoolean(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( mpAttachedAxis ) + { + if( mpAttachedAxis->nAxisIndex > 0 ) + { + // secondary axis => property has to be set (primary is default) + mnAttachedAxis = 2; + } + } + + try + { + SAL_WARN_IF( !mxNewDoc.is(), "xmloff.chart", "mxNewDoc is NULL"); + if( m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable && ! bHasRange ) + m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable = false; + + bool bIsCandleStick = maGlobalChartTypeName == "com.sun.star.chart2.CandleStickChartType"; + if( !maSeriesChartTypeName.isEmpty() ) + { + bIsCandleStick = maSeriesChartTypeName == "com.sun.star.chart2.CandleStickChartType"; + } + else + { + if( bIsCandleStick + && m_bStockHasVolume + && mnSeriesIndex == 0 ) + { + maSeriesChartTypeName = "com.sun.star.chart2.ColumnChartType"; + bIsCandleStick = false; + } + else + { + maSeriesChartTypeName = maGlobalChartTypeName; + } + } + if( ! mrGlobalChartTypeUsedBySeries ) + mrGlobalChartTypeUsedBySeries = (maSeriesChartTypeName == maGlobalChartTypeName); + sal_Int32 const nCoordinateSystemIndex = 0;//so far we can only import one coordinate system + m_xSeries.set( + SchXMLImportHelper::GetNewDataSeries( mxNewDoc, nCoordinateSystemIndex, maSeriesChartTypeName, ! mrGlobalChartTypeUsedBySeries )); + Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( SchXMLTools::GetNewLabeledDataSequence(), uno::UNO_QUERY_THROW ); + + Reference< beans::XPropertySet > xSeriesProp( m_xSeries, uno::UNO_QUERY ); + if (xSeriesProp.is()) + { + if (bHideLegend) + xSeriesProp->setPropertyValue("ShowLegendEntry", uno::Any(false)); + + if( bIsCandleStick ) + { + // set default color for range-line to black (before applying styles) + xSeriesProp->setPropertyValue("Color", + uno::Any( sal_Int32( 0x000000 ))); // black + } + else if ( maSeriesChartTypeName == "com.sun.star.chart2.PieChartType" ) + { + //@todo: this property should be saved + xSeriesProp->setPropertyValue("VaryColorsByPoint", + uno::Any( true )); + } + + } + + Reference<chart2::data::XDataProvider> xDataProvider(mxNewDoc->getDataProvider()); + Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xDataProvider, uno::UNO_QUERY); + + Reference<chart2::data::XDataSequence> xSequenceValues; + + // values + if (xPivotTableDataProvider.is()) // is pivot chart + { + xSequenceValues.set(xPivotTableDataProvider->createDataSequenceOfValuesByIndex(mnSeriesIndex)); + } + else + { + if (bHasRange && !m_aSeriesRange.isEmpty()) + xSequenceValues = SchXMLTools::CreateDataSequence(m_aSeriesRange, mxNewDoc); + } + + Reference<beans::XPropertySet> xSeqProp(xSequenceValues, uno::UNO_QUERY); + if (xSeqProp.is()) + { + OUString aMainRole("values-y"); + if (maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType") + aMainRole = "values-size"; + xSeqProp->setPropertyValue("Role", uno::Any(aMainRole)); + } + xLabeledSeq->setValues(xSequenceValues); + + // register for setting local data if external data provider is not present + maPostponedSequences.emplace( + tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_VALUES ), xLabeledSeq ); + + // label + Reference<chart2::data::XDataSequence> xSequenceLabel; + + if (xPivotTableDataProvider.is()) + { + xSequenceLabel.set(xPivotTableDataProvider->createDataSequenceOfLabelsByIndex(mnSeriesIndex)); + } + else + { + if (!aSeriesLabelRange.isEmpty()) + { + xSequenceLabel.set(SchXMLTools::CreateDataSequence(aSeriesLabelRange, mxNewDoc)); + } + else if (!aSeriesLabelString.isEmpty()) + { + xSequenceLabel.set(SchXMLTools::CreateDataSequenceWithoutConvert(aSeriesLabelString, mxNewDoc)); + } + } + + //Labels should always include hidden cells + Reference<beans::XPropertySet> xSeqLabelProp(xSequenceLabel, uno::UNO_QUERY); + if (xSeqLabelProp.is() && xSeqLabelProp->getPropertySetInfo()->hasPropertyByName("IncludeHiddenCells")) + { + xSeqLabelProp->setPropertyValue( "IncludeHiddenCells", uno::Any(true)); + } + + xLabeledSeq->setLabel(xSequenceLabel); + + // Note: Even if we have no label, we have to register the label + // for creation, because internal data always has labels. If + // they don't exist in the original, auto-generated labels are + // used for the internal data. + maPostponedSequences.emplace( + tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_LABEL ), xLabeledSeq ); + + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( &xLabeledSeq, 1 ); + Reference< chart2::data::XDataSink > xSink( m_xSeries, uno::UNO_QUERY_THROW ); + xSink->setData( aSeq ); + } + catch( const uno::Exception &) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + + //init mbSymbolSizeIsMissingInFile: + try + { + if( !msAutoStyleName.isEmpty() ) + { + const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); + if( pStylesCtxt ) + { + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), msAutoStyleName ); + + const XMLPropStyleContext* pPropStyleContext = dynamic_cast< const XMLPropStyleContext * >( pStyle ); + + uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize" + , pPropStyleContext, pStylesCtxt ) ); + mbSymbolSizeIsMissingInFile = !aASymbolSize.hasValue(); + } + } + } + catch( const uno::Exception & ) + { + } +} + +namespace { + +struct DomainInfo +{ + DomainInfo( OUString _aRole, OUString _aRange, sal_Int32 nIndex ) + : aRole(std::move(_aRole)), aRange(std::move(_aRange)), nIndexForLocalData(nIndex) + {} + + OUString aRole; + OUString aRange; + sal_Int32 nIndexForLocalData; +}; + +} + +void SchXMLSeries2Context::endFastElement(sal_Int32 ) +{ + // special handling for different chart types. This is necessary as the + // roles are not yet saved in the file format + sal_Int32 nDomainCount = maDomainAddresses.size(); + bool bIsScatterChart = maSeriesChartTypeName == "com.sun.star.chart2.ScatterChartType"; + bool bIsBubbleChart = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType"; + bool bDeleteSeries = false; + std::vector< DomainInfo > aDomainInfos; + + //different handling for different chart types necessary + if( bIsScatterChart || ( nDomainCount==1 && !bIsBubbleChart ) ) + { + DomainInfo aDomainInfo( "values-x", m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; + bool bCreateXValues = true; + if( !maDomainAddresses.empty() ) + { + if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front(); + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfo.aRange = maDomainAddresses.front(); + aDomainInfo.nIndexForLocalData = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() && !m_bHasDomainContext && mnSeriesIndex==0 ) + { + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) //wrong old chart files: + { + //for xy charts the first series needs to have a domain + //if this by error iss not the case the first series is taken s x values + //needed for wrong files created while having an addin (e.g. BoxPlot) + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = m_aSeriesRange; + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + bDeleteSeries = true; + bCreateXValues = false;//they will be created for the next series + } + } + if( bCreateXValues ) + aDomainInfos.push_back( aDomainInfo ); + } + else if( bIsBubbleChart ) + { + if( nDomainCount>1 ) + { + DomainInfo aDomainInfo( "values-x", maDomainAddresses[1], m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; + if( m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() ) + { + //for bubble chart the second domain contains the x values which should become an index smaller than y values for own data table + //->so second first + m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress = maDomainAddresses[1]; + m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfos.push_back( aDomainInfo ); + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( !m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() ) + { + DomainInfo aDomainInfo( "values-x", m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress, m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex ) ; + aDomainInfos.push_back( aDomainInfo ); + } + if( nDomainCount>0) + { + DomainInfo aDomainInfo( "values-y", maDomainAddresses.front(), m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; + if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front(); + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfos.push_back( aDomainInfo ); + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( !m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + DomainInfo aDomainInfo( "values-y", m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; + aDomainInfos.push_back( aDomainInfo ); + } + } + + if( bDeleteSeries ) + { + //delete created series + SchXMLImportHelper::DeleteDataSeries( + m_xSeries, Reference< chart2::XChartDocument >( GetImport().GetModel(), uno::UNO_QUERY ) ); + } + else + { + //add style + if( !msAutoStyleName.isEmpty() || mnAttachedAxis != 1 ) + { + DataRowPointStyle aStyle( + DataRowPointStyle::DATA_SERIES, + m_xSeries, + -1, 1, + msAutoStyleName, mnAttachedAxis ); + aStyle.mbSymbolSizeForSeriesIsMissingInFile=mbSymbolSizeIsMissingInFile; + mrStyleVector.push_back( aStyle ); + } + // And styles for a data-label child element too. In contrast to data-labels as child of data points, + // an information about absolute position is useless here. We need only style information. + if (!mDataLabel.msStyleName.isEmpty()) + { + mDataLabel.msStyleNameOfParent = msAutoStyleName; + mDataLabel.m_xSeries = m_xSeries; + mDataLabel.mnAttachedAxis = mnAttachedAxis; // not needed, but be consistent with its parent + mrStyleVector.push_back(mDataLabel); + } + } + + for( std::vector< DomainInfo >::reverse_iterator aIt( aDomainInfos.rbegin() ); aIt!= aDomainInfos.rend(); ++aIt ) + { + DomainInfo aDomainInfo( *aIt ); + Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq = + lcl_createAndAddSequenceToSeries( aDomainInfo.aRole, aDomainInfo.aRange, mxNewDoc, m_xSeries ); + if( xLabeledSeq.is() ) + { + // register for setting local data if external data provider is not present + mrLSequencesPerIndex.emplace( + tSchXMLIndexWithPart( aDomainInfo.nIndexForLocalData, SCH_XML_PART_VALUES ), + Reference< chart2::data::XLabeledDataSequence >(xLabeledSeq, uno::UNO_QUERY_THROW) ); + } + } + + if( !bDeleteSeries ) + { + for (auto const& postponedSequence : maPostponedSequences) + { + sal_Int32 nNewIndex = postponedSequence.first.first + nDomainCount; + mrLSequencesPerIndex.emplace( tSchXMLIndexWithPart( nNewIndex, postponedSequence.first.second ), postponedSequence.second ); + } + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + maPostponedSequences.clear(); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLSeries2Context::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + switch(nElement) + { + case XML_ELEMENT(CHART, XML_DOMAIN): + if( m_xSeries.is()) + { + m_bHasDomainContext = true; + pContext = new SchXMLDomain2Context( + GetImport(), maDomainAddresses ); + } + break; + + case XML_ELEMENT(CHART, XML_MEAN_VALUE): + pContext = new SchXMLStatisticsObjectContext( + mrImportHelper, GetImport(), + msAutoStyleName, + mrStyleVector, m_xSeries, + SchXMLStatisticsObjectContext::CONTEXT_TYPE_MEAN_VALUE_LINE, + mrLSequencesPerIndex ); + break; + case XML_ELEMENT(CHART, XML_REGRESSION_CURVE): + pContext = new SchXMLRegressionCurveObjectContext( + mrImportHelper, GetImport(), + mrRegressionStyleVector, + m_xSeries, maChartSize ); + break; + case XML_ELEMENT(CHART, XML_ERROR_INDICATOR): + pContext = new SchXMLStatisticsObjectContext( + mrImportHelper, GetImport(), + msAutoStyleName, + mrStyleVector, m_xSeries, + SchXMLStatisticsObjectContext::CONTEXT_TYPE_ERROR_INDICATOR, + mrLSequencesPerIndex ); + break; + + case XML_ELEMENT(CHART, XML_DATA_POINT): + pContext = new SchXMLDataPointContext( GetImport(), + mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile ); + break; + case XML_ELEMENT(CHART, XML_DATA_LABEL): + // CustomLabels are useless for a data label element as child of a series, because it serves as default + // for all data labels. But the ctor expects it, so use that of the mDataLabel struct as ersatz. + pContext = new SchXMLDataLabelContext(GetImport(), mDataLabel.mCustomLabels, + mDataLabel); + break; + + case XML_ELEMENT(LO_EXT, XML_PROPERTY_MAPPING): + pContext = new SchXMLPropertyMappingContext( + GetImport(), + mrLSequencesPerIndex, m_xSeries ); + break; + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + + return pContext; +} + +//static +void SchXMLSeries2Context::initSeriesPropertySets( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const uno::Reference< frame::XModel >& xChartModel ) +{ + // iterate over series first and remind propertysets in map + // new api <-> old api wrapper + ::std::map< Reference< chart2::XDataSeries >, Reference< beans::XPropertySet > > aSeriesMap; + for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + if( !seriesStyle.m_xOldAPISeries.is() ) + seriesStyle.m_xOldAPISeries = SchXMLSeriesHelper::createOldAPISeriesPropertySet( seriesStyle.m_xSeries, xChartModel ); + + aSeriesMap[seriesStyle.m_xSeries] = seriesStyle.m_xOldAPISeries; + + } + + //initialize m_xOldAPISeries for all other styles also + for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType == DataRowPointStyle::DATA_SERIES ) + continue; + seriesStyle.m_xOldAPISeries = aSeriesMap[seriesStyle.m_xSeries]; + } +} + +//static +void SchXMLSeries2Context::setDefaultsToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles ) +{ + // iterate over series + // call initSeriesPropertySets first + + for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + try + { + uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries ); + if( !xSeries.is() ) + continue; + + if( rSeriesDefaultsAndStyles.maSymbolTypeDefault.hasValue() ) + xSeries->setPropertyValue("SymbolType",rSeriesDefaultsAndStyles.maSymbolTypeDefault); + if( rSeriesDefaultsAndStyles.maDataCaptionDefault.hasValue() ) + xSeries->setPropertyValue("DataCaption",rSeriesDefaultsAndStyles.maDataCaptionDefault); + + if( rSeriesDefaultsAndStyles.maErrorIndicatorDefault.hasValue() ) + xSeries->setPropertyValue("ErrorIndicator",rSeriesDefaultsAndStyles.maErrorIndicatorDefault); + if( rSeriesDefaultsAndStyles.maErrorCategoryDefault.hasValue() ) + xSeries->setPropertyValue("ErrorCategory",rSeriesDefaultsAndStyles.maErrorCategoryDefault); + if( rSeriesDefaultsAndStyles.maConstantErrorLowDefault.hasValue() ) + xSeries->setPropertyValue("ConstantErrorLow",rSeriesDefaultsAndStyles.maConstantErrorLowDefault); + if( rSeriesDefaultsAndStyles.maConstantErrorHighDefault.hasValue() ) + xSeries->setPropertyValue("ConstantErrorHigh",rSeriesDefaultsAndStyles.maConstantErrorHighDefault); + if( rSeriesDefaultsAndStyles.maPercentageErrorDefault.hasValue() ) + xSeries->setPropertyValue("PercentageError",rSeriesDefaultsAndStyles.maPercentageErrorDefault); + if( rSeriesDefaultsAndStyles.maErrorMarginDefault.hasValue() ) + xSeries->setPropertyValue("ErrorMargin",rSeriesDefaultsAndStyles.maErrorMarginDefault); + + if( rSeriesDefaultsAndStyles.maMeanValueDefault.hasValue() ) + xSeries->setPropertyValue("MeanValue",rSeriesDefaultsAndStyles.maMeanValueDefault); + if( rSeriesDefaultsAndStyles.maRegressionCurvesDefault.hasValue() ) + xSeries->setPropertyValue("RegressionCurves",rSeriesDefaultsAndStyles.maRegressionCurvesDefault); + } + catch( uno::Exception & ) + { + //end of series reached + } + } +} + +// ODF has the line and fill properties in a <style:style> element, which is referenced by the +// <chart:data-label> element. But LibreOffice has them as special label properties of the series +// or point respectively. The following array maps the API name of the ODF property to the name of +// the internal property. Those are of kind "LabelFoo". +// The array is used in methods setStylesToSeries and setStylesToDataPoints. +const std::pair<OUString, OUString> aApiToLabelFooPairs[] + = { { "LineStyle", "LabelBorderStyle" }, + { "LineWidth", "LabelBorderWidth" }, + { "LineColor", "LabelBorderColor" }, + // The name "LabelBorderDash" is defined, but the associated API name "LineDash" belongs to + // the <draw:stroke-dash> element and is not used directly as line property. + //{"LineDash", "LabelBorderDash"}, + { "LineDashName", "LabelBorderDashName" }, + { "LineTransparence", "LabelBorderTransparency" }, + { "FillStyle", "LabelFillStyle" }, + { "FillBackground", "LabelFillBackground" }, + { "FillHatchName", "LabelFillHatchName" }, + { "FillColor", "LabelFillColor" } }; + + +//static +void SchXMLSeries2Context::setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart + , tSchXMLLSequencesPerIndex & rInOutLSequencesPerIndex ) +{ + // iterate over series + for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if (seriesStyle.meType != DataRowPointStyle::DATA_SERIES) + continue; + try + { + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if( !xSeriesProp.is() ) + continue; + + if( seriesStyle.mnAttachedAxis != 1 ) + { + xSeriesProp->setPropertyValue("Axis" + , uno::Any(chart::ChartAxisAssign::SECONDARY_Y) ); + } + + if( seriesStyle.msStyleName.isEmpty()) + continue; + + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + //set style to series + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + + if (!pPropStyleContext) + continue; + + // error bar style must be set before the other error + // bar properties (which may be alphabetically before + // this property) + bool bHasErrorBarRangesFromData = false; + { + static constexpr OUString aErrorBarStylePropName( u"ErrorBarStyle"_ustr); + uno::Any aErrorBarStyle( + SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt )); + if( aErrorBarStyle.hasValue()) + { + xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle ); + sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE; + bHasErrorBarRangesFromData = + ( ( aErrorBarStyle >>= eEBStyle ) && + eEBStyle == chart::ErrorBarStyle::FROM_DATA ); + } + } + + //don't set the style to the min max line series of a stock chart + //otherwise the min max line properties gets overwritten and the series becomes invisible typically + if (bIsStockChart) + { + if (SchXMLSeriesHelper::isCandleStickSeries( + seriesStyle.m_xSeries, + rImportHelper.GetChartDocument())) + continue; + } + + // Has the series a data-label child element? + auto pItLabel + = std::find_if(rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(), + rSeriesDefaultsAndStyles.maSeriesStyleVector.end(), + [&seriesStyle](const DataRowPointStyle& rStyle) { + return rStyle.meType == DataRowPointStyle::DATA_LABEL_SERIES + && rStyle.msStyleNameOfParent == seriesStyle.msStyleName; + }); + if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end()) + { + // Bring the information from the data-label to the series + const SvXMLStyleContext* pLabelStyleContext(pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext* pLabelPropStyleContext = const_cast<XMLPropStyleContext*>( + dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); + if (pLabelPropStyleContext) + { + // Test each to be mapped property whether the data-label has a value for it. + // If found, set it at series. + uno::Reference<beans::XPropertySetInfo> xSeriesPropInfo( + xSeriesProp->getPropertySetInfo()); + for (const auto& rPropPair : aApiToLabelFooPairs) + { + uno::Any aPropValue(SchXMLTools::getPropertyFromContext( + rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); + if (aPropValue.hasValue() + && xSeriesPropInfo->hasPropertyByName(rPropPair.second)) + xSeriesProp->setPropertyValue(rPropPair.second, aPropValue); + } + } + } + + pPropStyleContext->FillPropertySet( xSeriesProp ); + if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile ) + lcl_setSymbolSizeIfNeeded( xSeriesProp, rImport ); + if( bHasErrorBarRangesFromData ) + lcl_insertErrorBarLSequencesToMap( rInOutLSequencesPerIndex, xSeriesProp ); + + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + } +} + +// static +void SchXMLSeries2Context::setStylesToRegressionCurves( + SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles, + const SvXMLStylesContext* pStylesCtxt, + const SvXMLStyleContext*& rpStyle, + OUString const & rCurrentStyleName ) +{ + // iterate over regression etc + for (auto const& regressionStyle : rSeriesDefaultsAndStyles.maRegressionStyleVector) + { + try + { + OUString aServiceName; + XMLPropStyleContext* pPropStyleContext = nullptr; + + if (!rCurrentStyleName.isEmpty()) + { + XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, rCurrentStyleName); + if( pCurrent ) + { + pPropStyleContext = pCurrent; + uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt); + if ( aAny.hasValue() ) + { + aAny >>= aServiceName; + } + } + } + + if (!regressionStyle.msStyleName.isEmpty()) + { + XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, regressionStyle.msStyleName); + if( pCurrent ) + { + pPropStyleContext = pCurrent; + uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt); + if ( aAny.hasValue() ) + { + aAny >>= aServiceName; + } + } + } + + if( !aServiceName.isEmpty() ) + { + Reference< lang::XMultiServiceFactory > xMSF = comphelper::getProcessServiceFactory(); + Reference< chart2::XRegressionCurve > xRegCurve( xMSF->createInstance( aServiceName ), uno::UNO_QUERY_THROW ); + Reference< chart2::XRegressionCurveContainer > xRegCurveCont( regressionStyle.m_xSeries, uno::UNO_QUERY_THROW ); + + Reference< beans::XPropertySet > xCurveProperties( xRegCurve, uno::UNO_QUERY ); + if( pPropStyleContext != nullptr) + pPropStyleContext->FillPropertySet( xCurveProperties ); + + xRegCurve->setEquationProperties( regressionStyle.m_xEquationProperties ); + + xRegCurveCont->addRegressionCurve( xRegCurve ); + } + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + + } +} + +// static +void SchXMLSeries2Context::setStylesToStatisticsObjects( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName ) +{ + // iterate over regression etc + for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR || + seriesStyle.meType == DataRowPointStyle::MEAN_VALUE ) + { + if ( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR ) + { + uno::Reference< beans::XPropertySet > xNewSeriesProp(seriesStyle.m_xSeries,uno::UNO_QUERY); + + if (seriesStyle.m_xErrorXProperties.is()) + xNewSeriesProp->setPropertyValue("ErrorBarX",uno::Any(seriesStyle.m_xErrorXProperties)); + + if (seriesStyle.m_xErrorYProperties.is()) + xNewSeriesProp->setPropertyValue("ErrorBarY",uno::Any(seriesStyle.m_xErrorYProperties)); + } + + try + { + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if( !xSeriesProp.is() ) + continue; + + if( !seriesStyle.msStyleName.isEmpty()) + { + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if( pPropStyleContext ) + { + Reference< beans::XPropertySet > xStatPropSet; + switch( seriesStyle.meType ) + { + case DataRowPointStyle::MEAN_VALUE: + xSeriesProp->getPropertyValue("DataMeanValueProperties") >>= xStatPropSet; + break; + case DataRowPointStyle::ERROR_INDICATOR: + xSeriesProp->getPropertyValue("DataErrorProperties") >>= xStatPropSet; + break; + default: + break; + } + if( xStatPropSet.is()) + pPropStyleContext->FillPropertySet( xStatPropSet ); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + } + } +} + +//static +void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart, bool bIsDonutChart, bool bSwitchOffLinesForScatter ) +{ + for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_POINT ) + continue; + + if( seriesStyle.m_nPointIndex == -1 ) + continue; + + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if(!xSeriesProp.is()) + continue; + + //ignore datapoint properties for stock charts + //... todo ... + if( bIsStockChart ) + { + if( SchXMLSeriesHelper::isCandleStickSeries( seriesStyle.m_xSeries, rImportHelper.GetChartDocument() ) ) + continue; + } + + // data point style + for( sal_Int32 i = 0; i < seriesStyle.m_nPointRepeat; i++ ) + { + try + { + uno::Reference< beans::XPropertySet > xPointProp( + SchXMLSeriesHelper::createOldAPIDataPointPropertySet( seriesStyle.m_xSeries, seriesStyle.m_nPointIndex + i + , rImportHelper.GetChartDocument() ) ); + + if( !xPointProp.is() ) + continue; + + if( bIsDonutChart ) + { + //set special series styles for donut charts first + if( rCurrStyleName != seriesStyle.msSeriesStyleNameForDonuts ) + { + rCurrStyleName = seriesStyle.msSeriesStyleNameForDonuts; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if( pPropStyleContext ) + pPropStyleContext->FillPropertySet( xPointProp ); + } + + try + { + //need to set this explicitly here for old files as the new api does not support this property fully anymore + if( bSwitchOffLinesForScatter ) + xPointProp->setPropertyValue("Lines",uno::Any(false)); + } + catch( const uno::Exception & ) + { + } + + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if (pPropStyleContext) + { + // Has the point a data-label child element? + auto pItLabel = std::find_if( + rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(), + rSeriesDefaultsAndStyles.maSeriesStyleVector.end(), + [&seriesStyle](const DataRowPointStyle& rStyle) { + return rStyle.meType == DataRowPointStyle::DATA_LABEL_POINT + && rStyle.msStyleNameOfParent == seriesStyle.msStyleName; + }); + if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end()) + { + // Bring the information from the data-label to the point + const SvXMLStyleContext* pLabelStyleContext( + pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext* pLabelPropStyleContext + = const_cast<XMLPropStyleContext*>( + dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); + if (pLabelPropStyleContext) + { + // Test each to be mapped property whether the data-label has a value for it. + // If found, set it at the point. + uno::Reference<beans::XPropertySetInfo> xPointPropInfo( + xPointProp->getPropertySetInfo()); + for (const auto& rPropPair : aApiToLabelFooPairs) + { + uno::Any aPropValue(SchXMLTools::getPropertyFromContext( + rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); + if (aPropValue.hasValue() + && xPointPropInfo->hasPropertyByName(rPropPair.second)) + xPointProp->setPropertyValue(rPropPair.second, aPropValue); + } + } + } + + pPropStyleContext->FillPropertySet( xPointProp ); + if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile ) + lcl_resetSymbolSizeForPointsIfNecessary( xPointProp, rImport, pPropStyleContext, pStylesCtxt ); + if( !pPropStyleContext->isEmptyDataStyleName() ) + lcl_setLinkNumberFormatToSourceIfNeeded( xPointProp, pPropStyleContext, pStylesCtxt ); + } + + // Custom labels might be passed as property + if(const size_t nLabelCount = seriesStyle.mCustomLabels.mLabels.size(); nLabelCount > 0) + { + auto& rCustomLabels = seriesStyle.mCustomLabels; + + Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount); + auto pxLabels = xLabels.getArray(); + Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); + for( size_t j = 0; j < nLabelCount; ++j ) + { + Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext); + pxLabels[j] = xCustomLabel; + xCustomLabel->setString(rCustomLabels.mLabels[j]); + if ( j == 0 && rCustomLabels.mbDataLabelsRange) + { + xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE); + xCustomLabel->setGuid(rCustomLabels.msLabelGuid); + xCustomLabel->setCellRange(rCustomLabels.msLabelsCellRange); + xCustomLabel->setDataLabelsRange(true); + } + else + { + xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT); + } + + // Restore character properties on the text span manually, till + // SchXMLExportHelper_Impl::exportCustomLabel() does not write the style. + uno::Reference<beans::XPropertySetInfo> xPointPropInfo + = xPointProp->getPropertySetInfo(); + if (xPointPropInfo.is()) + { + uno::Sequence<beans::Property> aProperties = xPointPropInfo->getProperties(); + for (const auto& rProperty : std::as_const(aProperties)) + { + if (!rProperty.Name.startsWith("Char") + || rProperty.Name.startsWith("Chart")) + { + continue; + } + + xCustomLabel->setPropertyValue( + rProperty.Name, xPointProp->getPropertyValue(rProperty.Name)); + } + } + } + + xPointProp->setPropertyValue("CustomLabelFields", uno::Any(xLabels)); + xPointProp->setPropertyValue("DataCaption", uno::Any(chart::ChartDataCaption::CUSTOM)); + } + + if( seriesStyle.mCustomLabelPos[0] != 0.0 || seriesStyle.mCustomLabelPos[1] != 0.0 ) + { + chart2::RelativePosition aCustomlabelPosition; + aCustomlabelPosition.Primary = seriesStyle.mCustomLabelPos[0]; + aCustomlabelPosition.Secondary = seriesStyle.mCustomLabelPos[1]; + xPointProp->setPropertyValue("CustomLabelPosition", uno::Any(aCustomlabelPosition)); + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to data points" ); + } + } + } // styles iterator +} + +//static +void SchXMLSeries2Context::switchSeriesLinesOff( ::std::vector< DataRowPointStyle >& rSeriesStyleVector ) +{ + // iterate over series + for (auto const& seriesStyle : rSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + try + { + uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries ); + if( !xSeries.is() ) + continue; + + xSeries->setPropertyValue("Lines",uno::Any(false)); + } + catch( uno::Exception & ) + { + //end of series reached + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |