tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / oox / source / drawingml / chart / plotareaconverter.cxx
blob1fa8b6f9b6737b3e1c0564df92545d3e68254b3d
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/plotareaconverter.hxx>
22 #include <com/sun/star/chart/XChartDocument.hpp>
23 #include <com/sun/star/chart/XDiagramPositioning.hpp>
24 #include <com/sun/star/chart2/XChartDocument.hpp>
25 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
26 #include <com/sun/star/chart2/XDiagram.hpp>
27 #include <com/sun/star/drawing/Direction3D.hpp>
28 #include <com/sun/star/drawing/ProjectionMode.hpp>
29 #include <com/sun/star/drawing/ShadeMode.hpp>
30 #include <osl/diagnose.h>
31 #include <drawingml/chart/datatableconverter.hxx>
32 #include <drawingml/chart/axisconverter.hxx>
33 #include <drawingml/chart/plotareamodel.hxx>
34 #include <drawingml/chart/typegroupconverter.hxx>
35 #include <oox/core/xmlfilterbase.hxx>
36 #include <oox/token/namespaces.hxx>
37 #include <oox/token/properties.hxx>
38 #include <oox/token/tokens.hxx>
39 #include <tools/helpers.hxx>
41 namespace oox::drawingml::chart {
43 using namespace ::com::sun::star;
44 using namespace ::com::sun::star::chart2;
45 using namespace ::com::sun::star::uno;
47 namespace {
49 /** Axes set model. This is a helper for the plot area converter collecting all
50 type groups and axes of the primary or secondary axes set. */
51 struct AxesSetModel
53 typedef ModelVector< TypeGroupModel > TypeGroupVector;
54 typedef ModelMap< sal_Int32, AxisModel > AxisMap;
56 TypeGroupVector maTypeGroups; /// All type groups containing data series.
57 AxisMap maAxes; /// All axes mapped by API axis type.
59 explicit AxesSetModel() {}
62 /** Axes set converter. This is a helper class for the plot area converter. */
63 class AxesSetConverter : public ConverterBase< AxesSetModel >
65 public:
66 explicit AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel );
68 /** Converts the axes set model to a chart2 diagram. Returns an automatic
69 chart title from a single series title, if possible. */
70 void convertFromModel(
71 const Reference< XDiagram >& rxDiagram,
72 View3DModel& rView3DModel,
73 sal_Int32 nAxesSetIdx,
74 bool bSupportsVaryColorsByPoint,
75 bool bUseFixedInnerSize );
77 /** Returns the automatic chart title if the axes set contains only one series. */
78 const OUString& getAutomaticTitle() const { return maAutoTitle; }
79 /** Returns true, if the chart contains only one series and have title textbox (even empty). */
80 bool isSingleSeriesTitle() const { return mbSingleSeriesTitle; }
81 /** Returns true, if the chart is three-dimensional. */
82 bool is3dChart() const { return mb3dChart; }
83 /** Returns true, if chart type supports wall and floor format in 3D mode. */
84 bool isWall3dChart() const { return mbWall3dChart; }
85 /** Returns true, if chart is a pie chart or doughnut chart. */
86 bool isPieChart() const { return mbPieChart; }
88 private:
89 OUString maAutoTitle;
90 bool mb3dChart;
91 bool mbWall3dChart;
92 bool mbPieChart;
93 bool mbSingleSeriesTitle;
96 AxesSetConverter::AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel ) :
97 ConverterBase< AxesSetModel >( rParent, rModel ),
98 mb3dChart( false ),
99 mbWall3dChart( false ),
100 mbPieChart( false ),
101 mbSingleSeriesTitle( false )
105 ModelRef< AxisModel > lclGetOrCreateAxis( const AxesSetModel::AxisMap& rFromAxes, sal_Int32 nAxisIdx, sal_Int32 nDefTypeId, bool bMSO2007Doc )
107 ModelRef< AxisModel > xAxis = rFromAxes.get( nAxisIdx );
108 if( !xAxis )
109 xAxis.create( nDefTypeId, bMSO2007Doc ).mbDeleted = true; // missing axis is invisible
110 return xAxis;
113 void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
114 View3DModel& rView3DModel, sal_Int32 nAxesSetIdx,
115 bool bSupportsVaryColorsByPoint, bool bUseFixedInnerSize)
117 // create type group converter objects for all type groups
118 typedef RefVector< TypeGroupConverter > TypeGroupConvVector;
119 TypeGroupConvVector aTypeGroups;
120 for (auto const& typeGroup : mrModel.maTypeGroups)
121 aTypeGroups.push_back( std::make_shared<TypeGroupConverter>( *this, *typeGroup ) );
123 OSL_ENSURE( !aTypeGroups.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" );
124 if( aTypeGroups.empty() )
125 return;
129 // first type group needed for coordinate system and axis conversion
130 TypeGroupConverter& rFirstTypeGroup = *aTypeGroups.front();
132 // get automatic chart title, if there is only one type group
133 if( aTypeGroups.size() == 1 )
135 maAutoTitle = rFirstTypeGroup.getSingleSeriesTitle();
136 mbSingleSeriesTitle = rFirstTypeGroup.isSingleSeriesTitle();
139 /* Create a coordinate system. For now, all type groups from all axes sets
140 have to be inserted into one coordinate system. Later, chart2 should
141 support using one coordinate system for each axes set. */
142 Reference< XCoordinateSystem > xCoordSystem;
143 Reference< XCoordinateSystemContainer > xCoordSystemCont( rxDiagram, UNO_QUERY_THROW );
144 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
145 if( aCoordSystems.hasElements() )
147 OSL_ENSURE( aCoordSystems.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" );
148 xCoordSystem = aCoordSystems[ 0 ];
149 OSL_ENSURE( xCoordSystem.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" );
151 else
153 xCoordSystem = rFirstTypeGroup.createCoordinateSystem();
154 if( xCoordSystem.is() )
155 xCoordSystemCont->addCoordinateSystem( xCoordSystem );
158 // 3D view settings
159 mb3dChart = rFirstTypeGroup.is3dChart();
160 mbWall3dChart = rFirstTypeGroup.isWall3dChart();
161 mbPieChart = rFirstTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE;
162 if( mb3dChart )
164 View3DConverter aView3DConv( *this, rView3DModel );
165 aView3DConv.convertFromModel( rxDiagram, rFirstTypeGroup );
168 /* Convert all chart type groups. Each type group will add its series
169 to the data provider attached to the chart document. */
170 if( xCoordSystem.is() )
172 bool bMSO2007Doc = getFilter().isMSO2007Document();
173 // convert all axes (create missing axis models)
174 ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : C_TOKEN( valAx ), bMSO2007Doc );
175 ModelRef< AxisModel > xYAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Y_AXIS, C_TOKEN( valAx ), bMSO2007Doc );
177 AxisConverter aXAxisConv( *this, *xXAxis );
178 aXAxisConv.convertFromModel(xCoordSystem, aTypeGroups, xYAxis.get(), nAxesSetIdx,
179 API_X_AXIS, bUseFixedInnerSize);
180 AxisConverter aYAxisConv( *this, *xYAxis );
181 aYAxisConv.convertFromModel(xCoordSystem, aTypeGroups, xXAxis.get(), nAxesSetIdx,
182 API_Y_AXIS, bUseFixedInnerSize);
184 if( rFirstTypeGroup.isDeep3dChart() )
186 ModelRef< AxisModel > xZAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Z_AXIS, C_TOKEN( serAx ), bMSO2007Doc );
187 AxisConverter aZAxisConv( *this, *xZAxis );
188 aZAxisConv.convertFromModel(xCoordSystem, aTypeGroups, nullptr, nAxesSetIdx,
189 API_Z_AXIS, bUseFixedInnerSize);
192 // convert all chart type groups, this converts all series data and formatting
193 for (auto const& typeGroup : aTypeGroups)
194 typeGroup->convertFromModel( rxDiagram, xCoordSystem, nAxesSetIdx, bSupportsVaryColorsByPoint );
197 catch( Exception& )
202 } // namespace
204 View3DConverter::View3DConverter( const ConverterRoot& rParent, View3DModel& rModel ) :
205 ConverterBase< View3DModel >( rParent, rModel )
209 View3DConverter::~View3DConverter()
213 void View3DConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, TypeGroupConverter const & rTypeGroup )
215 namespace cssd = css::drawing;
216 PropertySet aPropSet( rxDiagram );
218 sal_Int32 nRotationY = 0;
219 sal_Int32 nRotationX = 0;
220 bool bRightAngled = false;
221 sal_Int32 nAmbientColor = 0;
222 sal_Int32 nLightColor = 0;
224 if( rTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE )
226 // Y rotation used as 'first pie slice angle' in 3D pie charts
227 rTypeGroup.convertPieRotation( aPropSet, mrModel.monRotationY.value_or( 0 ) );
228 // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0])
229 nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.value_or( 15 ), 0, 90 ) - 90;
230 // no right-angled axes in pie charts
231 bRightAngled = false;
232 // ambient color (Gray 30%)
233 nAmbientColor = 0xB3B3B3;
234 // light color (Gray 70%)
235 nLightColor = 0x4C4C4C;
237 else // 3D bar/area/line charts
239 // Y rotation (OOXML [0..359], Chart2 [-179,180])
240 nRotationY = mrModel.monRotationY.value_or( 20 );
241 // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180])
242 nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.value_or( 15 ), -90, 90 );
243 // right-angled axes
244 bRightAngled = mrModel.mbRightAngled;
245 // ambient color (Gray 20%)
246 nAmbientColor = 0xCCCCCC;
247 // light color (Gray 60%)
248 nLightColor = 0x666666;
251 // Y rotation (map OOXML [0..359] to Chart2 [-179,180])
252 nRotationY = NormAngle180(nRotationY);
253 /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is
254 buggy here, the XML plugin of MSO 2003 writes the correct perspective in
255 the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */
256 sal_Int32 nPerspective = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.mnPerspective / 2, 0, 100 );
257 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
258 bool bParallel = bRightAngled || (nPerspective == 0);
259 cssd::ProjectionMode eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
261 // set rotation properties
262 aPropSet.setProperty( PROP_RightAngledAxes, bRightAngled );
263 aPropSet.setProperty( PROP_RotationVertical, nRotationY );
264 aPropSet.setProperty( PROP_RotationHorizontal, nRotationX );
265 aPropSet.setProperty( PROP_Perspective, nPerspective );
266 aPropSet.setProperty( PROP_D3DScenePerspective, eProjMode );
268 // set light settings
269 aPropSet.setProperty( PROP_D3DSceneShadeMode, cssd::ShadeMode_FLAT );
270 aPropSet.setProperty( PROP_D3DSceneAmbientColor, nAmbientColor );
271 aPropSet.setProperty( PROP_D3DSceneLightOn1, false );
272 aPropSet.setProperty( PROP_D3DSceneLightOn2, true );
273 aPropSet.setProperty( PROP_D3DSceneLightColor2, nLightColor );
274 aPropSet.setProperty( PROP_D3DSceneLightDirection2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
277 WallFloorConverter::WallFloorConverter( const ConverterRoot& rParent, WallFloorModel& rModel ) :
278 ConverterBase< WallFloorModel >( rParent, rModel )
282 WallFloorConverter::~WallFloorConverter()
286 void WallFloorConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, ObjectType eObjType )
288 bool bMSO2007Doc = getFilter().isMSO2007Document();
289 if( rxDiagram.is() )
291 PropertySet aPropSet;
292 switch( eObjType )
294 case OBJECTTYPE_FLOOR: aPropSet.set( rxDiagram->getFloor() ); break;
295 case OBJECTTYPE_WALL: aPropSet.set( rxDiagram->getWall() ); break;
296 default: OSL_FAIL( "WallFloorConverter::convertFromModel - invalid object type" );
298 if( aPropSet.is() )
299 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), eObjType );
303 PlotAreaConverter::PlotAreaConverter( const ConverterRoot& rParent, PlotAreaModel& rModel ) :
304 ConverterBase< PlotAreaModel >( rParent, rModel ),
305 mb3dChart( false ),
306 mbWall3dChart( false ),
307 mbPieChart( false ),
308 mbSingleSeriesTitle( false )
312 PlotAreaConverter::~PlotAreaConverter()
316 void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel )
318 /* Create the diagram object and attach it to the chart document. One
319 diagram is used to carry all coordinate systems and data series. */
320 Reference< XDiagram > xDiagram;
323 xDiagram.set( createInstance( u"com.sun.star.chart2.Diagram"_ustr ), UNO_QUERY_THROW );
324 getChartDocument()->setFirstDiagram( xDiagram );
326 catch( Exception& )
330 // store all axis models in a map, keyed by axis identifier
331 typedef ModelMap< sal_Int32, AxisModel > AxisMap;
332 AxisMap aAxisMap;
333 std::vector<sal_Int32>rValAxisIds;
334 std::vector<sal_Int32>rRealValAxisIds;
336 for (auto const& atypeGroup : mrModel.maTypeGroups)
338 if (atypeGroup->maAxisIds.size() > 1)
340 // let's collect which axId belongs to the Y Axis according to maTypeGroups
341 rRealValAxisIds.push_back(atypeGroup->maAxisIds[1]);
345 for (auto const& axis : mrModel.maAxes)
347 OSL_ENSURE( axis->mnAxisId >= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" );
348 OSL_ENSURE( !aAxisMap.has( axis->mnAxisId ), "PlotAreaConverter::convertFromModel - axis identifiers not unique" );
349 if( axis->mnAxisId != -1 )
350 aAxisMap[ axis->mnAxisId ] = axis;
352 if ( axis->mnAxisId != -1 && axis->mnTypeId == C_TOKEN(valAx) )
354 for (size_t i = 0; i < rRealValAxisIds.size(); i++)
356 if (axis->mnAxisId == rRealValAxisIds[i])
358 // let's collect which axId belongs to the Y Axis according to maAxes
359 rValAxisIds.push_back(axis->mnAxisId);
365 // group the type group models into different axes sets
366 typedef ModelVector< AxesSetModel > AxesSetVector;
367 AxesSetVector aAxesSets;
368 sal_Int32 nMaxSeriesIdx = -1;
369 for (auto const& typeGroup : mrModel.maTypeGroups)
371 if( !typeGroup->maSeries.empty() )
373 // try to find a compatible axes set for the type group
374 AxesSetModel* pAxesSet = nullptr;
375 for (auto const& axesSet : aAxesSets)
377 if( axesSet->maTypeGroups.front()->maAxisIds == typeGroup->maAxisIds )
379 pAxesSet = axesSet.get();
380 if (pAxesSet)
381 break;
385 // not possible to insert into an existing axes set -> start a new axes set
386 if( !pAxesSet )
388 pAxesSet = &aAxesSets.create();
389 // find axis models used by the type group
390 const std::vector<sal_Int32>& rAxisIds = typeGroup->maAxisIds;
391 if( !rAxisIds.empty() )
392 pAxesSet->maAxes[ API_X_AXIS ] = aAxisMap.get( rAxisIds[ 0 ] );
393 if( rAxisIds.size() >= 2 )
394 pAxesSet->maAxes[ API_Y_AXIS ] = aAxisMap.get( rAxisIds[ 1 ] );
395 if( rAxisIds.size() >= 3 )
396 pAxesSet->maAxes[ API_Z_AXIS ] = aAxisMap.get( rAxisIds[ 2 ] );
399 // insert the type group model
400 pAxesSet->maTypeGroups.push_back( typeGroup );
402 // collect the maximum series index for automatic series formatting
403 for (auto const& elemSeries : typeGroup->maSeries)
404 nMaxSeriesIdx = ::std::max( nMaxSeriesIdx, elemSeries->mnIndex );
407 getFormatter().setMaxSeriesIndex( nMaxSeriesIdx );
409 // varying point colors only for single series in single chart type
410 bool bSupportsVaryColorsByPoint = mrModel.maTypeGroups.size() == 1;
412 bool bIsCombinedChart = mrModel.maTypeGroups.size() == 2 &&
413 mrModel.maTypeGroups[0]->mnTypeId != mrModel.maTypeGroups[1]->mnTypeId;
415 // convert all axes sets, and check which axis is attached to the first maTypeGroups
416 sal_Int32 nStartAxesSetIdx = bIsCombinedChart ? ((rValAxisIds.size() > 1 && aAxesSets.size() > 0 && aAxesSets[0]->maAxes.count( API_Y_AXIS )
417 && aAxesSets[0]->maAxes[ API_Y_AXIS ]->mnAxisId != rValAxisIds[0] ) ? 1 : 0)
418 : 0;
419 sal_Int32 nAxesSetIdx = nStartAxesSetIdx;
421 bool bUseFixedInnerSize = false;
422 if (mrModel.mxLayout && !mrModel.mxLayout->mbAutoLayout)
423 bUseFixedInnerSize = mrModel.mxLayout->mnTarget == XML_inner;
425 for (auto const& axesSet : aAxesSets)
427 AxesSetConverter aAxesSetConv(*this, *axesSet);
428 aAxesSetConv.convertFromModel(xDiagram, rView3DModel, nAxesSetIdx,
429 bSupportsVaryColorsByPoint, bUseFixedInnerSize);
430 if(nAxesSetIdx == nStartAxesSetIdx)
432 maAutoTitle = aAxesSetConv.getAutomaticTitle();
433 mbSingleSeriesTitle = aAxesSetConv.isSingleSeriesTitle();
434 mb3dChart = aAxesSetConv.is3dChart();
435 mbWall3dChart = aAxesSetConv.isWall3dChart();
436 mbPieChart = aAxesSetConv.isPieChart();
438 else
440 maAutoTitle.clear();
442 nAxesSetIdx = 1 - nAxesSetIdx;
445 if (mrModel.mxDataTable)
447 DataTableConverter dataTableConverter(*this, *mrModel.mxDataTable);
448 dataTableConverter.convertFromModel(xDiagram);
451 // plot area formatting
452 if( xDiagram.is() && !mb3dChart )
454 PropertySet aPropSet( xDiagram->getWall() );
455 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, OBJECTTYPE_PLOTAREA2D );
459 void PlotAreaConverter::convertPositionFromModel()
461 LayoutModel& rLayout = mrModel.mxLayout.getOrCreate();
462 LayoutConverter aLayoutConv( *this, rLayout );
463 awt::Rectangle aDiagramRect;
464 if( !aLayoutConv.calcAbsRectangle( aDiagramRect ) )
465 return;
469 namespace cssc = css::chart;
470 Reference< cssc::XChartDocument > xChart1Doc( getChartDocument(), UNO_QUERY_THROW );
471 Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
472 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
473 sal_Int32 nTarget = (mbPieChart && (rLayout.mnTarget == XML_outer)) ? XML_inner : rLayout.mnTarget;
474 switch( nTarget )
476 case XML_inner:
477 xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
478 break;
479 case XML_outer:
480 xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
481 break;
482 default:
483 OSL_FAIL( "PlotAreaConverter::convertPositionFromModel - unknown positioning target" );
486 catch( Exception& )
491 } // namespace oox
493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */