xref: /core/oox/source/drawingml/chart/typegroupconverter.cxx (revision c84c7b3c81308fbafa273c76a75f4a71596666b8)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <drawingml/chart/typegroupconverter.hxx>
21 
22 #include <com/sun/star/chart/DataLabelPlacement.hpp>
23 #include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
24 #include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
25 #include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
26 #include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
27 #include <com/sun/star/chart2/CurveStyle.hpp>
28 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
29 #include <com/sun/star/chart2/PieChartSubType.hpp>
30 #include <com/sun/star/chart2/StackingDirection.hpp>
31 #include <com/sun/star/chart2/Symbol.hpp>
32 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
33 #include <com/sun/star/chart2/XCoordinateSystem.hpp>
34 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
35 #include <com/sun/star/chart2/XDiagram.hpp>
36 #include <com/sun/star/chart2/data/XDataSink.hpp>
37 #include <com/sun/star/drawing/LineStyle.hpp>
38 
39 #include <comphelper/sequence.hxx>
40 #include <osl/diagnose.h>
41 #include <drawingml/lineproperties.hxx>
42 #include <drawingml/chart/seriesmodel.hxx>
43 #include <drawingml/chart/seriesconverter.hxx>
44 #include <drawingml/chart/typegroupmodel.hxx>
45 #include <oox/core/xmlfilterbase.hxx>
46 #include <oox/token/namespaces.hxx>
47 #include <oox/token/properties.hxx>
48 #include <oox/token/tokens.hxx>
49 #include <tools/UnitConversion.hxx>
50 
51 namespace oox::drawingml::chart {
52 
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::chart2;
55 using namespace ::com::sun::star::chart2::data;
56 using namespace ::com::sun::star::uno;
57 
58 namespace {
59 
60 // chart type service names
61 const char SERVICE_CHART2_AREA[]      = "com.sun.star.chart2.AreaChartType";
62 const char SERVICE_CHART2_CANDLE[]    = "com.sun.star.chart2.CandleStickChartType";
63 const char SERVICE_CHART2_COLUMN[]    = "com.sun.star.chart2.ColumnChartType";
64 const char SERVICE_CHART2_LINE[]      = "com.sun.star.chart2.LineChartType";
65 const char SERVICE_CHART2_NET[]       = "com.sun.star.chart2.NetChartType";
66 const char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
67 const char SERVICE_CHART2_PIE[]       = "com.sun.star.chart2.PieChartType";
68 const char SERVICE_CHART2_SCATTER[]   = "com.sun.star.chart2.ScatterChartType";
69 const char SERVICE_CHART2_BUBBLE[]    = "com.sun.star.chart2.BubbleChartType";
70 const char SERVICE_CHART2_SURFACE[]   = "com.sun.star.chart2.ColumnChartType";    // Todo
71 const char SERVICE_CHART2_FUNNEL[]    = "com.sun.star.chart2.FunnelChartType";
72 const char SERVICE_CHART2_HISTO[]     = "com.sun.star.chart2.HistogramChartType";
73 
74 namespace csscd = css::chart::DataLabelPlacement;
75 
76 const TypeGroupInfo spTypeInfos[] =
77 {
78     // type-id          type-category         service                   varied-point-color   default label pos     polar  area2d 1stvis xcateg swap   stack  picopt
79     { TYPEID_BAR,       TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,    VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  false, true,  true  },
80     { TYPEID_HORBAR,    TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,    VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  true,  true,  true  },
81     { TYPEID_HISTO,     TYPECATEGORY_HISTO,   SERVICE_CHART2_HISTO,     VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  true,  true,  true  },
82     { TYPEID_LINE,      TYPECATEGORY_LINE,    SERVICE_CHART2_LINE,      VARPOINTMODE_SINGLE, csscd::RIGHT,         false, false, false, true,  false, true,  false },
83     { TYPEID_AREA,      TYPECATEGORY_LINE,    SERVICE_CHART2_AREA,      VARPOINTMODE_NONE,   csscd::CENTER,        false, true,  false, true,  false, true,  false },
84     { TYPEID_STOCK,     TYPECATEGORY_LINE,    SERVICE_CHART2_CANDLE,    VARPOINTMODE_NONE,   csscd::RIGHT,         false, false, false, true,  false, true,  false },
85     { TYPEID_RADARLINE, TYPECATEGORY_RADAR,   SERVICE_CHART2_NET,       VARPOINTMODE_SINGLE, csscd::OUTSIDE,       true,  false, false, true,  false, false, false },
86     { TYPEID_RADARAREA, TYPECATEGORY_RADAR,   SERVICE_CHART2_FILLEDNET, VARPOINTMODE_NONE,   csscd::OUTSIDE,       true,  true,  false, true,  false, false, false },
87     { TYPEID_PIE,       TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  true,  true,  false, false, false },
88     { TYPEID_DOUGHNUT,  TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  false, true,  false, false, false },
89     { TYPEID_OFPIE,     TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  true,  true,  false, false, false },
90     { TYPEID_SCATTER,   TYPECATEGORY_SCATTER, SERVICE_CHART2_SCATTER,   VARPOINTMODE_SINGLE, csscd::RIGHT,         false, false, false, false, false, false, false },
91     { TYPEID_BUBBLE,    TYPECATEGORY_SCATTER, SERVICE_CHART2_BUBBLE,    VARPOINTMODE_SINGLE, csscd::RIGHT,         false, true,  false, false, false, false, false },
92     { TYPEID_SURFACE,   TYPECATEGORY_SURFACE, SERVICE_CHART2_SURFACE,   VARPOINTMODE_NONE,   csscd::RIGHT,         false, true,  false, true,  false, false, false },
93     { TYPEID_FUNNEL,    TYPECATEGORY_FUNNEL,  SERVICE_CHART2_FUNNEL,    VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  false, false, false }
94 };
95 
96 const TypeGroupInfo saUnknownTypeInfo =
97     { TYPEID_UNKNOWN,   TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,  VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  false, true, true  };
98 
lclGetTypeInfoFromTypeId(TypeId eTypeId)99 const TypeGroupInfo& lclGetTypeInfoFromTypeId( TypeId eTypeId )
100 {
101     for( auto const &rIt : spTypeInfos)
102     {
103         if( rIt.meTypeId == eTypeId )
104             return rIt;
105     }
106     OSL_ENSURE( eTypeId == TYPEID_UNKNOWN, "lclGetTypeInfoFromTypeId - unexpected chart type identifier" );
107     return saUnknownTypeInfo;
108 }
109 
110 } // namespace
111 
GetTypeGroupInfo(TypeId eType)112 const TypeGroupInfo& GetTypeGroupInfo( TypeId eType )
113 {
114     return lclGetTypeInfoFromTypeId(eType);
115 }
116 
UpDownBarsConverter(const ConverterRoot & rParent,UpDownBarsModel & rModel)117 UpDownBarsConverter::UpDownBarsConverter( const ConverterRoot& rParent, UpDownBarsModel& rModel ) :
118     ConverterBase< UpDownBarsModel >( rParent, rModel )
119 {
120 }
121 
~UpDownBarsConverter()122 UpDownBarsConverter::~UpDownBarsConverter()
123 {
124 }
125 
convertFromModel(const Reference<XChartType> & rxChartType)126 void UpDownBarsConverter::convertFromModel( const Reference< XChartType >& rxChartType )
127 {
128     PropertySet aTypeProp( rxChartType );
129 
130     // upbar format
131     Reference< XPropertySet > xWhitePropSet;
132     if( aTypeProp.getProperty( xWhitePropSet, PROP_WhiteDay ) )
133     {
134         PropertySet aPropSet( xWhitePropSet );
135         getFormatter().convertFrameFormatting( aPropSet, mrModel.mxUpBars, OBJECTTYPE_UPBAR );
136     }
137 
138     // downbar format
139     Reference< XPropertySet > xBlackPropSet;
140     if( aTypeProp.getProperty( xBlackPropSet, PROP_BlackDay ) )
141     {
142         PropertySet aPropSet( xBlackPropSet );
143         getFormatter().convertFrameFormatting( aPropSet, mrModel.mxDownBars, OBJECTTYPE_DOWNBAR );
144     }
145 }
146 
TypeGroupConverter(const ConverterRoot & rParent,TypeGroupModel & rModel)147 TypeGroupConverter::TypeGroupConverter( const ConverterRoot& rParent, TypeGroupModel& rModel ) :
148     ConverterBase< TypeGroupModel >( rParent, rModel ),
149     mb3dChart( false )
150 {
151     TypeId eTypeId = TYPEID_UNKNOWN;
152     switch( mrModel.mnTypeId )
153     {
154 #define ENSURE_AXESCOUNT( min, max ) OSL_ENSURE( (min <= static_cast<int>(mrModel.maAxisIds.size())) && (static_cast<int>(mrModel.maAxisIds.size()) <= max), "TypeGroupConverter::TypeGroupConverter - invalid axes count" )
155         case C_TOKEN( area3DChart ):    ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_AREA;      mb3dChart = true;   break;
156         case C_TOKEN( areaChart ):      ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_AREA;      mb3dChart = false;  break;
157         case C_TOKEN( bar3DChart ):     ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_BAR;       mb3dChart = true;   break;
158         case C_TOKEN( barChart ):       ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BAR;       mb3dChart = false;  break;
159         case C_TOKEN( bubbleChart ):    ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BUBBLE;    mb3dChart = false;  break;
160         case C_TOKEN( doughnutChart ):  ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_DOUGHNUT;  mb3dChart = false;  break;
161         case CX_TOKEN( funnel ):        ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_FUNNEL;    mb3dChart = false;  break;
162         case C_TOKEN( line3DChart ):    ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_LINE;      mb3dChart = true;   break;
163         case C_TOKEN( lineChart ):      ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_LINE;      mb3dChart = false;  break;
164         case C_TOKEN( ofPieChart ):     ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_OFPIE;     mb3dChart = false;  break;
165         case C_TOKEN( pie3DChart ):     ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE;       mb3dChart = true;   break;
166         case C_TOKEN( pieChart ):       ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE;       mb3dChart = false;  break;
167         case C_TOKEN( radarChart ):     ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_RADARLINE; mb3dChart = false;  break;
168         case C_TOKEN( scatterChart ):   ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_SCATTER;   mb3dChart = false;  break;
169         case C_TOKEN( stockChart ):     ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_STOCK;     mb3dChart = false;  break;
170         case C_TOKEN( surface3DChart ): ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_SURFACE;   mb3dChart = true;   break;
171         case C_TOKEN( surfaceChart ):   ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_SURFACE;   mb3dChart = true;   break;    // 3D bar chart from all surface charts
172         default:    OSL_FAIL( "TypeGroupConverter::TypeGroupConverter - unknown chart type" );
173 #undef ENSURE_AXESCOUNT
174     }
175 
176     // special handling for some chart types
177     switch( eTypeId )
178     {
179         case TYPEID_BAR:
180             if( mrModel.mnBarDir == XML_bar )
181                 eTypeId = TYPEID_HORBAR;
182         break;
183         case TYPEID_RADARLINE:
184             if( mrModel.mnRadarStyle == XML_filled )
185                 eTypeId = TYPEID_RADARAREA;
186         break;
187         case TYPEID_SURFACE:
188             // create a deep 3D bar chart from surface charts
189             mrModel.mnGrouping = XML_standard;
190         break;
191         default:;
192     }
193 
194     // set the chart type info struct for the current chart type
195     maTypeInfo = lclGetTypeInfoFromTypeId( eTypeId );
196 }
197 
~TypeGroupConverter()198 TypeGroupConverter::~TypeGroupConverter()
199 {
200 }
201 
isStacked() const202 bool TypeGroupConverter::isStacked() const
203 {
204     return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_stacked);
205 }
206 
isPercent() const207 bool TypeGroupConverter::isPercent() const
208 {
209     return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_percentStacked);
210 }
211 
isWall3dChart() const212 bool TypeGroupConverter::isWall3dChart() const
213 {
214     return mb3dChart && (maTypeInfo.meTypeCategory != TYPECATEGORY_PIE);
215 }
216 
isDeep3dChart() const217 bool TypeGroupConverter::isDeep3dChart() const
218 {
219     return isWall3dChart() && (mrModel.mnGrouping == XML_standard);
220 }
221 
isSeriesFrameFormat() const222 bool TypeGroupConverter::isSeriesFrameFormat() const
223 {
224     return mb3dChart || maTypeInfo.mbSeriesIsFrame2d;
225 }
226 
getSeriesObjectType() const227 ObjectType TypeGroupConverter::getSeriesObjectType() const
228 {
229     return mb3dChart ? OBJECTTYPE_FILLEDSERIES3D :
230         (maTypeInfo.mbSeriesIsFrame2d ? OBJECTTYPE_FILLEDSERIES2D : OBJECTTYPE_LINEARSERIES2D);
231 }
232 
getSingleSeriesTitle() const233 OUString TypeGroupConverter::getSingleSeriesTitle() const
234 {
235     OUString aSeriesTitle;
236     if( !mrModel.maSeries.empty() && (maTypeInfo.mbSingleSeriesVis || (mrModel.maSeries.size() == 1)) )
237         if( const TextModel* pText = mrModel.maSeries.front()->mxText.get() )
238             if( const DataSequenceModel* pDataSeq = pText->mxDataSeq.get() )
239                 if( !pDataSeq->maData.empty() )
240                     pDataSeq->maData.begin()->second >>= aSeriesTitle;
241     return aSeriesTitle;
242 }
243 
isSingleSeriesTitle() const244 bool TypeGroupConverter::isSingleSeriesTitle() const
245 {
246     if (!mrModel.maSeries.empty() && (maTypeInfo.mbSingleSeriesVis || (mrModel.maSeries.size() == 1)) &&
247         mrModel.maSeries.front()->mxText.is())
248         return true;
249 
250     return false;
251 }
252 
createCoordinateSystem()253 Reference< XCoordinateSystem > TypeGroupConverter::createCoordinateSystem()
254 {
255     // create the coordinate system object
256     Reference< css::uno::XComponentContext > xContext = getComponentContext();
257     Reference< XCoordinateSystem > xCoordSystem;
258     if( maTypeInfo.mbPolarCoordSystem )
259     {
260         if( mb3dChart )
261             xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
262         else
263             xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
264     }
265     else
266     {
267         if( mb3dChart )
268             xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
269         else
270             xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
271     }
272 
273     // swap X and Y axis
274     if( maTypeInfo.mbSwappedAxesSet )
275     {
276         PropertySet aPropSet( xCoordSystem );
277         aPropSet.setProperty( PROP_SwapXAndYAxis, true );
278     }
279 
280     return xCoordSystem;
281 }
282 
createCategorySequence()283 Reference< XLabeledDataSequence > TypeGroupConverter::createCategorySequence()
284 {
285     sal_Int32 nMaxValues = 0;
286     Reference< XLabeledDataSequence > xLabeledSeq;
287     /*  Find first existing category sequence. The behaviour of Excel 2007 is
288         different to Excel 2003, which always used the category sequence of the
289         first series, even if it was empty. */
290     for (auto const& elem : mrModel.maSeries)
291     {
292         if( elem->maSources.has( DataSourceType::CATEGORIES ) )
293         {
294             SeriesConverter aSeriesConv(*this, *elem);
295             xLabeledSeq = aSeriesConv.createCategorySequence( u"categories"_ustr );
296             if (xLabeledSeq.is())
297                 break;
298         }
299         else if( nMaxValues <= 0 && elem->maSources.has( DataSourceType::VALUES ) )
300         {
301             DataSourceModel *pValues = elem->maSources.get( DataSourceType::VALUES ).get();
302             if( pValues->mxDataSeq.is() )
303                 nMaxValues = pValues->mxDataSeq->maData.size();
304         }
305     }
306     /* n#839727 Create Category Sequence when none are found */
307     if( !xLabeledSeq.is() && !mrModel.maSeries.empty() ) {
308         if( nMaxValues < 0 )
309             nMaxValues = 2;
310         typedef RefVector<SeriesModel> SeriesModelVector;
311         SeriesModelVector::value_type aModel = mrModel.maSeries.get(0);
312         if (!aModel->maSources.has(DataSourceType::CATEGORIES))
313         {
314             DataSourceModel &aSrc = aModel->maSources.create( DataSourceType::CATEGORIES );
315             DataSequenceModel &aSeq = aSrc.mxDataSeq.create();
316             aSeq.mnPointCount = nMaxValues;
317             for( sal_Int32 i = 0; i < nMaxValues; i++ )
318                 aSeq.maData[ i ] <<= OUString::number( i + 1 );
319         }
320         SeriesConverter aSeriesConv( *this,  *aModel );
321         xLabeledSeq = aSeriesConv.createCategorySequence( u"categories"_ustr );
322     }
323     return xLabeledSeq;
324 }
325 
convertFromModel(const Reference<XDiagram> & rxDiagram,const Reference<XCoordinateSystem> & rxCoordSystem,sal_Int32 nAxesSetIdx,bool bSupportsVaryColorsByPoint)326 void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
327         const Reference< XCoordinateSystem >& rxCoordSystem,
328         sal_Int32 nAxesSetIdx,
329         bool bSupportsVaryColorsByPoint )
330 {
331     try
332     {
333         // create the chart type object
334         OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
335         Reference< XChartType > xChartType( createInstance( aService ), UNO_QUERY_THROW );
336 
337         Reference< XChartTypeContainer > xChartTypeContOld( rxCoordSystem, UNO_QUERY_THROW );
338         Sequence< Reference< XChartType > > xOldChartTypes( xChartTypeContOld->getChartTypes() );
339         sal_Int32 nOldChartTypeIdx = -1;
340 
341         // additional properties
342         PropertySet aDiaProp( rxDiagram );
343         PropertySet aTypeProp( xChartType );
344         switch( maTypeInfo.meTypeCategory )
345         {
346             case TYPECATEGORY_BAR:
347             {
348                 Sequence< sal_Int32 > aInt32Seq{ mrModel.mnOverlap, mrModel.mnOverlap };
349                 aTypeProp.setProperty( PROP_OverlapSequence, aInt32Seq );
350                 aInt32Seq = { mrModel.mnGapWidth, mrModel.mnGapWidth };
351                 aTypeProp.setProperty( PROP_GapwidthSequence, aInt32Seq );
352             }
353             break;
354             case TYPECATEGORY_PIE:
355             {
356                 aTypeProp.setProperty( PROP_UseRings, maTypeInfo.meTypeId == TYPEID_DOUGHNUT );
357                 /*  #i85166# starting angle of first pie slice. 3D pie charts
358                     use Y rotation setting in view3D element. Of-pie charts do
359                     not support pie rotation. */
360                 if( !is3dChart() && (maTypeInfo.meTypeId != TYPEID_OFPIE) )
361                     convertPieRotation( aDiaProp, mrModel.mnFirstAngle );
362 
363                 if (maTypeInfo.meTypeId == TYPEID_OFPIE) {
364                     aDiaProp.setProperty(PROP_SubPieType,
365                             convertOfPieType(mrModel.mnOfPieType));
366                     if (mrModel.mnSplitType == XML_auto ||
367                             mrModel.mnSplitType == XML_pos) {
368                         aDiaProp.setProperty(PROP_SplitPos, mrModel.mfSplitPos);
369                     }
370                 } else {
371                     aDiaProp.setProperty(PROP_SubPieType, PieChartSubType_NONE);
372                 }
373             }
374             break;
375             default:;
376         }
377 
378         // create converter objects for all series models
379         typedef RefVector< SeriesConverter > SeriesConvVector;
380         SeriesConvVector aSeries;
381         for (auto const& elemSeries : mrModel.maSeries)
382             aSeries.push_back( std::make_shared<SeriesConverter>(*this, *elemSeries) );
383 
384         // decide whether to use varying colors for each data point
385         bool bVaryColorsByPoint = bSupportsVaryColorsByPoint && mrModel.mbVaryColors;
386         switch( maTypeInfo.meVarPointMode )
387         {
388             case VARPOINTMODE_NONE:     bVaryColorsByPoint = false;                             break;
389             case VARPOINTMODE_SINGLE:   bVaryColorsByPoint &= (mrModel.maSeries.size() == 1);   break;
390             case VARPOINTMODE_MULTI:                                                            break;
391         }
392 
393         /*  Stock chart needs special processing. Create one 'big' series with
394             data sequences of different roles. */
395         if( maTypeInfo.meTypeId == TYPEID_STOCK )
396         {
397             // create the data series object
398             Reference< XDataSeries > xDataSeries( createInstance( u"com.sun.star.chart2.DataSeries"_ustr ), UNO_QUERY );
399             Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
400             if( xDataSink.is() )
401             {
402                 // create a list of data sequences from all series
403                 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
404                 OSL_ENSURE( aSeries.size() >= 3, "TypeGroupConverter::convertFromModel - too few stock chart series" );
405                 int nRoleIdx = (aSeries.size() == 3) ? 1 : 0;
406                 for( auto& rxSeriesConv : aSeries )
407                 {
408                     // create a data sequence with a specific role
409                     OUString aRole;
410                     switch( nRoleIdx )
411                     {
412                         case 0: aRole = "values-first";  break;
413                         case 1: aRole = "values-max";    break;
414                         case 2: aRole = "values-min";    break;
415                         case 3: aRole = "values-last";   break;
416                     }
417                     Reference< XLabeledDataSequence > xDataSeq = rxSeriesConv->createValueSequence( aRole );
418                     if( xDataSeq.is() )
419                         aLabeledSeqVec.push_back( xDataSeq );
420 
421                     ++nRoleIdx;
422                     if (nRoleIdx >= 4)
423                         break;
424                 }
425 
426                 // attach labeled data sequences to series and insert series into chart type
427                 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
428 
429                 // formatting of high/low lines
430                 aTypeProp.setProperty( PROP_ShowHighLow, true );
431                 PropertySet aSeriesProp( xDataSeries );
432                 if( mrModel.mxHiLowLines.is() )
433                     getFormatter().convertFrameFormatting( aSeriesProp, mrModel.mxHiLowLines, OBJECTTYPE_HILOLINE );
434                 else
435                     // hi/low-lines cannot be switched off via "ShowHighLow" property (?)
436                     aSeriesProp.setProperty( PROP_LineStyle, css::drawing::LineStyle_NONE );
437 
438                 // formatting of up/down bars
439                 bool bUpDownBars = mrModel.mxUpDownBars.is();
440                 aTypeProp.setProperty( PROP_Japanese, bUpDownBars );
441                 aTypeProp.setProperty( PROP_ShowFirst, bUpDownBars );
442                 if( bUpDownBars )
443                 {
444                     UpDownBarsConverter aUpDownConv( *this, *mrModel.mxUpDownBars );
445                     aUpDownConv.convertFromModel( xChartType );
446                 }
447 
448                 // insert the series into the chart type object
449                 insertDataSeries( xChartType, xDataSeries, nAxesSetIdx );
450             }
451         }
452         else
453         {
454             for( sal_Int32 nCTIdx=0; nCTIdx<xOldChartTypes.getLength(); ++nCTIdx )
455             {
456                 if ( xChartType->getChartType() == xOldChartTypes[nCTIdx]->getChartType() )
457                 {
458                     nOldChartTypeIdx = nCTIdx;
459                 }
460             }
461 
462             for (auto const& elem : aSeries)
463             {
464                 SeriesConverter& rSeriesConv = *elem;
465                 Reference< XDataSeries > xDataSeries = rSeriesConv.createDataSeries( *this, bVaryColorsByPoint );
466                 insertDataSeries( nOldChartTypeIdx == -1 ? xChartType : xOldChartTypes[nOldChartTypeIdx], xDataSeries, nAxesSetIdx );
467 
468                 /*  Excel does not use the value of the c:smooth element of the
469                     chart type to set a default line smoothing for the data
470                     series. Line smoothing is always controlled by the c:smooth
471                     element of the respective data series. If the element in the
472                     data series is missing, line smoothing is off, regardless of
473                     the c:smooth element of the chart type. */
474 #if !OOX_CHART_SMOOTHED_PER_SERIES
475                 if( rSeriesConv.getModel().mbSmooth )
476                     convertLineSmooth( aTypeProp, true );
477 #endif
478             }
479         }
480 
481         // add chart type object to coordinate system
482         Reference< XChartTypeContainer > xChartTypeCont( rxCoordSystem, UNO_QUERY_THROW );
483         if (nOldChartTypeIdx == -1)
484         {
485             xChartTypeCont->addChartType(xChartType);
486         }
487 
488         // set existence of bar connector lines at diagram (only in stacked 2D bar charts)
489         if( mrModel.mxSerLines.is() && !mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR) && (isStacked() || isPercent()) )
490             aDiaProp.setProperty( PROP_ConnectBars, true );
491     }
492     catch( Exception& )
493     {
494         OSL_FAIL( "TypeGroupConverter::convertFromModel - cannot add chart type" );
495     }
496 }
497 
convertMarker(PropertySet & rPropSet,sal_Int32 nOoxSymbol,sal_Int32 nOoxSize,const ModelRef<Shape> & xShapeProps) const498 void TypeGroupConverter::convertMarker( PropertySet& rPropSet, sal_Int32 nOoxSymbol, sal_Int32 nOoxSize,
499        const ModelRef< Shape >& xShapeProps ) const
500 {
501     if( isSeriesFrameFormat() )
502         return;
503 
504     namespace cssc = css::chart2;
505 
506     // symbol style
507     cssc::Symbol aSymbol;
508     aSymbol.Style = cssc::SymbolStyle_STANDARD;
509     switch( nOoxSymbol ) // compare with XclChPropSetHelper::WriteMarkerProperties in xlchart.cxx
510     {
511         case XML_auto:      aSymbol.Style = cssc::SymbolStyle_AUTO; break;
512         case XML_none:      aSymbol.Style = cssc::SymbolStyle_NONE; break;
513         case XML_square:    aSymbol.StandardSymbol = 0;             break;  // square
514         case XML_diamond:   aSymbol.StandardSymbol = 1;             break;  // diamond
515         case XML_triangle:  aSymbol.StandardSymbol = 3;             break;  // arrow up
516         case XML_x:         aSymbol.StandardSymbol = 10;            break;  // X, legacy bow tie
517         case XML_star:      aSymbol.StandardSymbol = 12;            break;  // asterisk, legacy sand glass
518         case XML_dot:       aSymbol.StandardSymbol = 4;             break;  // arrow right
519         case XML_dash:      aSymbol.StandardSymbol = 13;            break;  // horizontal bar, legacy arrow down
520         case XML_circle:    aSymbol.StandardSymbol = 8;             break;  // circle, legacy arrow right
521         case XML_plus:      aSymbol.StandardSymbol = 11;            break;  // plus, legacy arrow left
522     }
523 
524     // symbol size (points in OOXML, 1/100 mm in Chart2)
525     sal_Int32 nSize = convertPointToMm100(nOoxSize);
526     aSymbol.Size.Width = aSymbol.Size.Height = nSize;
527 
528     if(xShapeProps.is())
529     {
530         Color aFillColor = xShapeProps->getFillProperties().maFillColor;
531         aSymbol.FillColor = sal_Int32(aFillColor.getColor(getFilter().getGraphicHelper()));
532         // tdf#124817: if there is no fill color, use line color of the symbol
533         if( aSymbol.FillColor < 0 )
534         {
535             Color aLineColor = xShapeProps->getLineProperties().maLineFill.maFillColor;
536             aSymbol.BorderColor = sal_Int32(aLineColor.getColor(getFilter().getGraphicHelper()));
537             rPropSet.setProperty(PROP_Color, aSymbol.BorderColor);
538         }
539         else
540             rPropSet.setProperty(PROP_Color, aSymbol.FillColor);
541     }
542 
543     // set the property
544     rPropSet.setProperty( PROP_Symbol, aSymbol );
545 }
546 
convertLineSmooth(PropertySet & rPropSet,bool bOoxSmooth) const547 void TypeGroupConverter::convertLineSmooth( PropertySet& rPropSet, bool bOoxSmooth ) const
548 {
549     if( !isSeriesFrameFormat() && (maTypeInfo.meTypeCategory != TYPECATEGORY_RADAR) )
550     {
551         namespace cssc = css::chart2;
552         cssc::CurveStyle eCurveStyle = bOoxSmooth ? cssc::CurveStyle_CUBIC_SPLINES : cssc::CurveStyle_LINES;
553         rPropSet.setProperty( PROP_CurveStyle, eCurveStyle );
554     }
555 }
556 
convertBarGeometry(PropertySet & rPropSet,sal_Int32 nOoxShape) const557 void TypeGroupConverter::convertBarGeometry( PropertySet& rPropSet, sal_Int32 nOoxShape ) const
558 {
559     if( !(mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR)) )
560         return;
561 
562     namespace cssc = css::chart2;
563 
564     sal_Int32 nGeom3d = cssc::DataPointGeometry3D::CUBOID;
565     switch( nOoxShape )
566     {
567         case XML_box:           nGeom3d = cssc::DataPointGeometry3D::CUBOID;    break;
568         case XML_cone:          nGeom3d = cssc::DataPointGeometry3D::CONE;      break;
569         case XML_coneToMax:     nGeom3d = cssc::DataPointGeometry3D::CONE;      break;
570         case XML_cylinder:      nGeom3d = cssc::DataPointGeometry3D::CYLINDER;  break;
571         case XML_pyramid:       nGeom3d = cssc::DataPointGeometry3D::PYRAMID;   break;
572         case XML_pyramidToMax:  nGeom3d = cssc::DataPointGeometry3D::PYRAMID;   break;
573         default:                OSL_FAIL( "TypeGroupConverter::convertBarGeometry - unknown 3D bar shape type" );
574     }
575     rPropSet.setProperty( PROP_Geometry3D, nGeom3d );
576 }
577 
convertPieRotation(PropertySet & rPropSet,sal_Int32 nOoxAngle) const578 void TypeGroupConverter::convertPieRotation( PropertySet& rPropSet, sal_Int32 nOoxAngle ) const
579 {
580     if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
581     {
582         // map OOXML [0,360] clockwise (0deg top) to Chart2 counterclockwise (0deg left)
583         sal_Int32 nAngle = (450 - nOoxAngle) % 360;
584         rPropSet.setProperty( PROP_StartingAngle, nAngle );
585     }
586 }
587 
convertPieExplosion(PropertySet & rPropSet,sal_Int32 nOoxExplosion) const588 void TypeGroupConverter::convertPieExplosion( PropertySet& rPropSet, sal_Int32 nOoxExplosion ) const
589 {
590     if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
591     {
592         // pie explosion restricted to 100% in Chart2, set as double in range [0,1]
593         double fOffset = getLimitedValue< double >( nOoxExplosion / 100.0, 0.0, 1.0 );
594         rPropSet.setProperty( PROP_Offset, fOffset );
595     }
596 }
597 
convertOfPieType(sal_Int32 nOoxOfPieType) const598 PieChartSubType TypeGroupConverter::convertOfPieType(sal_Int32 nOoxOfPieType ) const
599 {
600     if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE ) {
601         switch (nOoxOfPieType) {
602         case XML_pie:
603             return PieChartSubType_PIE;
604             break;
605         case XML_bar:
606             return PieChartSubType_BAR;
607             break;
608         default:
609             OSL_FAIL( "TypeGroupConverter::convertOfPieType - unknown of-pie type" );
610             return PieChartSubType_NONE;
611         }
612     } else {
613         return PieChartSubType_NONE;
614     }
615 }
616 
moveDataToSeries(DataSourceCxModel::DataMap & raDataMap)617 void TypeGroupConverter::moveDataToSeries(DataSourceCxModel::DataMap& raDataMap)
618 {
619     // For chartex, move data from rDataMap to the appropriate series. In
620     // chartex the data is given outside the series, in a child element of
621     // <cx:chartSpace>. Pre-2016 charts have the data inside the series, and
622     // SeriesModel and subsequent handling reflects this. So here we get the
623     // data to the right place for processing.
624     if (!raDataMap.empty()) {
625         // should only happen for chartex
626         for (auto const& elem : mrModel.maSeries) {
627             // This ID must be present in the map
628             assert(raDataMap.find(elem->mnDataId) != raDataMap.end());
629             elem->maSources = *(raDataMap[elem->mnDataId]);
630         }
631     }
632 }
633 
634 // private --------------------------------------------------------------------
635 
insertDataSeries(const Reference<XChartType> & rxChartType,const Reference<XDataSeries> & rxSeries,sal_Int32 nAxesSetIdx)636 void TypeGroupConverter::insertDataSeries( const Reference< XChartType >& rxChartType, const Reference< XDataSeries >& rxSeries, sal_Int32 nAxesSetIdx )
637 {
638     if( !rxSeries.is() )
639         return;
640 
641     PropertySet aSeriesProp( rxSeries );
642 
643     // series stacking mode
644     namespace cssc = css::chart2;
645     cssc::StackingDirection eStacking = cssc::StackingDirection_NO_STACKING;
646     // stacked overrides deep-3d
647     if( isStacked() || isPercent() )
648         eStacking = cssc::StackingDirection_Y_STACKING;
649     else if( isDeep3dChart() )
650         eStacking = cssc::StackingDirection_Z_STACKING;
651     aSeriesProp.setProperty( PROP_StackingDirection, eStacking );
652 
653     // additional series properties
654     aSeriesProp.setProperty( PROP_AttachedAxisIndex, nAxesSetIdx );
655 
656     // insert series into container
657     try
658     {
659         Reference< XDataSeriesContainer > xSeriesCont( rxChartType, UNO_QUERY_THROW );
660         xSeriesCont->addDataSeries( rxSeries );
661     }
662     catch( Exception& )
663     {
664         OSL_FAIL( "TypeGroupConverter::insertDataSeries - cannot add data series" );
665     }
666 }
667 
668 } // namespace oox
669 
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
671