Change-Id: I5b6d62104e46c6eb5d142e7e11666806383eed09 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210566 Tested-by: Jenkins Reviewed-by: Julien Nabet <serval2412@yahoo.fr>
416 lines
17 KiB
C++
416 lines
17 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <drawingml/chart/titleconverter.hxx>
|
|
|
|
#include <com/sun/star/chart/ChartLegendExpansion.hpp>
|
|
#include <com/sun/star/chart2/FormattedString.hpp>
|
|
#include <com/sun/star/chart2/LegendPosition.hpp>
|
|
#include <com/sun/star/chart2/XDiagram.hpp>
|
|
#include <com/sun/star/chart2/XLegend.hpp>
|
|
#include <com/sun/star/chart2/XTitle.hpp>
|
|
#include <com/sun/star/chart2/XTitled.hpp>
|
|
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
|
|
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
|
|
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
|
|
|
|
#include <comphelper/sequence.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <drawingml/textbody.hxx>
|
|
#include <drawingml/textparagraph.hxx>
|
|
#include <drawingml/chart/axismodel.hxx>
|
|
#include <drawingml/chart/datasourceconverter.hxx>
|
|
#include <drawingml/chart/titlemodel.hxx>
|
|
#include <oox/token/properties.hxx>
|
|
#include <oox/token/tokens.hxx>
|
|
#include <com/sun/star/chart2/RelativePosition.hpp>
|
|
#include <com/sun/star/drawing/Alignment.hpp>
|
|
|
|
#include <oox/drawingml/chart/modelbase.hxx>
|
|
namespace oox::drawingml::chart {
|
|
|
|
using namespace ::com::sun::star::chart2;
|
|
using namespace ::com::sun::star::chart2::data;
|
|
using namespace ::com::sun::star::drawing;
|
|
using namespace ::com::sun::star::uno;
|
|
|
|
|
|
TextConverter::TextConverter( const ConverterRoot& rParent, TextModel& rModel ) :
|
|
ConverterBase< TextModel >( rParent, rModel )
|
|
{
|
|
}
|
|
|
|
TextConverter::~TextConverter()
|
|
{
|
|
}
|
|
|
|
Reference< XDataSequence > TextConverter::createDataSequence( const OUString& rRole )
|
|
{
|
|
Reference< XDataSequence > xDataSeq;
|
|
if( mrModel.mxDataSeq.is() )
|
|
{
|
|
DataSequenceConverter aDataSeqConv( *this, *mrModel.mxDataSeq );
|
|
xDataSeq = aDataSeqConv.createDataSequence( rRole, DataSourceType::UNKNOWN );
|
|
}
|
|
return xDataSeq;
|
|
}
|
|
|
|
Sequence< Reference< XFormattedString > > TextConverter::createStringSequence(
|
|
const OUString& rDefaultText, const ModelRef< TextBody >& rxTextProp, ObjectType eObjType )
|
|
{
|
|
OSL_ENSURE( !mrModel.mxDataSeq || !mrModel.mxTextBody, "TextConverter::createStringSequence - linked string and rich text found" );
|
|
::std::vector< Reference< XFormattedString > > aStringVec;
|
|
bool bTextFound = false;
|
|
if( mrModel.mxTextBody.is() )
|
|
{
|
|
// rich-formatted text objects can be created, but currently Chart2 is not able to show them
|
|
const TextParagraphVector& rTextParas = mrModel.mxTextBody->getParagraphs();
|
|
for( TextParagraphVector::const_iterator aPIt = rTextParas.begin(), aPEnd = rTextParas.end(); aPIt != aPEnd; ++aPIt )
|
|
{
|
|
const TextParagraph& rTextPara = **aPIt;
|
|
const TextCharacterProperties& rParaProps = rTextPara.getProperties().getTextCharacterProperties();
|
|
for( TextRunVector::const_iterator aRIt = rTextPara.getRuns().begin(), aREnd = rTextPara.getRuns().end(); aRIt != aREnd; ++aRIt )
|
|
{
|
|
const TextRun& rTextRun = **aRIt;
|
|
bool bAddNewLine = ((aRIt + 1 == aREnd) && (aPIt + 1 != aPEnd)) || rTextRun.isLineBreak();
|
|
Reference< XFormattedString > xFmtStr = appendFormattedString( aStringVec, rTextRun.getText(), bAddNewLine );
|
|
PropertySet aPropSet( xFmtStr );
|
|
TextCharacterProperties aRunProps;
|
|
if (rParaProps.mbHasEmptyParaProperties && rxTextProp.is() && rxTextProp->hasParagraphProperties())
|
|
{
|
|
const TextParagraphVector rDefTextParas = rxTextProp->getParagraphs();
|
|
TextParagraphVector::const_iterator aDefPIt = rDefTextParas.begin();
|
|
const TextParagraph& rDefTextPara = **aDefPIt;
|
|
aRunProps = rDefTextPara.getProperties().getTextCharacterProperties();
|
|
}
|
|
else
|
|
aRunProps = rParaProps;
|
|
aRunProps.assignUsed( rTextRun.getTextCharacterProperties() );
|
|
getFormatter().convertTextFormatting( aPropSet, aRunProps, eObjType );
|
|
|
|
bTextFound = true;
|
|
}
|
|
}
|
|
}
|
|
else if (rxTextProp.is() && !rxTextProp->getParagraphs().empty()) {
|
|
// <c:txPr> or <cx:txPr> can contain <a:p>. Which seems odd, but handle
|
|
// it here.
|
|
const TextParagraphVector& rTextParas = rxTextProp->getParagraphs();
|
|
for( TextParagraphVector::const_iterator aPIt = rTextParas.begin(), aPEnd = rTextParas.end(); aPIt != aPEnd; ++aPIt )
|
|
{
|
|
const TextParagraph& rTextPara = **aPIt;
|
|
const TextCharacterProperties& rParaProps = rTextPara.getProperties().getTextCharacterProperties();
|
|
for( TextRunVector::const_iterator aRIt = rTextPara.getRuns().begin(), aREnd = rTextPara.getRuns().end(); aRIt != aREnd; ++aRIt )
|
|
{
|
|
const TextRun& rTextRun = **aRIt;
|
|
bool bAddNewLine = ((aRIt + 1 == aREnd) && (aPIt + 1 != aPEnd)) || rTextRun.isLineBreak();
|
|
Reference< XFormattedString > xFmtStr = appendFormattedString( aStringVec, rTextRun.getText(), bAddNewLine );
|
|
PropertySet aPropSet( xFmtStr );
|
|
TextCharacterProperties aRunProps;
|
|
if (rParaProps.mbHasEmptyParaProperties && rxTextProp->hasParagraphProperties())
|
|
{
|
|
const TextParagraphVector rDefTextParas = rxTextProp->getParagraphs();
|
|
TextParagraphVector::const_iterator aDefPIt = rDefTextParas.begin();
|
|
const TextParagraph& rDefTextPara = **aDefPIt;
|
|
aRunProps = rDefTextPara.getProperties().getTextCharacterProperties();
|
|
}
|
|
else
|
|
aRunProps = rParaProps;
|
|
aRunProps.assignUsed( rTextRun.getTextCharacterProperties() );
|
|
getFormatter().convertTextFormatting( aPropSet, aRunProps, eObjType );
|
|
|
|
bTextFound = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bTextFound)
|
|
{
|
|
OUString aString;
|
|
// try to create string from linked data
|
|
if( mrModel.mxDataSeq.is() && !mrModel.mxDataSeq->maData.empty() )
|
|
mrModel.mxDataSeq->maData.begin()->second >>= aString;
|
|
// no linked string -> fall back to default string
|
|
if( aString.isEmpty() )
|
|
aString = rDefaultText;
|
|
|
|
// create formatted string object
|
|
if( !aString.isEmpty() )
|
|
{
|
|
Reference< XFormattedString > xFmtStr = appendFormattedString( aStringVec, aString, false );
|
|
PropertySet aPropSet( xFmtStr );
|
|
getFormatter().convertTextFormatting( aPropSet, rxTextProp, eObjType );
|
|
}
|
|
}
|
|
|
|
return comphelper::containerToSequence( aStringVec );
|
|
}
|
|
|
|
Reference< XFormattedString > TextConverter::appendFormattedString(
|
|
::std::vector< Reference< XFormattedString > >& orStringVec, const OUString& rString, bool bAddNewLine ) const
|
|
{
|
|
Reference< XFormattedString2 > xFmtStr;
|
|
try
|
|
{
|
|
xFmtStr = FormattedString::create( ConverterRoot::getComponentContext() );
|
|
xFmtStr->setString( bAddNewLine ? (rString + "\n") : rString );
|
|
orStringVec.emplace_back(xFmtStr );
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
}
|
|
return xFmtStr;
|
|
}
|
|
|
|
TitleConverter::TitleConverter( const ConverterRoot& rParent, TitleModel& rModel ) :
|
|
ConverterBase< TitleModel >( rParent, rModel )
|
|
{
|
|
}
|
|
|
|
TitleConverter::~TitleConverter()
|
|
{
|
|
}
|
|
|
|
void TitleConverter::convertFromModel( const Reference< XTitled >& rxTitled, const OUString& rAutoTitle, ObjectType eObjType, ChartType eCT, sal_Int32 nMainIdx, sal_Int32 nSubIdx )
|
|
{
|
|
if( !rxTitled.is() )
|
|
return;
|
|
|
|
// create the formatted strings
|
|
TextModel& rText = mrModel.mxText.getOrCreate();
|
|
TextConverter aTextConv( *this, rText );
|
|
Sequence< Reference< XFormattedString > > aStringSeq = aTextConv.createStringSequence( rAutoTitle, mrModel.mxTextProp, eObjType );
|
|
if( !aStringSeq.hasElements() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
// create the title object and set the string data
|
|
Reference< XTitle > xTitle( createInstance( u"com.sun.star.chart2.Title"_ustr ), UNO_QUERY_THROW );
|
|
xTitle->setText( aStringSeq );
|
|
rxTitled->setTitleObject( xTitle );
|
|
|
|
// frame formatting (text formatting already done in TextConverter::createStringSequence())
|
|
PropertySet aPropSet( xTitle );
|
|
getFormatter().convertFrameFormatting( aPropSet, mrModel.mxShapeProp, eObjType );
|
|
|
|
if (eCT == ChartType::CX && mrModel.mxShapeProp.is()) {
|
|
aPropSet.setProperty(PROP_HasExplicitSpPr, true);
|
|
}
|
|
|
|
// frame rotation
|
|
OSL_ENSURE( !mrModel.mxTextProp || !rText.mxTextBody, "TitleConverter::convertFromModel - multiple text properties" );
|
|
ModelRef< TextBody > xTextProp = mrModel.mxTextProp.is() ? mrModel.mxTextProp : rText.mxTextBody;
|
|
ObjectFormatter::convertTextRotation( aPropSet, xTextProp, true, mrModel.mnDefaultRotation );
|
|
|
|
// register the title and layout data for conversion of position
|
|
registerTitleLayout( xTitle, mrModel.mxLayout, eObjType, nMainIdx, nSubIdx );
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
LegendConverter::LegendConverter( const ConverterRoot& rParent, LegendModel& rModel ) :
|
|
ConverterBase< LegendModel >( rParent, rModel )
|
|
{
|
|
}
|
|
|
|
LegendConverter::~LegendConverter()
|
|
{
|
|
}
|
|
|
|
void LegendConverter::convertFromModel( const Reference< XDiagram >& rxDiagram, ChartType eCT )
|
|
{
|
|
if( !rxDiagram.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
namespace cssc = css::chart;
|
|
namespace cssc2 = css::chart2;
|
|
|
|
// create the legend
|
|
Reference< XLegend > xLegend( createInstance( u"com.sun.star.chart2.Legend"_ustr ), UNO_QUERY_THROW );
|
|
rxDiagram->setLegend( xLegend );
|
|
PropertySet aPropSet( xLegend );
|
|
aPropSet.setProperty( PROP_Show, true );
|
|
|
|
// legend formatting
|
|
getFormatter().convertFormatting( aPropSet, mrModel.mxShapeProp, mrModel.mxTextProp, OBJECTTYPE_LEGEND );
|
|
|
|
// predefined legend position and expansion
|
|
cssc2::LegendPosition eLegendPos = cssc2::LegendPosition_LINE_END;
|
|
cssc::ChartLegendExpansion eLegendExpand = cssc::ChartLegendExpansion_CUSTOM;
|
|
RelativePosition eRelPos;
|
|
bool bTopRight = false;
|
|
switch( mrModel.mnPosition )
|
|
{
|
|
case XML_l:
|
|
eLegendPos = cssc2::LegendPosition_LINE_START;
|
|
eLegendExpand = cssc::ChartLegendExpansion_HIGH;
|
|
break;
|
|
case XML_r:
|
|
eLegendPos = cssc2::LegendPosition_LINE_END;
|
|
eLegendExpand = cssc::ChartLegendExpansion_HIGH;
|
|
break;
|
|
case XML_tr: // top-right not supported
|
|
eRelPos.Primary = 1;
|
|
eRelPos.Secondary = 0;
|
|
eRelPos.Anchor = Alignment_TOP_RIGHT;
|
|
bTopRight = true;
|
|
break;
|
|
case XML_t:
|
|
eLegendPos = cssc2::LegendPosition_PAGE_START;
|
|
eLegendExpand = cssc::ChartLegendExpansion_WIDE;
|
|
break;
|
|
case XML_b:
|
|
eLegendPos = cssc2::LegendPosition_PAGE_END;
|
|
eLegendExpand = cssc::ChartLegendExpansion_WIDE;
|
|
break;
|
|
}
|
|
bool bManualLayout = false;
|
|
// manual positioning and size
|
|
if( mrModel.mxLayout )
|
|
{
|
|
LayoutConverter aLayoutConv( *this, *mrModel.mxLayout );
|
|
// manual size needs ChartLegendExpansion_CUSTOM
|
|
if( aLayoutConv.convertFromModel( aPropSet ) )
|
|
{
|
|
eLegendExpand = cssc::ChartLegendExpansion_CUSTOM;
|
|
}
|
|
bManualLayout = !aLayoutConv.getAutoLayout();
|
|
}
|
|
|
|
// set position and expansion properties
|
|
aPropSet.setProperty( PROP_AnchorPosition, eLegendPos );
|
|
aPropSet.setProperty( PROP_Expansion, eLegendExpand );
|
|
|
|
if (bTopRight && !bManualLayout)
|
|
aPropSet.setProperty( PROP_RelativePosition, Any(eRelPos));
|
|
|
|
aPropSet.setProperty(PROP_Overlay, mrModel.mbOverlay);
|
|
|
|
if (mrModel.maLegendEntries.size() > 0)
|
|
legendEntriesFormatting(rxDiagram);
|
|
|
|
if (eCT == ChartType::CX && mrModel.mxShapeProp.is()) {
|
|
aPropSet.setProperty(PROP_HasExplicitSpPr, true);
|
|
}
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
void LegendConverter::legendEntriesFormatting(const Reference<XDiagram>& rxDiagram)
|
|
{
|
|
Reference<XCoordinateSystemContainer> xCooSysContainer(rxDiagram, UNO_QUERY_THROW);
|
|
const Sequence<Reference<XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());
|
|
if (!xCooSysSequence.hasElements())
|
|
return;
|
|
|
|
sal_Int32 nIndex = 0;
|
|
for (const auto& rCooSys : xCooSysSequence)
|
|
{
|
|
PropertySet aCooSysProp(rCooSys);
|
|
bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);
|
|
|
|
Reference<XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
|
|
const Sequence<Reference<XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
|
|
if (!xChartTypeSequence.hasElements())
|
|
continue;
|
|
|
|
for (const auto& rCT : xChartTypeSequence)
|
|
{
|
|
Reference<XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
|
|
if (!xDSCont.is())
|
|
continue;
|
|
|
|
bool bIsPie
|
|
= rCT->getChartType().equalsIgnoreAsciiCase("com.sun.star.chart2.PieChartType");
|
|
if (bIsPie)
|
|
{
|
|
PropertySet xChartTypeProp(rCT);
|
|
bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
|
|
}
|
|
const Sequence<Reference<XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
|
|
if (bSwapXAndY)
|
|
nIndex += aDataSeriesSeq.getLength() - 1;
|
|
for (const auto& rDataSeries : aDataSeriesSeq)
|
|
{
|
|
PropertySet aSeriesProp(rDataSeries);
|
|
bool bVaryColorsByPoint = aSeriesProp.getBoolProperty(PROP_VaryColorsByPoint);
|
|
|
|
if (bVaryColorsByPoint || bIsPie)
|
|
{
|
|
Reference<XDataSource> xDSrc(rDataSeries, UNO_QUERY);
|
|
if (!xDSrc.is())
|
|
continue;
|
|
|
|
const Sequence<Reference<XLabeledDataSequence> > aDataSeqs = xDSrc->getDataSequences();
|
|
std::vector<sal_Int32> deletedLegendEntries;
|
|
sal_Int32 j = 0;
|
|
for (const auto& rDataSeq : aDataSeqs)
|
|
{
|
|
Reference<XDataSequence> xValues = rDataSeq->getValues();
|
|
if (!xValues.is())
|
|
continue;
|
|
|
|
sal_Int32 nDataSeqSize = xValues->getData().getLength();
|
|
for (sal_Int32 i = 0; i < nDataSeqSize; ++i)
|
|
{
|
|
for (const auto& rLegendEntry : mrModel.maLegendEntries)
|
|
{
|
|
if (nIndex == rLegendEntry->mnLegendEntryIdx && rLegendEntry->mbLabelDeleted)
|
|
{
|
|
deletedLegendEntries.push_back(j + i);
|
|
break;
|
|
}
|
|
}
|
|
nIndex++;
|
|
}
|
|
j += nDataSeqSize;
|
|
}
|
|
if (deletedLegendEntries.size() > 0)
|
|
aSeriesProp.setProperty(PROP_DeletedLegendEntries, comphelper::containerToSequence(deletedLegendEntries));
|
|
}
|
|
else
|
|
{
|
|
for (const auto& rLegendEntry : mrModel.maLegendEntries)
|
|
{
|
|
if (nIndex == rLegendEntry->mnLegendEntryIdx)
|
|
{
|
|
aSeriesProp.setProperty(PROP_ShowLegendEntry, !rLegendEntry->mbLabelDeleted);
|
|
break;
|
|
}
|
|
}
|
|
bSwapXAndY ? nIndex-- : nIndex++;
|
|
}
|
|
}
|
|
if (bSwapXAndY)
|
|
nIndex += aDataSeriesSeq.getLength() + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace oox::drawingml::chart
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|