1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawingml/chart/seriesconverter.hxx>
22 #include <com/sun/star/chart/DataLabelPlacement.hpp>
23 #include <com/sun/star/chart/ErrorBarStyle.hpp>
24 #include <com/sun/star/chart2/DataPointLabel.hpp>
25 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
26 #include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
27 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
28 #include <com/sun/star/chart2/XDataSeries.hpp>
29 #include <com/sun/star/chart2/XRegressionCurve.hpp>
30 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
31 #include <com/sun/star/chart2/data/XDataSink.hpp>
32 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
33 #include <com/sun/star/chart2/XFormattedString2.hpp>
34 #include <com/sun/star/chart2/FormattedString.hpp>
35 #include <osl/diagnose.h>
36 #include <basegfx/numeric/ftools.hxx>
37 #include <drawingml/chart/datasourceconverter.hxx>
38 #include <drawingml/chart/seriesmodel.hxx>
39 #include <drawingml/chart/titleconverter.hxx>
40 #include <drawingml/chart/typegroupconverter.hxx>
41 #include <drawingml/chart/typegroupmodel.hxx>
42 #include <oox/core/xmlfilterbase.hxx>
43 #include <oox/helper/containerhelper.hxx>
44 #include <oox/helper/attributelist.hxx>
45 #include <oox/token/namespaces.hxx>
46 #include <oox/token/properties.hxx>
47 #include <oox/token/tokens.hxx>
48 #include <drawingml/lineproperties.hxx>
49 #include <drawingml/textparagraph.hxx>
50 #include <drawingml/textrun.hxx>
51 #include <drawingml/textfield.hxx>
52 #include <drawingml/textbody.hxx>
58 using namespace com::sun::star
;
59 using namespace ::com::sun::star::beans
;
60 using namespace ::com::sun::star::chart2
;
61 using namespace ::com::sun::star::chart2::data
;
62 using namespace ::com::sun::star::uno
;
66 /** Function to get vertical position of label from chart height factor.
67 Value can be negative, prefer top placement.
69 int lclGetPositionY( double nVal
)
73 else if( nVal
<= 0.6 )
79 /** Function to get horizontal position of label from chart width factor.
80 Value can be negative, prefer center placement.
82 int lclGetPositionX( double nVal
)
86 else if( nVal
<= 0.2 )
92 Reference
< XLabeledDataSequence
> lclCreateLabeledDataSequence(
93 const ConverterRoot
& rParent
,
94 DataSourceModel
* pValues
, const OUString
& rRole
,
95 TextModel
* pTitle
= nullptr )
97 // create data sequence for values
98 Reference
< XDataSequence
> xValueSeq
;
101 DataSourceConverter
aSourceConv( rParent
, *pValues
);
102 xValueSeq
= aSourceConv
.createDataSequence( rRole
);
105 // create data sequence for title
106 Reference
< XDataSequence
> xTitleSeq
;
109 TextConverter
aTextConv( rParent
, *pTitle
);
110 xTitleSeq
= aTextConv
.createDataSequence( "label" );
113 // create the labeled data sequence, if values or title are present
114 Reference
< XLabeledDataSequence
> xLabeledSeq
;
115 if( xValueSeq
.is() || xTitleSeq
.is() )
117 xLabeledSeq
= LabeledDataSequence::create(rParent
.getComponentContext());
118 if( xLabeledSeq
.is() )
120 xLabeledSeq
->setValues( xValueSeq
);
121 xLabeledSeq
->setLabel( xTitleSeq
);
127 void convertTextProperty(PropertySet
& rPropSet
, ObjectFormatter
& rFormatter
,
128 DataLabelModelBase::TextBodyRef xTextProps
)
130 rFormatter
.convertTextFormatting( rPropSet
, xTextProps
, OBJECTTYPE_DATALABEL
);
131 ObjectFormatter::convertTextRotation( rPropSet
, xTextProps
, false );
132 ObjectFormatter::convertTextWrap( rPropSet
, xTextProps
);
135 void lclConvertLabelFormatting( PropertySet
& rPropSet
, ObjectFormatter
& rFormatter
,
136 const DataLabelModelBase
& rDataLabel
, const TypeGroupConverter
& rTypeGroup
,
137 bool bDataSeriesLabel
, bool bMSO2007Doc
, const PropertySet
* pSeriesPropSet
)
139 const TypeGroupInfo
& rTypeInfo
= rTypeGroup
.getTypeInfo();
141 /* Excel 2007 does not change the series setting for a single data point,
142 if none of some specific elements occur. But only one existing element
143 in a data point will reset most other of these elements from the series
144 (e.g.: series has <c:showVal>, data point has <c:showCatName>, this
145 will reset <c:showVal> for this point, unless <c:showVal> is repeated
146 in the data point). The elements <c:layout>, <c:numberFormat>,
147 <c:spPr>, <c:tx>, and <c:txPr> are not affected at all. */
148 bool bHasAnyElement
= true;
151 bHasAnyElement
= rDataLabel
.moaSeparator
.has() || rDataLabel
.monLabelPos
.has() ||
152 rDataLabel
.mobShowCatName
.has() || rDataLabel
.mobShowLegendKey
.has() ||
153 rDataLabel
.mobShowPercent
.has() || rDataLabel
.mobShowSerName
.has() ||
154 rDataLabel
.mobShowVal
.has();
157 bool bShowValue
= !rDataLabel
.mbDeleted
&& rDataLabel
.mobShowVal
.get( !bMSO2007Doc
);
158 bool bShowPercent
= !rDataLabel
.mbDeleted
&& rDataLabel
.mobShowPercent
.get( !bMSO2007Doc
) && (rTypeInfo
.meTypeCategory
== TYPECATEGORY_PIE
);
160 !bShowPercent
&& rTypeInfo
.meTypeCategory
== TYPECATEGORY_PIE
&&
161 rDataLabel
.maNumberFormat
.maFormatCode
.indexOf('%') >= 0 )
166 bool bShowCateg
= !rDataLabel
.mbDeleted
&& rDataLabel
.mobShowCatName
.get( !bMSO2007Doc
);
167 bool bShowSymbol
= !rDataLabel
.mbDeleted
&& rDataLabel
.mobShowLegendKey
.get( !bMSO2007Doc
);
169 // type of attached label
170 if( bHasAnyElement
|| rDataLabel
.mbDeleted
)
172 DataPointLabel
aPointLabel( bShowValue
, bShowPercent
, bShowCateg
, bShowSymbol
);
173 rPropSet
.setProperty( PROP_Label
, aPointLabel
);
176 if( !rDataLabel
.mbDeleted
)
178 // data label number format (percentage format wins over value format)
179 rFormatter
.convertNumberFormat( rPropSet
, rDataLabel
.maNumberFormat
, false, bShowPercent
);
181 // data label text formatting (frame formatting not supported by Chart2)
182 convertTextProperty(rPropSet
, rFormatter
, rDataLabel
.mxTextProp
);
184 // data label separator (do not overwrite series separator, if no explicit point separator is present)
185 // Set the data label separator to "new line" if the value is shown as percentage with a category name,
186 // just like in MS-Office. In any other case the default separator will be a semicolon.
187 if( bShowPercent
&& !bShowValue
&& ( bDataSeriesLabel
|| rDataLabel
.moaSeparator
.has() ) )
188 rPropSet
.setProperty( PROP_LabelSeparator
, rDataLabel
.moaSeparator
.get( "\n" ) );
189 else if( bDataSeriesLabel
|| rDataLabel
.moaSeparator
.has() )
190 rPropSet
.setProperty( PROP_LabelSeparator
, rDataLabel
.moaSeparator
.get( "; " ) );
192 // data label placement (do not overwrite series placement, if no explicit point placement is present)
193 if( bDataSeriesLabel
|| rDataLabel
.monLabelPos
.has() )
195 namespace csscd
= ::com::sun::star::chart::DataLabelPlacement
;
196 sal_Int32 nPlacement
= rTypeInfo
.mnDefLabelPos
;
197 switch( rDataLabel
.monLabelPos
.get( XML_TOKEN_INVALID
) )
199 case XML_outEnd
: nPlacement
= csscd::OUTSIDE
; break;
200 case XML_inEnd
: nPlacement
= csscd::INSIDE
; break;
201 case XML_ctr
: nPlacement
= csscd::CENTER
; break;
202 case XML_inBase
: nPlacement
= csscd::NEAR_ORIGIN
; break;
203 case XML_t
: nPlacement
= csscd::TOP
; break;
204 case XML_b
: nPlacement
= csscd::BOTTOM
; break;
205 case XML_l
: nPlacement
= csscd::LEFT
; break;
206 case XML_r
: nPlacement
= csscd::RIGHT
; break;
207 case XML_bestFit
: nPlacement
= csscd::AVOID_OVERLAP
; break;
210 sal_Int32 nGlobalPlacement
= 0;
211 if ( !bDataSeriesLabel
&& nPlacement
== rTypeInfo
.mnDefLabelPos
&& pSeriesPropSet
&&
212 pSeriesPropSet
->getProperty( nGlobalPlacement
, PROP_LabelPlacement
) )
213 nPlacement
= nGlobalPlacement
;
215 rPropSet
.setProperty( PROP_LabelPlacement
, nPlacement
);
220 void importBorderProperties( PropertySet
& rPropSet
, Shape
& rShape
, const GraphicHelper
& rGraphicHelper
)
222 LineProperties
& rLP
= rShape
.getLineProperties();
223 // no fill has the same effect as no border so skip it
224 if (rLP
.maLineFill
.moFillType
.get() == XML_noFill
)
227 if (rLP
.moLineWidth
.has())
229 sal_Int32 nWidth
= convertEmuToHmm(rLP
.moLineWidth
.get());
230 rPropSet
.setProperty(PROP_LabelBorderWidth
, uno::makeAny(nWidth
));
231 rPropSet
.setProperty(PROP_LabelBorderStyle
, uno::makeAny(drawing::LineStyle_SOLID
));
233 const Color
& aColor
= rLP
.maLineFill
.maFillColor
;
234 ::Color nColor
= aColor
.getColor(rGraphicHelper
);
235 rPropSet
.setProperty(PROP_LabelBorderColor
, uno::makeAny(nColor
));
238 DataPointCustomLabelFieldType
lcl_ConvertFieldNameToFieldEnum( const OUString
& rField
)
240 if (rField
== "VALUE")
241 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_VALUE
;
242 else if (rField
== "SERIESNAME")
243 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_SERIESNAME
;
244 else if (rField
== "CATEGORYNAME")
245 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CATEGORYNAME
;
246 else if (rField
== "CELLREF")
247 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLREF
;
249 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT
;
254 DataLabelConverter::DataLabelConverter( const ConverterRoot
& rParent
, DataLabelModel
& rModel
) :
255 ConverterBase
< DataLabelModel
>( rParent
, rModel
)
259 DataLabelConverter::~DataLabelConverter()
263 void DataLabelConverter::convertFromModel( const Reference
< XDataSeries
>& rxDataSeries
, const TypeGroupConverter
& rTypeGroup
,
264 const PropertySet
& rSeriesPropSet
)
266 if (!rxDataSeries
.is())
271 bool bMSO2007Doc
= getFilter().isMSO2007Document();
272 PropertySet
aPropSet( rxDataSeries
->getDataPointByIndex( mrModel
.mnIndex
) );
273 lclConvertLabelFormatting( aPropSet
, getFormatter(), mrModel
, rTypeGroup
, false, bMSO2007Doc
, &rSeriesPropSet
);
274 const TypeGroupInfo
& rTypeInfo
= rTypeGroup
.getTypeInfo();
275 bool bIsPie
= rTypeInfo
.meTypeCategory
== TYPECATEGORY_PIE
;
276 if( mrModel
.mxLayout
&& !mrModel
.mxLayout
->mbAutoLayout
&& !bIsPie
)
278 // bnc#694340 - nasty hack - chart2 cannot individually
279 // place data labels, let's try to find a useful
280 // compromise instead
281 namespace csscd
= ::com::sun::star::chart::DataLabelPlacement
;
282 const sal_Int32 aPositionsLookupTable
[] =
284 csscd::TOP_LEFT
, csscd::TOP
, csscd::TOP_RIGHT
,
285 csscd::LEFT
, csscd::CENTER
, csscd::RIGHT
,
286 csscd::BOTTOM_LEFT
, csscd::BOTTOM
, csscd::BOTTOM_RIGHT
288 const int simplifiedX
= lclGetPositionX(mrModel
.mxLayout
->mfX
);
289 const int simplifiedY
= lclGetPositionY(mrModel
.mxLayout
->mfY
);
290 aPropSet
.setProperty( PROP_LabelPlacement
,
291 aPositionsLookupTable
[ simplifiedX
+1 + 3*(simplifiedY
+1) ] );
294 if (mrModel
.mxShapeProp
)
295 importBorderProperties(aPropSet
, *mrModel
.mxShapeProp
, getFilter().getGraphicHelper());
297 if( mrModel
.mxText
&& mrModel
.mxText
->mxTextBody
&& !mrModel
.mxText
->mxTextBody
->getParagraphs().empty() )
299 css::uno::Reference
< XComponentContext
> xContext
= getComponentContext();
300 uno::Sequence
< css::uno::Reference
< XDataPointCustomLabelField
> > aSequence
;
302 auto& rParagraphs
= mrModel
.mxText
->mxTextBody
->getParagraphs();
304 int nSequenceSize
= 0;
305 for( auto& pParagraph
: rParagraphs
)
306 nSequenceSize
+= pParagraph
->getRuns().size();
308 int nParagraphs
= rParagraphs
.size();
309 if( nParagraphs
> 1 )
310 nSequenceSize
+= nParagraphs
- 1;
312 aSequence
.realloc( nSequenceSize
);
315 for( auto& pParagraph
: rParagraphs
)
317 for( auto& pRun
: pParagraph
->getRuns() )
319 css::uno::Reference
< XDataPointCustomLabelField
> xCustomLabel
= DataPointCustomLabelField::create( xContext
);
322 oox::PropertySet
aPropertySet( xCustomLabel
);
323 pRun
->getTextCharacterProperties().pushToPropSet( aPropertySet
, getFilter() );
325 TextField
* pField
= nullptr;
326 if( ( pField
= dynamic_cast< TextField
* >( pRun
.get() ) ) )
328 xCustomLabel
->setString( pField
->getText() );
329 xCustomLabel
->setFieldType( lcl_ConvertFieldNameToFieldEnum( pField
->getType() ) );
330 xCustomLabel
->setGuid( pField
->getUuid() );
332 else if( pRun
.get() )
334 xCustomLabel
->setString( pRun
->getText() );
335 xCustomLabel
->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT
);
337 aSequence
[ nPos
++ ] = xCustomLabel
;
340 if( nParagraphs
> 1 && nPos
< nSequenceSize
)
342 css::uno::Reference
< XDataPointCustomLabelField
> xCustomLabel
= DataPointCustomLabelField::create( xContext
);
343 xCustomLabel
->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_NEWLINE
);
344 xCustomLabel
->setString("\n");
345 aSequence
[ nPos
++ ] = xCustomLabel
;
349 aPropSet
.setProperty( PROP_CustomLabelFields
, makeAny( aSequence
) );
350 convertTextProperty(aPropSet
, getFormatter(), mrModel
.mxText
->mxTextBody
);
358 DataLabelsConverter::DataLabelsConverter( const ConverterRoot
& rParent
, DataLabelsModel
& rModel
) :
359 ConverterBase
< DataLabelsModel
>( rParent
, rModel
)
363 DataLabelsConverter::~DataLabelsConverter()
367 void DataLabelsConverter::convertFromModel( const Reference
< XDataSeries
>& rxDataSeries
, const TypeGroupConverter
& rTypeGroup
)
369 PropertySet
aPropSet( rxDataSeries
);
370 if( !mrModel
.mbDeleted
)
372 bool bMSO2007Doc
= getFilter().isMSO2007Document();
373 lclConvertLabelFormatting( aPropSet
, getFormatter(), mrModel
, rTypeGroup
, true, bMSO2007Doc
, nullptr );
375 if (mrModel
.mxShapeProp
)
376 // Import baseline border properties for these data labels.
377 importBorderProperties(aPropSet
, *mrModel
.mxShapeProp
, getFilter().getGraphicHelper());
380 // data point label settings
381 for (auto const& pointLabel
: mrModel
.maPointLabels
)
383 if (pointLabel
->maNumberFormat
.maFormatCode
.isEmpty())
384 pointLabel
->maNumberFormat
= mrModel
.maNumberFormat
;
386 DataLabelConverter
aLabelConv(*this, *pointLabel
);
387 aLabelConv
.convertFromModel( rxDataSeries
, rTypeGroup
, aPropSet
);
391 ErrorBarConverter::ErrorBarConverter( const ConverterRoot
& rParent
, ErrorBarModel
& rModel
) :
392 ConverterBase
< ErrorBarModel
>( rParent
, rModel
)
396 ErrorBarConverter::~ErrorBarConverter()
400 void ErrorBarConverter::convertFromModel( const Reference
< XDataSeries
>& rxDataSeries
)
402 bool bShowPos
= (mrModel
.mnTypeId
== XML_plus
) || (mrModel
.mnTypeId
== XML_both
);
403 bool bShowNeg
= (mrModel
.mnTypeId
== XML_minus
) || (mrModel
.mnTypeId
== XML_both
);
404 if( bShowPos
|| bShowNeg
) try
406 Reference
< XPropertySet
> xErrorBar( createInstance( "com.sun.star.chart2.ErrorBar" ), UNO_QUERY_THROW
);
407 PropertySet
aBarProp( xErrorBar
);
410 aBarProp
.setProperty( PROP_ShowPositiveError
, bShowPos
);
411 aBarProp
.setProperty( PROP_ShowNegativeError
, bShowNeg
);
413 // type of displayed error
414 namespace cssc
= ::com::sun::star::chart
;
415 switch( mrModel
.mnValueType
)
419 // #i87806# manual error bars
420 aBarProp
.setProperty( PROP_ErrorBarStyle
, cssc::ErrorBarStyle::FROM_DATA
);
421 // attach data sequences to error bar
422 Reference
< XDataSink
> xDataSink( xErrorBar
, UNO_QUERY
);
425 // create vector of all value sequences
426 ::std::vector
< Reference
< XLabeledDataSequence
> > aLabeledSeqVec
;
427 // add positive values
430 Reference
< XLabeledDataSequence
> xValueSeq
= createLabeledDataSequence( ErrorBarModel::PLUS
);
432 aLabeledSeqVec
.push_back( xValueSeq
);
434 // add negative values
437 Reference
< XLabeledDataSequence
> xValueSeq
= createLabeledDataSequence( ErrorBarModel::MINUS
);
439 aLabeledSeqVec
.push_back( xValueSeq
);
441 // attach labeled data sequences to series
442 if( aLabeledSeqVec
.empty() )
445 xDataSink
->setData( ContainerHelper::vectorToSequence( aLabeledSeqVec
) );
450 aBarProp
.setProperty( PROP_ErrorBarStyle
, cssc::ErrorBarStyle::ABSOLUTE
);
451 aBarProp
.setProperty( PROP_PositiveError
, mrModel
.mfValue
);
452 aBarProp
.setProperty( PROP_NegativeError
, mrModel
.mfValue
);
455 aBarProp
.setProperty( PROP_ErrorBarStyle
, cssc::ErrorBarStyle::RELATIVE
);
456 aBarProp
.setProperty( PROP_PositiveError
, mrModel
.mfValue
);
457 aBarProp
.setProperty( PROP_NegativeError
, mrModel
.mfValue
);
460 aBarProp
.setProperty( PROP_ErrorBarStyle
, cssc::ErrorBarStyle::STANDARD_DEVIATION
);
461 aBarProp
.setProperty( PROP_Weight
, mrModel
.mfValue
);
464 aBarProp
.setProperty( PROP_ErrorBarStyle
, cssc::ErrorBarStyle::STANDARD_ERROR
);
467 OSL_FAIL( "ErrorBarConverter::convertFromModel - unknown error bar type" );
471 // error bar formatting
472 getFormatter().convertFrameFormatting( aBarProp
, mrModel
.mxShapeProp
, OBJECTTYPE_ERRORBAR
);
476 PropertySet
aSeriesProp( rxDataSeries
);
477 switch( mrModel
.mnDirection
)
479 case XML_x
: aSeriesProp
.setProperty( PROP_ErrorBarX
, xErrorBar
); break;
480 case XML_y
: aSeriesProp
.setProperty( PROP_ErrorBarY
, xErrorBar
); break;
481 default: OSL_FAIL( "ErrorBarConverter::convertFromModel - invalid error bar direction" );
487 OSL_FAIL( "ErrorBarConverter::convertFromModel - error while creating error bars" );
491 Reference
< XLabeledDataSequence
> ErrorBarConverter::createLabeledDataSequence( ErrorBarModel::SourceType eSourceType
)
494 switch( eSourceType
)
496 case ErrorBarModel::PLUS
:
497 switch( mrModel
.mnDirection
)
499 case XML_x
: aRole
= "error-bars-x-positive"; break;
500 case XML_y
: aRole
= "error-bars-y-positive"; break;
503 case ErrorBarModel::MINUS
:
504 switch( mrModel
.mnDirection
)
506 case XML_x
: aRole
= "error-bars-x-negative"; break;
507 case XML_y
: aRole
= "error-bars-y-negative"; break;
511 OSL_ENSURE( !aRole
.isEmpty(), "ErrorBarConverter::createLabeledDataSequence - invalid error bar direction" );
512 return lclCreateLabeledDataSequence( *this, mrModel
.maSources
.get( eSourceType
).get(), aRole
);
515 TrendlineLabelConverter::TrendlineLabelConverter( const ConverterRoot
& rParent
, TrendlineLabelModel
& rModel
) :
516 ConverterBase
< TrendlineLabelModel
>( rParent
, rModel
)
520 TrendlineLabelConverter::~TrendlineLabelConverter()
524 void TrendlineLabelConverter::convertFromModel( PropertySet
& rPropSet
)
527 getFormatter().convertFormatting( rPropSet
, mrModel
.mxShapeProp
, mrModel
.mxTextProp
, OBJECTTYPE_TRENDLINELABEL
);
530 TrendlineConverter::TrendlineConverter( const ConverterRoot
& rParent
, TrendlineModel
& rModel
) :
531 ConverterBase
< TrendlineModel
>( rParent
, rModel
)
535 TrendlineConverter::~TrendlineConverter()
539 void TrendlineConverter::convertFromModel( const Reference
< XDataSeries
>& rxDataSeries
)
544 OUString aServiceName
;
545 switch( mrModel
.mnTypeId
)
548 aServiceName
= "com.sun.star.chart2.ExponentialRegressionCurve";
551 aServiceName
= "com.sun.star.chart2.LinearRegressionCurve";
554 aServiceName
= "com.sun.star.chart2.LogarithmicRegressionCurve";
557 aServiceName
= "com.sun.star.chart2.MovingAverageRegressionCurve";
560 aServiceName
= "com.sun.star.chart2.PolynomialRegressionCurve";
563 aServiceName
= "com.sun.star.chart2.PotentialRegressionCurve";
566 OSL_FAIL( "TrendlineConverter::convertFromModel - unknown trendline type" );
568 if( !aServiceName
.isEmpty() )
570 Reference
< XRegressionCurve
> xRegCurve( createInstance( aServiceName
), UNO_QUERY_THROW
);
571 PropertySet
aPropSet( xRegCurve
);
574 aPropSet
.setProperty( PROP_CurveName
, mrModel
.maName
);
575 aPropSet
.setProperty( PROP_PolynomialDegree
, mrModel
.mnOrder
);
576 aPropSet
.setProperty( PROP_MovingAveragePeriod
, mrModel
.mnPeriod
);
579 bool hasIntercept
= mrModel
.mfIntercept
.has();
580 aPropSet
.setProperty( PROP_ForceIntercept
, hasIntercept
);
582 aPropSet
.setProperty( PROP_InterceptValue
, mrModel
.mfIntercept
.get());
585 if (mrModel
.mfForward
.has())
586 aPropSet
.setProperty( PROP_ExtrapolateForward
, mrModel
.mfForward
.get() );
587 if (mrModel
.mfBackward
.has())
588 aPropSet
.setProperty( PROP_ExtrapolateBackward
, mrModel
.mfBackward
.get() );
590 // trendline formatting
591 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, OBJECTTYPE_TRENDLINE
);
593 // #i83100# show equation and correlation coefficient
594 PropertySet
aLabelProp( xRegCurve
->getEquationProperties() );
595 aLabelProp
.setProperty( PROP_ShowEquation
, mrModel
.mbDispEquation
);
596 aLabelProp
.setProperty( PROP_ShowCorrelationCoefficient
, mrModel
.mbDispRSquared
);
598 // #i83100# formatting of the equation text box
599 if( mrModel
.mbDispEquation
|| mrModel
.mbDispRSquared
)
601 TrendlineLabelConverter
aLabelConv( *this, mrModel
.mxLabel
.getOrCreate() );
602 aLabelConv
.convertFromModel( aLabelProp
);
605 // unsupported: #i5085# manual trendline size
606 // unsupported: #i34093# manual crossing point
608 Reference
< XRegressionCurveContainer
> xRegCurveCont( rxDataSeries
, UNO_QUERY_THROW
);
609 xRegCurveCont
->addRegressionCurve( xRegCurve
);
614 OSL_FAIL( "TrendlineConverter::convertFromModel - error while creating trendline" );
618 DataPointConverter::DataPointConverter( const ConverterRoot
& rParent
, DataPointModel
& rModel
) :
619 ConverterBase
< DataPointModel
>( rParent
, rModel
)
623 DataPointConverter::~DataPointConverter()
627 void DataPointConverter::convertFromModel( const Reference
< XDataSeries
>& rxDataSeries
,
628 const TypeGroupConverter
& rTypeGroup
, const SeriesModel
& rSeries
)
630 bool bMSO2007Doc
= getFilter().isMSO2007Document();
633 PropertySet
aPropSet( rxDataSeries
->getDataPointByIndex( mrModel
.mnIndex
) );
636 if( mrModel
.monMarkerSymbol
.differsFrom( rSeries
.mnMarkerSymbol
) || mrModel
.monMarkerSize
.differsFrom( rSeries
.mnMarkerSize
) )
637 rTypeGroup
.convertMarker( aPropSet
, mrModel
.monMarkerSymbol
.get( rSeries
.mnMarkerSymbol
),
638 mrModel
.monMarkerSize
.get( rSeries
.mnMarkerSize
), mrModel
.mxMarkerProp
);
640 // data point pie explosion
641 if( mrModel
.monExplosion
.differsFrom( rSeries
.mnExplosion
) )
642 rTypeGroup
.convertPieExplosion( aPropSet
, mrModel
.monExplosion
.get() );
645 if( mrModel
.mxShapeProp
.is() )
647 if( rTypeGroup
.getTypeInfo().mbPictureOptions
)
648 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, mrModel
.mxPicOptions
.getOrCreate(bMSO2007Doc
), rTypeGroup
.getSeriesObjectType(), rSeries
.mnIndex
);
650 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, rTypeGroup
.getSeriesObjectType(), rSeries
.mnIndex
);
652 else if (rSeries
.mxShapeProp
.is())
654 getFormatter().convertFrameFormatting( aPropSet
, rSeries
.mxShapeProp
, rTypeGroup
.getSeriesObjectType(), rSeries
.mnIndex
);
662 SeriesConverter::SeriesConverter( const ConverterRoot
& rParent
, SeriesModel
& rModel
) :
663 ConverterBase
< SeriesModel
>( rParent
, rModel
)
667 SeriesConverter::~SeriesConverter()
671 Reference
< XLabeledDataSequence
> SeriesConverter::createCategorySequence( const OUString
& rRole
)
673 return createLabeledDataSequence(SeriesModel::CATEGORIES
, rRole
, false);
676 Reference
< XLabeledDataSequence
> SeriesConverter::createValueSequence( const OUString
& rRole
)
678 return createLabeledDataSequence( SeriesModel::VALUES
, rRole
, true );
681 Reference
< XDataSeries
> SeriesConverter::createDataSeries( const TypeGroupConverter
& rTypeGroup
, bool bVaryColorsByPoint
)
683 const TypeGroupInfo
& rTypeInfo
= rTypeGroup
.getTypeInfo();
685 // create the data series object
686 Reference
< XDataSeries
> xDataSeries( createInstance( "com.sun.star.chart2.DataSeries" ), UNO_QUERY
);
687 PropertySet
aSeriesProp( xDataSeries
);
689 // attach data and title sequences to series
690 sal_Int32 nDataPointCount
= 0;
691 Reference
< XDataSink
> xDataSink( xDataSeries
, UNO_QUERY
);
694 // create vector of all value sequences
695 ::std::vector
< Reference
< XLabeledDataSequence
> > aLabeledSeqVec
;
697 Reference
< XLabeledDataSequence
> xYValueSeq
= createValueSequence( "values-y" );
698 if( xYValueSeq
.is() )
700 aLabeledSeqVec
.push_back( xYValueSeq
);
701 Reference
< XDataSequence
> xValues
= xYValueSeq
->getValues();
703 nDataPointCount
= xValues
->getData().getLength();
705 if (!nDataPointCount
)
706 // No values present. Don't create a data series.
707 return Reference
<XDataSeries
>();
709 // add X values of scatter and bubble charts
710 if( !rTypeInfo
.mbCategoryAxis
)
712 Reference
< XLabeledDataSequence
> xXValueSeq
= createCategorySequence( "values-x" );
713 if( xXValueSeq
.is() )
714 aLabeledSeqVec
.push_back( xXValueSeq
);
715 // add size values of bubble charts
716 if( rTypeInfo
.meTypeId
== TYPEID_BUBBLE
)
718 Reference
< XLabeledDataSequence
> xSizeValueSeq
= createLabeledDataSequence( SeriesModel::POINTS
, "values-size", true );
719 if( xSizeValueSeq
.is() )
720 aLabeledSeqVec
.push_back( xSizeValueSeq
);
723 // attach labeled data sequences to series
724 if( !aLabeledSeqVec
.empty() )
725 xDataSink
->setData( ContainerHelper::vectorToSequence( aLabeledSeqVec
) );
729 for (auto const& errorBar
: mrModel
.maErrorBars
)
731 ErrorBarConverter
aErrorBarConv(*this, *errorBar
);
732 aErrorBarConv
.convertFromModel( xDataSeries
);
736 for (auto const& trendLine
: mrModel
.maTrendlines
)
738 TrendlineConverter
aTrendlineConv(*this, *trendLine
);
739 aTrendlineConv
.convertFromModel( xDataSeries
);
742 // data point markers
743 rTypeGroup
.convertMarker( aSeriesProp
, mrModel
.mnMarkerSymbol
, mrModel
.mnMarkerSize
, mrModel
.mxMarkerProp
);
744 #if OOX_CHART_SMOOTHED_PER_SERIES
745 // #i66858# smoothed series lines
746 rTypeGroup
.convertLineSmooth( aSeriesProp
, mrModel
.mbSmooth
);
748 // 3D bar style (not possible to set at chart type -> set at all series)
749 rTypeGroup
.convertBarGeometry( aSeriesProp
, mrModel
.monShape
.get( rTypeGroup
.getModel().mnShape
) );
750 // pie explosion (restricted to [0%,100%] in Chart2)
751 rTypeGroup
.convertPieExplosion( aSeriesProp
, mrModel
.mnExplosion
);
754 ObjectFormatter
& rFormatter
= getFormatter();
755 ObjectType eObjType
= rTypeGroup
.getSeriesObjectType();
756 bool bMSO2007Doc
= getFilter().isMSO2007Document();
757 if( rTypeInfo
.mbPictureOptions
)
758 rFormatter
.convertFrameFormatting( aSeriesProp
, mrModel
.mxShapeProp
, mrModel
.mxPicOptions
.getOrCreate(bMSO2007Doc
), eObjType
, mrModel
.mnIndex
);
760 rFormatter
.convertFrameFormatting( aSeriesProp
, mrModel
.mxShapeProp
, eObjType
, mrModel
.mnIndex
);
762 // set the (unused) property default value used by the Chart2 templates (true for pie/doughnut charts)
763 bool bIsPie
= rTypeInfo
.meTypeCategory
== TYPECATEGORY_PIE
;
764 aSeriesProp
.setProperty( PROP_VaryColorsByPoint
, bVaryColorsByPoint
);
766 // own area formatting for every data point (TODO: varying line color not supported)
767 // #i91271# always set area formatting for every point in pie/doughnut charts to override their automatic point formatting
768 if( bIsPie
|| (bVaryColorsByPoint
&& rTypeGroup
.isSeriesFrameFormat() && ObjectFormatter::isAutomaticFill( mrModel
.mxShapeProp
)) )
770 /* Set the series point number as color cycle size at the object
771 formatter to get correct start-shade/end-tint. TODO: in doughnut
772 charts, the sizes of the series may vary, need to use the maximum
773 point count of all series. */
774 sal_Int32 nOldMax
= rFormatter
.getMaxSeriesIndex();
775 if( bVaryColorsByPoint
)
776 rFormatter
.setMaxSeriesIndex( nDataPointCount
- 1 );
777 for( sal_Int32 nIndex
= 0; nIndex
< nDataPointCount
; ++nIndex
)
781 PropertySet
aPointProp( xDataSeries
->getDataPointByIndex( nIndex
) );
782 rFormatter
.convertAutomaticFill( aPointProp
, eObjType
, bVaryColorsByPoint
? nIndex
: mrModel
.mnIndex
);
788 rFormatter
.setMaxSeriesIndex( nOldMax
);
791 // data point settings
792 for (auto const& point
: mrModel
.maPoints
)
794 DataPointConverter
aPointConv(*this, *point
);
795 aPointConv
.convertFromModel( xDataSeries
, rTypeGroup
, mrModel
);
798 /* Series data label settings. If and only if the series does not contain
799 a c:dLbls element, then the c:dLbls element of the parent chart type is
800 used (data label settings of the parent chart type are *not* merged
801 into own existing data label settings). */
802 ModelRef
< DataLabelsModel
> xLabels
= mrModel
.mxLabels
.is() ? mrModel
.mxLabels
: rTypeGroup
.getModel().mxLabels
;
805 if( xLabels
->maNumberFormat
.maFormatCode
.isEmpty() )
807 // Use number format code from Value series
808 DataSourceModel
* pValues
= mrModel
.maSources
.get( SeriesModel::VALUES
).get();
810 xLabels
->maNumberFormat
.maFormatCode
= pValues
->mxDataSeq
->maFormatCode
;
812 DataLabelsConverter
aLabelsConv( *this, *xLabels
);
813 aLabelsConv
.convertFromModel( xDataSeries
, rTypeGroup
);
819 // private --------------------------------------------------------------------
821 Reference
< XLabeledDataSequence
> SeriesConverter::createLabeledDataSequence(
822 SeriesModel::SourceType eSourceType
, const OUString
& rRole
, bool bUseTextLabel
)
824 DataSourceModel
* pValues
= mrModel
.maSources
.get( eSourceType
).get();
825 TextModel
* pTitle
= bUseTextLabel
? mrModel
.mxText
.get() : nullptr;
826 return lclCreateLabeledDataSequence( *this, pValues
, rRole
, pTitle
);
830 } // namespace drawingml
833 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */