1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "DialogModel.hxx"
21 #include <DataBrowserModel.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>
52 using namespace ::com::sun::star
;
54 using ::com::sun::star::uno::Reference
;
55 using ::com::sun::star::uno::Sequence
;
62 const Reference
< chart2::data::XDataSequence
> & xSeq
)
65 Reference
< beans::XPropertySet
> xProp( xSeq
, uno::UNO_QUERY
);
70 xProp
->getPropertyValue( u
"Role"_ustr
) >>= aResult
;
72 catch( const uno::Exception
& )
74 DBG_UNHANDLED_EXCEPTION("chart2");
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
);
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;
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
119 catch( const uno::Exception
& )
121 DBG_UNHANDLED_EXCEPTION("chart2");
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 )
137 for( uno::Reference
< chart2::data::XLabeledDataSequence
> const & labeledDataSeq
: rSeries
[0]->getDataSequences2() )
139 Reference
< chart2::data::XDataSequence
> xValues( labeledDataSeq
->getValues());
141 for( std::size_t nSeriesIdx
=1; nSeriesIdx
<rSeries
.size(); ++nSeriesIdx
)
143 bShared
= lcl_SequenceOfSeriesIsShared( rSeries
[nSeriesIdx
], xValues
);
148 aResult
.push_back( labeledDataSeq
);
154 sal_Int32
lcl_getValuesRepresentationIndex(
155 const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
157 sal_Int32 nResult
= -1;
160 Reference
< chart2::data::XDataSequence
> xSeq( xLSeq
->getValues());
163 OUString
aRep( xSeq
->getSourceRangeRepresentation());
164 nResult
= aRep
.toInt32();
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())
177 bool operator() ( const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
179 if (!xLSeq
.is() || !xLSeq
->getValues().is())
182 return xLSeq
->getValues()->getSourceRangeRepresentation() == m_aValuesRep
;
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
;
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
;
217 tDataColumn() : m_eCellType( TEXT
), m_nNumberFormatKey( 0 ) {}
220 rtl::Reference
<DataSeries
> xDataSeries
,
221 OUString aUIRoleName
,
222 uno::Reference
<chart2::data::XLabeledDataSequence
> xLabeledDataSequence
,
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
));
246 DataBrowserModel::DataBrowserModel(
247 const rtl::Reference
<::chart::ChartModel
> & xChartDoc
) :
248 m_xChartDocument( xChartDoc
),
249 m_apDialogModel( new DialogModel( xChartDoc
))
254 DataBrowserModel::~DataBrowserModel()
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
);
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())
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;
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
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.
311 xSeries
->getPropertyValue(CHART_UNONAME_NUMFMT
) >>= nSeriesNumberFormat
;
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())
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.
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());
351 // Insert a new column in the internal data for the new sequence.
352 xDataProvider
->insertSequence( nIndex
- 1 );
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
);
363 Reference
< chart2::data::XDataSequence
> xNewLabelSeq(
364 xDataProvider
->createDataSequenceByRangeRepresentation(
366 OUString::number( nIndex
)));
367 lcl_copyDataSequenceProperties(
368 aLSequences
[nSeqIdx
]->getLabel(), xNewLabelSeq
);
369 aLSequences
[nSeqIdx
]->setLabel( xNewLabelSeq
);
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
));
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())
392 if( !isCategoriesColumn(nAfterColumnIndex
) )
393 nAfterColumnIndex
= getCategoryColumnCount()-1;
395 if(nAfterColumnIndex
<0)
397 OSL_FAIL( "wrong index for category level insertion" );
401 m_apDialogModel
->startControllerLockTimer();
402 ControllerLockGuardUNO
aLockedControllers( m_xChartDocument
);
403 xDataProvider
->insertComplexCategoryLevel( nAfterColumnIndex
+1 );
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())
416 m_apDialogModel
->startControllerLockTimer();
417 ControllerLockGuardUNO
aLockedControllers( m_xChartDocument
);
418 xDataProvider
->deleteComplexCategoryLevel( nAtColumnIndex
);
423 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex
)
425 OSL_ASSERT(m_apDialogModel
);
426 if (nAtColumnIndex
< 0 || o3tl::make_unsigned(nAtColumnIndex
) >= m_aColumns
.size())
430 if (isCategoriesColumn(nAtColumnIndex
))
432 removeComplexCategoryLevel(nAtColumnIndex
);
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.
451 rtl::Reference
<ChartType
> xSeriesCnt(getHeaderForSeries(xSeries
).m_xChartType
);
452 if (!xSeriesCnt
.is())
454 // Unexpected happened. Bail out.
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
)
484 xDataProvider
->deleteSequence( *aIt
);
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
);
498 m_apDialogModel
->moveSeries( xSeries
, DialogModel::MoveDirection::Down
);
504 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex
)
506 OSL_ASSERT(m_apDialogModel
);
507 Reference
< chart2::XInternalDataProvider
> xDataProvider(
508 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
510 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
511 if( xDataProvider
.is())
512 xDataProvider
->swapDataPointWithNextOneForAllSequences( nFirstIndex
);
516 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex
)
518 Reference
< chart2::XInternalDataProvider
> xDataProvider(
519 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
521 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
522 if( xDataProvider
.is())
523 xDataProvider
->insertDataPointForAllSequences( nAfterIndex
);
527 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex
)
529 Reference
< chart2::XInternalDataProvider
> xDataProvider(
530 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
532 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
533 if( xDataProvider
.is())
534 xDataProvider
->deleteDataPointForAllSequences( nAtIndex
);
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
)
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
;
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
;
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
);
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
)
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() );
599 Sequence
< uno::Any
> aValues( xData
->getData());
600 if( nAtRow
< aValues
.getLength())
601 aResult
= aValues
[nAtRow
];
607 OUString
DataBrowserModel::getCellText( sal_Int32 nAtColumn
, sal_Int32 nAtRow
)
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
);
619 Sequence
< OUString
> aValues( xData
->getTextualData());
620 if( nAtRow
< aValues
.getLength())
621 aResult
= aValues
[nAtRow
];
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
;
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())
645 ControllerLockGuardUNO
aLockedControllers( m_xChartDocument
);
650 Reference
< container::XIndexReplace
> xIndexReplace(
651 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getLabel(), uno::UNO_QUERY_THROW
);
652 xIndexReplace
->replaceByIndex( 0, rValue
);
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
& )
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());
702 sal_Int32
nLength( xSeq
->getData().getLength());
703 if( nLength
> nResult
)
711 const 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 EMPTY_OUSTRING
;
719 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex
) const
721 if (nColumnIndex
< 0)
724 if (o3tl::make_unsigned(nColumnIndex
) >= m_aColumns
.size())
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
++;
741 return nLastTextColumnIndex
+1;
744 void DataBrowserModel::updateFromModel()
746 if( !m_xChartDocument
.is())
751 rtl::Reference
< Diagram
> xDiagram( m_xChartDocument
->getFirstChartDiagram());
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 const Reference
< chart2::data::XLabeledDataSequence
>& xCategories( rSplitCategoriesList
[nL
] );
772 if( !xCategories
.is() )
775 tDataColumn aCategories
;
776 aCategories
.m_xLabeledDataSequence
= xCategories
;
777 if( lcl_ShowCategoriesAsDataLabel( xDiagram
))
778 aCategories
.m_aUIRoleName
= DialogModel::GetRoleDataLabel();
780 aCategories
.m_aUIRoleName
= lcl_getUIRoleName( xCategories
);
781 aCategories
.m_eCellType
= TEXTORDATE
;
782 m_aColumns
.push_back( aCategories
);
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 const 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
);
815 for( rtl::Reference
< DataSeries
> const & dataSeries
: aSeries
)
817 tDataColumnVector::size_type nStartColIndex
= m_aColumns
.size();
818 const rtl::Reference
< DataSeries
>& xSeries( dataSeries
);
821 const std::vector
< uno::Reference
< chart2::data::XLabeledDataSequence
> > & aLSeqs( xSeries
->getDataSequences2());
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 );
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(
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(
851 lcl_getUIRoleName( aLSeqs
[nSeqIdx
] ),
854 nSequenceNumberFormatKey
);
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(
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(
905 /* bPositiveValue = */ true,
907 if( xErrorLSequence
.is())
908 aSequences
.push_back( xErrorLSequence
);
911 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
913 /* bPositiveValue = */ false,
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
;
926 catch( const uno::Exception
& )
928 DBG_UNHANDLED_EXCEPTION("chart2");
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */