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