tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / oox / source / drawingml / chart / seriesconverter.cxx
blob27f25057badfd1441cf712ca3c9f4ce28a26a041
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/chart2/RelativePosition.hpp>
24 #include <com/sun/star/chart2/RelativeSize.hpp>
25 #include <com/sun/star/chart/ErrorBarStyle.hpp>
26 #include <com/sun/star/chart2/DataPointLabel.hpp>
27 #include <com/sun/star/drawing/Hatch.hpp>
28 #include <com/sun/star/chart2/XChartDocument.hpp>
29 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
30 #include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
31 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
32 #include <com/sun/star/chart2/XDataSeries.hpp>
33 #include <com/sun/star/chart2/XRegressionCurve.hpp>
34 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
35 #include <com/sun/star/chart2/data/XDataSink.hpp>
36 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <com/sun/star/drawing/FillStyle.hpp>
40 #include <comphelper/sequence.hxx>
41 #include <osl/diagnose.h>
42 #include <drawingml/chart/datasourceconverter.hxx>
43 #include <drawingml/chart/seriesmodel.hxx>
44 #include <drawingml/chart/titleconverter.hxx>
45 #include <drawingml/chart/typegroupconverter.hxx>
46 #include <drawingml/chart/typegroupmodel.hxx>
47 #include <drawingml/fillproperties.hxx>
48 #include <oox/core/xmlfilterbase.hxx>
49 #include <oox/helper/modelobjecthelper.hxx>
50 #include <oox/token/properties.hxx>
51 #include <oox/token/tokens.hxx>
52 #include <drawingml/lineproperties.hxx>
53 #include <drawingml/textparagraph.hxx>
54 #include <drawingml/textrun.hxx>
55 #include <drawingml/textfield.hxx>
56 #include <drawingml/textbody.hxx>
57 #include <drawingml/hatchmap.hxx>
59 namespace oox::drawingml::chart {
61 using namespace com::sun::star;
62 using namespace ::com::sun::star::beans;
63 using namespace ::com::sun::star::chart2;
64 using namespace ::com::sun::star::chart2::data;
65 using namespace ::com::sun::star::uno;
67 namespace {
69 Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
70 const ConverterRoot& rParent,
71 DataSourceModel* pValues, const OUString& rRole,
72 TextModel* pTitle = nullptr )
74 // create data sequence for values
75 Reference< XDataSequence > xValueSeq;
76 if( pValues )
78 DataSourceConverter aSourceConv( rParent, *pValues );
79 xValueSeq = aSourceConv.createDataSequence( rRole );
82 // create data sequence for title
83 Reference< XDataSequence > xTitleSeq;
84 if( pTitle )
86 TextConverter aTextConv( rParent, *pTitle );
87 xTitleSeq = aTextConv.createDataSequence( u"label"_ustr );
90 // create the labeled data sequence, if values or title are present
91 Reference< XLabeledDataSequence > xLabeledSeq;
92 if( xValueSeq.is() || xTitleSeq.is() )
94 xLabeledSeq = LabeledDataSequence::create(rParent.getComponentContext());
95 if( xLabeledSeq.is() )
97 xLabeledSeq->setValues( xValueSeq );
98 xLabeledSeq->setLabel( xTitleSeq );
101 return xLabeledSeq;
104 void convertTextProperty(PropertySet& rPropSet, ObjectFormatter& rFormatter,
105 DataLabelModelBase::TextBodyRef xTextProps)
107 rFormatter.convertTextFormatting( rPropSet, xTextProps, OBJECTTYPE_DATALABEL );
108 ObjectFormatter::convertTextRotation( rPropSet, xTextProps, false );
109 ObjectFormatter::convertTextWrap( rPropSet, xTextProps );
112 void lclConvertLabelFormatting( PropertySet& rPropSet, ObjectFormatter& rFormatter,
113 DataLabelModelBase& rDataLabel, const TypeGroupConverter& rTypeGroup,
114 bool bDataSeriesLabel, bool bCustomLabelField, bool bHasInternalData, bool bMSO2007Doc )
116 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
118 /* Excel 2007 does not change the series setting for a single data point,
119 if none of some specific elements occur. But only one existing element
120 in a data point will reset most other of these elements from the series
121 (e.g.: series has <c:showVal>, data point has <c:showCatName>, this
122 will reset <c:showVal> for this point, unless <c:showVal> is repeated
123 in the data point). The elements <c:layout>, <c:numberFormat>,
124 <c:spPr>, <c:tx>, and <c:txPr> are not affected at all. */
125 bool bHasAnyElement = true;
126 if (bMSO2007Doc)
128 bHasAnyElement = rDataLabel.moaSeparator.has_value() || rDataLabel.monLabelPos.has_value() ||
129 rDataLabel.mobShowCatName.has_value() || rDataLabel.mobShowLegendKey.has_value() ||
130 rDataLabel.mobShowPercent.has_value() || rDataLabel.mobShowSerName.has_value() ||
131 rDataLabel.mobShowVal.has_value();
134 bool bShowValue = !rDataLabel.mbDeleted && rDataLabel.mobShowVal.value_or( !bMSO2007Doc );
135 bool bShowPercent = !rDataLabel.mbDeleted && rDataLabel.mobShowPercent.value_or( !bMSO2007Doc ) && (rTypeInfo.meTypeCategory == TYPECATEGORY_PIE);
136 bool bShowCateg = !rDataLabel.mbDeleted && rDataLabel.mobShowCatName.value_or( !bMSO2007Doc );
137 bool bShowSerName = !rDataLabel.mbDeleted && rDataLabel.mobShowSerName.value_or( !bMSO2007Doc );
138 bool bShowSymbol = !rDataLabel.mbDeleted && rDataLabel.mobShowLegendKey.value_or( !bMSO2007Doc );
140 // tdf#132174, tdf#136650: the inner data table has no own cell number format.
141 if( bHasInternalData && bShowValue && !bShowPercent )
142 rDataLabel.maNumberFormat.mbSourceLinked = false;
144 // type of attached label
145 if( bHasAnyElement || rDataLabel.mbDeleted )
147 DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, bCustomLabelField, bShowSerName );
148 rPropSet.setProperty( PROP_Label, aPointLabel );
151 if( rDataLabel.mbDeleted )
152 return;
154 // data label number format (percentage format wins over value format)
155 rFormatter.convertNumberFormat( rPropSet, rDataLabel.maNumberFormat, false, bShowPercent );
157 // data label text formatting (frame formatting not supported by Chart2)
158 if( bDataSeriesLabel || (rDataLabel.mxTextProp.is() && !rDataLabel.mxTextProp->getParagraphs().empty()) )
159 convertTextProperty(rPropSet, rFormatter, rDataLabel.mxTextProp);
161 // data label separator (do not overwrite series separator, if no explicit point separator is present)
162 // Set the data label separator to "new line" if the value is shown as percentage with a category name,
163 // just like in MS-Office. In any other case the default separator will be a semicolon.
164 if( bShowPercent && !bShowValue && ( bDataSeriesLabel || rDataLabel.moaSeparator.has_value() ) )
165 rPropSet.setProperty( PROP_LabelSeparator, rDataLabel.moaSeparator.value_or( "\n" ) );
166 else if( bDataSeriesLabel || rDataLabel.moaSeparator.has_value() )
167 rPropSet.setProperty( PROP_LabelSeparator, rDataLabel.moaSeparator.value_or( "; " ) );
169 // data label placement (do not overwrite series placement, if no explicit point placement is present)
170 if( !(bDataSeriesLabel || rDataLabel.monLabelPos.has_value()) )
171 return;
173 namespace csscd = css::chart::DataLabelPlacement;
174 sal_Int32 nPlacement = -1;
175 switch( rDataLabel.monLabelPos.value_or( XML_TOKEN_INVALID ) )
177 case XML_outEnd: nPlacement = csscd::OUTSIDE; break;
178 case XML_inEnd: nPlacement = csscd::INSIDE; break;
179 case XML_ctr: nPlacement = csscd::CENTER; break;
180 case XML_inBase: nPlacement = csscd::NEAR_ORIGIN; break;
181 case XML_t: nPlacement = csscd::TOP; break;
182 case XML_b: nPlacement = csscd::BOTTOM; break;
183 case XML_l: nPlacement = csscd::LEFT; break;
184 case XML_r: nPlacement = csscd::RIGHT; break;
185 case XML_bestFit: nPlacement = csscd::AVOID_OVERLAP; break;
188 if( !bDataSeriesLabel && nPlacement == -1 )
189 return;
191 if( nPlacement == -1 )
192 nPlacement = rTypeInfo.mnDefLabelPos;
194 rPropSet.setProperty( PROP_LabelPlacement, nPlacement );
197 void importBorderProperties( PropertySet& rPropSet, Shape& rShape, const GraphicHelper& rGraphicHelper )
199 LineProperties& rLP = rShape.getLineProperties();
200 // no fill has the same effect as no border so skip it
201 if (rLP.maLineFill.moFillType.has_value() && rLP.maLineFill.moFillType.value() == XML_noFill)
202 return;
204 if (rLP.moLineWidth.has_value())
206 sal_Int32 nWidth = convertEmuToHmm(rLP.moLineWidth.value());
207 rPropSet.setProperty(PROP_LabelBorderWidth, uno::Any(nWidth));
208 rPropSet.setProperty(PROP_LabelBorderStyle, uno::Any(drawing::LineStyle_SOLID));
210 const Color& aColor = rLP.maLineFill.maFillColor;
211 ::Color nColor = aColor.getColor(rGraphicHelper);
212 rPropSet.setProperty(PROP_LabelBorderColor, uno::Any(nColor));
215 void lcl_ImportLeaderLineProperties(PropertySet& rPropSet, Shape& rShape,
216 const GraphicHelper& rGraphicHelper)
218 LineProperties& rLP = rShape.getLineProperties();
219 // no fill has the same effect as no line so skip it
220 if (rLP.maLineFill.moFillType.has_value() && rLP.maLineFill.moFillType.value() == XML_noFill)
221 return;
223 if (rLP.moLineWidth.has_value())
225 sal_Int32 nWidth = convertEmuToHmm(rLP.moLineWidth.value());
226 rPropSet.setProperty(PROP_LineWidth, uno::Any(nWidth));
229 if (rLP.maLineFill.moFillType.has_value() && rLP.maLineFill.moFillType.value() == XML_solidFill)
231 const Color& aColor = rLP.maLineFill.maFillColor;
232 ::Color nColor = aColor.getColor(rGraphicHelper);
233 rPropSet.setProperty(PROP_LineColor, uno::Any(nColor));
237 void importFillProperties( PropertySet& rPropSet, Shape& rShape, const GraphicHelper& rGraphicHelper, ModelObjectHelper& rModelObjHelper )
239 FillProperties& rFP = rShape.getFillProperties();
241 if (rFP.moFillType.has_value() && rFP.moFillType.value() == XML_solidFill)
243 rPropSet.setProperty(PROP_LabelFillStyle, drawing::FillStyle_SOLID);
245 const Color& aColor = rFP.maFillColor;
246 ::Color nColor = aColor.getColor(rGraphicHelper);
247 rPropSet.setProperty(PROP_LabelFillColor, uno::Any(nColor));
249 else if(rFP.moFillType.has_value() && rFP.moFillType.value() == XML_pattFill)
251 rPropSet.setProperty(PROP_LabelFillStyle, drawing::FillStyle_HATCH);
252 rPropSet.setProperty(PROP_LabelFillBackground, true);
254 Color aHatchColor( rFP.maPatternProps.maPattFgColor );
255 drawing::Hatch aHatch = createHatch(rFP.maPatternProps.moPattPreset.value(), aHatchColor.getColor(rGraphicHelper, 0));
257 OUString sHatchName = rModelObjHelper.insertFillHatch(aHatch);
258 rPropSet.setProperty(PROP_LabelFillHatchName, sHatchName);
260 const Color& aColor = rFP.maPatternProps.maPattBgColor;
261 ::Color nColor = aColor.getColor(rGraphicHelper);
262 rPropSet.setProperty(PROP_LabelFillColor, uno::Any(nColor));
267 DataPointCustomLabelFieldType lcl_ConvertFieldNameToFieldEnum( std::u16string_view rField )
269 if (rField == u"VALUE")
270 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_VALUE;
271 else if (rField == u"SERIESNAME")
272 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_SERIESNAME;
273 else if (rField == u"CATEGORYNAME")
274 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CATEGORYNAME;
275 else if (rField == u"CELLREF")
276 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLREF;
277 else if (rField == u"CELLRANGE")
278 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE;
279 else if (rField == u"PERCENTAGE")
280 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_PERCENTAGE;
281 else
282 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT;
285 } // namespace
287 DataLabelConverter::DataLabelConverter( const ConverterRoot& rParent, DataLabelModel& rModel ) :
288 ConverterBase< DataLabelModel >( rParent, rModel )
292 DataLabelConverter::~DataLabelConverter()
296 void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDataSeries, const TypeGroupConverter& rTypeGroup )
298 if (!rxDataSeries.is())
299 return;
303 bool bMSO2007Doc = getFilter().isMSO2007Document();
304 bool bHasInternalData = getChartDocument()->hasInternalDataProvider();
305 bool bCustomLabelField = mrModel.mxText && mrModel.mxText->mxTextBody && !mrModel.mxText->mxTextBody->getParagraphs().empty();
306 PropertySet aPropSet( rxDataSeries->getDataPointByIndex( mrModel.mnIndex ) );
308 lclConvertLabelFormatting( aPropSet, getFormatter(), mrModel, rTypeGroup, false, bCustomLabelField, bHasInternalData, bMSO2007Doc );
310 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
311 bool bIsPie = rTypeInfo.meTypeCategory == TYPECATEGORY_PIE;
313 if( mrModel.mxLayout && !mrModel.mxLayout->mbAutoLayout )
315 RelativePosition aPos(mrModel.mxLayout->mfX, mrModel.mxLayout->mfY, css::drawing::Alignment_TOP_LEFT);
316 aPropSet.setProperty(PROP_CustomLabelPosition, aPos);
317 const RelativeSize aSize(mrModel.mxLayout->mfW, mrModel.mxLayout->mfH);
318 aPropSet.setProperty(PROP_CustomLabelSize, aSize);
319 sal_Int32 nPlacement = -1;
320 if (bIsPie && aPropSet.getProperty(nPlacement, PROP_LabelPlacement)
321 && nPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP)
322 aPropSet.setProperty(PROP_LabelPlacement, css::chart::DataLabelPlacement::CUSTOM);
325 if (mrModel.mxShapeProp)
327 importBorderProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper());
328 uno::Reference<lang::XMultiServiceFactory> xFactory(getChartDocument(), uno::UNO_QUERY);
329 ModelObjectHelper& rHelper = getFilter().getModelObjectHelperForModel(xFactory);
330 importFillProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper(),
331 rHelper);
333 if( bCustomLabelField )
335 css::uno::Reference< XComponentContext > xContext = getComponentContext();
337 auto& rParagraphs = mrModel.mxText->mxTextBody->getParagraphs();
339 int nSequenceSize = 0;
340 for( auto& pParagraph : rParagraphs )
341 nSequenceSize += pParagraph->getRuns().size();
343 int nParagraphs = rParagraphs.size();
344 if( nParagraphs > 1 )
345 nSequenceSize += nParagraphs - 1;
347 std::optional< OUString > oaLabelText;
348 std::optional< OUString > oaCellRange;
349 if (mrModel.mobShowDataLabelsRange.value_or(false))
351 const DataSourceModel* pLabelSource = mrModel.mrParent.mpLabelsSource;
352 if (pLabelSource && pLabelSource->mxDataSeq.is())
354 oaCellRange = pLabelSource->mxDataSeq->maFormula;
355 const auto& rLabelMap = pLabelSource->mxDataSeq->maData;
356 const auto aKV = rLabelMap.find(mrModel.mnIndex);
357 if (aKV != rLabelMap.end())
359 oaLabelText.emplace();
360 aKV->second >>= *oaLabelText;
365 uno::Sequence< css::uno::Reference< XDataPointCustomLabelField > > aSequence( nSequenceSize );
366 auto aSequenceRange = asNonConstRange(aSequence);
368 int nPos = 0;
370 for( auto& pParagraph : rParagraphs )
372 for( auto& pRun : pParagraph->getRuns() )
374 css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
376 // Store properties
377 oox::PropertySet aPropertySet( xCustomLabel );
378 convertTextProperty( aPropertySet, getFormatter(), mrModel.mxText->mxTextBody );
379 pRun->getTextCharacterProperties().pushToPropSet( aPropertySet, getFilter() );
381 if (TextField* pField = dynamic_cast<TextField*>(pRun.get()))
383 DataPointCustomLabelFieldType eType = lcl_ConvertFieldNameToFieldEnum( pField->getType() );
385 if (eType == DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE && oaCellRange.has_value())
387 xCustomLabel->setCellRange( oaCellRange.value() );
388 xCustomLabel->setString( oaLabelText.value_or("") );
389 xCustomLabel->setDataLabelsRange( true );
391 else
392 xCustomLabel->setString( pField->getText() );
394 xCustomLabel->setFieldType( eType );
395 xCustomLabel->setGuid( pField->getUuid() );
397 else
399 xCustomLabel->setString( pRun->getText() );
400 xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT );
402 aSequenceRange[ nPos++ ] = std::move(xCustomLabel);
405 if( nParagraphs > 1 && nPos < nSequenceSize )
407 css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
408 xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_NEWLINE );
409 xCustomLabel->setString(u"\n"_ustr);
410 aSequenceRange[ nPos++ ] = std::move(xCustomLabel);
414 aPropSet.setProperty( PROP_CustomLabelFields, Any( aSequence ) );
415 convertTextProperty(aPropSet, getFormatter(), mrModel.mxText->mxTextBody);
418 catch( Exception& )
423 DataLabelsConverter::DataLabelsConverter( const ConverterRoot& rParent, DataLabelsModel& rModel ) :
424 ConverterBase< DataLabelsModel >( rParent, rModel )
428 DataLabelsConverter::~DataLabelsConverter()
432 namespace
434 /// Inherit <c:dLbl> text props (if not set) from <c:dLbls> text props (if set).
435 void InheritFromDataLabelsTextProps(const DataLabelsModel& rLabels, const DataLabelModel& rLabel)
437 // See if <c:dLbls> contains text properties to inherit.
438 if (!rLabels.mxTextProp.is() || rLabels.mxTextProp->getParagraphs().empty())
440 return;
443 const std::shared_ptr<TextParagraph>& rLabelsParagraph = rLabels.mxTextProp->getParagraphs()[0];
445 // See if <c:dLbl> lacks text properties.
446 if (rLabel.mxTextProp.is())
448 return;
451 if (!rLabel.mxText || !rLabel.mxText->mxTextBody
452 || rLabel.mxText->mxTextBody->getParagraphs().empty())
454 return;
457 const std::shared_ptr<TextParagraph>& rLabelParagraph
458 = rLabel.mxText->mxTextBody->getParagraphs()[0];
460 // Inherit rLabel.mxText's char props from rLabels.mxTextProp's char props.
461 TextCharacterProperties aCharProps;
462 aCharProps.assignUsed(rLabelsParagraph->getProperties().getTextCharacterProperties());
463 aCharProps.assignUsed(rLabelParagraph->getProperties().getTextCharacterProperties());
464 rLabelParagraph->getProperties().getTextCharacterProperties().assignUsed(aCharProps);
468 void DataLabelsConverter::convertFromModel( const Reference< XDataSeries >& rxDataSeries, const TypeGroupConverter& rTypeGroup )
470 PropertySet aPropSet( rxDataSeries );
471 if( !mrModel.mbDeleted )
473 bool bMSO2007Doc = getFilter().isMSO2007Document();
474 bool bHasInternalData = getChartDocument()->hasInternalDataProvider();
476 lclConvertLabelFormatting( aPropSet, getFormatter(), mrModel, rTypeGroup, true, false, bHasInternalData, bMSO2007Doc );
478 if (mrModel.mxShapeProp)
480 // Import baseline border properties for these data labels.
481 importBorderProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper());
482 uno::Reference<lang::XMultiServiceFactory> xFactory(getChartDocument(), uno::UNO_QUERY);
483 ModelObjectHelper& rHelper = getFilter().getModelObjectHelperForModel(xFactory);
484 importFillProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper(),
485 rHelper);
488 // import leaderline of data labels
489 if( !mrModel.mbShowLeaderLines )
490 aPropSet.setProperty( PROP_ShowCustomLeaderLines, false );
492 if (mrModel.mxLeaderLines)
494 // Import leaderline properties (SolidFill color, and width)
495 lcl_ImportLeaderLineProperties(aPropSet, *mrModel.mxLeaderLines,
496 getFilter().getGraphicHelper());
499 // data point label settings
500 for (auto const& pointLabel : mrModel.maPointLabels)
502 if (pointLabel->maNumberFormat.maFormatCode.isEmpty())
503 pointLabel->maNumberFormat = mrModel.maNumberFormat;
504 InheritFromDataLabelsTextProps(mrModel, *pointLabel);
506 DataLabelConverter aLabelConv(*this, *pointLabel);
507 aLabelConv.convertFromModel( rxDataSeries, rTypeGroup );
511 ErrorBarConverter::ErrorBarConverter( const ConverterRoot& rParent, ErrorBarModel& rModel ) :
512 ConverterBase< ErrorBarModel >( rParent, rModel )
516 ErrorBarConverter::~ErrorBarConverter()
520 void ErrorBarConverter::convertFromModel( const Reference< XDataSeries >& rxDataSeries )
522 bool bShowPos = (mrModel.mnTypeId == XML_plus) || (mrModel.mnTypeId == XML_both);
523 bool bShowNeg = (mrModel.mnTypeId == XML_minus) || (mrModel.mnTypeId == XML_both);
524 if( !(bShowPos || bShowNeg) )
525 return;
529 Reference< XPropertySet > xErrorBar( createInstance( u"com.sun.star.chart2.ErrorBar"_ustr ), UNO_QUERY_THROW );
530 PropertySet aBarProp( xErrorBar );
532 // plus/minus bars
533 aBarProp.setProperty( PROP_ShowPositiveError, bShowPos );
534 aBarProp.setProperty( PROP_ShowNegativeError, bShowNeg );
536 // type of displayed error
537 namespace cssc = css::chart;
538 switch( mrModel.mnValueType )
540 case XML_cust:
542 // #i87806# manual error bars
543 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::FROM_DATA );
544 // attach data sequences to error bar
545 Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
546 if( xDataSink.is() )
548 // create vector of all value sequences
549 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
550 // add positive values
551 if( bShowPos )
553 Reference< XLabeledDataSequence > xValueSeq = createLabeledDataSequence( ErrorBarModel::PLUS );
554 if( xValueSeq.is() )
555 aLabeledSeqVec.push_back( xValueSeq );
557 // add negative values
558 if( bShowNeg )
560 Reference< XLabeledDataSequence > xValueSeq = createLabeledDataSequence( ErrorBarModel::MINUS );
561 if( xValueSeq.is() )
562 aLabeledSeqVec.push_back( xValueSeq );
564 // attach labeled data sequences to series
565 if( aLabeledSeqVec.empty() )
566 xErrorBar.clear();
567 else
568 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
571 break;
572 case XML_fixedVal:
573 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::ABSOLUTE );
574 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
575 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
576 break;
577 case XML_percentage:
578 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::RELATIVE );
579 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
580 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
581 break;
582 case XML_stdDev:
583 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_DEVIATION );
584 aBarProp.setProperty( PROP_Weight, mrModel.mfValue );
585 break;
586 case XML_stdErr:
587 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_ERROR );
588 break;
589 default:
590 OSL_FAIL( "ErrorBarConverter::convertFromModel - unknown error bar type" );
591 xErrorBar.clear();
594 // error bar formatting
595 getFormatter().convertFrameFormatting( aBarProp, mrModel.mxShapeProp, OBJECTTYPE_ERRORBAR );
597 if( xErrorBar.is() )
599 PropertySet aSeriesProp( rxDataSeries );
600 switch( mrModel.mnDirection )
602 case XML_x: aSeriesProp.setProperty( PROP_ErrorBarX, xErrorBar ); break;
603 case XML_y: aSeriesProp.setProperty( PROP_ErrorBarY, xErrorBar ); break;
604 default: OSL_FAIL( "ErrorBarConverter::convertFromModel - invalid error bar direction" );
608 catch( Exception& )
610 OSL_FAIL( "ErrorBarConverter::convertFromModel - error while creating error bars" );
614 Reference< XLabeledDataSequence > ErrorBarConverter::createLabeledDataSequence( ErrorBarModel::SourceType eSourceType )
616 OUString aRole;
617 switch( eSourceType )
619 case ErrorBarModel::PLUS:
620 switch( mrModel.mnDirection )
622 case XML_x: aRole = "error-bars-x-positive"; break;
623 case XML_y: aRole = "error-bars-y-positive"; break;
625 break;
626 case ErrorBarModel::MINUS:
627 switch( mrModel.mnDirection )
629 case XML_x: aRole = "error-bars-x-negative"; break;
630 case XML_y: aRole = "error-bars-y-negative"; break;
632 break;
634 OSL_ENSURE( !aRole.isEmpty(), "ErrorBarConverter::createLabeledDataSequence - invalid error bar direction" );
635 return lclCreateLabeledDataSequence( *this, mrModel.maSources.get( eSourceType ).get(), aRole );
638 TrendlineLabelConverter::TrendlineLabelConverter( const ConverterRoot& rParent, TrendlineLabelModel& rModel ) :
639 ConverterBase< TrendlineLabelModel >( rParent, rModel )
643 TrendlineLabelConverter::~TrendlineLabelConverter()
647 void TrendlineLabelConverter::convertFromModel( PropertySet& rPropSet )
649 // formatting
650 getFormatter().convertFormatting( rPropSet, mrModel.mxShapeProp, mrModel.mxTextProp, OBJECTTYPE_TRENDLINELABEL );
653 TrendlineConverter::TrendlineConverter( const ConverterRoot& rParent, TrendlineModel& rModel ) :
654 ConverterBase< TrendlineModel >( rParent, rModel )
658 TrendlineConverter::~TrendlineConverter()
662 void TrendlineConverter::convertFromModel( const Reference< XDataSeries >& rxDataSeries )
666 // trend line type
667 OUString aServiceName;
668 switch( mrModel.mnTypeId )
670 case XML_exp:
671 aServiceName = "com.sun.star.chart2.ExponentialRegressionCurve";
672 break;
673 case XML_linear:
674 aServiceName = "com.sun.star.chart2.LinearRegressionCurve";
675 break;
676 case XML_log:
677 aServiceName = "com.sun.star.chart2.LogarithmicRegressionCurve";
678 break;
679 case XML_movingAvg:
680 aServiceName = "com.sun.star.chart2.MovingAverageRegressionCurve";
681 break;
682 case XML_poly:
683 aServiceName = "com.sun.star.chart2.PolynomialRegressionCurve";
684 break;
685 case XML_power:
686 aServiceName = "com.sun.star.chart2.PotentialRegressionCurve";
687 break;
688 default:
689 OSL_FAIL( "TrendlineConverter::convertFromModel - unknown trendline type" );
691 if( !aServiceName.isEmpty() )
693 Reference< XRegressionCurve > xRegCurve( createInstance( aServiceName ), UNO_QUERY_THROW );
694 PropertySet aPropSet( xRegCurve );
696 // Name
697 aPropSet.setProperty( PROP_CurveName, mrModel.maName );
698 aPropSet.setProperty( PROP_PolynomialDegree, mrModel.mnOrder );
699 aPropSet.setProperty( PROP_MovingAveragePeriod, mrModel.mnPeriod );
701 // Intercept
702 bool hasIntercept = mrModel.mfIntercept.has_value();
703 aPropSet.setProperty( PROP_ForceIntercept, hasIntercept);
704 if (hasIntercept)
705 aPropSet.setProperty( PROP_InterceptValue, mrModel.mfIntercept.value());
707 // Extrapolation
708 if (mrModel.mfForward.has_value())
709 aPropSet.setProperty( PROP_ExtrapolateForward, mrModel.mfForward.value() );
710 if (mrModel.mfBackward.has_value())
711 aPropSet.setProperty( PROP_ExtrapolateBackward, mrModel.mfBackward.value() );
713 // trendline formatting
714 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, OBJECTTYPE_TRENDLINE );
716 // #i83100# show equation and correlation coefficient
717 PropertySet aLabelProp( xRegCurve->getEquationProperties() );
718 aLabelProp.setProperty( PROP_ShowEquation, mrModel.mbDispEquation );
719 aLabelProp.setProperty( PROP_ShowCorrelationCoefficient, mrModel.mbDispRSquared );
721 // #i83100# formatting of the equation text box
722 if( mrModel.mbDispEquation || mrModel.mbDispRSquared )
724 TrendlineLabelConverter aLabelConv( *this, mrModel.mxLabel.getOrCreate() );
725 aLabelConv.convertFromModel( aLabelProp );
728 // unsupported: #i5085# manual trendline size
729 // unsupported: #i34093# manual crossing point
731 Reference< XRegressionCurveContainer > xRegCurveCont( rxDataSeries, UNO_QUERY_THROW );
732 xRegCurveCont->addRegressionCurve( xRegCurve );
735 catch( Exception& )
737 OSL_FAIL( "TrendlineConverter::convertFromModel - error while creating trendline" );
741 DataPointConverter::DataPointConverter( const ConverterRoot& rParent, DataPointModel& rModel ) :
742 ConverterBase< DataPointModel >( rParent, rModel )
746 DataPointConverter::~DataPointConverter()
750 void DataPointConverter::convertFromModel( const Reference< XDataSeries >& rxDataSeries,
751 const TypeGroupConverter& rTypeGroup, const SeriesModel& rSeries )
753 bool bMSO2007Doc = getFilter().isMSO2007Document();
756 PropertySet aPropSet( rxDataSeries->getDataPointByIndex( mrModel.mnIndex ) );
758 // data point marker
759 if( ( mrModel.monMarkerSymbol.has_value() && mrModel.monMarkerSymbol.value() != rSeries.mnMarkerSymbol ) ||
760 ( mrModel.monMarkerSize.has_value() && mrModel.monMarkerSize.value() != rSeries.mnMarkerSize ) )
761 rTypeGroup.convertMarker( aPropSet, mrModel.monMarkerSymbol.value_or( rSeries.mnMarkerSymbol ),
762 mrModel.monMarkerSize.value_or( rSeries.mnMarkerSize ), mrModel.mxMarkerProp );
764 // data point pie explosion
765 if( mrModel.monExplosion.has_value() && mrModel.monExplosion.value() != rSeries.mnExplosion )
766 rTypeGroup.convertPieExplosion( aPropSet, mrModel.monExplosion.value() );
768 // data point invert negative
769 if( mrModel.mbInvertNeg != rSeries.mbInvertNeg ) {
770 aPropSet.setProperty( PROP_InvertNegative, mrModel.mbInvertNeg);
773 // point formatting
774 if( mrModel.mxShapeProp.is() )
776 if( rTypeGroup.getTypeInfo().mbPictureOptions )
777 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), rTypeGroup.getSeriesObjectType(), rSeries.mnIndex );
778 else
779 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, rTypeGroup.getSeriesObjectType(), rSeries.mnIndex );
781 else if (rSeries.mxShapeProp.is())
783 getFormatter().convertFrameFormatting( aPropSet, rSeries.mxShapeProp, rTypeGroup.getSeriesObjectType(), rSeries.mnIndex );
786 catch( Exception& )
791 SeriesConverter::SeriesConverter( const ConverterRoot& rParent, SeriesModel& rModel ) :
792 ConverterBase< SeriesModel >( rParent, rModel )
796 SeriesConverter::~SeriesConverter()
800 Reference< XLabeledDataSequence > SeriesConverter::createCategorySequence( const OUString& rRole )
802 return createLabeledDataSequence(SeriesModel::CATEGORIES, rRole, false);
805 Reference< XLabeledDataSequence > SeriesConverter::createValueSequence( const OUString& rRole )
807 return createLabeledDataSequence( SeriesModel::VALUES, rRole, true );
810 Reference< XDataSeries > SeriesConverter::createDataSeries( const TypeGroupConverter& rTypeGroup, bool bVaryColorsByPoint )
812 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
814 // create the data series object
815 Reference< XDataSeries > xDataSeries( createInstance( u"com.sun.star.chart2.DataSeries"_ustr ), UNO_QUERY );
816 PropertySet aSeriesProp( xDataSeries );
818 // attach data and title sequences to series
819 sal_Int32 nDataPointCount = 0;
820 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
821 if( xDataSink.is() )
823 // create vector of all value sequences
824 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
825 // add Y values
826 Reference< XLabeledDataSequence > xYValueSeq = createValueSequence( u"values-y"_ustr );
827 if( xYValueSeq.is() )
829 aLabeledSeqVec.push_back( xYValueSeq );
830 Reference< XDataSequence > xValues = xYValueSeq->getValues();
831 if( xValues.is() )
832 nDataPointCount = xValues->getData().getLength();
834 if (!nDataPointCount)
835 // No values present. Don't create a data series.
836 return Reference<XDataSeries>();
838 // add X values of scatter and bubble charts
839 if( !rTypeInfo.mbCategoryAxis )
841 Reference< XLabeledDataSequence > xXValueSeq = createCategorySequence( u"values-x"_ustr );
842 if( xXValueSeq.is() )
843 aLabeledSeqVec.push_back( xXValueSeq );
844 // add size values of bubble charts
845 if( rTypeInfo.meTypeId == TYPEID_BUBBLE )
847 Reference< XLabeledDataSequence > xSizeValueSeq = createLabeledDataSequence( SeriesModel::POINTS, u"values-size"_ustr, true );
848 if( xSizeValueSeq.is() )
849 aLabeledSeqVec.push_back( xSizeValueSeq );
852 // attach labeled data sequences to series
853 if( !aLabeledSeqVec.empty() )
854 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
857 // error bars
858 for (auto const& errorBar : mrModel.maErrorBars)
860 ErrorBarConverter aErrorBarConv(*this, *errorBar);
861 aErrorBarConv.convertFromModel( xDataSeries );
864 // trendlines
865 for (auto const& trendLine : mrModel.maTrendlines)
867 TrendlineConverter aTrendlineConv(*this, *trendLine);
868 aTrendlineConv.convertFromModel( xDataSeries );
871 // data point markers
872 rTypeGroup.convertMarker( aSeriesProp, mrModel.mnMarkerSymbol, mrModel.mnMarkerSize, mrModel.mxMarkerProp );
873 #if OOX_CHART_SMOOTHED_PER_SERIES
874 // #i66858# smoothed series lines
875 rTypeGroup.convertLineSmooth( aSeriesProp, mrModel.mbSmooth );
876 #endif
877 // 3D bar style (not possible to set at chart type -> set at all series)
878 rTypeGroup.convertBarGeometry( aSeriesProp, mrModel.monShape.value_or( rTypeGroup.getModel().mnShape ) );
879 // pie explosion (restricted to [0%,100%] in Chart2)
880 rTypeGroup.convertPieExplosion( aSeriesProp, mrModel.mnExplosion );
881 // invert if negative
882 aSeriesProp.setProperty(PROP_InvertNegative, mrModel.mbInvertNeg);
884 // series formatting
885 ObjectFormatter& rFormatter = getFormatter();
886 ObjectType eObjType = rTypeGroup.getSeriesObjectType();
887 bool bMSO2007Doc = getFilter().isMSO2007Document();
888 if( rTypeInfo.mbPictureOptions )
889 rFormatter.convertFrameFormatting( aSeriesProp, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), eObjType, mrModel.mnIndex );
890 else
891 rFormatter.convertFrameFormatting( aSeriesProp, mrModel.mxShapeProp, eObjType, mrModel.mnIndex );
893 // set the (unused) property default value used by the Chart2 templates (true for pie/doughnut charts)
894 bool bIsPie = rTypeInfo.meTypeCategory == TYPECATEGORY_PIE;
895 aSeriesProp.setProperty( PROP_VaryColorsByPoint, bVaryColorsByPoint );
897 // own area formatting for every data point (TODO: varying line color not supported)
898 // #i91271# always set area formatting for every point in pie/doughnut charts to override their automatic point formatting
899 if( bIsPie || (bVaryColorsByPoint && rTypeGroup.isSeriesFrameFormat() && ObjectFormatter::isAutomaticFill( mrModel.mxShapeProp )) )
901 /* Set the series point number as color cycle size at the object
902 formatter to get correct start-shade/end-tint. TODO: in doughnut
903 charts, the sizes of the series may vary, need to use the maximum
904 point count of all series. */
905 sal_Int32 nOldMax = rFormatter.getMaxSeriesIndex();
906 if( bVaryColorsByPoint )
907 rFormatter.setMaxSeriesIndex( nDataPointCount - 1 );
908 for( sal_Int32 nIndex = 0; nIndex < nDataPointCount; ++nIndex )
912 PropertySet aPointProp( xDataSeries->getDataPointByIndex( nIndex ) );
913 rFormatter.convertAutomaticFill( aPointProp, eObjType, bVaryColorsByPoint ? nIndex : mrModel.mnIndex );
915 catch( Exception& )
919 rFormatter.setMaxSeriesIndex( nOldMax );
922 // data point settings
923 for (auto const& point : mrModel.maPoints)
925 DataPointConverter aPointConv(*this, *point);
926 aPointConv.convertFromModel( xDataSeries, rTypeGroup, mrModel );
929 /* Series data label settings. If and only if the series does not contain
930 a c:dLbls element, then the c:dLbls element of the parent chart type is
931 used (data label settings of the parent chart type are *not* merged
932 into own existing data label settings). */
933 ModelRef< DataLabelsModel > xLabels = mrModel.mxLabels.is() ? mrModel.mxLabels : rTypeGroup.getModel().mxLabels;
934 if( xLabels.is() )
936 if( xLabels->maNumberFormat.maFormatCode.isEmpty() )
938 // Use number format code from Value series
939 DataSourceModel* pValues = mrModel.maSources.get( SeriesModel::VALUES ).get();
940 if( pValues )
941 xLabels->maNumberFormat.maFormatCode = pValues->mxDataSeq->maFormatCode;
943 DataLabelsConverter aLabelsConv( *this, *xLabels );
944 aLabelsConv.convertFromModel( xDataSeries, rTypeGroup );
947 return xDataSeries;
950 // private --------------------------------------------------------------------
952 Reference< XLabeledDataSequence > SeriesConverter::createLabeledDataSequence(
953 SeriesModel::SourceType eSourceType, const OUString& rRole, bool bUseTextLabel )
955 DataSourceModel* pValues = mrModel.maSources.get( eSourceType ).get();
956 TextModel* pTitle = bUseTextLabel ? mrModel.mxText.get() : nullptr;
957 return lclCreateLabeledDataSequence( *this, pValues, rRole, pTitle );
960 } // namespace oox
962 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */