Line data Source code
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 "oox/drawingml/chart/plotareaconverter.hxx"
21 :
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 "oox/drawingml/chart/axisconverter.hxx"
31 : #include "oox/drawingml/chart/plotareamodel.hxx"
32 : #include "oox/drawingml/chart/typegroupconverter.hxx"
33 :
34 : namespace oox {
35 : namespace drawingml {
36 : namespace chart {
37 :
38 : // ============================================================================
39 :
40 : using namespace ::com::sun::star;
41 : using namespace ::com::sun::star::chart2;
42 : using namespace ::com::sun::star::uno;
43 :
44 : // ============================================================================
45 :
46 : namespace {
47 :
48 : /** Axes set model. This is a helper for the plot area converter collecting all
49 : type groups and axes of the primary or secondary axes set. */
50 : struct AxesSetModel
51 : {
52 : typedef ModelVector< TypeGroupModel > TypeGroupVector;
53 : typedef ModelMap< sal_Int32, AxisModel > AxisMap;
54 :
55 : TypeGroupVector maTypeGroups; /// All type groups containing data series.
56 : AxisMap maAxes; /// All axes mapped by API axis type.
57 :
58 0 : inline explicit AxesSetModel() {}
59 0 : inline ~AxesSetModel() {}
60 : };
61 :
62 : // ============================================================================
63 :
64 : /** Axes set converter. This is a helper class for the plot area converter. */
65 : class AxesSetConverter : public ConverterBase< AxesSetModel >
66 : {
67 : public:
68 : explicit AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel );
69 : virtual ~AxesSetConverter();
70 :
71 : /** Converts the axes set model to a chart2 diagram. Returns an automatic
72 : chart title from a single series title, if possible. */
73 : void convertFromModel(
74 : const Reference< XDiagram >& rxDiagram,
75 : View3DModel& rView3DModel,
76 : sal_Int32 nAxesSetIdx,
77 : bool bSupportsVaryColorsByPoint );
78 :
79 : /** Returns the automatic chart title if the axes set contains only one series. */
80 0 : inline const OUString& getAutomaticTitle() const { return maAutoTitle; }
81 : /** Returns true, if the chart is three-dimensional. */
82 0 : inline bool is3dChart() const { return mb3dChart; }
83 : /** Returns true, if chart type supports wall and floor format in 3D mode. */
84 0 : inline bool isWall3dChart() const { return mbWall3dChart; }
85 : /** Returns true, if chart is a pie chart or doughnut chart. */
86 0 : inline bool isPieChart() const { return mbPieChart; }
87 :
88 : private:
89 : OUString maAutoTitle;
90 : bool mb3dChart;
91 : bool mbWall3dChart;
92 : bool mbPieChart;
93 : };
94 :
95 : // ----------------------------------------------------------------------------
96 :
97 0 : AxesSetConverter::AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel ) :
98 : ConverterBase< AxesSetModel >( rParent, rModel ),
99 : mb3dChart( false ),
100 : mbWall3dChart( false ),
101 0 : mbPieChart( false )
102 : {
103 0 : }
104 :
105 0 : AxesSetConverter::~AxesSetConverter()
106 : {
107 0 : }
108 :
109 0 : ModelRef< AxisModel > lclGetOrCreateAxis( const AxesSetModel::AxisMap& rFromAxes, sal_Int32 nAxisIdx, sal_Int32 nDefTypeId )
110 : {
111 0 : ModelRef< AxisModel > xAxis = rFromAxes.get( nAxisIdx );
112 0 : if( !xAxis )
113 0 : xAxis.create( nDefTypeId ).mbDeleted = true; // missing axis is invisible
114 0 : return xAxis;
115 : }
116 :
117 0 : void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
118 : View3DModel& rView3DModel, sal_Int32 nAxesSetIdx, bool bSupportsVaryColorsByPoint )
119 : {
120 : // create type group converter objects for all type groups
121 : typedef RefVector< TypeGroupConverter > TypeGroupConvVector;
122 0 : TypeGroupConvVector aTypeGroups;
123 0 : for( AxesSetModel::TypeGroupVector::iterator aIt = mrModel.maTypeGroups.begin(), aEnd = mrModel.maTypeGroups.end(); aIt != aEnd; ++aIt )
124 0 : aTypeGroups.push_back( TypeGroupConvVector::value_type( new TypeGroupConverter( *this, **aIt ) ) );
125 :
126 : OSL_ENSURE( !aTypeGroups.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" );
127 0 : if( !aTypeGroups.empty() ) try
128 : {
129 : // first type group needed for coordinate system and axis conversion
130 0 : TypeGroupConverter& rFirstTypeGroup = *aTypeGroups.front();
131 :
132 : // get automatic chart title, if there is only one type group
133 0 : if( aTypeGroups.size() == 1 )
134 0 : maAutoTitle = rFirstTypeGroup.getSingleSeriesTitle();
135 :
136 : /* Create a coordinate system. For now, all type groups from all axes sets
137 : have to be inserted into one coordinate system. Later, chart2 should
138 : support using one coordinate system for each axes set. */
139 0 : Reference< XCoordinateSystem > xCoordSystem;
140 0 : Reference< XCoordinateSystemContainer > xCoordSystemCont( rxDiagram, UNO_QUERY_THROW );
141 0 : Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
142 0 : if( aCoordSystems.hasElements() )
143 : {
144 : OSL_ENSURE( aCoordSystems.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" );
145 0 : xCoordSystem = aCoordSystems[ 0 ];
146 : OSL_ENSURE( xCoordSystem.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" );
147 : }
148 : else
149 : {
150 0 : xCoordSystem = rFirstTypeGroup.createCoordinateSystem();
151 0 : if( xCoordSystem.is() )
152 0 : xCoordSystemCont->addCoordinateSystem( xCoordSystem );
153 : }
154 :
155 : // 3D view settings
156 0 : mb3dChart = rFirstTypeGroup.is3dChart();
157 0 : mbWall3dChart = rFirstTypeGroup.isWall3dChart();
158 0 : mbPieChart = rFirstTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE;
159 0 : if( mb3dChart )
160 : {
161 0 : View3DConverter aView3DConv( *this, rView3DModel );
162 0 : aView3DConv.convertFromModel( rxDiagram, rFirstTypeGroup );
163 : }
164 :
165 : /* Convert all chart type groups. Each type group will add its series
166 : to the data provider attached to the chart document. */
167 0 : if( xCoordSystem.is() )
168 : {
169 : // convert all axes (create missing axis models)
170 0 : ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : C_TOKEN( valAx ) );
171 0 : ModelRef< AxisModel > xYAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Y_AXIS, C_TOKEN( valAx ) );
172 :
173 0 : AxisConverter aXAxisConv( *this, *xXAxis );
174 0 : aXAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, xYAxis.get(), nAxesSetIdx, API_X_AXIS );
175 0 : AxisConverter aYAxisConv( *this, *xYAxis );
176 0 : aYAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, xXAxis.get(), nAxesSetIdx, API_Y_AXIS );
177 :
178 0 : if( rFirstTypeGroup.isDeep3dChart() )
179 : {
180 0 : ModelRef< AxisModel > xZAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Z_AXIS, C_TOKEN( serAx ) );
181 0 : AxisConverter aZAxisConv( *this, *xZAxis );
182 0 : aZAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, 0, nAxesSetIdx, API_Z_AXIS );
183 : }
184 :
185 : // convert all chart type groups, this converts all series data and formatting
186 0 : for( TypeGroupConvVector::iterator aTIt = aTypeGroups.begin(), aTEnd = aTypeGroups.end(); aTIt != aTEnd; ++aTIt )
187 0 : (*aTIt)->convertFromModel( rxDiagram, xCoordSystem, nAxesSetIdx, bSupportsVaryColorsByPoint );
188 0 : }
189 : }
190 0 : catch( Exception& )
191 : {
192 0 : }
193 0 : }
194 :
195 : } // namespace
196 :
197 : // ============================================================================
198 :
199 0 : View3DConverter::View3DConverter( const ConverterRoot& rParent, View3DModel& rModel ) :
200 0 : ConverterBase< View3DModel >( rParent, rModel )
201 : {
202 0 : }
203 :
204 0 : View3DConverter::~View3DConverter()
205 : {
206 0 : }
207 :
208 0 : void View3DConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, TypeGroupConverter& rTypeGroup )
209 : {
210 : namespace cssd = ::com::sun::star::drawing;
211 0 : PropertySet aPropSet( rxDiagram );
212 :
213 0 : sal_Int32 nRotationY = 0;
214 0 : sal_Int32 nRotationX = 0;
215 0 : bool bRightAngled = false;
216 0 : sal_Int32 nAmbientColor = 0;
217 0 : sal_Int32 nLightColor = 0;
218 :
219 0 : if( rTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE )
220 : {
221 : // Y rotation used as 'first pie slice angle' in 3D pie charts
222 0 : rTypeGroup.convertPieRotation( aPropSet, mrModel.monRotationY.get( 0 ) );
223 : // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0])
224 0 : nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), 0, 90 ) - 90;
225 : // no right-angled axes in pie charts
226 0 : bRightAngled = false;
227 : // ambient color (Gray 30%)
228 0 : nAmbientColor = 0xB3B3B3;
229 : // light color (Gray 70%)
230 0 : nLightColor = 0x4C4C4C;
231 : }
232 : else // 3D bar/area/line charts
233 : {
234 : // Y rotation (OOXML [0..359], Chart2 [-179,180])
235 0 : nRotationY = mrModel.monRotationY.get( 20 );
236 : // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180])
237 0 : nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), -90, 90 );
238 : // right-angled axes
239 0 : bRightAngled = mrModel.mbRightAngled;
240 : // ambient color (Gray 20%)
241 0 : nAmbientColor = 0xCCCCCC;
242 : // light color (Gray 60%)
243 0 : nLightColor = 0x666666;
244 : }
245 :
246 : // Y rotation (map OOXML [0..359] to Chart2 [-179,180])
247 0 : nRotationY %= 360;
248 0 : if( nRotationY > 180 ) nRotationY -= 360;
249 : /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is
250 : buggy here, the XML plugin of MSO 2003 writes the correct perspective in
251 : the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */
252 0 : sal_Int32 nPerspective = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.mnPerspective / 2, 0, 100 );
253 : // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
254 0 : bool bParallel = bRightAngled || (nPerspective == 0);
255 0 : cssd::ProjectionMode eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
256 :
257 : // set rotation properties
258 0 : aPropSet.setProperty( PROP_RotationVertical, nRotationY );
259 0 : aPropSet.setProperty( PROP_RotationHorizontal, nRotationX );
260 0 : aPropSet.setProperty( PROP_Perspective, nPerspective );
261 0 : aPropSet.setProperty( PROP_RightAngledAxes, bRightAngled );
262 0 : aPropSet.setProperty( PROP_D3DScenePerspective, eProjMode );
263 :
264 : // set light settings
265 0 : aPropSet.setProperty( PROP_D3DSceneShadeMode, cssd::ShadeMode_FLAT );
266 0 : aPropSet.setProperty( PROP_D3DSceneAmbientColor, nAmbientColor );
267 0 : aPropSet.setProperty( PROP_D3DSceneLightOn1, false );
268 0 : aPropSet.setProperty( PROP_D3DSceneLightOn2, true );
269 0 : aPropSet.setProperty( PROP_D3DSceneLightColor2, nLightColor );
270 0 : aPropSet.setProperty( PROP_D3DSceneLightDirection2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
271 0 : }
272 :
273 : // ============================================================================
274 :
275 0 : WallFloorConverter::WallFloorConverter( const ConverterRoot& rParent, WallFloorModel& rModel ) :
276 0 : ConverterBase< WallFloorModel >( rParent, rModel )
277 : {
278 0 : }
279 :
280 0 : WallFloorConverter::~WallFloorConverter()
281 : {
282 0 : }
283 :
284 0 : void WallFloorConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, ObjectType eObjType )
285 : {
286 0 : if( rxDiagram.is() )
287 : {
288 0 : PropertySet aPropSet;
289 0 : switch( eObjType )
290 : {
291 0 : case OBJECTTYPE_FLOOR: aPropSet.set( rxDiagram->getFloor() ); break;
292 0 : case OBJECTTYPE_WALL: aPropSet.set( rxDiagram->getWall() ); break;
293 : default: OSL_FAIL( "WallFloorConverter::convertFromModel - invalid object type" );
294 : }
295 0 : if( aPropSet.is() )
296 0 : getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(), eObjType );
297 : }
298 0 : }
299 :
300 : // ============================================================================
301 :
302 0 : PlotAreaConverter::PlotAreaConverter( const ConverterRoot& rParent, PlotAreaModel& rModel ) :
303 : ConverterBase< PlotAreaModel >( rParent, rModel ),
304 : mb3dChart( false ),
305 : mbWall3dChart( false ),
306 0 : mbPieChart( false )
307 : {
308 0 : }
309 :
310 0 : PlotAreaConverter::~PlotAreaConverter()
311 : {
312 0 : }
313 :
314 0 : void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel )
315 : {
316 : /* Create the diagram object and attach it to the chart document. One
317 : diagram is used to carry all coordinate systems and data series. */
318 0 : Reference< XDiagram > xDiagram;
319 : try
320 : {
321 0 : xDiagram.set( createInstance( CREATE_OUSTRING( "com.sun.star.chart2.Diagram" ) ), UNO_QUERY_THROW );
322 0 : getChartDocument()->setFirstDiagram( xDiagram );
323 : }
324 0 : catch( Exception& )
325 : {
326 : }
327 :
328 : // store all axis models in a map, keyed by axis identifier
329 : typedef ModelMap< sal_Int32, AxisModel > AxisMap;
330 0 : AxisMap aAxisMap;
331 0 : for( PlotAreaModel::AxisVector::iterator aAIt = mrModel.maAxes.begin(), aAEnd = mrModel.maAxes.end(); aAIt != aAEnd; ++aAIt )
332 : {
333 0 : PlotAreaModel::AxisVector::value_type xAxis = *aAIt;
334 : OSL_ENSURE( xAxis->mnAxisId >= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" );
335 : OSL_ENSURE( !aAxisMap.has( xAxis->mnAxisId ), "PlotAreaConverter::convertFromModel - axis identifiers not unique" );
336 0 : if( xAxis->mnAxisId >= 0 )
337 0 : aAxisMap[ xAxis->mnAxisId ] = xAxis;
338 0 : }
339 :
340 : // group the type group models into different axes sets
341 : typedef ModelVector< AxesSetModel > AxesSetVector;
342 0 : AxesSetVector aAxesSets;
343 0 : sal_Int32 nMaxSeriesIdx = -1;
344 0 : for( PlotAreaModel::TypeGroupVector::iterator aTIt = mrModel.maTypeGroups.begin(), aTEnd = mrModel.maTypeGroups.end(); aTIt != aTEnd; ++aTIt )
345 : {
346 0 : PlotAreaModel::TypeGroupVector::value_type xTypeGroup = *aTIt;
347 0 : if( !xTypeGroup->maSeries.empty() )
348 : {
349 : // try to find a compatible axes set for the type group
350 0 : AxesSetModel* pAxesSet = 0;
351 0 : for( AxesSetVector::iterator aASIt = aAxesSets.begin(), aASEnd = aAxesSets.end(); !pAxesSet && (aASIt != aASEnd); ++aASIt )
352 0 : if( (*aASIt)->maTypeGroups.front()->maAxisIds == xTypeGroup->maAxisIds )
353 0 : pAxesSet = aASIt->get();
354 :
355 : // not possible to insert into an existing axes set -> start a new axes set
356 0 : if( !pAxesSet )
357 : {
358 0 : pAxesSet = &aAxesSets.create();
359 : // find axis models used by the type group
360 0 : const TypeGroupModel::AxisIdVector& rAxisIds = xTypeGroup->maAxisIds;
361 0 : if( rAxisIds.size() >= 1 )
362 0 : pAxesSet->maAxes[ API_X_AXIS ] = aAxisMap.get( rAxisIds[ 0 ] );
363 0 : if( rAxisIds.size() >= 2 )
364 0 : pAxesSet->maAxes[ API_Y_AXIS ] = aAxisMap.get( rAxisIds[ 1 ] );
365 0 : if( rAxisIds.size() >= 3 )
366 0 : pAxesSet->maAxes[ API_Z_AXIS ] = aAxisMap.get( rAxisIds[ 2 ] );
367 : }
368 :
369 : // insert the type group model
370 0 : pAxesSet->maTypeGroups.push_back( xTypeGroup );
371 :
372 : // collect the maximum series index for automatic series formatting
373 0 : for( TypeGroupModel::SeriesVector::iterator aSIt = xTypeGroup->maSeries.begin(), aSEnd = xTypeGroup->maSeries.end(); aSIt != aSEnd; ++aSIt )
374 0 : nMaxSeriesIdx = ::std::max( nMaxSeriesIdx, (*aSIt)->mnIndex );
375 : }
376 0 : }
377 0 : getFormatter().setMaxSeriesIndex( nMaxSeriesIdx );
378 :
379 : // varying point colors only for single series in single chart type
380 0 : bool bSupportsVaryColorsByPoint = mrModel.maTypeGroups.size() == 1;
381 :
382 : // convert all axes sets
383 0 : for( AxesSetVector::iterator aASBeg = aAxesSets.begin(), aASIt = aASBeg, aASEnd = aAxesSets.end(); aASIt != aASEnd; ++aASIt )
384 : {
385 0 : AxesSetConverter aAxesSetConv( *this, **aASIt );
386 0 : sal_Int32 nAxesSetIdx = static_cast< sal_Int32 >( aASIt - aASBeg );
387 0 : aAxesSetConv.convertFromModel( xDiagram, rView3DModel, nAxesSetIdx, bSupportsVaryColorsByPoint );
388 0 : if( nAxesSetIdx == 0 )
389 : {
390 0 : maAutoTitle = aAxesSetConv.getAutomaticTitle();
391 0 : mb3dChart = aAxesSetConv.is3dChart();
392 0 : mbWall3dChart = aAxesSetConv.isWall3dChart();
393 0 : mbPieChart = aAxesSetConv.isPieChart();
394 : }
395 : else
396 : {
397 0 : maAutoTitle = OUString();
398 : }
399 0 : }
400 :
401 : // plot area formatting
402 0 : if( xDiagram.is() && !mb3dChart )
403 : {
404 0 : PropertySet aPropSet( xDiagram->getWall() );
405 0 : getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, OBJECTTYPE_PLOTAREA2D );
406 0 : }
407 0 : }
408 :
409 0 : void PlotAreaConverter::convertPositionFromModel()
410 : {
411 0 : LayoutModel& rLayout = mrModel.mxLayout.getOrCreate();
412 0 : LayoutConverter aLayoutConv( *this, rLayout );
413 0 : awt::Rectangle aDiagramRect;
414 0 : if( aLayoutConv.calcAbsRectangle( aDiagramRect ) ) try
415 : {
416 : namespace cssc = ::com::sun::star::chart;
417 0 : Reference< cssc::XChartDocument > xChart1Doc( getChartDocument(), UNO_QUERY_THROW );
418 0 : Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
419 : // for pie charts, always set inner plot area size to exclude the data labels as Excel does
420 0 : sal_Int32 nTarget = (mbPieChart && (rLayout.mnTarget == XML_outer)) ? XML_inner : rLayout.mnTarget;
421 0 : switch( nTarget )
422 : {
423 : case XML_inner:
424 0 : xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
425 0 : break;
426 : case XML_outer:
427 0 : xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
428 0 : break;
429 : default:
430 : OSL_FAIL( "PlotAreaConverter::convertPositionFromModel - unknown positioning target" );
431 0 : }
432 : }
433 0 : catch( Exception& )
434 : {
435 0 : }
436 0 : }
437 :
438 : // ============================================================================
439 :
440 : } // namespace chart
441 : } // namespace drawingml
442 51 : } // namespace oox
443 :
444 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|