Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / chart2 / source / controller / dialogs / DataBrowserModel.cxx
blob272693f1d7c6471ea4c298a914c45d8a926f0bc1
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 <ControllerLockGuard.hxx>
26 #include <StatisticsHelper.hxx>
27 #include <ChartTypeHelper.hxx>
28 #include <chartview/ExplicitValueProvider.hxx>
29 #include <ExplicitCategoriesProvider.hxx>
31 #include <ChartModel.hxx>
32 #include <unonames.hxx>
34 #include <com/sun/star/container/XIndexReplace.hpp>
35 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
36 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
37 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
38 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
39 #include <com/sun/star/chart2/data/XDataSource.hpp>
40 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
41 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
42 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
43 #include <com/sun/star/util/XModifiable.hpp>
44 #include <tools/diagnose_ex.h>
45 #include <comphelper/property.hxx>
47 #include <rtl/math.hxx>
49 #include <algorithm>
51 using namespace ::com::sun::star;
53 using ::com::sun::star::uno::Reference;
54 using ::com::sun::star::uno::Sequence;
56 namespace chart {
58 namespace {
60 OUString lcl_getRole(
61 const Reference< chart2::data::XDataSequence > & xSeq )
63 OUString aResult;
64 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
65 if( xProp.is())
67 try
69 xProp->getPropertyValue( "Role" ) >>= aResult;
71 catch( const uno::Exception & )
73 DBG_UNHANDLED_EXCEPTION("chart2");
76 return aResult;
79 OUString lcl_getUIRoleName(
80 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
82 OUString aResult = DataSeriesHelper::getRole(xLSeq);
83 if( !aResult.isEmpty())
84 aResult = DialogModel::ConvertRoleFromInternalToUI(aResult);
85 return aResult;
88 void lcl_copyDataSequenceProperties(
89 const Reference< chart2::data::XDataSequence > & xOldSequence,
90 const Reference< chart2::data::XDataSequence > & xNewSequence )
92 Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY );
93 Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY );
94 comphelper::copyProperties( xOldSeqProp, xNewSeqProp );
97 bool lcl_SequenceOfSeriesIsShared(
98 const Reference< chart2::XDataSeries > & xSeries,
99 const Reference< chart2::data::XDataSequence > & xValues )
101 bool bResult = false;
102 if( !xValues.is())
103 return bResult;
106 OUString aValuesRole( lcl_getRole( xValues ));
107 OUString aValuesRep( xValues->getSourceRangeRepresentation());
108 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY_THROW );
109 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
110 for( sal_Int32 i=0; i<aLSeq.getLength(); ++i )
111 if (aLSeq[i].is() && DataSeriesHelper::getRole(aLSeq[i]) == aValuesRole)
113 // getValues().is(), because lcl_getRole checked that already
114 bResult = (aValuesRep == aLSeq[i]->getValues()->getSourceRangeRepresentation());
115 // assumption: a role appears only once in a series
116 break;
119 catch( const uno::Exception & )
121 DBG_UNHANDLED_EXCEPTION("chart2");
123 return bResult;
126 typedef std::vector< Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec;
128 lcl_tSharedSeqVec lcl_getSharedSequences( const Sequence< Reference< chart2::XDataSeries > > & rSeries )
130 // @todo: if only some series share a sequence, those have to be duplicated
131 // and made unshared for all series
132 lcl_tSharedSeqVec aResult;
133 // if we have only one series, we don't want any shared sequences
134 if( rSeries.getLength() <= 1 )
135 return aResult;
137 Reference< chart2::data::XDataSource > xSource( rSeries[0], uno::UNO_QUERY );
138 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeq( xSource->getDataSequences());
139 for( sal_Int32 nIdx=0; nIdx<aLSeq.getLength(); ++nIdx )
141 Reference< chart2::data::XDataSequence > xValues( aLSeq[nIdx]->getValues());
142 bool bShared = true;
143 for( sal_Int32 nSeriesIdx=1; nSeriesIdx<rSeries.getLength(); ++nSeriesIdx )
145 bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues );
146 if( !bShared )
147 break;
149 if( bShared )
150 aResult.push_back( aLSeq[nIdx] );
153 return aResult;
156 sal_Int32 lcl_getValuesRepresentationIndex(
157 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
159 sal_Int32 nResult = -1;
160 if( xLSeq.is())
162 Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues());
163 if( xSeq.is())
165 OUString aRep( xSeq->getSourceRangeRepresentation());
166 nResult = aRep.toInt32();
169 return nResult;
172 struct lcl_RepresentationsOfLSeqMatch
174 explicit lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
175 m_aValuesRep( xLSeq.is() ?
176 (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString())
177 : OUString() )
179 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
181 if (!xLSeq.is() || !xLSeq->getValues().is())
182 return false;
184 return xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep;
186 private:
187 OUString m_aValuesRep;
190 struct lcl_RolesOfLSeqMatch
192 explicit lcl_RolesOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
193 m_aRole(DataSeriesHelper::getRole(xLSeq)) {}
195 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
197 return DataSeriesHelper::getRole(xLSeq) == m_aRole;
199 private:
200 OUString m_aRole;
203 bool lcl_ShowCategoriesAsDataLabel( const Reference< chart2::XDiagram > & xDiagram )
205 return !DiagramHelper::isCategoryDiagram(xDiagram);
208 } // anonymous namespace
210 struct DataBrowserModel::tDataColumn
212 uno::Reference<chart2::XDataSeries> m_xDataSeries;
213 OUString m_aUIRoleName;
214 uno::Reference<chart2::data::XLabeledDataSequence> m_xLabeledDataSequence;
215 eCellType m_eCellType;
216 sal_Int32 m_nNumberFormatKey;
218 // default CTOR
219 tDataColumn() : m_eCellType( TEXT ), m_nNumberFormatKey( 0 ) {}
220 // "full" CTOR
221 tDataColumn(
222 const uno::Reference<chart2::XDataSeries> & xDataSeries,
223 const OUString& aUIRoleName,
224 const uno::Reference<chart2::data::XLabeledDataSequence>& xLabeledDataSequence,
225 eCellType aCellType,
226 sal_Int32 nNumberFormatKey ) :
227 m_xDataSeries( xDataSeries ),
228 m_aUIRoleName( aUIRoleName ),
229 m_xLabeledDataSequence( xLabeledDataSequence ),
230 m_eCellType( aCellType ),
231 m_nNumberFormatKey( nNumberFormatKey )
235 struct DataBrowserModel::implColumnLess
237 bool operator() ( const DataBrowserModel::tDataColumn & rLeft, const DataBrowserModel::tDataColumn & rRight )
239 if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is())
241 return DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rLeft.m_xLabeledDataSequence)) <
242 DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rRight.m_xLabeledDataSequence));
244 return true;
248 DataBrowserModel::DataBrowserModel(
249 const Reference< chart2::XChartDocument > & xChartDoc,
250 const Reference< uno::XComponentContext > & xContext ) :
251 m_xChartDocument( xChartDoc ),
252 m_apDialogModel( new DialogModel( xChartDoc, xContext ))
254 updateFromModel();
257 DataBrowserModel::~DataBrowserModel()
260 namespace
262 struct lcl_DataSeriesOfHeaderMatches
264 explicit lcl_DataSeriesOfHeaderMatches(
265 const Reference< chart2::XDataSeries > & xSeriesToCompareWith ) :
266 m_xSeries( xSeriesToCompareWith )
268 bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader )
270 return (m_xSeries == rHeader.m_xDataSeries);
272 private:
273 Reference< chart2::XDataSeries > m_xSeries;
277 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex )
279 OSL_ASSERT(m_apDialogModel);
280 Reference< chart2::XInternalDataProvider > xDataProvider(
281 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
283 if (!xDataProvider.is())
284 return;
286 if( isCategoriesColumn(nAfterColumnIndex) )
287 // Move to the last category column.
288 nAfterColumnIndex = getCategoryColumnCount()-1;
290 sal_Int32 nStartCol = 0;
291 Reference<chart2::XDiagram> xDiagram = ChartModelHelper::findDiagram(m_xChartDocument);
292 Reference<chart2::XChartType> xChartType;
293 Reference<chart2::XDataSeries> xSeries;
294 if (static_cast<size_t>(nAfterColumnIndex) < m_aColumns.size())
295 // Get the data series at specific column position (if available).
296 xSeries.set( m_aColumns[nAfterColumnIndex].m_xDataSeries );
298 sal_Int32 nSeriesNumberFormat = 0;
299 if( xSeries.is())
301 // Use the chart type of the currently selected data series.
302 xChartType.set( DiagramHelper::getChartTypeOfSeries( xDiagram, xSeries ));
304 // Find the corresponding header and determine the last column of this
305 // data series.
306 tDataHeaderVector::const_iterator aIt(
307 std::find_if( m_aHeaders.begin(), m_aHeaders.end(),
308 lcl_DataSeriesOfHeaderMatches( xSeries )));
309 if( aIt != m_aHeaders.end())
310 nStartCol = aIt->m_nEndColumn;
312 // Get the number format too.
313 Reference< beans::XPropertySet > xSeriesProps( xSeries, uno::UNO_QUERY );
314 if( xSeriesProps.is() )
315 xSeriesProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nSeriesNumberFormat;
317 else
319 // No data series at specified column position. Use the first chart type.
320 xChartType.set( DiagramHelper::getChartTypeByIndex( xDiagram, 0 ));
321 nStartCol = nAfterColumnIndex;
324 if (!xChartType.is())
325 return;
327 // Get shared sequences of current series. Normally multiple data series
328 // only share "values-x" sequences. (TODO: simplify this logic).
329 Reference< chart2::XDataSeriesContainer > xSeriesCnt( xChartType, uno::UNO_QUERY );
330 lcl_tSharedSeqVec aSharedSequences;
331 if( xSeriesCnt.is())
332 aSharedSequences = lcl_getSharedSequences( xSeriesCnt->getDataSeries());
334 Reference<chart2::XDataSeries> xNewSeries =
335 m_apDialogModel->insertSeriesAfter(xSeries, xChartType, true);
337 if (!xNewSeries.is())
338 // Failed to insert new data series to the model. Bail out.
339 return;
341 Reference< chart2::data::XDataSource > xSource( xNewSeries, uno::UNO_QUERY );
342 if (xSource.is())
344 Sequence<Reference<chart2::data::XLabeledDataSequence> > aLSequences = xSource->getDataSequences();
345 sal_Int32 nSeqIdx = 0;
346 sal_Int32 nSeqSize = aLSequences.getLength();
347 for (sal_Int32 nIndex = nStartCol; nSeqIdx < nSeqSize; ++nSeqIdx)
349 lcl_tSharedSeqVec::const_iterator aSharedIt(
350 std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
351 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] )));
353 if( aSharedIt != aSharedSequences.end())
355 // Shared sequence. Most likely "values-x" sequence. Copy it from existing sequence.
356 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues());
357 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel());
359 else
361 // Insert a new column in the internal data for the new sequence.
362 xDataProvider->insertSequence( nIndex - 1 );
364 // values
365 Reference< chart2::data::XDataSequence > xNewSeq(
366 xDataProvider->createDataSequenceByRangeRepresentation(
367 OUString::number( nIndex )));
368 lcl_copyDataSequenceProperties(
369 aLSequences[nSeqIdx]->getValues(), xNewSeq );
370 aLSequences[nSeqIdx]->setValues( xNewSeq );
372 // labels
373 Reference< chart2::data::XDataSequence > xNewLabelSeq(
374 xDataProvider->createDataSequenceByRangeRepresentation(
375 "label " +
376 OUString::number( nIndex )));
377 lcl_copyDataSequenceProperties(
378 aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq );
379 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq );
380 ++nIndex;
385 if( nSeriesNumberFormat != 0 )
387 //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
388 Reference< beans::XPropertySet > xNewSeriesProps( xNewSeries, uno::UNO_QUERY );
389 if( xNewSeriesProps.is() )
390 xNewSeriesProps->setPropertyValue(CHART_UNONAME_NUMFMT , uno::Any(nSeriesNumberFormat));
393 updateFromModel();
396 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex )
398 //create a new text column for complex categories
400 OSL_ASSERT(m_apDialogModel);
401 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
402 if (!xDataProvider.is())
403 return;
405 if( !isCategoriesColumn(nAfterColumnIndex) )
406 nAfterColumnIndex = getCategoryColumnCount()-1;
408 if(nAfterColumnIndex<0)
410 OSL_FAIL( "wrong index for category level insertion" );
411 return;
414 m_apDialogModel->startControllerLockTimer();
415 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
416 xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 );
417 updateFromModel();
420 void DataBrowserModel::removeComplexCategoryLevel( sal_Int32 nAtColumnIndex )
422 //delete a category column if there is more than one level (in case of a single column we do not get here)
423 OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" );
425 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
426 if (!xDataProvider.is())
427 return;
429 m_apDialogModel->startControllerLockTimer();
430 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
431 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex );
433 updateFromModel();
436 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex )
438 OSL_ASSERT(m_apDialogModel);
439 if (nAtColumnIndex < 0 || static_cast<size_t>(nAtColumnIndex) >= m_aColumns.size())
440 // Out of bound.
441 return;
443 if (isCategoriesColumn(nAtColumnIndex))
445 removeComplexCategoryLevel(nAtColumnIndex);
446 return;
449 const Reference<chart2::XDataSeries>& xSeries = m_aColumns[nAtColumnIndex].m_xDataSeries;
451 m_apDialogModel->deleteSeries(xSeries, getHeaderForSeries(xSeries).m_xChartType);
453 //delete sequences from internal data provider that are not used anymore
454 //but do not delete sequences that are still in use by the remaining series
456 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
457 Reference< chart2::data::XDataSource > xSourceOfDeleted( xSeries, uno::UNO_QUERY );
458 if (!xDataProvider.is() || !xSourceOfDeleted.is())
460 // Something went wrong. Bail out.
461 updateFromModel();
462 return;
465 Reference<chart2::XDataSeriesContainer> xSeriesCnt(
466 getHeaderForSeries(xSeries).m_xChartType, uno::UNO_QUERY);
467 if (!xSeriesCnt.is())
469 // Unexpected happened. Bail out.
470 updateFromModel();
471 return;
474 // Collect all the remaining data sequences in the same chart type. The
475 // deleted data series is already gone by this point.
476 std::vector<Reference<chart2::data::XLabeledDataSequence> > aAllDataSeqs =
477 DataSeriesHelper::getAllDataSequences(xSeriesCnt->getDataSeries());
479 // Check if the sequences to be deleted are still referenced by any of
480 // the other data series. If not, mark them for deletion.
481 std::vector<sal_Int32> aSequenceIndexesToDelete;
482 Sequence<Reference<chart2::data::XLabeledDataSequence> > aSequencesOfDeleted = xSourceOfDeleted->getDataSequences();
483 for (sal_Int32 i = 0; i < aSequencesOfDeleted.getLength(); ++i)
485 // if not used by the remaining series this sequence can be deleted
486 if( std::none_of( aAllDataSeqs.begin(), aAllDataSeqs.end(),
487 lcl_RepresentationsOfLSeqMatch( aSequencesOfDeleted[i] )) )
488 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( aSequencesOfDeleted[i] ) );
491 // delete unnecessary sequences of the internal data
492 // iterate using greatest index first, so that deletion does not
493 // shift other sequences that will be deleted later
494 std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
495 for( std::vector< sal_Int32 >::reverse_iterator aIt(
496 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
498 if( *aIt != -1 )
499 xDataProvider->deleteSequence( *aIt );
502 updateFromModel();
505 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex )
507 OSL_ASSERT(m_apDialogModel);
508 if( static_cast< tDataColumnVector::size_type >( nFirstColumnIndex ) < m_aColumns.size() - 1 )
510 Reference< chart2::XDataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries );
511 if( xSeries.is())
513 m_apDialogModel->moveSeries( xSeries, DialogModel::MoveDirection::Down );
514 updateFromModel();
519 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex )
521 OSL_ASSERT(m_apDialogModel);
522 Reference< chart2::XInternalDataProvider > xDataProvider(
523 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
524 // lockControllers
525 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
526 if( xDataProvider.is())
527 xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex );
528 // unlockControllers
531 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex )
533 Reference< chart2::XInternalDataProvider > xDataProvider(
534 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
535 // lockControllers
536 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
537 if( xDataProvider.is())
538 xDataProvider->insertDataPointForAllSequences( nAfterIndex );
539 // unlockControllers
542 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex )
544 Reference< chart2::XInternalDataProvider > xDataProvider(
545 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
546 // lockControllers
547 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
548 if( xDataProvider.is())
549 xDataProvider->deleteDataPointForAllSequences( nAtIndex );
550 // unlockControllers
553 DataBrowserModel::tDataHeader DataBrowserModel::getHeaderForSeries(
554 const Reference< chart2::XDataSeries > & xSeries ) const
556 for (auto const& elemHeader : m_aHeaders)
558 if( elemHeader.m_xDataSeries == xSeries )
559 return elemHeader;
561 return tDataHeader();
564 Reference< chart2::XDataSeries >
565 DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn ) const
567 tDataColumnVector::size_type nIndex( nColumn );
568 if( nIndex < m_aColumns.size())
569 return m_aColumns[nIndex].m_xDataSeries;
570 return nullptr;
573 DataBrowserModel::eCellType DataBrowserModel::getCellType( sal_Int32 nAtColumn ) const
575 eCellType eResult = TEXT;
576 tDataColumnVector::size_type nIndex( nAtColumn );
577 if( nIndex < m_aColumns.size())
578 eResult = m_aColumns[nIndex].m_eCellType;
579 return eResult;
582 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow )
584 double fResult;
585 ::rtl::math::setNan( & fResult );
587 tDataColumnVector::size_type nIndex( nAtColumn );
588 if( nIndex < m_aColumns.size() &&
589 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
591 Reference< chart2::data::XNumericalDataSequence > xData(
592 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
593 if( xData.is())
595 Sequence< double > aValues( xData->getNumericalData());
596 if( nAtRow < aValues.getLength())
597 fResult = aValues[nAtRow];
600 return fResult;
603 uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow )
605 uno::Any aResult;
607 tDataColumnVector::size_type nIndex( nAtColumn );
608 if( nIndex < m_aColumns.size() &&
609 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
611 Reference< chart2::data::XDataSequence > xData(
612 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() );
613 if( xData.is() )
615 Sequence< uno::Any > aValues( xData->getData());
616 if( nAtRow < aValues.getLength())
617 aResult = aValues[nAtRow];
620 return aResult;
623 OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow )
625 OUString aResult;
627 tDataColumnVector::size_type nIndex( nAtColumn );
628 if( nIndex < m_aColumns.size() &&
629 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
631 Reference< chart2::data::XTextualDataSequence > xData(
632 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
633 if( xData.is())
635 Sequence< OUString > aValues( xData->getTextualData());
636 if( nAtRow < aValues.getLength())
637 aResult = aValues[nAtRow];
640 return aResult;
643 sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn )
645 tDataColumnVector::size_type nIndex( nAtColumn );
646 if( nIndex < m_aColumns.size())
647 return m_aColumns[ nIndex ].m_nNumberFormatKey;
648 return 0;
651 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue )
653 bool bResult = false;
654 tDataColumnVector::size_type nIndex( nAtColumn );
655 if( nIndex < m_aColumns.size() &&
656 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
658 bResult = true;
661 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
663 // label
664 if( nAtRow == -1 )
666 Reference< container::XIndexReplace > xIndexReplace(
667 m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW );
668 xIndexReplace->replaceByIndex( 0, rValue );
670 else
672 Reference< container::XIndexReplace > xIndexReplace(
673 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW );
674 xIndexReplace->replaceByIndex( nAtRow, rValue );
677 m_apDialogModel->startControllerLockTimer();
678 //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#)
679 Reference< util::XModifiable > xModifiable( m_xChartDocument, uno::UNO_QUERY );
680 if( xModifiable.is() )
681 xModifiable->setModified(true);
683 catch( const uno::Exception & )
685 bResult = false;
688 return bResult;
691 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue )
693 return (getCellType( nAtColumn ) == NUMBER) &&
694 setCellAny( nAtColumn, nAtRow, uno::Any( fValue ));
697 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const OUString & rText )
699 return (getCellType( nAtColumn ) == TEXT) &&
700 setCellAny( nAtColumn, nAtRow, uno::Any( rText ));
703 sal_Int32 DataBrowserModel::getColumnCount() const
705 return static_cast< sal_Int32 >( m_aColumns.size());
708 sal_Int32 DataBrowserModel::getMaxRowCount() const
710 sal_Int32 nResult = 0;
711 for (auto const& column : m_aColumns)
713 if( column.m_xLabeledDataSequence.is())
715 Reference< chart2::data::XDataSequence > xSeq(
716 column.m_xLabeledDataSequence->getValues());
717 if( !xSeq.is())
718 continue;
719 sal_Int32 nLength( xSeq->getData().getLength());
720 if( nLength > nResult )
721 nResult = nLength;
725 return nResult;
728 OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const
730 if( nColumnIndex != -1 &&
731 static_cast< sal_uInt32 >( nColumnIndex ) < m_aColumns.size())
732 return m_aColumns[ nColumnIndex ].m_aUIRoleName;
733 return OUString();
736 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const
738 if (nColumnIndex < 0)
739 return false;
741 if (static_cast<size_t>(nColumnIndex) >= m_aColumns.size())
742 return false;
744 // A column is a category when it doesn't have an associated data series.
745 return !m_aColumns[nColumnIndex].m_xDataSeries.is();
748 sal_Int32 DataBrowserModel::getCategoryColumnCount()
750 sal_Int32 nLastTextColumnIndex = -1;
751 for (auto const& column : m_aColumns)
753 if( !column.m_xDataSeries.is() )
754 nLastTextColumnIndex++;
755 else
756 break;
758 return nLastTextColumnIndex+1;
761 void DataBrowserModel::updateFromModel()
763 if( !m_xChartDocument.is())
764 return;
765 m_aColumns.clear();
766 m_aHeaders.clear();
768 Reference< chart2::XDiagram > xDiagram( ChartModelHelper::findDiagram( m_xChartDocument ));
769 if( !xDiagram.is())
770 return;
772 // set template at DialogModel
773 uno::Reference< lang::XMultiServiceFactory > xFact( m_xChartDocument->getChartTypeManager(), uno::UNO_QUERY );
774 DiagramHelper::tTemplateWithServiceName aTemplateAndService =
775 DiagramHelper::getTemplateForDiagram( xDiagram, xFact );
776 if( aTemplateAndService.first.is())
777 m_apDialogModel->setTemplate( aTemplateAndService.first );
779 sal_Int32 nHeaderStart = 0;
780 sal_Int32 nHeaderEnd = 0;
782 Reference< frame::XModel > xChartModel = m_xChartDocument;
783 ChartModel* pModel = dynamic_cast<ChartModel*>(xChartModel.get());
784 if (!pModel)
785 return;
786 ExplicitCategoriesProvider aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(xChartModel), *pModel );
788 const Sequence< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList( aExplicitCategoriesProvider.getSplitCategoriesList() );
789 sal_Int32 nLevelCount = rSplitCategoriesList.getLength();
790 for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
792 Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] );
793 if( !xCategories.is() )
794 continue;
796 tDataColumn aCategories;
797 aCategories.m_xLabeledDataSequence.set( xCategories );
798 if( lcl_ShowCategoriesAsDataLabel( xDiagram ))
799 aCategories.m_aUIRoleName = DialogModel::GetRoleDataLabel();
800 else
801 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories );
802 aCategories.m_eCellType = TEXTORDATE;
803 m_aColumns.push_back( aCategories );
804 ++nHeaderStart;
808 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY );
809 if( !xCooSysCnt.is())
810 return;
811 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
812 for( sal_Int32 nCooSysIdx=0; nCooSysIdx<aCooSysSeq.getLength(); ++nCooSysIdx )
814 Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY_THROW );
815 Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes());
816 sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( nullptr, aCooSysSeq[nCooSysIdx], 0, 0 );
818 for( sal_Int32 nCTIdx=0; nCTIdx<aChartTypes.getLength(); ++nCTIdx )
820 Reference< chart2::XDataSeriesContainer > xSeriesCnt( aChartTypes[nCTIdx], uno::UNO_QUERY );
821 if( xSeriesCnt.is())
823 OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( aChartTypes[nCTIdx] );
825 Sequence< Reference< chart2::XDataSeries > > aSeries( xSeriesCnt->getDataSeries());
826 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries ));
827 for (auto const& sharedSequence : aSharedSequences)
829 tDataColumn aSharedSequence;
830 aSharedSequence.m_xLabeledDataSequence = sharedSequence;
831 aSharedSequence.m_aUIRoleName = lcl_getUIRoleName(sharedSequence);
832 aSharedSequence.m_eCellType = NUMBER;
833 // as the sequences are shared it should be ok to take the first series
834 // @todo: dimension index 0 for x-values used here. This is just a guess.
835 // Also, the axis index is 0, as there is usually only one x-axis
836 aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat;
837 m_aColumns.push_back( aSharedSequence );
838 ++nHeaderStart;
840 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeries.getLength(); ++nSeriesIdx )
842 tDataColumnVector::size_type nStartColIndex = m_aColumns.size();
843 Reference< chart2::XDataSeries > xSeries( aSeries[nSeriesIdx] );
844 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
845 if( xSource.is())
847 Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSeqs( xSource->getDataSequences());
848 if( !aLSeqs.hasElements() )
849 continue;
850 nHeaderEnd = nHeaderStart;
852 // @todo: dimension index 1 for y-values used here. This is just a guess
853 sal_Int32 nYAxisNumberFormatKey =
854 DataSeriesHelper::getNumberFormatKeyFromAxis(
855 aSeries[nSeriesIdx], aCooSysSeq[nCooSysIdx], 1 );
857 sal_Int32 nSeqIdx=0;
858 for( ; nSeqIdx<aLSeqs.getLength(); ++nSeqIdx )
860 sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey;
861 OUString aRole = DataSeriesHelper::getRole(aLSeqs[nSeqIdx]);
863 if( aRole == aRoleForDataLabelNumberFormat )
865 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
866 Reference< beans::XPropertySet >( xSeries, uno::UNO_QUERY ), xSeries, -1, xDiagram );
868 else if( aRole == "values-x" )
869 nSequenceNumberFormatKey = nXAxisNumberFormat;
871 if( std::none_of( aSharedSequences.begin(), aSharedSequences.end(),
872 lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) )
874 // no shared sequence
875 m_aColumns.emplace_back(
876 aSeries[nSeriesIdx],
877 lcl_getUIRoleName( aLSeqs[nSeqIdx] ),
878 aLSeqs[nSeqIdx],
879 NUMBER,
880 nSequenceNumberFormatKey );
881 ++nHeaderEnd;
883 // else skip
885 bool bSwapXAndYAxis = false;
888 Reference< beans::XPropertySet > xProp( aCooSysSeq[nCooSysIdx], uno::UNO_QUERY );
889 xProp->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndYAxis;
891 catch( const beans::UnknownPropertyException & ) {}
893 // add ranges for error bars if present for a series
894 if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx] ))
895 addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, true );
897 if( StatisticsHelper::usesErrorBarRanges( aSeries[nSeriesIdx], /* bYError = */ false ))
898 addErrorBarRanges( aSeries[nSeriesIdx], nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, false );
900 m_aHeaders.emplace_back(
901 aSeries[nSeriesIdx],
902 aChartTypes[nCTIdx],
903 bSwapXAndYAxis,
904 nHeaderStart,
905 nHeaderEnd - 1 );
907 nHeaderStart = nHeaderEnd;
909 std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() );
917 void DataBrowserModel::addErrorBarRanges(
918 const Reference< chart2::XDataSeries > & xDataSeries,
919 sal_Int32 nNumberFormatKey,
920 sal_Int32 & rInOutSequenceIndex,
921 sal_Int32 & rInOutHeaderEnd, bool bYError )
925 std::vector< Reference< chart2::data::XLabeledDataSequence > > aSequences;
927 Reference< chart2::data::XDataSource > xErrorSource(
928 StatisticsHelper::getErrorBars( xDataSeries, bYError ), uno::UNO_QUERY );
930 Reference< chart2::data::XLabeledDataSequence > xErrorLSequence(
931 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
932 xErrorSource,
933 /* bPositiveValue = */ true,
934 bYError ));
935 if( xErrorLSequence.is())
936 aSequences.push_back( xErrorLSequence );
938 xErrorLSequence.set(
939 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
940 xErrorSource,
941 /* bPositiveValue = */ false,
942 bYError ));
943 if( xErrorLSequence.is())
944 aSequences.push_back( xErrorLSequence );
946 for (Reference<chart2::data::XLabeledDataSequence> const & rDataSequence : aSequences)
948 m_aColumns.emplace_back(xDataSeries, lcl_getUIRoleName(rDataSequence),
949 rDataSequence, NUMBER, nNumberFormatKey);
950 ++rInOutSequenceIndex;
951 ++rInOutHeaderEnd;
954 catch( const uno::Exception & )
956 DBG_UNHANDLED_EXCEPTION("chart2");
960 } // namespace chart
962 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */