LibreOffice Module chart2 (master) 1
DataBrowserModel.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "DataBrowserModel.hxx"
21#include "DialogModel.hxx"
22#include <ChartModelHelper.hxx>
23#include <ChartType.hxx>
24#include <ChartTypeManager.hxx>
25#include <DiagramHelper.hxx>
26#include <Diagram.hxx>
27#include <DataSeries.hxx>
28#include <DataSeriesHelper.hxx>
30#include <StatisticsHelper.hxx>
31#include <ChartTypeHelper.hxx>
35#include <ChartModel.hxx>
36#include <unonames.hxx>
37
38#include <com/sun/star/container/XIndexReplace.hpp>
39#include <com/sun/star/chart2/XInternalDataProvider.hpp>
40#include <com/sun/star/chart2/data/XDataSource.hpp>
41#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
42#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
43#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
44#include <o3tl/safeint.hxx>
47
48#include <algorithm>
49#include <cstddef>
50#include <limits>
51#include <utility>
52
53using namespace ::com::sun::star;
54
55using ::com::sun::star::uno::Reference;
56using ::com::sun::star::uno::Sequence;
57
58namespace chart {
59
60namespace {
61
62OUString lcl_getRole(
63 const Reference< chart2::data::XDataSequence > & xSeq )
64{
65 OUString aResult;
66 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
67 if( xProp.is())
68 {
69 try
70 {
71 xProp->getPropertyValue( "Role" ) >>= aResult;
72 }
73 catch( const uno::Exception & )
74 {
76 }
77 }
78 return aResult;
79}
80
81OUString lcl_getUIRoleName(
83{
84 OUString aResult = DataSeriesHelper::getRole(xLSeq);
85 if( !aResult.isEmpty())
87 return aResult;
88}
89
90void lcl_copyDataSequenceProperties(
91 const Reference< chart2::data::XDataSequence > & xOldSequence,
92 const Reference< chart2::data::XDataSequence > & xNewSequence )
93{
94 Reference< beans::XPropertySet > xOldSeqProp( xOldSequence, uno::UNO_QUERY );
95 Reference< beans::XPropertySet > xNewSeqProp( xNewSequence, uno::UNO_QUERY );
96 comphelper::copyProperties( xOldSeqProp, xNewSeqProp );
97}
98
99bool lcl_SequenceOfSeriesIsShared(
101 const Reference< chart2::data::XDataSequence > & xValues )
102{
103 bool bResult = false;
104 if( !xValues.is())
105 return bResult;
106 try
107 {
108 OUString aValuesRole( lcl_getRole( xValues ));
109 OUString aValuesRep( xValues->getSourceRangeRepresentation());
110 const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aLSeq( xSeries->getDataSequences2());
111 for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledDataSeq : aLSeq )
112 if (labeledDataSeq.is() && DataSeriesHelper::getRole(labeledDataSeq) == aValuesRole)
113 {
114 // getValues().is(), because lcl_getRole checked that already
115 bResult = (aValuesRep == labeledDataSeq->getValues()->getSourceRangeRepresentation());
116 // assumption: a role appears only once in a series
117 break;
118 }
119 }
120 catch( const uno::Exception & )
121 {
122 DBG_UNHANDLED_EXCEPTION("chart2");
123 }
124 return bResult;
125}
126
127typedef std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > lcl_tSharedSeqVec;
128
129lcl_tSharedSeqVec lcl_getSharedSequences( const std::vector< rtl::Reference< DataSeries > > & rSeries )
130{
131 // @todo: if only some series share a sequence, those have to be duplicated
132 // and made unshared for all series
133 lcl_tSharedSeqVec aResult;
134 // if we have only one series, we don't want any shared sequences
135 if( rSeries.size() <= 1 )
136 return aResult;
137
138 for( uno::Reference< chart2::data::XLabeledDataSequence > const & labeledDataSeq : rSeries[0]->getDataSequences2() )
139 {
140 Reference< chart2::data::XDataSequence > xValues( labeledDataSeq->getValues());
141 bool bShared = true;
142 for( std::size_t nSeriesIdx=1; nSeriesIdx<rSeries.size(); ++nSeriesIdx )
143 {
144 bShared = lcl_SequenceOfSeriesIsShared( rSeries[nSeriesIdx], xValues );
145 if( !bShared )
146 break;
147 }
148 if( bShared )
149 aResult.push_back( labeledDataSeq );
150 }
151
152 return aResult;
153}
154
155sal_Int32 lcl_getValuesRepresentationIndex(
157{
158 sal_Int32 nResult = -1;
159 if( xLSeq.is())
160 {
161 Reference< chart2::data::XDataSequence > xSeq( xLSeq->getValues());
162 if( xSeq.is())
163 {
164 OUString aRep( xSeq->getSourceRangeRepresentation());
165 nResult = aRep.toInt32();
166 }
167 }
168 return nResult;
169}
170
171struct lcl_RepresentationsOfLSeqMatch
172{
173 explicit lcl_RepresentationsOfLSeqMatch( const Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
174 m_aValuesRep( xLSeq.is() ?
175 (xLSeq->getValues().is() ? xLSeq->getValues()->getSourceRangeRepresentation() : OUString())
176 : OUString() )
177 {}
178 bool operator() ( const Reference< chart2::data::XLabeledDataSequence > & xLSeq )
179 {
180 if (!xLSeq.is() || !xLSeq->getValues().is())
181 return false;
182
183 return xLSeq->getValues()->getSourceRangeRepresentation() == m_aValuesRep;
184 }
185private:
186 OUString m_aValuesRep;
187};
188
189struct lcl_RolesOfLSeqMatch
190{
191 explicit lcl_RolesOfLSeqMatch( const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq ) :
193
194 bool operator() ( const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq )
195 {
196 return DataSeriesHelper::getRole(xLSeq) == m_aRole;
197 }
198private:
199 OUString m_aRole;
200};
201
202bool lcl_ShowCategoriesAsDataLabel( const rtl::Reference< ::chart::Diagram > & xDiagram )
203{
204 return !xDiagram->isCategory();
205}
206
207} // anonymous namespace
208
210{
216
217 // default CTOR
219 // "full" CTOR
221 rtl::Reference<DataSeries> xDataSeries,
222 OUString aUIRoleName,
224 eCellType aCellType,
225 sal_Int32 nNumberFormatKey ) :
226 m_xDataSeries(std::move( xDataSeries )),
227 m_aUIRoleName(std::move( aUIRoleName )),
228 m_xLabeledDataSequence(std::move( xLabeledDataSequence )),
229 m_eCellType( aCellType ),
230 m_nNumberFormatKey( nNumberFormatKey )
231 {}
232};
233
235{
237 {
238 if( rLeft.m_xLabeledDataSequence.is() && rRight.m_xLabeledDataSequence.is())
239 {
242 }
243 return true;
244 }
245};
246
248 const rtl::Reference<::chart::ChartModel> & xChartDoc ) :
249 m_xChartDocument( xChartDoc ),
250 m_apDialogModel( new DialogModel( xChartDoc ))
251{
253}
254
256{}
257
258namespace
259{
260struct lcl_DataSeriesOfHeaderMatches
261{
262 explicit lcl_DataSeriesOfHeaderMatches(
263 rtl::Reference< ::chart::DataSeries > xSeriesToCompareWith ) :
264 m_xSeries(std::move( xSeriesToCompareWith ))
265 {}
266 bool operator() ( const ::chart::DataBrowserModel::tDataHeader & rHeader )
267 {
268 return (m_xSeries == rHeader.m_xDataSeries);
269 }
270private:
272};
273}
274
275void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex )
276{
277 OSL_ASSERT(m_apDialogModel);
279 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
280
281 if (!xDataProvider.is())
282 return;
283
284 if( isCategoriesColumn(nAfterColumnIndex) )
285 // Move to the last category column.
286 nAfterColumnIndex = getCategoryColumnCount()-1;
287
288 sal_Int32 nStartCol = 0;
289 rtl::Reference< Diagram > xDiagram = m_xChartDocument->getFirstChartDiagram();
290 rtl::Reference<ChartType> xChartType;
292 if (o3tl::make_unsigned(nAfterColumnIndex) < m_aColumns.size())
293 // Get the data series at specific column position (if available).
294 xSeries = m_aColumns[nAfterColumnIndex].m_xDataSeries;
295
296 sal_Int32 nSeriesNumberFormat = 0;
297 if( xSeries.is())
298 {
299 // Use the chart type of the currently selected data series.
300 xChartType = xDiagram->getChartTypeOfSeries( xSeries );
301
302 // Find the corresponding header and determine the last column of this
303 // data series.
304 tDataHeaderVector::const_iterator aIt(
305 std::find_if( m_aHeaders.begin(), m_aHeaders.end(),
306 lcl_DataSeriesOfHeaderMatches( xSeries )));
307 if( aIt != m_aHeaders.end())
308 nStartCol = aIt->m_nEndColumn;
309
310 // Get the number format too.
311 if( xSeries.is() )
312 xSeries->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nSeriesNumberFormat;
313 }
314 else
315 {
316 // No data series at specified column position. Use the first chart type.
317 xChartType = xDiagram->getChartTypeByIndex( 0 );
318 nStartCol = nAfterColumnIndex;
319 }
320
321 if (!xChartType.is())
322 return;
323
324 // Get shared sequences of current series. Normally multiple data series
325 // only share "values-x" sequences. (TODO: simplify this logic).
326 lcl_tSharedSeqVec aSharedSequences = lcl_getSharedSequences( xChartType->getDataSeries2());
327
329 m_apDialogModel->insertSeriesAfter(xSeries, xChartType, true);
330
331 if (!xNewSeries.is())
332 // Failed to insert new data series to the model. Bail out.
333 return;
334
335 const std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > & aLSequences = xNewSeries->getDataSequences2();
336 sal_Int32 nSeqIdx = 0;
337 sal_Int32 nSeqSize = aLSequences.size();
338 for (sal_Int32 nIndex = nStartCol; nSeqIdx < nSeqSize; ++nSeqIdx)
339 {
340 lcl_tSharedSeqVec::const_iterator aSharedIt(
341 std::find_if( aSharedSequences.begin(), aSharedSequences.end(),
342 lcl_RolesOfLSeqMatch( aLSequences[nSeqIdx] )));
343
344 if( aSharedIt != aSharedSequences.end())
345 {
346 // Shared sequence. Most likely "values-x" sequence. Copy it from existing sequence.
347 aLSequences[nSeqIdx]->setValues( (*aSharedIt)->getValues());
348 aLSequences[nSeqIdx]->setLabel( (*aSharedIt)->getLabel());
349 }
350 else
351 {
352 // Insert a new column in the internal data for the new sequence.
353 xDataProvider->insertSequence( nIndex - 1 );
354
355 // values
357 xDataProvider->createDataSequenceByRangeRepresentation(
358 OUString::number( nIndex )));
359 lcl_copyDataSequenceProperties(
360 aLSequences[nSeqIdx]->getValues(), xNewSeq );
361 aLSequences[nSeqIdx]->setValues( xNewSeq );
362
363 // labels
365 xDataProvider->createDataSequenceByRangeRepresentation(
366 "label " +
367 OUString::number( nIndex )));
368 lcl_copyDataSequenceProperties(
369 aLSequences[nSeqIdx]->getLabel(), xNewLabelSeq );
370 aLSequences[nSeqIdx]->setLabel( xNewLabelSeq );
371 ++nIndex;
372 }
373 }
374
375 if( nSeriesNumberFormat != 0 )
376 {
377 //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
378 xNewSeries->setPropertyValue(CHART_UNONAME_NUMFMT , uno::Any(nSeriesNumberFormat));
379 }
380
382}
383
384void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex )
385{
386 //create a new text column for complex categories
387
388 OSL_ASSERT(m_apDialogModel);
389 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
390 if (!xDataProvider.is())
391 return;
392
393 if( !isCategoriesColumn(nAfterColumnIndex) )
394 nAfterColumnIndex = getCategoryColumnCount()-1;
395
396 if(nAfterColumnIndex<0)
397 {
398 OSL_FAIL( "wrong index for category level insertion" );
399 return;
400 }
401
402 m_apDialogModel->startControllerLockTimer();
403 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
404 xDataProvider->insertComplexCategoryLevel( nAfterColumnIndex+1 );
406}
407
408void DataBrowserModel::removeComplexCategoryLevel( sal_Int32 nAtColumnIndex )
409{
410 //delete a category column if there is more than one level (in case of a single column we do not get here)
411 OSL_ENSURE(nAtColumnIndex>0, "wrong index for categories deletion" );
412
413 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
414 if (!xDataProvider.is())
415 return;
416
417 m_apDialogModel->startControllerLockTimer();
418 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
419 xDataProvider->deleteComplexCategoryLevel( nAtColumnIndex );
420
422}
423
425{
426 OSL_ASSERT(m_apDialogModel);
427 if (nAtColumnIndex < 0 || o3tl::make_unsigned(nAtColumnIndex) >= m_aColumns.size())
428 // Out of bound.
429 return;
430
431 if (isCategoriesColumn(nAtColumnIndex))
432 {
433 removeComplexCategoryLevel(nAtColumnIndex);
434 return;
435 }
436
437 const rtl::Reference<DataSeries>& xSeries = m_aColumns[nAtColumnIndex].m_xDataSeries;
438
439 m_apDialogModel->deleteSeries(xSeries, getHeaderForSeries(xSeries).m_xChartType);
440
441 //delete sequences from internal data provider that are not used anymore
442 //but do not delete sequences that are still in use by the remaining series
443
444 Reference< chart2::XInternalDataProvider > xDataProvider( m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
445 if (!xDataProvider.is() || !xSeries.is())
446 {
447 // Something went wrong. Bail out.
449 return;
450 }
451
452 rtl::Reference<ChartType> xSeriesCnt(getHeaderForSeries(xSeries).m_xChartType);
453 if (!xSeriesCnt.is())
454 {
455 // Unexpected happened. Bail out.
457 return;
458 }
459
460 // Collect all the remaining data sequences in the same chart type. The
461 // deleted data series is already gone by this point.
462 std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > aAllDataSeqs =
463 DataSeriesHelper::getAllDataSequences(xSeriesCnt->getDataSeries2());
464
465 // Check if the sequences to be deleted are still referenced by any of
466 // the other data series. If not, mark them for deletion.
467 std::vector<sal_Int32> aSequenceIndexesToDelete;
468 const std::vector<uno::Reference<chart2::data::XLabeledDataSequence> > & aSequencesOfDeleted = xSeries->getDataSequences2();
469 for (auto const & labeledDataSeq : aSequencesOfDeleted)
470 {
471 // if not used by the remaining series this sequence can be deleted
472 if( std::none_of( aAllDataSeqs.begin(), aAllDataSeqs.end(),
473 lcl_RepresentationsOfLSeqMatch( labeledDataSeq )) )
474 aSequenceIndexesToDelete.push_back( lcl_getValuesRepresentationIndex( labeledDataSeq ) );
475 }
476
477 // delete unnecessary sequences of the internal data
478 // iterate using greatest index first, so that deletion does not
479 // shift other sequences that will be deleted later
480 std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end());
481 for( std::vector< sal_Int32 >::reverse_iterator aIt(
482 aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt )
483 {
484 if( *aIt != -1 )
485 xDataProvider->deleteSequence( *aIt );
486 }
487
489}
490
491void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex )
492{
493 OSL_ASSERT(m_apDialogModel);
494 if( o3tl::make_unsigned( nFirstColumnIndex ) < m_aColumns.size() - 1 )
495 {
496 rtl::Reference< DataSeries > xSeries( m_aColumns[nFirstColumnIndex].m_xDataSeries );
497 if( xSeries.is())
498 {
501 }
502 }
503}
504
506{
507 OSL_ASSERT(m_apDialogModel);
509 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
510 // lockControllers
511 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
512 if( xDataProvider.is())
513 xDataProvider->swapDataPointWithNextOneForAllSequences( nFirstIndex );
514 // unlockControllers
515}
516
518{
520 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
521 // lockControllers
522 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
523 if( xDataProvider.is())
524 xDataProvider->insertDataPointForAllSequences( nAfterIndex );
525 // unlockControllers
526}
527
529{
531 m_apDialogModel->getDataProvider(), uno::UNO_QUERY );
532 // lockControllers
533 ControllerLockGuardUNO aGuard( m_apDialogModel->getChartModel());
534 if( xDataProvider.is())
535 xDataProvider->deleteDataPointForAllSequences( nAtIndex );
536 // unlockControllers
537}
538
540 const Reference< chart2::XDataSeries > & xSeries ) const
541{
542 rtl::Reference<DataSeries> pSeries = dynamic_cast<DataSeries*>(xSeries.get());
543 assert(!xSeries || pSeries);
544 for (auto const& elemHeader : m_aHeaders)
545 {
546 if( elemHeader.m_xDataSeries == pSeries )
547 return elemHeader;
548 }
549 return tDataHeader();
550}
551
554{
555 tDataColumnVector::size_type nIndex( nColumn );
556 if( nIndex < m_aColumns.size())
557 return m_aColumns[nIndex].m_xDataSeries;
558 return nullptr;
559}
560
562{
563 eCellType eResult = TEXT;
564 tDataColumnVector::size_type nIndex( nAtColumn );
565 if( nIndex < m_aColumns.size())
566 eResult = m_aColumns[nIndex].m_eCellType;
567 return eResult;
568}
569
570double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow )
571{
572 tDataColumnVector::size_type nIndex( nAtColumn );
573 if( nIndex < m_aColumns.size() &&
574 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
575 {
577 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
578 if( xData.is())
579 {
580 Sequence< double > aValues( xData->getNumericalData());
581 if( nAtRow < aValues.getLength())
582 return aValues[nAtRow];
583 }
584 }
585 return std::numeric_limits<double>::quiet_NaN();
586}
587
588uno::Any DataBrowserModel::getCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow )
589{
590 uno::Any aResult;
591
592 tDataColumnVector::size_type nIndex( nAtColumn );
593 if( nIndex < m_aColumns.size() &&
594 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
595 {
597 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues() );
598 if( xData.is() )
599 {
600 Sequence< uno::Any > aValues( xData->getData());
601 if( nAtRow < aValues.getLength())
602 aResult = aValues[nAtRow];
603 }
604 }
605 return aResult;
606}
607
608OUString DataBrowserModel::getCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow )
609{
610 OUString aResult;
611
612 tDataColumnVector::size_type nIndex( nAtColumn );
613 if( nIndex < m_aColumns.size() &&
614 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
615 {
617 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY );
618 if( xData.is())
619 {
620 Sequence< OUString > aValues( xData->getTextualData());
621 if( nAtRow < aValues.getLength())
622 aResult = aValues[nAtRow];
623 }
624 }
625 return aResult;
626}
627
628sal_uInt32 DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn )
629{
630 tDataColumnVector::size_type nIndex( nAtColumn );
631 if( nIndex < m_aColumns.size())
632 return m_aColumns[ nIndex ].m_nNumberFormatKey;
633 return 0;
634}
635
636bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn, sal_Int32 nAtRow, const uno::Any & rValue )
637{
638 bool bResult = false;
639 tDataColumnVector::size_type nIndex( nAtColumn );
640 if( nIndex < m_aColumns.size() &&
641 m_aColumns[ nIndex ].m_xLabeledDataSequence.is())
642 {
643 bResult = true;
644 try
645 {
646 ControllerLockGuardUNO aLockedControllers( m_xChartDocument );
647
648 // label
649 if( nAtRow == -1 )
650 {
652 m_aColumns[ nIndex ].m_xLabeledDataSequence->getLabel(), uno::UNO_QUERY_THROW );
653 xIndexReplace->replaceByIndex( 0, rValue );
654 }
655 else
656 {
658 m_aColumns[ nIndex ].m_xLabeledDataSequence->getValues(), uno::UNO_QUERY_THROW );
659 xIndexReplace->replaceByIndex( nAtRow, rValue );
660 }
661
662 m_apDialogModel->startControllerLockTimer();
663 //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#)
664 if( m_xChartDocument.is() )
665 m_xChartDocument->setModified(true);
666 }
667 catch( const uno::Exception & )
668 {
669 bResult = false;
670 }
671 }
672 return bResult;
673}
674
675bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue )
676{
677 return (getCellType( nAtColumn ) == NUMBER) &&
678 setCellAny( nAtColumn, nAtRow, uno::Any( fValue ));
679}
680
681bool DataBrowserModel::setCellText( sal_Int32 nAtColumn, sal_Int32 nAtRow, const OUString & rText )
682{
683 return (getCellType( nAtColumn ) == TEXT) &&
684 setCellAny( nAtColumn, nAtRow, uno::Any( rText ));
685}
686
688{
689 return static_cast< sal_Int32 >( m_aColumns.size());
690}
691
693{
694 sal_Int32 nResult = 0;
695 for (auto const& column : m_aColumns)
696 {
697 if( column.m_xLabeledDataSequence.is())
698 {
700 column.m_xLabeledDataSequence->getValues());
701 if( !xSeq.is())
702 continue;
703 sal_Int32 nLength( xSeq->getData().getLength());
704 if( nLength > nResult )
705 nResult = nLength;
706 }
707 }
708
709 return nResult;
710}
711
712OUString DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex ) const
713{
714 if( nColumnIndex != -1 &&
715 o3tl::make_unsigned( nColumnIndex ) < m_aColumns.size())
716 return m_aColumns[ nColumnIndex ].m_aUIRoleName;
717 return OUString();
718}
719
720bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex ) const
721{
722 if (nColumnIndex < 0)
723 return false;
724
725 if (o3tl::make_unsigned(nColumnIndex) >= m_aColumns.size())
726 return false;
727
728 // A column is a category when it doesn't have an associated data series.
729 return !m_aColumns[nColumnIndex].m_xDataSeries.is();
730}
731
733{
734 sal_Int32 nLastTextColumnIndex = -1;
735 for (auto const& column : m_aColumns)
736 {
737 if( !column.m_xDataSeries.is() )
738 nLastTextColumnIndex++;
739 else
740 break;
741 }
742 return nLastTextColumnIndex+1;
743}
744
746{
747 if( !m_xChartDocument.is())
748 return;
749 m_aColumns.clear();
750 m_aHeaders.clear();
751
752 rtl::Reference< Diagram > xDiagram( m_xChartDocument->getFirstChartDiagram());
753 if( !xDiagram.is())
754 return;
755
756 // set template at DialogModel
757 rtl::Reference< ::chart::ChartTypeManager > xChartTypeManager = m_xChartDocument->getTypeManager();
758 Diagram::tTemplateWithServiceName aTemplateAndService =
759 xDiagram->getTemplate( xChartTypeManager );
760 if( aTemplateAndService.xChartTypeTemplate.is())
761 m_apDialogModel->setTemplate( aTemplateAndService.xChartTypeTemplate );
762
763 sal_Int32 nHeaderStart = 0;
764 sal_Int32 nHeaderEnd = 0;
765 {
767
768 const std::vector< Reference< chart2::data::XLabeledDataSequence> >& rSplitCategoriesList = aExplicitCategoriesProvider.getSplitCategoriesList();
769 sal_Int32 nLevelCount = rSplitCategoriesList.size();
770 for( sal_Int32 nL = 0; nL<nLevelCount; nL++ )
771 {
772 Reference< chart2::data::XLabeledDataSequence > xCategories( rSplitCategoriesList[nL] );
773 if( !xCategories.is() )
774 continue;
775
776 tDataColumn aCategories;
777 aCategories.m_xLabeledDataSequence = xCategories;
778 if( lcl_ShowCategoriesAsDataLabel( xDiagram ))
780 else
781 aCategories.m_aUIRoleName = lcl_getUIRoleName( xCategories );
782 aCategories.m_eCellType = TEXTORDATE;
783 m_aColumns.push_back( aCategories );
784 ++nHeaderStart;
785 }
786 }
787
788 if( !xDiagram.is())
789 return;
790 const std::vector< rtl::Reference< BaseCoordinateSystem > > aCooSysSeq( xDiagram->getBaseCoordinateSystems());
791 for( rtl::Reference< BaseCoordinateSystem > const & coords : aCooSysSeq )
792 {
793 const std::vector< rtl::Reference< ChartType > > aChartTypes( coords->getChartTypes2());
794 sal_Int32 nXAxisNumberFormat = DataSeriesHelper::getNumberFormatKeyFromAxis( nullptr, coords, 0, 0 );
795
796 for( auto const & CT: aChartTypes )
797 {
798 rtl::Reference< ChartType > xSeriesCnt( CT );
799 OUString aRoleForDataLabelNumberFormat = ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( CT );
800
801 const std::vector< rtl::Reference< DataSeries > > & aSeries( xSeriesCnt->getDataSeries2());
802 lcl_tSharedSeqVec aSharedSequences( lcl_getSharedSequences( aSeries ));
803 for (auto const& sharedSequence : aSharedSequences)
804 {
805 tDataColumn aSharedSequence;
806 aSharedSequence.m_xLabeledDataSequence = sharedSequence;
807 aSharedSequence.m_aUIRoleName = lcl_getUIRoleName(sharedSequence);
808 aSharedSequence.m_eCellType = NUMBER;
809 // as the sequences are shared it should be ok to take the first series
810 // @todo: dimension index 0 for x-values used here. This is just a guess.
811 // Also, the axis index is 0, as there is usually only one x-axis
812 aSharedSequence.m_nNumberFormatKey = nXAxisNumberFormat;
813 m_aColumns.push_back( aSharedSequence );
814 ++nHeaderStart;
815 }
816 for( rtl::Reference< DataSeries > const & dataSeries : aSeries )
817 {
818 tDataColumnVector::size_type nStartColIndex = m_aColumns.size();
819 rtl::Reference< DataSeries > xSeries( dataSeries );
820 if( xSeries.is())
821 {
822 const std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > & aLSeqs( xSeries->getDataSequences2());
823 if( aLSeqs.empty() )
824 continue;
825 nHeaderEnd = nHeaderStart;
826
827 // @todo: dimension index 1 for y-values used here. This is just a guess
828 sal_Int32 nYAxisNumberFormatKey =
830 dataSeries, coords, 1 );
831
832 sal_Int32 nSeqIdx=0;
833 for( ; nSeqIdx<static_cast<sal_Int32>(aLSeqs.size()); ++nSeqIdx )
834 {
835 sal_Int32 nSequenceNumberFormatKey = nYAxisNumberFormatKey;
836 OUString aRole = DataSeriesHelper::getRole(aLSeqs[nSeqIdx]);
837
838 if( aRole == aRoleForDataLabelNumberFormat )
839 {
840 nSequenceNumberFormatKey = ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
841 xSeries);
842 }
843 else if( aRole == "values-x" )
844 nSequenceNumberFormatKey = nXAxisNumberFormat;
845
846 if( std::none_of( aSharedSequences.begin(), aSharedSequences.end(),
847 lcl_RepresentationsOfLSeqMatch( aLSeqs[nSeqIdx] )) )
848 {
849 // no shared sequence
850 m_aColumns.emplace_back(
851 dataSeries,
852 lcl_getUIRoleName( aLSeqs[nSeqIdx] ),
853 aLSeqs[nSeqIdx],
854 NUMBER,
855 nSequenceNumberFormatKey );
856 ++nHeaderEnd;
857 }
858 // else skip
859 }
860 bool bSwapXAndYAxis = false;
861 try
862 {
863 coords->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndYAxis;
864 }
865 catch( const beans::UnknownPropertyException & ) {}
866
867 // add ranges for error bars if present for a series
868 if( StatisticsHelper::usesErrorBarRanges( dataSeries ))
869 addErrorBarRanges( dataSeries, nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, true );
870
871 if( StatisticsHelper::usesErrorBarRanges( dataSeries, /* bYError = */ false ))
872 addErrorBarRanges( dataSeries, nYAxisNumberFormatKey, nSeqIdx, nHeaderEnd, false );
873
874 m_aHeaders.emplace_back(
875 dataSeries,
876 CT,
877 bSwapXAndYAxis,
878 nHeaderStart,
879 nHeaderEnd - 1 );
880
881 nHeaderStart = nHeaderEnd;
882
883 std::sort( m_aColumns.begin() + nStartColIndex, m_aColumns.end(), implColumnLess() );
884 }
885 }
886 }
887 }
888}
889
891 const rtl::Reference< DataSeries > & xDataSeries,
892 sal_Int32 nNumberFormatKey,
893 sal_Int32 & rInOutSequenceIndex,
894 sal_Int32 & rInOutHeaderEnd, bool bYError )
895{
896 try
897 {
898 std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences;
899
901 StatisticsHelper::getErrorBars( xDataSeries, bYError ), uno::UNO_QUERY );
902
905 xErrorSource,
906 /* bPositiveValue = */ true,
907 bYError );
908 if( xErrorLSequence.is())
909 aSequences.push_back( xErrorLSequence );
910
911 xErrorLSequence =
913 xErrorSource,
914 /* bPositiveValue = */ false,
915 bYError );
916 if( xErrorLSequence.is())
917 aSequences.push_back( xErrorLSequence );
918
919 for (uno::Reference<chart2::data::XLabeledDataSequence> const & rDataSequence : aSequences)
920 {
921 m_aColumns.emplace_back(xDataSeries, lcl_getUIRoleName(rDataSequence),
922 rDataSequence, NUMBER, nNumberFormatKey);
923 ++rInOutSequenceIndex;
924 ++rInOutHeaderEnd;
925 }
926 }
927 catch( const uno::Exception & )
928 {
929 DBG_UNHANDLED_EXCEPTION("chart2");
930 }
931}
932
933} // namespace chart
934
935/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString m_aValuesRep
rtl::Reference< ::chart::DataSeries > m_xSeries
OUString m_aRole
static rtl::Reference< ::chart::BaseCoordinateSystem > getFirstCoordinateSystem(const rtl::Reference<::chart::ChartModel > &xModel)
static OUString getRoleOfSequenceForDataLabelNumberFormatDetection(const rtl::Reference< ::chart::ChartType > &xChartType)
This guard calls lockControllers at the given Model in the CTOR and unlockControllers in the DTOR.
void removeDataSeriesOrComplexCategoryLevel(sal_Int32 nAtColumnIndex)
Removes a data series to which the data column with index nAtColumnIndex belongs.
bool setCellAny(sal_Int32 nAtColumn, sal_Int32 nAtRow, const css::uno::Any &aValue)
void addErrorBarRanges(const rtl::Reference<::chart::DataSeries > &xDataSeries, sal_Int32 nNumberFormatKey, sal_Int32 &rInOutSequenceIndex, sal_Int32 &rInOutHeaderEnd, bool bYError)
tDataHeaderVector m_aHeaders
rtl::Reference< ::chart::DataSeries > getDataSeriesByColumn(sal_Int32 nColumn) const
void insertDataSeries(sal_Int32 nAfterColumnIndex)
Inserts a new data series after the data series to which the data column with index nAfterColumnIndex...
void insertDataPointForAllSeries(sal_Int32 nAfterIndex)
std::unique_ptr< DialogModel > m_apDialogModel
void swapDataPointForAllSeries(sal_Int32 nFirstIndex)
sal_uInt32 getNumberFormatKey(sal_Int32 nAtColumn)
tDataHeader getHeaderForSeries(const css::uno::Reference< css::chart2::XDataSeries > &xSeries) const
void swapDataSeries(sal_Int32 nFirstIndex)
Swaps the series to which the data column with index nFirstIndex belongs with the next series (which ...
OUString getRoleOfColumn(sal_Int32 nColumnIndex) const
double getCellNumber(sal_Int32 nAtColumn, sal_Int32 nAtRow)
If getCellType( nAtColumn, nAtRow ) returns TEXT, the result will be Nan.
eCellType getCellType(sal_Int32 nAtColumn) const
void removeDataPointForAllSeries(sal_Int32 nAtIndex)
sal_Int32 getColumnCount() const
void removeComplexCategoryLevel(sal_Int32 nAtColumnIndex)
rtl::Reference<::chart::ChartModel > m_xChartDocument
sal_Int32 getMaxRowCount() const
bool setCellNumber(sal_Int32 nAtColumn, sal_Int32 nAtRow, double fValue)
returns </sal_True> if the number could successfully be set at the given position
css::uno::Any getCellAny(sal_Int32 nAtColumn, sal_Int32 nAtRow)
bool setCellText(sal_Int32 nAtColumn, sal_Int32 nAtRow, const OUString &rText)
returns </sal_True> if the text could successfully be set at the given position
void insertComplexCategoryLevel(sal_Int32 nAfterColumnIndex)
Inserts a new text column for complex categories.
bool isCategoriesColumn(sal_Int32 nColumnIndex) const
tDataColumnVector m_aColumns
DataBrowserModel(const rtl::Reference<::chart::ChartModel > &xChartDoc)
OUString getCellText(sal_Int32 nAtColumn, sal_Int32 nAtRow)
static OUString GetRoleDataLabel()
static OUString ConvertRoleFromInternalToUI(const OUString &rRoleString)
static sal_Int32 GetRoleIndexForSorting(const OUString &rInternalRoleString)
const std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > > & getSplitCategoriesList() const
#define DBG_UNHANDLED_EXCEPTION(...)
sal_Int32 nIndex
OOO_DLLPUBLIC_CHARTTOOLS std::vector< css::uno::Reference< css::chart2::data::XLabeledDataSequence > > getAllDataSequences(const std::vector< rtl::Reference<::chart::DataSeries > > &aSeries)
OOO_DLLPUBLIC_CHARTTOOLS sal_Int32 getNumberFormatKeyFromAxis(const rtl::Reference< ::chart::DataSeries > &xSeries, const rtl::Reference< ::chart::BaseCoordinateSystem > &xCorrespondingCoordinateSystem, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex=-1)
OOO_DLLPUBLIC_CHARTTOOLS OUString getRole(const css::uno::Reference< css::chart2::data::XLabeledDataSequence > &xLabeledDataSequence)
OOO_DLLPUBLIC_CHARTTOOLS css::uno::Reference< css::beans::XPropertySet > getErrorBars(const rtl::Reference< ::chart::DataSeries > &xDataSeries, bool bYError=true)
OOO_DLLPUBLIC_CHARTTOOLS bool usesErrorBarRanges(const rtl::Reference< ::chart::DataSeries > &xDataSeries, bool bYError=true)
OOO_DLLPUBLIC_CHARTTOOLS css::uno::Reference< css::chart2::data::XLabeledDataSequence > getErrorLabeledDataSequenceFromDataSource(const css::uno::Reference< css::chart2::data::XDataSource > &xDataSource, bool bPositiveValue, bool bYError=true)
void copyProperties(const Reference< XPropertySet > &_rxSource, const Reference< XPropertySet > &_rxDest)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
bool operator()(const DataBrowserModel::tDataColumn &rLeft, const DataBrowserModel::tDataColumn &rRight)
rtl::Reference< DataSeries > m_xDataSeries
tDataColumn(rtl::Reference< DataSeries > xDataSeries, OUString aUIRoleName, uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence, eCellType aCellType, sal_Int32 nNumberFormatKey)
uno::Reference< chart2::data::XLabeledDataSequence > m_xLabeledDataSequence
rtl::Reference< ::chart::ChartTypeTemplate > xChartTypeTemplate
Definition: Diagram.hxx:314
constexpr OUStringLiteral CHART_UNONAME_NUMFMT
Definition: unonames.hxx:20
sal_Int32 nLength