/* -*- 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 .
*/
try
{ // count all charttype groups to be able to leave at least one
sal_Int32 nRemainingGroups = 0;
uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW ); const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > >
aCooSysSeq( xCooSysCnt->getCoordinateSystems()); for( autoconst & i : aCooSysSeq )
{
uno::Reference< chart2::XChartTypeContainer > xCTCnt( i, uno::UNO_QUERY_THROW );
nRemainingGroups += xCTCnt->getChartTypes().getLength();
}
// delete all empty groups, but leave at least group (empty or not) for( sal_Int32 nI = aCooSysSeq.getLength(); nI-- && (nRemainingGroups > 1); )
{
uno::Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nI], uno::UNO_QUERY_THROW );
uno::Sequence< uno::Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes()); for( sal_Int32 nJ=aCTSeq.getLength(); nJ-- && (nRemainingGroups > 1); )
{
uno::Reference< chart2::XDataSeriesContainer > xDSCnt( aCTSeq[nJ], uno::UNO_QUERY_THROW ); if( !xDSCnt->getDataSeries().hasElements() )
{ // note: iterator stays valid as we have a local sequence
xCTCnt->removeChartType( aCTSeq[nJ] );
--nRemainingGroups;
}
}
}
} catch(const uno::Exception&)
{
TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught while removing empty chart types");
}
}
Reference<lang::XMultiServiceFactory> xFact(xChild->getParent(), uno::UNO_QUERY); if (xFact.is())
{ if (!xChartDoc->getDataProvider().is())
{ bool bHasDataPilotSource = !sDataPilotSource.isEmpty();
OUString aDataProviderServiceName(u"com.sun.star.chart2.data.DataProvider"_ustr); if (bHasDataPilotSource)
aDataProviderServiceName = "com.sun.star.chart2.data.PivotTableDataProvider";
if (lcl_hasServiceName(xFact, aDataProviderServiceName))
{
Reference<chart2::data::XDataProvider> xProvider(xFact->createInstance(aDataProviderServiceName), uno::UNO_QUERY);
if (xProvider.is())
{ if (bHasDataPilotSource)
{
Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xProvider, uno::UNO_QUERY);
xPivotTableDataProvider->setPivotTableName(sDataPilotSource);
xDataReceiver->attachDataProvider(xProvider);
bHasOwnData = !xPivotTableDataProvider->hasPivotTable();
} else
{
xDataReceiver->attachDataProvider(xProvider);
bHasOwnData = false;
}
}
}
} else
bHasOwnData = false;
} // else we have no parent => we have our own data
uno::Reference< embed::XVisualObject > xVisualObject( mrImportHelper.GetChartDocument(), uno::UNO_QUERY);
SAL_WARN_IF(!xVisualObject.is(), "xmloff.chart", "need xVisualObject for page size"); if( xVisualObject.is() )
maChartSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); //#i103460# take the size given from the parent frame as default
if( aOldChartTypeName.isEmpty() )
{
SAL_WARN("xmloff.chart", "need a charttype to create a diagram" ); //set a fallback value: const OUString& aChartClass_Bar( GetXMLToken(XML_BAR ) );
aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, true/* bUseOldNames */ );
maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, false/* bUseOldNames */ );
}
// Set the size of the draw page. if( xVisualObject.is() )
xVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, maChartSize );
InitChart( aOldChartTypeName);
if( bHasAddin )
{ //correct charttype service name when having an addin //and don't refresh addin during load
uno::Reference< beans::XPropertySet > xDocProp( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); if( xDocProp.is() )
{ try
{
xDocProp->getPropertyValue(u"BaseDiagram"_ustr) >>= aOldChartTypeName;
maChartTypeServiceName = SchXMLTools::GetNewChartTypeName( aOldChartTypeName );
xDocProp->setPropertyValue(u"RefreshAddInAllowed"_ustr, uno::Any( false) );
} catch(const uno::Exception&)
{
TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::StartElement" );
}
}
}
// set auto-styles for Area
uno::Reference<beans::XPropertySet> xProp = mrImportHelper.GetChartDocument()->getArea();
mrImportHelper.FillAutoStyle(sAutoStyleName, xProp);
}
//initialize new series styles
::std::map< Reference< chart2::XDataSeries >, sal_Int32 >::const_iterator aSeriesMapEnd( aSeriesMap.end() );
//sort by index
::std::vector< NewDonutSeries > aNewSeriesVector;
{
::std::map< sal_Int32, Reference< chart2::XDataSeries > > aIndexSeriesMap; for (autoconst& series : aSeriesMap)
aIndexSeriesMap[series.second] = series.first;
for (autoconst& indexSeries : aIndexSeriesMap)
aNewSeriesVector.emplace_back(indexSeries.second,nOldSeriesCount );
}
//overwrite attached axis information according to old series styles for (autoconst& style : rStyleVector)
{
DataRowPointStyle aStyle(style); if(aStyle.meType == DataRowPointStyle::DATA_SERIES )
{ auto aSeriesMapIt = aSeriesMap.find( aStyle.m_xSeries ); if( aSeriesMapIt != aSeriesMapEnd && aSeriesMapIt->second < static_cast<sal_Int32>(aNewSeriesVector.size()) )
aNewSeriesVector[aSeriesMapIt->second].mnAttachedAxis = aStyle.mnAttachedAxis;
}
}
//overwrite new series style names with old series style name information for (autoconst& style : rStyleVector)
{
DataRowPointStyle aStyle(style); if( aStyle.meType == DataRowPointStyle::DATA_SERIES )
{ auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries); if( aSeriesMapEnd != aSeriesMapIt )
{
sal_Int32 nNewPointIndex = aSeriesMapIt->second;
aArgs.realloc( aArgs.getLength() + 2 ); auto pArgs = aArgs.getArray();
pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 2 ] = beans::PropertyValue(
u"HasCategories"_ustr,
-1, uno::Any( bHasCateories ),
beans::PropertyState_DIRECT_VALUE );
pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue(
u"UseCategoriesAsX"_ustr,
-1, uno::Any( false ),//categories in ODF files are not to be used as x values (independent from what is offered in our ui)
beans::PropertyState_DIRECT_VALUE );
// cleanup: remove empty chart type groups
lcl_removeEmptyChartTypeGroups( xNewDoc );
// Handle of-pie parameters. Is this the right place to do this? if (maChartTypeServiceName == "com.sun.star.chart2.PieChartType") {
Reference< chart2::XDiagram> xDia(xNewDoc->getFirstDiagram());
uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY ); if( xDiaProp.is()) {
xDiaProp->setPropertyValue(u"SubPieType"_ustr, uno::Any(mPieSubType));
xDiaProp->setPropertyValue(u"SplitPos"_ustr, uno::Any(mfPieSplitPos));
}
}
// set stack mode before a potential chart type detection (in case we have a rectangular range)
uno::Reference< chart::XDiagram > xDiagram( xDoc->getDiagram() );
uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY ); if( xDiaProp.is())
{ if( maSeriesDefaultsAndStyles.maStackedDefault.hasValue())
xDiaProp->setPropertyValue(u"Stacked"_ustr,maSeriesDefaultsAndStyles.maStackedDefault); if( maSeriesDefaultsAndStyles.maPercentDefault.hasValue())
xDiaProp->setPropertyValue(u"Percent"_ustr,maSeriesDefaultsAndStyles.maPercentDefault); if( maSeriesDefaultsAndStyles.maDeepDefault.hasValue())
xDiaProp->setPropertyValue(u"Deep"_ustr,maSeriesDefaultsAndStyles.maDeepDefault); if( maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault.hasValue())
xDiaProp->setPropertyValue(u"StackedBarsConnected"_ustr,maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault);
}
//the OOo 2.0 implementation and older has a bug with donuts bool bSpecialHandlingForDonutChart = lcl_SpecialHandlingForDonutChartNeeded(
maChartTypeServiceName, GetImport());
// apply data if(!xNewDoc.is()) return;
bool bHasOwnData = false; if( m_aXLinkHRefAttributeToIndicateDataProvider == "." ) //data comes from the chart itself
bHasOwnData = true; elseif( m_aXLinkHRefAttributeToIndicateDataProvider == ".." ) //data comes from the parent application
bHasOwnData = false; elseif( !m_aXLinkHRefAttributeToIndicateDataProvider.isEmpty() ) //not supported so far to get the data by sibling objects -> fall back to chart itself if data are available
bHasOwnData = m_bHasTableElement; else
bHasOwnData = !m_bHasRangeAtPlotArea;
if( xNewDoc->hasInternalDataProvider())
{ if( !m_bHasTableElement && m_aXLinkHRefAttributeToIndicateDataProvider != "." )
{ //#i103147# ODF, workaround broken files with a missing table:cell-range-address at the plot-area bool bSwitchSuccessful = SchXMLTools::switchBackToDataProviderFromParent( xNewDoc, maLSequencesPerIndex );
bHasOwnData = !bSwitchSuccessful;
} else
bHasOwnData = true;//e.g. in case of copy->paste from calc to impress
} elseif( bHasOwnData )
{
xNewDoc->createInternalDataProvider( false/* bCloneExistingData */ );
} if( bHasOwnData )
msChartAddress = "all";
bool bSwitchRangesFromOuterToInternalIfNecessary = false; if(!bHasOwnData && mbIsStockChart)
{ // special handling for stock chart (merge series together)
MergeSeriesForStockChart();
} elseif((bHasOwnData || !mbAllRangeAddressesAvailable) && !msChartAddress.isEmpty())
{ //own data or only rectangular range available
bool bOlderThan2_3 = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( xNewDoc ); bool bOldFileWithOwnDataFromRows = (bOlderThan2_3 && bHasOwnData && (meDataRowSource==chart::ChartDataRowSource_ROWS)); // in this case there are range addresses that are simply wrong.
if( mbAllRangeAddressesAvailable && !bSpecialHandlingForDonutChart && !mbIsStockChart &&
!bOldFileWithOwnDataFromRows )
{ //bHasOwnData is true in this case! //e.g. for normal files with own data or also in case of copy paste scenario (e.g. calc to impress)
bSwitchRangesFromOuterToInternalIfNecessary = true;
} else
{ //apply data from rectangular range
// create datasource from data provider with rectangular range parameters and change the diagram setDiagramData try
{ if( bOlderThan2_3 && xDiaProp.is() )//for older charts the hidden cells were removed by calc on the fly
xDiaProp->setPropertyValue(u"IncludeHiddenCells"_ustr,uno::Any(false));
// note: mbRowHasLabels means the first row contains labels, that means we have "column-descriptions", // (analogously mbColHasLabels means we have "row-descriptions")
lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans );
} catch(const uno::Exception&)
{ //try to fallback to internal data
TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram try to fallback to internal data" ); if(!bHasOwnData)
{
bHasOwnData = true;
msChartAddress = "all"; if( !xNewDoc->hasInternalDataProvider() )
{
xNewDoc->createInternalDataProvider( false/* bCloneExistingData */ );
SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc ); try
{
lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans );
} catch(const uno::Exception&)
{
TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram fallback to internal data failed also" );
}
}
}
}
}
} else
{
SAL_WARN("xmloff.chart", "Must not get here" );
}
// now all series and data point properties are available and can be set
{ if( bSpecialHandlingForDonutChart )
{
uno::Reference< chart2::XDiagram > xNewDiagram( xNewDoc->getFirstDiagram() );
lcl_swapPointAndSeriesStylesForDonutCharts( maSeriesDefaultsAndStyles.maSeriesStyleVector
, SchXMLSeriesHelper::getDataSeriesIndexMapFromDiagram(xNewDiagram) );
}
//set defaults from diagram to the new series: //check whether we need to remove lines from symbol only charts bool bSwitchOffLinesForScatter = false;
{ bool bLinesOn = true; if( (maSeriesDefaultsAndStyles.maLinesOnProperty >>= bLinesOn) && !bLinesOn )
{ if( maChartTypeServiceName == "com.sun.star.chart2.ScatterChartType" )
{
bSwitchOffLinesForScatter = true;
SchXMLSeries2Context::switchSeriesLinesOff( maSeriesDefaultsAndStyles.maSeriesStyleVector );
}
}
}
SchXMLSeries2Context::setDefaultsToSeries( maSeriesDefaultsAndStyles );
// set autostyles for series and data points const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); const SvXMLStyleContext* pStyle = nullptr;
OUString sCurrStyleName;
if( pStylesCtxt )
{ //iterate over data-series first //don't set series styles for donut charts if( !bSpecialHandlingForDonutChart )
{
SchXMLSeries2Context::setStylesToSeries(
maSeriesDefaultsAndStyles, pStylesCtxt, pStyle,
sCurrStyleName, mrImportHelper, GetImport(),
mbIsStockChart, maLSequencesPerIndex ); // ... then set attributes for statistics (after their existence was set in the series)
SchXMLSeries2Context::setStylesToStatisticsObjects(
maSeriesDefaultsAndStyles, pStylesCtxt,
pStyle, sCurrStyleName );
//#i98319# call switchRangesFromOuterToInternalIfNecessary before the data point styles are applied, otherwise in copy->paste scenario the data point styles do get lost if( bSwitchRangesFromOuterToInternalIfNecessary )
{ if( xNewDoc->hasInternalDataProvider() )
SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary( maTable, maLSequencesPerIndex, xNewDoc, meDataRowSource );
}
if( pStylesCtxt )
{ // ... then iterate over data-point attributes, so the latter are not overwritten
SchXMLSeries2Context::setStylesToDataPoints( maSeriesDefaultsAndStyles
, pStylesCtxt, pStyle, sCurrStyleName, mrImportHelper, GetImport(), mbIsStockChart, bSpecialHandlingForDonutChart, bSwitchOffLinesForScatter );
}
}
switch(nElement)
{ case XML_ELEMENT(CHART, XML_PLOT_AREA):
pContext = new SchXMLPlotAreaContext( mrImportHelper, GetImport(),
m_aXLinkHRefAttributeToIndicateDataProvider,
msCategoriesAddress,
msChartAddress, m_bHasRangeAtPlotArea, mbAllRangeAddressesAvailable,
mbColHasLabels, mbRowHasLabels,
meDataRowSource,
maSeriesDefaultsAndStyles,
maChartTypeServiceName,
maLSequencesPerIndex, maChartSize ); break; case XML_ELEMENT(CHART, XML_TITLE): if( xDoc.is())
{ if( xProp.is())
{
xProp->setPropertyValue(u"HasMainTitle"_ustr, uno::Any(true) );
}
pContext = new SchXMLTitleContext( mrImportHelper, GetImport(),
maMainTitle, xDoc->getTitle() );
} break; case XML_ELEMENT(CHART, XML_SUBTITLE): if( xDoc.is())
{ if( xProp.is())
{
xProp->setPropertyValue(u"HasSubTitle"_ustr, uno::Any(true) );
}
pContext = new SchXMLTitleContext( mrImportHelper, GetImport(),
maSubTitle, xDoc->getSubTitle() );
} break; case XML_ELEMENT(CHART, XML_LEGEND):
pContext = new SchXMLLegendContext( mrImportHelper, GetImport() ); break; case XML_ELEMENT(LO_EXT, XML_DATA_TABLE):
pContext = new SchXMLDataTableContext(mrImportHelper, GetImport()); break; case XML_ELEMENT(TABLE, XML_TABLE):
{
SchXMLTableContext * pTableContext = new SchXMLTableContext( GetImport(), maTable );
m_bHasTableElement = true; // #i85913# take into account column- and row- mapping for // charts with own data only for those which were not copied // from a place where they got data from the container. Note, // that this requires the plot-area been read before the table // (which is required in the ODF spec) // Note: For stock charts and donut charts with special handling // the mapping must not be applied! if( msChartAddress.isEmpty() && !mbIsStockChart &&
!lcl_SpecialHandlingForDonutChartNeeded(
maChartTypeServiceName, GetImport()))
{ if( !msColTrans.isEmpty() )
{
OSL_ASSERT( msRowTrans.isEmpty() );
pTableContext->setColumnPermutation( lcl_getNumberSequenceFromString( msColTrans, true ));
msColTrans.clear();
} elseif( !msRowTrans.isEmpty() )
{
pTableContext->setRowPermutation( lcl_getNumberSequenceFromString( msRowTrans, true ));
msRowTrans.clear();
}
}
pContext = pTableContext;
} break;
/* With a locked controller the following is done here: 1. Hide title, subtitle, and legend. 2. Set the size of the draw page. 3. Set a (logically) empty data set. 4. Set the chart type.
*/ void SchXMLChartContext::InitChart( const OUString & rChartTypeServiceName // currently the old service name
)
{
uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument();
SAL_WARN_IF( !xDoc.is(), "xmloff.chart", "No valid document!" );
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.