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