1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <VDataSeries.hxx>
22 #include <ObjectIdentifier.hxx>
23 #include <CommonConverters.hxx>
24 #include <LabelPositionHelper.hxx>
25 #include <ChartTypeHelper.hxx>
26 #include <RegressionCurveHelper.hxx>
27 #include <unonames.hxx>
28 
29 #include <com/sun/star/chart/MissingValueTreatment.hpp>
30 #include <com/sun/star/chart2/DataPointLabel.hpp>
31 #include <com/sun/star/chart2/Symbol.hpp>
32 #include <com/sun/star/chart2/XDataSeries.hpp>
33 #include <com/sun/star/chart2/XRegressionCurveCalculator.hpp>
34 #include <com/sun/star/chart2/RelativePosition.hpp>
35 
36 #include <rtl/math.hxx>
37 #include <osl/diagnose.h>
38 #include <tools/color.hxx>
39 #include <tools/diagnose_ex.h>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/beans/XPropertyState.hpp>
42 #include <com/sun/star/chart2/data/XDataSource.hpp>
43 
44 namespace chart {
45 
46 using namespace ::com::sun::star;
47 using namespace ::com::sun::star::chart2;
48 using ::com::sun::star::uno::Reference;
49 
init(const uno::Reference<data::XDataSequence> & xModel)50 void VDataSequence::init( const uno::Reference< data::XDataSequence >& xModel )
51 {
52     Model = xModel;
53     Doubles = DataSequenceToDoubleSequence( xModel );
54 }
55 
is() const56 bool VDataSequence::is() const
57 {
58     return Model.is();
59 }
clear()60 void VDataSequence::clear()
61 {
62     Model = nullptr;
63     Doubles.realloc(0);
64 }
65 
getValue(sal_Int32 index) const66 double VDataSequence::getValue( sal_Int32 index ) const
67 {
68     if( 0<=index && index<Doubles.getLength() )
69         return Doubles[index];
70     else
71     {
72         double fNan;
73         ::rtl::math::setNan( & fNan );
74         return fNan;
75     }
76 }
77 
detectNumberFormatKey(sal_Int32 index) const78 sal_Int32 VDataSequence::detectNumberFormatKey( sal_Int32 index ) const
79 {
80     sal_Int32 nNumberFormatKey = -1;
81 
82     // -1 is allowed and means a key for the whole sequence
83     if( -1<=index && index<Doubles.getLength() &&
84         Model.is())
85     {
86         nNumberFormatKey = Model->getNumberFormatKeyByIndex( index );
87     }
88 
89     return nNumberFormatKey;
90 }
91 
getLength() const92 sal_Int32 VDataSequence::getLength() const
93 {
94     return Doubles.getLength();
95 }
96 
97 namespace
98 {
99 struct lcl_LessXOfPoint
100 {
operator ()chart::__anon104b489a0111::lcl_LessXOfPoint101     bool operator() ( const std::vector< double >& first,
102                              const std::vector< double >& second )
103     {
104         if( !first.empty() && !second.empty() )
105         {
106             return first[0]<second[0];
107         }
108         return false;
109     }
110 };
111 
lcl_clearIfNoValuesButTextIsContained(VDataSequence & rData,const uno::Reference<data::XDataSequence> & xDataSequence)112 void lcl_clearIfNoValuesButTextIsContained( VDataSequence& rData, const uno::Reference<data::XDataSequence>& xDataSequence )
113 {
114     //#i71686#, #i101968#, #i102428#
115     sal_Int32 nCount = rData.Doubles.getLength();
116     for( sal_Int32 i = 0; i < nCount; ++i )
117     {
118         if( !std::isnan( rData.Doubles[i] ) )
119             return;
120     }
121     //no double value is contained
122     //is there any text?
123     uno::Sequence< OUString > aStrings( DataSequenceToStringSequence( xDataSequence ) );
124     sal_Int32 nTextCount = aStrings.getLength();
125     for( sal_Int32 j = 0; j < nTextCount; ++j )
126     {
127         if( !aStrings[j].isEmpty() )
128         {
129             rData.clear();
130             return;
131         }
132     }
133     //no content at all
134 }
135 
lcl_maybeReplaceNanWithZero(double & rfValue,sal_Int32 nMissingValueTreatment)136 void lcl_maybeReplaceNanWithZero( double& rfValue, sal_Int32 nMissingValueTreatment )
137 {
138     if( nMissingValueTreatment == css::chart::MissingValueTreatment::USE_ZERO
139         && (std::isnan(rfValue) || std::isinf(rfValue)) )
140             rfValue = 0.0;
141 }
142 
143 }
144 
VDataSeries(const uno::Reference<XDataSeries> & xDataSeries)145 VDataSeries::VDataSeries( const uno::Reference< XDataSeries >& xDataSeries )
146     : m_nPolygonIndex(0)
147     , m_fLogicMinX(0.0)
148     , m_fLogicMaxX(0.0)
149     , m_fLogicZPos(0.0)
150     , m_xDataSeries(xDataSeries)
151     , m_nPointCount(0)
152 
153     , m_pValueSequenceForDataLabelNumberFormatDetection(&m_aValues_Y)
154 
155     , m_fXMeanValue(1.0)
156     , m_fYMeanValue(1.0)
157 
158     , m_aAttributedDataPointIndexList()
159 
160     , m_eStackingDirection(StackingDirection_NO_STACKING)
161     , m_nAxisIndex(0)
162     , m_bConnectBars(false)
163     , m_bGroupBarsPerAxis(true)
164     , m_nStartingAngle(90)
165 
166     , m_nGlobalSeriesIndex(0)
167 
168     , m_nCurrentAttributedPoint(-1)
169     , m_nMissingValueTreatment(css::chart::MissingValueTreatment::LEAVE_GAP)
170     , m_bAllowPercentValueInDataLabel(false)
171     , mpOldSeries(nullptr)
172     , mnPercent(0.0)
173 {
174     ::rtl::math::setNan( & m_fXMeanValue );
175     ::rtl::math::setNan( & m_fYMeanValue );
176 
177     uno::Reference<data::XDataSource> xDataSource( xDataSeries, uno::UNO_QUERY );
178 
179     uno::Sequence< uno::Reference<
180         chart2::data::XLabeledDataSequence > > aDataSequences =
181             xDataSource->getDataSequences();
182 
183     for(sal_Int32 nN = aDataSequences.getLength();nN--;)
184     {
185         if(!aDataSequences[nN].is())
186             continue;
187         uno::Reference<data::XDataSequence>  xDataSequence( aDataSequences[nN]->getValues());
188         uno::Reference<beans::XPropertySet> xProp(xDataSequence, uno::UNO_QUERY );
189         if( xProp.is())
190         {
191             try
192             {
193                 uno::Any aARole = xProp->getPropertyValue("Role");
194                 OUString aRole;
195                 aARole >>= aRole;
196 
197                 if (aRole == "values-x")
198                 {
199                     m_aValues_X.init( xDataSequence );
200                     lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xDataSequence );
201                 }
202                 else if (aRole =="values-y")
203                     m_aValues_Y.init( xDataSequence );
204                 else if (aRole == "values-min")
205                     m_aValues_Y_Min.init( xDataSequence );
206                 else if (aRole == "values-max")
207                     m_aValues_Y_Max.init( xDataSequence );
208                 else if (aRole == "values-first")
209                     m_aValues_Y_First.init( xDataSequence );
210                 else if (aRole == "values-last")
211                     m_aValues_Y_Last.init( xDataSequence );
212                 else if (aRole == "values-size")
213                     m_aValues_Bubble_Size.init( xDataSequence );
214                 else
215                 {
216                     VDataSequence aSequence;
217                     aSequence.init(xDataSequence);
218                     m_PropertyMap.insert(std::make_pair(aRole, aSequence));
219                 }
220             }
221             catch( const uno::Exception& )
222             {
223                 TOOLS_WARN_EXCEPTION("chart2", "" );
224             }
225         }
226     }
227 
228     //determine the point count
229     m_nPointCount = m_aValues_Y.getLength();
230     {
231         if( m_nPointCount < m_aValues_Bubble_Size.getLength() )
232             m_nPointCount = m_aValues_Bubble_Size.getLength();
233         if( m_nPointCount < m_aValues_Y_Min.getLength() )
234             m_nPointCount = m_aValues_Y_Min.getLength();
235         if( m_nPointCount < m_aValues_Y_Max.getLength() )
236             m_nPointCount = m_aValues_Y_Max.getLength();
237         if( m_nPointCount < m_aValues_Y_First.getLength() )
238             m_nPointCount = m_aValues_Y_First.getLength();
239         if( m_nPointCount < m_aValues_Y_Last.getLength() )
240             m_nPointCount = m_aValues_Y_Last.getLength();
241     }
242 
243     uno::Reference<beans::XPropertySet> xProp(xDataSeries, uno::UNO_QUERY );
244     if( !xProp.is())
245         return;
246 
247     try
248     {
249         //get AttributedDataPoints
250         xProp->getPropertyValue("AttributedDataPoints") >>= m_aAttributedDataPointIndexList;
251 
252         xProp->getPropertyValue("StackingDirection") >>= m_eStackingDirection;
253 
254         xProp->getPropertyValue("AttachedAxisIndex") >>= m_nAxisIndex;
255         if(m_nAxisIndex<0)
256             m_nAxisIndex=0;
257     }
258     catch( const uno::Exception& )
259     {
260         TOOLS_WARN_EXCEPTION("chart2", "" );
261     }
262 }
263 
~VDataSeries()264 VDataSeries::~VDataSeries()
265 {
266 }
267 
doSortByXValues()268 void VDataSeries::doSortByXValues()
269 {
270     if( !(m_aValues_X.is() && m_aValues_X.Doubles.hasElements()) )
271         return;
272 
273     //prepare a vector for sorting
274     std::vector< std::vector< double > > aTmp;//outer vector are points, inner vector are the different values of the point
275     double fNan;
276     ::rtl::math::setNan( & fNan );
277     sal_Int32 nPointIndex = 0;
278     for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
279     {
280         aTmp.push_back(
281                         { ((nPointIndex < m_aValues_X.Doubles.getLength()) ? m_aValues_X.Doubles[nPointIndex] : fNan),
282                           ((nPointIndex < m_aValues_Y.Doubles.getLength()) ? m_aValues_Y.Doubles[nPointIndex] : fNan)
283                         }
284                       );
285     }
286 
287     //do sort
288     std::stable_sort( aTmp.begin(), aTmp.end(), lcl_LessXOfPoint() );
289 
290     //fill the sorted points back to the members
291     m_aValues_X.Doubles.realloc( m_nPointCount );
292     m_aValues_Y.Doubles.realloc( m_nPointCount );
293 
294     for( nPointIndex=0; nPointIndex < m_nPointCount; nPointIndex++ )
295     {
296         m_aValues_X.Doubles[nPointIndex]=aTmp[nPointIndex][0];
297         m_aValues_Y.Doubles[nPointIndex]=aTmp[nPointIndex][1];
298     }
299 }
300 
releaseShapes()301 void VDataSeries::releaseShapes()
302 {
303     m_xGroupShape.set(nullptr);
304     m_xLabelsGroupShape.set(nullptr);
305     m_xErrorXBarsGroupShape.set(nullptr);
306     m_xErrorYBarsGroupShape.set(nullptr);
307     m_xFrontSubGroupShape.set(nullptr);
308     m_xBackSubGroupShape.set(nullptr);
309 
310     m_aPolyPolygonShape3D.SequenceX.realloc(0);
311     m_aPolyPolygonShape3D.SequenceY.realloc(0);
312     m_aPolyPolygonShape3D.SequenceZ.realloc(0);
313     m_nPolygonIndex = 0;
314 }
315 
getModel() const316 const uno::Reference<css::chart2::XDataSeries>& VDataSeries::getModel() const
317 {
318     return m_xDataSeries;
319 }
320 
setCategoryXAxis()321 void VDataSeries::setCategoryXAxis()
322 {
323     m_aValues_X.clear();
324     m_bAllowPercentValueInDataLabel = true;
325 }
326 
setXValues(const Reference<chart2::data::XDataSequence> & xValues)327 void VDataSeries::setXValues( const Reference< chart2::data::XDataSequence >& xValues )
328 {
329     m_aValues_X.clear();
330     m_aValues_X.init( xValues );
331     m_bAllowPercentValueInDataLabel = true;
332 }
333 
setXValuesIfNone(const Reference<chart2::data::XDataSequence> & xValues)334 void VDataSeries::setXValuesIfNone( const Reference< chart2::data::XDataSequence >& xValues )
335 {
336     if( m_aValues_X.is() )
337         return;
338 
339     m_aValues_X.init( xValues );
340     lcl_clearIfNoValuesButTextIsContained( m_aValues_X, xValues );
341 }
342 
setGlobalSeriesIndex(sal_Int32 nGlobalSeriesIndex)343 void VDataSeries::setGlobalSeriesIndex( sal_Int32 nGlobalSeriesIndex )
344 {
345     m_nGlobalSeriesIndex = nGlobalSeriesIndex;
346 }
347 
setParticle(const OUString & rSeriesParticle)348 void VDataSeries::setParticle( const OUString& rSeriesParticle )
349 {
350     m_aSeriesParticle = rSeriesParticle;
351 
352     //get CID
353     m_aCID = ObjectIdentifier::createClassifiedIdentifierForParticle( m_aSeriesParticle );
354     m_aPointCID_Stub = ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT, m_aSeriesParticle );
355 
356     m_aLabelCID_Stub = ObjectIdentifier::createClassifiedIdentifierWithParent(
357                         OBJECTTYPE_DATA_LABEL, u"", getLabelsCID() );
358 }
getErrorBarsCID(bool bYError) const359 OUString VDataSeries::getErrorBarsCID(bool bYError) const
360 {
361     OUString aChildParticle( ObjectIdentifier::getStringForType(
362                                       bYError ? OBJECTTYPE_DATA_ERRORS_Y : OBJECTTYPE_DATA_ERRORS_X )
363                              + "=" );
364 
365     return ObjectIdentifier::createClassifiedIdentifierForParticles(
366             m_aSeriesParticle, aChildParticle );
367 }
getLabelsCID() const368 OUString VDataSeries::getLabelsCID() const
369 {
370     OUString aChildParticle( ObjectIdentifier::getStringForType( OBJECTTYPE_DATA_LABELS ) + "=" );
371 
372     return ObjectIdentifier::createClassifiedIdentifierForParticles(
373             m_aSeriesParticle, aChildParticle );
374 }
getDataCurveCID(sal_Int32 nCurveIndex,bool bAverageLine) const375 OUString VDataSeries::getDataCurveCID( sal_Int32 nCurveIndex, bool bAverageLine ) const
376 {
377     return ObjectIdentifier::createDataCurveCID( m_aSeriesParticle, nCurveIndex, bAverageLine );
378 }
379 
getDataCurveEquationCID(sal_Int32 nCurveIndex) const380 OUString VDataSeries::getDataCurveEquationCID( sal_Int32 nCurveIndex ) const
381 {
382     return ObjectIdentifier::createDataCurveEquationCID( m_aSeriesParticle, nCurveIndex );
383 }
setPageReferenceSize(const awt::Size & rPageRefSize)384 void VDataSeries::setPageReferenceSize( const awt::Size & rPageRefSize )
385 {
386     m_aReferenceSize = rPageRefSize;
387 }
388 
setConnectBars(bool bConnectBars)389 void VDataSeries::setConnectBars( bool bConnectBars )
390 {
391     m_bConnectBars = bConnectBars;
392 }
393 
getConnectBars() const394 bool VDataSeries::getConnectBars() const
395 {
396     return m_bConnectBars;
397 }
398 
setGroupBarsPerAxis(bool bGroupBarsPerAxis)399 void VDataSeries::setGroupBarsPerAxis( bool bGroupBarsPerAxis )
400 {
401     m_bGroupBarsPerAxis = bGroupBarsPerAxis;
402 }
403 
getGroupBarsPerAxis() const404 bool VDataSeries::getGroupBarsPerAxis() const
405 {
406     return m_bGroupBarsPerAxis;
407 }
408 
setStartingAngle(sal_Int32 nStartingAngle)409 void VDataSeries::setStartingAngle( sal_Int32 nStartingAngle )
410 {
411     m_nStartingAngle = nStartingAngle;
412 }
413 
getStartingAngle() const414 sal_Int32 VDataSeries::getStartingAngle() const
415 {
416     return m_nStartingAngle;
417 }
418 
getStackingDirection() const419 chart2::StackingDirection VDataSeries::getStackingDirection() const
420 {
421     return m_eStackingDirection;
422 }
423 
getAttachedAxisIndex() const424 sal_Int32 VDataSeries::getAttachedAxisIndex() const
425 {
426     return m_nAxisIndex;
427 }
428 
setAttachedAxisIndex(sal_Int32 nAttachedAxisIndex)429 void VDataSeries::setAttachedAxisIndex( sal_Int32 nAttachedAxisIndex )
430 {
431     if( nAttachedAxisIndex < 0 )
432         nAttachedAxisIndex = 0;
433     m_nAxisIndex = nAttachedAxisIndex;
434 }
435 
getXValue(sal_Int32 index) const436 double VDataSeries::getXValue( sal_Int32 index ) const
437 {
438     double fRet = 0.0;
439     if(m_aValues_X.is())
440     {
441         if( 0<=index && index<m_aValues_X.getLength() )
442         {
443             fRet = m_aValues_X.Doubles[index];
444             if(mpOldSeries && index < mpOldSeries->m_aValues_X.getLength())
445             {
446                 double nOldVal = mpOldSeries->m_aValues_X.Doubles[index];
447                 fRet = nOldVal + (fRet - nOldVal) * mnPercent;
448             }
449         }
450         else
451             ::rtl::math::setNan( &fRet );
452     }
453     else
454     {
455         // #i70133# always return correct X position - needed for short data series
456         if( 0<=index /*&& index < m_nPointCount*/ )
457             fRet = index+1;//first category (index 0) matches with real number 1.0
458         else
459             ::rtl::math::setNan( &fRet );
460     }
461     lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
462     return fRet;
463 }
464 
getYValue(sal_Int32 index) const465 double VDataSeries::getYValue( sal_Int32 index ) const
466 {
467     double fRet = 0.0;
468     if(m_aValues_Y.is())
469     {
470         if( 0<=index && index<m_aValues_Y.getLength() )
471         {
472             fRet = m_aValues_Y.Doubles[index];
473             if(mpOldSeries && index < mpOldSeries->m_aValues_Y.getLength())
474             {
475                 double nOldVal = mpOldSeries->m_aValues_Y.Doubles[index];
476                 fRet = nOldVal + (fRet - nOldVal) * mnPercent;
477             }
478         }
479         else
480             ::rtl::math::setNan( &fRet );
481     }
482     else
483     {
484         // #i70133# always return correct X position - needed for short data series
485         if( 0<=index /*&& index < m_nPointCount*/ )
486             fRet = index+1;//first category (index 0) matches with real number 1.0
487         else
488             ::rtl::math::setNan( &fRet );
489     }
490     lcl_maybeReplaceNanWithZero( fRet, getMissingValueTreatment() );
491     return fRet;
492 }
493 
getMinMaxXValue(double & fMin,double & fMax) const494 void VDataSeries::getMinMaxXValue(double& fMin, double& fMax) const
495 {
496     rtl::math::setNan( &fMax );
497     rtl::math::setNan( &fMin );
498 
499     uno::Sequence< double > aValuesX = getAllX();
500 
501     if(!aValuesX.hasElements())
502         return;
503 
504     sal_Int32 i = 0;
505     while ( i < aValuesX.getLength() && std::isnan(aValuesX[i]) )
506         i++;
507     if ( i < aValuesX.getLength() )
508         fMax = fMin = aValuesX[i++];
509 
510     for ( ; i < aValuesX.getLength(); i++)
511     {
512         const double aValue = aValuesX[i];
513         if ( aValue > fMax)
514         {
515             fMax = aValue;
516         }
517         else if ( aValue < fMin)
518         {
519             fMin = aValue;
520         }
521     }
522 }
getY_Min(sal_Int32 index) const523 double VDataSeries::getY_Min( sal_Int32 index ) const
524 {
525     return m_aValues_Y_Min.getValue( index );
526 }
getY_Max(sal_Int32 index) const527 double VDataSeries::getY_Max( sal_Int32 index ) const
528 {
529     return m_aValues_Y_Max.getValue( index );
530 }
getY_First(sal_Int32 index) const531 double VDataSeries::getY_First( sal_Int32 index ) const
532 {
533     return m_aValues_Y_First.getValue( index );
534 }
getY_Last(sal_Int32 index) const535 double VDataSeries::getY_Last( sal_Int32 index ) const
536 {
537     return m_aValues_Y_Last.getValue( index );
538 }
getBubble_Size(sal_Int32 index) const539 double VDataSeries::getBubble_Size( sal_Int32 index ) const
540 {
541     double nNewVal = m_aValues_Bubble_Size.getValue( index );
542     if(mpOldSeries && index < mpOldSeries->m_aValues_Bubble_Size.getLength())
543     {
544         double nOldVal = mpOldSeries->m_aValues_Bubble_Size.getValue( index );
545         nNewVal = nOldVal + (nNewVal - nOldVal) * mnPercent;
546     }
547 
548     return nNewVal;
549 }
550 
hasExplicitNumberFormat(sal_Int32 nPointIndex,bool bForPercentage) const551 bool VDataSeries::hasExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
552 {
553     OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
554     bool bHasNumberFormat = false;
555     bool bLinkToSource = true;
556     uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
557     if( xPointProp.is() && (xPointProp->getPropertyValue(CHART_UNONAME_LINK_TO_SRC_NUMFMT) >>= bLinkToSource))
558     {
559         sal_Int32 nNumberFormat = -1;
560         if( !bLinkToSource && (xPointProp->getPropertyValue(aPropName) >>= nNumberFormat))
561             bHasNumberFormat = true;
562     }
563     return bHasNumberFormat;
564 }
getExplicitNumberFormat(sal_Int32 nPointIndex,bool bForPercentage) const565 sal_Int32 VDataSeries::getExplicitNumberFormat( sal_Int32 nPointIndex, bool bForPercentage ) const
566 {
567     OUString aPropName = bForPercentage ? OUString("PercentageNumberFormat") : OUString(CHART_UNONAME_NUMFMT);
568     sal_Int32 nNumberFormat = -1;
569     uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( nPointIndex ));
570     if( xPointProp.is() )
571         xPointProp->getPropertyValue(aPropName) >>= nNumberFormat;
572     return nNumberFormat;
573 }
setRoleOfSequenceForDataLabelNumberFormatDetection(std::u16string_view rRole)574 void VDataSeries::setRoleOfSequenceForDataLabelNumberFormatDetection( std::u16string_view rRole )
575 {
576     if (rRole == u"values-y")
577         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y;
578     else if (rRole == u"values-size")
579         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Bubble_Size;
580     else if (rRole == u"values-min")
581         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Min;
582     else if (rRole == u"values-max")
583         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Max;
584     else if (rRole == u"values-first")
585         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_First;
586     else if (rRole == u"values-last")
587         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_Y_Last;
588     else if (rRole == u"values-x")
589         m_pValueSequenceForDataLabelNumberFormatDetection = &m_aValues_X;
590 }
detectNumberFormatKey(sal_Int32 index) const591 sal_Int32 VDataSeries::detectNumberFormatKey( sal_Int32 index ) const
592 {
593     sal_Int32 nRet = 0;
594     if( m_pValueSequenceForDataLabelNumberFormatDetection )
595         nRet = m_pValueSequenceForDataLabelNumberFormatDetection->detectNumberFormatKey( index );
596     return nRet;
597 }
598 
getLabelPlacement(sal_Int32 nPointIndex,const uno::Reference<chart2::XChartType> & xChartType,bool bSwapXAndY) const599 sal_Int32 VDataSeries::getLabelPlacement( sal_Int32 nPointIndex, const uno::Reference< chart2::XChartType >& xChartType, bool bSwapXAndY ) const
600 {
601     sal_Int32 nLabelPlacement=0;
602     try
603     {
604         uno::Reference< beans::XPropertySet > xPointProps( getPropertiesOfPoint( nPointIndex ) );
605         if( xPointProps.is() )
606             xPointProps->getPropertyValue("LabelPlacement") >>= nLabelPlacement;
607 
608         uno::Sequence < sal_Int32 > aAvailablePlacements( ChartTypeHelper::getSupportedLabelPlacements(
609                 xChartType, bSwapXAndY, m_xDataSeries ) );
610 
611         for( sal_Int32 n : aAvailablePlacements )
612             if( n == nLabelPlacement )
613                 return nLabelPlacement; //ok
614 
615         //otherwise use the first supported one
616         if( aAvailablePlacements.hasElements() )
617         {
618             nLabelPlacement = aAvailablePlacements[0];
619             if( xPointProps.is() )
620                 xPointProps->setPropertyValue("LabelPlacement", uno::Any(nLabelPlacement));
621             return nLabelPlacement;
622         }
623 
624         OSL_FAIL("no label placement supported");
625     }
626     catch( const uno::Exception& )
627     {
628         TOOLS_WARN_EXCEPTION("chart2", "" );
629     }
630     return nLabelPlacement;
631 }
632 
getLabelPosition(awt::Point aTextShapePos,sal_Int32 nPointIndex) const633 awt::Point VDataSeries::getLabelPosition( awt::Point aTextShapePos, sal_Int32 nPointIndex ) const
634 {
635     awt::Point aPos(-1, -1);
636     try
637     {
638         RelativePosition aCustomLabelPosition;
639         uno::Reference< beans::XPropertySet > xPointProps(getPropertiesOfPoint(nPointIndex));
640         if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition))
641         {
642             aPos.X = static_cast<sal_Int32>(aCustomLabelPosition.Primary * m_aReferenceSize.Width) + aTextShapePos.X;
643             aPos.Y = static_cast<sal_Int32>(aCustomLabelPosition.Secondary * m_aReferenceSize.Height) + aTextShapePos.Y;
644         }
645     }
646     catch (const uno::Exception&)
647     {
648         TOOLS_WARN_EXCEPTION("chart2", "");
649     }
650     return aPos;
651 }
652 
isLabelCustomPos(sal_Int32 nPointIndex) const653 bool VDataSeries::isLabelCustomPos(sal_Int32 nPointIndex) const
654 {
655     bool bCustom = false;
656     try
657     {
658         if( isAttributedDataPoint(nPointIndex) )
659         {
660             uno::Reference< beans::XPropertySet > xPointProps(m_xDataSeries->getDataPointByIndex(nPointIndex));
661             RelativePosition aCustomLabelPosition;
662             if( xPointProps.is() && (xPointProps->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition) )
663                 bCustom = true;
664         }
665     }
666     catch (const uno::Exception&)
667     {
668         TOOLS_WARN_EXCEPTION("chart2", "");
669     }
670     return bCustom;
671 }
672 
getMinimumofAllDifferentYValues(sal_Int32 index) const673 double VDataSeries::getMinimumofAllDifferentYValues( sal_Int32 index ) const
674 {
675     double fMin=0.0;
676     ::rtl::math::setInf(&fMin, false);
677 
678     if( !m_aValues_Y.is() &&
679         (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
680         || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
681     {
682         double fY_Min = getY_Min( index );
683         double fY_Max = getY_Max( index );
684         double fY_First = getY_First( index );
685         double fY_Last = getY_Last( index );
686 
687         if(fMin>fY_First)
688             fMin=fY_First;
689         if(fMin>fY_Last)
690             fMin=fY_Last;
691         if(fMin>fY_Min)
692             fMin=fY_Min;
693         if(fMin>fY_Max)
694             fMin=fY_Max;
695     }
696     else
697     {
698         double fY = getYValue( index );
699         if(fMin>fY)
700             fMin=fY;
701     }
702 
703     if( std::isinf(fMin) )
704         ::rtl::math::setNan(&fMin);
705 
706     return fMin;
707 }
708 
getMaximumofAllDifferentYValues(sal_Int32 index) const709 double VDataSeries::getMaximumofAllDifferentYValues( sal_Int32 index ) const
710 {
711     double fMax=0.0;
712     ::rtl::math::setInf(&fMax, true);
713 
714     if( !m_aValues_Y.is() &&
715         (m_aValues_Y_Min.is() || m_aValues_Y_Max.is()
716         || m_aValues_Y_First.is() || m_aValues_Y_Last.is() ) )
717     {
718         double fY_Min = getY_Min( index );
719         double fY_Max = getY_Max( index );
720         double fY_First = getY_First( index );
721         double fY_Last = getY_Last( index );
722 
723         if(fMax<fY_First)
724             fMax=fY_First;
725         if(fMax<fY_Last)
726             fMax=fY_Last;
727         if(fMax<fY_Min)
728             fMax=fY_Min;
729         if(fMax<fY_Max)
730             fMax=fY_Max;
731     }
732     else
733     {
734         double fY = getYValue( index );
735         if(fMax<fY)
736             fMax=fY;
737     }
738 
739     if( std::isinf(fMax) )
740         ::rtl::math::setNan(&fMax);
741 
742     return fMax;
743 }
744 
getAllX() const745 uno::Sequence< double > const & VDataSeries::getAllX() const
746 {
747     if(!m_aValues_X.is() && !m_aValues_X.getLength() && m_nPointCount)
748     {
749         //init x values from category indexes
750         //first category (index 0) matches with real number 1.0
751         m_aValues_X.Doubles.realloc( m_nPointCount );
752         for(sal_Int32 nN=m_aValues_X.getLength();nN--;)
753             m_aValues_X.Doubles[nN] = nN+1;
754     }
755     return m_aValues_X.Doubles;
756 }
757 
getAllY() const758 uno::Sequence< double > const & VDataSeries::getAllY() const
759 {
760     if(!m_aValues_Y.is() && !m_aValues_Y.getLength() && m_nPointCount)
761     {
762         //init y values from indexes
763         //first y-value (index 0) matches with real number 1.0
764         m_aValues_Y.Doubles.realloc( m_nPointCount );
765         for(sal_Int32 nN=m_aValues_Y.getLength();nN--;)
766             m_aValues_Y.Doubles[nN] = nN+1;
767     }
768     return m_aValues_Y.Doubles;
769 }
770 
getXMeanValue() const771 double VDataSeries::getXMeanValue() const
772 {
773     if( std::isnan( m_fXMeanValue ) )
774     {
775         uno::Reference< XRegressionCurveCalculator > xCalculator( RegressionCurveHelper::createRegressionCurveCalculatorByServiceName( u"com.sun.star.chart2.MeanValueRegressionCurve" ) );
776         uno::Sequence< double > aXValuesDummy;
777         xCalculator->recalculateRegression( aXValuesDummy, getAllX() );
778         m_fXMeanValue = xCalculator->getCurveValue( 1.0 );
779     }
780     return m_fXMeanValue;
781 }
782 
getYMeanValue() const783 double VDataSeries::getYMeanValue() const
784 {
785     if( std::isnan( m_fYMeanValue ) )
786     {
787         uno::Reference< XRegressionCurveCalculator > xCalculator(
788             RegressionCurveHelper::createRegressionCurveCalculatorByServiceName(u"com.sun.star.chart2.MeanValueRegressionCurve"));
789         uno::Sequence< double > aXValuesDummy;
790         xCalculator->recalculateRegression( aXValuesDummy, getAllY() );
791         m_fYMeanValue = xCalculator->getCurveValue( 1.0 );
792     }
793     return m_fYMeanValue;
794 }
795 
getSymbolPropertiesFromPropertySet(const uno::Reference<beans::XPropertySet> & xProp)796 static std::unique_ptr<Symbol> getSymbolPropertiesFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
797 {
798     std::unique_ptr< Symbol > apSymbolProps( new Symbol() );
799     try
800     {
801         if( xProp->getPropertyValue("Symbol") >>= *apSymbolProps )
802         {
803             //use main color to fill symbols
804             xProp->getPropertyValue("Color") >>= apSymbolProps->FillColor;
805             // border of symbols always same as fill color
806             apSymbolProps->BorderColor = apSymbolProps->FillColor;
807         }
808         else
809             apSymbolProps.reset();
810     }
811     catch(const uno::Exception &)
812     {
813         TOOLS_WARN_EXCEPTION("chart2", "" );
814     }
815     return apSymbolProps;
816 }
817 
getSymbolProperties(sal_Int32 index) const818 Symbol* VDataSeries::getSymbolProperties( sal_Int32 index ) const
819 {
820     Symbol* pRet=nullptr;
821     if( isAttributedDataPoint( index ) )
822     {
823         adaptPointCache( index );
824         if (!m_apSymbolProperties_AttributedPoint)
825             m_apSymbolProperties_AttributedPoint
826                 = getSymbolPropertiesFromPropertySet(getPropertiesOfPoint(index));
827         pRet = m_apSymbolProperties_AttributedPoint.get();
828         //if a single data point does not have symbols but the dataseries itself has symbols
829         //we create an invisible symbol shape to enable selection of that point
830         if( !pRet || pRet->Style == SymbolStyle_NONE )
831         {
832             if (!m_apSymbolProperties_Series)
833                 m_apSymbolProperties_Series
834                     = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
835             if( m_apSymbolProperties_Series && m_apSymbolProperties_Series->Style != SymbolStyle_NONE )
836             {
837                 if (!m_apSymbolProperties_InvisibleSymbolForSelection)
838                 {
839                     m_apSymbolProperties_InvisibleSymbolForSelection.reset(new Symbol);
840                     m_apSymbolProperties_InvisibleSymbolForSelection->Style = SymbolStyle_STANDARD;
841                     m_apSymbolProperties_InvisibleSymbolForSelection->StandardSymbol = 0;//square
842                     m_apSymbolProperties_InvisibleSymbolForSelection->Size = com::sun::star::awt::Size(0, 0);//tdf#126033
843                     m_apSymbolProperties_InvisibleSymbolForSelection->BorderColor = 0xff000000;//invisible
844                     m_apSymbolProperties_InvisibleSymbolForSelection->FillColor = 0xff000000;//invisible
845                 }
846                 pRet = m_apSymbolProperties_InvisibleSymbolForSelection.get();
847             }
848         }
849     }
850     else
851     {
852         if (!m_apSymbolProperties_Series)
853             m_apSymbolProperties_Series
854                 = getSymbolPropertiesFromPropertySet(getPropertiesOfSeries());
855         pRet = m_apSymbolProperties_Series.get();
856     }
857 
858     if( pRet && pRet->Style == SymbolStyle_AUTO )
859     {
860         pRet->Style = SymbolStyle_STANDARD;
861 
862         sal_Int32 nIndex = m_nGlobalSeriesIndex;
863         if(m_aValues_X.is())
864             nIndex++;
865         pRet->StandardSymbol = nIndex;
866     }
867 
868     return pRet;
869 }
870 
getXErrorBarProperties(sal_Int32 index) const871 uno::Reference< beans::XPropertySet > VDataSeries::getXErrorBarProperties( sal_Int32 index ) const
872 {
873     uno::Reference< beans::XPropertySet > xErrorBarProp;
874 
875     uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
876     if( xPointProp.is() )
877         xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_X) >>= xErrorBarProp;
878     return xErrorBarProp;
879 }
880 
getYErrorBarProperties(sal_Int32 index) const881 uno::Reference< beans::XPropertySet > VDataSeries::getYErrorBarProperties( sal_Int32 index ) const
882 {
883     uno::Reference< beans::XPropertySet > xErrorBarProp;
884 
885     uno::Reference< beans::XPropertySet > xPointProp( getPropertiesOfPoint( index ));
886     if( xPointProp.is() )
887         xPointProp->getPropertyValue(CHART_UNONAME_ERRORBAR_Y) >>= xErrorBarProp;
888     return xErrorBarProp;
889 }
890 
hasPointOwnColor(sal_Int32 index) const891 bool VDataSeries::hasPointOwnColor( sal_Int32 index ) const
892 {
893     if( !isAttributedDataPoint(index) )
894         return false;
895 
896     try
897     {
898         uno::Reference< beans::XPropertyState > xPointState( getPropertiesOfPoint(index), uno::UNO_QUERY_THROW );
899         return (xPointState->getPropertyState("Color") != beans::PropertyState_DEFAULT_VALUE );
900     }
901     catch(const uno::Exception&)
902     {
903         TOOLS_WARN_EXCEPTION("chart2", "" );
904     }
905     return false;
906 }
907 
isAttributedDataPoint(sal_Int32 index) const908 bool VDataSeries::isAttributedDataPoint( sal_Int32 index ) const
909 {
910     //returns true if the data point assigned by the given index has set its own properties
911     if( index>=m_nPointCount || m_nPointCount==0)
912         return false;
913     for(sal_Int32 n : m_aAttributedDataPointIndexList)
914     {
915         if(index == n)
916             return true;
917     }
918     return false;
919 }
920 
isVaryColorsByPoint() const921 bool VDataSeries::isVaryColorsByPoint() const
922 {
923     bool bVaryColorsByPoint = false;
924     Reference< beans::XPropertySet > xSeriesProp( getPropertiesOfSeries() );
925     if( xSeriesProp.is() )
926         xSeriesProp->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint;
927     return bVaryColorsByPoint;
928 }
929 
getPropertiesOfPoint(sal_Int32 index) const930 uno::Reference< beans::XPropertySet > VDataSeries::getPropertiesOfPoint( sal_Int32 index ) const
931 {
932     if( isAttributedDataPoint( index ) )
933         return m_xDataSeries->getDataPointByIndex(index);
934     return getPropertiesOfSeries();
935 }
936 
getPropertiesOfSeries() const937 uno::Reference<beans::XPropertySet> VDataSeries::getPropertiesOfSeries() const
938 {
939     return uno::Reference<css::beans::XPropertySet>(m_xDataSeries, css::uno::UNO_QUERY);
940 }
941 
getDataPointLabelFromPropertySet(const uno::Reference<beans::XPropertySet> & xProp)942 static std::unique_ptr<DataPointLabel> getDataPointLabelFromPropertySet( const uno::Reference< beans::XPropertySet >& xProp )
943 {
944     std::unique_ptr< DataPointLabel > apLabel( new DataPointLabel() );
945     try
946     {
947         if( !(xProp->getPropertyValue(CHART_UNONAME_LABEL) >>= *apLabel) )
948             apLabel.reset();
949     }
950     catch(const uno::Exception &)
951     {
952         TOOLS_WARN_EXCEPTION("chart2", "" );
953     }
954     return apLabel;
955 }
956 
adaptPointCache(sal_Int32 nNewPointIndex) const957 void VDataSeries::adaptPointCache( sal_Int32 nNewPointIndex ) const
958 {
959     if( m_nCurrentAttributedPoint != nNewPointIndex )
960     {
961         m_apLabel_AttributedPoint.reset();
962         m_apLabelPropNames_AttributedPoint.reset();
963         m_apLabelPropValues_AttributedPoint.reset();
964         m_apSymbolProperties_AttributedPoint.reset();
965         m_nCurrentAttributedPoint = nNewPointIndex;
966     }
967 }
968 
getDataPointLabel(sal_Int32 index) const969 DataPointLabel* VDataSeries::getDataPointLabel( sal_Int32 index ) const
970 {
971     DataPointLabel* pRet = nullptr;
972     if( isAttributedDataPoint( index ) )
973     {
974         adaptPointCache( index );
975         if (!m_apLabel_AttributedPoint)
976             m_apLabel_AttributedPoint
977                 = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
978         pRet = m_apLabel_AttributedPoint.get();
979     }
980     else
981     {
982         if (!m_apLabel_Series)
983             m_apLabel_Series
984                 = getDataPointLabelFromPropertySet(getPropertiesOfPoint(index));
985         pRet = m_apLabel_Series.get();
986     }
987     if( !m_bAllowPercentValueInDataLabel )
988     {
989         if( pRet )
990             pRet->ShowNumberInPercent = false;
991     }
992     return pRet;
993 }
994 
getDataPointLabelIfLabel(sal_Int32 index) const995 DataPointLabel* VDataSeries::getDataPointLabelIfLabel( sal_Int32 index ) const
996 {
997     DataPointLabel* pLabel = getDataPointLabel( index );
998     if( !pLabel || (!pLabel->ShowNumber && !pLabel->ShowNumberInPercent
999         && !pLabel->ShowCategoryName && !pLabel->ShowCustomLabel && !pLabel->ShowSeriesName ) )
1000         return nullptr;
1001     return pLabel;
1002 }
1003 
getTextLabelMultiPropertyLists(sal_Int32 index,tNameSequence * & pPropNames,tAnySequence * & pPropValues) const1004 bool VDataSeries::getTextLabelMultiPropertyLists( sal_Int32 index
1005     , tNameSequence*& pPropNames
1006     , tAnySequence*& pPropValues ) const
1007 {
1008     pPropNames = nullptr; pPropValues = nullptr;
1009     uno::Reference< beans::XPropertySet > xTextProp;
1010     bool bDoDynamicFontResize = false;
1011     if( isAttributedDataPoint( index ) )
1012     {
1013         adaptPointCache( index );
1014         if (!m_apLabelPropValues_AttributedPoint)
1015         {
1016             // Cache these properties for this point.
1017             m_apLabelPropNames_AttributedPoint.reset(new tNameSequence);
1018             m_apLabelPropValues_AttributedPoint.reset(new tAnySequence);
1019             xTextProp.set( getPropertiesOfPoint( index ));
1020             PropertyMapper::getTextLabelMultiPropertyLists(
1021                 xTextProp, *m_apLabelPropNames_AttributedPoint, *m_apLabelPropValues_AttributedPoint);
1022             bDoDynamicFontResize = true;
1023         }
1024         pPropNames = m_apLabelPropNames_AttributedPoint.get();
1025         pPropValues = m_apLabelPropValues_AttributedPoint.get();
1026     }
1027     else
1028     {
1029         if (!m_apLabelPropValues_Series)
1030         {
1031             // Cache these properties for the whole series.
1032             m_apLabelPropNames_Series.reset(new tNameSequence);
1033             m_apLabelPropValues_Series.reset(new tAnySequence);
1034             xTextProp.set( getPropertiesOfPoint( index ));
1035             PropertyMapper::getTextLabelMultiPropertyLists(
1036                 xTextProp, *m_apLabelPropNames_Series, *m_apLabelPropValues_Series);
1037             bDoDynamicFontResize = true;
1038         }
1039         pPropNames = m_apLabelPropNames_Series.get();
1040         pPropValues = m_apLabelPropValues_Series.get();
1041     }
1042 
1043     if( bDoDynamicFontResize &&
1044         pPropNames && pPropValues &&
1045         xTextProp.is())
1046     {
1047         LabelPositionHelper::doDynamicFontResize( *pPropValues, *pPropNames, xTextProp, m_aReferenceSize );
1048     }
1049 
1050     return (pPropNames && pPropValues);
1051 }
1052 
setMissingValueTreatment(sal_Int32 nMissingValueTreatment)1053 void VDataSeries::setMissingValueTreatment( sal_Int32 nMissingValueTreatment )
1054 {
1055     m_nMissingValueTreatment = nMissingValueTreatment;
1056 }
1057 
getMissingValueTreatment() const1058 sal_Int32 VDataSeries::getMissingValueTreatment() const
1059 {
1060     return m_nMissingValueTreatment;
1061 }
1062 
VDataSeries()1063 VDataSeries::VDataSeries()
1064     : m_nPolygonIndex(0)
1065     , m_fLogicMinX(0)
1066     , m_fLogicMaxX(0)
1067     , m_fLogicZPos(0)
1068     , m_nPointCount(0)
1069     , m_pValueSequenceForDataLabelNumberFormatDetection(nullptr)
1070     , m_fXMeanValue(0)
1071     , m_fYMeanValue(0)
1072     , m_eStackingDirection(chart2::StackingDirection_NO_STACKING)
1073     , m_nAxisIndex(0)
1074     , m_bConnectBars(false)
1075     , m_bGroupBarsPerAxis(false)
1076     , m_nStartingAngle(0)
1077     , m_nGlobalSeriesIndex(0)
1078     , m_nCurrentAttributedPoint(0)
1079     , m_nMissingValueTreatment(0)
1080     , m_bAllowPercentValueInDataLabel(false)
1081     , mpOldSeries(nullptr)
1082     , mnPercent(0)
1083 {
1084 }
1085 
setOldTimeBased(VDataSeries * pOldSeries,double nPercent)1086 void VDataSeries::setOldTimeBased( VDataSeries* pOldSeries, double nPercent )
1087 {
1088     mnPercent = nPercent;
1089     mpOldSeries = pOldSeries;
1090     mpOldSeries->mpOldSeries = nullptr;
1091 }
1092 
createCopyForTimeBased() const1093 VDataSeries* VDataSeries::createCopyForTimeBased() const
1094 {
1095     VDataSeries* pNew = new VDataSeries();
1096     pNew->m_aValues_X = m_aValues_X;
1097     pNew->m_aValues_Y = m_aValues_Y;
1098     pNew->m_aValues_Z = m_aValues_Z;
1099     pNew->m_aValues_Y_Min = m_aValues_Y_Min;
1100     pNew->m_aValues_Y_Max = m_aValues_Y_Max;
1101     pNew->m_aValues_Y_First = m_aValues_Y_First;
1102     pNew->m_aValues_Y_Last = m_aValues_Y_Last;
1103     pNew->m_aValues_Bubble_Size = m_aValues_Bubble_Size;
1104     pNew->m_PropertyMap = m_PropertyMap;
1105 
1106     pNew->m_nPointCount = m_nPointCount;
1107 
1108     return pNew;
1109 }
1110 
getValueByProperty(sal_Int32 nIndex,const OUString & rPropName) const1111 double VDataSeries::getValueByProperty( sal_Int32 nIndex, const OUString& rPropName ) const
1112 {
1113     auto const itr = m_PropertyMap.find(rPropName);
1114     if (itr == m_PropertyMap.end())
1115     {
1116         double fNan;
1117         ::rtl::math::setNan( &fNan );
1118         return fNan;
1119     }
1120 
1121     const VDataSequence* pData = &itr->second;
1122     double fValue = pData->getValue(nIndex);
1123     if(mpOldSeries && mpOldSeries->hasPropertyMapping(rPropName))
1124     {
1125         double fOldValue = mpOldSeries->getValueByProperty( nIndex, rPropName );
1126         if(rPropName.endsWith("Color"))
1127         {
1128             //optimized interpolation for color values
1129             Color aColor(ColorTransparency, static_cast<sal_uInt32>(fValue));
1130             Color aOldColor(ColorTransparency, static_cast<sal_uInt32>(fOldValue));
1131             sal_uInt8 r = aOldColor.GetRed() + (aColor.GetRed() - aOldColor.GetRed()) * mnPercent;
1132             sal_uInt8 g = aOldColor.GetGreen() + (aColor.GetGreen() - aOldColor.GetGreen()) * mnPercent;
1133             sal_uInt8 b = aOldColor.GetBlue() + (aColor.GetBlue() - aOldColor.GetBlue()) * mnPercent;
1134             sal_uInt8 a = aOldColor.GetAlpha() + (aColor.GetAlpha() - aOldColor.GetAlpha()) * mnPercent;
1135             Color aRet(ColorAlpha, a, r, g, b);
1136             return sal_uInt32(aRet);
1137         }
1138         return fOldValue + (fValue - fOldValue) * mnPercent;
1139     }
1140     return fValue;
1141 }
1142 
hasPropertyMapping(const OUString & rPropName) const1143 bool VDataSeries::hasPropertyMapping(const OUString& rPropName ) const
1144 {
1145     return m_PropertyMap.find(rPropName) != m_PropertyMap.end();
1146 }
1147 
1148 } //namespace chart
1149 
1150 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1151