/* -*- 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 .
*/
// create a new sequence
xLabeledSeq = SchXMLTools::GetNewLabeledDataSequence();
// set values at the new sequence
Reference< chart2::data::XDataSequence > xSeq = SchXMLTools::CreateDataSequence( rRange, xChartDoc );
Reference< beans::XPropertySet > xSeqProp( xSeq, uno::UNO_QUERY ); if( xSeqProp.is())
xSeqProp->setPropertyValue(u"Role"_ustr, uno::Any( rRole));
xLabeledSeq->setValues( xSeq );
// add new sequence to data series / push to front to have the correct sequence order if charttype is changed afterwards const Sequence< Reference< chart2::data::XLabeledDataSequence > > aOldSeq( xSeriesSource->getDataSequences());
sal_Int32 nOldCount = aOldSeq.getLength();
Sequence< Reference< chart2::data::XLabeledDataSequence > > aNewSeq( nOldCount + 1 ); auto pNewSeq = aNewSeq.getArray();
pNewSeq[0].set(xLabeledSeq, uno::UNO_QUERY_THROW);
std::copy(aOldSeq.begin(), aOldSeq.end(), std::next(pNewSeq));
xSeriesSink->setData( aNewSeq );
Reference< beans::XPropertySet > xSeriesProp( m_xSeries, uno::UNO_QUERY ); if (xSeriesProp.is())
{ if (bHideLegend)
xSeriesProp->setPropertyValue(u"ShowLegendEntry"_ustr, uno::Any(false));
if( bIsCandleStick )
{ // set default color for range-line to black (before applying styles)
xSeriesProp->setPropertyValue(u"Color"_ustr,
uno::Any( sal_Int32( 0x000000 ))); // black
} elseif ( maSeriesChartTypeName == "com.sun.star.chart2.PieChartType" )
{ //@todo: this property should be saved
xSeriesProp->setPropertyValue(u"VaryColorsByPoint"_ustr,
uno::Any( true ));
}
// values if (xPivotTableDataProvider.is()) // is pivot chart
{
xSequenceValues.set(xPivotTableDataProvider->createDataSequenceOfValuesByIndex(mnSeriesIndex));
} else
{ if (bHasRange && !m_aSeriesRange.isEmpty())
xSequenceValues = SchXMLTools::CreateDataSequence(m_aSeriesRange, mxNewDoc);
}
Reference<beans::XPropertySet> xSeqProp(xSequenceValues, uno::UNO_QUERY); if (xSeqProp.is())
{
OUString aMainRole(u"values-y"_ustr); if (maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType")
aMainRole = "values-size";
xSeqProp->setPropertyValue(u"Role"_ustr, uno::Any(aMainRole));
}
xLabeledSeq->setValues(xSequenceValues);
// register for setting local data if external data provider is not present
maPostponedSequences.emplace(
tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_VALUES ), xLabeledSeq );
if (xPivotTableDataProvider.is())
{
xSequenceLabel.set(xPivotTableDataProvider->createDataSequenceOfLabelsByIndex(mnSeriesIndex));
} else
{ if (!aSeriesLabelRange.isEmpty())
{
xSequenceLabel.set(SchXMLTools::CreateDataSequence(aSeriesLabelRange, mxNewDoc));
} elseif (!aSeriesLabelString.isEmpty())
{
xSequenceLabel.set(SchXMLTools::CreateDataSequenceWithoutConvert(aSeriesLabelString, mxNewDoc));
}
}
//Labels should always include hidden cells
Reference<beans::XPropertySet> xSeqLabelProp(xSequenceLabel, uno::UNO_QUERY); if (xSeqLabelProp.is() && xSeqLabelProp->getPropertySetInfo()->hasPropertyByName(u"IncludeHiddenCells"_ustr))
{
xSeqLabelProp->setPropertyValue( u"IncludeHiddenCells"_ustr, uno::Any(true));
}
xLabeledSeq->setLabel(xSequenceLabel);
// Note: Even if we have no label, we have to register the label // for creation, because internal data always has labels. If // they don't exist in the original, auto-generated labels are // used for the internal data.
maPostponedSequences.emplace(
tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_LABEL ), xLabeledSeq );
void SchXMLSeries2Context::endFastElement(sal_Int32 )
{ // special handling for different chart types. This is necessary as the // roles are not yet saved in the file format
sal_Int32 nDomainCount = maDomainAddresses.size(); bool bIsScatterChart = maSeriesChartTypeName == "com.sun.star.chart2.ScatterChartType"; bool bIsBubbleChart = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType"; bool bDeleteSeries = false;
std::vector< DomainInfo > aDomainInfos;
//different handling for different chart types necessary if( bIsScatterChart || ( nDomainCount==1 && !bIsBubbleChart ) )
{
DomainInfo aDomainInfo( u"values-x"_ustr, m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; bool bCreateXValues = true; if( !maDomainAddresses.empty() )
{ if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
{
m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front();
m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
}
aDomainInfo.aRange = maDomainAddresses.front();
aDomainInfo.nIndexForLocalData = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
} elseif( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() && !m_bHasDomainContext && mnSeriesIndex==0 )
{ if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) //wrong old chart files:
{ //for xy charts the first series needs to have a domain //if this by error iss not the case the first series is taken s x values //needed for wrong files created while having an addin (e.g. BoxPlot)
m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = m_aSeriesRange;
m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
bDeleteSeries = true;
bCreateXValues = false;//they will be created for the next series
}
} if( bCreateXValues )
aDomainInfos.push_back( aDomainInfo );
} elseif( bIsBubbleChart )
{ if( nDomainCount>1 )
{
DomainInfo aDomainInfo( u"values-x"_ustr, maDomainAddresses[1], m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; if( m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() )
{ //for bubble chart the second domain contains the x values which should become an index smaller than y values for own data table //->so second first
m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress = maDomainAddresses[1];
m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
}
aDomainInfos.push_back( aDomainInfo );
m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
} elseif( !m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() )
{
DomainInfo aDomainInfo( u"values-x"_ustr, m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress, m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex ) ;
aDomainInfos.push_back( aDomainInfo );
} if( nDomainCount>0)
{
DomainInfo aDomainInfo( u"values-y"_ustr, maDomainAddresses.front(), m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
{
m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front();
m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex;
}
aDomainInfos.push_back( aDomainInfo );
m_rGlobalSeriesImportInfo.nCurrentDataIndex++;
} elseif( !m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() )
{
DomainInfo aDomainInfo( u"values-y"_ustr, m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ;
aDomainInfos.push_back( aDomainInfo );
}
}
if( bDeleteSeries )
{ //delete created series
SchXMLImportHelper::DeleteDataSeries(
m_xSeries, Reference< chart2::XChartDocument >( GetImport().GetModel(), uno::UNO_QUERY ) );
} else
{ //add style if( !msAutoStyleName.isEmpty() || mnAttachedAxis != 1 )
{
DataRowPointStyle aStyle(
DataRowPointStyle::DATA_SERIES,
m_xSeries,
-1, 1,
msAutoStyleName, mnAttachedAxis );
aStyle.mbSymbolSizeForSeriesIsMissingInFile=mbSymbolSizeIsMissingInFile;
mrStyleVector.push_back( aStyle );
} // And styles for a data-label child element too. In contrast to data-labels as child of data points, // an information about absolute position is useless here. We need only style information. if (!mDataLabel.msStyleName.isEmpty())
{
mDataLabel.msStyleNameOfParent = msAutoStyleName;
mDataLabel.m_xSeries = m_xSeries;
mDataLabel.mnAttachedAxis = mnAttachedAxis; // not needed, but be consistent with its parent
mrStyleVector.push_back(mDataLabel);
}
}
for( std::vector< DomainInfo >::reverse_iterator aIt( aDomainInfos.rbegin() ); aIt!= aDomainInfos.rend(); ++aIt )
{
DomainInfo aDomainInfo( *aIt );
Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq =
lcl_createAndAddSequenceToSeries( aDomainInfo.aRole, aDomainInfo.aRange, mxNewDoc, m_xSeries ); if( xLabeledSeq.is() )
{ // register for setting local data if external data provider is not present
mrLSequencesPerIndex.emplace(
tSchXMLIndexWithPart( aDomainInfo.nIndexForLocalData, SCH_XML_PART_VALUES ),
Reference< chart2::data::XLabeledDataSequence >(xLabeledSeq, uno::UNO_QUERY_THROW) );
}
}
switch(nElement)
{ case XML_ELEMENT(CHART, XML_DOMAIN): if( m_xSeries.is())
{
m_bHasDomainContext = true;
pContext = new SchXMLDomain2Context(
GetImport(), maDomainAddresses );
} break;
case XML_ELEMENT(CHART, XML_MEAN_VALUE):
pContext = new SchXMLStatisticsObjectContext(
mrImportHelper, GetImport(),
msAutoStyleName,
mrStyleVector, m_xSeries,
SchXMLStatisticsObjectContext::CONTEXT_TYPE_MEAN_VALUE_LINE,
mrLSequencesPerIndex ); break; case XML_ELEMENT(CHART, XML_REGRESSION_CURVE):
pContext = new SchXMLRegressionCurveObjectContext(
mrImportHelper, GetImport(),
mrRegressionStyleVector,
m_xSeries, maChartSize ); break; case XML_ELEMENT(CHART, XML_ERROR_INDICATOR):
pContext = new SchXMLStatisticsObjectContext(
mrImportHelper, GetImport(),
msAutoStyleName,
mrStyleVector, m_xSeries,
SchXMLStatisticsObjectContext::CONTEXT_TYPE_ERROR_INDICATOR,
mrLSequencesPerIndex ); break;
case XML_ELEMENT(CHART, XML_DATA_POINT):
pContext = new SchXMLDataPointContext( GetImport(),
mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile ); break; case XML_ELEMENT(CHART, XML_DATA_LABEL): // CustomLabels are useless for a data label element as child of a series, because it serves as default // for all data labels. But the ctor expects it, so use that of the mDataLabel struct as ersatz.
pContext = new SchXMLDataLabelContext(GetImport(), mDataLabel.mCustomLabels,
mDataLabel); break;
case XML_ELEMENT(LO_EXT, XML_PROPERTY_MAPPING):
pContext = new SchXMLPropertyMappingContext(
GetImport(),
mrLSequencesPerIndex, m_xSeries ); break; default:
XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
}
return pContext;
}
//static void SchXMLSeries2Context::initSeriesPropertySets( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles
, const uno::Reference< frame::XModel >& xChartModel )
{ // iterate over series first and remind propertysets in map // new api <-> old api wrapper
::std::map< Reference< chart2::XDataSeries >, Reference< beans::XPropertySet > > aSeriesMap; for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
{ if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) continue;
//initialize m_xOldAPISeries for all other styles also for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector)
{ if( seriesStyle.meType == DataRowPointStyle::DATA_SERIES ) continue;
seriesStyle.m_xOldAPISeries = aSeriesMap[seriesStyle.m_xSeries];
}
}
//static void SchXMLSeries2Context::setDefaultsToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles )
{ // iterate over series // call initSeriesPropertySets first
// ODF has the line and fill properties in a <style:style> element, which is referenced by the // <chart:data-label> element. But LibreOffice has them as special label properties of the series // or point respectively. The following array maps the API name of the ODF property to the name of // the internal property. Those are of kind "LabelFoo". // The array is used in methods setStylesToSeries and setStylesToDataPoints. const std::pair<OUString, OUString> aApiToLabelFooPairs[]
= { { "LineStyle", "LabelBorderStyle" },
{ "LineWidth", "LabelBorderWidth" },
{ "LineColor", "LabelBorderColor" }, // The name "LabelBorderDash" is defined, but the associated API name "LineDash" belongs to // the <draw:stroke-dash> element and is not used directly as line property. //{"LineDash", "LabelBorderDash"},
{ "LineDashName", "LabelBorderDashName" },
{ "LineTransparence", "LabelBorderTransparency" },
{ "FillStyle", "LabelFillStyle" },
{ "FillBackground", "LabelFillBackground" },
{ "FillHatchName", "LabelFillHatchName" },
{ "FillColor", "LabelFillColor" } };
//set style to series // note: SvXMLStyleContext::FillPropertySet is not const
XMLPropStyleContext * pPropStyleContext = const_cast< XMLPropStyleContext * >( dynamic_cast< const XMLPropStyleContext * >( rpStyle ));
if (!pPropStyleContext) continue;
// error bar style must be set before the other error // bar properties (which may be alphabetically before // this property) bool bHasErrorBarRangesFromData = false;
{ static constexpr OUString aErrorBarStylePropName( u"ErrorBarStyle"_ustr);
uno::Any aErrorBarStyle(
SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt )); if( aErrorBarStyle.hasValue())
{
xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle );
sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE;
bHasErrorBarRangesFromData =
( ( aErrorBarStyle >>= eEBStyle ) &&
eEBStyle == chart::ErrorBarStyle::FROM_DATA );
}
}
//don't set the style to the min max line series of a stock chart //otherwise the min max line properties gets overwritten and the series becomes invisible typically if (bIsStockChart)
{ if (SchXMLSeriesHelper::isCandleStickSeries(
seriesStyle.m_xSeries,
rImportHelper.GetChartDocument())) continue;
}
// Has the series a data-label child element? auto pItLabel
= std::find_if(rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
[&seriesStyle](const DataRowPointStyle& rStyle) { return rStyle.meType == DataRowPointStyle::DATA_LABEL_SERIES
&& rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
}); if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
{ // Bring the information from the data-label to the series const SvXMLStyleContext* pLabelStyleContext(pStylesCtxt->FindStyleChildContext(
SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); // note: SvXMLStyleContext::FillPropertySet is not const
XMLPropStyleContext* pLabelPropStyleContext = const_cast<XMLPropStyleContext*>( dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); if (pLabelPropStyleContext)
{ // Test each to be mapped property whether the data-label has a value for it. // If found, set it at series.
uno::Reference<beans::XPropertySetInfo> xSeriesPropInfo(
xSeriesProp->getPropertySetInfo()); for (constauto& rPropPair : aApiToLabelFooPairs)
{
uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); if (aPropValue.hasValue()
&& xSeriesPropInfo->hasPropertyByName(rPropPair.second))
xSeriesProp->setPropertyValue(rPropPair.second, aPropValue);
}
}
}
try
{ //need to set this explicitly here for old files as the new api does not support this property fully anymore if( bSwitchOffLinesForScatter )
xPointProp->setPropertyValue(u"Lines"_ustr,uno::Any(false));
} catch( const uno::Exception & )
{
}
// note: SvXMLStyleContext::FillPropertySet is not const
XMLPropStyleContext * pPropStyleContext = const_cast< XMLPropStyleContext * >( dynamic_cast< const XMLPropStyleContext * >( rpStyle )); if (pPropStyleContext)
{ // Has the point a data-label child element? auto pItLabel = std::find_if(
rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(),
rSeriesDefaultsAndStyles.maSeriesStyleVector.end(),
[&seriesStyle](const DataRowPointStyle& rStyle) { return rStyle.meType == DataRowPointStyle::DATA_LABEL_POINT
&& rStyle.msStyleNameOfParent == seriesStyle.msStyleName;
}); if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end())
{ // Bring the information from the data-label to the point const SvXMLStyleContext* pLabelStyleContext(
pStylesCtxt->FindStyleChildContext(
SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); // note: SvXMLStyleContext::FillPropertySet is not const
XMLPropStyleContext* pLabelPropStyleContext
= const_cast<XMLPropStyleContext*>( dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); if (pLabelPropStyleContext)
{ // Test each to be mapped property whether the data-label has a value for it. // If found, set it at the point.
uno::Reference<beans::XPropertySetInfo> xPointPropInfo(
xPointProp->getPropertySetInfo()); for (constauto& rPropPair : aApiToLabelFooPairs)
{
uno::Any aPropValue(SchXMLTools::getPropertyFromContext(
rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); if (aPropValue.hasValue()
&& xPointPropInfo->hasPropertyByName(rPropPair.second))
xPointProp->setPropertyValue(rPropPair.second, aPropValue);
}
}
}
// Restore character properties on the text span manually, till // SchXMLExportHelper_Impl::exportCustomLabel() does not write the style.
uno::Reference<beans::XPropertySetInfo> xPointPropInfo
= xPointProp->getPropertySetInfo(); if (xPointPropInfo.is())
{
uno::Sequence<beans::Property> aProperties = xPointPropInfo->getProperties(); for (constauto& rProperty : aProperties)
{ if (!rProperty.Name.startsWith("Char")
|| rProperty.Name.startsWith("Chart"))
{ continue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.