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