/* -*- 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 <drawingml/chart/typegroupconverter.hxx>
 
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
#include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
#include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
#include <com/sun/star/chart2/CurveStyle.hpp>
#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
#include <com/sun/star/chart2/StackingDirection.hpp>
#include <com/sun/star/chart2/Symbol.hpp>
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
#include <com/sun/star/chart2/XCoordinateSystem.hpp>
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
#include <com/sun/star/chart2/data/XDataSink.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <osl/diagnose.h>
#include <drawingml/lineproperties.hxx>
#include <drawingml/chart/seriesconverter.hxx>
#include <drawingml/chart/typegroupmodel.hxx>
#include <oox/core/xmlfilterbase.hxx>
#include <oox/helper/containerhelper.hxx>
#include <oox/token/namespaces.hxx>
#include <oox/token/properties.hxx>
#include <oox/token/tokens.hxx>
 
namespace oox {
namespace drawingml {
namespace chart {
 
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::chart2;
using namespace ::com::sun::star::chart2::data;
using namespace ::com::sun::star::uno;
 
namespace {
 
// chart type service names
const sal_Char SERVICE_CHART2_AREA[]      = "com.sun.star.chart2.AreaChartType";
const sal_Char SERVICE_CHART2_CANDLE[]    = "com.sun.star.chart2.CandleStickChartType";
const sal_Char SERVICE_CHART2_COLUMN[]    = "com.sun.star.chart2.ColumnChartType";
const sal_Char SERVICE_CHART2_LINE[]      = "com.sun.star.chart2.LineChartType";
const sal_Char SERVICE_CHART2_NET[]       = "com.sun.star.chart2.NetChartType";
const sal_Char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
const sal_Char SERVICE_CHART2_PIE[]       = "com.sun.star.chart2.PieChartType";
const sal_Char SERVICE_CHART2_SCATTER[]   = "com.sun.star.chart2.ScatterChartType";
const sal_Char SERVICE_CHART2_BUBBLE[]    = "com.sun.star.chart2.BubbleChartType";
const sal_Char SERVICE_CHART2_SURFACE[]   = "com.sun.star.chart2.ColumnChartType";    // Todo
 
namespace csscd = ::com::sun::star::chart::DataLabelPlacement;
 
static const TypeGroupInfo spTypeInfos[] =
{
    // type-id          type-category         service                   varied-point-color   default label pos     polar  area2d 1stvis xcateg swap   stack  revers picopt
    { TYPEID_BAR,       TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,    VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  false, true,  false, true  },
    { TYPEID_HORBAR,    TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,    VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  true,  true,  false, true  },
    { TYPEID_LINE,      TYPECATEGORY_LINE,    SERVICE_CHART2_LINE,      VARPOINTMODE_SINGLE, csscd::RIGHT,         false, false, false, true,  false, true,  false, false },
    { TYPEID_AREA,      TYPECATEGORY_LINE,    SERVICE_CHART2_AREA,      VARPOINTMODE_NONE,   csscd::CENTER,        false, true,  false, true,  false, true,  true,  false },
    { TYPEID_STOCK,     TYPECATEGORY_LINE,    SERVICE_CHART2_CANDLE,    VARPOINTMODE_NONE,   csscd::RIGHT,         false, false, false, true,  false, true,  false, false },
    { TYPEID_RADARLINE, TYPECATEGORY_RADAR,   SERVICE_CHART2_NET,       VARPOINTMODE_SINGLE, csscd::TOP,           true,  false, false, true,  false, false, false, false },
    { TYPEID_RADARAREA, TYPECATEGORY_RADAR,   SERVICE_CHART2_FILLEDNET, VARPOINTMODE_NONE,   csscd::TOP,           true,  true,  false, true,  false, false, true,  false },
    { TYPEID_PIE,       TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  true,  true,  false, false, false, false },
    { TYPEID_DOUGHNUT,  TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  false, true,  false, false, false, false },
    { TYPEID_OFPIE,     TYPECATEGORY_PIE,     SERVICE_CHART2_PIE,       VARPOINTMODE_MULTI,  csscd::AVOID_OVERLAP, true,  true,  true,  true,  false, false, false, false },
    { TYPEID_SCATTER,   TYPECATEGORY_SCATTER, SERVICE_CHART2_SCATTER,   VARPOINTMODE_SINGLE, csscd::RIGHT,         false, false, false, false, false, false, false, false },
    { TYPEID_BUBBLE,    TYPECATEGORY_SCATTER, SERVICE_CHART2_BUBBLE,    VARPOINTMODE_SINGLE, csscd::RIGHT,         false, true,  false, false, false, false, false, false },
    { TYPEID_SURFACE,   TYPECATEGORY_SURFACE, SERVICE_CHART2_SURFACE,   VARPOINTMODE_NONE,   csscd::RIGHT,         false, true,  false, true,  false, false, false, false }
};
 
static const TypeGroupInfo saUnknownTypeInfo =
    { TYPEID_UNKNOWN,   TYPECATEGORY_BAR,     SERVICE_CHART2_COLUMN,  VARPOINTMODE_SINGLE, csscd::OUTSIDE,       false, true,  false, true,  false, true,  false, true  };
 
const TypeGroupInfo& lclGetTypeInfoFromTypeId( TypeId eTypeId )
{
    for( auto const &rIt : spTypeInfos)
    {
        if( rIt.meTypeId == eTypeId )
            return rIt;
    }
    OSL_ENSURE( eTypeId == TYPEID_UNKNOWN, "lclGetTypeInfoFromTypeId - unexpected chart type identifier" );
    return saUnknownTypeInfo;
}
 
} // namespace
 
const TypeGroupInfo& GetTypeGroupInfo( TypeId eType )
{
    return lclGetTypeInfoFromTypeId(eType);
}
 
UpDownBarsConverter::UpDownBarsConverter( const ConverterRoot& rParent, UpDownBarsModel& rModel ) :
    ConverterBase< UpDownBarsModel >( rParent, rModel )
{
}
 
UpDownBarsConverter::~UpDownBarsConverter()
{
}
 
void UpDownBarsConverter::convertFromModel( const Reference< XChartType >& rxChartType )
{
    PropertySet aTypeProp( rxChartType );
 
    // upbar format
    Reference< XPropertySet > xWhitePropSet;
    if( aTypeProp.getProperty( xWhitePropSet, PROP_WhiteDay ) )
    {
        PropertySet aPropSet( xWhitePropSet );
        getFormatter().convertFrameFormatting( aPropSet, mrModel.mxUpBars, OBJECTTYPE_UPBAR );
    }
 
    // downbar format
    Reference< XPropertySet > xBlackPropSet;
    if( aTypeProp.getProperty( xBlackPropSet, PROP_BlackDay ) )
    {
        PropertySet aPropSet( xBlackPropSet );
        getFormatter().convertFrameFormatting( aPropSet, mrModel.mxDownBars, OBJECTTYPE_DOWNBAR );
    }
}
 
TypeGroupConverter::TypeGroupConverter( const ConverterRoot& rParent, TypeGroupModel& rModel ) :
    ConverterBase< TypeGroupModel >( rParent, rModel ),
    mb3dChart( false )
{
    TypeId eTypeId = TYPEID_UNKNOWN;
    switch( mrModel.mnTypeId )
    {
#define ENSURE_AXESCOUNT( min, max ) OSL_ENSURE( (min <= static_cast<int>(mrModel.maAxisIds.size())) && (static_cast<int>(mrModel.maAxisIds.size()) <= max), "TypeGroupConverter::TypeGroupConverter - invalid axes count" )
        case C_TOKEN( area3DChart ):    ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_AREA;      mb3dChart = true;   break;
        case C_TOKEN( areaChart ):      ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_AREA;      mb3dChart = false;  break;
        case C_TOKEN( bar3DChart ):     ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_BAR;       mb3dChart = true;   break;
        case C_TOKEN( barChart ):       ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BAR;       mb3dChart = false;  break;
        case C_TOKEN( bubbleChart ):    ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BUBBLE;    mb3dChart = false;  break;
        case C_TOKEN( doughnutChart ):  ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_DOUGHNUT;  mb3dChart = false;  break;
        case C_TOKEN( line3DChart ):    ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_LINE;      mb3dChart = true;   break;
        case C_TOKEN( lineChart ):      ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_LINE;      mb3dChart = false;  break;
        case C_TOKEN( ofPieChart ):     ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_OFPIE;     mb3dChart = false;  break;
        case C_TOKEN( pie3DChart ):     ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE;       mb3dChart = true;   break;
        case C_TOKEN( pieChart ):       ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE;       mb3dChart = false;  break;
        case C_TOKEN( radarChart ):     ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_RADARLINE; mb3dChart = false;  break;
        case C_TOKEN( scatterChart ):   ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_SCATTER;   mb3dChart = false;  break;
        case C_TOKEN( stockChart ):     ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_STOCK;     mb3dChart = false;  break;
        case C_TOKEN( surface3DChart ): ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_SURFACE;   mb3dChart = true;   break;
        case C_TOKEN( surfaceChart ):   ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_SURFACE;   mb3dChart = true;   break;    // 3D bar chart from all surface charts
        default:    OSL_FAIL( "TypeGroupConverter::TypeGroupConverter - unknown chart type" );
#undef ENSURE_AXESCOUNT
    }
 
    // special handling for some chart types
    switch( eTypeId )
    {
        case TYPEID_BAR:
            if( mrModel.mnBarDir == XML_bar )
                eTypeId = TYPEID_HORBAR;
        break;
        case TYPEID_RADARLINE:
            if( mrModel.mnRadarStyle == XML_filled )
                eTypeId = TYPEID_RADARAREA;
        break;
        case TYPEID_SURFACE:
            // create a deep 3D bar chart from surface charts
            mrModel.mnGrouping = XML_standard;
        break;
        default:;
    }
 
    // set the chart type info struct for the current chart type
    maTypeInfo = lclGetTypeInfoFromTypeId( eTypeId );
}
 
TypeGroupConverter::~TypeGroupConverter()
{
}
 
bool TypeGroupConverter::isStacked() const
{
    return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_stacked);
}
 
bool TypeGroupConverter::isPercent() const
{
    return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_percentStacked);
}
 
bool TypeGroupConverter::isWall3dChart() const
{
    return mb3dChart && (maTypeInfo.meTypeCategory != TYPECATEGORY_PIE);
}
 
bool TypeGroupConverter::isDeep3dChart() const
{
    return isWall3dChart() && (mrModel.mnGrouping == XML_standard);
}
 
bool TypeGroupConverter::isSeriesFrameFormat() const
{
    return mb3dChart || maTypeInfo.mbSeriesIsFrame2d;
}
 
ObjectType TypeGroupConverter::getSeriesObjectType() const
{
    return mb3dChart ? OBJECTTYPE_FILLEDSERIES3D :
        (maTypeInfo.mbSeriesIsFrame2d ? OBJECTTYPE_FILLEDSERIES2D : OBJECTTYPE_LINEARSERIES2D);
}
 
OUString TypeGroupConverter::getSingleSeriesTitle() const
{
    OUString aSeriesTitle;
    if( !mrModel.maSeries.empty() && (maTypeInfo.mbSingleSeriesVis || (mrModel.maSeries.size() == 1)) )
        if( const TextModel* pText = mrModel.maSeries.front()->mxText.get() )
            if( const DataSequenceModel* pDataSeq = pText->mxDataSeq.get() )
                if( !pDataSeq->maData.empty() )
                    pDataSeq->maData.begin()->second >>= aSeriesTitle;
    return aSeriesTitle;
}
 
Reference< XCoordinateSystem > TypeGroupConverter::createCoordinateSystem()
{
    // create the coordinate system object
    Reference< css::uno::XComponentContext > xContext = getComponentContext();
    Reference< XCoordinateSystem > xCoordSystem;
    if( maTypeInfo.mbPolarCoordSystem )
    {
        if( mb3dChart )
            xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
        else
            xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
    }
    else
    {
        if( mb3dChart )
            xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
        else
            xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
    }
 
    // swap X and Y axis
    if( maTypeInfo.mbSwappedAxesSet )
    {
        PropertySet aPropSet( xCoordSystem );
        aPropSet.setProperty( PROP_SwapXAndYAxis, true );
    }
 
    return xCoordSystem;
}
 
Reference< XLabeledDataSequence > TypeGroupConverter::createCategorySequence()
{
    sal_Int32 nMaxValues = 0;
    Reference< XLabeledDataSequence > xLabeledSeq;
    /*  Find first existing category sequence. The behaviour of Excel 2007 is
        different to Excel 2003, which always used the category sequence of the
        first series, even if it was empty. */
    for (auto const& elem : mrModel.maSeries)
    {
        if( elem->maSources.has( SeriesModel::CATEGORIES ) )
        {
            SeriesConverter aSeriesConv(*this, *elem);
            xLabeledSeq = aSeriesConv.createCategorySequence( "categories" );
            if (xLabeledSeq.is())
                break;
        }
        else if( nMaxValues <= 0 && elem->maSources.has( SeriesModel::VALUES ) )
        {
            DataSourceModel *pValues = elem->maSources.get( SeriesModel::VALUES ).get();
            if( pValues->mxDataSeq.is() )
                nMaxValues = pValues->mxDataSeq.get()->maData.size();
        }
    }
    /* n#839727 Create Category Sequence when none are found */
    if( !xLabeledSeq.is() && mrModel.maSeries.size() > 0 ) {
        if( nMaxValues < 0 )
            nMaxValues = 2;
        SeriesModel &aModel = *mrModel.maSeries.get(0);
        DataSourceModel &aSrc = aModel.maSources.create( SeriesModel::CATEGORIES );
        DataSequenceModel &aSeq = aSrc.mxDataSeq.create();
        aSeq.mnPointCount = nMaxValues;
        for( sal_Int32 i = 0; i < nMaxValues; i++ )
            aSeq.maData[ i ] <<= OUString::number( i + 1 );
        SeriesConverter aSeriesConv( *this,  aModel );
        xLabeledSeq = aSeriesConv.createCategorySequence( "categories" );
    }
    return xLabeledSeq;
}
 
void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
        const Reference< XCoordinateSystem >& rxCoordSystem,
        sal_Int32 nAxesSetIdx, bool bSupportsVaryColorsByPoint )
{
    try
    {
        // create the chart type object
        OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
        Reference< XChartType > xChartType( createInstance( aService ), UNO_QUERY_THROW );
 
        // additional properties
        PropertySet aDiaProp( rxDiagram );
        PropertySet aTypeProp( xChartType );
        switch( maTypeInfo.meTypeCategory )
        {
            case TYPECATEGORY_BAR:
            {
                Sequence< sal_Int32 > aInt32Seq( 2 );
                aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = mrModel.mnOverlap;
                aTypeProp.setProperty( PROP_OverlapSequence, aInt32Seq );
                aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = mrModel.mnGapWidth;
                aTypeProp.setProperty( PROP_GapwidthSequence, aInt32Seq );
            }
            break;
            case TYPECATEGORY_PIE:
            {
                aTypeProp.setProperty( PROP_UseRings, maTypeInfo.meTypeId == TYPEID_DOUGHNUT );
                /*  #i85166# starting angle of first pie slice. 3D pie charts
                    use Y rotation setting in view3D element. Of-pie charts do
                    not support pie rotation. */
                if( !is3dChart() && (maTypeInfo.meTypeId != TYPEID_OFPIE) )
                    convertPieRotation( aDiaProp, mrModel.mnFirstAngle );
            }
            break;
            default:;
        }
 
        // create converter objects for all series models
        typedef RefVector< SeriesConverter > SeriesConvVector;
        SeriesConvVector aSeries;
        for (auto const& elemSeries : mrModel.maSeries)
            aSeries.push_back( std::make_shared<SeriesConverter>(*this, *elemSeries) );
 
        // reverse series order for some unstacked 2D chart types
        if( maTypeInfo.mbReverseSeries && !mb3dChart && !isStacked() && !isPercent() )
            ::std::reverse( aSeries.begin(), aSeries.end() );
 
        // decide whether to use varying colors for each data point
        bool bVaryColorsByPoint = bSupportsVaryColorsByPoint && mrModel.mbVaryColors;
        switch( maTypeInfo.meVarPointMode )
        {
            case VARPOINTMODE_NONE:     bVaryColorsByPoint = false;                             break;
            case VARPOINTMODE_SINGLE:   bVaryColorsByPoint &= (mrModel.maSeries.size() == 1);   break;
            case VARPOINTMODE_MULTI:                                                            break;
        }
 
        /*  Stock chart needs special processing. Create one 'big' series with
            data sequences of different roles. */
        if( maTypeInfo.meTypeId == TYPEID_STOCK )
        {
            // create the data series object
            Reference< XDataSeries > xDataSeries( createInstance( "com.sun.star.chart2.DataSeries" ), UNO_QUERY );
            Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
            if( xDataSink.is() )
            {
                // create a list of data sequences from all series
                ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
                OSL_ENSURE( aSeries.size() >= 3, "TypeGroupConverter::convertFromModel - too few stock chart series" );
                int nRoleIdx = (aSeries.size() == 3) ? 1 : 0;
                for( SeriesConvVector::iterator aIt = aSeries.begin(), aEnd = aSeries.end(); (nRoleIdx < 4) && (aIt != aEnd); ++nRoleIdx, ++aIt )
                {
                    // create a data sequence with a specific role
                    OUString aRole;
                    switch( nRoleIdx )
                    {
                        case 0: aRole = "values-first";  break;
                        case 1: aRole = "values-max";    break;
                        case 2: aRole = "values-min";    break;
                        case 3: aRole = "values-last";   break;
                    }
                    Reference< XLabeledDataSequence > xDataSeq = (*aIt)->createValueSequence( aRole );
                    if( xDataSeq.is() )
                        aLabeledSeqVec.push_back( xDataSeq );
                }
 
                // attach labeled data sequences to series and insert series into chart type
                xDataSink->setData( ContainerHelper::vectorToSequence( aLabeledSeqVec ) );
 
                // formatting of high/low lines
                aTypeProp.setProperty( PROP_ShowHighLow, true );
                PropertySet aSeriesProp( xDataSeries );
                if( mrModel.mxHiLowLines.is() )
                    getFormatter().convertFrameFormatting( aSeriesProp, mrModel.mxHiLowLines, OBJECTTYPE_HILOLINE );
                else
                    // hi/low-lines cannot be switched off via "ShowHighLow" property (?)
                    aSeriesProp.setProperty( PROP_LineStyle, css::drawing::LineStyle_NONE );
 
                // formatting of up/down bars
                bool bUpDownBars = mrModel.mxUpDownBars.is();
                aTypeProp.setProperty( PROP_Japanese, bUpDownBars );
                aTypeProp.setProperty( PROP_ShowFirst, bUpDownBars );
                if( bUpDownBars )
                {
                    UpDownBarsConverter aUpDownConv( *this, *mrModel.mxUpDownBars );
                    aUpDownConv.convertFromModel( xChartType );
                }
 
                // insert the series into the chart type object
                insertDataSeries( xChartType, xDataSeries, nAxesSetIdx );
            }
        }
        else
        {
            for (auto const& elem : aSeries)
            {
                SeriesConverter& rSeriesConv = *elem;
                Reference< XDataSeries > xDataSeries = rSeriesConv.createDataSeries( *this, bVaryColorsByPoint );
                insertDataSeries( xChartType, xDataSeries, nAxesSetIdx );
 
                /*  Excel does not use the value of the c:smooth element of the
                    chart type to set a default line smoothing for the data
                    series. Line smoothing is always controlled by the c:smooth
                    element of the respective data series. If the element in the
                    data series is missing, line smoothing is off, regardless of
                    the c:smooth element of the chart type. */
#if !OOX_CHART_SMOOTHED_PER_SERIES
                if( rSeriesConv.getModel().mbSmooth )
                    convertLineSmooth( aTypeProp, true );
#endif
            }
        }
 
        // add chart type object to coordinate system
        Reference< XChartTypeContainer > xChartTypeCont( rxCoordSystem, UNO_QUERY_THROW );
        xChartTypeCont->addChartType( xChartType );
 
        // set existence of bar connector lines at diagram (only in stacked 2D bar charts)
        if( mrModel.mxSerLines.is() && !mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR) && (isStacked() || isPercent()) )
            aDiaProp.setProperty( PROP_ConnectBars, true );
    }
    catch( Exception& )
    {
        OSL_FAIL( "TypeGroupConverter::convertFromModel - cannot add chart type" );
    }
}
 
void TypeGroupConverter::convertMarker( PropertySet& rPropSet, sal_Int32 nOoxSymbol, sal_Int32 nOoxSize,
       const ModelRef< Shape >& xShapeProps ) const
{
    if( !isSeriesFrameFormat() )
    {
        namespace cssc = ::com::sun::star::chart2;
 
        // symbol style
        cssc::Symbol aSymbol;
        aSymbol.Style = cssc::SymbolStyle_STANDARD;
        switch( nOoxSymbol ) // compare with XclChPropSetHelper::WriteMarkerProperties in xlchart.cxx
        {
            case XML_auto:      aSymbol.Style = cssc::SymbolStyle_AUTO; break;
            case XML_none:      aSymbol.Style = cssc::SymbolStyle_NONE; break;
            case XML_square:    aSymbol.StandardSymbol = 0;             break;  // square
            case XML_diamond:   aSymbol.StandardSymbol = 1;             break;  // diamond
            case XML_triangle:  aSymbol.StandardSymbol = 3;             break;  // arrow up
            case XML_x:         aSymbol.StandardSymbol = 10;            break;  // X, legacy bow tie
            case XML_star:      aSymbol.StandardSymbol = 12;            break;  // asterisk, legacy sand glass
            case XML_dot:       aSymbol.StandardSymbol = 4;             break;  // arrow right
            case XML_dash:      aSymbol.StandardSymbol = 13;            break;  // horizontal bar, legacy arrow down
            case XML_circle:    aSymbol.StandardSymbol = 8;             break;  // circle, legacy arrow right
            case XML_plus:      aSymbol.StandardSymbol = 11;            break;  // plus, legacy arrow left
        }
 
        // symbol size (points in OOXML, 1/100 mm in Chart2)
        sal_Int32 nSize = static_cast< sal_Int32 >( nOoxSize * (2540.0 / 72.0) + 0.5 );
        aSymbol.Size.Width = aSymbol.Size.Height = nSize;
 
        if(xShapeProps.is())
        {
            Color aFillColor = xShapeProps->getFillProperties().maFillColor;
            aSymbol.FillColor = sal_Int32(aFillColor.getColor(getFilter().getGraphicHelper()));
            rPropSet.setProperty(PROP_Color, aSymbol.FillColor);
        }
 
        // set the property
        rPropSet.setProperty( PROP_Symbol, aSymbol );
    }
}
 
void TypeGroupConverter::convertLineSmooth( PropertySet& rPropSet, bool bOoxSmooth ) const
{
    if( !isSeriesFrameFormat() && (maTypeInfo.meTypeCategory != TYPECATEGORY_RADAR) )
    {
        namespace cssc = ::com::sun::star::chart2;
        cssc::CurveStyle eCurveStyle = bOoxSmooth ? cssc::CurveStyle_CUBIC_SPLINES : cssc::CurveStyle_LINES;
        rPropSet.setProperty( PROP_CurveStyle, eCurveStyle );
    }
}
 
void TypeGroupConverter::convertBarGeometry( PropertySet& rPropSet, sal_Int32 nOoxShape ) const
{
    if( mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR) )
    {
        namespace cssc = ::com::sun::star::chart2;
 
        sal_Int32 nGeom3d = cssc::DataPointGeometry3D::CUBOID;
        switch( nOoxShape )
        {
            case XML_box:           nGeom3d = cssc::DataPointGeometry3D::CUBOID;    break;
            case XML_cone:          nGeom3d = cssc::DataPointGeometry3D::CONE;      break;
            case XML_coneToMax:     nGeom3d = cssc::DataPointGeometry3D::CONE;      break;
            case XML_cylinder:      nGeom3d = cssc::DataPointGeometry3D::CYLINDER;  break;
            case XML_pyramid:       nGeom3d = cssc::DataPointGeometry3D::PYRAMID;   break;
            case XML_pyramidToMax:  nGeom3d = cssc::DataPointGeometry3D::PYRAMID;   break;
            default:                OSL_FAIL( "TypeGroupConverter::convertBarGeometry - unknown 3D bar shape type" );
        }
        rPropSet.setProperty( PROP_Geometry3D, nGeom3d );
    }
}
 
void TypeGroupConverter::convertPieRotation( PropertySet& rPropSet, sal_Int32 nOoxAngle ) const
{
    if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
    {
        // map OOXML [0,360] clockwise (0deg top) to Chart2 counterclockwise (0deg left)
        sal_Int32 nAngle = (450 - nOoxAngle) % 360;
        rPropSet.setProperty( PROP_StartingAngle, nAngle );
    }
}
 
void TypeGroupConverter::convertPieExplosion( PropertySet& rPropSet, sal_Int32 nOoxExplosion ) const
{
    if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
    {
        // pie explosion restricted to 100% in Chart2, set as double in range [0,1]
        double fOffset = getLimitedValue< double >( nOoxExplosion / 100.0, 0.0, 1.0 );
        rPropSet.setProperty( PROP_Offset, fOffset );
    }
}
 
// private --------------------------------------------------------------------
 
void TypeGroupConverter::insertDataSeries( const Reference< XChartType >& rxChartType, const Reference< XDataSeries >& rxSeries, sal_Int32 nAxesSetIdx )
{
    if( rxSeries.is() )
    {
        PropertySet aSeriesProp( rxSeries );
 
        // series stacking mode
        namespace cssc = ::com::sun::star::chart2;
        cssc::StackingDirection eStacking = cssc::StackingDirection_NO_STACKING;
        // stacked overrides deep-3d
        if( isStacked() || isPercent() )
            eStacking = cssc::StackingDirection_Y_STACKING;
        else if( isDeep3dChart() )
            eStacking = cssc::StackingDirection_Z_STACKING;
        aSeriesProp.setProperty( PROP_StackingDirection, eStacking );
 
        // additional series properties
        aSeriesProp.setProperty( PROP_AttachedAxisIndex, nAxesSetIdx );
 
        // insert series into container
        try
        {
            Reference< XDataSeriesContainer > xSeriesCont( rxChartType, UNO_QUERY_THROW );
            xSeriesCont->addDataSeries( rxSeries );
        }
        catch( Exception& )
        {
            OSL_FAIL( "TypeGroupConverter::insertDataSeries - cannot add data series" );
        }
    }
}
 
} // namespace chart
} // namespace drawingml
} // namespace oox
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V601 The bool type is implicitly cast to the class type. Inspect the second argument.

V601 The 'true' value becomes a class object. Inspect the second argument.

V601 The bool type is implicitly cast to the class type. Inspect the second argument.

V601 The bool type is implicitly cast to the class type. Inspect the second argument.

V601 The 'true' value becomes a class object. Inspect the second argument.

V601 The 'true' value becomes a class object. Inspect the second argument.