| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "oox/drawingml/chart/plotareaconverter.hxx" |
| |
| #include <com/sun/star/chart/XChartDocument.hpp> |
| #include <com/sun/star/chart/XDiagramPositioning.hpp> |
| #include <com/sun/star/chart2/XChartDocument.hpp> |
| #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> |
| #include <com/sun/star/chart2/XDiagram.hpp> |
| #include <com/sun/star/drawing/Direction3D.hpp> |
| #include <com/sun/star/drawing/ProjectionMode.hpp> |
| #include <com/sun/star/drawing/ShadeMode.hpp> |
| #include "oox/drawingml/chart/axisconverter.hxx" |
| #include "oox/drawingml/chart/plotareamodel.hxx" |
| #include "oox/drawingml/chart/typegroupconverter.hxx" |
| |
| namespace oox { |
| namespace drawingml { |
| namespace chart { |
| |
| // ============================================================================ |
| |
| using namespace ::com::sun::star::awt; |
| using namespace ::com::sun::star::chart2; |
| using namespace ::com::sun::star::uno; |
| |
| using ::rtl::OUString; |
| |
| // ============================================================================ |
| |
| namespace { |
| |
| /** Axes set model. This is a helper for the plot area converter collecting all |
| type groups and axes of the primary or secondary axes set. */ |
| struct AxesSetModel |
| { |
| typedef ModelVector< TypeGroupModel > TypeGroupVector; |
| typedef ModelMap< sal_Int32, AxisModel > AxisMap; |
| |
| TypeGroupVector maTypeGroups; /// All type groups containing data series. |
| AxisMap maAxes; /// All axes mapped by API axis type. |
| |
| inline explicit AxesSetModel() {} |
| inline ~AxesSetModel() {} |
| }; |
| |
| // ============================================================================ |
| |
| /** Axes set converter. This is a helper class for the plot area converter. */ |
| class AxesSetConverter : public ConverterBase< AxesSetModel > |
| { |
| public: |
| explicit AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel ); |
| virtual ~AxesSetConverter(); |
| |
| /** Converts the axes set model to a chart2 diagram. Returns an automatic |
| chart title from a single series title, if possible. */ |
| void convertFromModel( |
| const Reference< XDiagram >& rxDiagram, |
| View3DModel& rView3DModel, |
| sal_Int32 nAxesSetIdx, |
| bool bSupportsVaryColorsByPoint ); |
| |
| /** Returns the automatic chart title if the axes set contains only one series. */ |
| inline const ::rtl::OUString& getAutomaticTitle() const { return maAutoTitle; } |
| /** Returns true, if the chart is three-dimensional. */ |
| inline bool is3dChart() const { return mb3dChart; } |
| /** Returns true, if chart type supports wall and floor format in 3D mode. */ |
| inline bool isWall3dChart() const { return mbWall3dChart; } |
| /** Returns true, if chart is a pie chart or doughnut chart. */ |
| inline bool isPieChart() const { return mbPieChart; } |
| |
| private: |
| ::rtl::OUString maAutoTitle; |
| bool mb3dChart; |
| bool mbWall3dChart; |
| bool mbPieChart; |
| }; |
| |
| // ---------------------------------------------------------------------------- |
| |
| AxesSetConverter::AxesSetConverter( const ConverterRoot& rParent, AxesSetModel& rModel ) : |
| ConverterBase< AxesSetModel >( rParent, rModel ), |
| mb3dChart( false ), |
| mbWall3dChart( false ), |
| mbPieChart( false ) |
| { |
| } |
| |
| AxesSetConverter::~AxesSetConverter() |
| { |
| } |
| |
| ModelRef< AxisModel > lclGetOrCreateAxis( const AxesSetModel::AxisMap& rFromAxes, sal_Int32 nAxisIdx, sal_Int32 nDefTypeId ) |
| { |
| ModelRef< AxisModel > xAxis = rFromAxes.get( nAxisIdx ); |
| if( !xAxis ) |
| xAxis.create( nDefTypeId ).mbDeleted = true; // missing axis is invisible |
| return xAxis; |
| } |
| |
| void AxesSetConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, |
| View3DModel& rView3DModel, sal_Int32 nAxesSetIdx, bool bSupportsVaryColorsByPoint ) |
| { |
| // create type group converter objects for all type groups |
| typedef RefVector< TypeGroupConverter > TypeGroupConvVector; |
| TypeGroupConvVector aTypeGroups; |
| for( AxesSetModel::TypeGroupVector::iterator aIt = mrModel.maTypeGroups.begin(), aEnd = mrModel.maTypeGroups.end(); aIt != aEnd; ++aIt ) |
| aTypeGroups.push_back( TypeGroupConvVector::value_type( new TypeGroupConverter( *this, **aIt ) ) ); |
| |
| OSL_ENSURE( !aTypeGroups.empty(), "AxesSetConverter::convertFromModel - no type groups in axes set" ); |
| if( !aTypeGroups.empty() ) try |
| { |
| // first type group needed for coordinate system and axis conversion |
| TypeGroupConverter& rFirstTypeGroup = *aTypeGroups.front(); |
| |
| // get automatic chart title, if there is only one type group |
| if( aTypeGroups.size() == 1 ) |
| maAutoTitle = rFirstTypeGroup.getSingleSeriesTitle(); |
| |
| /* Create a coordinate system. For now, all type groups from all axes sets |
| have to be inserted into one coordinate system. Later, chart2 should |
| support using one coordinate system for each axes set. */ |
| Reference< XCoordinateSystem > xCoordSystem; |
| Reference< XCoordinateSystemContainer > xCoordSystemCont( rxDiagram, UNO_QUERY_THROW ); |
| Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems(); |
| if( aCoordSystems.hasElements() ) |
| { |
| OSL_ENSURE( aCoordSystems.getLength() == 1, "AxesSetConverter::convertFromModel - too many coordinate systems" ); |
| xCoordSystem = aCoordSystems[ 0 ]; |
| OSL_ENSURE( xCoordSystem.is(), "AxesSetConverter::convertFromModel - invalid coordinate system" ); |
| } |
| else |
| { |
| xCoordSystem = rFirstTypeGroup.createCoordinateSystem(); |
| if( xCoordSystem.is() ) |
| xCoordSystemCont->addCoordinateSystem( xCoordSystem ); |
| } |
| |
| // 3D view settings |
| mb3dChart = rFirstTypeGroup.is3dChart(); |
| mbWall3dChart = rFirstTypeGroup.isWall3dChart(); |
| mbPieChart = rFirstTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE; |
| if( mb3dChart ) |
| { |
| View3DConverter aView3DConv( *this, rView3DModel ); |
| aView3DConv.convertFromModel( rxDiagram, rFirstTypeGroup ); |
| } |
| |
| /* Convert all chart type groups. Each type group will add its series |
| to the data provider attached to the chart document. */ |
| if( xCoordSystem.is() ) |
| { |
| // convert all axes (create missing axis models) |
| ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : C_TOKEN( valAx ) ); |
| ModelRef< AxisModel > xYAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Y_AXIS, C_TOKEN( valAx ) ); |
| |
| AxisConverter aXAxisConv( *this, *xXAxis ); |
| aXAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, xYAxis.get(), nAxesSetIdx, API_X_AXIS ); |
| AxisConverter aYAxisConv( *this, *xYAxis ); |
| aYAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, xXAxis.get(), nAxesSetIdx, API_Y_AXIS ); |
| |
| if( rFirstTypeGroup.isDeep3dChart() ) |
| { |
| ModelRef< AxisModel > xZAxis = lclGetOrCreateAxis( mrModel.maAxes, API_Z_AXIS, C_TOKEN( serAx ) ); |
| AxisConverter aZAxisConv( *this, *xZAxis ); |
| aZAxisConv.convertFromModel( xCoordSystem, rFirstTypeGroup, 0, nAxesSetIdx, API_Z_AXIS ); |
| } |
| |
| // convert all chart type groups, this converts all series data and formatting |
| for( TypeGroupConvVector::iterator aTIt = aTypeGroups.begin(), aTEnd = aTypeGroups.end(); aTIt != aTEnd; ++aTIt ) |
| (*aTIt)->convertFromModel( rxDiagram, xCoordSystem, nAxesSetIdx, bSupportsVaryColorsByPoint ); |
| } |
| } |
| catch( Exception& ) |
| { |
| } |
| } |
| |
| } // namespace |
| |
| // ============================================================================ |
| |
| View3DConverter::View3DConverter( const ConverterRoot& rParent, View3DModel& rModel ) : |
| ConverterBase< View3DModel >( rParent, rModel ) |
| { |
| } |
| |
| View3DConverter::~View3DConverter() |
| { |
| } |
| |
| void View3DConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, TypeGroupConverter& rTypeGroup ) |
| { |
| namespace cssd = ::com::sun::star::drawing; |
| PropertySet aPropSet( rxDiagram ); |
| |
| sal_Int32 nRotationY = 0; |
| sal_Int32 nRotationX = 0; |
| bool bRightAngled = false; |
| sal_Int32 nAmbientColor = 0; |
| sal_Int32 nLightColor = 0; |
| |
| if( rTypeGroup.getTypeInfo().meTypeCategory == TYPECATEGORY_PIE ) |
| { |
| // Y rotation used as 'first pie slice angle' in 3D pie charts |
| rTypeGroup.convertPieRotation( aPropSet, mrModel.monRotationY.get( 0 ) ); |
| // X rotation a.k.a. elevation (map OOXML [0..90] to Chart2 [-90,0]) |
| nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), 0, 90 ) - 90; |
| // no right-angled axes in pie charts |
| bRightAngled = false; |
| // ambient color (Gray 30%) |
| nAmbientColor = 0xB3B3B3; |
| // light color (Gray 70%) |
| nLightColor = 0x4C4C4C; |
| } |
| else // 3D bar/area/line charts |
| { |
| // Y rotation (OOXML [0..359], Chart2 [-179,180]) |
| nRotationY = mrModel.monRotationY.get( 20 ); |
| // X rotation a.k.a. elevation (OOXML [-90..90], Chart2 [-179,180]) |
| nRotationX = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.monRotationX.get( 15 ), -90, 90 ); |
| // right-angled axes |
| bRightAngled = mrModel.mbRightAngled; |
| // ambient color (Gray 20%) |
| nAmbientColor = 0xCCCCCC; |
| // light color (Gray 60%) |
| nLightColor = 0x666666; |
| } |
| |
| // Y rotation (map OOXML [0..359] to Chart2 [-179,180]) |
| nRotationY %= 360; |
| if( nRotationY > 180 ) nRotationY -= 360; |
| /* Perspective (map OOXML [0..200] to Chart2 [0,100]). Seems that MSO 2007 is |
| buggy here, the XML plugin of MSO 2003 writes the correct perspective in |
| the range from 0 to 100. We will emulate the wrong behaviour of MSO 2007. */ |
| sal_Int32 nPerspective = getLimitedValue< sal_Int32, sal_Int32 >( mrModel.mnPerspective / 2, 0, 100 ); |
| // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%) |
| bool bParallel = bRightAngled || (nPerspective == 0); |
| cssd::ProjectionMode eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE; |
| |
| // set rotation properties |
| aPropSet.setProperty( PROP_RotationVertical, nRotationY ); |
| aPropSet.setProperty( PROP_RotationHorizontal, nRotationX ); |
| aPropSet.setProperty( PROP_Perspective, nPerspective ); |
| aPropSet.setProperty( PROP_RightAngledAxes, bRightAngled ); |
| aPropSet.setProperty( PROP_D3DScenePerspective, eProjMode ); |
| |
| // set light settings |
| aPropSet.setProperty( PROP_D3DSceneShadeMode, cssd::ShadeMode_FLAT ); |
| aPropSet.setProperty( PROP_D3DSceneAmbientColor, nAmbientColor ); |
| aPropSet.setProperty( PROP_D3DSceneLightOn1, false ); |
| aPropSet.setProperty( PROP_D3DSceneLightOn2, true ); |
| aPropSet.setProperty( PROP_D3DSceneLightColor2, nLightColor ); |
| aPropSet.setProperty( PROP_D3DSceneLightDirection2, cssd::Direction3D( 0.2, 0.4, 1.0 ) ); |
| } |
| |
| // ============================================================================ |
| |
| WallFloorConverter::WallFloorConverter( const ConverterRoot& rParent, WallFloorModel& rModel ) : |
| ConverterBase< WallFloorModel >( rParent, rModel ) |
| { |
| } |
| |
| WallFloorConverter::~WallFloorConverter() |
| { |
| } |
| |
| void WallFloorConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, ObjectType eObjType ) |
| { |
| if( rxDiagram.is() ) |
| { |
| PropertySet aPropSet; |
| switch( eObjType ) |
| { |
| case OBJECTTYPE_FLOOR: aPropSet.set( rxDiagram->getFloor() ); break; |
| case OBJECTTYPE_WALL: aPropSet.set( rxDiagram->getWall() ); break; |
| default: OSL_ENSURE( false, "WallFloorConverter::convertFromModel - invalid object type" ); |
| } |
| if( aPropSet.is() ) |
| getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(), eObjType ); |
| } |
| } |
| |
| // ============================================================================ |
| |
| PlotAreaConverter::PlotAreaConverter( const ConverterRoot& rParent, PlotAreaModel& rModel ) : |
| ConverterBase< PlotAreaModel >( rParent, rModel ), |
| mb3dChart( false ), |
| mbWall3dChart( false ), |
| mbPieChart( false ) |
| { |
| } |
| |
| PlotAreaConverter::~PlotAreaConverter() |
| { |
| } |
| |
| void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel ) |
| { |
| /* Create the diagram object and attach it to the chart document. One |
| diagram is used to carry all coordinate systems and data series. */ |
| Reference< XDiagram > xDiagram; |
| try |
| { |
| xDiagram.set( createInstance( CREATE_OUSTRING( "com.sun.star.chart2.Diagram" ) ), UNO_QUERY_THROW ); |
| getChartDocument()->setFirstDiagram( xDiagram ); |
| } |
| catch( Exception& ) |
| { |
| } |
| |
| // store all axis models in a map, keyed by axis identifier |
| typedef ModelMap< sal_Int32, AxisModel > AxisMap; |
| AxisMap aAxisMap; |
| for( PlotAreaModel::AxisVector::iterator aAIt = mrModel.maAxes.begin(), aAEnd = mrModel.maAxes.end(); aAIt != aAEnd; ++aAIt ) |
| { |
| PlotAreaModel::AxisVector::value_type xAxis = *aAIt; |
| OSL_ENSURE( xAxis->mnAxisId >= 0, "PlotAreaConverter::convertFromModel - invalid axis identifier" ); |
| OSL_ENSURE( !aAxisMap.has( xAxis->mnAxisId ), "PlotAreaConverter::convertFromModel - axis identifiers not unique" ); |
| if( xAxis->mnAxisId >= 0 ) |
| aAxisMap[ xAxis->mnAxisId ] = xAxis; |
| } |
| |
| // group the type group models into different axes sets |
| typedef ModelVector< AxesSetModel > AxesSetVector; |
| AxesSetVector aAxesSets; |
| sal_Int32 nMaxSeriesIdx = -1; |
| for( PlotAreaModel::TypeGroupVector::iterator aTIt = mrModel.maTypeGroups.begin(), aTEnd = mrModel.maTypeGroups.end(); aTIt != aTEnd; ++aTIt ) |
| { |
| PlotAreaModel::TypeGroupVector::value_type xTypeGroup = *aTIt; |
| if( !xTypeGroup->maSeries.empty() ) |
| { |
| // try to find a compatible axes set for the type group |
| AxesSetModel* pAxesSet = 0; |
| for( AxesSetVector::iterator aASIt = aAxesSets.begin(), aASEnd = aAxesSets.end(); !pAxesSet && (aASIt != aASEnd); ++aASIt ) |
| if( (*aASIt)->maTypeGroups.front()->maAxisIds == xTypeGroup->maAxisIds ) |
| pAxesSet = aASIt->get(); |
| |
| // not possible to insert into an existing axes set -> start a new axes set |
| if( !pAxesSet ) |
| { |
| pAxesSet = &aAxesSets.create(); |
| // find axis models used by the type group |
| const TypeGroupModel::AxisIdVector& rAxisIds = xTypeGroup->maAxisIds; |
| if( rAxisIds.size() >= 1 ) |
| pAxesSet->maAxes[ API_X_AXIS ] = aAxisMap.get( rAxisIds[ 0 ] ); |
| if( rAxisIds.size() >= 2 ) |
| pAxesSet->maAxes[ API_Y_AXIS ] = aAxisMap.get( rAxisIds[ 1 ] ); |
| if( rAxisIds.size() >= 3 ) |
| pAxesSet->maAxes[ API_Z_AXIS ] = aAxisMap.get( rAxisIds[ 2 ] ); |
| } |
| |
| // insert the type group model |
| pAxesSet->maTypeGroups.push_back( xTypeGroup ); |
| |
| // collect the maximum series index for automatic series formatting |
| for( TypeGroupModel::SeriesVector::iterator aSIt = xTypeGroup->maSeries.begin(), aSEnd = xTypeGroup->maSeries.end(); aSIt != aSEnd; ++aSIt ) |
| nMaxSeriesIdx = ::std::max( nMaxSeriesIdx, (*aSIt)->mnIndex ); |
| } |
| } |
| getFormatter().setMaxSeriesIndex( nMaxSeriesIdx ); |
| |
| // varying point colors only for single series in single chart type |
| bool bSupportsVaryColorsByPoint = mrModel.maTypeGroups.size() == 1; |
| |
| // convert all axes sets |
| for( AxesSetVector::iterator aASBeg = aAxesSets.begin(), aASIt = aASBeg, aASEnd = aAxesSets.end(); aASIt != aASEnd; ++aASIt ) |
| { |
| AxesSetConverter aAxesSetConv( *this, **aASIt ); |
| sal_Int32 nAxesSetIdx = static_cast< sal_Int32 >( aASIt - aASBeg ); |
| aAxesSetConv.convertFromModel( xDiagram, rView3DModel, nAxesSetIdx, bSupportsVaryColorsByPoint ); |
| if( nAxesSetIdx == 0 ) |
| { |
| maAutoTitle = aAxesSetConv.getAutomaticTitle(); |
| mb3dChart = aAxesSetConv.is3dChart(); |
| mbWall3dChart = aAxesSetConv.isWall3dChart(); |
| mbPieChart = aAxesSetConv.isPieChart(); |
| } |
| else |
| { |
| maAutoTitle = OUString(); |
| } |
| } |
| |
| // plot area formatting |
| if( xDiagram.is() && !mb3dChart ) |
| { |
| PropertySet aPropSet( xDiagram->getWall() ); |
| getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, OBJECTTYPE_PLOTAREA2D ); |
| } |
| } |
| |
| void PlotAreaConverter::convertPositionFromModel() |
| { |
| LayoutModel& rLayout = mrModel.mxLayout.getOrCreate(); |
| LayoutConverter aLayoutConv( *this, rLayout ); |
| Rectangle aDiagramRect; |
| if( aLayoutConv.calcAbsRectangle( aDiagramRect ) ) try |
| { |
| namespace cssc = ::com::sun::star::chart; |
| Reference< cssc::XChartDocument > xChart1Doc( getChartDocument(), UNO_QUERY_THROW ); |
| Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW ); |
| // for pie charts, always set inner plot area size to exclude the data labels as Excel does |
| sal_Int32 nTarget = (mbPieChart && (rLayout.mnTarget == XML_outer)) ? XML_inner : rLayout.mnTarget; |
| switch( nTarget ) |
| { |
| case XML_inner: |
| xPositioning->setDiagramPositionExcludingAxes( aDiagramRect ); |
| break; |
| case XML_outer: |
| xPositioning->setDiagramPositionIncludingAxes( aDiagramRect ); |
| break; |
| default: |
| OSL_ENSURE( false, "PlotAreaConverter::convertPositionFromModel - unknown positioning target" ); |
| } |
| } |
| catch( Exception& ) |
| { |
| } |
| } |
| |
| // ============================================================================ |
| |
| } // namespace chart |
| } // namespace drawingml |
| } // namespace oox |