Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / drawingml / chart / seriesconverter.cxx
bloba310549e0535665fb757638e78dca24f16fbf7e5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
54 namespace oox {
55 namespace drawingml {
56 namespace chart {
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;
64 namespace {
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 )
71 if( nVal <= 0.1 )
72 return -1;
73 else if( nVal <= 0.6 )
74 return 0;
75 else
76 return 1;
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 )
84 if( nVal <= -0.2 )
85 return -1;
86 else if( nVal <= 0.2 )
87 return 0;
88 else
89 return 1;
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;
99 if( pValues )
101 DataSourceConverter aSourceConv( rParent, *pValues );
102 xValueSeq = aSourceConv.createDataSequence( rRole );
105 // create data sequence for title
106 Reference< XDataSequence > xTitleSeq;
107 if( pTitle )
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 );
124 return xLabeledSeq;
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;
149 if (bMSO2007Doc)
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);
159 if( bShowValue &&
160 !bShowPercent && rTypeInfo.meTypeCategory == TYPECATEGORY_PIE &&
161 rDataLabel.maNumberFormat.maFormatCode.indexOf('%') >= 0 )
163 bShowValue = false;
164 bShowPercent = true;
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)
225 return;
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;
248 else
249 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT;
252 } // namespace
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())
267 return;
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 );
314 int nPos = 0;
315 for( auto& pParagraph : rParagraphs )
317 for( auto& pRun : pParagraph->getRuns() )
319 css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
321 // Store properties
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);
353 catch( Exception& )
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 );
409 // plus/minus bars
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 )
417 case XML_cust:
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 );
423 if( xDataSink.is() )
425 // create vector of all value sequences
426 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
427 // add positive values
428 if( bShowPos )
430 Reference< XLabeledDataSequence > xValueSeq = createLabeledDataSequence( ErrorBarModel::PLUS );
431 if( xValueSeq.is() )
432 aLabeledSeqVec.push_back( xValueSeq );
434 // add negative values
435 if( bShowNeg )
437 Reference< XLabeledDataSequence > xValueSeq = createLabeledDataSequence( ErrorBarModel::MINUS );
438 if( xValueSeq.is() )
439 aLabeledSeqVec.push_back( xValueSeq );
441 // attach labeled data sequences to series
442 if( aLabeledSeqVec.empty() )
443 xErrorBar.clear();
444 else
445 xDataSink->setData( ContainerHelper::vectorToSequence( aLabeledSeqVec ) );
448 break;
449 case XML_fixedVal:
450 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::ABSOLUTE );
451 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
452 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
453 break;
454 case XML_percentage:
455 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::RELATIVE );
456 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
457 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
458 break;
459 case XML_stdDev:
460 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_DEVIATION );
461 aBarProp.setProperty( PROP_Weight, mrModel.mfValue );
462 break;
463 case XML_stdErr:
464 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_ERROR );
465 break;
466 default:
467 OSL_FAIL( "ErrorBarConverter::convertFromModel - unknown error bar type" );
468 xErrorBar.clear();
471 // error bar formatting
472 getFormatter().convertFrameFormatting( aBarProp, mrModel.mxShapeProp, OBJECTTYPE_ERRORBAR );
474 if( xErrorBar.is() )
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" );
485 catch( Exception& )
487 OSL_FAIL( "ErrorBarConverter::convertFromModel - error while creating error bars" );
491 Reference< XLabeledDataSequence > ErrorBarConverter::createLabeledDataSequence( ErrorBarModel::SourceType eSourceType )
493 OUString aRole;
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;
502 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;
509 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 )
526 // formatting
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 )
543 // trend line type
544 OUString aServiceName;
545 switch( mrModel.mnTypeId )
547 case XML_exp:
548 aServiceName = "com.sun.star.chart2.ExponentialRegressionCurve";
549 break;
550 case XML_linear:
551 aServiceName = "com.sun.star.chart2.LinearRegressionCurve";
552 break;
553 case XML_log:
554 aServiceName = "com.sun.star.chart2.LogarithmicRegressionCurve";
555 break;
556 case XML_movingAvg:
557 aServiceName = "com.sun.star.chart2.MovingAverageRegressionCurve";
558 break;
559 case XML_poly:
560 aServiceName = "com.sun.star.chart2.PolynomialRegressionCurve";
561 break;
562 case XML_power:
563 aServiceName = "com.sun.star.chart2.PotentialRegressionCurve";
564 break;
565 default:
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 );
573 // Name
574 aPropSet.setProperty( PROP_CurveName, mrModel.maName );
575 aPropSet.setProperty( PROP_PolynomialDegree, mrModel.mnOrder );
576 aPropSet.setProperty( PROP_MovingAveragePeriod, mrModel.mnPeriod );
578 // Intercept
579 bool hasIntercept = mrModel.mfIntercept.has();
580 aPropSet.setProperty( PROP_ForceIntercept, hasIntercept);
581 if (hasIntercept)
582 aPropSet.setProperty( PROP_InterceptValue, mrModel.mfIntercept.get());
584 // Extrapolation
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 );
612 catch( Exception& )
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 ) );
635 // data point marker
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() );
644 // point formatting
645 if( mrModel.mxShapeProp.is() )
647 if( rTypeGroup.getTypeInfo().mbPictureOptions )
648 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), rTypeGroup.getSeriesObjectType(), rSeries.mnIndex );
649 else
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 );
657 catch( Exception& )
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 );
692 if( xDataSink.is() )
694 // create vector of all value sequences
695 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
696 // add Y values
697 Reference< XLabeledDataSequence > xYValueSeq = createValueSequence( "values-y" );
698 if( xYValueSeq.is() )
700 aLabeledSeqVec.push_back( xYValueSeq );
701 Reference< XDataSequence > xValues = xYValueSeq->getValues();
702 if( xValues.is() )
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 ) );
728 // error bars
729 for (auto const& errorBar : mrModel.maErrorBars)
731 ErrorBarConverter aErrorBarConv(*this, *errorBar);
732 aErrorBarConv.convertFromModel( xDataSeries );
735 // trendlines
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 );
747 #endif
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 );
753 // series formatting
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 );
759 else
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 );
784 catch( Exception& )
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;
803 if( xLabels.is() )
805 if( xLabels->maNumberFormat.maFormatCode.isEmpty() )
807 // Use number format code from Value series
808 DataSourceModel* pValues = mrModel.maSources.get( SeriesModel::VALUES ).get();
809 if( pValues )
810 xLabels->maNumberFormat.maFormatCode = pValues->mxDataSeq->maFormatCode;
812 DataLabelsConverter aLabelsConv( *this, *xLabels );
813 aLabelsConv.convertFromModel( xDataSeries, rTypeGroup );
816 return xDataSeries;
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 );
829 } // namespace chart
830 } // namespace drawingml
831 } // namespace oox
833 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */