tdf#150621 Changed Korean word counting to use words
[LibreOffice.git] / chart2 / source / controller / dialogs / DataBrowserModel.cxx
blob1db3abc60c9a4a37551cf70def49499eae62e97d
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 <ChartType.hxx>
24 #include <ChartTypeManager.hxx>
25 #include <Diagram.hxx>
26 #include <DataSeries.hxx>
27 #include <DataSeriesHelper.hxx>
28 #include <ControllerLockGuard.hxx>
29 #include <StatisticsHelper.hxx>
30 #include <ChartTypeHelper.hxx>
31 #include <chartview/ExplicitValueProvider.hxx>
32 #include <ExplicitCategoriesProvider.hxx>
33 #include <BaseCoordinateSystem.hxx>
34 #include <ChartModel.hxx>
35 #include <unonames.hxx>
37 #include <com/sun/star/container/XIndexReplace.hpp>
38 #include <com/sun/star/chart2/XInternalDataProvider.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 <o3tl/safeint.hxx>
44 #include <comphelper/diagnose_ex.hxx>
45 #include <comphelper/property.hxx>
47 #include <algorithm>
48 #include <cstddef>
49 #include <limits>
50 #include <utility>
52 using namespace ::com::sun::star;
54 using ::com::sun::star::uno::Reference;
55 using ::com::sun::star::uno::Sequence;
57 namespace chart {
59 namespace {
61 OUString lcl_getRole(
62 const Reference< chart2::data::XDataSequence > & xSeq )
64 OUString aResult;
65 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
66 if( xProp.is())
68 try
70 xProp->getPropertyValue( u"Role"_ustr ) >>= aResult;
72 catch( const uno::Exception & )
74 DBG_UNHANDLED_EXCEPTION("chart2");
77 return aResult;
80 OUString lcl_getUIRoleName(
81 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
83 OUString aResult = DataSeriesHelper::getRole(xLSeq);
84 if( !aResult.isEmpty())
85 aResult = DialogModel::ConvertRoleFromInternalToUI(aResult);
86 return aResult;
89 void lcl_copyDataSequenceProperties(
90 const Reference< chart2::data::XDataSequence > & xOldSequence,
91 const Reference< chart2::data::XDataSequence > & xNewSequence )
93 Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY );
94 Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY );
95 comphelper::copyProperties( xOldSeqProp, xNewSeqProp );
98 bool lcl_SequenceOfSeriesIsShared(
99 const rtl::Reference< ::chart::DataSeries > & xSeries,
100 const Reference< chart2::data::XDataSequence > & xValues )
102 bool bResult = false;
103 if( !xValues.is())
104 return bResult;
107 OUString aValuesRole( lcl_getRole( xValues ));
108 OUString aValuesRep( xValues->getSourceRangeRepresentation());
109 const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aLSeq( xSeries->getDataSequences2());
110 for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledDataSeq : aLSeq )
111 if (labeledDataSeq.is() && DataSeriesHelper::getRole(labeledDataSeq) == aValuesRole)
113 // getValues().is(), because lcl_getRole checked that already
114 bResult = (aValuesRep == labeledDataSeq->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< uno::Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec;
128 lcl_tSharedSeqVec lcl_getSharedSequences( const std::vector< rtl::Reference< DataSeries > > & 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.size() <= 1 )
135 return aResult;
137 for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledDataSeq : rSeries[0]->getDataSequences2() )
139 Reference< chart2::data::XDataSequence > xValues( labeledDataSeq->getValues());
140 bool bShared = true;
141 for( std::size_t nSeriesIdx=1; nSeriesIdx<rSeries.size(); ++nSeriesIdx )
143 bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues );
144 if( !bShared )
145 break;
147 if( bShared )
148 aResult.push_back( labeledDataSeq );
151 return aResult;
154 sal_Int32 lcl_getValuesRepresentationIndex(
155 const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
157 sal_Int32 nResult = -1;
158 if( xLSeq.is())
160 Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues());
161 if( xSeq.is())
163 OUString aRep( xSeq->getSourceRangeRepresentation());
164 nResult = aRep.toInt32();
167 return nResult;
170 struct lcl_RepresentationsOfLSeqMatch
172 explicit lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
173 m_aValuesRep( xLSeq.is() ?
174 (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString())
175 : OUString() )
177 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
179 if (!xLSeq.is() || !xLSeq->getValues().is())
180 return false;
182 return xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep;
184 private:
185 OUString m_aValuesRep;
188 struct lcl_RolesOfLSeqMatch
190 explicit lcl_RolesOfLSeqMatch( const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
191 m_aRole(DataSeriesHelper::getRole(xLSeq)) {}
193 bool operator() ( const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq )
195 return DataSeriesHelper::getRole(xLSeq) == m_aRole;
197 private:
198 OUString m_aRole;
201 bool lcl_ShowCategoriesAsDataLabel( const rtl::Reference< ::chart::Diagram > & xDiagram )
203 return !xDiagram->isCategory();
206 } // anonymous namespace
208 struct DataBrowserModel::tDataColumn
210 rtl::Reference<DataSeries> m_xDataSeries;
211 OUString m_aUIRoleName;
212 uno::Reference<chart2::data::XLabeledDataSequence> m_xLabeledDataSequence;
213 eCellType m_eCellType;
214 sal_Int32 m_nNumberFormatKey;
216 // default CTOR
217 tDataColumn() : m_eCellType( TEXT ), m_nNumberFormatKey( 0 ) {}
218 // "full" CTOR
219 tDataColumn(
220 rtl::Reference<DataSeries> xDataSeries,
221 OUString aUIRoleName,
222 uno::Reference<chart2::data::XLabeledDataSequence> xLabeledDataSequence,
223 eCellType aCellType,
224 sal_Int32 nNumberFormatKey ) :
225 m_xDataSeries(std::move( xDataSeries )),
226 m_aUIRoleName(std::move( aUIRoleName )),
227 m_xLabeledDataSequence(std::move( xLabeledDataSequence )),
228 m_eCellType( aCellType ),
229 m_nNumberFormatKey( nNumberFormatKey )
233 struct DataBrowserModel::implColumnLess
235 bool operator() ( const DataBrowserModel::tDataColumn & rLeft, const DataBrowserModel::tDataColumn & rRight )
237 if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is())
239 return DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rLeft.m_xLabeledDataSequence)) <
240 DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rRight.m_xLabeledDataSequence));
242 return true;
246 DataBrowserModel::DataBrowserModel(
247 const rtl::Reference<::chart::ChartModel> & xChartDoc ) :
248 m_xChartDocument( xChartDoc ),
249 m_apDialogModel( new DialogModel( xChartDoc ))
251 updateFromModel();
254 DataBrowserModel::~DataBrowserModel()
257 namespace
259 struct lcl_DataSeriesOfHeaderMatches
261 explicit lcl_DataSeriesOfHeaderMatches(
262 rtl::Reference< ::chart::DataSeries > xSeriesToCompareWith ) :
263 m_xSeries(std::move( xSeriesToCompareWith ))
265 bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader )
267 return (m_xSeries == rHeader.m_xDataSeries);
269 private:
270 rtl::Reference< ::chart::DataSeries > m_xSeries;
274 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex )
276 OSL_ASSERT(m_apDialogModel);
277 Reference< chart2::XInternalDataProvider > xDataProvider(
278 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
280 if (!xDataProvider.is())
281 return;
283 if( isCategoriesColumn(nAfterColumnIndex) )
284 // Move to the last category column.
285 nAfterColumnIndex = getCategoryColumnCount()-1;
287 sal_Int32 nStartCol = 0;
288 rtl::Reference< Diagram > xDiagram = m_xChartDocument->getFirstChartDiagram();
289 rtl::Reference<ChartType> xChartType;
290 rtl::Reference<DataSeries> xSeries;
291 if (o3tl::make_unsigned(nAfterColumnIndex) < m_aColumns.size())
292 // Get the data series at specific column position (if available).
293 xSeries = m_aColumns[nAfterColumnIndex].m_xDataSeries;
295 sal_Int32 nSeriesNumberFormat = 0;
296 if( xSeries.is())
298 // Use the chart type of the currently selected data series.
299 xChartType = xDiagram->getChartTypeOfSeries( xSeries );
301 // Find the corresponding header and determine the last column of this
302 // data series.
303 tDataHeaderVector::const_iterator aIt(
304 std::find_if( m_aHeaders.begin(), m_aHeaders.end(),
305 lcl_DataSeriesOfHeaderMatches( xSeries )));
306 if( aIt != m_aHeaders.end())
307 nStartCol = aIt->m_nEndColumn;
309 // Get the number format too.
310 if( xSeries.is() )
311 xSeries->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nSeriesNumberFormat;
313 else
315 // No data series at specified column position. Use the first chart type.
316 xChartType = xDiagram->getChartTypeByIndex( 0 );
317 nStartCol = nAfterColumnIndex;
320 if (!xChartType.is())
321 return;
323 // Get shared sequences of current series. Normally multiple data series
324 // only share "values-x" sequences. (TODO: simplify this logic).
325 lcl_tSharedSeqVec aSharedSequences = lcl_getSharedSequences( xChartType->getDataSeries2());
327 rtl::Reference<::chart::DataSeries> xNewSeries =
328 m_apDialogModel->insertSeriesAfter(xSeries, xChartType, true);
330 if (!xNewSeries.is())
331 // Failed to insert new data series to the model. Bail out.
332 return;
334 const std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > & aLSequences = xNewSeries->getDataSequences2();
335 sal_Int32 nSeqIdx = 0;
336 sal_Int32 nSeqSize = aLSequences.size();
337 for (sal_Int32 nIndex = nStartCol; nSeqIdx < nSeqSize; ++nSeqIdx)
339 lcl_tSharedSeqVec::const_iterator aSharedIt(
340 std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
341 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] )));
343 if( aSharedIt != aSharedSequences.end())
345 // Shared sequence. Most likely "values-x" sequence. Copy it from existing sequence.
346 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues());
347 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel());
349 else
351 // Insert a new column in the internal data for the new sequence.
352 xDataProvider->insertSequence( nIndex - 1 );
354 // values
355 Reference< chart2::data::XDataSequence > xNewSeq(
356 xDataProvider->createDataSequenceByRangeRepresentation(
357 OUString::number( nIndex )));
358 lcl_copyDataSequenceProperties(
359 aLSequences[nSeqIdx]->getValues(), xNewSeq );
360 aLSequences[nSeqIdx]->setValues( xNewSeq );
362 // labels
363 Reference< chart2::data::XDataSequence > xNewLabelSeq(
364 xDataProvider->createDataSequenceByRangeRepresentation(
365 "label " +
366 OUString::number( nIndex )));
367 lcl_copyDataSequenceProperties(
368 aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq );
369 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq );
370 ++nIndex;
374 if( nSeriesNumberFormat != 0 )
376 //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
377 xNewSeries->setPropertyValue(CHART_UNONAME_NUMFMT , uno::Any(nSeriesNumberFormat));
380 updateFromModel();
383 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex )
385 //create a new text column for complex categories
387 OSL_ASSERT(m_apDialogModel);
388 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
389 if (!xDataProvider.is())
390 return;
392 if( !isCategoriesColumn(nAfterColumnIndex) )
393 nAfterColumnIndex = getCategoryColumnCount()-1;
395 if(nAfterColumnIndex<0)
397 OSL_FAIL( "wrong index for category level insertion" );
398 return;
401 m_apDialogModel->startControllerLockTimer();
402 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
403 xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 );
404 updateFromModel();
407 void DataBrowserModel::removeComplexCategoryLevel( sal_Int32 nAtColumnIndex )
409 //delete a category column if there is more than one level (in case of a single column we do not get here)
410 OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" );
412 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
413 if (!xDataProvider.is())
414 return;
416 m_apDialogModel->startControllerLockTimer();
417 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
418 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex );
420 updateFromModel();
423 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex )
425 OSL_ASSERT(m_apDialogModel);
426 if (nAtColumnIndex < 0 || o3tl::make_unsigned(nAtColumnIndex) >= m_aColumns.size())
427 // Out of bound.
428 return;
430 if (isCategoriesColumn(nAtColumnIndex))
432 removeComplexCategoryLevel(nAtColumnIndex);
433 return;
436 const rtl::Reference<DataSeries>& xSeries = m_aColumns[nAtColumnIndex].m_xDataSeries;
438 m_apDialogModel->deleteSeries(xSeries, getHeaderForSeries(xSeries).m_xChartType);
440 //delete sequences from internal data provider that are not used anymore
441 //but do not delete sequences that are still in use by the remaining series
443 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
444 if (!xDataProvider.is() || !xSeries.is())
446 // Something went wrong. Bail out.
447 updateFromModel();
448 return;
451 rtl::Reference<ChartType> xSeriesCnt(getHeaderForSeries(xSeries).m_xChartType);
452 if (!xSeriesCnt.is())
454 // Unexpected happened. Bail out.
455 updateFromModel();
456 return;
459 // Collect all the remaining data sequences in the same chart type. The
460 // deleted data series is already gone by this point.
461 std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > aAllDataSeqs =
462 DataSeriesHelper::getAllDataSequences(xSeriesCnt->getDataSeries2());
464 // Check if the sequences to be deleted are still referenced by any of
465 // the other data series. If not, mark them for deletion.
466 std::vector<sal_Int32> aSequenceIndexesToDelete;
467 const std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > & aSequencesOfDeleted = xSeries->getDataSequences2();
468 for (auto const & labeledDataSeq : aSequencesOfDeleted)
470 // if not used by the remaining series this sequence can be deleted
471 if( std::none_of( aAllDataSeqs.begin(), aAllDataSeqs.end(),
472 lcl_RepresentationsOfLSeqMatch( labeledDataSeq )) )
473 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( labeledDataSeq ) );
476 // delete unnecessary sequences of the internal data
477 // iterate using greatest index first, so that deletion does not
478 // shift other sequences that will be deleted later
479 std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
480 for( std::vector< sal_Int32 >::reverse_iterator aIt(
481 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
483 if( *aIt != -1 )
484 xDataProvider->deleteSequence( *aIt );
487 updateFromModel();
490 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex )
492 OSL_ASSERT(m_apDialogModel);
493 if( o3tl::make_unsigned( nFirstColumnIndex ) < m_aColumns.size() - 1 )
495 rtl::Reference< DataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries );
496 if( xSeries.is())
498 m_apDialogModel->moveSeries( xSeries, DialogModel::MoveDirection::Down );
499 updateFromModel();
504 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex )
506 OSL_ASSERT(m_apDialogModel);
507 Reference< chart2::XInternalDataProvider > xDataProvider(
508 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
509 // lockControllers
510 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
511 if( xDataProvider.is())
512 xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex );
513 // unlockControllers
516 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex )
518 Reference< chart2::XInternalDataProvider > xDataProvider(
519 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
520 // lockControllers
521 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
522 if( xDataProvider.is())
523 xDataProvider->insertDataPointForAllSequences( nAfterIndex );
524 // unlockControllers
527 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex )
529 Reference< chart2::XInternalDataProvider > xDataProvider(
530 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
531 // lockControllers
532 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
533 if( xDataProvider.is())
534 xDataProvider->deleteDataPointForAllSequences( nAtIndex );
535 // unlockControllers
538 DataBrowserModel::tDataHeader DataBrowserModel::getHeaderForSeries(
539 const Reference< chart2::XDataSeries > & xSeries ) const
541 rtl::Reference<DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
542 assert(!xSeries || pSeries);
543 for (auto const& elemHeader : m_aHeaders)
545 if( elemHeader.m_xDataSeries == pSeries )
546 return elemHeader;
548 return tDataHeader();
551 rtl::Reference< DataSeries >
552 DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn ) const
554 tDataColumnVector::size_type nIndex( nColumn );
555 if( nIndex < m_aColumns.size())
556 return m_aColumns[nIndex].m_xDataSeries;
557 return nullptr;
560 DataBrowserModel::eCellType DataBrowserModel::getCellType( sal_Int32 nAtColumn ) const
562 eCellType eResult = TEXT;
563 tDataColumnVector::size_type nIndex( nAtColumn );
564 if( nIndex < m_aColumns.size())
565 eResult = m_aColumns[nIndex].m_eCellType;
566 return eResult;
569 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow )
571 tDataColumnVector::size_type nIndex( nAtColumn );
572 if( nIndex < m_aColumns.size() &&
573 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
575 Reference< chart2::data::XNumericalDataSequence > xData(
576 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
577 if( xData.is())
579 Sequence< double > aValues( xData->getNumericalData());
580 if( nAtRow < aValues.getLength())
581 return aValues[nAtRow];
584 return std::numeric_limits<double>::quiet_NaN();
587 uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow )
589 uno::Any aResult;
591 tDataColumnVector::size_type nIndex( nAtColumn );
592 if( nIndex < m_aColumns.size() &&
593 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
595 Reference< chart2::data::XDataSequence > xData(
596 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() );
597 if( xData.is() )
599 Sequence< uno::Any > aValues( xData->getData());
600 if( nAtRow < aValues.getLength())
601 aResult = aValues[nAtRow];
604 return aResult;
607 OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow )
609 OUString aResult;
611 tDataColumnVector::size_type nIndex( nAtColumn );
612 if( nIndex < m_aColumns.size() &&
613 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
615 Reference< chart2::data::XTextualDataSequence > xData(
616 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
617 if( xData.is())
619 Sequence< OUString > aValues( xData->getTextualData());
620 if( nAtRow < aValues.getLength())
621 aResult = aValues[nAtRow];
624 return aResult;
627 sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn )
629 tDataColumnVector::size_type nIndex( nAtColumn );
630 if( nIndex < m_aColumns.size())
631 return m_aColumns[ nIndex ].m_nNumberFormatKey;
632 return 0;
635 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue )
637 bool bResult = false;
638 tDataColumnVector::size_type nIndex( nAtColumn );
639 if( nIndex < m_aColumns.size() &&
640 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
642 bResult = true;
645 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
647 // label
648 if( nAtRow == -1 )
650 Reference< container::XIndexReplace > xIndexReplace(
651 m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW );
652 xIndexReplace->replaceByIndex( 0, rValue );
654 else
656 Reference< container::XIndexReplace > xIndexReplace(
657 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW );
658 xIndexReplace->replaceByIndex( nAtRow, rValue );
661 m_apDialogModel->startControllerLockTimer();
662 //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#)
663 if( m_xChartDocument.is() )
664 m_xChartDocument->setModified(true);
666 catch( const uno::Exception & )
668 bResult = false;
671 return bResult;
674 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue )
676 return (getCellType( nAtColumn ) == NUMBER) &&
677 setCellAny( nAtColumn, nAtRow, uno::Any( fValue ));
680 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const OUString & rText )
682 return (getCellType( nAtColumn ) == TEXT) &&
683 setCellAny( nAtColumn, nAtRow, uno::Any( rText ));
686 sal_Int32 DataBrowserModel::getColumnCount() const
688 return static_cast< sal_Int32 >( m_aColumns.size());
691 sal_Int32 DataBrowserModel::getMaxRowCount() const
693 sal_Int32 nResult = 0;
694 for (auto const& column : m_aColumns)
696 if( column.m_xLabeledDataSequence.is())
698 Reference< chart2::data::XDataSequence > xSeq(
699 column.m_xLabeledDataSequence->getValues());
700 if( !xSeq.is())
701 continue;
702 sal_Int32 nLength( xSeq->getData().getLength());
703 if( nLength > nResult )
704 nResult = nLength;
708 return nResult;
711 OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const
713 if( nColumnIndex != -1 &&
714 o3tl::make_unsigned( nColumnIndex ) < m_aColumns.size())
715 return m_aColumns[ nColumnIndex ].m_aUIRoleName;
716 return OUString();
719 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const
721 if (nColumnIndex < 0)
722 return false;
724 if (o3tl::make_unsigned(nColumnIndex) >= m_aColumns.size())
725 return false;
727 // A column is a category when it doesn't have an associated data series.
728 return !m_aColumns[nColumnIndex].m_xDataSeries.is();
731 sal_Int32 DataBrowserModel::getCategoryColumnCount()
733 sal_Int32 nLastTextColumnIndex = -1;
734 for (auto const& column : m_aColumns)
736 if( !column.m_xDataSeries.is() )
737 nLastTextColumnIndex++;
738 else
739 break;
741 return nLastTextColumnIndex+1;
744 void DataBrowserModel::updateFromModel()
746 if( !m_xChartDocument.is())
747 return;
748 m_aColumns.clear();
749 m_aHeaders.clear();
751 rtl::Reference< Diagram > xDiagram( m_xChartDocument->getFirstChartDiagram());
752 if( !xDiagram.is())
753 return;
755 // set template at DialogModel
756 rtl::Reference< ::chart::ChartTypeManager > xChartTypeManager = m_xChartDocument->getTypeManager();
757 Diagram::tTemplateWithServiceName aTemplateAndService =
758 xDiagram->getTemplate( xChartTypeManager );
759 if( aTemplateAndService.xChartTypeTemplate.is())
760 m_apDialogModel->setTemplate( aTemplateAndService.xChartTypeTemplate );
762 sal_Int32 nHeaderStart = 0;
763 sal_Int32 nHeaderEnd = 0;
765 ExplicitCategoriesProvider aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(m_xChartDocument), *m_xChartDocument );
767 const std::vector< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList = aExplicitCategoriesProvider.getSplitCategoriesList();
768 sal_Int32 nLevelCount = rSplitCategoriesList.size();
769 for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
771 Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] );
772 if( !xCategories.is() )
773 continue;
775 tDataColumn aCategories;
776 aCategories.m_xLabeledDataSequence = xCategories;
777 if( lcl_ShowCategoriesAsDataLabel( xDiagram ))
778 aCategories.m_aUIRoleName = DialogModel::GetRoleDataLabel();
779 else
780 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories );
781 aCategories.m_eCellType = TEXTORDATE;
782 m_aColumns.push_back( aCategories );
783 ++nHeaderStart;
787 if( !xDiagram.is())
788 return;
789 const std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq( xDiagram->getBaseCoordinateSystems());
790 for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysSeq )
792 const std::vector< rtl::Reference< ChartType > > aChartTypes( coords->getChartTypes2());
793 sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( nullptr, coords, 0, 0 );
795 for( auto const & CT: aChartTypes )
797 rtl::Reference< ChartType > xSeriesCnt( CT );
798 OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( CT );
800 const std::vector< rtl::Reference< DataSeries > > & aSeries( xSeriesCnt->getDataSeries2());
801 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries ));
802 for (auto const& sharedSequence : aSharedSequences)
804 tDataColumn aSharedSequence;
805 aSharedSequence.m_xLabeledDataSequence = sharedSequence;
806 aSharedSequence.m_aUIRoleName = lcl_getUIRoleName(sharedSequence);
807 aSharedSequence.m_eCellType = NUMBER;
808 // as the sequences are shared it should be ok to take the first series
809 // @todo: dimension index 0 for x-values used here. This is just a guess.
810 // Also, the axis index is 0, as there is usually only one x-axis
811 aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat;
812 m_aColumns.push_back( aSharedSequence );
813 ++nHeaderStart;
815 for( rtl::Reference< DataSeries > const & dataSeries : aSeries )
817 tDataColumnVector::size_type nStartColIndex = m_aColumns.size();
818 rtl::Reference< DataSeries > xSeries( dataSeries );
819 if( xSeries.is())
821 const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aLSeqs( xSeries->getDataSequences2());
822 if( aLSeqs.empty() )
823 continue;
824 nHeaderEnd = nHeaderStart;
826 // @todo: dimension index 1 for y-values used here. This is just a guess
827 sal_Int32 nYAxisNumberFormatKey =
828 DataSeriesHelper::getNumberFormatKeyFromAxis(
829 dataSeries, coords, 1 );
831 sal_Int32 nSeqIdx=0;
832 for( ; nSeqIdx<static_cast<sal_Int32>(aLSeqs.size()); ++nSeqIdx )
834 sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey;
835 OUString aRole = DataSeriesHelper::getRole(aLSeqs[nSeqIdx]);
837 if( aRole == aRoleForDataLabelNumberFormat )
839 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
840 xSeries);
842 else if( aRole == "values-x" )
843 nSequenceNumberFormatKey = nXAxisNumberFormat;
845 if( std::none_of( aSharedSequences.begin(), aSharedSequences.end(),
846 lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) )
848 // no shared sequence
849 m_aColumns.emplace_back(
850 dataSeries,
851 lcl_getUIRoleName( aLSeqs[nSeqIdx] ),
852 aLSeqs[nSeqIdx],
853 NUMBER,
854 nSequenceNumberFormatKey );
855 ++nHeaderEnd;
857 // else skip
859 bool bSwapXAndYAxis = false;
862 coords->getPropertyValue( u"SwapXAndYAxis"_ustr ) >>= bSwapXAndYAxis;
864 catch( const beans::UnknownPropertyException & ) {}
866 // add ranges for error bars if present for a series
867 if( StatisticsHelper::usesErrorBarRanges( dataSeries ))
868 addErrorBarRanges( dataSeries, nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, true );
870 if( StatisticsHelper::usesErrorBarRanges( dataSeries, /* bYError = */ false ))
871 addErrorBarRanges( dataSeries, nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, false );
873 m_aHeaders.emplace_back(
874 dataSeries,
876 bSwapXAndYAxis,
877 nHeaderStart,
878 nHeaderEnd - 1 );
880 nHeaderStart = nHeaderEnd;
882 std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() );
889 void DataBrowserModel::addErrorBarRanges(
890 const rtl::Reference< DataSeries > & xDataSeries,
891 sal_Int32 nNumberFormatKey,
892 sal_Int32 & rInOutSequenceIndex,
893 sal_Int32 & rInOutHeaderEnd, bool bYError )
897 std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences;
899 Reference< chart2::data::XDataSource > xErrorSource(
900 StatisticsHelper::getErrorBars( xDataSeries, bYError ), uno::UNO_QUERY );
902 uno::Reference< chart2::data::XLabeledDataSequence > xErrorLSequence =
903 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
904 xErrorSource,
905 /* bPositiveValue = */ true,
906 bYError );
907 if( xErrorLSequence.is())
908 aSequences.push_back( xErrorLSequence );
910 xErrorLSequence =
911 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
912 xErrorSource,
913 /* bPositiveValue = */ false,
914 bYError );
915 if( xErrorLSequence.is())
916 aSequences.push_back( xErrorLSequence );
918 for (uno::Reference<chart2::data::XLabeledDataSequence> const & rDataSequence : aSequences)
920 m_aColumns.emplace_back(xDataSeries, lcl_getUIRoleName(rDataSequence),
921 rDataSequence, NUMBER, nNumberFormatKey);
922 ++rInOutSequenceIndex;
923 ++rInOutHeaderEnd;
926 catch( const uno::Exception & )
928 DBG_UNHANDLED_EXCEPTION("chart2");
932 } // namespace chart
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */