Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / chart2 / source / controller / dialogs / DataBrowserModel.cxx
blobdcd67beb533000ef46bd27bc7934f320dd66cc5d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "DataBrowserModel.hxx"
21 #include "DialogModel.hxx"
22 #include <ChartModelHelper.hxx>
23 #include <DiagramHelper.hxx>
24 #include <DataSeriesHelper.hxx>
25 #include <PropertyHelper.hxx>
26 #include <ControllerLockGuard.hxx>
27 #include <StatisticsHelper.hxx>
28 #include <ChartTypeHelper.hxx>
29 #include <chartview/ExplicitValueProvider.hxx>
30 #include <ExplicitCategoriesProvider.hxx>
32 #include <ChartModel.hxx>
33 #include <unonames.hxx>
35 #include <com/sun/star/container/XIndexReplace.hpp>
36 #include <com/sun/star/chart2/XAxis.hpp>
37 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
38 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
39 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
40 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
41 #include <com/sun/star/chart2/data/XDataSource.hpp>
42 #include <com/sun/star/chart2/data/XDataSink.hpp>
43 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
44 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
45 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
46 #include <com/sun/star/util/XModifiable.hpp>
47 #include <tools/diagnose_ex.h>
49 #include <rtl/math.hxx>
51 #include <algorithm>
53 using namespace ::com::sun::star;
55 using ::com::sun::star::uno::Reference;
56 using ::com::sun::star::uno::Sequence;
58 namespace chart {
60 namespace {
62 OUString lcl_getRole(
63 const Reference< chart2::data::XDataSequence > & xSeq )
65 OUString aResult;
66 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
67 if( xProp.is())
69 try
71 xProp->getPropertyValue( "Role" ) >>= aResult;
73 catch( const uno::Exception & )
75 DBG_UNHANDLED_EXCEPTION("chart2");
78 return aResult;
81 OUString lcl_getUIRoleName(
82 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
84 OUString aResult = DataSeriesHelper::getRole(xLSeq);
85 if( !aResult.isEmpty())
86 aResult = DialogModel::ConvertRoleFromInternalToUI(aResult);
87 return aResult;
90 void lcl_copyDataSequenceProperties(
91 const Reference< chart2::data::XDataSequence > & xOldSequence,
92 const Reference< chart2::data::XDataSequence > & xNewSequence )
94 Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY );
95 Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY );
96 comphelper::copyProperties( xOldSeqProp, xNewSeqProp );
99 bool lcl_SequenceOfSeriesIsShared(
100 const Reference< chart2::XDataSeries > & xSeries,
101 const Reference< chart2::data::XDataSequence > & xValues )
103 bool bResult = false;
104 if( !xValues.is())
105 return bResult;
108 OUString aValuesRole( lcl_getRole( xValues ));
109 OUString aValuesRep( xValues->getSourceRangeRepresentation());
110 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY_THROW );
111 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
112 for( sal_Int32 i=0; i<aLSeq.getLength(); ++i )
113 if (aLSeq[i].is() && DataSeriesHelper::getRole(aLSeq[i]) == aValuesRole)
115 // getValues().is(), because lcl_getRole checked that already
116 bResult = (aValuesRep == aLSeq[i]->getValues()->getSourceRangeRepresentation());
117 // assumption: a role appears only once in a series
118 break;
121 catch( const uno::Exception & )
123 DBG_UNHANDLED_EXCEPTION("chart2");
125 return bResult;
128 typedef std::vector< Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec;
130 lcl_tSharedSeqVec lcl_getSharedSequences( const Sequence< Reference< chart2::XDataSeries > > & rSeries )
132 // @todo: if only some series share a sequence, those have to be duplicated
133 // and made unshared for all series
134 lcl_tSharedSeqVec aResult;
135 // if we have only one series, we don't want any shared sequences
136 if( rSeries.getLength() <= 1 )
137 return aResult;
139 Reference< chart2::data::XDataSource > xSource( rSeries[0], uno::UNO_QUERY );
140 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
141 for( sal_Int32 nIdx=0; nIdx<aLSeq.getLength(); ++nIdx )
143 Reference< chart2::data::XDataSequence > xValues( aLSeq[nIdx]->getValues());
144 bool bShared = true;
145 for( sal_Int32 nSeriesIdx=1; nSeriesIdx<rSeries.getLength(); ++nSeriesIdx )
147 bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues );
148 if( !bShared )
149 break;
151 if( bShared )
152 aResult.push_back( aLSeq[nIdx] );
155 return aResult;
158 sal_Int32 lcl_getValuesRepresentationIndex(
159 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
161 sal_Int32 nResult = -1;
162 if( xLSeq.is())
164 Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues());
165 if( xSeq.is())
167 OUString aRep( xSeq->getSourceRangeRepresentation());
168 nResult = aRep.toInt32();
171 return nResult;
174 struct lcl_RepresentationsOfLSeqMatch
176 explicit lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
177 m_aValuesRep( xLSeq.is() ?
178 (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString())
179 : OUString() )
181 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
183 if (!xLSeq.is() || !xLSeq->getValues().is())
184 return false;
186 return xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep;
188 private:
189 OUString m_aValuesRep;
192 struct lcl_RolesOfLSeqMatch
194 explicit lcl_RolesOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
195 m_aRole(DataSeriesHelper::getRole(xLSeq)) {}
197 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
199 return DataSeriesHelper::getRole(xLSeq) == m_aRole;
201 private:
202 OUString m_aRole;
205 bool lcl_ShowCategoriesAsDataLabel( const Reference< chart2::XDiagram > & xDiagram )
207 return !DiagramHelper::isCategoryDiagram(xDiagram);
210 } // anonymous namespace
212 struct DataBrowserModel::tDataColumn
214 uno::Reference<chart2::XDataSeries> m_xDataSeries;
215 OUString m_aUIRoleName;
216 uno::Reference<chart2::data::XLabeledDataSequence> m_xLabeledDataSequence;
217 eCellType m_eCellType;
218 sal_Int32 m_nNumberFormatKey;
220 // default CTOR
221 tDataColumn() : m_eCellType( TEXT ), m_nNumberFormatKey( 0 ) {}
222 // "full" CTOR
223 tDataColumn(
224 const uno::Reference<chart2::XDataSeries> & xDataSeries,
225 const OUString& aUIRoleName,
226 const uno::Reference<chart2::data::XLabeledDataSequence>& xLabeledDataSequence,
227 eCellType aCellType,
228 sal_Int32 nNumberFormatKey ) :
229 m_xDataSeries( xDataSeries ),
230 m_aUIRoleName( aUIRoleName ),
231 m_xLabeledDataSequence( xLabeledDataSequence ),
232 m_eCellType( aCellType ),
233 m_nNumberFormatKey( nNumberFormatKey )
237 struct DataBrowserModel::implColumnLess
239 bool operator() ( const DataBrowserModel::tDataColumn & rLeft, const DataBrowserModel::tDataColumn & rRight )
241 if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is())
243 return DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rLeft.m_xLabeledDataSequence)) <
244 DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rRight.m_xLabeledDataSequence));
246 return true;
250 DataBrowserModel::DataBrowserModel(
251 const Reference< chart2::XChartDocument > & xChartDoc,
252 const Reference< uno::XComponentContext > & xContext ) :
253 m_xChartDocument( xChartDoc ),
254 m_apDialogModel( new DialogModel( xChartDoc, xContext ))
256 updateFromModel();
259 DataBrowserModel::~DataBrowserModel()
262 namespace
264 struct lcl_DataSeriesOfHeaderMatches
266 explicit lcl_DataSeriesOfHeaderMatches(
267 const Reference< chart2::XDataSeries > & xSeriesToCompareWith ) :
268 m_xSeries( xSeriesToCompareWith )
270 bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader )
272 return (m_xSeries == rHeader.m_xDataSeries);
274 private:
275 Reference< chart2::XDataSeries > m_xSeries;
279 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex )
281 OSL_ASSERT( m_apDialogModel.get());
282 Reference< chart2::XInternalDataProvider > xDataProvider(
283 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
285 if (!xDataProvider.is())
286 return;
288 if( isCategoriesColumn(nAfterColumnIndex) )
289 // Move to the last category column.
290 nAfterColumnIndex = getCategoryColumnCount()-1;
292 sal_Int32 nStartCol = 0;
293 Reference<chart2::XDiagram> xDiagram = ChartModelHelper::findDiagram(m_xChartDocument);
294 Reference<chart2::XChartType> xChartType;
295 Reference<chart2::XDataSeries> xSeries;
296 if (static_cast<size_t>(nAfterColumnIndex) < m_aColumns.size())
297 // Get the data series at specific column position (if available).
298 xSeries.set( m_aColumns[nAfterColumnIndex].m_xDataSeries );
300 sal_Int32 nSeriesNumberFormat = 0;
301 if( xSeries.is())
303 // Use the chart type of the currently selected data series.
304 xChartType.set( DiagramHelper::getChartTypeOfSeries( xDiagram, xSeries ));
306 // Find the corresponding header and determine the last column of this
307 // data series.
308 tDataHeaderVector::const_iterator aIt(
309 std::find_if( m_aHeaders.begin(), m_aHeaders.end(),
310 lcl_DataSeriesOfHeaderMatches( xSeries )));
311 if( aIt != m_aHeaders.end())
312 nStartCol = aIt->m_nEndColumn;
314 // Get the number format too.
315 Reference< beans::XPropertySet > xSeriesProps( xSeries, uno::UNO_QUERY );
316 if( xSeriesProps.is() )
317 xSeriesProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nSeriesNumberFormat;
319 else
321 // No data series at specified column position. Use the first chart type.
322 xChartType.set( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ));
323 nStartCol = nAfterColumnIndex;
326 if (!xChartType.is())
327 return;
329 // Get shared sequences of current series. Normally multiple data series
330 // only share "values-x" sequences. (TODO: simplify this logic).
331 Reference< chart2::XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY );
332 lcl_tSharedSeqVec aSharedSequences;
333 if( xSeriesCnt.is())
334 aSharedSequences = lcl_getSharedSequences( xSeriesCnt->getDataSeries());
336 Reference<chart2::XDataSeries> xNewSeries =
337 m_apDialogModel->insertSeriesAfter(xSeries, xChartType, true);
339 if (!xNewSeries.is())
340 // Failed to insert new data series to the model. Bail out.
341 return;
343 Reference< chart2::data::XDataSource > xSource( xNewSeries, uno::UNO_QUERY );
344 if (xSource.is())
346 Sequence<Reference<chart2::data::XLabeledDataSequence> > aLSequences = xSource->getDataSequences();
347 sal_Int32 nSeqIdx = 0;
348 sal_Int32 nSeqSize = aLSequences.getLength();
349 for (sal_Int32 nIndex = nStartCol; nSeqIdx < nSeqSize; ++nSeqIdx)
351 lcl_tSharedSeqVec::const_iterator aSharedIt(
352 std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
353 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] )));
355 if( aSharedIt != aSharedSequences.end())
357 // Shared sequence. Most likely "values-x" sequence. Copy it from existing sequence.
358 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues());
359 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel());
361 else
363 // Insert a new column in the internal data for the new sequence.
364 xDataProvider->insertSequence( nIndex - 1 );
366 // values
367 Reference< chart2::data::XDataSequence > xNewSeq(
368 xDataProvider->createDataSequenceByRangeRepresentation(
369 OUString::number( nIndex )));
370 lcl_copyDataSequenceProperties(
371 aLSequences[nSeqIdx]->getValues(), xNewSeq );
372 aLSequences[nSeqIdx]->setValues( xNewSeq );
374 // labels
375 Reference< chart2::data::XDataSequence > xNewLabelSeq(
376 xDataProvider->createDataSequenceByRangeRepresentation(
377 "label " +
378 OUString::number( nIndex )));
379 lcl_copyDataSequenceProperties(
380 aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq );
381 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq );
382 ++nIndex;
387 if( nSeriesNumberFormat != 0 )
389 //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
390 Reference< beans::XPropertySet > xNewSeriesProps( xNewSeries, uno::UNO_QUERY );
391 if( xNewSeriesProps.is() )
392 xNewSeriesProps->setPropertyValue(CHART_UNONAME_NUMFMT , uno::Any(nSeriesNumberFormat));
395 updateFromModel();
398 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex )
400 //create a new text column for complex categories
402 OSL_ASSERT( m_apDialogModel.get());
403 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
404 if (!xDataProvider.is())
405 return;
407 if( !isCategoriesColumn(nAfterColumnIndex) )
408 nAfterColumnIndex = getCategoryColumnCount()-1;
410 if(nAfterColumnIndex<0)
412 OSL_FAIL( "wrong index for category level insertion" );
413 return;
416 m_apDialogModel->startControllerLockTimer();
417 ControllerLockGuardUNO aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
418 xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 );
419 updateFromModel();
422 void DataBrowserModel::removeComplexCategoryLevel( sal_Int32 nAtColumnIndex )
424 //delete a category column if there is more than one level (in case of a single column we do not get here)
425 OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" );
427 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
428 if (!xDataProvider.is())
429 return;
431 m_apDialogModel->startControllerLockTimer();
432 ControllerLockGuardUNO aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
433 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex );
435 updateFromModel();
438 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex )
440 OSL_ASSERT( m_apDialogModel.get());
441 if (nAtColumnIndex < 0 || static_cast<size_t>(nAtColumnIndex) >= m_aColumns.size())
442 // Out of bound.
443 return;
445 if (isCategoriesColumn(nAtColumnIndex))
447 removeComplexCategoryLevel(nAtColumnIndex);
448 return;
451 const Reference<chart2::XDataSeries>& xSeries = m_aColumns[nAtColumnIndex].m_xDataSeries;
453 m_apDialogModel->deleteSeries(xSeries, getHeaderForSeries(xSeries).m_xChartType);
455 //delete sequences from internal data provider that are not used anymore
456 //but do not delete sequences that are still in use by the remaining series
458 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
459 Reference< chart2::data::XDataSource > xSourceOfDeleted( xSeries, uno::UNO_QUERY );
460 if (!xDataProvider.is() || !xSourceOfDeleted.is())
462 // Something went wrong. Bail out.
463 updateFromModel();
464 return;
467 Reference<chart2::XDataSeriesContainer> xSeriesCnt(
468 getHeaderForSeries(xSeries).m_xChartType, uno::UNO_QUERY);
469 if (!xSeriesCnt.is())
471 // Unexpected happened. Bail out.
472 updateFromModel();
473 return;
476 // Collect all the remaining data sequences in the same chart type. The
477 // deleted data series is already gone by this point.
478 std::vector<Reference<chart2::data::XLabeledDataSequence> > aAllDataSeqs =
479 DataSeriesHelper::getAllDataSequences(xSeriesCnt->getDataSeries());
481 // Check if the sequences to be deleted are still referenced by any of
482 // the other data series. If not, mark them for deletion.
483 std::vector<sal_Int32> aSequenceIndexesToDelete;
484 Sequence<Reference<chart2::data::XLabeledDataSequence> > aSequencesOfDeleted = xSourceOfDeleted->getDataSequences();
485 for (sal_Int32 i = 0; i < aSequencesOfDeleted.getLength(); ++i)
487 std::vector<Reference<chart2::data::XLabeledDataSequence> >::const_iterator aHitIt(
488 std::find_if( aAllDataSeqs.begin(), aAllDataSeqs.end(),
489 lcl_RepresentationsOfLSeqMatch( aSequencesOfDeleted[i] )));
490 // if not used by the remaining series this sequence can be deleted
491 if( aHitIt == aAllDataSeqs.end() )
492 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( aSequencesOfDeleted[i] ) );
495 // delete unnecessary sequences of the internal data
496 // iterate using greatest index first, so that deletion does not
497 // shift other sequences that will be deleted later
498 std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
499 for( std::vector< sal_Int32 >::reverse_iterator aIt(
500 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
502 if( *aIt != -1 )
503 xDataProvider->deleteSequence( *aIt );
506 updateFromModel();
509 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex )
511 OSL_ASSERT( m_apDialogModel.get());
512 if( static_cast< tDataColumnVector::size_type >( nFirstColumnIndex ) < m_aColumns.size() - 1 )
514 Reference< chart2::XDataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries );
515 if( xSeries.is())
517 m_apDialogModel->moveSeries( xSeries, DialogModel::MOVE_DOWN );
518 updateFromModel();
523 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex )
525 OSL_ASSERT( m_apDialogModel.get());
526 Reference< chart2::XInternalDataProvider > xDataProvider(
527 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
528 // lockControllers
529 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
530 if( xDataProvider.is())
531 xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex );
532 // unlockControllers
535 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex )
537 Reference< chart2::XInternalDataProvider > xDataProvider(
538 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
539 // lockControllers
540 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
541 if( xDataProvider.is())
542 xDataProvider->insertDataPointForAllSequences( nAfterIndex );
543 // unlockControllers
546 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex )
548 Reference< chart2::XInternalDataProvider > xDataProvider(
549 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
550 // lockControllers
551 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
552 if( xDataProvider.is())
553 xDataProvider->deleteDataPointForAllSequences( nAtIndex );
554 // unlockControllers
557 DataBrowserModel::tDataHeader DataBrowserModel::getHeaderForSeries(
558 const Reference< chart2::XDataSeries > & xSeries ) const
560 for (auto const& elemHeader : m_aHeaders)
562 if( elemHeader.m_xDataSeries == xSeries )
563 return elemHeader;
565 return tDataHeader();
568 Reference< chart2::XDataSeries >
569 DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn ) const
571 tDataColumnVector::size_type nIndex( nColumn );
572 if( nIndex < m_aColumns.size())
573 return m_aColumns[nIndex].m_xDataSeries;
574 return nullptr;
577 DataBrowserModel::eCellType DataBrowserModel::getCellType( sal_Int32 nAtColumn ) const
579 eCellType eResult = TEXT;
580 tDataColumnVector::size_type nIndex( nAtColumn );
581 if( nIndex < m_aColumns.size())
582 eResult = m_aColumns[nIndex].m_eCellType;
583 return eResult;
586 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow )
588 double fResult;
589 ::rtl::math::setNan( & fResult );
591 tDataColumnVector::size_type nIndex( nAtColumn );
592 if( nIndex < m_aColumns.size() &&
593 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
595 Reference< chart2::data::XNumericalDataSequence > xData(
596 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
597 if( xData.is())
599 Sequence< double > aValues( xData->getNumericalData());
600 if( nAtRow < aValues.getLength())
601 fResult = aValues[nAtRow];
604 return fResult;
607 uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow )
609 uno::Any aResult;
611 tDataColumnVector::size_type nIndex( nAtColumn );
612 if( nIndex < m_aColumns.size() &&
613 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
615 Reference< chart2::data::XDataSequence > xData(
616 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() );
617 if( xData.is() )
619 Sequence< uno::Any > aValues( xData->getData());
620 if( nAtRow < aValues.getLength())
621 aResult = aValues[nAtRow];
624 return aResult;
627 OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow )
629 OUString aResult;
631 tDataColumnVector::size_type nIndex( nAtColumn );
632 if( nIndex < m_aColumns.size() &&
633 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
635 Reference< chart2::data::XTextualDataSequence > xData(
636 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
637 if( xData.is())
639 Sequence< OUString > aValues( xData->getTextualData());
640 if( nAtRow < aValues.getLength())
641 aResult = aValues[nAtRow];
644 return aResult;
647 sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn )
649 tDataColumnVector::size_type nIndex( nAtColumn );
650 if( nIndex < m_aColumns.size())
651 return m_aColumns[ nIndex ].m_nNumberFormatKey;
652 return 0;
655 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue )
657 bool bResult = false;
658 tDataColumnVector::size_type nIndex( nAtColumn );
659 if( nIndex < m_aColumns.size() &&
660 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
662 bResult = true;
665 ControllerLockGuardUNO aLockedControllers( Reference< frame::XModel >( m_xChartDocument, uno::UNO_QUERY ) );
667 // label
668 if( nAtRow == -1 )
670 Reference< container::XIndexReplace > xIndexReplace(
671 m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW );
672 xIndexReplace->replaceByIndex( 0, rValue );
674 else
676 Reference< container::XIndexReplace > xIndexReplace(
677 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW );
678 xIndexReplace->replaceByIndex( nAtRow, rValue );
681 m_apDialogModel->startControllerLockTimer();
682 //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#)
683 Reference< util::XModifiable > xModifiable( m_xChartDocument, uno::UNO_QUERY );
684 if( xModifiable.is() )
685 xModifiable->setModified(true);
687 catch( const uno::Exception & )
689 bResult = false;
692 return bResult;
695 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue )
697 return (getCellType( nAtColumn ) == NUMBER) &&
698 setCellAny( nAtColumn, nAtRow, uno::Any( fValue ));
701 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const OUString & rText )
703 return (getCellType( nAtColumn ) == TEXT) &&
704 setCellAny( nAtColumn, nAtRow, uno::Any( rText ));
707 sal_Int32 DataBrowserModel::getColumnCount() const
709 return static_cast< sal_Int32 >( m_aColumns.size());
712 sal_Int32 DataBrowserModel::getMaxRowCount() const
714 sal_Int32 nResult = 0;
715 for (auto const& column : m_aColumns)
717 if( column.m_xLabeledDataSequence.is())
719 Reference< chart2::data::XDataSequence > xSeq(
720 column.m_xLabeledDataSequence->getValues());
721 if( !xSeq.is())
722 continue;
723 sal_Int32 nLength( xSeq->getData().getLength());
724 if( nLength > nResult )
725 nResult = nLength;
729 return nResult;
732 OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const
734 if( nColumnIndex != -1 &&
735 static_cast< sal_uInt32 >( nColumnIndex ) < m_aColumns.size())
736 return m_aColumns[ nColumnIndex ].m_aUIRoleName;
737 return OUString();
740 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const
742 if (nColumnIndex < 0)
743 return false;
745 if (static_cast<size_t>(nColumnIndex) >= m_aColumns.size())
746 return false;
748 // A column is a category when it doesn't have an associated data series.
749 return !m_aColumns[nColumnIndex].m_xDataSeries.is();
752 sal_Int32 DataBrowserModel::getCategoryColumnCount()
754 sal_Int32 nLastTextColumnIndex = -1;
755 for (auto const& column : m_aColumns)
757 if( !column.m_xDataSeries.is() )
758 nLastTextColumnIndex++;
759 else
760 break;
762 return nLastTextColumnIndex+1;
765 void DataBrowserModel::updateFromModel()
767 if( !m_xChartDocument.is())
768 return;
769 m_aColumns.clear();
770 m_aHeaders.clear();
772 Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument ));
773 if( !xDiagram.is())
774 return;
776 // set template at DialogModel
777 uno::Reference< lang::XMultiServiceFactory > xFact( m_xChartDocument->getChartTypeManager(), uno::UNO_QUERY );
778 DiagramHelper::tTemplateWithServiceName aTemplateAndService =
779 DiagramHelper::getTemplateForDiagram( xDiagram, xFact );
780 if( aTemplateAndService.first.is())
781 m_apDialogModel->setTemplate( aTemplateAndService.first );
783 sal_Int32 nHeaderStart = 0;
784 sal_Int32 nHeaderEnd = 0;
786 Reference< frame::XModel > xChartModel( m_xChartDocument, uno::UNO_QUERY );
787 ChartModel* pModel = dynamic_cast<ChartModel*>(xChartModel.get());
788 if (!pModel)
789 return;
790 ExplicitCategoriesProvider aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(xChartModel), *pModel );
792 const Sequence< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() );
793 sal_Int32 nLevelCount = rSplitCategoriesList.getLength();
794 for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
796 Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] );
797 if( !xCategories.is() )
798 continue;
800 tDataColumn aCategories;
801 aCategories.m_xLabeledDataSequence.set( xCategories );
802 if( lcl_ShowCategoriesAsDataLabel( xDiagram ))
803 aCategories.m_aUIRoleName = DialogModel::GetRoleDataLabel();
804 else
805 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories );
806 aCategories.m_eCellType = TEXTORDATE;
807 m_aColumns.push_back( aCategories );
808 ++nHeaderStart;
812 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
813 if( !xCooSysCnt.is())
814 return;
815 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
816 for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
818 Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
819 Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes());
820 sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( nullptr, aCooSysSeq[nCooSysIdx], 0, 0 );
822 for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypes.getLength(); ++nCTIdx )
824 Reference< chart2::XDataSeriesContainer > xSeriesCnt( aChartTypes[nCTIdx], uno::UNO_QUERY );
825 if( xSeriesCnt.is())
827 OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( aChartTypes[nCTIdx] );
829 Sequence< Reference< chart2::XDataSeries > > aSeries( xSeriesCnt->getDataSeries());
830 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries ));
831 for (auto const& sharedSequence : aSharedSequences)
833 tDataColumn aSharedSequence;
834 aSharedSequence.m_xLabeledDataSequence = sharedSequence;
835 aSharedSequence.m_aUIRoleName = lcl_getUIRoleName(sharedSequence);
836 aSharedSequence.m_eCellType = NUMBER;
837 // as the sequences are shared it should be ok to take the first series
838 // @todo: dimension index 0 for x-values used here. This is just a guess.
839 // Also, the axis index is 0, as there is usually only one x-axis
840 aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat;
841 m_aColumns.push_back( aSharedSequence );
842 ++nHeaderStart;
844 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx )
846 tDataColumnVector::size_type nStartColIndex = m_aColumns.size();
847 Reference< chart2::XDataSeries > xSeries( aSeries[nSeriesIdx] );
848 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
849 if( xSource.is())
851 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqs( xSource->getDataSequences());
852 if( aLSeqs.getLength() == 0 )
853 continue;
854 nHeaderEnd = nHeaderStart;
856 // @todo: dimension index 1 for y-values used here. This is just a guess
857 sal_Int32 nYAxisNumberFormatKey =
858 DataSeriesHelper::getNumberFormatKeyFromAxis(
859 aSeries[nSeriesIdx], aCooSysSeq[nCooSysIdx], 1 );
861 sal_Int32 nSeqIdx=0;
862 for( ; nSeqIdx<aLSeqs.getLength(); ++nSeqIdx )
864 sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey;
865 OUString aRole = DataSeriesHelper::getRole(aLSeqs[nSeqIdx]);
867 if( aRole == aRoleForDataLabelNumberFormat )
869 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
870 Reference< beans::XPropertySet >( xSeries, uno::UNO_QUERY ), xSeries, -1, xDiagram );
872 else if( aRole == "values-x" )
873 nSequenceNumberFormatKey = nXAxisNumberFormat;
875 if( std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
876 lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) == aSharedSequences.end())
878 // no shared sequence
879 m_aColumns.emplace_back(
880 aSeries[nSeriesIdx],
881 lcl_getUIRoleName( aLSeqs[nSeqIdx] ),
882 aLSeqs[nSeqIdx],
883 NUMBER,
884 nSequenceNumberFormatKey );
885 ++nHeaderEnd;
887 // else skip
889 bool bSwapXAndYAxis = false;
892 Reference< beans::XPropertySet > xProp( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY );
893 xProp->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndYAxis;
895 catch( const beans::UnknownPropertyException & ) {}
897 // add ranges for error bars if present for a series
898 if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx] ))
899 addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, true );
901 if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx], /* bYError = */ false ))
902 addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, false );
904 m_aHeaders.emplace_back(
905 aSeries[nSeriesIdx],
906 aChartTypes[nCTIdx],
907 bSwapXAndYAxis,
908 nHeaderStart,
909 nHeaderEnd - 1 );
911 nHeaderStart = nHeaderEnd;
913 std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() );
921 void DataBrowserModel::addErrorBarRanges(
922 const Reference< chart2::XDataSeries > & xDataSeries,
923 sal_Int32 nNumberFormatKey,
924 sal_Int32 & rInOutSequenceIndex,
925 sal_Int32 & rInOutHeaderEnd, bool bYError )
929 std::vector< Reference< chart2::data::XLabeledDataSequence > > aSequences;
931 Reference< chart2::data::XDataSource > xErrorSource(
932 StatisticsHelper::getErrorBars( xDataSeries, bYError ), uno::UNO_QUERY );
934 Reference< chart2::data::XLabeledDataSequence > xErrorLSequence(
935 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
936 xErrorSource,
937 /* bPositiveValue = */ true,
938 bYError ));
939 if( xErrorLSequence.is())
940 aSequences.push_back( xErrorLSequence );
942 xErrorLSequence.set(
943 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
944 xErrorSource,
945 /* bPositiveValue = */ false,
946 bYError ));
947 if( xErrorLSequence.is())
948 aSequences.push_back( xErrorLSequence );
950 for (Reference<chart2::data::XLabeledDataSequence> const & rDataSequence : aSequences)
952 m_aColumns.emplace_back(xDataSeries, lcl_getUIRoleName(rDataSequence),
953 rDataSequence, NUMBER, nNumberFormatKey);
954 ++rInOutSequenceIndex;
955 ++rInOutHeaderEnd;
958 catch( const uno::Exception & )
960 DBG_UNHANDLED_EXCEPTION("chart2");
964 } // namespace chart
966 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */