1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "DataSeriesHelper.hxx"
31 #include "DiagramHelper.hxx"
32 #include "DataSource.hxx"
33 #include "macros.hxx"
34 #include "ContainerHelper.hxx"
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/chart2/DataPointLabel.hpp>
37 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
38 #include <com/sun/star/chart2/StackingDirection.hpp>
39 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
40 #include <com/sun/star/chart2/AxisType.hpp>
41 #include <com/sun/star/chart2/SymbolStyle.hpp>
42 #include <com/sun/star/chart2/Symbol.hpp>
43 #include <com/sun/star/drawing/LineStyle.hpp>
44 
45 
46 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
47 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
48 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
49 #include <rtl/ustrbuf.hxx>
50 
51 #include <functional>
52 #include <algorithm>
53 #include <iterator>
54 #include <vector>
55 #include <set>
56 
57 using namespace ::com::sun::star;
58 using namespace ::com::sun::star::chart2;
59 
60 using ::com::sun::star::uno::Reference;
61 using ::com::sun::star::uno::Sequence;
62 using ::rtl::OUString;
63 using ::rtl::OUStringBuffer;
64 
65 // ----------------------------------------
66 namespace
67 {
68 
69 class lcl_MatchesRole : public ::std::unary_function< Reference< chart2::data::XLabeledDataSequence >, bool >
70 {
71 public:
72     explicit lcl_MatchesRole( const OUString & aRole, bool bMatchPrefix ) :
73             m_aRole( aRole ),
74             m_bMatchPrefix( bMatchPrefix )
75     {}
76 
77     bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
78     {
79         if(!xSeq.is())
80             return false;
81         Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
82         OUString aRole;
83 
84         if( m_bMatchPrefix )
85             return ( xProp.is() &&
86                      (xProp->getPropertyValue(
87                          OUString( RTL_CONSTASCII_USTRINGPARAM( "Role" )) ) >>= aRole ) &&
88                      aRole.match( m_aRole ));
89 
90         return ( xProp.is() &&
91                  (xProp->getPropertyValue(
92                      OUString( RTL_CONSTASCII_USTRINGPARAM( "Role" )) ) >>= aRole ) &&
93                  m_aRole.equals( aRole ));
94     }
95 
96 private:
97     OUString m_aRole;
98     bool     m_bMatchPrefix;
99 };
100 
101 Reference< chart2::data::XLabeledDataSequence > lcl_findLSequenceWithOnlyLabel(
102     const Reference< chart2::data::XDataSource > & xDataSource )
103 {
104     Reference< chart2::data::XLabeledDataSequence > xResult;
105     Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
106 
107     for( sal_Int32 i=0; i<aSequences.getLength(); ++i )
108     {
109         OSL_ENSURE( aSequences[i].is(), "empty LabeledDataSequence" );
110         // no values are set but a label exists
111         if( aSequences[i].is() &&
112             ( ! aSequences[i]->getValues().is() &&
113               aSequences[i]->getLabel().is()))
114         {
115             xResult.set( aSequences[i] );
116             break;
117         }
118     }
119 
120     return xResult;
121 }
122 
123 void lcl_getCooSysAndChartTypeOfSeries(
124     const Reference< chart2::XDataSeries > & xSeries,
125     const Reference< chart2::XDiagram > & xDiagram,
126     Reference< chart2::XCoordinateSystem > & xOutCooSys,
127     Reference< chart2::XChartType > & xOutChartType )
128 {
129     Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
130     if( xCooSysCnt.is())
131     {
132         Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
133         for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
134         {
135             Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
136             Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes());
137             for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypes.getLength(); ++nCTIdx )
138             {
139                 Reference< chart2::XDataSeriesContainer > xSeriesCnt( aChartTypes[nCTIdx], uno::UNO_QUERY );
140                 if( xSeriesCnt.is())
141                 {
142                     Sequence< Reference< chart2::XDataSeries > > aSeries( xSeriesCnt->getDataSeries());
143                     for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx )
144                     {
145                         if( aSeries[nSeriesIdx] == xSeries )
146                         {
147                             xOutCooSys.set( aCooSysSeq[nCooSysIdx] );
148                             xOutChartType.set( aChartTypes[nCTIdx] );
149                         }
150                     }
151                 }
152             }
153         }
154     }
155 }
156 
157 void lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries, bool bInsert )
158 {
159     try
160     {
161         Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
162         if( xSeriesProperties.is() )
163         {
164             DataPointLabel aLabelAtSeries;
165             xSeriesProperties->getPropertyValue( C2U( "Label" ) ) >>= aLabelAtSeries;
166             aLabelAtSeries.ShowNumber = bInsert;
167             if( !bInsert )
168             {
169                 aLabelAtSeries.ShowNumberInPercent = false;
170                 aLabelAtSeries.ShowCategoryName = false;
171             }
172             xSeriesProperties->setPropertyValue( C2U( "Label" ), uno::makeAny( aLabelAtSeries ) );
173             uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
174             if( xSeriesProperties->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
175             {
176                 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
177                 {
178                     Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
179                     if( xPointProp.is() )
180                     {
181                         DataPointLabel aLabel;
182                         xPointProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel;
183                         aLabel.ShowNumber = bInsert;
184                         if( !bInsert )
185                         {
186                             aLabel.ShowNumberInPercent = false;
187                             aLabel.ShowCategoryName = false;
188                         }
189                         xPointProp->setPropertyValue( C2U( "Label" ), uno::makeAny( aLabel ) );
190                     }
191                 }
192             }
193         }
194     }
195     catch( uno::Exception &e)
196     {
197         ASSERT_EXCEPTION( e );
198     }
199 }
200 
201 } // anonymous namespace
202 // ----------------------------------------
203 
204 namespace chart
205 {
206 
207 namespace DataSeriesHelper
208 {
209 
210 OUString GetRole( const uno::Reference< chart2::data::XLabeledDataSequence >& xLabeledDataSequence )
211 {
212     OUString aRet;
213     if( xLabeledDataSequence.is() )
214     {
215         Reference< beans::XPropertySet > xProp( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
216         if( xProp.is() )
217             xProp->getPropertyValue( C2U("Role") ) >>= aRet;
218     }
219     return aRet;
220 }
221 
222 Reference< chart2::data::XLabeledDataSequence >
223     getDataSequenceByRole(
224         const Reference< chart2::data::XDataSource > & xSource, OUString aRole,
225         bool bMatchPrefix /* = false */ )
226 {
227     Reference< chart2::data::XLabeledDataSequence > aNoResult;
228     if( ! xSource.is())
229         return aNoResult;
230     Sequence< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq( xSource->getDataSequences());
231 
232     const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
233     const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
234     const Reference< chart2::data::XLabeledDataSequence > * pMatch =
235         ::std::find_if( pBegin, pEnd, lcl_MatchesRole( aRole, bMatchPrefix ));
236 
237     if( pMatch != pEnd )
238         return *pMatch;
239 
240     return aNoResult;
241 }
242 
243 ::std::vector< Reference< chart2::data::XLabeledDataSequence > >
244     getAllDataSequencesByRole( const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aDataSequences,
245                                OUString aRole, bool bMatchPrefix /* = false */ )
246 {
247     ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aResultVec;
248     ::std::remove_copy_if( aDataSequences.getConstArray(), aDataSequences.getConstArray() + aDataSequences.getLength(),
249                            ::std::back_inserter( aResultVec ),
250                            ::std::not1( lcl_MatchesRole( aRole, bMatchPrefix )));
251     return aResultVec;
252 }
253 
254 Reference< chart2::data::XDataSource >
255     getDataSource( const Sequence< Reference< chart2::XDataSeries > > & aSeries )
256 {
257     ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aSeqVec;
258 
259     for( sal_Int32 i = 0; i < aSeries.getLength(); ++i )
260     {
261         Reference< chart2::data::XDataSource > xSource( aSeries[ i ], uno::UNO_QUERY );
262         if( xSource.is())
263         {
264             Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( xSource->getDataSequences());
265             ::std::copy( aSeq.getConstArray(), aSeq.getConstArray() + aSeq.getLength(),
266                          ::std::back_inserter( aSeqVec ));
267         }
268     }
269 
270     return Reference< chart2::data::XDataSource >(
271         new DataSource( ContainerHelper::ContainerToSequence( aSeqVec )));
272 }
273 
274 namespace
275 {
276 OUString lcl_getDataSequenceLabel( const Reference< chart2::data::XDataSequence > & xSequence )
277 {
278     OUString aResult;
279 
280     Reference< chart2::data::XTextualDataSequence > xTextSeq( xSequence, uno::UNO_QUERY );
281     if( xTextSeq.is())
282     {
283         Sequence< OUString > aSeq( xTextSeq->getTextualData());
284 
285         const sal_Int32 nMax = aSeq.getLength() - 1;
286         OUString aVal;
287         OUStringBuffer aBuf;
288 
289         for( sal_Int32 i = 0; i <= nMax; ++i )
290         {
291             aBuf.append( aSeq[i] );
292             if( i < nMax )
293                 aBuf.append( sal_Unicode( ' ' ));
294         }
295         aResult = aBuf.makeStringAndClear();
296     }
297     else if( xSequence.is())
298     {
299         Sequence< uno::Any > aSeq( xSequence->getData());
300 
301         const sal_Int32 nMax = aSeq.getLength() - 1;
302         OUString aVal;
303         OUStringBuffer aBuf;
304         double fNum = 0;
305 
306         for( sal_Int32 i = 0; i <= nMax; ++i )
307         {
308             if( aSeq[i] >>= aVal )
309             {
310                 aBuf.append( aVal );
311                 if( i < nMax )
312                     aBuf.append( sal_Unicode( ' ' ));
313             }
314             else if( aSeq[ i ] >>= fNum )
315             {
316                 aBuf.append( fNum );
317                 if( i < nMax )
318                     aBuf.append( sal_Unicode( ' ' ));
319             }
320         }
321         aResult = aBuf.makeStringAndClear();
322     }
323 
324     return aResult;
325 }
326 }
327 
328 OUString getLabelForLabeledDataSequence(
329     const Reference< chart2::data::XLabeledDataSequence > & xLabeledSeq )
330 {
331     OUString aResult;
332     if( xLabeledSeq.is())
333     {
334         Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
335         if( xSeq.is() )
336             aResult = lcl_getDataSequenceLabel( xSeq );
337         if( !xSeq.is() || !aResult.getLength() )
338         {
339             // no label set or label content is empty -> use auto-generated one
340             Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues() );
341             if( xValueSeq.is() )
342             {
343                 Sequence< OUString > aLabels( xValueSeq->generateLabel(
344                     chart2::data::LabelOrigin_SHORT_SIDE ) );
345                 // no labels returned is interpreted as: auto-generation not
346                 // supported by sequence
347                 if( aLabels.getLength() )
348                     aResult=aLabels[0];
349                 else
350                 {
351                     //todo?: maybe use the index of the series as name
352                     //but as the index may change it would be better to have such a name persistent
353                     //what is not possible at the moment
354                     //--> maybe use the identifier as part of the name ...
355                     aResult = lcl_getDataSequenceLabel( xValueSeq );
356                 }
357             }
358         }
359     }
360     return aResult;
361 }
362 
363 OUString getDataSeriesLabel(
364     const Reference< chart2::XDataSeries > & xSeries,
365     const OUString & rLabelSequenceRole )
366 {
367     OUString aResult;
368 
369     Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
370     if( xSource.is())
371     {
372         Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
373             ::chart::DataSeriesHelper::getDataSequenceByRole( xSource, rLabelSequenceRole ));
374         if( xLabeledSeq.is())
375             aResult = getLabelForLabeledDataSequence( xLabeledSeq );
376         else
377         {
378             // special case: labeled data series with only a label and no values may
379             // serve as label
380             xLabeledSeq.set( lcl_findLSequenceWithOnlyLabel( xSource ));
381             if( xLabeledSeq.is())
382             {
383                 Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getLabel());
384                 if( xSeq.is())
385                     aResult = lcl_getDataSequenceLabel( xSeq );
386             }
387         }
388 
389     }
390 
391     return aResult;
392 }
393 
394 void setStackModeAtSeries(
395     const Sequence< Reference< chart2::XDataSeries > > & aSeries,
396     const Reference< chart2::XCoordinateSystem > & xCorrespondingCoordinateSystem,
397     StackMode eStackMode )
398 {
399     if( eStackMode == StackMode_AMBIGUOUS )
400         return;
401 
402     const OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( "StackingDirection" ));
403     const uno::Any aPropValue = uno::makeAny(
404         ( (eStackMode == StackMode_Y_STACKED) ||
405           (eStackMode == StackMode_Y_STACKED_PERCENT) )
406         ? chart2::StackingDirection_Y_STACKING
407         : (eStackMode == StackMode_Z_STACKED )
408         ? chart2::StackingDirection_Z_STACKING
409         : chart2::StackingDirection_NO_STACKING );
410 
411     std::set< sal_Int32 > aAxisIndexSet;
412     for( sal_Int32 i=0; i<aSeries.getLength(); ++i )
413     {
414         try
415         {
416             Reference< beans::XPropertySet > xProp( aSeries[i], uno::UNO_QUERY );
417             if( xProp.is() )
418             {
419                 xProp->setPropertyValue( aPropName, aPropValue );
420 
421                 sal_Int32 nAxisIndex;
422                 xProp->getPropertyValue( C2U("AttachedAxisIndex") ) >>= nAxisIndex;
423                 aAxisIndexSet.insert(nAxisIndex);
424             }
425         }
426         catch( uno::Exception & ex )
427         {
428             ASSERT_EXCEPTION( ex );
429         }
430     }
431 
432     if( xCorrespondingCoordinateSystem.is() &&
433         1 < xCorrespondingCoordinateSystem->getDimension() )
434     {
435         sal_Int32 nAxisIndexCount = aAxisIndexSet.size();
436         if( !nAxisIndexCount )
437         {
438             aAxisIndexSet.insert(0);
439             nAxisIndexCount = aAxisIndexSet.size();
440         }
441 
442         for( ::std::set< sal_Int32 >::const_iterator aIt = aAxisIndexSet.begin();
443             aIt != aAxisIndexSet.end(); ++aIt )
444         {
445             sal_Int32 nAxisIndex = *aIt;
446             Reference< chart2::XAxis > xAxis(
447                 xCorrespondingCoordinateSystem->getAxisByDimension( 1, nAxisIndex ));
448             if( xAxis.is())
449             {
450                 sal_Bool bPercent = (eStackMode == StackMode_Y_STACKED_PERCENT);
451                 chart2::ScaleData aScaleData = xAxis->getScaleData();
452 
453                 if( bPercent != (aScaleData.AxisType==chart2::AxisType::PERCENT) )
454                 {
455                     if( bPercent )
456                         aScaleData.AxisType = chart2::AxisType::PERCENT;
457                     else
458                         aScaleData.AxisType = chart2::AxisType::REALNUMBER;
459                     xAxis->setScaleData( aScaleData );
460                 }
461             }
462         }
463     }
464 }
465 
466 sal_Int32 getAttachedAxisIndex( const Reference< chart2::XDataSeries > & xSeries )
467 {
468     sal_Int32 nRet = 0;
469     try
470     {
471         Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
472         if( xProp.is() )
473         {
474             xProp->getPropertyValue( C2U("AttachedAxisIndex") ) >>= nRet;
475         }
476     }
477     catch( uno::Exception & ex )
478     {
479         ASSERT_EXCEPTION( ex );
480     }
481     return nRet;
482 }
483 
484 sal_Int32 getNumberFormatKeyFromAxis(
485     const Reference< chart2::XDataSeries > & xSeries,
486     const Reference< chart2::XCoordinateSystem > & xCorrespondingCoordinateSystem,
487     sal_Int32 nDimensionIndex,
488     sal_Int32 nAxisIndex /* = -1 */ )
489 {
490     sal_Int32 nResult =  0;
491     if( nAxisIndex == -1 )
492         nAxisIndex = getAttachedAxisIndex( xSeries );
493     try
494     {
495         Reference< beans::XPropertySet > xAxisProp(
496             xCorrespondingCoordinateSystem->getAxisByDimension( nDimensionIndex, nAxisIndex ), uno::UNO_QUERY );
497         if( xAxisProp.is())
498             xAxisProp->getPropertyValue( C2U("NumberFormat")) >>= nResult;
499     }
500     catch( const uno::Exception & ex )
501     {
502         ASSERT_EXCEPTION( ex );
503     }
504 
505     return nResult;
506 }
507 
508 Reference< chart2::XCoordinateSystem > getCoordinateSystemOfSeries(
509     const Reference< chart2::XDataSeries > & xSeries,
510     const Reference< chart2::XDiagram > & xDiagram )
511 {
512     Reference< chart2::XCoordinateSystem > xResult;
513     Reference< chart2::XChartType > xDummy;
514     lcl_getCooSysAndChartTypeOfSeries( xSeries, xDiagram, xResult, xDummy );
515 
516     return xResult;
517 }
518 
519 Reference< chart2::XChartType > getChartTypeOfSeries(
520     const Reference< chart2::XDataSeries > & xSeries,
521     const Reference< chart2::XDiagram > & xDiagram )
522 {
523     Reference< chart2::XChartType > xResult;
524     Reference< chart2::XCoordinateSystem > xDummy;
525     lcl_getCooSysAndChartTypeOfSeries( xSeries, xDiagram, xDummy, xResult );
526 
527     return xResult;
528 }
529 
530 void deleteSeries(
531     const Reference< chart2::XDataSeries > & xSeries,
532     const Reference< chart2::XChartType > & xChartType )
533 {
534     try
535     {
536         Reference< chart2::XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY_THROW );
537         ::std::vector< Reference< chart2::XDataSeries > > aSeries(
538             ContainerHelper::SequenceToVector( xSeriesCnt->getDataSeries()));
539         ::std::vector< Reference< chart2::XDataSeries > >::iterator aIt =
540               ::std::find( aSeries.begin(), aSeries.end(), xSeries );
541         if( aIt != aSeries.end())
542         {
543             aSeries.erase( aIt );
544             xSeriesCnt->setDataSeries( ContainerHelper::ContainerToSequence( aSeries ));
545         }
546     }
547     catch( uno::Exception & ex )
548     {
549         ASSERT_EXCEPTION( ex );
550     }
551 }
552 
553 void switchSymbolsOnOrOff( const Reference< beans::XPropertySet > & xSeriesProperties,
554                     bool bSymbolsOn, sal_Int32 nSeriesIndex )
555 {
556     if( !xSeriesProperties.is() )
557         return;
558 
559     chart2::Symbol aSymbProp;
560     if( (xSeriesProperties->getPropertyValue( C2U( "Symbol" )) >>= aSymbProp ) )
561     {
562         if( !bSymbolsOn )
563             aSymbProp.Style = chart2::SymbolStyle_NONE;
564         else if( aSymbProp.Style == chart2::SymbolStyle_NONE )
565         {
566             aSymbProp.Style = chart2::SymbolStyle_STANDARD;
567             aSymbProp.StandardSymbol = nSeriesIndex;
568         }
569         xSeriesProperties->setPropertyValue( C2U( "Symbol" ), uno::makeAny( aSymbProp ));
570     }
571     //todo: check attributed data points
572 }
573 
574 void switchLinesOnOrOff( const Reference< beans::XPropertySet > & xSeriesProperties, bool bLinesOn )
575 {
576     if( !xSeriesProperties.is() )
577         return;
578 
579     if( bLinesOn )
580     {
581         // keep line-styles that are not NONE
582         drawing::LineStyle eLineStyle;
583         if( (xSeriesProperties->getPropertyValue( C2U( "LineStyle" )) >>= eLineStyle ) &&
584             eLineStyle == drawing::LineStyle_NONE )
585         {
586             xSeriesProperties->setPropertyValue( C2U( "LineStyle" ), uno::makeAny( drawing::LineStyle_SOLID ) );
587         }
588     }
589     else
590         xSeriesProperties->setPropertyValue( C2U( "LineStyle" ), uno::makeAny( drawing::LineStyle_NONE ) );
591 }
592 
593 void makeLinesThickOrThin( const Reference< beans::XPropertySet > & xSeriesProperties, bool bThick )
594 {
595     if( !xSeriesProperties.is() )
596         return;
597 
598     sal_Int32 nNewValue = bThick ? 80 : 0;
599     sal_Int32 nOldValue = 0;
600     if( (xSeriesProperties->getPropertyValue( C2U( "LineWidth" )) >>= nOldValue ) &&
601         nOldValue != nNewValue )
602     {
603         if( !(bThick && nOldValue>0))
604             xSeriesProperties->setPropertyValue( C2U( "LineWidth" ), uno::makeAny( nNewValue ) );
605     }
606 }
607 
608 void setPropertyAlsoToAllAttributedDataPoints( const Reference< chart2::XDataSeries >& xSeries,
609                                               const OUString& rPropertyName, const uno::Any& rPropertyValue )
610 {
611     Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
612     if( !xSeriesProperties.is() )
613         return;
614 
615     xSeriesProperties->setPropertyValue( rPropertyName, rPropertyValue );
616     uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
617     if( xSeriesProperties->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
618     {
619         for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
620         {
621             Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
622             if(!xPointProp.is())
623                 continue;
624             xPointProp->setPropertyValue( rPropertyName, rPropertyValue );
625         }
626     }
627 }
628 
629 bool hasAttributedDataPointDifferentValue( const Reference< chart2::XDataSeries >& xSeries,
630                                               const OUString& rPropertyName, const uno::Any& rPropertyValue )
631 {
632     Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
633     if( !xSeriesProperties.is() )
634         return false;
635 
636     uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
637     if( xSeriesProperties->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
638     {
639         for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
640         {
641             Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
642             if(!xPointProp.is())
643                 continue;
644             uno::Any aPointValue( xPointProp->getPropertyValue( rPropertyName ) );
645             if( !( rPropertyValue==aPointValue ) )
646                 return true;
647         }
648     }
649     return false;
650 }
651 
652 bool areAllSeriesAttachedToSameAxis( const uno::Reference< chart2::XChartType >& xChartType, sal_Int32 & rOutAxisIndex )
653 {
654     try
655     {
656         uno::Reference< chart2::XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY_THROW );
657         uno::Sequence< uno::Reference< chart2::XDataSeries > > aSeriesSeq( xDataSeriesContainer->getDataSeries());
658 
659         const sal_Int32 nSeriesCount( aSeriesSeq.getLength());
660         // AxisIndex can only be 0 or 1
661         sal_Int32 nSeriesAtFirstAxis = 0;
662         sal_Int32 nSeriesAtSecondAxis = 0;
663 
664         for( sal_Int32 nI = 0; nI < nSeriesCount; ++nI )
665         {
666             uno::Reference< chart2::XDataSeries > xSeries( aSeriesSeq[nI], uno::UNO_QUERY );
667             sal_Int32 nAxisIndex = DataSeriesHelper::getAttachedAxisIndex( xSeries );
668             if( nAxisIndex == 0 )
669                 ++nSeriesAtFirstAxis;
670             else if( nAxisIndex == 1 )
671                 ++nSeriesAtSecondAxis;
672         }
673         OSL_ENSURE( nSeriesAtFirstAxis + nSeriesAtSecondAxis == nSeriesCount, "Invalid axis index found" );
674 
675         if( nSeriesAtFirstAxis == nSeriesCount )
676             rOutAxisIndex = 0;
677         else if( nSeriesAtSecondAxis == nSeriesCount )
678             rOutAxisIndex = 1;
679 
680         return ( nSeriesAtFirstAxis == nSeriesCount ||
681                  nSeriesAtSecondAxis == nSeriesCount );
682     }
683     catch( const uno::Exception & ex )
684     {
685         ASSERT_EXCEPTION( ex );
686         return false;
687     }
688 }
689 
690 namespace
691 {
692 
693 bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
694 {
695     if( !xDataSequence.is() )
696         return false;
697     uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY );
698     if( xProp.is() )
699     {
700         uno::Sequence< sal_Int32 > aHiddenValues;
701         try
702         {
703             xProp->getPropertyValue( C2U( "HiddenValues" ) ) >>= aHiddenValues;
704             if( !aHiddenValues.getLength() )
705                 return true;
706         }
707         catch( uno::Exception& e )
708         {
709             (void)e; // avoid warning
710             return true;
711         }
712     }
713     if( xDataSequence->getData().getLength() )
714         return true;
715     return false;
716 }
717 
718 }
719 
720 bool hasUnhiddenData( const uno::Reference< chart2::XDataSeries >& xSeries )
721 {
722     uno::Reference< chart2::data::XDataSource > xDataSource =
723         uno::Reference< chart2::data::XDataSource >( xSeries, uno::UNO_QUERY );
724 
725     uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDataSequences = xDataSource->getDataSequences();
726 
727     for(sal_Int32 nN = aDataSequences.getLength();nN--;)
728     {
729         if( !aDataSequences[nN].is() )
730             continue;
731         if( lcl_SequenceHasUnhiddenData( aDataSequences[nN]->getValues() ) )
732             return true;
733         if( lcl_SequenceHasUnhiddenData( aDataSequences[nN]->getLabel() ) )
734             return true;
735     }
736     return false;
737 }
738 
739 struct lcl_LessIndex
740 {
741     inline bool operator() ( const sal_Int32& first, const sal_Int32& second )
742     {
743         return ( first < second );
744     }
745 };
746 
747 sal_Int32 translateIndexFromHiddenToFullSequence( sal_Int32 nIndex, const Reference< chart2::data::XDataSequence >& xDataSequence, bool bTranslate )
748 {
749     if( !bTranslate )
750         return nIndex;
751 
752     try
753     {
754         uno::Reference<beans::XPropertySet> xProp( xDataSequence, uno::UNO_QUERY );
755 	    if( xProp.is())
756         {
757             Sequence<sal_Int32> aHiddenIndicesSeq;
758             xProp->getPropertyValue( C2U("HiddenValues") ) >>= aHiddenIndicesSeq;
759             if( aHiddenIndicesSeq.getLength() )
760             {
761                 ::std::vector< sal_Int32 > aHiddenIndices( ContainerHelper::SequenceToVector( aHiddenIndicesSeq ) );
762                 ::std::sort( aHiddenIndices.begin(), aHiddenIndices.end(), lcl_LessIndex() );
763 
764                 sal_Int32 nHiddenCount = static_cast<sal_Int32>(aHiddenIndices.size());
765                 for( sal_Int32 nN = 0; nN < nHiddenCount; ++nN)
766                 {
767                     if( aHiddenIndices[nN] <= nIndex )
768                         nIndex += 1;
769                     else
770                         break;
771                 }
772             }
773         }
774     }
775     catch (const beans::UnknownPropertyException&)
776     {
777     }
778     return nIndex;
779 }
780 
781 bool hasDataLabelsAtSeries( const Reference< chart2::XDataSeries >& xSeries )
782 {
783     bool bRet = false;
784     try
785     {
786         Reference< beans::XPropertySet > xProp( xSeries, uno::UNO_QUERY );
787         if( xProp.is() )
788         {
789             DataPointLabel aLabel;
790             if( (xProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel) )
791                 bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName;
792         }
793     }
794     catch( uno::Exception &e)
795     {
796         ASSERT_EXCEPTION( e );
797     }
798     return bRet;
799 }
800 
801 bool hasDataLabelsAtPoints( const Reference< chart2::XDataSeries >& xSeries )
802 {
803     bool bRet = false;
804     try
805     {
806         Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
807         if( xSeriesProperties.is() )
808         {
809             uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
810             if( xSeriesProperties->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
811             {
812                 for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;)
813                 {
814                     Reference< beans::XPropertySet > xPointProp( xSeries->getDataPointByIndex(aAttributedDataPointIndexList[nN]) );
815                     if( xPointProp.is() )
816                     {
817                         DataPointLabel aLabel;
818                         if( (xPointProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel) )
819                             bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName;
820                         if( bRet )
821                             break;
822                     }
823                 }
824             }
825         }
826     }
827     catch( uno::Exception &e)
828     {
829         ASSERT_EXCEPTION( e );
830     }
831     return bRet;
832 }
833 
834 bool hasDataLabelAtPoint( const Reference< chart2::XDataSeries >& xSeries, sal_Int32 nPointIndex )
835 {
836     bool bRet = false;
837     try
838     {
839         Reference< beans::XPropertySet > xProp;
840         Reference< beans::XPropertySet > xSeriesProperties( xSeries, uno::UNO_QUERY );
841         if( xSeriesProperties.is() )
842         {
843             uno::Sequence< sal_Int32 > aAttributedDataPointIndexList;
844             if( xSeriesProperties->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList )
845             {
846                 ::std::vector< sal_Int32 > aIndices( ContainerHelper::SequenceToVector( aAttributedDataPointIndexList ) );
847                 ::std::vector< sal_Int32 >::iterator aIt = ::std::find( aIndices.begin(), aIndices.end(), nPointIndex );
848                 if( aIt != aIndices.end())
849                     xProp = xSeries->getDataPointByIndex(nPointIndex);
850                 else
851                     xProp = xSeriesProperties;
852             }
853             if( xProp.is() )
854             {
855                 DataPointLabel aLabel;
856                 if( (xProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel) )
857                     bRet = aLabel.ShowNumber || aLabel.ShowNumberInPercent || aLabel.ShowCategoryName;
858             }
859         }
860     }
861     catch( uno::Exception &e)
862     {
863         ASSERT_EXCEPTION( e );
864     }
865     return bRet;
866 }
867 
868 void insertDataLabelsToSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries )
869 {
870     lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, true /*bInsert*/ );
871 }
872 
873 void deleteDataLabelsFromSeriesAndAllPoints( const Reference< chart2::XDataSeries >& xSeries )
874 {
875     lcl_insertOrDeleteDataLabelsToSeriesAndAllPoints( xSeries, false /*bInsert*/ );
876 }
877 
878 
879 void insertDataLabelToPoint( const Reference< beans::XPropertySet >& xPointProp )
880 {
881     try
882     {
883         if( xPointProp.is() )
884         {
885             DataPointLabel aLabel;
886             xPointProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel;
887             aLabel.ShowNumber = true;
888             xPointProp->setPropertyValue( C2U( "Label" ), uno::makeAny( aLabel ) );
889         }
890     }
891     catch( uno::Exception &e)
892     {
893         ASSERT_EXCEPTION( e );
894     }
895 }
896 
897 void deleteDataLabelsFromPoint( const Reference< beans::XPropertySet >& xPointProp )
898 {
899     try
900     {
901         if( xPointProp.is() )
902         {
903             DataPointLabel aLabel;
904             xPointProp->getPropertyValue( C2U( "Label" ) ) >>= aLabel;
905             aLabel.ShowNumber = false;
906             aLabel.ShowNumberInPercent = false;
907             aLabel.ShowCategoryName = false;
908             xPointProp->setPropertyValue( C2U( "Label" ), uno::makeAny( aLabel ) );
909         }
910     }
911     catch( uno::Exception &e)
912     {
913         ASSERT_EXCEPTION( e );
914     }
915 }
916 
917 } //  namespace DataSeriesHelper
918 } //  namespace chart
919