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 
31 #include "DataBrowserModel.hxx"
32 #include "DialogModel.hxx"
33 #include "ChartModelHelper.hxx"
34 #include "DiagramHelper.hxx"
35 #include "DataSeriesHelper.hxx"
36 #include "PropertyHelper.hxx"
37 #include "ControllerLockGuard.hxx"
38 #include "macros.hxx"
39 #include "StatisticsHelper.hxx"
40 #include "ContainerHelper.hxx"
41 #include "ChartTypeHelper.hxx"
42 #include "chartview/ExplicitValueProvider.hxx"
43 #include "ExplicitCategoriesProvider.hxx"
44 
45 #include <com/sun/star/container/XIndexReplace.hpp>
46 #include <com/sun/star/chart2/XAxis.hpp>
47 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
48 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
49 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
50 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
51 #include <com/sun/star/chart2/data/XDataSource.hpp>
52 #include <com/sun/star/chart2/data/XDataSink.hpp>
53 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
54 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
55 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
56 #include <com/sun/star/util/XModifiable.hpp>
57 
58 #include <rtl/math.hxx>
59 
60 #include <algorithm>
61 
62 #if OSL_DEBUG_LEVEL > 1
63 #include <cstdio>
64 #endif
65 
66 using namespace ::com::sun::star;
67 
68 using ::com::sun::star::uno::Reference;
69 using ::com::sun::star::uno::Sequence;
70 using ::rtl::OUString;
71 
72 namespace
73 {
74 OUString lcl_getRole(
75     const Reference< chart2::data::XDataSequence > & xSeq )
76 {
77     OUString aResult;
78     Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
79     if( xProp.is())
80     {
81         try
82         {
83             xProp->getPropertyValue( C2U("Role")) >>= aResult;
84         }
85         catch( const uno::Exception & ex )
86         {
87             ASSERT_EXCEPTION( ex );
88         }
89     }
90     return aResult;
91 }
92 
93 
94 OUString lcl_getRole(
95     const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
96 {
97     OUString aResult;
98     if( xLSeq.is())
99         aResult = lcl_getRole( xLSeq->getValues());
100     return aResult;
101 }
102 
103 OUString lcl_getUIRoleName(
104     const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
105 {
106     OUString aResult( lcl_getRole( xLSeq ));
107     if( aResult.getLength())
108         aResult = ::chart::DialogModel::ConvertRoleFromInternalToUI( aResult );
109     return aResult;
110 }
111 
112 void lcl_copyDataSequenceProperties(
113     const Reference< chart2::data::XDataSequence > & xOldSequence,
114     const Reference< chart2::data::XDataSequence > & xNewSequence )
115 {
116     Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY );
117     Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY );
118     comphelper::copyProperties( xOldSeqProp, xNewSeqProp );
119 }
120 
121 bool lcl_SequenceOfSeriesIsShared(
122     const Reference< chart2::XDataSeries > & xSeries,
123     const Reference< chart2::data::XDataSequence > & xValues )
124 {
125     bool bResult = false;
126     if( !xValues.is())
127         return bResult;
128     try
129     {
130         OUString aValuesRole( lcl_getRole( xValues ));
131         OUString aValuesRep( xValues->getSourceRangeRepresentation());
132         Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY_THROW );
133         Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
134         for( sal_Int32 i=0; i<aLSeq.getLength(); ++i )
135             if( aLSeq[i].is() &&
136                 lcl_getRole( aLSeq[i] ).equals( aValuesRole ))
137             {
138                 // getValues().is(), because lcl_getRole checked that already
139                 bResult = (aValuesRep == aLSeq[i]->getValues()->getSourceRangeRepresentation());
140                 // assumption: a role appears only once in a series
141                 break;
142             }
143     }
144     catch( const uno::Exception & ex )
145     {
146         ASSERT_EXCEPTION( ex );
147     }
148     return bResult;
149 }
150 
151 typedef ::std::vector< Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec;
152 
153 lcl_tSharedSeqVec lcl_getSharedSequences( const Sequence< Reference< chart2::XDataSeries > > & rSeries )
154 {
155     // @todo: if only some series share a sequence, those have to be duplicated
156     // and made unshared for all series
157     lcl_tSharedSeqVec aResult;
158     // if we have only one series, we don't want any shared sequences
159     if( rSeries.getLength() <= 1 )
160         return aResult;
161 
162     Reference< chart2::data::XDataSource > xSource( rSeries[0], uno::UNO_QUERY );
163     Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
164     for( sal_Int32 nIdx=0; nIdx<aLSeq.getLength(); ++nIdx )
165     {
166         Reference< chart2::data::XDataSequence > xValues( aLSeq[nIdx]->getValues());
167         bool bShared = true;
168         for( sal_Int32 nSeriesIdx=1; nSeriesIdx<rSeries.getLength(); ++nSeriesIdx )
169         {
170             bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues );
171             if( !bShared )
172                 break;
173         }
174         if( bShared )
175             aResult.push_back( aLSeq[nIdx] );
176     }
177 
178     return aResult;
179 }
180 
181 sal_Int32 lcl_getValuesRepresentationIndex(
182     const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
183 {
184     sal_Int32 nResult = -1;
185     if( xLSeq.is())
186     {
187         Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues());
188         if( xSeq.is())
189         {
190             OUString aRep( xSeq->getSourceRangeRepresentation());
191             nResult = aRep.toInt32();
192         }
193     }
194     return nResult;
195 }
196 
197 struct lcl_RepresentationsOfLSeqMatch : public ::std::unary_function< Reference< chart2::data::XLabeledDataSequence >, bool >
198 {
199     lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
200             m_aValuesRep( xLSeq.is() ?
201                           (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString())
202                           : OUString() )
203     {}
204     bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
205     {
206         return (xLSeq.is() &&
207                 xLSeq->getValues().is() &&
208                 (xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep ));
209     }
210 private:
211     OUString m_aValuesRep;
212 };
213 
214 struct lcl_RolesOfLSeqMatch : public ::std::unary_function< Reference< chart2::data::XLabeledDataSequence >, bool >
215 {
216     lcl_RolesOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
217             m_aRole( lcl_getRole( xLSeq ))
218     {}
219     bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
220     {
221         return lcl_getRole( xLSeq ).equals( m_aRole );
222     }
223 private:
224     OUString m_aRole;
225 };
226 
227 bool lcl_ShowCategories( const Reference< chart2::XDiagram > & /* xDiagram */ )
228 {
229     // show categories for all charts
230     return true;
231 //     return DiagramHelper::isCategoryDiagram( xDiagram );
232 }
233 
234 bool lcl_ShowCategoriesAsDataLabel( const Reference< chart2::XDiagram > & xDiagram )
235 {
236     return ! ::chart::DiagramHelper::isCategoryDiagram( xDiagram );
237 }
238 
239 } // anonymous namespace
240 
241 namespace chart
242 {
243 
244 
245 struct DataBrowserModel::tDataColumn
246 {
247     ::com::sun::star::uno::Reference<
248             ::com::sun::star::chart2::XDataSeries >                m_xDataSeries;
249     sal_Int32                                                  m_nIndexInDataSeries;
250     ::rtl::OUString                                            m_aUIRoleName;
251     ::com::sun::star::uno::Reference<
252             ::com::sun::star::chart2::data::XLabeledDataSequence > m_xLabeledDataSequence;
253     eCellType                                                  m_eCellType;
254     sal_Int32                                                  m_nNumberFormatKey;
255 
256     // default CTOR
257     tDataColumn() : m_nIndexInDataSeries( -1 ), m_eCellType( TEXT ), m_nNumberFormatKey( 0 ) {}
258     // "full" CTOR
259     tDataColumn(
260         const ::com::sun::star::uno::Reference<
261         ::com::sun::star::chart2::XDataSeries > & xDataSeries,
262         sal_Int32 nIndexInDataSeries,
263         ::rtl::OUString aUIRoleName,
264         ::com::sun::star::uno::Reference<
265         ::com::sun::star::chart2::data::XLabeledDataSequence >  xLabeledDataSequence,
266         eCellType aCellType,
267         sal_Int32 nNumberFormatKey ) :
268             m_xDataSeries( xDataSeries ),
269             m_nIndexInDataSeries( nIndexInDataSeries ),
270             m_aUIRoleName( aUIRoleName ),
271             m_xLabeledDataSequence( xLabeledDataSequence ),
272             m_eCellType( aCellType ),
273             m_nNumberFormatKey( nNumberFormatKey )
274     {}
275 };
276 
277 struct DataBrowserModel::implColumnLess : public ::std::binary_function<
278         DataBrowserModel::tDataColumn, DataBrowserModel::tDataColumn, bool >
279 {
280     bool operator() ( const first_argument_type & rLeft, const second_argument_type & rRight )
281     {
282         if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is())
283         {
284             return DialogModel::GetRoleIndexForSorting( lcl_getRole( rLeft.m_xLabeledDataSequence )) <
285                 DialogModel::GetRoleIndexForSorting( lcl_getRole( rRight.m_xLabeledDataSequence ));
286         }
287         return true;
288     }
289 };
290 
291 DataBrowserModel::DataBrowserModel(
292     const Reference< chart2::XChartDocument > & xChartDoc,
293     const Reference< uno::XComponentContext > & xContext ) :
294         m_xChartDocument( xChartDoc ),
295         m_xContext( xContext ),
296         m_apDialogModel( new DialogModel( xChartDoc, xContext ))
297 {
298     updateFromModel();
299 }
300 
301 DataBrowserModel::~DataBrowserModel()
302 {}
303 
304 namespace
305 {
306 struct lcl_DataSeriesOfHeaderMatches : public ::std::unary_function< ::chart::DataBrowserModel::tDataHeader, bool >
307 {
308     lcl_DataSeriesOfHeaderMatches(
309         const Reference< chart2::XDataSeries > & xSeriesToCompareWith ) :
310             m_xSeries( xSeriesToCompareWith )
311     {}
312     bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader )
313     {
314         return (m_xSeries == rHeader.m_xDataSeries);
315     }
316 private:
317     Reference< chart2::XDataSeries  > m_xSeries;
318 };
319 }
320 
321 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex )
322 {
323     OSL_ASSERT( m_apDialogModel.get());
324     Reference< chart2::XInternalDataProvider > xDataProvider(
325         m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
326     if( xDataProvider.is())
327     {
328         if( isCategoriesColumn(nAfterColumnIndex) )
329             nAfterColumnIndex = getCategoryColumnCount()-1;
330 
331         sal_Int32 nStartCol = 0;
332         Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument ));
333         Reference< chart2::XChartType > xChartType;
334         Reference< chart2::XDataSeries > xSeries;
335         if( static_cast< tDataColumnVector::size_type >( nAfterColumnIndex ) <= m_aColumns.size())
336             xSeries.set( m_aColumns[nAfterColumnIndex].m_xDataSeries );
337 
338         sal_Int32 nSeriesNumberFormat = 0;
339         if( xSeries.is())
340         {
341             xChartType.set( DiagramHelper::getChartTypeOfSeries( xDiagram, xSeries ));
342             tDataHeaderVector::const_iterator aIt(
343                 ::std::find_if( m_aHeaders.begin(), m_aHeaders.end(),
344                                 lcl_DataSeriesOfHeaderMatches( xSeries )));
345             if( aIt != m_aHeaders.end())
346                 nStartCol = aIt->m_nEndColumn;
347 
348             Reference< beans::XPropertySet > xSeriesProps( xSeries, uno::UNO_QUERY );
349             if( xSeriesProps.is() )
350                 xSeriesProps->getPropertyValue( C2U( "NumberFormat" )) >>= nSeriesNumberFormat;
351         }
352         else
353         {
354             xChartType.set( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ));
355             nStartCol = nAfterColumnIndex;
356         }
357 
358         if( xChartType.is())
359         {
360             sal_Int32 nOffset = 0;
361             if( xDiagram.is() && lcl_ShowCategories( xDiagram ))
362                 nOffset=getCategoryColumnCount();
363             // get shared sequences of current series
364             Reference< chart2::XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY );
365             lcl_tSharedSeqVec aSharedSequences;
366             if( xSeriesCnt.is())
367                 aSharedSequences = lcl_getSharedSequences( xSeriesCnt->getDataSeries());
368             Reference< chart2::XDataSeries > xNewSeries(
369                 m_apDialogModel->insertSeriesAfter( xSeries, xChartType, true /* bCreateDataCachedSequences */ ));
370             if( xNewSeries.is())
371             {
372                 {
373                     Reference< chart2::data::XDataSource > xSource( xNewSeries, uno::UNO_QUERY );
374                     if( xSource.is())
375                     {
376                         Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSequences(
377                             xSource->getDataSequences());
378                         sal_Int32 nSeqIdx = 0;
379                         sal_Int32 nSeqSize = aLSequences.getLength();
380                         nStartCol -= (nOffset - 1);
381                         for( sal_Int32 nIndex = nStartCol;
382                              (nSeqIdx < nSeqSize);
383                              ++nSeqIdx )
384                         {
385                             lcl_tSharedSeqVec::const_iterator aSharedIt(
386                                 ::std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
387                                                 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] )));
388                             if( aSharedIt != aSharedSequences.end())
389                             {
390                                 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues());
391                                 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel());
392                             }
393                             else
394                             {
395                                 xDataProvider->insertSequence( nIndex - 1 );
396 
397                                 // values
398                                 Reference< chart2::data::XDataSequence > xNewSeq(
399                                     xDataProvider->createDataSequenceByRangeRepresentation(
400                                         OUString::valueOf( nIndex )));
401                                 lcl_copyDataSequenceProperties(
402                                     aLSequences[nSeqIdx]->getValues(), xNewSeq );
403                                 aLSequences[nSeqIdx]->setValues( xNewSeq );
404 
405                                 // labels
406                                 Reference< chart2::data::XDataSequence > xNewLabelSeq(
407                                     xDataProvider->createDataSequenceByRangeRepresentation(
408                                         OUString( RTL_CONSTASCII_USTRINGPARAM( "label " )) +
409                                         OUString::valueOf( nIndex )));
410                                 lcl_copyDataSequenceProperties(
411                                     aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq );
412                                 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq );
413                                 ++nIndex;
414                             }
415                         }
416                     }
417                 }
418                 if( nSeriesNumberFormat != 0 )
419                 {
420                     //give the new series the same number format as the former series especially for bubble charts thus the bubble size values can be edited with same format immidiately
421                     Reference< beans::XPropertySet > xNewSeriesProps( xNewSeries, uno::UNO_QUERY );
422                     if( xNewSeriesProps.is() )
423                         xNewSeriesProps->setPropertyValue( C2U( "NumberFormat" ), uno::makeAny( nSeriesNumberFormat ) );
424                 }
425 
426                 updateFromModel();
427             }
428         }
429     }
430 }
431 
432 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex )
433 {
434     //create a new text column for complex categories
435 
436     OSL_ASSERT( m_apDialogModel.get());
437     Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
438     if( xDataProvider.is() )
439     {
440         if( !isCategoriesColumn(nAfterColumnIndex) )
441             nAfterColumnIndex = getCategoryColumnCount()-1;
442 
443         if(nAfterColumnIndex<0)
444         {
445             OSL_ENSURE( false, "wrong index for category level insertion" );
446             return;
447         }
448 
449         m_apDialogModel->startControllerLockTimer();
450         ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
451         xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 );
452         updateFromModel();
453     }
454 }
455 
456 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex )
457 {
458     OSL_ASSERT( m_apDialogModel.get());
459     if( static_cast< tDataColumnVector::size_type >( nAtColumnIndex ) < m_aColumns.size())
460     {
461         Reference< chart2::XDataSeries > xSeries( m_aColumns[nAtColumnIndex].m_xDataSeries );
462         if( xSeries.is())
463         {
464             m_apDialogModel->deleteSeries(
465                 xSeries, getHeaderForSeries( xSeries ).m_xChartType );
466 
467             //delete sequences from internal data provider that are not used anymore
468             //but do not delete sequences that are still in use by the remaining series
469             Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
470             Reference< chart2::data::XDataSource > xSourceOfDeletedSeries( xSeries, uno::UNO_QUERY );
471             if( xDataProvider.is() && xSourceOfDeletedSeries.is())
472             {
473                 ::std::vector< sal_Int32 > aSequenceIndexesToDelete;
474                 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequencesOfDeletedSeries( xSourceOfDeletedSeries->getDataSequences() );
475                 Reference< chart2::XDataSeriesContainer > xSeriesCnt( getHeaderForSeries( xSeries ).m_xChartType, uno::UNO_QUERY );
476                 if( xSeriesCnt.is())
477                 {
478                     Reference< chart2::data::XDataSource > xRemainingDataSource( DataSeriesHelper::getDataSource( xSeriesCnt->getDataSeries() ) );
479                     if( xRemainingDataSource.is() )
480                     {
481                         ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aRemainingSeq( ContainerHelper::SequenceToVector( xRemainingDataSource->getDataSequences() ) );
482                         for( sal_Int32 i=0; i<aSequencesOfDeletedSeries.getLength(); ++i )
483                         {
484                             ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::const_iterator aHitIt(
485                                 ::std::find_if( aRemainingSeq.begin(), aRemainingSeq.end(),
486                                     lcl_RepresentationsOfLSeqMatch( aSequencesOfDeletedSeries[i] )));
487                             // if not used by the remaining series this sequence can be deleted
488                             if( aHitIt == aRemainingSeq.end() )
489                                 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( aSequencesOfDeletedSeries[i] ) );
490                         }
491                     }
492                 }
493 
494                 // delete unnecessary sequences of the internal data
495                 // iterate using greatest index first, so that deletion does not
496                 // shift other sequences that will be deleted later
497                 ::std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
498                 for( ::std::vector< sal_Int32 >::reverse_iterator aIt(
499                          aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
500                 {
501                     if( *aIt != -1 )
502                         xDataProvider->deleteSequence( *aIt );
503                 }
504             }
505             updateFromModel();
506         }
507         else
508         {
509             //delete a category column if there is more than one level (in case of a single column we do not get here)
510             OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" );
511 
512             Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
513             if( xDataProvider.is() )
514             {
515                 m_apDialogModel->startControllerLockTimer();
516                 ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
517                 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex );
518                 updateFromModel();
519             }
520         }
521     }
522 }
523 
524 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex )
525 {
526     OSL_ASSERT( m_apDialogModel.get());
527     if( static_cast< tDataColumnVector::size_type >( nFirstColumnIndex ) < m_aColumns.size() - 1 )
528     {
529         Reference< chart2::XDataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries );
530         if( xSeries.is())
531         {
532             m_apDialogModel->moveSeries( xSeries, DialogModel::MOVE_DOWN );
533             updateFromModel();
534         }
535     }
536 }
537 
538 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex )
539 {
540     OSL_ASSERT( m_apDialogModel.get());
541     Reference< chart2::XInternalDataProvider > xDataProvider(
542         m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
543     // lockControllers
544     ControllerLockGuard aGuard( m_apDialogModel->getChartModel());
545     if( xDataProvider.is())
546         xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex );
547     // unlockControllers
548 }
549 
550 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex )
551 {
552     Reference< chart2::XInternalDataProvider > xDataProvider(
553         m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
554     // lockControllers
555     ControllerLockGuard aGuard( m_apDialogModel->getChartModel());
556     if( xDataProvider.is())
557         xDataProvider->insertDataPointForAllSequences( nAfterIndex );
558     // unlockControllers
559 }
560 
561 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex )
562 {
563     Reference< chart2::XInternalDataProvider > xDataProvider(
564         m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
565     // lockControllers
566     ControllerLockGuard aGuard( m_apDialogModel->getChartModel());
567     if( xDataProvider.is())
568         xDataProvider->deleteDataPointForAllSequences( nAtIndex );
569     // unlockControllers
570 }
571 
572 DataBrowserModel::tDataHeader DataBrowserModel::getHeaderForSeries(
573     const Reference< chart2::XDataSeries > & xSeries ) const
574 {
575     for( tDataHeaderVector::const_iterator aIt( m_aHeaders.begin());
576          aIt != m_aHeaders.end(); ++aIt )
577     {
578         if( aIt->m_xDataSeries == xSeries )
579             return (*aIt);
580     }
581     return tDataHeader();
582 }
583 
584 Reference< chart2::XDataSeries >
585     DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn ) const
586 {
587     tDataColumnVector::size_type nIndex( nColumn );
588     if( nIndex < m_aColumns.size())
589         return m_aColumns[nIndex].m_xDataSeries;
590     return 0;
591 }
592 
593 DataBrowserModel::eCellType DataBrowserModel::getCellType( sal_Int32 nAtColumn, sal_Int32 /* nAtRow */ ) const
594 {
595     eCellType eResult = TEXT;
596     tDataColumnVector::size_type nIndex( nAtColumn );
597     if( nIndex < m_aColumns.size())
598         eResult = m_aColumns[nIndex].m_eCellType;
599     return eResult;
600 }
601 
602 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow )
603 {
604     double fResult;
605     ::rtl::math::setNan( & fResult );
606 
607     tDataColumnVector::size_type nIndex( nAtColumn );
608     if( nIndex < m_aColumns.size() &&
609         m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
610     {
611         Reference< chart2::data::XNumericalDataSequence > xData(
612             m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
613         if( xData.is())
614         {
615             Sequence< double > aValues( xData->getNumericalData());
616             if( nAtRow < aValues.getLength())
617                 fResult = aValues[nAtRow];
618         }
619     }
620     return fResult;
621 }
622 
623 uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow )
624 {
625     uno::Any aResult;
626 
627     tDataColumnVector::size_type nIndex( nAtColumn );
628     if( nIndex < m_aColumns.size() &&
629         m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
630     {
631         Reference< chart2::data::XDataSequence > xData(
632             m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() );
633         if( xData.is() )
634         {
635             Sequence< uno::Any > aValues( xData->getData());
636             if( nAtRow < aValues.getLength())
637                 aResult = aValues[nAtRow];
638         }
639     }
640     return aResult;
641 }
642 
643 OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow )
644 {
645     OUString aResult;
646 
647     tDataColumnVector::size_type nIndex( nAtColumn );
648     if( nIndex < m_aColumns.size() &&
649         m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
650     {
651         Reference< chart2::data::XTextualDataSequence > xData(
652             m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
653         if( xData.is())
654         {
655             Sequence< OUString > aValues( xData->getTextualData());
656             if( nAtRow < aValues.getLength())
657                 aResult = aValues[nAtRow];
658         }
659     }
660     return aResult;
661 }
662 
663 sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn, sal_Int32 /* nAtRow */ )
664 {
665     tDataColumnVector::size_type nIndex( nAtColumn );
666     if( nIndex < m_aColumns.size())
667         return m_aColumns[ nIndex ].m_nNumberFormatKey;
668     return 0;
669 }
670 
671 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue )
672 {
673     bool bResult = false;
674     tDataColumnVector::size_type nIndex( nAtColumn );
675     if( nIndex < m_aColumns.size() &&
676         m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
677     {
678         bResult = true;
679         try
680         {
681             ControllerLockGuard aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
682 
683             // label
684             if( nAtRow == -1 )
685             {
686                 Reference< container::XIndexReplace > xIndexReplace(
687                     m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW );
688                 xIndexReplace->replaceByIndex( 0, rValue );
689             }
690             else
691             {
692                 Reference< container::XIndexReplace > xIndexReplace(
693                     m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW );
694                 xIndexReplace->replaceByIndex( nAtRow, rValue );
695             }
696 
697             m_apDialogModel->startControllerLockTimer();
698             //notify change directly to the model (this is necessary here as sequences for complex categories not known directly to the chart model so they do not notify their changes) (for complex categories see issue #i82971#)
699             Reference< util::XModifiable > xModifiable( m_xChartDocument, uno::UNO_QUERY );
700             if( xModifiable.is() )
701                 xModifiable->setModified(true);
702         }
703         catch( const uno::Exception & ex )
704         {
705             (void)(ex);
706             bResult = false;
707         }
708     }
709     return bResult;
710 }
711 
712 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue )
713 {
714     return (getCellType( nAtColumn, nAtRow ) == NUMBER) &&
715         setCellAny( nAtColumn, nAtRow, uno::makeAny( fValue ));
716 }
717 
718 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const ::rtl::OUString & rText )
719 {
720     return (getCellType( nAtColumn, nAtRow ) == TEXT) &&
721         setCellAny( nAtColumn, nAtRow, uno::makeAny( rText ));
722 }
723 
724 sal_Int32 DataBrowserModel::getColumnCount() const
725 {
726     return static_cast< sal_Int32 >( m_aColumns.size());
727 }
728 
729 sal_Int32 DataBrowserModel::getMaxRowCount() const
730 {
731     sal_Int32 nResult = 0;
732     tDataColumnVector::const_iterator aIt( m_aColumns.begin());
733     for( ; aIt != m_aColumns.end(); ++aIt )
734     {
735         if( aIt->m_xLabeledDataSequence.is())
736         {
737             Reference< chart2::data::XDataSequence > xSeq(
738                 aIt->m_xLabeledDataSequence->getValues());
739             if( !xSeq.is())
740                 continue;
741             sal_Int32 nLength( xSeq->getData().getLength());
742             if( nLength > nResult )
743                 nResult = nLength;
744         }
745     }
746 
747     return nResult;
748 }
749 
750 OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const
751 {
752     if( nColumnIndex != -1 &&
753         static_cast< sal_uInt32 >( nColumnIndex ) < m_aColumns.size())
754         return m_aColumns[ nColumnIndex ].m_aUIRoleName;
755     return OUString();
756 }
757 
758 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const
759 {
760     bool bIsCategories = false;
761     if( nColumnIndex>=0 && nColumnIndex<static_cast< sal_Int32 >(m_aColumns.size()) )
762         bIsCategories = !m_aColumns[ nColumnIndex ].m_xDataSeries.is();
763     return bIsCategories;
764 }
765 
766 sal_Int32 DataBrowserModel::getCategoryColumnCount()
767 {
768     sal_Int32 nLastTextColumnIndex = -1;
769     tDataColumnVector::const_iterator aIt = m_aColumns.begin();
770     for( ; aIt != m_aColumns.end(); ++aIt )
771     {
772         if( !aIt->m_xDataSeries.is() )
773             nLastTextColumnIndex++;
774         else
775             break;
776     }
777     return nLastTextColumnIndex+1;
778 }
779 
780 const DataBrowserModel::tDataHeaderVector& DataBrowserModel::getDataHeaders() const
781 {
782     return m_aHeaders;
783 }
784 
785 void DataBrowserModel::updateFromModel()
786 {
787     if( !m_xChartDocument.is())
788         return;
789     m_aColumns.clear();
790     m_aHeaders.clear();
791 
792     Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument ));
793     if( !xDiagram.is())
794         return;
795 
796     // set template at DialogModel
797     uno::Reference< lang::XMultiServiceFactory > xFact( m_xChartDocument->getChartTypeManager(), uno::UNO_QUERY );
798     DiagramHelper::tTemplateWithServiceName aTemplateAndService =
799         DiagramHelper::getTemplateForDiagram( xDiagram, xFact );
800     if( aTemplateAndService.first.is())
801         m_apDialogModel->setTemplate( aTemplateAndService.first );
802 
803     sal_Int32 nHeaderStart = 0;
804     sal_Int32 nHeaderEnd   = 0;
805     if( lcl_ShowCategories( xDiagram ))
806     {
807         Reference< frame::XModel > xChartModel( m_xChartDocument, uno::UNO_QUERY );
808         ExplicitCategoriesProvider aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(xChartModel), xChartModel );
809 
810         const Sequence< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() );
811         sal_Int32 nLevelCount = rSplitCategoriesList.getLength();
812         for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
813         {
814             Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] );
815             if( !xCategories.is() )
816                 continue;
817 
818             tDataColumn aCategories;
819             aCategories.m_xLabeledDataSequence.set( xCategories );
820             if( lcl_ShowCategoriesAsDataLabel( xDiagram ))
821                 aCategories.m_aUIRoleName = DialogModel::GetRoleDataLabel();
822             else
823                 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories );
824             aCategories.m_eCellType = TEXTORDATE;
825             m_aColumns.push_back( aCategories );
826             ++nHeaderStart;
827         }
828     }
829 
830     Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
831     if( !xCooSysCnt.is())
832         return;
833     Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
834     for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
835     {
836         Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
837         Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes());
838         sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( 0, aCooSysSeq[nCooSysIdx], 0, 0 );
839 
840         for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypes.getLength(); ++nCTIdx )
841         {
842             Reference< chart2::XDataSeriesContainer > xSeriesCnt( aChartTypes[nCTIdx], uno::UNO_QUERY );
843             if( xSeriesCnt.is())
844             {
845                 rtl::OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( aChartTypes[nCTIdx] );
846 
847                 Sequence< Reference< chart2::XDataSeries > > aSeries( xSeriesCnt->getDataSeries());
848                 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries ));
849                 for( lcl_tSharedSeqVec::const_iterator aIt( aSharedSequences.begin());
850                      aIt != aSharedSequences.end(); ++aIt )
851                 {
852                     tDataColumn aSharedSequence;
853                     aSharedSequence.m_xLabeledDataSequence = *aIt;
854                     aSharedSequence.m_aUIRoleName = lcl_getUIRoleName( *aIt );
855                     aSharedSequence.m_eCellType = NUMBER;
856                     // as the sequences are shared it should be ok to take the first series
857                     // @todo: dimension index 0 for x-values used here. This is just a guess.
858                     // Also, the axis index is 0, as there is usually only one x-axis
859                     aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat;
860                     m_aColumns.push_back( aSharedSequence );
861                     ++nHeaderStart;
862                 }
863                 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx )
864                 {
865                     tDataColumnVector::size_type nStartColIndex = m_aColumns.size();
866                     Reference< chart2::XDataSeries > xSeries( aSeries[nSeriesIdx] );
867                     Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
868                     if( xSource.is())
869                     {
870                         Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqs( xSource->getDataSequences());
871                         if( aLSeqs.getLength() == 0 )
872                             continue;
873                         nHeaderEnd = nHeaderStart;
874 
875                         // @todo: dimension index 1 for y-values used here. This is just a guess
876                         sal_Int32 nYAxisNumberFormatKey =
877                             DataSeriesHelper::getNumberFormatKeyFromAxis(
878                                 aSeries[nSeriesIdx], aCooSysSeq[nCooSysIdx], 1 );
879 
880                         sal_Int32 nSeqIdx=0;
881                         for( ; nSeqIdx<aLSeqs.getLength(); ++nSeqIdx )
882                         {
883                             sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey;
884                             OUString aRole = lcl_getRole( aLSeqs[nSeqIdx] );
885 
886                             if( aRole.equals( aRoleForDataLabelNumberFormat ) )
887                             {
888                                 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
889                                     Reference< beans::XPropertySet >( xSeries, uno::UNO_QUERY ), xSeries, -1, xDiagram );
890                             }
891                             else if( aRole.equals( C2U( "values-x" ) ) )
892                                 nSequenceNumberFormatKey = nXAxisNumberFormat;
893 
894                             if( ::std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
895                                              lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) == aSharedSequences.end())
896                             {
897                                 // no shared sequence
898                                 m_aColumns.push_back(
899                                     tDataColumn(
900                                         aSeries[nSeriesIdx],
901                                         nSeqIdx,
902                                         lcl_getUIRoleName( aLSeqs[nSeqIdx] ),
903                                         aLSeqs[nSeqIdx],
904                                         NUMBER,
905                                         nSequenceNumberFormatKey ));
906                                 ++nHeaderEnd;
907                             }
908                             // else skip
909                         }
910                         bool bSwapXAndYAxis = false;
911                         try
912                         {
913                             Reference< beans::XPropertySet > xProp( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY );
914                             xProp->getPropertyValue( OUString(RTL_CONSTASCII_USTRINGPARAM("SwapXAndYAxis"))) >>= bSwapXAndYAxis;
915                         }
916                         catch( const beans::UnknownPropertyException & ex )
917                         {
918                             (void)ex;
919                         }
920 
921                         // add ranges for error bars if present for a series
922                         if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx], /* bYError = */ true ))
923                             addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd );
924 
925                         m_aHeaders.push_back(
926                             tDataHeader(
927                                 aSeries[nSeriesIdx],
928                                 aChartTypes[nCTIdx],
929                                 bSwapXAndYAxis,
930                                 nHeaderStart,
931                                 nHeaderEnd - 1 ));
932 
933                         nHeaderStart = nHeaderEnd;
934 
935                         ::std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() );
936                     }
937                 }
938             }
939         }
940     }
941 }
942 
943 void DataBrowserModel::addErrorBarRanges(
944     const Reference< chart2::XDataSeries > & xDataSeries,
945     sal_Int32 nNumberFormatKey,
946     sal_Int32 & rInOutSequenceIndex,
947     sal_Int32 & rInOutHeaderEnd )
948 {
949     try
950     {
951         ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aSequences;
952 
953         // x error bars
954         // ------------
955         Reference< chart2::data::XDataSource > xErrorSource(
956             StatisticsHelper::getErrorBars( xDataSeries, /* bYError = */ false ), uno::UNO_QUERY );
957 
958         // positive x error bars
959         Reference< chart2::data::XLabeledDataSequence > xErrorLSequence(
960             StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
961                 xErrorSource,
962                 /* bPositiveValue = */ true,
963                 /* bYError = */ false ));
964         if( xErrorLSequence.is())
965             aSequences.push_back( xErrorLSequence );
966 
967         // negative x error bars
968         xErrorLSequence.set(
969             StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
970                 xErrorSource,
971                 /* bPositiveValue = */ false,
972                 /* bYError = */ false ));
973         if( xErrorLSequence.is())
974             aSequences.push_back( xErrorLSequence );
975 
976         // y error bars
977         // ------------
978         xErrorSource.set(
979             StatisticsHelper::getErrorBars( xDataSeries, /* bYError = */ true ), uno::UNO_QUERY );
980 
981         // positive y error bars
982         xErrorLSequence.set(
983             StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
984                 xErrorSource,
985                 /* bPositiveValue = */ true,
986                 /* bYError = */ true ));
987         if( xErrorLSequence.is())
988             aSequences.push_back( xErrorLSequence );
989 
990         // negative y error bars
991         xErrorLSequence.set(
992             StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
993                 xErrorSource,
994                 /* bPositiveValue = */ false,
995                 /* bYError = */ true ));
996         if( xErrorLSequence.is())
997             aSequences.push_back( xErrorLSequence );
998 
999 
1000         for( ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::const_iterator aIt( aSequences.begin());
1001              aIt != aSequences.end(); ++aIt )
1002         {
1003             m_aColumns.push_back(
1004                 tDataColumn(
1005                     xDataSeries,
1006                     rInOutSequenceIndex,
1007                     lcl_getUIRoleName( *aIt ),
1008                     *aIt,
1009                     NUMBER,
1010                     nNumberFormatKey ));
1011             ++rInOutSequenceIndex;
1012             ++rInOutHeaderEnd;
1013         }
1014     }
1015     catch( const uno::Exception & ex )
1016     {
1017         ASSERT_EXCEPTION( ex );
1018     }
1019 }
1020 
1021 } //  namespace chart
1022