/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #include "PieChartTypeTemplate.hxx" #include "PieChartType.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using ::com::sun::star::beans::Property; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::Reference; namespace { enum { PROP_PIE_TEMPLATE_DEFAULT_OFFSET, PROP_PIE_TEMPLATE_OFFSET_MODE, PROP_PIE_TEMPLATE_DIMENSION, PROP_PIE_TEMPLATE_USE_RINGS }; ::chart::tPropertyValueMap& StaticPieChartTypeTemplateDefaults() { static ::chart::tPropertyValueMap aStaticDefaults = []{ ::chart::tPropertyValueMap aOutMap; ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_PIE_TEMPLATE_OFFSET_MODE, chart2::PieChartOffsetMode_NONE ); ::chart::PropertyHelper::setPropertyValueDefault< double >( aOutMap, PROP_PIE_TEMPLATE_DEFAULT_OFFSET, 0.5 ); ::chart::PropertyHelper::setPropertyValueDefault< sal_Int32 >( aOutMap, PROP_PIE_TEMPLATE_DIMENSION, 2 ); ::chart::PropertyHelper::setPropertyValueDefault( aOutMap, PROP_PIE_TEMPLATE_USE_RINGS, false ); return aOutMap; }(); return aStaticDefaults; } ::cppu::OPropertyArrayHelper& StaticPieChartTypeTemplateInfoHelper() { static ::cppu::OPropertyArrayHelper aPropHelper( []() { std::vector< css::beans::Property > aProperties { { "OffsetMode", PROP_PIE_TEMPLATE_OFFSET_MODE, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::MAYBEDEFAULT }, { "DefaultOffset", PROP_PIE_TEMPLATE_DEFAULT_OFFSET, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::MAYBEDEFAULT }, { "Dimension", PROP_PIE_TEMPLATE_DIMENSION, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::MAYBEDEFAULT }, { "UseRings", PROP_PIE_TEMPLATE_USE_RINGS, cppu::UnoType::get(), beans::PropertyAttribute::BOUND | beans::PropertyAttribute::MAYBEDEFAULT } }; std::sort( aProperties.begin(), aProperties.end(), ::chart::PropertyNameLess() ); return comphelper::containerToSequence( aProperties ); }() ); return aPropHelper; } uno::Reference< beans::XPropertySetInfo >& StaticPieChartTypeTemplateInfo() { static uno::Reference< beans::XPropertySetInfo > xPropertySetInfo( ::cppu::OPropertySetHelper::createPropertySetInfo(StaticPieChartTypeTemplateInfoHelper() ) ); return xPropertySetInfo; } } // anonymous namespace namespace chart { PieChartTypeTemplate::PieChartTypeTemplate( uno::Reference< uno::XComponentContext > const & xContext, const OUString & rServiceName, chart2::PieChartOffsetMode eMode, bool bRings /* = false */, sal_Int32 nDim /* = 2 */ ) : ChartTypeTemplate( xContext, rServiceName ), ::property::OPropertySet( m_aMutex ) { setFastPropertyValue_NoBroadcast( PROP_PIE_TEMPLATE_OFFSET_MODE, uno::Any( eMode )); setFastPropertyValue_NoBroadcast( PROP_PIE_TEMPLATE_DIMENSION, uno::Any( nDim )); setFastPropertyValue_NoBroadcast( PROP_PIE_TEMPLATE_USE_RINGS, uno::Any( bRings )); } PieChartTypeTemplate::~PieChartTypeTemplate() {} // ____ OPropertySet ____ void PieChartTypeTemplate::GetDefaultValue( sal_Int32 nHandle, uno::Any& rAny ) const { const tPropertyValueMap& rStaticDefaults = StaticPieChartTypeTemplateDefaults(); tPropertyValueMap::const_iterator aFound( rStaticDefaults.find( nHandle ) ); if( aFound == rStaticDefaults.end() ) rAny.clear(); else rAny = (*aFound).second; } ::cppu::IPropertyArrayHelper & SAL_CALL PieChartTypeTemplate::getInfoHelper() { return StaticPieChartTypeTemplateInfoHelper(); } // ____ XPropertySet ____ uno::Reference< beans::XPropertySetInfo > SAL_CALL PieChartTypeTemplate::getPropertySetInfo() { return StaticPieChartTypeTemplateInfo(); } // ____ ChartTypeTemplate ____ sal_Int32 PieChartTypeTemplate::getDimension() const { sal_Int32 nDim = 2; try { // note: UNO-methods are never const const_cast< PieChartTypeTemplate * >( this )-> getFastPropertyValue( PROP_PIE_TEMPLATE_DIMENSION ) >>= nDim; } catch( const beans::UnknownPropertyException & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } return nDim; } sal_Int32 PieChartTypeTemplate::getAxisCountByDimension( sal_Int32 /*nDimension*/ ) { return 0; } void PieChartTypeTemplate::adaptAxes( const std::vector< rtl::Reference< BaseCoordinateSystem > > & /*rCoordSys*/ ) { // hide existing axes //hhhh todo } void PieChartTypeTemplate::adaptScales( const std::vector< rtl::Reference< BaseCoordinateSystem > > & aCooSysSeq, const Reference< chart2::data::XLabeledDataSequence > & xCategories //@todo: in future there may be more than one sequence of categories (e.g. charttype with categories at x and y axis ) ) { ChartTypeTemplate::adaptScales( aCooSysSeq, xCategories ); //remove explicit scalings from radius axis //and ensure correct orientation of scales for donuts for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysSeq ) { try { rtl::Reference< Axis > xAxis = AxisHelper::getAxis( 1 /*nDimensionIndex*/,0 /*nAxisIndex*/ , coords ); if( xAxis.is() ) { chart2::ScaleData aScaleData( xAxis->getScaleData() ); AxisHelper::removeExplicitScaling( aScaleData ); // tdf#108059 Create new pie/donut charts with clockwise orientation if (!officecfg::Office::Compatibility::View::ClockwisePieChartDirection::get()) { aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; } else { aScaleData.Orientation = chart2::AxisOrientation_REVERSE; } xAxis->setScaleData( aScaleData ); } xAxis = AxisHelper::getAxis( 0 /*nDimensionIndex*/,0 /*nAxisIndex*/ , coords ); if( xAxis.is() ) { chart2::ScaleData aScaleData( xAxis->getScaleData() ); //tdf#123218 Don't reverse the orientation in OOXML-heavy environments if( officecfg::Office::Compatibility::View::ReverseXAxisOrientationDoughnutChart::get() ) aScaleData.Orientation = chart2::AxisOrientation_REVERSE; else aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; xAxis->setScaleData( aScaleData ); } } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } } } void PieChartTypeTemplate::createChartTypes( const std::vector< std::vector< rtl::Reference< DataSeries > > > & aSeriesSeq, const std::vector< rtl::Reference< BaseCoordinateSystem > > & rCoordSys, const std::vector< rtl::Reference< ChartType > >& /* aOldChartTypesSeq */ ) { if( rCoordSys.empty() ) return; try { rtl::Reference< ChartType > xCT = new PieChartType(); xCT->setPropertyValue( "UseRings", getFastPropertyValue( PROP_PIE_TEMPLATE_USE_RINGS )); rCoordSys[0]->setChartTypes( std::vector{xCT} ); if( !aSeriesSeq.empty() ) { std::vector< rtl::Reference< DataSeries > > aFlatSeriesSeq = FlattenSequence( aSeriesSeq ); xCT->setDataSeries( aFlatSeriesSeq ); DataSeriesHelper::setStackModeAtSeries( aFlatSeriesSeq, rCoordSys[0], getStackMode( 0 )); } } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } } // ____ XChartTypeTemplate ____ bool PieChartTypeTemplate::matchesTemplate2( const rtl::Reference< ::chart::Diagram >& xDiagram, bool bAdaptProperties ) { bool bResult = ChartTypeTemplate::matchesTemplate2( xDiagram, bAdaptProperties ); bool bTemplateUsesRings = false; getFastPropertyValue( PROP_PIE_TEMPLATE_USE_RINGS ) >>= bTemplateUsesRings; chart2::PieChartOffsetMode ePieOffsetMode; getFastPropertyValue( PROP_PIE_TEMPLATE_OFFSET_MODE ) >>= ePieOffsetMode; //check offset-mode if( bResult ) { try { double fOffset=0.0; bool bAllOffsetsEqual = true; sal_Int32 nOuterSeriesIndex = 0; std::vector< rtl::Reference< DataSeries > > aSeriesVec = DiagramHelper::getDataSeriesFromDiagram( xDiagram ); //tdf#108067 The outer series is the last series in OOXML-heavy environments if( !officecfg::Office::Compatibility::View::ReverseXAxisOrientationDoughnutChart::get() ) nOuterSeriesIndex = aSeriesVec.size() - 1; //check offset of outer series if( !aSeriesVec.empty() ) { //@todo in future this will depend on Orientation of the radius axis scale rtl::Reference< DataSeries > xSeries( aSeriesVec[nOuterSeriesIndex] ); xSeries->getPropertyValue( "Offset") >>= fOffset; //get AttributedDataPoints uno::Sequence< sal_Int32 > aAttributedDataPointIndexList; if( xSeries->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList ) { for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;) { uno::Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) ); if(xPointProp.is()) { double fPointOffset=0.0; if( xSeries->getPropertyValue( "Offset") >>= fPointOffset ) { if( ! ::rtl::math::approxEqual( fPointOffset, fOffset ) ) { bAllOffsetsEqual = false; break; } } } } } } chart2::PieChartOffsetMode eOffsetMode = chart2::PieChartOffsetMode_NONE; if( bAllOffsetsEqual && fOffset > 0.0 ) { eOffsetMode = chart2::PieChartOffsetMode_ALL_EXPLODED; if( bAdaptProperties ) setFastPropertyValue_NoBroadcast( PROP_PIE_TEMPLATE_DEFAULT_OFFSET, uno::Any( fOffset )); } bResult = ( eOffsetMode == ePieOffsetMode ); } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); bResult = false; } } //check UseRings if( bResult ) { rtl::Reference< ChartType > xCTProp = DiagramHelper::getChartTypeByIndex( xDiagram, 0 ); bool bUseRings = false; if( xCTProp->getPropertyValue( "UseRings") >>= bUseRings ) { bResult = ( bTemplateUsesRings == bUseRings ); } } return bResult; } rtl::Reference< ChartType > PieChartTypeTemplate::getChartTypeForIndex( sal_Int32 /*nChartTypeIndex*/ ) { rtl::Reference< ChartType > xResult; try { xResult = new PieChartType(); xResult->setPropertyValue( "UseRings", getFastPropertyValue( PROP_PIE_TEMPLATE_USE_RINGS )); } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } return xResult; } rtl::Reference< ChartType > PieChartTypeTemplate::getChartTypeForNewSeries2( const std::vector< rtl::Reference< ChartType > >& aFormerlyUsedChartTypes ) { rtl::Reference< ChartType > xResult; try { xResult = new PieChartType(); ChartTypeTemplate::copyPropertiesFromOldToNewCoordinateSystem( aFormerlyUsedChartTypes, xResult ); xResult->setPropertyValue( "UseRings", getFastPropertyValue( PROP_PIE_TEMPLATE_USE_RINGS )); } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } return xResult; } void PieChartTypeTemplate::applyStyle2( const rtl::Reference< DataSeries >& xSeries, ::sal_Int32 nChartTypeIndex, ::sal_Int32 nSeriesIndex, ::sal_Int32 nSeriesCount ) { ChartTypeTemplate::applyStyle2( xSeries, nChartTypeIndex, nSeriesIndex, nSeriesCount ); try { bool bTemplateUsesRings = false; sal_Int32 nOuterSeriesIndex = 0; getFastPropertyValue( PROP_PIE_TEMPLATE_USE_RINGS ) >>= bTemplateUsesRings; //tdf#108067 The outer series is the last series in OOXML-heavy environments if( !officecfg::Office::Compatibility::View::ReverseXAxisOrientationDoughnutChart::get() ) nOuterSeriesIndex = nSeriesCount - 1; if( nSeriesIndex == nOuterSeriesIndex ) //@todo in future this will depend on Orientation of the radius axis scale { static const OUStringLiteral aOffsetPropName( u"Offset" ); // get offset mode chart2::PieChartOffsetMode ePieOffsetMode; getFastPropertyValue( PROP_PIE_TEMPLATE_OFFSET_MODE ) >>= ePieOffsetMode; // get default offset double fDefaultOffset = 0.5; getFastPropertyValue( PROP_PIE_TEMPLATE_DEFAULT_OFFSET ) >>= fDefaultOffset; double fOffsetToSet = fDefaultOffset; uno::Sequence< sal_Int32 > aAttributedDataPointIndexList; xSeries->getPropertyValue( "AttributedDataPoints" ) >>= aAttributedDataPointIndexList; // determine whether to set the new offset bool bSetOffset = ( ePieOffsetMode == chart2::PieChartOffsetMode_ALL_EXPLODED ); if( !bSetOffset && (ePieOffsetMode == chart2::PieChartOffsetMode_NONE) ) { // set offset to 0 if the offset was exactly "all exploded" // before (individual offsets are kept) double fOffset = 0.0; if( (xSeries->getPropertyValue( aOffsetPropName ) >>= fOffset) && ::rtl::math::approxEqual( fOffset, fDefaultOffset )) { fOffsetToSet = 0.0; bSetOffset = true; for( auto const & pointIndex : std::as_const(aAttributedDataPointIndexList) ) { uno::Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex( pointIndex )); uno::Reference< beans::XPropertyState > xPointState( xPointProp, uno::UNO_QUERY ); double fPointOffset = 0.0; if( xPointState.is() && (xPointState->getPropertyState( aOffsetPropName ) == beans::PropertyState_DIRECT_VALUE) && xPointProp.is() && (xPointProp->getPropertyValue( aOffsetPropName ) >>= fPointOffset ) && ! ::rtl::math::approxEqual( fPointOffset, fDefaultOffset ) ) { bSetOffset = false; break; } } } } if( bSetOffset ) { // set the offset to the series and to the attributed data points xSeries->setPropertyValue( aOffsetPropName, uno::Any( fOffsetToSet )); // remove hard attributes from data points for( auto const & pointIndex : std::as_const(aAttributedDataPointIndexList) ) { uno::Reference< beans::XPropertyState > xPointState( xSeries->getDataPointByIndex( pointIndex ), uno::UNO_QUERY ); if( xPointState.is()) xPointState->setPropertyToDefault( aOffsetPropName ); } } } // line style DataSeriesHelper::setPropertyAlsoToAllAttributedDataPoints( xSeries, "BorderStyle", uno::Any( drawing::LineStyle_NONE ) ); // vary colors by point xSeries->setPropertyValue( "VaryColorsByPoint", uno::Any( true )); } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } } void PieChartTypeTemplate::resetStyles2( const rtl::Reference< ::chart::Diagram >& xDiagram ) { // reset axes and grids if( xDiagram.is()) { const std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq( xDiagram->getBaseCoordinateSystems()); ChartTypeTemplate::createAxes( aCooSysSeq ); //reset scale orientation for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysSeq ) { try { rtl::Reference< Axis > xAxis = AxisHelper::getAxis( 0 /*nDimensionIndex*/,0 /*nAxisIndex*/ , coords ); if( xAxis.is() ) { chart2::ScaleData aScaleData( xAxis->getScaleData() ); aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; xAxis->setScaleData( aScaleData ); } xAxis = AxisHelper::getAxis( 1, 0, coords ); if( xAxis.is() ) { chart2::ScaleData aScaleData( xAxis->getScaleData() ); aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; xAxis->setScaleData( aScaleData ); } } catch( const uno::Exception & ) { DBG_UNHANDLED_EXCEPTION("chart2"); } } } ChartTypeTemplate::resetStyles2( xDiagram ); // vary colors by point, // line style std::vector< rtl::Reference< DataSeries > > aSeriesVec = DiagramHelper::getDataSeriesFromDiagram( xDiagram ); uno::Any aLineStyleAny( drawing::LineStyle_NONE ); for (auto const& series : aSeriesVec) { series->setPropertyToDefault( "VaryColorsByPoint"); if( series->getPropertyValue( "BorderStyle") == aLineStyleAny ) { series->setPropertyToDefault( "BorderStyle"); } } //reset scene properties ThreeDHelper::setDefaultRotation( xDiagram, false ); } // ____ XChartTypeTemplate ____ void PieChartTypeTemplate::adaptDiagram( const rtl::Reference< ::chart::Diagram >& xDiagram ) { if( !xDiagram.is() ) return; //different default for scene geometry: ThreeDHelper::setDefaultRotation( xDiagram, true ); } IMPLEMENT_FORWARD_XINTERFACE2( PieChartTypeTemplate, ChartTypeTemplate, OPropertySet ) IMPLEMENT_FORWARD_XTYPEPROVIDER2( PieChartTypeTemplate, ChartTypeTemplate, OPropertySet ) } // namespace chart /* vim:set shiftwidth=4 softtabstop=4 expandtab: */