chart2/source/model/main/FormattedString.cxx | 18 + chart2/source/model/main/FormattedString.hxx | 3 chart2/source/view/charttypes/VSeriesPlotter.cxx | 25 + download.lst | 4 include/oox/export/chartexport.hxx | 26 +- include/xmloff/xmltoken.hxx | 2 offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl | 10 oox/inc/drawingml/chart/datasourcecontext.hxx | 1 oox/inc/drawingml/chart/seriesmodel.hxx | 15 + oox/source/drawingml/chart/datasourcecontext.cxx | 21 + oox/source/drawingml/chart/seriescontext.cxx | 55 +++- oox/source/drawingml/chart/seriesconverter.cxx | 51 +++- oox/source/drawingml/chart/seriesmodel.cxx | 4 oox/source/export/chartexport.cxx | 153 +++++++++++- oox/source/token/tokens.txt | 3 schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng | 12 svx/uiconfig/ui/sidebarparagraph.ui | 18 - xmloff/source/chart/SchXMLExport.cxx | 78 +++++- xmloff/source/chart/SchXMLPlotAreaContext.cxx | 21 + xmloff/source/chart/SchXMLPlotAreaContext.hxx | 4 xmloff/source/chart/SchXMLSeries2Context.cxx | 25 + xmloff/source/chart/transporttypes.hxx | 10 xmloff/source/core/xmltoken.cxx | 2 xmloff/source/token/tokens.txt | 2 24 files changed, 499 insertions(+), 64 deletions(-)
New commits: commit 74324c80b33d20721285bd1e4afe244082ae468e Author: Michael Stahl <[email protected]> AuthorDate: Wed Aug 25 11:16:46 2021 +0200 Commit: Andras Timar <[email protected]> CommitDate: Mon Aug 30 10:27:16 2021 +0200 openssl: upgrade to release 1.1.1l Fixes CVE-2021-3712, CVE-2021-3711 (not obvious if any of them affect LO) Change-Id: I98652348977a5a3c728f1d4fdf7293b76a93b630 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121026 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 5c391f4346e86bd5d7528fbb42a3af64f98a03d3) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120987 Reviewed-by: Caolán McNamara <[email protected]> diff --git a/download.lst b/download.lst index a2a08016976e..82dc3e21626f 100644 --- a/download.lst +++ b/download.lst @@ -226,8 +226,8 @@ export OFFICEOTRON_SHA256SUM := f2443f27561af52324eee03a1892d9f569adc8db9e7bca55 export OFFICEOTRON_JAR := 8249374c274932a21846fa7629c2aa9b-officeotron-0.7.4-master.jar export OPENLDAP_SHA256SUM := cdd6cffdebcd95161a73305ec13fc7a78e9707b46ca9f84fb897cd5626df3824 export OPENLDAP_TARBALL := openldap-2.4.45.tgz -export OPENSSL_SHA256SUM := 892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 -export OPENSSL_TARBALL := openssl-1.1.1k.tar.gz +export OPENSSL_SHA256SUM := 0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1 +export OPENSSL_TARBALL := openssl-1.1.1l.tar.gz export ORCUS_SHA256SUM := c700d1325f744104d9fca0d5a019434901e9d51a16eedfb05792f90a298587a4 export ORCUS_TARBALL := liborcus-0.16.1.tar.bz2 export OWNCLOUD_ANDROID_LIB_SHA256SUM := b18b3e3ef7fae6a79b62f2bb43cc47a5346b6330f6a383dc4be34439aca5e9fb commit 5b04f8fbd7e9e41c653734b03c9c930b5d75908b Author: Caolán McNamara <[email protected]> AuthorDate: Thu Aug 26 16:12:42 2021 +0100 Commit: Andras Timar <[email protected]> CommitDate: Mon Aug 30 10:27:10 2021 +0200 tdf#139660 one of the group needs to be active in this case using ToolButtons instead of ToggleToolButtons will work ok Change-Id: I806a8fc409ae346dd6734be13d69c4b171e54c08 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121123 Tested-by: Jenkins Reviewed-by: Adolfo Jayme Barrientos <[email protected]> diff --git a/svx/uiconfig/ui/sidebarparagraph.ui b/svx/uiconfig/ui/sidebarparagraph.ui index 2db34a7d6ef3..1f34c35b1c67 100644 --- a/svx/uiconfig/ui/sidebarparagraph.ui +++ b/svx/uiconfig/ui/sidebarparagraph.ui @@ -55,7 +55,7 @@ <property name="show_arrow">False</property> <property name="icon_size">2</property> <child> - <object class="GtkToggleToolButton" id=".uno:LeftPara"> + <object class="GtkToolButton" id=".uno:LeftPara"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -66,7 +66,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:CenterPara"> + <object class="GtkToolButton" id=".uno:CenterPara"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -77,7 +77,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:RightPara"> + <object class="GtkToolButton" id=".uno:RightPara"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -88,7 +88,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:JustifyPara"> + <object class="GtkToolButton" id=".uno:JustifyPara"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -115,7 +115,7 @@ <property name="show_arrow">False</property> <property name="icon_size">2</property> <child> - <object class="GtkToggleToolButton" id=".uno:ParaLeftToRight"> + <object class="GtkToolButton" id=".uno:ParaLeftToRight"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -126,7 +126,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:ParaRightToLeft"> + <object class="GtkToolButton" id=".uno:ParaRightToLeft"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -152,7 +152,7 @@ <property name="show_arrow">False</property> <property name="icon_size">2</property> <child> - <object class="GtkToggleToolButton" id=".uno:CellVertTop"> + <object class="GtkToolButton" id=".uno:CellVertTop"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -163,7 +163,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:CellVertCenter"> + <object class="GtkToolButton" id=".uno:CellVertCenter"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> @@ -174,7 +174,7 @@ </packing> </child> <child> - <object class="GtkToggleToolButton" id=".uno:CellVertBottom"> + <object class="GtkToolButton" id=".uno:CellVertBottom"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> commit 2ca9ba73573a1b579b1711ce72dc5859d2dfea52 Author: Dennis Francis <[email protected]> AuthorDate: Wed Aug 25 20:51:56 2021 +0530 Commit: Andras Timar <[email protected]> CommitDate: Mon Aug 30 10:26:40 2021 +0200 tdf#143942: oox: import/export labels from <c15:datalabelsRange> When <c15:showDataLabelsRange> boolean flag is present, the imported label texts are added as the first text field in oox data label model. The cell-range associated is also preserved. The export part preserves the how labels were store originally in <c15:datalabelsRange>. However in order to make the custom labels reflect the contents of the cells in the associated cell-range, more work needs to be done. For this the labels present in <c15:datalabelsRange> needs to be made available as a data-sequence with a new "role" like "point-labels" in XInternalDataProvider implementation and and make the label renderer read this data source rather than consulting the custom label fields property which is static after import. Change-Id: Ibc7045fa5ea209d463680c96efb49a06662d2500 diff --git a/chart2/source/model/main/FormattedString.cxx b/chart2/source/model/main/FormattedString.cxx index 0d6e733a2c31..55960c081336 100644 --- a/chart2/source/model/main/FormattedString.cxx +++ b/chart2/source/model/main/FormattedString.cxx @@ -100,6 +100,7 @@ FormattedString::FormattedString() : m_aString(), m_aType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT), m_aGuid(), + m_bDataLabelsRange(false), m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder()) {} @@ -109,6 +110,7 @@ FormattedString::FormattedString( const FormattedString & rOther ) : m_aString( rOther.m_aString ), m_aType(rOther.m_aType), m_aGuid(rOther.m_aGuid), + m_bDataLabelsRange(rOther.m_bDataLabelsRange), m_xModifyEventForwarder( ModifyListenerHelper::createModifyEventForwarder()) {} @@ -174,6 +176,22 @@ void SAL_CALL FormattedString::setGuid( const OUString& guid ) } +sal_Bool SAL_CALL FormattedString::getDataLabelsRange() +{ + MutexGuard aGuard( m_aMutex); + return m_bDataLabelsRange; +} + +void SAL_CALL FormattedString::setDataLabelsRange( sal_Bool dataLabelsRange ) +{ + { + MutexGuard aGuard( m_aMutex); + m_bDataLabelsRange = dataLabelsRange; + } + //don't keep the mutex locked while calling out + fireModifyEvent(); +} + // ____ XModifyBroadcaster ____ void SAL_CALL FormattedString::addModifyListener( const uno::Reference< util::XModifyListener >& aListener ) { diff --git a/chart2/source/model/main/FormattedString.hxx b/chart2/source/model/main/FormattedString.hxx index a7f9e36bb134..763c929d6589 100644 --- a/chart2/source/model/main/FormattedString.hxx +++ b/chart2/source/model/main/FormattedString.hxx @@ -87,6 +87,8 @@ private: setFieldType( const css::chart2::DataPointCustomLabelFieldType FieldType ) override; virtual OUString SAL_CALL getGuid() override; void SAL_CALL setGuid( const OUString& guid ) override; + virtual sal_Bool SAL_CALL getDataLabelsRange() override; + virtual void SAL_CALL setDataLabelsRange( sal_Bool dataLabelsRange ) override; // ____ OPropertySet ____ virtual css::uno::Any GetDefaultValue( sal_Int32 nHandle ) const override; @@ -127,6 +129,7 @@ private: // ____ XDataPointCustomLabelField ____ css::chart2::DataPointCustomLabelFieldType m_aType; OUString m_aGuid; + bool m_bDataLabelsRange; css::uno::Reference< css::util::XModifyListener > m_xModifyEventForwarder; }; diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx index f405396e113c..6f2b3f820ff5 100644 --- a/chart2/source/view/charttypes/VSeriesPlotter.cxx +++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx @@ -502,6 +502,7 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re Sequence< OUString > aTextList( nTextListLength ); bool bUseCustomLabel = nCustomLabelsCount > 0; + size_t nVisibleFieldLen = 0; if( bUseCustomLabel ) { nTextListLength = ( nCustomLabelsCount > 3 ) ? nCustomLabelsCount : 3; @@ -509,6 +510,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re aTextList = Sequence< OUString >( nTextListLength ); for( sal_uInt32 i = 0; i < nCustomLabelsCount; ++i ) { + ++nVisibleFieldLen; + bool bPreserveContent = false; switch( aCustomLabels[i]->getFieldType() ) { case DataPointCustomLabelFieldType_VALUE: @@ -541,8 +544,16 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re aTextList[i] = getLabelTextForValue(rDataSeries, nPointIndex, fValue, true); break; } - case DataPointCustomLabelFieldType_CELLREF: case DataPointCustomLabelFieldType_CELLRANGE: + { + if (aCustomLabels[i]->getDataLabelsRange()) + { + bPreserveContent = true; + --nVisibleFieldLen; + } + } + [[fallthrough]]; + case DataPointCustomLabelFieldType_CELLREF: { // TODO: for now doesn't show placeholder aTextList[i] = OUString(); @@ -561,7 +572,8 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re default: break; } - aCustomLabels[i]->setString( aTextList[i] ); + if (!bPreserveContent) + aCustomLabels[i]->setString( aTextList[i] ); } } else @@ -613,10 +625,13 @@ uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Re if( bUseCustomLabel ) { - Sequence< uno::Reference< XFormattedString > > aFormattedLabels( aCustomLabels.getLength() ); - for( int i = 0; i < aFormattedLabels.getLength(); i++ ) + Sequence< uno::Reference< XFormattedString > > aFormattedLabels( nVisibleFieldLen ); + int nFormattedIndex = 0; + for( int i = 0; i < aCustomLabels.getLength(); i++ ) { - aFormattedLabels[i] = aCustomLabels[i]; + if (aCustomLabels[i]->getFieldType() == DataPointCustomLabelFieldType_CELLRANGE && aCustomLabels[i]->getDataLabelsRange()) + continue; + aFormattedLabels[nFormattedIndex++] = aCustomLabels[i]; } // create text shape diff --git a/include/oox/export/chartexport.hxx b/include/oox/export/chartexport.hxx index b900c68b51a2..3771809c9d87 100644 --- a/include/oox/export/chartexport.hxx +++ b/include/oox/export/chartexport.hxx @@ -90,6 +90,29 @@ struct AxisIdPair{ {} }; +class DataLabelsRange +{ +public: + + typedef std::map<sal_Int32, OUString> LabelsRangeMap; + + bool empty() const; + size_t count() const; + bool hasLabel(sal_Int32 nIndex) const; + OUString getRange() const; + + void setRange(const OUString& rRange); + void setLabel(sal_Int32 nIndex, const OUString& rText); + + LabelsRangeMap::const_iterator begin() const; + LabelsRangeMap::const_iterator end() const; + +private: + OUString maRange; + LabelsRangeMap maLabels; +}; + + class OOX_DLLPUBLIC ChartExport final : public DrawingML { public: @@ -182,7 +205,8 @@ private: void exportDataPoints( const css::uno::Reference< css::beans::XPropertySet >& xSeriesProperties, sal_Int32 nSeriesLength, sal_Int32 eChartType ); - void exportDataLabels( const css::uno::Reference<css::chart2::XDataSeries>& xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType ); + void exportDataLabels( const css::uno::Reference<css::chart2::XDataSeries>& xSeries, sal_Int32 nSeriesLength, + sal_Int32 eChartType, DataLabelsRange& rDLblsRange ); void exportGrouping( bool isBar = false ); void exportTrendlines( const css::uno::Reference< css::chart2::XDataSeries >& xSeries ); void exportMarker( const css::uno::Reference< css::beans::XPropertySet >& xPropSet ); diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 1b145de4854a..1f4739c770e9 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -574,9 +574,11 @@ namespace xmloff::token { XML_DATA_BAR_ENTRY, XML_DATA_CELL_RANGE_ADDRESS, XML_DATA_LABEL, + XML_DATA_LABEL_GUID, XML_DATA_LABEL_NUMBER, XML_DATA_LABEL_SYMBOL, XML_DATA_LABEL_TEXT, + XML_DATA_LABELS_CELL_RANGE, XML_DATA_PILOT_SOURCE, XML_DATA_PILOT_FIELD, XML_DATA_PILOT_GRAND_TOTAL, diff --git a/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl index a6a1b0151c94..376e4ad751d2 100644 --- a/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl +++ b/offapi/com/sun/star/chart2/XDataPointCustomLabelField.idl @@ -29,6 +29,16 @@ interface XDataPointCustomLabelField : XFormattedString2 void setGuid( [in] string guid ); + /** + @since LibreOffice 7.3 + */ + boolean getDataLabelsRange(); + + /** + @since LibreOffice 7.3 + */ + void setDataLabelsRange( [in] boolean dataLabelsRange ); + }; diff --git a/oox/inc/drawingml/chart/datasourcecontext.hxx b/oox/inc/drawingml/chart/datasourcecontext.hxx index 8c893c1973a8..c90122282a2f 100644 --- a/oox/inc/drawingml/chart/datasourcecontext.hxx +++ b/oox/inc/drawingml/chart/datasourcecontext.hxx @@ -67,6 +67,7 @@ public: private: sal_Int32 mnPtIndex; /// Current data point index. + bool mbReadC15; /// Allow reading extension tags data under c15 namespace. }; diff --git a/oox/inc/drawingml/chart/seriesmodel.hxx b/oox/inc/drawingml/chart/seriesmodel.hxx index c4fb557d282d..460293a5feee 100644 --- a/oox/inc/drawingml/chart/seriesmodel.hxx +++ b/oox/inc/drawingml/chart/seriesmodel.hxx @@ -41,12 +41,18 @@ struct DataLabelModelBase OptValue< bool > mobShowPercent; /// True = show percentual value in pie/doughnut charts. OptValue< bool > mobShowSerName; /// True = show series name. OptValue< bool > mobShowVal; /// True = show data point value. + + /// True = the value from the <c15:datalabelsRange> corresponding to the + /// index of this label is used as the label text. + OptValue< bool > mobShowDataLabelsRange; bool mbDeleted; /// True = data label(s) deleted. explicit DataLabelModelBase(bool bMSO2007Doc); ~DataLabelModelBase(); }; +struct DataLabelsModel; + struct DataLabelModel : public DataLabelModelBase { typedef ModelRef< LayoutModel > LayoutRef; @@ -54,9 +60,10 @@ struct DataLabelModel : public DataLabelModelBase LayoutRef mxLayout; /// Layout/position of the data point label frame. TextRef mxText; /// Manual or linked text for this data point label. + const DataLabelsModel& mrParent; /// Reference to the labels container. sal_Int32 mnIndex; /// Data point index for this data label. - explicit DataLabelModel(bool bMSO2007Doc); + explicit DataLabelModel(const DataLabelsModel& rParent, bool bMSO2007Doc); ~DataLabelModel(); }; @@ -67,6 +74,9 @@ struct DataLabelsModel : public DataLabelModelBase DataLabelVector maPointLabels; /// Settings for individual data point labels. ShapeRef mxLeaderLines; /// Formatting of connector lines between data points and labels. + + /// Labels source (owned by SeriesModel's DataSourceMap) + const DataSourceModel* mpLabelsSource; bool mbShowLeaderLines; /// True = show connector lines between data points and labels. explicit DataLabelsModel(bool bMSO2007Doc); @@ -171,7 +181,8 @@ struct SeriesModel { CATEGORIES, /// Data point categories. VALUES, /// Data point values. - POINTS /// Data point size (e.g. bubble size in bubble charts). + POINTS, /// Data point size (e.g. bubble size in bubble charts). + DATALABELS, /// Data point labels. }; typedef ModelMap< SourceType, DataSourceModel > DataSourceMap; diff --git a/oox/source/drawingml/chart/datasourcecontext.cxx b/oox/source/drawingml/chart/datasourcecontext.cxx index e9f2f3b189f6..15ca02975137 100644 --- a/oox/source/drawingml/chart/datasourcecontext.cxx +++ b/oox/source/drawingml/chart/datasourcecontext.cxx @@ -165,6 +165,7 @@ SvNumberFormatter* DoubleSequenceContext::getNumberFormatter() StringSequenceContext::StringSequenceContext( ContextHandler2Helper& rParent, DataSequenceModel& rModel ) : DataSequenceContextBase( rParent, rModel ) , mnPtIndex(-1) + , mbReadC15(false) { } @@ -185,6 +186,16 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co } break; + case C15_TOKEN( datalabelsRange ): + mbReadC15 = true; + switch( nElement ) + { + case C15_TOKEN( f ): + case C15_TOKEN( dlblRangeCache ): + return this; + } + break; + case C_TOKEN( strRef ): switch( nElement ) { @@ -196,6 +207,10 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co case C_TOKEN( strCache ): case C_TOKEN( strLit ): + case C15_TOKEN( dlblRangeCache ): + if (nElement == C15_TOKEN( dlblRangeCache ) && !mbReadC15) + break; + switch( nElement ) { case C_TOKEN( ptCount ): @@ -247,6 +262,10 @@ void StringSequenceContext::onCharacters( const OUString& rChars ) case C_TOKEN( f ): mrModel.maFormula = rChars; break; + case C15_TOKEN( f ): + if (mbReadC15) + mrModel.maFormula = rChars; + break; case C_TOKEN( v ): if( mnPtIndex >= 0 ) mrModel.maData[ (mrModel.mnLevelCount-1) * mrModel.mnPointCount + mnPtIndex ] <<= rChars; @@ -269,11 +288,13 @@ ContextHandlerRef DataSourceContext::onCreateContext( sal_Int32 nElement, const { case C_TOKEN( cat ): case C_TOKEN( xVal ): + case C_TOKEN( ext ): switch( nElement ) { case C_TOKEN( multiLvlStrRef ): case C_TOKEN( strLit ): case C_TOKEN( strRef ): + case C15_TOKEN( datalabelsRange ): OSL_ENSURE( !mrModel.mxDataSeq, "DataSourceContext::onCreateContext - multiple data sequences" ); return new StringSequenceContext( *this, mrModel.mxDataSeq.create() ); diff --git a/oox/source/drawingml/chart/seriescontext.cxx b/oox/source/drawingml/chart/seriescontext.cxx index 3f92818c5544..5afc32c1497d 100644 --- a/oox/source/drawingml/chart/seriescontext.cxx +++ b/oox/source/drawingml/chart/seriescontext.cxx @@ -100,15 +100,37 @@ DataLabelContext::~DataLabelContext() ContextHandlerRef DataLabelContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) { - if( isRootElement() ) switch( nElement ) + if( isRootElement() ) { - case C_TOKEN( idx ): - mrModel.mnIndex = rAttribs.getInteger( XML_val, -1 ); - return nullptr; - case C_TOKEN( layout ): - return new LayoutContext( *this, mrModel.mxLayout.create() ); - case C_TOKEN( tx ): - return new TextContext( *this, mrModel.mxText.create() ); + switch( nElement ) + { + case C_TOKEN( idx ): + mrModel.mnIndex = rAttribs.getInteger( XML_val, -1 ); + return nullptr; + case C_TOKEN( layout ): + return new LayoutContext( *this, mrModel.mxLayout.create() ); + case C_TOKEN( tx ): + return new TextContext( *this, mrModel.mxText.create() ); + case C_TOKEN( extLst ): + return this; + } + } + else + { + switch( getCurrentElement() ) + { + case C_TOKEN( extLst ): + if ( nElement == C_TOKEN( ext ) ) + return this; + break; + case C_TOKEN( ext ): + if ( nElement == C15_TOKEN( showDataLabelsRange ) ) + { + mrModel.mobShowDataLabelsRange = rAttribs.getBool( XML_val ); + return nullptr; + } + break; + } } bool bMSO2007 = getFilter().isMSO2007Document(); return lclDataLabelSharedCreateContext( *this, nElement, rAttribs, mrModel, bMSO2007 ); @@ -135,7 +157,7 @@ ContextHandlerRef DataLabelsContext::onCreateContext( sal_Int32 nElement, const if( isRootElement() ) switch( nElement ) { case C_TOKEN( dLbl ): - return new DataLabelContext( *this, mrModel.maPointLabels.create(bMSO2007Doc) ); + return new DataLabelContext( *this, mrModel.maPointLabels.create(mrModel, bMSO2007Doc) ); case C_TOKEN( leaderLines ): return new ShapePrWrapperContext( *this, mrModel.mxLeaderLines.create() ); case C_TOKEN( showLeaderLines ): @@ -390,6 +412,8 @@ ContextHandlerRef SeriesContextBase::onCreateContext( sal_Int32 nElement, const return new ShapePropertiesContext( *this, mrModel.mxShapeProp.create() ); case C_TOKEN( tx ): return new TextContext( *this, mrModel.mxText.create() ); + case C_TOKEN( extLst ): + return this; } break; @@ -406,6 +430,19 @@ ContextHandlerRef SeriesContextBase::onCreateContext( sal_Int32 nElement, const return nullptr; } break; + + case C_TOKEN( extLst ): + switch( nElement ) + { + case C_TOKEN( ext ): + if (mrModel.maSources.has( SeriesModel::DATALABELS )) + break; + + DataSourceModel& rLabelsSource = mrModel.maSources.create( SeriesModel::DATALABELS ); + if (mrModel.mxLabels.is()) + mrModel.mxLabels->mpLabelsSource = &rLabelsSource; + return new DataSourceContext( *this, rLabelsSource ); + } } return nullptr; } diff --git a/oox/source/drawingml/chart/seriesconverter.cxx b/oox/source/drawingml/chart/seriesconverter.cxx index 2de8ee82d2f9..6f57cc8e73e4 100644 --- a/oox/source/drawingml/chart/seriesconverter.cxx +++ b/oox/source/drawingml/chart/seriesconverter.cxx @@ -318,9 +318,47 @@ void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDat if( nParagraphs > 1 ) nSequenceSize += nParagraphs - 1; + OptValue< OUString > oaLabelText; + OptValue< OUString > oaCellRange; + if (mrModel.mobShowDataLabelsRange.get(false)) + { + const DataSourceModel* pLabelSource = mrModel.mrParent.mpLabelsSource; + if (pLabelSource && pLabelSource->mxDataSeq.is()) + { + oaCellRange = pLabelSource->mxDataSeq->maFormula; + const auto& rLabelMap = pLabelSource->mxDataSeq->maData; + const auto& rKV = rLabelMap.find(mrModel.mnIndex); + if (rKV != rLabelMap.end()) + rKV->second >>= oaLabelText.use(); + } + } + + if (oaLabelText.has()) + ++nSequenceSize; + aSequence.realloc( nSequenceSize ); int nPos = 0; + if (oaLabelText.has()) + { + // Insert a "text" custom label field with the text found in the labels data source. + css::uno::Reference< XDataPointCustomLabelField > xCustomLabelText = DataPointCustomLabelField::create( xContext ); + + // Store properties + oox::PropertySet aPropertySet( xCustomLabelText ); + convertTextProperty( aPropertySet, getFormatter(), mrModel.mxText->mxTextBody ); + if (nParagraphs && rParagraphs[0]) + { + auto& pRuns = rParagraphs[0]->getRuns(); + if (pRuns.size()) + pRuns[0]->getTextCharacterProperties().pushToPropSet( aPropertySet, getFilter() ); + } + xCustomLabelText->setString(oaLabelText.get()); + xCustomLabelText->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT ); + + aSequence[ nPos++ ] = xCustomLabelText; + } + for( auto& pParagraph : rParagraphs ) { for( auto& pRun : pParagraph->getRuns() ) @@ -335,8 +373,17 @@ void DataLabelConverter::convertFromModel( const Reference< XDataSeries >& rxDat TextField* pField = nullptr; if( ( pField = dynamic_cast< TextField* >( pRun.get() ) ) ) { - xCustomLabel->setString( pField->getText() ); - xCustomLabel->setFieldType( lcl_ConvertFieldNameToFieldEnum( pField->getType() ) ); + DataPointCustomLabelFieldType eType = lcl_ConvertFieldNameToFieldEnum( pField->getType() ); + + if (eType == DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE && oaCellRange.has()) + { + xCustomLabel->setString( oaCellRange.get() ); + xCustomLabel->setDataLabelsRange( true ); + } + else + xCustomLabel->setString( pField->getText() ); + + xCustomLabel->setFieldType( eType ); xCustomLabel->setGuid( pField->getUuid() ); } else if( pRun ) diff --git a/oox/source/drawingml/chart/seriesmodel.cxx b/oox/source/drawingml/chart/seriesmodel.cxx index 563e0e48e3bf..4ad9b2593af4 100644 --- a/oox/source/drawingml/chart/seriesmodel.cxx +++ b/oox/source/drawingml/chart/seriesmodel.cxx @@ -31,8 +31,9 @@ DataLabelModelBase::~DataLabelModelBase() { } -DataLabelModel::DataLabelModel(bool bMSO2007Doc) : +DataLabelModel::DataLabelModel(const DataLabelsModel& rParent, bool bMSO2007Doc) : DataLabelModelBase(bMSO2007Doc), + mrParent( rParent ), mnIndex( -1 ) { } @@ -43,6 +44,7 @@ DataLabelModel::~DataLabelModel() DataLabelsModel::DataLabelsModel(bool bMSO2007Doc) : DataLabelModelBase(bMSO2007Doc), + mpLabelsSource( nullptr ), mbShowLeaderLines( !bMSO2007Doc ) { } diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index 38295504105b..18fbfad9e7a9 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -485,6 +485,46 @@ static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient& rGrad return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; } +bool DataLabelsRange::empty() const +{ + return maLabels.empty(); +} + +size_t DataLabelsRange::count() const +{ + return maLabels.size(); +} + +bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const +{ + return maLabels.find(nIndex) != maLabels.end(); +} + +OUString DataLabelsRange::getRange() const +{ + return maRange; +} + +void DataLabelsRange::setRange(const OUString& rRange) +{ + maRange = rRange; +} + +void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText) +{ + maLabels.emplace(nIndex, rText); +} + +DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const +{ + return maLabels.begin(); +} + +DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const +{ + return maLabels.end(); +} + ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType ) : DrawingML( std::move(pFS), pFB, eDocumentType ) , mnXmlNamespace( nXmlNamespace ) @@ -2047,6 +2087,40 @@ std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(co return aSplitSeries; } +void writeDataLabelsRange(FSHelperPtr& pFS, XmlFilterBase* pFB, DataLabelsRange& rDLblsRange) +{ + if (rDLblsRange.empty()) + return; + + pFS->startElement(FSNS(XML_c, XML_extLst)); + pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15))); + // pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines)); + pFS->startElement(FSNS(XML_c15, XML_datalabelsRange)); + + // Write cell range. + pFS->startElement(FSNS(XML_c15, XML_f)); + pFS->writeEscaped(rDLblsRange.getRange()); + pFS->endElement(FSNS(XML_c15, XML_f)); + + // Write all labels. + pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache)); + pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count())); + for (const auto& rLabelKV: rDLblsRange) + { + pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first)); + pFS->startElement(FSNS(XML_c, XML_v)); + pFS->writeEscaped(rLabelKV.second); + pFS->endElement(FSNS( XML_c, XML_v )); + pFS->endElement(FSNS(XML_c, XML_pt)); + } + + pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache)); + + pFS->endElement(FSNS(XML_c15, XML_datalabelsRange)); + pFS->endElement(FSNS(XML_c, XML_ext)); + pFS->endElement(FSNS(XML_c, XML_extLst)); +} + } void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType ) @@ -2431,8 +2505,9 @@ void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType, // export data points exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType ); + DataLabelsRange aDLblsRange; // export data labels - exportDataLabels(rSeries, nSeriesLength, eChartType); + exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange); exportTrendlines( rSeries ); @@ -2505,6 +2580,9 @@ void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType, if( eChartType == chart::TYPEID_BUBBLE ) pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0"); + if (!aDLblsRange.empty()) + writeDataLabelsRange(pFS, GetFB(), aDLblsRange); + pFS->endElement( FSNS( XML_c, XML_ser ) ); } } @@ -3421,7 +3499,8 @@ void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> cons } void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, - const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields ) + const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields, + sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange ) { pFS->startElement(FSNS(XML_c, XML_tx)); pFS->startElement(FSNS(XML_c, XML_rich)); @@ -3430,15 +3509,54 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, pFS->singleElement(FSNS(XML_a, XML_bodyPr)); OUString sFieldType; + OUString sContent; + OUString sUid; + bool bShowDataLabelsRange = false; pFS->startElement(FSNS(XML_a, XML_p)); + for (auto& rField : rCustomLabelFields) + { + if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE && + rField->getDataLabelsRange()) + bShowDataLabelsRange = true; + } + for (auto& rField : rCustomLabelFields) { Reference<XPropertySet> xPropertySet(rField, UNO_QUERY); chart2::DataPointCustomLabelFieldType aType = rField->getFieldType(); sFieldType.clear(); + sContent.clear(); + sUid.clear(); bool bNewParagraph = false; + if (bShowDataLabelsRange) + { + if (aType == chart2::DataPointCustomLabelFieldType_TEXT) + { + if (rDLblsRange.hasLabel(nLabelIndex)) + continue; + + rDLblsRange.setLabel(nLabelIndex, rField->getString()); + // Don't write the text field here. + // This is written under the extension root <c15:datalabelsRange> + continue; + } + + if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE) + { + if (rDLblsRange.getRange().isEmpty()) + rDLblsRange.setRange(rField->getString()); + sContent = "[CELLRANGE]"; + } + } + else + { + sContent = rField->getString(); + } + + sUid = rField->getGuid(); + if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE) bNewParagraph = true; else if (aType != chart2::DataPointCustomLabelFieldType_TEXT) @@ -3458,7 +3576,7 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, writeRunProperties(pChartExport, xPropertySet); pFS->startElement(FSNS(XML_a, XML_t)); - pFS->writeEscaped(rField->getString()); + pFS->writeEscaped(sContent); pFS->endElement(FSNS(XML_a, XML_t)); pFS->endElement(FSNS(XML_a, XML_r)); @@ -3466,12 +3584,12 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, else { // Field - pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type, + pFS->startElement(FSNS(XML_a, XML_fld), XML_id, sUid, XML_type, sFieldType); writeRunProperties(pChartExport, xPropertySet); pFS->startElement(FSNS(XML_a, XML_t)); - pFS->writeEscaped(rField->getString()); + pFS->writeEscaped(sContent); pFS->endElement(FSNS(XML_a, XML_t)); pFS->endElement(FSNS(XML_a, XML_fld)); @@ -3484,7 +3602,8 @@ void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, } void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport, - const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam ) + const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam, + sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange ) { if (!xPropSet.is()) return; @@ -3539,7 +3658,7 @@ void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport, pChartExport->exportTextProps(xPropSet); if (aCustomLabelFields.hasElements()) - writeCustomLabel(pFS, pChartExport, aCustomLabelFields); + writeCustomLabel(pFS, pChartExport, aCustomLabelFields, nLabelIndex, rDLblsRange); if (rLabelParam.mbExport) { @@ -3568,12 +3687,26 @@ void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport, pFS->writeEscaped( nLabelSeparator ); pFS->endElement( FSNS( XML_c, XML_separator ) ); } + + if (rDLblsRange.hasLabel(nLabelIndex)) + { + pFS->startElement(FSNS(XML_c, XML_extLst)); + pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, + "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15), + pChartExport->GetFB()->getNamespaceURL(OOX_NS(c15))); + + pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1"); + + pFS->endElement(FSNS(XML_c, XML_ext)); + pFS->endElement(FSNS(XML_c, XML_extLst)); + } } } void ChartExport::exportDataLabels( - const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType ) + const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType, + DataLabelsRange& rDLblsRange) { if (!xSeries.is() || nSeriesLength <= 0) return; @@ -3704,12 +3837,12 @@ void ChartExport::exportDataLabels( } // Individual label property that overwrites the baseline. - writeLabelProperties(pFS, this, xLabelPropSet, aParam); + writeLabelProperties(pFS, this, xLabelPropSet, aParam, nIdx, rDLblsRange); pFS->endElement(FSNS(XML_c, XML_dLbl)); } // Baseline label properties for all labels. - writeLabelProperties(pFS, this, xPropSet, aParam); + writeLabelProperties(pFS, this, xPropSet, aParam, -1, rDLblsRange); bool bShowLeaderLines = false; xPropSet->getPropertyValue("ShowCustomLeaderLines") >>= bShowLeaderLines; diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt index 76dad54c8904..3bd4106b9c44 100644 --- a/oox/source/token/tokens.txt +++ b/oox/source/token/tokens.txt @@ -1656,6 +1656,7 @@ dataValidation dataValidations database databaseField +datalabelsRange datastoreItem date date1904 @@ -1871,6 +1872,7 @@ dkTurquoise dkUpDiag dkVert dkViolet +dlblRangeCache dllVersion dm dn @@ -4645,6 +4647,7 @@ showComments showDLblsOverMax showDataAs showDataDropDown +showDataLabelsRange showDataTips showDrill showDropDown diff --git a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng index 89913cbf7e63..19653c45cd35 100644 --- a/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng +++ b/schema/libreoffice/OpenDocument-schema-v1.3+libreoffice.rng @@ -2493,4 +2493,16 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. </rng:optional> </rng:define> + <!-- TODO no proposal --> + <rng:define name="chart-data-label-attlist" combine="interleave"> + <rng:optional> + <rng:attribute name="loext:data-label-guid"> + <rng:ref name="string"/> + </rng:attribute> + <rng:attribute name="loext:data-labels-cell-range"> + <rng:ref name="cellRangeAddress"/> + </rng:attribute> + </rng:optional> + </rng:define> + </rng:grammar> diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx index 0ee68425c6fc..3c41c52fefcf 100644 --- a/xmloff/source/chart/SchXMLExport.cxx +++ b/xmloff/source/chart/SchXMLExport.cxx @@ -113,7 +113,18 @@ using ::std::vector; namespace { - using CustomLabelSeq = Sequence<Reference<chart2::XDataPointCustomLabelField>>; + struct CustomLabelData + { + CustomLabelData(): + mbDataLabelsRange( false ) + { + } + + Sequence<Reference<chart2::XDataPointCustomLabelField>> maFields; + bool mbDataLabelsRange; + OUString maRange; + OUString maGuid; + }; struct SchXMLDataPointStruct { @@ -123,7 +134,7 @@ namespace // There is no internal equivalent for <chart:data-label>. It will be generated on the fly // on export. All about data label is hold in the data point. - CustomLabelSeq mCustomLabelText; // <text:p> child element in <chart:data-label> + CustomLabelData mCustomLabel; // <text:p> child element in <chart:data-label> OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label> SchXMLDataPointStruct() : mnRepeat( 1 ) {} @@ -282,30 +293,58 @@ public: namespace { -CustomLabelSeq lcl_getCustomLabelField(SvXMLExport const& rExport, +CustomLabelData lcl_getCustomLabelField(SvXMLExport const& rExport, sal_Int32 nDataPointIndex, const uno::Reference< chart2::XDataSeries >& rSeries) { if (!rSeries.is()) - return CustomLabelSeq(); + return CustomLabelData(); // Custom data label text will be written to the <text:p> child element of a // <chart:data-label> element. That exists only since ODF 1.2. const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( rExport.getSaneDefaultVersion()); if (nCurrentODFVersion < SvtSaveOptions::ODFSVER_012) - return CustomLabelSeq(); + return CustomLabelData(); if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is()) { if(Any aAny = xLabels->getPropertyValue("CustomLabelFields"); aAny.hasValue()) { + CustomLabelData aData; + uno::Reference<chart2::XDataPointCustomLabelField> aTextField; Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels; aAny >>= aCustomLabels; - return aCustomLabels; + for (auto& rField: aCustomLabels) + { + if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE) + { + if (rField->getDataLabelsRange()) + aData.mbDataLabelsRange = true; + aData.maRange = rField->getString(); + aData.maGuid = rField->getGuid(); + } + + if (!aTextField.is() && rField->getFieldType() == chart2::DataPointCustomLabelFieldType_TEXT) + { + aTextField = rField; + } + } + + if (aData.mbDataLabelsRange) + { + aData.maFields = Sequence<uno::Reference<chart2::XDataPointCustomLabelField>>(1); + aData.maFields[0] = aTextField; + } + else + { + aData.maFields = aCustomLabels; + } + + return aData; } } - return CustomLabelSeq(); + return CustomLabelData(); } css::chart2::RelativePosition lcl_getCustomLabelPosition( @@ -3466,7 +3505,7 @@ void SchXMLExportHelper_Impl::exportDataPoints( maAutoStyleNameQueue.pop(); } if(bExportNumFmt) - aPoint.mCustomLabelText = lcl_getCustomLabelField(mrExport, nElement, xSeries); + aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries); aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries); aDataPointVector.push_back( aPoint ); @@ -3545,7 +3584,7 @@ void SchXMLExportHelper_Impl::exportDataPoints( aPoint.maStyleName = maAutoStyleNameQueue.front(); maAutoStyleNameQueue.pop(); } - aPoint.mCustomLabelText = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries); + aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries); aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries); if (!aDataLabelPropertyStates.empty()) { @@ -3601,7 +3640,7 @@ void SchXMLExportHelper_Impl::exportDataPoints( aPoint = rPoint; if (aPoint.maStyleName == aLastPoint.maStyleName - && aLastPoint.mCustomLabelText.getLength() < 1 + && aLastPoint.mCustomLabel.maFields.getLength() < 1 && aLastPoint.mCustomLabelPos.Primary == 0.0 && aLastPoint.mCustomLabelPos.Secondary == 0.0 && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName) @@ -3658,15 +3697,30 @@ void SchXMLExportHelper_Impl::exportDataPoints( void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint) { - if (rPoint.mCustomLabelText.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty()) + if (rPoint.mCustomLabel.maFields.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty()) return; // nothing to export if (!rPoint.msDataLabelStyleName.isEmpty()) mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName); + + if (rPoint.mCustomLabel.mbDataLabelsRange) + { + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABELS_CELL_RANGE, rPoint.mCustomLabel.maRange); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABEL_GUID, rPoint.mCustomLabel.maGuid); + } // TODO svg:x and svg:y for <chart:data-label> SvXMLElementExport aLabelElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, true); SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT, XML_P, true, false ); - for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabelText) + if (rPoint.mCustomLabel.mbDataLabelsRange) + { + // Will have only a single text field in this case. + // TODO add style + SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false); + mrExport.GetDocHandler()->characters(rPoint.mCustomLabel.maFields[0]->getString()); + return; + } + + for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabel.maFields) { // TODO add style SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false); diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.cxx b/xmloff/source/chart/SchXMLPlotAreaContext.cxx index 521fcb58ce41..ce8f915f7c4b 100644 --- a/xmloff/source/chart/SchXMLPlotAreaContext.cxx +++ b/xmloff/source/chart/SchXMLPlotAreaContext.cxx @@ -617,7 +617,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelParaCon } SchXMLDataLabelContext::SchXMLDataLabelContext(SvXMLImport& rImport, - ::std::vector<OUString>& rLabels, + CustomLabelsInfo& rLabels, DataRowPointStyle& rDataLabelStyle) : SvXMLImportContext(rImport) , mrLabels(rLabels) @@ -630,7 +630,7 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelContext const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) { if ( nElement == XML_ELEMENT(TEXT, XML_P) ) - return new SchXMLDataLabelParaContext(GetImport(), mrLabels); + return new SchXMLDataLabelParaContext(GetImport(), mrLabels.mLabels); else XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); return nullptr; @@ -669,6 +669,19 @@ void SchXMLDataLabelContext::StartElement(const uno::Reference<xml::sax::XAttrib mrDataLabelStyle.msStyleName = sValue; } } + else if (nPrefix == XML_NAMESPACE_LO_EXT) + { + if (IsXMLToken(aLocalName, XML_DATA_LABEL_GUID)) + { + mrLabels.msLabelGuid = sValue; + mrLabels.mbDataLabelsRange = true; + } + else if (IsXMLToken(aLocalName, XML_DATA_LABELS_CELL_RANGE)) + { + mrLabels.msLabelsCellRange = sValue; + mrLabels.mbDataLabelsRange = true; + } + } } } @@ -744,7 +757,7 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr if( IsXMLToken( aLocalName, XML_CUSTOM_LABEL_FIELD) && !mbHasLabelParagraph) { sCustomLabelField = xAttrList->getValueByIndex( i ); - mDataPoint.mCustomLabels.push_back(sCustomLabelField); + mDataPoint.mCustomLabels.mLabels.push_back(sCustomLabelField); } else if (IsXMLToken(aLocalName, XML_HIDE_LEGEND)) { @@ -779,7 +792,7 @@ void SchXMLDataPointContext::StartElement( const uno::Reference< xml::sax::XAttr void SchXMLDataPointContext::endFastElement(sal_Int32 ) { - if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.size() > 0) + if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.mLabels.size() > 0) { mrStyleVector.push_back(mDataPoint); } diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.hxx b/xmloff/source/chart/SchXMLPlotAreaContext.hxx index 1e392e8a3b3c..487d2a93a382 100644 --- a/xmloff/source/chart/SchXMLPlotAreaContext.hxx +++ b/xmloff/source/chart/SchXMLPlotAreaContext.hxx @@ -163,11 +163,11 @@ public: class SchXMLDataLabelContext: public SvXMLImportContext { private: - ::std::vector<OUString>& mrLabels; + CustomLabelsInfo& mrLabels; DataRowPointStyle& mrDataLabelStyle; public: SchXMLDataLabelContext(SvXMLImport& rImport, - ::std::vector<OUString>& rLabels, DataRowPointStyle& rDataLabel); + CustomLabelsInfo& rLabels, DataRowPointStyle& rDataLabel); virtual void StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override; diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx index 992a03d64113..e9469e6822ec 100644 --- a/xmloff/source/chart/SchXMLSeries2Context.cxx +++ b/xmloff/source/chart/SchXMLSeries2Context.cxx @@ -1220,15 +1220,20 @@ void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeri } // Custom labels might be passed as property - if(auto nLabelCount = seriesStyle.mCustomLabels.size(); nLabelCount > 0) + if(const size_t nLabelCount = seriesStyle.mCustomLabels.mLabels.size(); nLabelCount > 0) { - Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount); + auto& rCustomLabels = seriesStyle.mCustomLabels; + size_t nFields = nLabelCount; + if (rCustomLabels.mbDataLabelsRange) + ++nFields; // For appending CELLRANGE field. + + Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nFields); Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); - for( auto j = 0; j< xLabels.getLength(); ++j ) + for( size_t j = 0; j < nLabelCount; ++j ) { Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext); xLabels[j] = xCustomLabel; - xCustomLabel->setString(seriesStyle.mCustomLabels[j]); + xCustomLabel->setString(rCustomLabels.mLabels[j]); xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT); // Restore character properties on the text span manually, till @@ -1251,6 +1256,18 @@ void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeri } } } + + if (rCustomLabels.mbDataLabelsRange) + { + // append CELLRANGE field using the attributes of <chart:data-label>. + Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext); + xCustomLabel->setDataLabelsRange(true); + xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE); + xCustomLabel->setString(rCustomLabels.msLabelsCellRange); + xCustomLabel->setGuid(rCustomLabels.msLabelGuid); + xLabels[nFields - 1] = xCustomLabel; + } + xPointProp->setPropertyValue("CustomLabelFields", uno::Any(xLabels)); xPointProp->setPropertyValue("DataCaption", uno::Any(chart::ChartDataCaption::CUSTOM)); } diff --git a/xmloff/source/chart/transporttypes.hxx b/xmloff/source/chart/transporttypes.hxx index 28527c6c06f8..4e395ead292c 100644 --- a/xmloff/source/chart/transporttypes.hxx +++ b/xmloff/source/chart/transporttypes.hxx @@ -146,6 +146,14 @@ struct RegressionStyle {} }; +struct CustomLabelsInfo +{ + ::std::vector<OUString> mLabels; + bool mbDataLabelsRange = false; + OUString msLabelGuid; + OUString msLabelsCellRange; +}; + struct DataRowPointStyle { enum StyleType @@ -171,7 +179,7 @@ struct DataRowPointStyle sal_Int32 m_nPointRepeat; OUString msStyleName; OUString msStyleNameOfParent; // e.g. target of line and fill styles of data-labels - ::std::vector<OUString> mCustomLabels; + CustomLabelsInfo mCustomLabels; double mCustomLabelPos[2] = { 0.0, 0.0 }; // for svg:x and svg:y attribute (in core unit), of element <chart:data-label> std::optional<sal_Int32> mo_nLabelAbsolutePosX; diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 02482b39515e..9600a0689fc5 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -579,9 +579,11 @@ namespace xmloff::token { TOKEN( "data-bar-entry", XML_DATA_BAR_ENTRY ), TOKEN( "data-cell-range-address", XML_DATA_CELL_RANGE_ADDRESS ), TOKEN( "data-label", XML_DATA_LABEL ), + TOKEN( "data-label-guid", XML_DATA_LABEL_GUID ), TOKEN( "data-label-number", XML_DATA_LABEL_NUMBER ), TOKEN( "data-label-symbol", XML_DATA_LABEL_SYMBOL ), TOKEN( "data-label-text", XML_DATA_LABEL_TEXT ), + TOKEN( "data-labels-cell-range", XML_DATA_LABELS_CELL_RANGE ), TOKEN( "data-pilot-source", XML_DATA_PILOT_SOURCE ), TOKEN( "data-pilot-field", XML_DATA_PILOT_FIELD ), TOKEN( "data-pilot-grand-total", XML_DATA_PILOT_GRAND_TOTAL ), diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index ead900650328..bb5e0e89f311 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -491,9 +491,11 @@ data-bar data-bar-entry data-cell-range-address data-label +data-label-guid data-label-number data-label-symbol data-label-text +data-labels-cell-range data-pilot-source data-pilot-field data-pilot-grand-total
