diff options
Diffstat (limited to 'sc/source/filter/excel/xechart.cxx')
-rw-r--r-- | sc/source/filter/excel/xechart.cxx | 3303 |
1 files changed, 3303 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xechart.cxx b/sc/source/filter/excel/xechart.cxx new file mode 100644 index 000000000000..b6b136da4b1e --- /dev/null +++ b/sc/source/filter/excel/xechart.cxx @@ -0,0 +1,3303 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "xechart.hxx" + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisPosition.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/MissingValueTreatment.hpp> +#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/XDiagram.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/XTitled.hpp> +#include <com/sun/star/chart2/XColorScheme.hpp> +#include <com/sun/star/chart2/data/XDataSource.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/CurveStyle.hpp> +#include <com/sun/star/chart2/DataPointGeometry3D.hpp> +#include <com/sun/star/chart2/DataPointLabel.hpp> +#include <com/sun/star/chart2/LegendExpansion.hpp> +#include <com/sun/star/chart2/LegendPosition.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart2/StackingDirection.hpp> +#include <com/sun/star/chart2/TickmarkStyle.hpp> + +#include <vcl/outdev.hxx> +#include <filter/msfilter/escherex.hxx> + +#include "document.hxx" +#include "rangelst.hxx" +#include "rangeutl.hxx" +#include "compiler.hxx" +#include "tokenarray.hxx" +#include "token.hxx" +#include "xeescher.hxx" +#include "xeformula.hxx" +#include "xehelper.hxx" +#include "xepage.hxx" +#include "xestyle.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::i18n::XBreakIterator; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; + +using ::com::sun::star::chart2::IncrementData; +using ::com::sun::star::chart2::RelativePosition; +using ::com::sun::star::chart2::ScaleData; +using ::com::sun::star::chart2::SubIncrement; +using ::com::sun::star::chart2::XAxis; +using ::com::sun::star::chart2::XChartDocument; +using ::com::sun::star::chart2::XChartTypeContainer; +using ::com::sun::star::chart2::XColorScheme; +using ::com::sun::star::chart2::XCoordinateSystem; +using ::com::sun::star::chart2::XCoordinateSystemContainer; +using ::com::sun::star::chart2::XChartType; +using ::com::sun::star::chart2::XDataSeries; +using ::com::sun::star::chart2::XDataSeriesContainer; +using ::com::sun::star::chart2::XDiagram; +using ::com::sun::star::chart2::XFormattedString; +using ::com::sun::star::chart2::XLegend; +using ::com::sun::star::chart2::XRegressionCurve; +using ::com::sun::star::chart2::XRegressionCurveContainer; +using ::com::sun::star::chart2::XScaling; +using ::com::sun::star::chart2::XTitle; +using ::com::sun::star::chart2::XTitled; + +using ::com::sun::star::chart2::data::XDataSequence; +using ::com::sun::star::chart2::data::XDataSource; +using ::com::sun::star::chart2::data::XLabeledDataSequence; + +using ::formula::FormulaGrammar; +using ::formula::FormulaToken; + +namespace cssc = ::com::sun::star::chart; +namespace cssc2 = ::com::sun::star::chart2; + +// Helpers ==================================================================== + +namespace { + +XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect ) +{ + return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight; +} + +inline void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef xRec ) +{ + if( xRec.is() ) + xRec->Save( rStrm ); +} + +/** Saves the passed record (group) together with a leading value record. */ +template< typename Type > +void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef xRec, sal_uInt16 nRecId, Type nValue ) +{ + if( xRec.is() ) + { + XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm ); + xRec->Save( rStrm ); + } +} + +void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin ) +{ + sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND; + rStrm.StartRecord( nRecId, 12 ); + rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2; + rStrm.EndRecord(); +} + +template< typename Type > +inline bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny ) +{ + return !rAny.hasValue() || !(rAny >>= rValue); +} + +bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale ) +{ + bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny ); + if( !bIsAuto && bLogScale ) + rfValue = log( rfValue ) / log( 10.0 ); + return bIsAuto; +} + +} // namespace + +// Common ===================================================================== + +/** Stores global data needed in various classes of the Chart export filter. */ +struct XclExpChRootData : public XclChRootData +{ + typedef ::std::vector< XclChFrBlock > XclChFrBlockVector; + + XclExpChChart& mrChartData; /// The chart data object. + XclChFrBlockVector maWrittenFrBlocks; /// Stack of future record levels already written out. + XclChFrBlockVector maUnwrittenFrBlocks; /// Stack of future record levels not yet written out. + + inline explicit XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {} + + /** Registers a new future record level. */ + void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ); + /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */ + void InitializeFutureRecBlock( XclExpStream& rStrm ); + /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */ + void FinalizeFutureRecBlock( XclExpStream& rStrm ); +}; + +// ---------------------------------------------------------------------------- + +void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ) +{ + maUnwrittenFrBlocks.push_back( rFrBlock ); +} + +void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm ) +{ + // first call from a future record writes all missing CHFRBLOCKBEGIN records + if( !maUnwrittenFrBlocks.empty() ) + { + // write the leading CHFRINFO record + if( maWrittenFrBlocks.empty() ) + { + rStrm.StartRecord( EXC_ID_CHFRINFO, 20 ); + rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 ); + rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B ); + rStrm.EndRecord(); + } + // write all unwritten CHFRBLOCKBEGIN records + for( XclChFrBlockVector::const_iterator aIt = maUnwrittenFrBlocks.begin(), aEnd = maUnwrittenFrBlocks.end(); aIt != aEnd; ++aIt ) + { + DBG_ASSERT( aIt->mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" ); + lclWriteChFrBlockRecord( rStrm, *aIt, true ); + } + // move all record infos to vector of written blocks + maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() ); + maUnwrittenFrBlocks.clear(); + } +} + +void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm ) +{ + DBG_ASSERT( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" ); + if( !maUnwrittenFrBlocks.empty() ) + { + // no future record has been written, just forget the topmost level + maUnwrittenFrBlocks.pop_back(); + } + else if( !maWrittenFrBlocks.empty() ) + { + // write the CHFRBLOCKEND record for the topmost block and delete it + lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false ); + maWrittenFrBlocks.pop_back(); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) : + XclExpRoot( rRoot ), + mxChData( new XclExpChRootData( rChartData ) ) +{ +} + +XclExpChRoot::~XclExpChRoot() +{ +} + +Reference< XChartDocument > XclExpChRoot::GetChartDocument() const +{ + return mxChData->mxChartDoc; +} + +XclExpChChart& XclExpChRoot::GetChartData() const +{ + return mxChData->mrChartData; +} + +const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfo( eType ); +} + +const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( const OUString& rServiceName ) const +{ + return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName ); +} + +const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const +{ + return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType ); +} + +void XclExpChRoot::InitConversion( XChartDocRef xChartDoc, const Rectangle& rChartRect ) const +{ + mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect ); +} + +void XclExpChRoot::FinishConversion() const +{ + mxChData->FinishConversion(); +} + +bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const +{ + XclExpPalette& rPal = GetPalette(); + return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx )); +} + +void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const +{ + DBG_ASSERT( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" ); + rColor = GetPalette().GetDefColor( nSysColorIdx ); + rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx ); +} + +sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const +{ + return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS ); +} + +sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const +{ + return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS ); +} + +XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const ::com::sun::star::awt::Rectangle& rRect ) const +{ + XclChRectangle aRect; + aRect.mnX = CalcChartXFromHmm( rRect.X ); + aRect.mnY = CalcChartYFromHmm( rRect.Y ); + aRect.mnWidth = CalcChartXFromHmm( rRect.Width ); + aRect.mnHeight = CalcChartYFromHmm( rRect.Height ); + return aRect; +} + +sal_Int32 XclExpChRoot::CalcChartXFromRelative( double fPosX ) const +{ + return CalcChartXFromHmm( static_cast< sal_Int32 >( fPosX * mxChData->maChartRect.GetWidth() + 0.5 ) ); +} + +sal_Int32 XclExpChRoot::CalcChartYFromRelative( double fPosY ) const +{ + return CalcChartYFromHmm( static_cast< sal_Int32 >( fPosY * mxChData->maChartRect.GetHeight() + 0.5 ) ); +} + +void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().ReadLineProperties( + rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode ); +} + +bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode ); +} + +void XclExpChRoot::ConvertEscherFormat( + XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt, + const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const +{ + GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt, + *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode ); +} + +sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const +{ + XclFontData aFontData; + GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript ); + return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT ); +} + +sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet ) +{ + sal_Int32 nApiRot = 0; + rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE ); + return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 ); +} + +void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock ) +{ + mxChData->RegisterFutureRecBlock( rFrBlock ); +} + +void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm ) +{ + mxChData->InitializeFutureRecBlock( rStrm ); +} + +void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm ) +{ + mxChData->FinalizeFutureRecBlock( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot, + sal_uInt16 nFrType, sal_uInt16 nRecId, sal_Size nRecSize ) : + XclExpRecord( nRecId, nRecSize ), + XclExpChRoot( rRoot ), + maFrBlock( nFrType ) +{ +} + +XclExpChGroupBase::~XclExpChGroupBase() +{ +} + +void XclExpChGroupBase::Save( XclExpStream& rStrm ) +{ + // header record + XclExpRecord::Save( rStrm ); + // group records + if( HasSubRecords() ) + { + // register the future record context corresponding to this record group + RegisterFutureRecBlock( maFrBlock ); + // CHBEGIN record + XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm ); + // embedded records + WriteSubRecords( rStrm ); + // finalize the future records, must be done before the closing CHEND + FinalizeFutureRecBlock( rStrm ); + // CHEND record + XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm ); + } +} + +bool XclExpChGroupBase::HasSubRecords() const +{ + return true; +} + +void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 ) +{ + maFrBlock.mnContext = nFrContext; + maFrBlock.mnValue1 = nFrValue1; + maFrBlock.mnValue2 = nFrValue2; +} + +// ---------------------------------------------------------------------------- + +XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot, + XclFutureRecType eRecType, sal_uInt16 nRecId, sal_Size nRecSize ) : + XclExpFutureRecord( eRecType, nRecId, nRecSize ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChFutureRecordBase::Save( XclExpStream& rStrm ) +{ + InitializeFutureRecBlock( rStrm ); + XclExpFutureRecord::Save( rStrm ); +} + +// Frame formatting =========================================================== + +XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode, sal_uInt16 nBRMode ) : + XclExpRecord( EXC_ID_CHFRAMEPOS, 20 ) +{ + maData.mnTLMode = nTLMode; + maData.mnBRMode = nBRMode; +} + +void XclExpChFramePos::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect; +} + +// ---------------------------------------------------------------------------- + +XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ), + mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType ) +{ + switch( eDefFrameType ) + { + case EXC_CHFRAMETYPE_AUTO: + SetAuto( true ); + break; + case EXC_CHFRAMETYPE_INVISIBLE: + SetAuto( false ); + maData.mnPattern = EXC_CHLINEFORMAT_NONE; + break; + default: + DBG_ERRORFILE( "XclExpChLineFormat::SetDefault - unknown frame type" ); + } +} + +void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode ); + if( HasLine() ) + { + // detect system color, set color identifier (TODO: detect automatic series line) + if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) ) + { + // store color index from automatic format data + mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx ); + // try to set automatic mode + bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight); + ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto ); + } + else + { + // user defined color - register in palette + mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE ); + } + } + else + { + // no line - set default system color + rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT ); + } +} + +bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const +{ + return + ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) || + ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto()); +} + +void XclExpChLineFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId ); +} + +namespace { + +/** Creates a CHLINEFORMAT record from the passed property set. */ +XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + XclExpChLineFormatRef xLineFmt( new XclExpChLineFormat( rRoot ) ); + xLineFmt->Convert( rRoot, rPropSet, eObjType ); + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) ) + xLineFmt.reset(); + return xLineFmt; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ), + mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ), + mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType ); + bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode ); + if( HasArea() ) + { + bool bSolid = maData.mnPattern == EXC_PATT_SOLID; + // detect system color, set color identifier (TODO: detect automatic series area) + if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) ) + { + // store color index from automatic format data + mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx ); + // set automatic mode + ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid ); + } + else + { + // user defined color - register color in palette + mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA ); + } + // background color (default system color for solid fills) + if( bSolid ) + rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT ); + else + mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA ); + } + else + { + // no area - set default system colors + rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK ); + rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT ); + } + return bComplexFill; +} + +void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType ) +{ + switch( eDefFrameType ) + { + case EXC_CHFRAMETYPE_AUTO: + SetAuto( true ); + break; + case EXC_CHFRAMETYPE_INVISIBLE: + SetAuto( false ); + maData.mnPattern = EXC_PATT_NONE; + break; + default: + DBG_ERRORFILE( "XclExpChAreaFormat::SetDefault - unknown frame type" ); + } +} + +bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const +{ + return + ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) || + ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto()); +} + +void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + const XclExpPalette& rPal = rStrm.GetRoot().GetPalette(); + rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ), + mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ), + mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ) +{ + DBG_ASSERT_BIFF( GetBiff() == EXC_BIFF8 ); +} + +void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType ); + ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode ); + // register colors in palette + mnColor1Id = RegisterColor( ESCHER_Prop_fillColor ); + mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor ); +} + +bool XclExpChEscherFormat::IsValid() const +{ + return maData.mxEscherSet.is(); +} + +void XclExpChEscherFormat::Save( XclExpStream& rStrm ) +{ + if( maData.mxEscherSet.is() ) + { + // replace RGB colors with palette indexes in the Escher container + const XclExpPalette& rPal = GetPalette(); + maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) ); + maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) ); + + // save the record group + XclExpChGroupBase::Save( rStrm ); + } +} + +bool XclExpChEscherFormat::HasSubRecords() const +{ + // no subrecords for gradients + return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE; +} + +void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm ) +{ + rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 ); + rStrm << maPicFmt.mnBmpMode << maPicFmt.mnFormat << maPicFmt.mnFlags << maPicFmt.mfScale; + rStrm.EndRecord(); +} + +sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId ) +{ + sal_uInt32 nBGRValue; + if( maData.mxEscherSet.is() && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) ) + { + // swap red and blue + Color aColor( RGB_COLORDATA( + COLORDATA_BLUE( nBGRValue ), + COLORDATA_GREEN( nBGRValue ), + COLORDATA_RED( nBGRValue ) ) ); + return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA ); + } + return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ); +} + +void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm ) +{ + DBG_ASSERT( maData.mxEscherSet.is(), "XclExpChEscherFormat::WriteBody - missing property container" ); + // write Escher property container via temporary memory stream + SvMemoryStream aMemStrm; + maData.mxEscherSet->Commit( aMemStrm ); + aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); + rStrm.CopyFromStream( aMemStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChFrameBase::XclExpChFrameBase() +{ +} + +XclExpChFrameBase::~XclExpChFrameBase() +{ +} + +void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + // line format + mxLineFmt.reset( new XclExpChLineFormat( rRoot ) ); + mxLineFmt->Convert( rRoot, rPropSet, eObjType ); + // area format (only for frame objects) + if( rRoot.GetFormatInfo( eObjType ).mbIsFrame ) + { + mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) ); + bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType ); + if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill ) + { + mxEscherFmt.reset( new XclExpChEscherFormat( rRoot ) ); + mxEscherFmt->Convert( rPropSet, eObjType ); + if( mxEscherFmt->IsValid() ) + mxAreaFmt->SetAuto( false ); + else + mxEscherFmt.reset(); + } + } +} + +void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot, + XclChFrameType eDefFrameType, bool bIsFrame ) +{ + // line format + mxLineFmt.reset( new XclExpChLineFormat( rRoot ) ); + mxLineFmt->SetDefault( eDefFrameType ); + // area format (only for frame objects) + if( bIsFrame ) + { + mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) ); + mxAreaFmt->SetDefault( eDefFrameType ); + mxEscherFmt.reset(); + } +} + +bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const +{ + return + (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) && + (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType )); +} + +void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxLineFmt ); + lclSaveRecord( rStrm, mxAreaFmt ); + lclSaveRecord( rStrm, mxEscherFmt ); +} + +// ---------------------------------------------------------------------------- + +XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ), + meObjType( eObjType ) +{ +} + +void XclExpChFrame::Convert( const ScfPropertySet& rPropSet ) +{ + ConvertFrameBase( GetChRoot(), rPropSet, meObjType ); +} + +bool XclExpChFrame::IsDefault() const +{ + return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType ); +} + +bool XclExpChFrame::IsDeleteable() const +{ + return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame; +} + +void XclExpChFrame::Save( XclExpStream& rStrm ) +{ + switch( meObjType ) + { + // wall/floor frame without CHFRAME header record + case EXC_CHOBJTYPE_WALL3D: + case EXC_CHOBJTYPE_FLOOR3D: + WriteFrameRecords( rStrm ); + break; + default: + XclExpChGroupBase::Save( rStrm ); + } +} + +void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm ) +{ + WriteFrameRecords( rStrm ); +} + +void XclExpChFrame::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnFormat << maData.mnFlags; +} + +namespace { + +/** Creates a CHFRAME record from the passed property set. */ +XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + XclExpChFrameRef xFrame( new XclExpChFrame( rRoot, eObjType ) ); + xFrame->Convert( rPropSet ); + if( xFrame->IsDeleteable() ) + xFrame.reset(); + return xFrame; +} + +} // namespace + +// Source links =============================================================== + +namespace { + +void lclAddDoubleRefData( + ScTokenArray& orArray, const FormulaToken& rToken, + SCsTAB nScTab1, SCsCOL nScCol1, SCsROW nScRow1, + SCsTAB nScTab2, SCsCOL nScCol2, SCsROW nScRow2 ) +{ + ScComplexRefData aComplexRef; + aComplexRef.InitFlags(); + aComplexRef.Ref1.SetFlag3D( true ); + aComplexRef.Ref1.nTab = nScTab1; + aComplexRef.Ref1.nCol = nScCol1; + aComplexRef.Ref1.nRow = nScRow1; + aComplexRef.Ref2.nTab = nScTab2; + aComplexRef.Ref2.nCol = nScCol2; + aComplexRef.Ref2.nRow = nScRow2; + + if( orArray.GetLen() > 0 ) + orArray.AddOpCode( ocUnion ); + + DBG_ASSERT( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef), + "lclAddDoubleRefData - double reference token expected"); + if( rToken.GetType() == ::formula::svExternalDoubleRef ) + orArray.AddExternalDoubleReference( rToken.GetIndex(), rToken.GetString(), aComplexRef ); + else + orArray.AddDoubleReference( aComplexRef ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) : + XclExpRecord( EXC_ID_CHSOURCELINK ), + XclExpChRoot( rRoot ) +{ + maData.mnDestType = nDestType; + maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY; +} + +sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount ) +{ + mxLinkFmla.reset(); + maData.mnLinkType = EXC_CHSRCLINK_DEFAULT; + + if( !xDataSeq.is() ) + return nDefCount; + + // Compile the range representation string into token array. Note that the + // source range text depends on the current grammar. + OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation(); + ScCompiler aComp( GetDocPtr(), ScAddress() ); + aComp.SetGrammar( GetDocPtr()->GetGrammar() ); + ScTokenArray* pArray = aComp.CompileString( aRangeRepr ); + if( !pArray ) + return nDefCount; + + ScTokenArray aArray; + sal_uInt32 nValueCount = 0; + pArray->Reset(); + for( const FormulaToken* pToken = pArray->First(); pToken; pToken = pArray->Next() ) + { + switch( pToken->GetType() ) + { + case ::formula::svSingleRef: + case ::formula::svExternalSingleRef: + // for a single ref token, just add it to the new token array as is + if( aArray.GetLen() > 0 ) + aArray.AddOpCode( ocUnion ); + aArray.AddToken( *pToken ); + ++nValueCount; + break; + + case ::formula::svDoubleRef: + case ::formula::svExternalDoubleRef: + { + // split 3-dimensional ranges into single sheets + const ScComplexRefData& rComplexRef = static_cast< const ScToken* >( pToken )->GetDoubleRef(); + const ScSingleRefData& rRef1 = rComplexRef.Ref1; + const ScSingleRefData& rRef2 = rComplexRef.Ref2; + for( SCsTAB nScTab = rRef1.nTab; nScTab <= rRef2.nTab; ++nScTab ) + { + // split 2-dimensional ranges into single columns + if( bSplitToColumns && (rRef1.nCol < rRef2.nCol) && (rRef1.nRow < rRef2.nRow) ) + for( SCsCOL nScCol = rRef1.nCol; nScCol <= rRef2.nCol; ++nScCol ) + lclAddDoubleRefData( aArray, *pToken, nScTab, nScCol, rRef1.nRow, nScTab, nScCol, rRef2.nRow ); + else + lclAddDoubleRefData( aArray, *pToken, nScTab, rRef1.nCol, rRef1.nRow, nScTab, rRef2.nCol, rRef2.nRow ); + } + sal_uInt32 nTabs = static_cast< sal_uInt32 >( rRef2.nTab - rRef1.nTab + 1 ); + sal_uInt32 nCols = static_cast< sal_uInt32 >( rRef2.nCol - rRef1.nCol + 1 ); + sal_uInt32 nRows = static_cast< sal_uInt32 >( rRef2.nRow - rRef1.nRow + 1 ); + nValueCount += nCols * nRows * nTabs; + } + break; + + default:; + } + } + + const ScAddress aBaseCell; + mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell ); + maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET; + return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT ); +} + +sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq ) +{ + mxString.reset(); + sal_uInt16 nFontIdx = EXC_FONT_APP; + if( rStringSeq.hasElements() ) + { + mxString = XclExpStringHelper::CreateString( GetRoot(), String::EmptyString(), EXC_STR_FORCEUNICODE | EXC_STR_8BITLENGTH | EXC_STR_SEPARATEFORMATS ); + Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + + // convert all formatted string entries from the sequence + const Reference< XFormattedString >* pBeg = rStringSeq.getConstArray(); + const Reference< XFormattedString >* pEnd = pBeg + rStringSeq.getLength(); + for( const Reference< XFormattedString >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + if( pIt->is() ) + { + sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND; + sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND; + sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND; + OUString aText = (*pIt)->getString(); + ScfPropertySet aStrProp( *pIt ); + + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText ); + + // process all script portions + sal_Int32 nPortionPos = 0; + sal_Int32 nTextLen = aText.getLength(); + while( nPortionPos < nTextLen ) + { + // get script type and end position of next script portion + sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos ); + sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript ); + + // reuse previous script for following weak portions + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = mxString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.copy( nPortionPos, nPortionEnd - nPortionPos ) ); + if( nXclPortionStart < mxString->Len() ) + { + // find font index variable dependent on script type + sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx : + ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx); + + // insert font into buffer (if not yet done) + if( rnFontIdx == EXC_FONT_NOTFOUND ) + rnFontIdx = ConvertFont( aStrProp, nScript ); + + // insert font index into format run vector + mxString->AppendFormat( nXclPortionStart, rnFontIdx ); + } + + // go to next script portion + nLastScript = nScript; + nPortionPos = nPortionEnd; + } + } + } + if( !mxString->IsEmpty() ) + { + // get leading font index + const XclFormatRunVec& rFormats = mxString->GetFormats(); + DBG_ASSERT( !rFormats.empty() && (rFormats.front().mnChar == 0), + "XclExpChSourceLink::ConvertStringSequenc - missing leading format" ); + // remove leading format run, if entire string is equally formatted + if( rFormats.size() == 1 ) + nFontIdx = mxString->RemoveLeadingFont(); + else if( !rFormats.empty() ) + nFontIdx = rFormats.front().mnFontIdx; + // add trailing format run, if string is rich-formatted + if( mxString->IsRich() ) + mxString->AppendTrailingFormat( EXC_FONT_APP ); + } + } + return nFontIdx; +} + +void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent ) +{ + sal_Int32 nApiNumFmt = 0; + if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) ) + { + ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT ); + maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) ); + } +} + +void XclExpChSourceLink::Save( XclExpStream& rStrm ) +{ + // CHFORMATRUNS record + if( mxString.is() && mxString->IsRich() ) + { + sal_Size nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1); + rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize ); + mxString->WriteFormats( rStrm, true ); + rStrm.EndRecord(); + } + // CHSOURCELINK record + XclExpRecord::Save( rStrm ); + // CHSTRING record + if( mxString.is() && !mxString->IsEmpty() ) + { + rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() ); + rStrm << sal_uInt16( 0 ) << *mxString; + rStrm.EndRecord(); + } +} + +void XclExpChSourceLink::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnDestType + << maData.mnLinkType + << maData.mnFlags + << maData.mnNumFmtIdx + << mxLinkFmla; +} + +// Text ======================================================================= + +XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) : + XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) : + XclExpRecord( EXC_ID_CHOBJECTLINK, 6 ) +{ + maData.mnTarget = nLinkTarget; + maData.maPointPos = rPointPos; +} + +void XclExpChObjectLink::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx; +} + +// ---------------------------------------------------------------------------- + +XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) : + XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 ) +{ +} + +void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet, bool bShowSeries, + bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble ) +{ + // label value flags + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES, bShowSeries ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG, bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE, bShowValue ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent ); + ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE, bShowBubble ); + + // label value separator + rPropSet.GetStringProperty( maData.maSeparator, EXC_CHPROP_LABELSEPARATOR ); + if( maData.maSeparator.Len() == 0 ) + maData.maSeparator = String( sal_Unicode( ' ' ) ); +} + +void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm ) +{ + XclExpString aXclSep( maData.maSeparator, EXC_STR_FORCEUNICODE | EXC_STR_SMARTFLAGS ); + rStrm << maData.mnFlags << aXclSep; +} + +// ---------------------------------------------------------------------------- + +XclExpChFontBase::~XclExpChFontBase() +{ +} + +void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx ) +{ + if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) ) + { + XclExpChFontRef xFont( new XclExpChFont( nFontIdx ) ); + SetFont( xFont, pFont->GetFontData().maColor, pFont->GetFontColorId() ); + } +} + +void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet ) +{ + ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) ); +} + +void XclExpChFontBase::ConvertRotationBase( + const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet, bool bSupportsStacked ) +{ + sal_uInt16 nRotation = rRoot.GetChartPropSetHelper().ReadRotationProperties( rPropSet, bSupportsStacked ); + SetRotation( nRotation ); +} + +// ---------------------------------------------------------------------------- + +XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ), + mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChText::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) +{ + mxFont = xFont; + maData.maTextColor = rColor; + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rColor == COL_AUTO ); + mnTextColorId = nColorId; +} + +void XclExpChText::SetRotation( sal_uInt16 nRotation ) +{ + maData.mnRotation = nRotation; + ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 ); +} + +void XclExpChText::ConvertTitle( Reference< XTitle > xTitle, sal_uInt16 nTarget ) +{ + switch( nTarget ) + { + case EXC_CHOBJLINK_TITLE: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE ); break; + case EXC_CHOBJLINK_YAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 ); break; + case EXC_CHOBJLINK_XAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 0 ); break; + case EXC_CHOBJLINK_ZAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 ); break; + } + + mxSrcLink.reset(); + mxObjLink.reset( new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) ) ); + + if( xTitle.is() ) + { + // title frame formatting + ScfPropertySet aTitleProp( xTitle ); + mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT ); + + // string sequence + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() ); + ConvertFontBase( GetChRoot(), nFontIdx ); + + // rotation + ConvertRotationBase( GetChRoot(), aTitleProp, true ); + + // manual text position - only for main title + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT, EXC_CHFRAMEPOS_PARENT ) ); + if( nTarget == EXC_CHOBJLINK_TITLE ) + { + Any aRelPos; + if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try + { + // calculate absolute position for CHTEXT record + Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW ); + ::com::sun::star::awt::Point aPos = xTitleShape->getPosition(); + ::com::sun::star::awt::Size aSize = xTitleShape->getSize(); + ::com::sun::star::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height ); + maData.maRect = CalcChartRectFromHmm( aRect ); + ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 ); + // manual title position implies manual plot area + GetChartData().SetManualPlotArea(); + // calculate the default title position in chart units + sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 ); + sal_Int32 nDefPosY = 85; + // set the position relative to the standard position + XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect; + rFrameRect.mnX = maData.maRect.mnX - nDefPosX; + rFrameRect.mnY = maData.maRect.mnY - nDefPosY; + } + catch( Exception& ) + { + } + } + } + else + { + ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED ); + } +} + +void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet ) +{ + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN ); + ConvertFontBase( GetChRoot(), rPropSet ); +} + +bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet, + const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos ) +{ + SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx ); + + cssc2::DataPointLabel aPointLabel; + if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) ) + return false; + + // percentage only allowed in pie and donut charts + bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE; + // bubble sizes only allowed in bubble charts + bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES; + DBG_ASSERT( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" ); + + // raw show flags + bool bShowValue = !bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size + bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent; // percentage only in pie/donut charts + bool bShowCateg = aPointLabel.ShowCategoryName; + bool bShowBubble = bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size + bool bShowAny = bShowValue || bShowPercent || bShowCateg || bShowBubble; + + // create the CHFRLABELPROPS record for extended settings in BIFF8 + if( bShowAny && (GetBiff() == EXC_BIFF8) ) + { + mxLabelProps.reset( new XclExpChFrLabelProps( GetChRoot() ) ); + mxLabelProps->Convert( rPropSet, false, bShowCateg, bShowValue, bShowPercent, bShowBubble ); + } + + // restrict to combinations allowed in CHTEXT + if( bShowPercent ) bShowValue = false; // percent wins over value + if( bShowValue ) bShowCateg = false; // value wins over category + if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size + + // set all flags + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol ); + ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny ); + + if( bShowAny ) + { + // font settings + ConvertFontBase( GetChRoot(), rPropSet ); + ConvertRotationBase( GetChRoot(), rPropSet, false ); + // label placement + sal_Int32 nPlacement = 0; + sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO; + if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) ) + { + using namespace ::com::sun::star::chart::DataLabelPlacement; + if( nPlacement == rTypeInfo.mnDefaultLabelPos ) + { + nLabelPos = EXC_CHTEXT_POS_DEFAULT; + } + else switch( nPlacement ) + { + case AVOID_OVERLAP: nLabelPos = EXC_CHTEXT_POS_AUTO; break; + case CENTER: nLabelPos = EXC_CHTEXT_POS_CENTER; break; + case TOP: nLabelPos = EXC_CHTEXT_POS_ABOVE; break; + case TOP_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case BOTTOM_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break; + case BOTTOM: nLabelPos = EXC_CHTEXT_POS_BELOW; break; + case BOTTOM_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case TOP_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break; + case INSIDE: nLabelPos = EXC_CHTEXT_POS_INSIDE; break; + case OUTSIDE: nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break; + case NEAR_ORIGIN: nLabelPos = EXC_CHTEXT_POS_AXIS; break; + default: DBG_ERRORFILE( "XclExpChText::ConvertDataLabel - unknown label placement type" ); + } + } + ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 ); + // source link (contains number format) + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + if( bShowValue || bShowPercent ) + // percentage format wins over value format + mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent ); + // object link + mxObjLink.reset( new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos ) ); + } + + /* Return true to indicate valid label settings: + - for existing labels at entire series + - for any settings at single data point (to be able to delete a point label) */ + return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS); +} + +void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos ) +{ + // required flags + ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT ); + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel + // frame formatting + mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT ); + // font settings + maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT; + maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT; + ConvertFontBase( GetChRoot(), rPropSet ); + // source link (contains number format) + mxSrcLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + mxSrcLink->ConvertNumFmt( rPropSet, false ); + // object link + mxObjLink.reset( new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos ) ); +} + +sal_uInt16 XclExpChText::GetAttLabelFlags() const +{ + sal_uInt16 nFlags = 0; + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) ); + ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) ); + return nFlags; +} + +void XclExpChText::WriteSubRecords( XclExpStream& rStrm ) +{ + // CHFRAMEPOS record + lclSaveRecord( rStrm, mxFramePos ); + // CHFONT record + lclSaveRecord( rStrm, mxFont ); + // CHSOURCELINK group + lclSaveRecord( rStrm, mxSrcLink ); + // CHFRAME group + lclSaveRecord( rStrm, mxFrame ); + // CHOBJECTLINK record + lclSaveRecord( rStrm, mxObjLink ); + // CHFRLABELPROPS record + lclSaveRecord( rStrm, mxLabelProps ); +} + +void XclExpChText::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnHAlign + << maData.mnVAlign + << maData.mnBackMode + << maData.maTextColor + << maData.maRect + << maData.mnFlags; + + if( GetBiff() == EXC_BIFF8 ) + { + rStrm << GetPalette().GetColorIndex( mnTextColorId ) + << maData.mnFlags2 + << maData.mnRotation; + } +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Creates and returns an Excel text object from the passed title. */ +XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > xTitled, sal_uInt16 nTarget ) +{ + Reference< XTitle > xTitle; + if( xTitled.is() ) + xTitle = xTitled->getTitleObject(); + + XclExpChTextRef xText( new XclExpChText( rRoot ) ); + xText->ConvertTitle( xTitle, nTarget ); + /* Do not delete the CHTEXT group for the main title. A missing CHTEXT + will be interpreted as auto-generated title showing the series title in + charts that contain exactly one data series. */ + if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() ) + xText.reset(); + + return xText; +} + +} + +// Data series ================================================================ + +XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ), + mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ), + mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ) +{ +} + +void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) +{ + rRoot.GetChartPropSetHelper().ReadMarkerProperties( maData, rPropSet, nFormatIdx ); + /* Set marker line/fill color to series line color. + TODO: remove this if OOChart supports own colors in markers. */ + Color aLineColor; + if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) ) + maData.maLineColor = maData.maFillColor = aLineColor; + // register colors in palette + RegisterColors( rRoot ); +} + +void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot, + const ScfPropertySet& rPropSet, bool bCloseSymbol ) +{ + // clear the automatic flag + ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false ); + // symbol type and color + if( bCloseSymbol ) + { + // set symbol type for the 'close' data series + maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ; + maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; + // set symbol line/fill color to series line color + Color aLineColor; + if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) ) + { + maData.maLineColor = maData.maFillColor = aLineColor; + RegisterColors( rRoot ); + } + } + else + { + // set invisible symbol + maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL; + } +} + +void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot ) +{ + if( HasMarker() ) + { + if( HasLineColor() ) + mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE ); + if( HasFillColor() ) + mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA ); + } +} + +void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags; + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + const XclExpPalette& rPal = rStrm.GetRoot().GetPalette(); + rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize; + } +} + +// ---------------------------------------------------------------------------- + +XclExpChPieFormat::XclExpChPieFormat() : + XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 ) +{ +} + +void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet ) +{ + double fApiDist(0.0); + if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) ) + SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) ); +} + +// ---------------------------------------------------------------------------- + +XclExpCh3dDataFormat::XclExpCh3dDataFormat() : + XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 ) +{ +} + +void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet ) +{ + sal_Int32 nApiType(0); + if( rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) ) + { + using namespace ::com::sun::star::chart2::DataPointGeometry3D; + switch( nApiType ) + { + case CUBOID: + maData.mnBase = EXC_CH3DDATAFORMAT_RECT; + maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT; + break; + case PYRAMID: + maData.mnBase = EXC_CH3DDATAFORMAT_RECT; + maData.mnTop = EXC_CH3DDATAFORMAT_SHARP; + break; + case CYLINDER: + maData.mnBase = EXC_CH3DDATAFORMAT_CIRC; + maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT; + break; + case CONE: + maData.mnBase = EXC_CH3DDATAFORMAT_CIRC; + maData.mnTop = EXC_CH3DDATAFORMAT_SHARP; + break; + default: + DBG_ERRORFILE( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" ); + } + } +} + +void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnBase << maData.mnTop; +} + +// ---------------------------------------------------------------------------- + +XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) : + XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags ) +{ +} + +// ---------------------------------------------------------------------------- + +XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot, + const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 ) +{ + maData.maPointPos = rPointPos; + maData.mnFormatIdx = nFormatIdx; +} + +void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo ) +{ + // line and area formatting + ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() ); + + // data point symbols + bool bIsFrame = rTypeInfo.IsSeriesFrameFormat(); + if( !bIsFrame ) + { + mxMarkerFmt.reset( new XclExpChMarkerFormat( GetChRoot() ) ); + mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx ); + } + + // pie segments + if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE ) + { + mxPieFmt.reset( new XclExpChPieFormat ); + mxPieFmt->Convert( rPropSet ); + } + + // 3D bars (only allowed for entire series in BIFF8) + if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) ) + { + mx3dDataFmt.reset( new XclExpCh3dDataFormat ); + mx3dDataFmt->Convert( rPropSet ); + } + + // spline + if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame ) + mxSeriesFmt.reset( new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED ) ); + + // data point labels + XclExpChTextRef xLabel( new XclExpChText( GetChRoot() ) ); + if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) ) + { + // CHTEXT groups for data labels are stored in global CHCHART group + GetChartData().SetDataLabel( xLabel ); + mxAttLabel.reset( new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() ) ); + } +} + +void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol ) +{ + // set line format to invisible + SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false ); + // set symbols to invisible or to 'close' series symbol + mxMarkerFmt.reset( new XclExpChMarkerFormat( GetChRoot() ) ); + mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol ); +} + +void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType ) +{ + ConvertFrameBase( GetChRoot(), rPropSet, eObjType ); +} + +void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mx3dDataFmt ); + WriteFrameRecords( rStrm ); + lclSaveRecord( rStrm, mxPieFmt ); + lclSaveRecord( rStrm, mxMarkerFmt ); + lclSaveRecord( rStrm, mxSeriesFmt ); + lclSaveRecord( rStrm, mxAttLabel ); +} + +void XclExpChDataFormat::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maPointPos.mnPointIdx + << maData.maPointPos.mnSeriesIdx + << maData.mnFormatIdx + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ), + XclExpChRoot( rRoot ) +{ +} + +bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > xRegCurve, sal_uInt16 nSeriesIdx ) +{ + if( !xRegCurve.is() ) + return false; + + // trend line type + ScfPropertySet aCurveProp( xRegCurve ); + OUString aService = aCurveProp.GetServiceName(); + if( aService == SERVICE_CHART2_LINEARREGCURVE ) + { + maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL; + maData.mnOrder = 1; + } + else if( aService == SERVICE_CHART2_EXPREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL; + else if( aService == SERVICE_CHART2_LOGREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC; + else if( aService == SERVICE_CHART2_POTREGCURVE ) + maData.mnLineType = EXC_CHSERTREND_POWER; + else + return false; + + // line formatting + XclChDataPointPos aPointPos( nSeriesIdx ); + mxDataFmt.reset( new XclExpChDataFormat( GetChRoot(), aPointPos, 0 ) ); + mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE ); + + // #i83100# show equation and correlation coefficient + ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() ); + maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0; + maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0; + + // #i83100# formatting of the equation text box + if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) ) + { + mxLabel.reset( new XclExpChText( GetChRoot() ) ); + mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos ); + } + + // missing features + // #i20819# polynomial trend lines + // #i66819# moving average trend lines + // #i5085# manual trend line size + // #i34093# manual crossing point + return true; +} + +void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnLineType + << maData.mnOrder + << maData.mfIntercept + << maData.mnShowEquation + << maData.mnShowRSquared + << maData.mfForecastFor + << maData.mfForecastBack; +} + +// ---------------------------------------------------------------------------- + +XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) : + XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ), + XclExpChRoot( rRoot ) +{ + maData.mnBarType = nBarType; +} + +bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet ) +{ + sal_Int32 nBarStyle = 0; + bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE ); + if( bOk ) + { + switch( nBarStyle ) + { + case cssc::ErrorBarStyle::ABSOLUTE: + maData.mnSourceType = EXC_CHSERERR_FIXED; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR ); + break; + case cssc::ErrorBarStyle::RELATIVE: + maData.mnSourceType = EXC_CHSERERR_PERCENT; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR ); + break; + case cssc::ErrorBarStyle::STANDARD_DEVIATION: + maData.mnSourceType = EXC_CHSERERR_STDDEV; + rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT ); + break; + case cssc::ErrorBarStyle::STANDARD_ERROR: + maData.mnSourceType = EXC_CHSERERR_STDERR; + break; + case cssc::ErrorBarStyle::FROM_DATA: + { + bOk = false; + maData.mnSourceType = EXC_CHSERERR_CUSTOM; + Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY ); + if( xDataSource.is() ) + { + // find first sequence with current role + OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ); + Reference< XDataSequence > xValueSeq; + + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; !xValueSeq.is() && (pIt != pEnd); ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aCurrRole; + if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) ) + xValueSeq = xTmpValueSeq; + } + if( xValueSeq.is() ) + { + // #i86465# pass value count back to series + rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true ); + bOk = maData.mnValueCount > 0; + } + } + } + break; + default: + bOk = false; + } + } + return bOk; +} + +void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnBarType + << maData.mnSourceType + << maData.mnLineEnd + << sal_uInt8( 1 ) // must be 1 to make line visible + << maData.mfValue + << maData.mnValueCount; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Returns the property set of the specified data point. */ +ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > xDataSeries, sal_Int32 nPointIdx ) +{ + ScfPropertySet aPropSet; + try + { + aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) ); + } + catch( Exception& ) + { + DBG_ERRORFILE( "lclGetPointPropSet - no data point property set" ); + } + return aPropSet; +} + +} // namespace + +XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ), + mnGroupIdx( EXC_CHSERGROUP_NONE ), + mnSeriesIdx( nSeriesIdx ), + mnParentIdx( EXC_CHSERIES_INVALID ) +{ + // CHSOURCELINK records are always required, even if unused + mxTitleLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) ); + mxValueLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES ) ); + mxCategLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY ) ); + if( GetBiff() == EXC_BIFF8 ) + mxBubbleLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES ) ); +} + +bool XclExpChSeries::ConvertDataSeries( + Reference< XDiagram > xDiagram, Reference< XDataSeries > xDataSeries, + const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx ) +{ + bool bOk = false; + Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY ); + if( xDataSource.is() ) + { + Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq; + + // find first sequence with role 'values-y' + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aRole; + if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) ) + { + if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) ) + { + xYValueSeq = xTmpValueSeq; + if( !xTitleSeq.is() ) + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) ) + { + xXValueSeq = xTmpValueSeq; + } + else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) ) + { + xBubbleSeq = xTmpValueSeq; + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + } + } + + bOk = xYValueSeq.is(); + if( bOk ) + { + // chart type group index + mnGroupIdx = nGroupIdx; + + // convert source links + maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true ); + mxTitleLink->ConvertDataSequence( xTitleSeq, true ); + + // X values of XY charts + maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount ); + + // size values of bubble charts + if( mxBubbleLink.is() ) + mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount ); + + // series formatting + XclChDataPointPos aPointPos( mnSeriesIdx ); + ScfPropertySet aSeriesProp( xDataSeries ); + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx ) ); + mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo ); + + // trend lines + CreateTrendLines( xDataSeries ); + + // error bars + CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS ); + CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS ); + + if( maData.mnValueCount > 0 ) + { + const sal_Int32 nMaxPointCount = maData.mnValueCount; + + /* #i91063# Create missing fill properties in pie/doughnut charts. + If freshly created (never saved to ODF), these charts show + varying point colors but do not return these points via API. */ + if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) ) + { + Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme(); + if( xColorScheme.is() ) + { + const OUString aFillStyleName = CREATE_OUSTRING( "FillStyle" ); + const OUString aColorName = CREATE_OUSTRING( "Color" ); + namespace cssd = ::com::sun::star::drawing; + for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx ) + { + aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx ); + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx ); + // test that the point fill style is solid, but no color is set + cssd::FillStyle eFillStyle = cssd::FillStyle_NONE; + if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) && + (eFillStyle == cssd::FillStyle_SOLID) && + !aPointProp.HasProperty( aColorName ) ) + { + aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) ); + } + } + } + } + + // data point formatting + Sequence< sal_Int32 > aPointIndexes; + if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() ) + { + const sal_Int32* pnBeg = aPointIndexes.getConstArray(); + const sal_Int32* pnEnd = pnBeg + aPointIndexes.getLength(); + for( const sal_Int32* pnIt = pnBeg; (pnIt != pnEnd) && (*pnIt < nMaxPointCount); ++pnIt ) + { + aPointPos.mnPointIdx = static_cast< sal_uInt16 >( *pnIt ); + ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, *pnIt ); + XclExpChDataFormatRef xPointFmt( new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx ) ); + xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo ); + maPointFmts.AppendRecord( xPointFmt ); + } + } + } + } + } + return bOk; +} + +bool XclExpChSeries::ConvertStockSeries( XDataSeriesRef xDataSeries, + const OUString& rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol ) +{ + bool bOk = false; + Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY ); + if( xDataSource.is() ) + { + Reference< XDataSequence > xYValueSeq, xTitleSeq; + + // find first sequence with passed role + Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences(); + const Reference< XLabeledDataSequence >* pBeg = aLabeledSeqVec.getConstArray(); + const Reference< XLabeledDataSequence >* pEnd = pBeg + aLabeledSeqVec.getLength(); + for( const Reference< XLabeledDataSequence >* pIt = pBeg; !xYValueSeq.is() && (pIt != pEnd); ++pIt ) + { + Reference< XDataSequence > xTmpValueSeq = (*pIt)->getValues(); + ScfPropertySet aValueProp( xTmpValueSeq ); + OUString aRole; + if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) ) + { + xYValueSeq = xTmpValueSeq; + xTitleSeq = (*pIt)->getLabel(); // ignore role of label sequence + } + } + + bOk = xYValueSeq.is(); + if( bOk ) + { + // chart type group index + mnGroupIdx = nGroupIdx; + // convert source links + maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true ); + mxTitleLink->ConvertDataSequence( xTitleSeq, true ); + // series formatting + ScfPropertySet aSeriesProp( xDataSeries ); + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx ) ); + mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol ); + } + } + return bOk; +} + +bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > xRegCurve ) +{ + InitFromParent( rParent ); + mxTrendLine.reset( new XclExpChSerTrendLine( GetChRoot() ) ); + bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx ); + if( bOk ) + { + mxSeriesFmt = mxTrendLine->GetDataFormat(); + GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() ); + } + return bOk; +} + +bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId ) +{ + InitFromParent( rParent ); + // error bar settings + mxErrorBar.reset( new XclExpChSerErrorBar( GetChRoot(), nBarId ) ); + bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet ); + if( bOk ) + { + // error bar formatting + mxSeriesFmt.reset( new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 ) ); + mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR ); + } + return bOk; +} + +void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > xCategSeq ) +{ + if( xCategSeq.is() ) + maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false ); +} + +void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxTitleLink ); + lclSaveRecord( rStrm, mxValueLink ); + lclSaveRecord( rStrm, mxCategLink ); + lclSaveRecord( rStrm, mxBubbleLink ); + lclSaveRecord( rStrm, mxSeriesFmt ); + maPointFmts.Save( rStrm ); + if( mnGroupIdx != EXC_CHSERGROUP_NONE ) + XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm ); + if( mnParentIdx != EXC_CHSERIES_INVALID ) + XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm ); + lclSaveRecord( rStrm, mxTrendLine ); + lclSaveRecord( rStrm, mxErrorBar ); +} + +void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent ) +{ + // index to parent series is stored 1-based + mnParentIdx = rParent.mnSeriesIdx + 1; + /* #i86465# MSO2007 SP1 expects correct point counts in child series + (there was no problem in Excel2003 or Excel2007 without SP1...) */ + maData.mnCategCount = rParent.maData.mnCategCount; + maData.mnValueCount = rParent.maData.mnValueCount; +} + +void XclExpChSeries::CreateTrendLines( XDataSeriesRef xDataSeries ) +{ + Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY ); + if( xRegCurveCont.is() ) + { + Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves(); + const Reference< XRegressionCurve >* pBeg = aRegCurveSeq.getConstArray(); + const Reference< XRegressionCurve >* pEnd = pBeg + aRegCurveSeq.getLength(); + for( const Reference< XRegressionCurve >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() && !xSeries->ConvertTrendLine( *this, *pIt ) ) + GetChartData().RemoveLastSeries(); + } + } +} + +void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet, + const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) +{ + Reference< XPropertySet > xErrorBar; + if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() ) + { + ScfPropertySet aErrorProp( xErrorBar ); + CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId ); + CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId ); + } +} + +void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet, + const OUString& rShowPropName, sal_uInt8 nBarId ) +{ + if( rPropSet.GetBoolProperty( rShowPropName ) ) + { + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) ) + GetChartData().RemoveLastSeries(); + } +} + +void XclExpChSeries::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount; + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnBubbleType << maData.mnBubbleCount; +} + +// Chart type groups ========================================================== + +XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHUNKNOWN ), + XclExpChRoot( rRoot ), + maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) ) +{ +} + +void XclExpChType::Convert( Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels ) +{ + if( xChartType.is() ) + { + maTypeInfo = GetChartTypeInfo( xChartType->getChartType() ); + // special handling for some chart types + switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_BAR: + { + maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR ); + ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet ); + ScfPropertySet aTypeProp( xChartType ); + Sequence< sal_Int32 > aInt32Seq; + maData.mnOverlap = 0; + if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) ) + maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 ); + maData.mnGap = 150; + if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) ) + maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 ); + } + break; + case EXC_CHTYPECATEG_RADAR: + ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels ); + break; + case EXC_CHTYPECATEG_PIE: + { + ScfPropertySet aTypeProp( xChartType ); + bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS ); + maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE ); + maData.mnPieHole = bDonut ? 50 : 0; + // #i85166# starting angle of first pie slice + ScfPropertySet aDiaProp( xDiagram ); + maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp ); + } + break; + case EXC_CHTYPECATEG_SCATTER: + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES ); + break; + default:; + } + SetRecId( maTypeInfo.mnRecId ); + } +} + +void XclExpChType::SetStacked( bool bPercent ) +{ + switch( maTypeInfo.meTypeCateg ) + { + case EXC_CHTYPECATEG_LINE: + ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED ); + ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent ); + break; + case EXC_CHTYPECATEG_BAR: + ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED ); + ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent ); + maData.mnOverlap = -100; + break; + default:; + } +} + +void XclExpChType::WriteBody( XclExpStream& rStrm ) +{ + switch( GetRecId() ) + { + case EXC_ID_CHBAR: + rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags; + break; + + case EXC_ID_CHLINE: + case EXC_ID_CHAREA: + case EXC_ID_CHRADARLINE: + case EXC_ID_CHRADARAREA: + rStrm << maData.mnFlags; + break; + + case EXC_ID_CHPIE: + rStrm << maData.mnRotation << maData.mnPieHole; + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnFlags; + break; + + case EXC_ID_CHSCATTER: + if( GetBiff() == EXC_BIFF8 ) + rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags; + break; + + default: + DBG_ERRORFILE( "XclExpChType::WriteBody - unknown chart type" ); + } +} + +// ---------------------------------------------------------------------------- + +XclExpChChart3d::XclExpChChart3d() : + XclExpRecord( EXC_ID_CHCHART3D, 14 ) +{ +} + +void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart ) +{ + sal_Int32 nRotationY = 0; + rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL ); + sal_Int32 nRotationX = 0; + rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL ); + sal_Int32 nPerspective = 15; + rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE ); + + if( b3dWallChart ) + { + // Y rotation (Excel [0..359], Chart2 [-179,180]) + if( nRotationY < 0 ) nRotationY += 360; + maData.mnRotation = static_cast< sal_uInt16 >( nRotationY ); + // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180]) + maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 ); + // perspective (Excel and Chart2 [0,100]) + maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 ); + // flags + maData.mnFlags = 0; + ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) ); + ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT ); + ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ); + } + else + { + // Y rotation not used in pie charts, but 'first pie slice angle' + maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet ); + // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80]) + maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 ); + // perspective (Excel and Chart2 [0,100]) + maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 ); + // flags + maData.mnFlags = 0; + } +} + +void XclExpChChart3d::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnRotation + << maData.mnElevation + << maData.mnEyeDist + << maData.mnRelHeight + << maData.mnRelDepth + << maData.mnDepthGap + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 ) +{ +} + +void XclExpChLegend::Convert( const ScfPropertySet& rPropSet ) +{ + // frame properties + mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND ); + // text properties + mxText.reset( new XclExpChText( GetChRoot() ) ); + mxText->ConvertLegend( rPropSet ); + + // legend position + Any aRelPosAny; + rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION ); + if( aRelPosAny.has< RelativePosition >() ) + { + try + { + /* The 'RelativePosition' property is used as indicator of manually + changed legend position, but due to the different anchor modes + used by this property (in the RelativePosition.Anchor member) + it cannot be used to calculate the position easily. For this, + the Chart1 API will be used instead. */ + Reference< ::com::sun::star::chart::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW ); + // coordinates in CHLEGEND record written but not used by Excel + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE, EXC_CHFRAMEPOS_PARENT ) ); + XclChFramePos& rFramePos = mxFramePos->GetFramePosData(); + rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( xChart1Legend->getPosition().X ); + rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( xChart1Legend->getPosition().Y ); + // manual legend position implies manual plot area + GetChartData().SetManualPlotArea(); + maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED; + } + catch( Exception& ) + { + OSL_ENSURE( false, "XclExpChLegend::Convert - cannot get legend shape" ); + maData.mnDockMode = EXC_CHLEGEND_RIGHT; + } + } + else + { + cssc2::LegendPosition eApiPos = cssc2::LegendPosition_CUSTOM; + rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION ); + switch( eApiPos ) + { + case cssc2::LegendPosition_LINE_START: maData.mnDockMode = EXC_CHLEGEND_LEFT; break; + case cssc2::LegendPosition_LINE_END: maData.mnDockMode = EXC_CHLEGEND_RIGHT; break; + case cssc2::LegendPosition_PAGE_START: maData.mnDockMode = EXC_CHLEGEND_TOP; break; + case cssc2::LegendPosition_PAGE_END: maData.mnDockMode = EXC_CHLEGEND_BOTTOM; break; + default: + OSL_ENSURE( false, "XclExpChLegend::Convert - unrecognized legend position" ); + maData.mnDockMode = EXC_CHLEGEND_RIGHT; + } + } + + // legend expansion + cssc2::LegendExpansion eApiExpand = cssc2::LegendExpansion_BALANCED; + rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION ); + ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand != cssc2::LegendExpansion_WIDE ); + + // other flags + ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES ); + const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY; + ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED ); +} + +void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxFramePos ); + lclSaveRecord( rStrm, mxText ); + lclSaveRecord( rStrm, mxFrame ); +} + +void XclExpChLegend::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ), + meObjType( eObjType ), + mnBarDist( 100 ) +{ +} + +void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet ) +{ + if( rPropSet.Is() ) + ConvertFrameBase( GetChRoot(), rPropSet, meObjType ); + else + SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true ); +} + +void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm ) +{ + WriteFrameRecords( rStrm ); +} + +void XclExpChDropBar::WriteBody( XclExpStream& rStrm ) +{ + rStrm << mnBarDist; +} + +// ---------------------------------------------------------------------------- + +XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ), + maType( rRoot ), + maTypeInfo( maType.GetTypeInfo() ) +{ + maData.mnGroupIdx = nGroupIdx; +} + +void XclExpChTypeGroup::ConvertType( + Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels ) +{ + // chart type settings + maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels ); + + // spline - TODO: get from single series (#i66858#) + ScfPropertySet aTypeProp( xChartType ); + ::com::sun::star::chart2::CurveStyle eCurveStyle; + bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) && + (eCurveStyle != ::com::sun::star::chart2::CurveStyle_LINES); + + // extended type info + maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline ); + + // 3d chart settings + if( maTypeInfo.mb3dChart ) // only true, if Excel chart supports 3d mode + { + mxChart3d.reset( new XclExpChChart3d ); + ScfPropertySet aDiaProp( xDiagram ); + mxChart3d->Convert( aDiaProp, Is3dWallChart() ); + } +} + +void XclExpChTypeGroup::ConvertSeries( + Reference< XDiagram > xDiagram, Reference< XChartType > xChartType, + sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars ) +{ + Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY ); + if( xSeriesCont.is() ) + { + typedef ::std::vector< Reference< XDataSeries > > XDataSeriesVec; + XDataSeriesVec aSeriesVec; + + // copy data series attached to the current axes set to the vector + Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries(); + const Reference< XDataSeries >* pBeg = aSeriesSeq.getConstArray(); + const Reference< XDataSeries >* pEnd = pBeg + aSeriesSeq.getLength(); + for( const Reference< XDataSeries >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + ScfPropertySet aSeriesProp( *pIt ); + sal_Int32 nSeriesAxesSetIdx(0); + if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) ) + aSeriesVec.push_back( *pIt ); + } + + // Are there any series in the current axes set? + if( !aSeriesVec.empty() ) + { + // stacking direction (stacked/percent/deep 3d) from first series + ScfPropertySet aSeriesProp( aSeriesVec.front() ); + cssc2::StackingDirection eStacking; + if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) ) + eStacking = cssc2::StackingDirection_NO_STACKING; + + // stacked or percent chart + if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) ) + { + // percent overrides simple stacking + maType.SetStacked( bPercent ); + + // connected data points (only in stacked bar charts) + if( bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) ) + maChartLines[ EXC_CHCHARTLINE_CONNECT ].reset( new XclExpChLineFormat( GetChRoot() ) ); + } + else + { + // reverse series order for some unstacked 2D chart types + if( maTypeInfo.mbReverseSeries && !Is3dChart() ) + ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() ); + } + + // deep 3d chart or clustered 3d chart (stacked is not clustered) + if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() ) + mxChart3d->SetClustered(); + + // varied point colors + ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) ); + + // process all series + for( XDataSeriesVec::const_iterator aIt = aSeriesVec.begin(), aEnd = aSeriesVec.end(); aIt != aEnd; ++aIt ) + { + // create Excel series object, stock charts need special processing + if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK ) + CreateAllStockSeries( xChartType, *aIt ); + else + CreateDataSeries( xDiagram, *aIt ); + } + } + } +} + +void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > xCategSeq ) +{ + for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx ) + maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq ); +} + +void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet ) +{ + if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) ) + { + mxLegend.reset( new XclExpChLegend( GetChRoot() ) ); + mxLegend->Convert( rPropSet ); + } +} + +void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm ) +{ + maType.Save( rStrm ); + lclSaveRecord( rStrm, mxChart3d ); + lclSaveRecord( rStrm, mxLegend ); + lclSaveRecord( rStrm, mxUpBar ); + lclSaveRecord( rStrm, mxDownBar ); + for( XclExpChLineFormatMap::iterator aLIt = maChartLines.begin(), aLEnd = maChartLines.end(); aLIt != aLEnd; ++aLIt ) + lclSaveRecord( rStrm, aLIt->second, EXC_ID_CHCHARTLINE, aLIt->first ); +} + +sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const +{ + return static_cast< sal_uInt16 >( maSeries.GetSize() ); +} + +void XclExpChTypeGroup::CreateDataSeries( + Reference< XDiagram > xDiagram, Reference< XDataSeries > xDataSeries ) +{ + // let chart create series object with correct series index + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() ) + { + if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) ) + maSeries.AppendRecord( xSeries ); + else + GetChartData().RemoveLastSeries(); + } +} + +void XclExpChTypeGroup::CreateAllStockSeries( + Reference< XChartType > xChartType, Reference< XDataSeries > xDataSeries ) +{ + // create existing series objects + bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false ); + bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false ); + bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false ); + bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen ); + + // formatting of special stock chart elements + ScfPropertySet aTypeProp( xChartType ); + // hi-lo lines + if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) ) + { + ScfPropertySet aSeriesProp( xDataSeries ); + XclExpChLineFormatRef xLineFmt( new XclExpChLineFormat( GetChRoot() ) ); + xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE ); + maChartLines[ EXC_CHCHARTLINE_HILO ] = xLineFmt; + } + // dropbars + if( bHasOpen && bHasClose ) + { + // dropbar type is dependent on position in the file - always create both + Reference< XPropertySet > xWhitePropSet, xBlackPropSet; + // white dropbar format + aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY ); + ScfPropertySet aWhiteProp( xWhitePropSet ); + mxUpBar.reset( new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR ) ); + mxUpBar->Convert( aWhiteProp ); + // black dropbar format + aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY ); + ScfPropertySet aBlackProp( xBlackPropSet ); + mxDownBar.reset( new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR ) ); + mxDownBar->Convert( aBlackProp ); + } +} + +bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > xDataSeries, + const OUString& rValueRole, bool bCloseSymbol ) +{ + bool bOk = false; + // let chart create series object with correct series index + XclExpChSeriesRef xSeries = GetChartData().CreateSeries(); + if( xSeries.is() ) + { + bOk = xSeries->ConvertStockSeries( xDataSeries, + rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol ); + if( bOk ) + maSeries.AppendRecord( xSeries ); + else + GetChartData().RemoveLastSeries(); + } + return bOk; +} + +void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm ) +{ + rStrm.WriteZeroBytes( 16 ); + rStrm << maData.mnFlags << maData.mnGroupIdx; +} + +// Axes ======================================================================= + +XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHLABELRANGE, 8 ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChLabelRange::Convert( const ScaleData& rScaleData, bool bMirrorOrient ) +{ + // origin + double fOrigin = 0.0; + if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) ) + maData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 ); + + // reverse order + if( (rScaleData.Orientation == ::com::sun::star::chart2::AxisOrientation_REVERSE) != bMirrorOrient ) + ::set_flag( maData.mnFlags, EXC_CHLABELRANGE_REVERSE ); +} + +void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet ) +{ + cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE; + rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ); + double fCrossingPos = 1.0; + rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE ); + switch( eAxisPos ) + { + case cssc::ChartAxisPosition_ZERO: maData.mnCross = 1; break; + case cssc::ChartAxisPosition_START: maData.mnCross = 1; break; + case cssc::ChartAxisPosition_END: ::set_flag( maData.mnFlags, EXC_CHLABELRANGE_MAXCROSS ); break; + case cssc::ChartAxisPosition_VALUE: maData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 ); break; + default: maData.mnCross = 1; + } +} + +void XclExpChLabelRange::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnCross << maData.mnLabelFreq << maData.mnTickFreq << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHVALUERANGE, 42 ), + XclExpChRoot( rRoot ) +{ +} + +void XclExpChValueRange::Convert( const ScaleData& rScaleData ) +{ + // scaling algorithm + bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == SERVICE_CHART2_LOGSCALING; + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale ); + + // min/max + bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin ); + bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax ); + + // origin + bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale ); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross ); + + // major increment + const IncrementData& rIncrementData = rScaleData.IncrementData; + bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0); + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor ); + // minor increment + const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements; + sal_Int32 nCount = 0; + bool bAutoMinor = bLogScale || bAutoMajor || (rSubIncrementSeq.getLength() < 1) || + lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1); + if( !bAutoMinor ) + maData.mfMinorStep = maData.mfMajorStep / nCount; + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor ); + + // reverse order + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE ); +} + +void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet ) +{ + cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE; + double fCrossingPos = 0.0; + if( rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE ) ) + { + switch( eAxisPos ) + { + case cssc::ChartAxisPosition_ZERO: + case cssc::ChartAxisPosition_START: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS ); + break; + case cssc::ChartAxisPosition_END: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS ); + break; + case cssc::ChartAxisPosition_VALUE: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false ); + maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos ); + break; + default: + ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS ); + } + } +} + +void XclExpChValueRange::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mfMin + << maData.mfMax + << maData.mfMajorStep + << maData.mfMinorStep + << maData.mfCross + << maData.mnFlags; +} + +// ---------------------------------------------------------------------------- + +namespace { + +sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks ) +{ + using namespace ::com::sun::star::chart2::TickmarkStyle; + sal_uInt8 nXclTickPos = 0; + ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE, ::get_flag( nApiTickmarks, INNER ) ); + ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) ); + return nXclTickPos; +} + +} // namespace + +XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) : + XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ), + XclExpChRoot( rRoot ), + mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ) +{ +} + +void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType ) +{ + // tick mark style + sal_Int32 nApiTickmarks = 0; + if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) ) + maData.mnMajor = lclGetXclTickPos( nApiTickmarks ); + if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) ) + maData.mnMinor = lclGetXclTickPos( nApiTickmarks ); + + // axis labels + if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) ) + { + /* Radar charts disable their category labels via chart type, not via + axis, and axis labels are always 'near axis'. */ + maData.mnLabelPos = EXC_CHTICK_NEXT; + } + else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) ) + { + // no labels + maData.mnLabelPos = EXC_CHTICK_NOLABEL; + } + else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) ) + { + // Excel expects 'near axis' at Y axes in 3D charts + maData.mnLabelPos = EXC_CHTICK_NEXT; + } + else + { + cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS; + rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION ); + switch( eApiLabelPos ) + { + case cssc::ChartAxisLabelPosition_NEAR_AXIS: + case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT; break; + case cssc::ChartAxisLabelPosition_OUTSIDE_START: maData.mnLabelPos = EXC_CHTICK_LOW; break; + case cssc::ChartAxisLabelPosition_OUTSIDE_END: maData.mnLabelPos = EXC_CHTICK_HIGH; break; + default: maData.mnLabelPos = EXC_CHTICK_NEXT; + } + } +} + +void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId ) +{ + maData.maTextColor = rColor; + ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO ); + mnTextColorId = nColorId; +} + +void XclExpChTick::SetRotation( sal_uInt16 nRotation ) +{ + maData.mnRotation = nRotation; + ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false ); + ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 ); +} + +void XclExpChTick::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnMajor + << maData.mnMinor + << maData.mnLabelPos + << maData.mnBackMode; + rStrm.WriteZeroBytes( 16 ); + rStrm << maData.maTextColor + << maData.mnFlags; + if( GetBiff() == EXC_BIFF8 ) + rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Returns an API axis object from the passed coordinate system. */ +Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > xCoordSystem, + sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx ) +{ + Reference< XAxis > xAxis; + if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try + { + xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx ); + } + catch( Exception& ) + { + } + return xAxis; +} + +} // namespace + +XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ), + mnNumFmtIdx( EXC_FORMAT_NOTFOUND ) +{ + maData.mnType = nAxisType; +} + +void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) +{ + mxFont = xFont; + if( mxTick.is() ) + mxTick->SetFontColor( rColor, nColorId ); +} + +void XclExpChAxis::SetRotation( sal_uInt16 nRotation ) +{ + if( mxTick.is() ) + mxTick->SetRotation( nRotation ); +} + +void XclExpChAxis::Convert( Reference< XAxis > xAxis, Reference< XAxis > xCrossingAxis, const XclChExtTypeInfo& rTypeInfo ) +{ + ScfPropertySet aAxisProp( xAxis ); + bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z); + + // axis line format ------------------------------------------------------- + + mxAxisLine.reset( new XclExpChLineFormat( GetChRoot() ) ); + mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE ); + // #i58688# axis enabled + mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) ); + + // axis scaling and increment --------------------------------------------- + + ScfPropertySet aCrossingProp( xCrossingAxis ); + if( bCategoryAxis ) + { + mxLabelRange.reset( new XclExpChLabelRange( GetChRoot() ) ); + mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg ); + if( xAxis.is() ) + // #i71684# radar charts have reversed rotation direction + mxLabelRange->Convert( xAxis->getScaleData(), (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) ); + // get position of crossing axis on this axis from passed axis object + if( aCrossingProp.Is() ) + mxLabelRange->ConvertAxisPosition( aCrossingProp ); + } + else + { + mxValueRange.reset( new XclExpChValueRange( GetChRoot() ) ); + if( xAxis.is() ) + mxValueRange->Convert( xAxis->getScaleData() ); + // get position of crossing axis on this axis from passed axis object + if( aCrossingProp.Is() ) + mxValueRange->ConvertAxisPosition( aCrossingProp ); + } + + // axis caption text ------------------------------------------------------ + + // axis ticks properties + mxTick.reset( new XclExpChTick( GetChRoot() ) ); + mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() ); + + // axis label formatting and rotation + ConvertFontBase( GetChRoot(), aAxisProp ); + ConvertRotationBase( GetChRoot(), aAxisProp, true ); + + // axis number format + sal_Int32 nApiNumFmt = 0; + if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) ) + mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) ); + + // grid ------------------------------------------------------------------- + + if( xAxis.is() ) + { + // main grid + ScfPropertySet aGridProp( xAxis->getGridProperties() ); + if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) ) + mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE ); + // sub grid + Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties(); + if( aSubGridPropSeq.hasElements() ) + { + ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] ); + if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) ) + mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE ); + } + } +} + +void XclExpChAxis::ConvertWall( XDiagramRef xDiagram ) +{ + if( xDiagram.is() ) switch( GetAxisType() ) + { + case EXC_CHAXIS_X: + { + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D ); + } + break; + case EXC_CHAXIS_Y: + { + ScfPropertySet aFloorProp( xDiagram->getFloor() ); + mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D ); + } + break; + default: + mxWallFrame.reset(); + } +} + +void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxLabelRange ); + lclSaveRecord( rStrm, mxValueRange ); + if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND ) + XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm ); + lclSaveRecord( rStrm, mxTick ); + lclSaveRecord( rStrm, mxFont ); + lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE ); + lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID ); + lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID ); + lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS ); +} + +void XclExpChAxis::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnType; + rStrm.WriteZeroBytes( 16 ); +} + +// ---------------------------------------------------------------------------- + +XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) : + XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 ) +{ + maData.mnAxesSetId = nAxesSetId; + SetFutureRecordContext( 0, nAxesSetId ); + + /* Need to set a reasonable size for the plot area, otherwise Excel will + move away embedded shapes while auto-sizing the plot area. This is just + a wild guess, but will be fixed with implementing manual positioning of + chart elements. */ + maData.maRect.mnX = 262; + maData.maRect.mnY = 626; + maData.maRect.mnWidth = 3187; + maData.maRect.mnHeight = 2633; +} + +sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > xDiagram, sal_uInt16 nFirstGroupIdx ) +{ + /* First unused chart type group index is passed to be able to continue + counting of chart type groups for secondary axes set. */ + sal_uInt16 nGroupIdx = nFirstGroupIdx; + Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY ); + if( xCoordSysCont.is() ) + { + Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems(); + if( aCoordSysSeq.getLength() > 0 ) + { + /* Process first coordinate system only. Import filter puts all + chart types into one coordinate system. */ + Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ]; + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + + // 3d mode + bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3); + + // percent charts + namespace ApiAxisType = ::com::sun::star::chart2::AxisType; + Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx ); + bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT); + + // connector lines in bar charts + ScfPropertySet aDiaProp( xDiagram ); + bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS ); + + // swapped axes sets + ScfPropertySet aCoordSysProp( xCoordSystem ); + bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS ); + + // X axis for later use + Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx ); + // X axis labels + ScfPropertySet aXAxisProp( xApiXAxis ); + bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ); + + // process chart types + Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY ); + if( xChartTypeCont.is() ) + { + Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes(); + const Reference< XChartType >* pBeg = aChartTypeSeq.getConstArray(); + const Reference< XChartType >* pEnd = pBeg + aChartTypeSeq.getLength(); + for( const Reference< XChartType >* pIt = pBeg; pIt != pEnd; ++pIt ) + { + XclExpChTypeGroupRef xTypeGroup( new XclExpChTypeGroup( GetChRoot(), nGroupIdx ) ); + xTypeGroup->ConvertType( xDiagram, *pIt, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels ); + /* If new chart type group cannot be inserted into a combination + chart with existing type groups, insert all series into last + contained chart type group instead of creating a new group. */ + XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup(); + if( xLastGroup.is() && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) ) + { + xLastGroup->ConvertSeries( xDiagram, *pIt, nApiAxesSetIdx, bPercent, bConnectBars ); + } + else + { + xTypeGroup->ConvertSeries( xDiagram, *pIt, nApiAxesSetIdx, bPercent, bConnectBars ); + if( xTypeGroup->IsValidGroup() ) + { + maTypeGroups.AppendRecord( xTypeGroup ); + ++nGroupIdx; + } + } + } + } + + if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() ) + { + const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo(); + + // create axes according to chart type (no axes for pie and donut charts) + if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE ) + { + ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y ); + ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X ); + if( pGroup->Is3dDeepChart() ) + ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE ); + } + + // X axis category ranges + if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() ) + { + const ScaleData aScaleData = xApiXAxis->getScaleData(); + for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx ) + maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories ); + } + + // legend + if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) ) + { + Reference< XLegend > xLegend = xDiagram->getLegend(); + if( xLegend.is() ) + { + ScfPropertySet aLegendProp( xLegend ); + pGroup->ConvertLegend( aLegendProp ); + } + } + } + } + } + + // wall/floor/diagram frame formatting + if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) ) + { + XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + if( xTypeGroup.is() && xTypeGroup->Is3dWallChart() ) + { + // wall/floor formatting (3D charts) + if( mxXAxis.is() ) + mxXAxis->ConvertWall( xDiagram ); + if( mxYAxis.is() ) + mxYAxis->ConvertWall( xDiagram ); + } + else + { + // diagram background formatting + ScfPropertySet aWallProp( xDiagram->getWall() ); + mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME ); + } + } + + // inner and outer plot area position and size + try + { + Reference< ::com::sun::star::chart::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW ); + Reference< ::com::sun::star::chart::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW ); + // set manual flag in chart data + if( !xPositioning->isAutomaticDiagramPositioning() ) + GetChartData().SetManualPlotArea(); + // the CHAXESSET record contains the inner plot area + maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() ); + // the embedded CHFRAMEPOS record contains the outer plot area + mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT, EXC_CHFRAMEPOS_PARENT ) ); + // for pie charts, always use inner plot area size to exclude the data labels as Excel does + const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get(); + bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE); + mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect : + CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() ); + } + catch( Exception& ) + { + } + + // return first unused chart type group index for next axes set + return nGroupIdx; +} + +bool XclExpChAxesSet::Is3dChart() const +{ + XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup(); + return xTypeGroup.is() && xTypeGroup->Is3dChart(); +} + +void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm ) +{ + lclSaveRecord( rStrm, mxFramePos ); + lclSaveRecord( rStrm, mxXAxis ); + lclSaveRecord( rStrm, mxYAxis ); + lclSaveRecord( rStrm, mxZAxis ); + lclSaveRecord( rStrm, mxXAxisTitle ); + lclSaveRecord( rStrm, mxYAxisTitle ); + lclSaveRecord( rStrm, mxZAxisTitle ); + if( mxPlotFrame.is() ) + { + XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm ); + mxPlotFrame->Save( rStrm ); + } + maTypeGroups.Save( rStrm ); +} + +XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const +{ + return maTypeGroups.GetFirstRecord(); +} + +XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const +{ + return maTypeGroups.GetLastRecord(); +} + +void XclExpChAxesSet::ConvertAxis( + XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType, + XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget, + Reference< XCoordinateSystem > xCoordSystem, const XclChExtTypeInfo& rTypeInfo, + sal_Int32 nCrossingAxisDim ) +{ + // create and convert axis object + rxChAxis.reset( new XclExpChAxis( GetChRoot(), nAxisType ) ); + sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension(); + sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex(); + Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx ); + Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx ); + rxChAxis->Convert( xAxis, xCrossingAxis, rTypeInfo ); + + // create and convert axis title + Reference< XTitled > xTitled( xAxis, UNO_QUERY ); + rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget ); +} + +void XclExpChAxesSet::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maData.mnAxesSetId << maData.maRect; +} + +// The chart object =========================================================== + +XclExpChChart::XclExpChChart( const XclExpRoot& rRoot, + Reference< XChartDocument > xChartDoc, const Rectangle& rChartRect ) : + XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 ) +{ + Size aPtSize = OutputDevice::LogicToLogic( rChartRect.GetSize(), MapMode( MAP_100TH_MM ), MapMode( MAP_POINT ) ); + // rectangle is stored in 16.16 fixed-point format + maRect.mnX = maRect.mnY = 0; + maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 ); + maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 ); + + // global chart properties (default values) + ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false ); + ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA ); + maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; + + // always create both axes set objects + mxPrimAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_PRIMARY ) ); + mxSecnAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_SECONDARY ) ); + + if( xChartDoc.is() ) + { + Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram(); + + // global chart properties (only 'include hidden cells' attribute for now) + ScfPropertySet aDiagramProp( xDiagram ); + bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS ); + ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden ); + + // initialize API conversion (remembers xChartDoc and rChartRect internally) + InitConversion( xChartDoc, rChartRect ); + + // chart frame + ScfPropertySet aFrameProp( xChartDoc->getPageBackground() ); + mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND ); + + // chart title + Reference< XTitled > xTitled( xChartDoc, UNO_QUERY ); + mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE ); + + // diagrams (axes sets) + sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 ); + if( !mxPrimAxesSet->Is3dChart() ) + mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx ); + + // treatment of missing values + ScfPropertySet aDiaProp( xDiagram ); + sal_Int32 nMissingValues = 0; + if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) ) + { + using namespace ::com::sun::star::chart::MissingValueTreatment; + switch( nMissingValues ) + { + case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; break; + case USE_ZERO: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO; break; + case CONTINUE: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE; break; + } + } + + // finish API conversion + FinishConversion(); + } +} + +XclExpChSeriesRef XclExpChChart::CreateSeries() +{ + XclExpChSeriesRef xSeries; + sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() ); + if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES ) + { + xSeries.reset( new XclExpChSeries( GetChRoot(), nSeriesIdx ) ); + maSeries.AppendRecord( xSeries ); + } + return xSeries; +} + +void XclExpChChart::RemoveLastSeries() +{ + if( !maSeries.IsEmpty() ) + maSeries.RemoveRecord( maSeries.GetSize() - 1 ); +} + +void XclExpChChart::SetDataLabel( XclExpChTextRef xText ) +{ + if( xText.is() ) + maLabels.AppendRecord( xText ); +} + +void XclExpChChart::SetManualPlotArea() +{ + // this flag does not exist in BIFF5 + if( GetBiff() == EXC_BIFF8 ) + ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA ); +} + +void XclExpChChart::WriteSubRecords( XclExpStream& rStrm ) +{ + // background format + lclSaveRecord( rStrm, mxFrame ); + + // data series + maSeries.Save( rStrm ); + + // CHPROPERTIES record + rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 ); + rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 ); + rStrm.EndRecord(); + + // axes sets (always save primary axes set) + sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1; + XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm ); + mxPrimAxesSet->Save( rStrm ); + if( mxSecnAxesSet->IsValidAxesSet() ) + mxSecnAxesSet->Save( rStrm ); + + // chart title and data labels + lclSaveRecord( rStrm, mxTitle ); + maLabels.Save( rStrm ); +} + +void XclExpChChart::WriteBody( XclExpStream& rStrm ) +{ + rStrm << maRect; +} + +// ---------------------------------------------------------------------------- + +XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot, + const Reference< XModel >& rxModel, const Size& rChartSize ) : + XclExpRoot( rRoot ) +{ + if( (rChartSize.Width() > 0) && (rChartSize.Height() > 0) ) + { + ScfPropertySet aPropSet( rxModel ); + Reference< XShapes > xShapes; + if( aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0) ) + { + /* Create a new independent object manager with own DFF stream for the + DGCONTAINER, pass global manager as parent for shared usage of + global DFF data (picture container etc.). */ + mxObjMgr.reset( new XclExpEmbeddedObjectManager( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS ) ); + // initialize the drawing object list + mxObjMgr->StartSheet(); + // process the draw page (convert all shapes) + mxObjRecs = mxObjMgr->ProcessDrawing( xShapes ); + // finalize the DFF stream + mxObjMgr->EndDocument(); + } + } +} + +XclExpChartDrawing::~XclExpChartDrawing() +{ +} + +void XclExpChartDrawing::Save( XclExpStream& rStrm ) +{ + if( mxObjRecs.is() ) + mxObjRecs->Save( rStrm ); +} + +// ---------------------------------------------------------------------------- + +XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > xModel, const Rectangle& rChartRect ) : + XclExpSubStream( EXC_BOF_CHART ), + XclExpRoot( rRoot ) +{ + AppendNewRecord( new XclExpChartPageSettings( rRoot ) ); + AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) ); + AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) ); + AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) ); + + Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY ); + AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) ); +} + +// ============================================================================ + |