1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/axisconverter.hxx"
32 #include "drawingml/chart/plotareamodel.hxx"
33 #include "drawingml/chart/typegroupconverter.hxx"
39 using namespace ::com::sun::star
;
40 using namespace ::com::sun::star::chart2
;
41 using namespace ::com::sun::star::uno
;
45 /** Axes set model. This is a helper for the plot area converter collecting all
46 type groups and axes of the primary or secondary axes set. */
49 typedef ModelVector
< TypeGroupModel
> TypeGroupVector
;
50 typedef ModelMap
< sal_Int32
, AxisModel
> AxisMap
;
52 TypeGroupVector maTypeGroups
; /// All type groups containing data series.
53 AxisMap maAxes
; /// All axes mapped by API axis type.
55 inline explicit AxesSetModel() {}
56 inline ~AxesSetModel() {}
59 /** Axes set converter. This is a helper class for the plot area converter. */
60 class AxesSetConverter
: public ConverterBase
< AxesSetModel
>
63 explicit AxesSetConverter( const ConverterRoot
& rParent
, AxesSetModel
& rModel
);
64 virtual ~AxesSetConverter();
66 /** Converts the axes set model to a chart2 diagram. Returns an automatic
67 chart title from a single series title, if possible. */
68 void convertFromModel(
69 const Reference
< XDiagram
>& rxDiagram
,
70 View3DModel
& rView3DModel
,
71 sal_Int32 nAxesSetIdx
,
72 bool bSupportsVaryColorsByPoint
);
74 /** Returns the automatic chart title if the axes set contains only one series. */
75 inline const OUString
& getAutomaticTitle() const { return maAutoTitle
; }
76 /** Returns true, if the chart is three-dimensional. */
77 inline bool is3dChart() const { return mb3dChart
; }
78 /** Returns true, if chart type supports wall and floor format in 3D mode. */
79 inline bool isWall3dChart() const { return mbWall3dChart
; }
80 /** Returns true, if chart is a pie chart or doughnut chart. */
81 inline bool isPieChart() const { return mbPieChart
; }
90 AxesSetConverter::AxesSetConverter( const ConverterRoot
& rParent
, AxesSetModel
& rModel
) :
91 ConverterBase
< AxesSetModel
>( rParent
, rModel
),
93 mbWall3dChart( false ),
98 AxesSetConverter::~AxesSetConverter()
102 ModelRef
< AxisModel
> lclGetOrCreateAxis( const AxesSetModel::AxisMap
& rFromAxes
, sal_Int32 nAxisIdx
, sal_Int32 nDefTypeId
, bool bMSO2007Doc
)
104 ModelRef
< AxisModel
> xAxis
= rFromAxes
.get( nAxisIdx
);
106 xAxis
.create( nDefTypeId
, bMSO2007Doc
).mbDeleted
= true; // missing axis is invisible
110 void AxesSetConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
,
111 View3DModel
& rView3DModel
, sal_Int32 nAxesSetIdx
, bool bSupportsVaryColorsByPoint
)
113 // create type group converter objects for all type groups
114 typedef RefVector
< TypeGroupConverter
> TypeGroupConvVector
;
115 TypeGroupConvVector aTypeGroups
;
116 for( AxesSetModel::TypeGroupVector::iterator aIt
= mrModel
.maTypeGroups
.begin(), aEnd
= mrModel
.maTypeGroups
.end(); aIt
!= aEnd
; ++aIt
)
117 aTypeGroups
.push_back( TypeGroupConvVector::value_type( new TypeGroupConverter( *this, **aIt
) ) );
119 OSL_ENSURE( !aTypeGroups
.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" );
120 if( !aTypeGroups
.empty() ) try
122 // first type group needed for coordinate system and axis conversion
123 TypeGroupConverter
& rFirstTypeGroup
= *aTypeGroups
.front();
125 // get automatic chart title, if there is only one type group
126 if( aTypeGroups
.size() == 1 )
127 maAutoTitle
= rFirstTypeGroup
.getSingleSeriesTitle();
129 /* Create a coordinate system. For now, all type groups from all axes sets
130 have to be inserted into one coordinate system. Later, chart2 should
131 support using one coordinate system for each axes set. */
132 Reference
< XCoordinateSystem
> xCoordSystem
;
133 Reference
< XCoordinateSystemContainer
> xCoordSystemCont( rxDiagram
, UNO_QUERY_THROW
);
134 Sequence
< Reference
< XCoordinateSystem
> > aCoordSystems
= xCoordSystemCont
->getCoordinateSystems();
135 if( aCoordSystems
.hasElements() )
137 OSL_ENSURE( aCoordSystems
.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" );
138 xCoordSystem
= aCoordSystems
[ 0 ];
139 OSL_ENSURE( xCoordSystem
.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" );
143 xCoordSystem
= rFirstTypeGroup
.createCoordinateSystem();
144 if( xCoordSystem
.is() )
145 xCoordSystemCont
->addCoordinateSystem( xCoordSystem
);
149 mb3dChart
= rFirstTypeGroup
.is3dChart();
150 mbWall3dChart
= rFirstTypeGroup
.isWall3dChart();
151 mbPieChart
= rFirstTypeGroup
.getTypeInfo().meTypeCategory
== TYPECATEGORY_PIE
;
154 View3DConverter
aView3DConv( *this, rView3DModel
);
155 aView3DConv
.convertFromModel( rxDiagram
, rFirstTypeGroup
);
158 /* Convert all chart type groups. Each type group will add its series
159 to the data provider attached to the chart document. */
160 if( xCoordSystem
.is() )
162 bool bMSO2007Doc
= getFilter().isMSO2007Document();
163 // convert all axes (create missing axis models)
164 ModelRef
< AxisModel
> xXAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_X_AXIS
, rFirstTypeGroup
.getTypeInfo().mbCategoryAxis
? C_TOKEN( catAx
) : C_TOKEN( valAx
), bMSO2007Doc
);
165 ModelRef
< AxisModel
> xYAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_Y_AXIS
, C_TOKEN( valAx
), bMSO2007Doc
);
167 AxisConverter
aXAxisConv( *this, *xXAxis
);
168 aXAxisConv
.convertFromModel( xCoordSystem
, aTypeGroups
, xYAxis
.get(), nAxesSetIdx
, API_X_AXIS
);
169 AxisConverter
aYAxisConv( *this, *xYAxis
);
170 aYAxisConv
.convertFromModel( xCoordSystem
, aTypeGroups
, xXAxis
.get(), nAxesSetIdx
, API_Y_AXIS
);
172 if( rFirstTypeGroup
.isDeep3dChart() )
174 ModelRef
< AxisModel
> xZAxis
= lclGetOrCreateAxis( mrModel
.maAxes
, API_Z_AXIS
, C_TOKEN( serAx
), bMSO2007Doc
);
175 AxisConverter
aZAxisConv( *this, *xZAxis
);
176 aZAxisConv
.convertFromModel( xCoordSystem
, aTypeGroups
, 0, nAxesSetIdx
, API_Z_AXIS
);
179 // convert all chart type groups, this converts all series data and formatting
180 for( TypeGroupConvVector::iterator aTIt
= aTypeGroups
.begin(), aTEnd
= aTypeGroups
.end(); aTIt
!= aTEnd
; ++aTIt
)
181 (*aTIt
)->convertFromModel( rxDiagram
, xCoordSystem
, nAxesSetIdx
, bSupportsVaryColorsByPoint
);
191 View3DConverter::View3DConverter( const ConverterRoot
& rParent
, View3DModel
& rModel
) :
192 ConverterBase
< View3DModel
>( rParent
, rModel
)
196 View3DConverter::~View3DConverter()
200 void View3DConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
, TypeGroupConverter
& rTypeGroup
)
202 namespace cssd
= ::com::sun::star::drawing
;
203 PropertySet
aPropSet( rxDiagram
);
205 sal_Int32 nRotationY
= 0;
206 sal_Int32 nRotationX
= 0;
207 bool bRightAngled
= false;
208 sal_Int32 nAmbientColor
= 0;
209 sal_Int32 nLightColor
= 0;
211 if( rTypeGroup
.getTypeInfo().meTypeCategory
== TYPECATEGORY_PIE
)
213 // Y rotation used as 'first pie slice angle' in 3D pie charts
214 rTypeGroup
.convertPieRotation( aPropSet
, mrModel
.monRotationY
.get( 0 ) );
215 // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0])
216 nRotationX
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.monRotationX
.get( 15 ), 0, 90 ) - 90;
217 // no right-angled axes in pie charts
218 bRightAngled
= false;
219 // ambient color (Gray 30%)
220 nAmbientColor
= 0xB3B3B3;
221 // light color (Gray 70%)
222 nLightColor
= 0x4C4C4C;
224 else // 3D bar/area/line charts
226 // Y rotation (OOXML [0..359], Chart2 [-179,180])
227 nRotationY
= mrModel
.monRotationY
.get( 20 );
228 // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180])
229 nRotationX
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.monRotationX
.get( 15 ), -90, 90 );
231 bRightAngled
= mrModel
.mbRightAngled
;
232 // ambient color (Gray 20%)
233 nAmbientColor
= 0xCCCCCC;
234 // light color (Gray 60%)
235 nLightColor
= 0x666666;
238 // Y rotation (map OOXML [0..359] to Chart2 [-179,180])
240 if( nRotationY
> 180 ) nRotationY
-= 360;
241 /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is
242 buggy here, the XML plugin of MSO 2003 writes the correct perspective in
243 the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */
244 sal_Int32 nPerspective
= getLimitedValue
< sal_Int32
, sal_Int32
>( mrModel
.mnPerspective
/ 2, 0, 100 );
245 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
246 bool bParallel
= bRightAngled
|| (nPerspective
== 0);
247 cssd::ProjectionMode eProjMode
= bParallel
? cssd::ProjectionMode_PARALLEL
: cssd::ProjectionMode_PERSPECTIVE
;
249 // set rotation properties
250 aPropSet
.setProperty( PROP_RightAngledAxes
, bRightAngled
);
251 aPropSet
.setProperty( PROP_RotationVertical
, nRotationY
);
252 aPropSet
.setProperty( PROP_RotationHorizontal
, nRotationX
);
253 aPropSet
.setProperty( PROP_Perspective
, nPerspective
);
254 aPropSet
.setProperty( PROP_D3DScenePerspective
, eProjMode
);
256 // set light settings
257 aPropSet
.setProperty( PROP_D3DSceneShadeMode
, cssd::ShadeMode_FLAT
);
258 aPropSet
.setProperty( PROP_D3DSceneAmbientColor
, nAmbientColor
);
259 aPropSet
.setProperty( PROP_D3DSceneLightOn1
, false );
260 aPropSet
.setProperty( PROP_D3DSceneLightOn2
, true );
261 aPropSet
.setProperty( PROP_D3DSceneLightColor2
, nLightColor
);
262 aPropSet
.setProperty( PROP_D3DSceneLightDirection2
, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
265 WallFloorConverter::WallFloorConverter( const ConverterRoot
& rParent
, WallFloorModel
& rModel
) :
266 ConverterBase
< WallFloorModel
>( rParent
, rModel
)
270 WallFloorConverter::~WallFloorConverter()
274 void WallFloorConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
, ObjectType eObjType
)
276 bool bMSO2007Doc
= getFilter().isMSO2007Document();
279 PropertySet aPropSet
;
282 case OBJECTTYPE_FLOOR
: aPropSet
.set( rxDiagram
->getFloor() ); break;
283 case OBJECTTYPE_WALL
: aPropSet
.set( rxDiagram
->getWall() ); break;
284 default: OSL_FAIL( "WallFloorConverter::convertFromModel - invalid object type" );
287 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, mrModel
.mxPicOptions
.getOrCreate(bMSO2007Doc
), eObjType
);
291 DataTableConverter::DataTableConverter( const ConverterRoot
& rParent
, DataTableModel
& rModel
) :
292 ConverterBase
< DataTableModel
>( rParent
, rModel
)
296 DataTableConverter::~DataTableConverter()
300 void DataTableConverter::convertFromModel( const Reference
< XDiagram
>& rxDiagram
)
302 PropertySet
aPropSet( rxDiagram
);
303 if (mrModel
.mbShowHBorder
)
304 aPropSet
.setProperty( PROP_DataTableHBorder
, mrModel
.mbShowHBorder
);
305 if (mrModel
.mbShowVBorder
)
306 aPropSet
.setProperty( PROP_DataTableVBorder
, mrModel
.mbShowVBorder
);
307 if (mrModel
.mbShowOutline
)
308 aPropSet
.setProperty( PROP_DataTableOutline
, mrModel
.mbShowOutline
);
311 PlotAreaConverter::PlotAreaConverter( const ConverterRoot
& rParent
, PlotAreaModel
& rModel
) :
312 ConverterBase
< PlotAreaModel
>( rParent
, rModel
),
314 mbWall3dChart( false ),
319 PlotAreaConverter::~PlotAreaConverter()
323 void PlotAreaConverter::convertFromModel( View3DModel
& rView3DModel
)
325 /* Create the diagram object and attach it to the chart document. One
326 diagram is used to carry all coordinate systems and data series. */
327 Reference
< XDiagram
> xDiagram
;
330 xDiagram
.set( createInstance( "com.sun.star.chart2.Diagram" ), UNO_QUERY_THROW
);
331 getChartDocument()->setFirstDiagram( xDiagram
);
337 // store all axis models in a map, keyed by axis identifier
338 typedef ModelMap
< sal_Int32
, AxisModel
> AxisMap
;
340 for( PlotAreaModel::AxisVector::iterator aAIt
= mrModel
.maAxes
.begin(), aAEnd
= mrModel
.maAxes
.end(); aAIt
!= aAEnd
; ++aAIt
)
342 PlotAreaModel::AxisVector::value_type xAxis
= *aAIt
;
343 OSL_ENSURE( xAxis
->mnAxisId
>= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" );
344 OSL_ENSURE( !aAxisMap
.has( xAxis
->mnAxisId
), "PlotAreaConverter::convertFromModel - axis identifiers not unique" );
345 if( xAxis
->mnAxisId
!= -1 )
346 aAxisMap
[ xAxis
->mnAxisId
] = xAxis
;
349 // group the type group models into different axes sets
350 typedef ModelVector
< AxesSetModel
> AxesSetVector
;
351 AxesSetVector aAxesSets
;
352 sal_Int32 nMaxSeriesIdx
= -1;
353 for( PlotAreaModel::TypeGroupVector::iterator aTIt
= mrModel
.maTypeGroups
.begin(), aTEnd
= mrModel
.maTypeGroups
.end(); aTIt
!= aTEnd
; ++aTIt
)
355 PlotAreaModel::TypeGroupVector::value_type xTypeGroup
= *aTIt
;
356 if( !xTypeGroup
->maSeries
.empty() )
358 // try to find a compatible axes set for the type group
359 AxesSetModel
* pAxesSet
= 0;
360 for( AxesSetVector::iterator aASIt
= aAxesSets
.begin(), aASEnd
= aAxesSets
.end(); !pAxesSet
&& (aASIt
!= aASEnd
); ++aASIt
)
361 if( (*aASIt
)->maTypeGroups
.front()->maAxisIds
== xTypeGroup
->maAxisIds
)
362 pAxesSet
= aASIt
->get();
364 // not possible to insert into an existing axes set -> start a new axes set
367 pAxesSet
= &aAxesSets
.create();
368 // find axis models used by the type group
369 const TypeGroupModel::AxisIdVector
& rAxisIds
= xTypeGroup
->maAxisIds
;
370 if( rAxisIds
.size() >= 1 )
371 pAxesSet
->maAxes
[ API_X_AXIS
] = aAxisMap
.get( rAxisIds
[ 0 ] );
372 if( rAxisIds
.size() >= 2 )
373 pAxesSet
->maAxes
[ API_Y_AXIS
] = aAxisMap
.get( rAxisIds
[ 1 ] );
374 if( rAxisIds
.size() >= 3 )
375 pAxesSet
->maAxes
[ API_Z_AXIS
] = aAxisMap
.get( rAxisIds
[ 2 ] );
378 // insert the type group model
379 pAxesSet
->maTypeGroups
.push_back( xTypeGroup
);
381 // collect the maximum series index for automatic series formatting
382 for( TypeGroupModel::SeriesVector::iterator aSIt
= xTypeGroup
->maSeries
.begin(), aSEnd
= xTypeGroup
->maSeries
.end(); aSIt
!= aSEnd
; ++aSIt
)
383 nMaxSeriesIdx
= ::std::max( nMaxSeriesIdx
, (*aSIt
)->mnIndex
);
386 getFormatter().setMaxSeriesIndex( nMaxSeriesIdx
);
388 // varying point colors only for single series in single chart type
389 bool bSupportsVaryColorsByPoint
= mrModel
.maTypeGroups
.size() == 1;
391 // convert all axes sets
392 for( AxesSetVector::iterator aASBeg
= aAxesSets
.begin(), aASIt
= aASBeg
, aASEnd
= aAxesSets
.end(); aASIt
!= aASEnd
; ++aASIt
)
394 AxesSetConverter
aAxesSetConv( *this, **aASIt
);
395 sal_Int32 nAxesSetIdx
= static_cast< sal_Int32
>( aASIt
- aASBeg
);
396 aAxesSetConv
.convertFromModel( xDiagram
, rView3DModel
, nAxesSetIdx
, bSupportsVaryColorsByPoint
);
397 if( nAxesSetIdx
== 0 )
399 maAutoTitle
= aAxesSetConv
.getAutomaticTitle();
400 mb3dChart
= aAxesSetConv
.is3dChart();
401 mbWall3dChart
= aAxesSetConv
.isWall3dChart();
402 mbPieChart
= aAxesSetConv
.isPieChart();
410 DataTableConverter
dataTableConverter (*this, mrModel
.mxDataTable
.getOrCreate());
411 dataTableConverter
.convertFromModel(xDiagram
);
412 // plot area formatting
413 if( xDiagram
.is() && !mb3dChart
)
415 PropertySet
aPropSet( xDiagram
->getWall() );
416 getFormatter().convertFrameFormatting( aPropSet
, mrModel
.mxShapeProp
, OBJECTTYPE_PLOTAREA2D
);
420 void PlotAreaConverter::convertPositionFromModel()
422 LayoutModel
& rLayout
= mrModel
.mxLayout
.getOrCreate();
423 LayoutConverter
aLayoutConv( *this, rLayout
);
424 awt::Rectangle aDiagramRect
;
425 if( aLayoutConv
.calcAbsRectangle( aDiagramRect
) ) try
427 namespace cssc
= ::com::sun::star::chart
;
428 Reference
< cssc::XChartDocument
> xChart1Doc( getChartDocument(), UNO_QUERY_THROW
);
429 Reference
< cssc::XDiagramPositioning
> xPositioning( xChart1Doc
->getDiagram(), UNO_QUERY_THROW
);
430 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
431 sal_Int32 nTarget
= (mbPieChart
&& (rLayout
.mnTarget
== XML_outer
)) ? XML_inner
: rLayout
.mnTarget
;
435 xPositioning
->setDiagramPositionExcludingAxes( aDiagramRect
);
438 xPositioning
->setDiagramPositionIncludingAxes( aDiagramRect
);
441 OSL_FAIL( "PlotAreaConverter::convertPositionFromModel - unknown positioning target" );
450 } // namespace drawingml
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */