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 "DataBrowserModel.hxx"
21 #include "DialogModel.hxx"
22 #include <ChartModelHelper.hxx>
23 #include <DiagramHelper.hxx>
24 #include <DataSeriesHelper.hxx>
25 #include <PropertyHelper.hxx>
26 #include <ControllerLockGuard.hxx>
27 #include <StatisticsHelper.hxx>
28 #include <ChartTypeHelper.hxx>
29 #include <chartview/ExplicitValueProvider.hxx>
30 #include <ExplicitCategoriesProvider.hxx>
32 #include <ChartModel.hxx>
33 #include <unonames.hxx>
35 #include <com/sun/star/container/XIndexReplace.hpp>
36 #include <com/sun/star/chart2/XAxis.hpp>
37 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
38 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
39 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
40 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
41 #include <com/sun/star/chart2/data/XDataSource.hpp>
42 #include <com/sun/star/chart2/data/XDataSink.hpp>
43 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
44 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
45 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
46 #include <com/sun/star/util/XModifiable.hpp>
47 #include <tools/diagnose_ex.h>
49 #include <rtl/math.hxx>
53 using namespace ::com::sun::star
;
55 using ::com::sun::star::uno::Reference
;
56 using ::com::sun::star::uno::Sequence
;
63 const Reference
< chart2::data::XDataSequence
> & xSeq
)
66 Reference
< beans::XPropertySet
> xProp( xSeq
, uno::UNO_QUERY
);
71 xProp
->getPropertyValue( "Role" ) >>= aResult
;
73 catch( const uno::Exception
& )
75 DBG_UNHANDLED_EXCEPTION("chart2");
81 OUString
lcl_getUIRoleName(
82 const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
84 OUString aResult
= DataSeriesHelper::getRole(xLSeq
);
85 if( !aResult
.isEmpty())
86 aResult
= DialogModel::ConvertRoleFromInternalToUI(aResult
);
90 void lcl_copyDataSequenceProperties(
91 const Reference
< chart2::data::XDataSequence
> & xOldSequence
,
92 const Reference
< chart2::data::XDataSequence
> & xNewSequence
)
94 Reference
< beans::XPropertySet
> xOldSeqProp( xOldSequence
, uno::UNO_QUERY
);
95 Reference
< beans::XPropertySet
> xNewSeqProp( xNewSequence
, uno::UNO_QUERY
);
96 comphelper::copyProperties( xOldSeqProp
, xNewSeqProp
);
99 bool lcl_SequenceOfSeriesIsShared(
100 const Reference
< chart2::XDataSeries
> & xSeries
,
101 const Reference
< chart2::data::XDataSequence
> & xValues
)
103 bool bResult
= false;
108 OUString
aValuesRole( lcl_getRole( xValues
));
109 OUString
aValuesRep( xValues
->getSourceRangeRepresentation());
110 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY_THROW
);
111 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aLSeq( xSource
->getDataSequences());
112 for( sal_Int32 i
=0; i
<aLSeq
.getLength(); ++i
)
113 if (aLSeq
[i
].is() && DataSeriesHelper::getRole(aLSeq
[i
]) == aValuesRole
)
115 // getValues().is(), because lcl_getRole checked that already
116 bResult
= (aValuesRep
== aLSeq
[i
]->getValues()->getSourceRangeRepresentation());
117 // assumption: a role appears only once in a series
121 catch( const uno::Exception
& )
123 DBG_UNHANDLED_EXCEPTION("chart2");
128 typedef std::vector
< Reference
< chart2::data::XLabeledDataSequence
> > lcl_tSharedSeqVec
;
130 lcl_tSharedSeqVec
lcl_getSharedSequences( const Sequence
< Reference
< chart2::XDataSeries
> > & rSeries
)
132 // @todo: if only some series share a sequence, those have to be duplicated
133 // and made unshared for all series
134 lcl_tSharedSeqVec aResult
;
135 // if we have only one series, we don't want any shared sequences
136 if( rSeries
.getLength() <= 1 )
139 Reference
< chart2::data::XDataSource
> xSource( rSeries
[0], uno::UNO_QUERY
);
140 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aLSeq( xSource
->getDataSequences());
141 for( sal_Int32 nIdx
=0; nIdx
<aLSeq
.getLength(); ++nIdx
)
143 Reference
< chart2::data::XDataSequence
> xValues( aLSeq
[nIdx
]->getValues());
145 for( sal_Int32 nSeriesIdx
=1; nSeriesIdx
<rSeries
.getLength(); ++nSeriesIdx
)
147 bShared
= lcl_SequenceOfSeriesIsShared( rSeries
[nSeriesIdx
], xValues
);
152 aResult
.push_back( aLSeq
[nIdx
] );
158 sal_Int32
lcl_getValuesRepresentationIndex(
159 const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
161 sal_Int32 nResult
= -1;
164 Reference
< chart2::data::XDataSequence
> xSeq( xLSeq
->getValues());
167 OUString
aRep( xSeq
->getSourceRangeRepresentation());
168 nResult
= aRep
.toInt32();
174 struct lcl_RepresentationsOfLSeqMatch
176 explicit lcl_RepresentationsOfLSeqMatch( const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
) :
177 m_aValuesRep( xLSeq
.is() ?
178 (xLSeq
->getValues().is() ? xLSeq
->getValues()->getSourceRangeRepresentation() : OUString())
181 bool operator() ( const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
183 if (!xLSeq
.is() || !xLSeq
->getValues().is())
186 return xLSeq
->getValues()->getSourceRangeRepresentation() == m_aValuesRep
;
189 OUString m_aValuesRep
;
192 struct lcl_RolesOfLSeqMatch
194 explicit lcl_RolesOfLSeqMatch( const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
) :
195 m_aRole(DataSeriesHelper::getRole(xLSeq
)) {}
197 bool operator() ( const Reference
< chart2::data::XLabeledDataSequence
> & xLSeq
)
199 return DataSeriesHelper::getRole(xLSeq
) == m_aRole
;
205 bool lcl_ShowCategoriesAsDataLabel( const Reference
< chart2::XDiagram
> & xDiagram
)
207 return !DiagramHelper::isCategoryDiagram(xDiagram
);
210 } // anonymous namespace
212 struct DataBrowserModel::tDataColumn
214 uno::Reference
<chart2::XDataSeries
> m_xDataSeries
;
215 OUString m_aUIRoleName
;
216 uno::Reference
<chart2::data::XLabeledDataSequence
> m_xLabeledDataSequence
;
217 eCellType m_eCellType
;
218 sal_Int32 m_nNumberFormatKey
;
221 tDataColumn() : m_eCellType( TEXT
), m_nNumberFormatKey( 0 ) {}
224 const uno::Reference
<chart2::XDataSeries
> & xDataSeries
,
225 const OUString
& aUIRoleName
,
226 const uno::Reference
<chart2::data::XLabeledDataSequence
>& xLabeledDataSequence
,
228 sal_Int32 nNumberFormatKey
) :
229 m_xDataSeries( xDataSeries
),
230 m_aUIRoleName( aUIRoleName
),
231 m_xLabeledDataSequence( xLabeledDataSequence
),
232 m_eCellType( aCellType
),
233 m_nNumberFormatKey( nNumberFormatKey
)
237 struct DataBrowserModel::implColumnLess
239 bool operator() ( const DataBrowserModel::tDataColumn
& rLeft
, const DataBrowserModel::tDataColumn
& rRight
)
241 if( rLeft
.m_xLabeledDataSequence
.is() && rRight
.m_xLabeledDataSequence
.is())
243 return DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rLeft
.m_xLabeledDataSequence
)) <
244 DialogModel::GetRoleIndexForSorting(DataSeriesHelper::getRole(rRight
.m_xLabeledDataSequence
));
250 DataBrowserModel::DataBrowserModel(
251 const Reference
< chart2::XChartDocument
> & xChartDoc
,
252 const Reference
< uno::XComponentContext
> & xContext
) :
253 m_xChartDocument( xChartDoc
),
254 m_apDialogModel( new DialogModel( xChartDoc
, xContext
))
259 DataBrowserModel::~DataBrowserModel()
264 struct lcl_DataSeriesOfHeaderMatches
266 explicit lcl_DataSeriesOfHeaderMatches(
267 const Reference
< chart2::XDataSeries
> & xSeriesToCompareWith
) :
268 m_xSeries( xSeriesToCompareWith
)
270 bool operator() ( const ::chart::DataBrowserModel::tDataHeader
& rHeader
)
272 return (m_xSeries
== rHeader
.m_xDataSeries
);
275 Reference
< chart2::XDataSeries
> m_xSeries
;
279 void DataBrowserModel::insertDataSeries( sal_Int32 nAfterColumnIndex
)
281 OSL_ASSERT( m_apDialogModel
.get());
282 Reference
< chart2::XInternalDataProvider
> xDataProvider(
283 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
285 if (!xDataProvider
.is())
288 if( isCategoriesColumn(nAfterColumnIndex
) )
289 // Move to the last category column.
290 nAfterColumnIndex
= getCategoryColumnCount()-1;
292 sal_Int32 nStartCol
= 0;
293 Reference
<chart2::XDiagram
> xDiagram
= ChartModelHelper::findDiagram(m_xChartDocument
);
294 Reference
<chart2::XChartType
> xChartType
;
295 Reference
<chart2::XDataSeries
> xSeries
;
296 if (static_cast<size_t>(nAfterColumnIndex
) < m_aColumns
.size())
297 // Get the data series at specific column position (if available).
298 xSeries
.set( m_aColumns
[nAfterColumnIndex
].m_xDataSeries
);
300 sal_Int32 nSeriesNumberFormat
= 0;
303 // Use the chart type of the currently selected data series.
304 xChartType
.set( DiagramHelper::getChartTypeOfSeries( xDiagram
, xSeries
));
306 // Find the corresponding header and determine the last column of this
308 tDataHeaderVector::const_iterator
aIt(
309 std::find_if( m_aHeaders
.begin(), m_aHeaders
.end(),
310 lcl_DataSeriesOfHeaderMatches( xSeries
)));
311 if( aIt
!= m_aHeaders
.end())
312 nStartCol
= aIt
->m_nEndColumn
;
314 // Get the number format too.
315 Reference
< beans::XPropertySet
> xSeriesProps( xSeries
, uno::UNO_QUERY
);
316 if( xSeriesProps
.is() )
317 xSeriesProps
->getPropertyValue(CHART_UNONAME_NUMFMT
) >>= nSeriesNumberFormat
;
321 // No data series at specified column position. Use the first chart type.
322 xChartType
.set( DiagramHelper::getChartTypeByIndex( xDiagram
, 0 ));
323 nStartCol
= nAfterColumnIndex
;
326 if (!xChartType
.is())
329 // Get shared sequences of current series. Normally multiple data series
330 // only share "values-x" sequences. (TODO: simplify this logic).
331 Reference
< chart2::XDataSeriesContainer
> xSeriesCnt( xChartType
, uno::UNO_QUERY
);
332 lcl_tSharedSeqVec aSharedSequences
;
334 aSharedSequences
= lcl_getSharedSequences( xSeriesCnt
->getDataSeries());
336 Reference
<chart2::XDataSeries
> xNewSeries
=
337 m_apDialogModel
->insertSeriesAfter(xSeries
, xChartType
, true);
339 if (!xNewSeries
.is())
340 // Failed to insert new data series to the model. Bail out.
343 Reference
< chart2::data::XDataSource
> xSource( xNewSeries
, uno::UNO_QUERY
);
346 Sequence
<Reference
<chart2::data::XLabeledDataSequence
> > aLSequences
= xSource
->getDataSequences();
347 sal_Int32 nSeqIdx
= 0;
348 sal_Int32 nSeqSize
= aLSequences
.getLength();
349 for (sal_Int32 nIndex
= nStartCol
; nSeqIdx
< nSeqSize
; ++nSeqIdx
)
351 lcl_tSharedSeqVec::const_iterator
aSharedIt(
352 std::find_if( aSharedSequences
.begin(), aSharedSequences
.end(),
353 lcl_RolesOfLSeqMatch( aLSequences
[nSeqIdx
] )));
355 if( aSharedIt
!= aSharedSequences
.end())
357 // Shared sequence. Most likely "values-x" sequence. Copy it from existing sequence.
358 aLSequences
[nSeqIdx
]->setValues( (*aSharedIt
)->getValues());
359 aLSequences
[nSeqIdx
]->setLabel( (*aSharedIt
)->getLabel());
363 // Insert a new column in the internal data for the new sequence.
364 xDataProvider
->insertSequence( nIndex
- 1 );
367 Reference
< chart2::data::XDataSequence
> xNewSeq(
368 xDataProvider
->createDataSequenceByRangeRepresentation(
369 OUString::number( nIndex
)));
370 lcl_copyDataSequenceProperties(
371 aLSequences
[nSeqIdx
]->getValues(), xNewSeq
);
372 aLSequences
[nSeqIdx
]->setValues( xNewSeq
);
375 Reference
< chart2::data::XDataSequence
> xNewLabelSeq(
376 xDataProvider
->createDataSequenceByRangeRepresentation(
378 OUString::number( nIndex
)));
379 lcl_copyDataSequenceProperties(
380 aLSequences
[nSeqIdx
]->getLabel(), xNewLabelSeq
);
381 aLSequences
[nSeqIdx
]->setLabel( xNewLabelSeq
);
387 if( nSeriesNumberFormat
!= 0 )
389 //give the new series the same number format as the former series especially for bubble charts thus the bubble size values can be edited with same format immediately
390 Reference
< beans::XPropertySet
> xNewSeriesProps( xNewSeries
, uno::UNO_QUERY
);
391 if( xNewSeriesProps
.is() )
392 xNewSeriesProps
->setPropertyValue(CHART_UNONAME_NUMFMT
, uno::Any(nSeriesNumberFormat
));
398 void DataBrowserModel::insertComplexCategoryLevel( sal_Int32 nAfterColumnIndex
)
400 //create a new text column for complex categories
402 OSL_ASSERT( m_apDialogModel
.get());
403 Reference
< chart2::XInternalDataProvider
> xDataProvider( m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
404 if (!xDataProvider
.is())
407 if( !isCategoriesColumn(nAfterColumnIndex
) )
408 nAfterColumnIndex
= getCategoryColumnCount()-1;
410 if(nAfterColumnIndex
<0)
412 OSL_FAIL( "wrong index for category level insertion" );
416 m_apDialogModel
->startControllerLockTimer();
417 ControllerLockGuardUNO
aLockedControllers( Reference
< frame::XModel
>( m_xChartDocument
, uno::UNO_QUERY
) );
418 xDataProvider
->insertComplexCategoryLevel( nAfterColumnIndex
+1 );
422 void DataBrowserModel::removeComplexCategoryLevel( sal_Int32 nAtColumnIndex
)
424 //delete a category column if there is more than one level (in case of a single column we do not get here)
425 OSL_ENSURE(nAtColumnIndex
>0, "wrong index for categories deletion" );
427 Reference
< chart2::XInternalDataProvider
> xDataProvider( m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
428 if (!xDataProvider
.is())
431 m_apDialogModel
->startControllerLockTimer();
432 ControllerLockGuardUNO
aLockedControllers( Reference
< frame::XModel
>( m_xChartDocument
, uno::UNO_QUERY
) );
433 xDataProvider
->deleteComplexCategoryLevel( nAtColumnIndex
);
438 void DataBrowserModel::removeDataSeriesOrComplexCategoryLevel( sal_Int32 nAtColumnIndex
)
440 OSL_ASSERT( m_apDialogModel
.get());
441 if (nAtColumnIndex
< 0 || static_cast<size_t>(nAtColumnIndex
) >= m_aColumns
.size())
445 if (isCategoriesColumn(nAtColumnIndex
))
447 removeComplexCategoryLevel(nAtColumnIndex
);
451 const Reference
<chart2::XDataSeries
>& xSeries
= m_aColumns
[nAtColumnIndex
].m_xDataSeries
;
453 m_apDialogModel
->deleteSeries(xSeries
, getHeaderForSeries(xSeries
).m_xChartType
);
455 //delete sequences from internal data provider that are not used anymore
456 //but do not delete sequences that are still in use by the remaining series
458 Reference
< chart2::XInternalDataProvider
> xDataProvider( m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
459 Reference
< chart2::data::XDataSource
> xSourceOfDeleted( xSeries
, uno::UNO_QUERY
);
460 if (!xDataProvider
.is() || !xSourceOfDeleted
.is())
462 // Something went wrong. Bail out.
467 Reference
<chart2::XDataSeriesContainer
> xSeriesCnt(
468 getHeaderForSeries(xSeries
).m_xChartType
, uno::UNO_QUERY
);
469 if (!xSeriesCnt
.is())
471 // Unexpected happened. Bail out.
476 // Collect all the remaining data sequences in the same chart type. The
477 // deleted data series is already gone by this point.
478 std::vector
<Reference
<chart2::data::XLabeledDataSequence
> > aAllDataSeqs
=
479 DataSeriesHelper::getAllDataSequences(xSeriesCnt
->getDataSeries());
481 // Check if the sequences to be deleted are still referenced by any of
482 // the other data series. If not, mark them for deletion.
483 std::vector
<sal_Int32
> aSequenceIndexesToDelete
;
484 Sequence
<Reference
<chart2::data::XLabeledDataSequence
> > aSequencesOfDeleted
= xSourceOfDeleted
->getDataSequences();
485 for (sal_Int32 i
= 0; i
< aSequencesOfDeleted
.getLength(); ++i
)
487 std::vector
<Reference
<chart2::data::XLabeledDataSequence
> >::const_iterator
aHitIt(
488 std::find_if( aAllDataSeqs
.begin(), aAllDataSeqs
.end(),
489 lcl_RepresentationsOfLSeqMatch( aSequencesOfDeleted
[i
] )));
490 // if not used by the remaining series this sequence can be deleted
491 if( aHitIt
== aAllDataSeqs
.end() )
492 aSequenceIndexesToDelete
.push_back( lcl_getValuesRepresentationIndex( aSequencesOfDeleted
[i
] ) );
495 // delete unnecessary sequences of the internal data
496 // iterate using greatest index first, so that deletion does not
497 // shift other sequences that will be deleted later
498 std::sort( aSequenceIndexesToDelete
.begin(), aSequenceIndexesToDelete
.end());
499 for( std::vector
< sal_Int32
>::reverse_iterator
aIt(
500 aSequenceIndexesToDelete
.rbegin()); aIt
!= aSequenceIndexesToDelete
.rend(); ++aIt
)
503 xDataProvider
->deleteSequence( *aIt
);
509 void DataBrowserModel::swapDataSeries( sal_Int32 nFirstColumnIndex
)
511 OSL_ASSERT( m_apDialogModel
.get());
512 if( static_cast< tDataColumnVector::size_type
>( nFirstColumnIndex
) < m_aColumns
.size() - 1 )
514 Reference
< chart2::XDataSeries
> xSeries( m_aColumns
[nFirstColumnIndex
].m_xDataSeries
);
517 m_apDialogModel
->moveSeries( xSeries
, DialogModel::MOVE_DOWN
);
523 void DataBrowserModel::swapDataPointForAllSeries( sal_Int32 nFirstIndex
)
525 OSL_ASSERT( m_apDialogModel
.get());
526 Reference
< chart2::XInternalDataProvider
> xDataProvider(
527 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
529 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
530 if( xDataProvider
.is())
531 xDataProvider
->swapDataPointWithNextOneForAllSequences( nFirstIndex
);
535 void DataBrowserModel::insertDataPointForAllSeries( sal_Int32 nAfterIndex
)
537 Reference
< chart2::XInternalDataProvider
> xDataProvider(
538 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
540 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
541 if( xDataProvider
.is())
542 xDataProvider
->insertDataPointForAllSequences( nAfterIndex
);
546 void DataBrowserModel::removeDataPointForAllSeries( sal_Int32 nAtIndex
)
548 Reference
< chart2::XInternalDataProvider
> xDataProvider(
549 m_apDialogModel
->getDataProvider(), uno::UNO_QUERY
);
551 ControllerLockGuardUNO
aGuard( m_apDialogModel
->getChartModel());
552 if( xDataProvider
.is())
553 xDataProvider
->deleteDataPointForAllSequences( nAtIndex
);
557 DataBrowserModel::tDataHeader
DataBrowserModel::getHeaderForSeries(
558 const Reference
< chart2::XDataSeries
> & xSeries
) const
560 for (auto const& elemHeader
: m_aHeaders
)
562 if( elemHeader
.m_xDataSeries
== xSeries
)
565 return tDataHeader();
568 Reference
< chart2::XDataSeries
>
569 DataBrowserModel::getDataSeriesByColumn( sal_Int32 nColumn
) const
571 tDataColumnVector::size_type
nIndex( nColumn
);
572 if( nIndex
< m_aColumns
.size())
573 return m_aColumns
[nIndex
].m_xDataSeries
;
577 DataBrowserModel::eCellType
DataBrowserModel::getCellType( sal_Int32 nAtColumn
) const
579 eCellType eResult
= TEXT
;
580 tDataColumnVector::size_type
nIndex( nAtColumn
);
581 if( nIndex
< m_aColumns
.size())
582 eResult
= m_aColumns
[nIndex
].m_eCellType
;
586 double DataBrowserModel::getCellNumber( sal_Int32 nAtColumn
, sal_Int32 nAtRow
)
589 ::rtl::math::setNan( & fResult
);
591 tDataColumnVector::size_type
nIndex( nAtColumn
);
592 if( nIndex
< m_aColumns
.size() &&
593 m_aColumns
[ nIndex
].m_xLabeledDataSequence
.is())
595 Reference
< chart2::data::XNumericalDataSequence
> xData(
596 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getValues(), uno::UNO_QUERY
);
599 Sequence
< double > aValues( xData
->getNumericalData());
600 if( nAtRow
< aValues
.getLength())
601 fResult
= aValues
[nAtRow
];
607 uno::Any
DataBrowserModel::getCellAny( 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::XDataSequence
> xData(
616 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getValues() );
619 Sequence
< uno::Any
> aValues( xData
->getData());
620 if( nAtRow
< aValues
.getLength())
621 aResult
= aValues
[nAtRow
];
627 OUString
DataBrowserModel::getCellText( sal_Int32 nAtColumn
, sal_Int32 nAtRow
)
631 tDataColumnVector::size_type
nIndex( nAtColumn
);
632 if( nIndex
< m_aColumns
.size() &&
633 m_aColumns
[ nIndex
].m_xLabeledDataSequence
.is())
635 Reference
< chart2::data::XTextualDataSequence
> xData(
636 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getValues(), uno::UNO_QUERY
);
639 Sequence
< OUString
> aValues( xData
->getTextualData());
640 if( nAtRow
< aValues
.getLength())
641 aResult
= aValues
[nAtRow
];
647 sal_uInt32
DataBrowserModel::getNumberFormatKey( sal_Int32 nAtColumn
)
649 tDataColumnVector::size_type
nIndex( nAtColumn
);
650 if( nIndex
< m_aColumns
.size())
651 return m_aColumns
[ nIndex
].m_nNumberFormatKey
;
655 bool DataBrowserModel::setCellAny( sal_Int32 nAtColumn
, sal_Int32 nAtRow
, const uno::Any
& rValue
)
657 bool bResult
= false;
658 tDataColumnVector::size_type
nIndex( nAtColumn
);
659 if( nIndex
< m_aColumns
.size() &&
660 m_aColumns
[ nIndex
].m_xLabeledDataSequence
.is())
665 ControllerLockGuardUNO
aLockedControllers( Reference
< frame::XModel
>( m_xChartDocument
, uno::UNO_QUERY
) );
670 Reference
< container::XIndexReplace
> xIndexReplace(
671 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getLabel(), uno::UNO_QUERY_THROW
);
672 xIndexReplace
->replaceByIndex( 0, rValue
);
676 Reference
< container::XIndexReplace
> xIndexReplace(
677 m_aColumns
[ nIndex
].m_xLabeledDataSequence
->getValues(), uno::UNO_QUERY_THROW
);
678 xIndexReplace
->replaceByIndex( nAtRow
, rValue
);
681 m_apDialogModel
->startControllerLockTimer();
682 //notify change directly to the model (this is necessary here as sequences for complex categories not known directly to the chart model so they do not notify their changes) (for complex categories see issue #i82971#)
683 Reference
< util::XModifiable
> xModifiable( m_xChartDocument
, uno::UNO_QUERY
);
684 if( xModifiable
.is() )
685 xModifiable
->setModified(true);
687 catch( const uno::Exception
& )
695 bool DataBrowserModel::setCellNumber( sal_Int32 nAtColumn
, sal_Int32 nAtRow
, double fValue
)
697 return (getCellType( nAtColumn
) == NUMBER
) &&
698 setCellAny( nAtColumn
, nAtRow
, uno::Any( fValue
));
701 bool DataBrowserModel::setCellText( sal_Int32 nAtColumn
, sal_Int32 nAtRow
, const OUString
& rText
)
703 return (getCellType( nAtColumn
) == TEXT
) &&
704 setCellAny( nAtColumn
, nAtRow
, uno::Any( rText
));
707 sal_Int32
DataBrowserModel::getColumnCount() const
709 return static_cast< sal_Int32
>( m_aColumns
.size());
712 sal_Int32
DataBrowserModel::getMaxRowCount() const
714 sal_Int32 nResult
= 0;
715 for (auto const& column
: m_aColumns
)
717 if( column
.m_xLabeledDataSequence
.is())
719 Reference
< chart2::data::XDataSequence
> xSeq(
720 column
.m_xLabeledDataSequence
->getValues());
723 sal_Int32
nLength( xSeq
->getData().getLength());
724 if( nLength
> nResult
)
732 OUString
DataBrowserModel::getRoleOfColumn( sal_Int32 nColumnIndex
) const
734 if( nColumnIndex
!= -1 &&
735 static_cast< sal_uInt32
>( nColumnIndex
) < m_aColumns
.size())
736 return m_aColumns
[ nColumnIndex
].m_aUIRoleName
;
740 bool DataBrowserModel::isCategoriesColumn( sal_Int32 nColumnIndex
) const
742 if (nColumnIndex
< 0)
745 if (static_cast<size_t>(nColumnIndex
) >= m_aColumns
.size())
748 // A column is a category when it doesn't have an associated data series.
749 return !m_aColumns
[nColumnIndex
].m_xDataSeries
.is();
752 sal_Int32
DataBrowserModel::getCategoryColumnCount()
754 sal_Int32 nLastTextColumnIndex
= -1;
755 for (auto const& column
: m_aColumns
)
757 if( !column
.m_xDataSeries
.is() )
758 nLastTextColumnIndex
++;
762 return nLastTextColumnIndex
+1;
765 void DataBrowserModel::updateFromModel()
767 if( !m_xChartDocument
.is())
772 Reference
< chart2::XDiagram
> xDiagram( ChartModelHelper::findDiagram( m_xChartDocument
));
776 // set template at DialogModel
777 uno::Reference
< lang::XMultiServiceFactory
> xFact( m_xChartDocument
->getChartTypeManager(), uno::UNO_QUERY
);
778 DiagramHelper::tTemplateWithServiceName aTemplateAndService
=
779 DiagramHelper::getTemplateForDiagram( xDiagram
, xFact
);
780 if( aTemplateAndService
.first
.is())
781 m_apDialogModel
->setTemplate( aTemplateAndService
.first
);
783 sal_Int32 nHeaderStart
= 0;
784 sal_Int32 nHeaderEnd
= 0;
786 Reference
< frame::XModel
> xChartModel( m_xChartDocument
, uno::UNO_QUERY
);
787 ChartModel
* pModel
= dynamic_cast<ChartModel
*>(xChartModel
.get());
790 ExplicitCategoriesProvider
aExplicitCategoriesProvider( ChartModelHelper::getFirstCoordinateSystem(xChartModel
), *pModel
);
792 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
> >& rSplitCategoriesList( aExplicitCategoriesProvider
.getSplitCategoriesList() );
793 sal_Int32 nLevelCount
= rSplitCategoriesList
.getLength();
794 for( sal_Int32 nL
= 0; nL
<nLevelCount
; nL
++ )
796 Reference
< chart2::data::XLabeledDataSequence
> xCategories( rSplitCategoriesList
[nL
] );
797 if( !xCategories
.is() )
800 tDataColumn aCategories
;
801 aCategories
.m_xLabeledDataSequence
.set( xCategories
);
802 if( lcl_ShowCategoriesAsDataLabel( xDiagram
))
803 aCategories
.m_aUIRoleName
= DialogModel::GetRoleDataLabel();
805 aCategories
.m_aUIRoleName
= lcl_getUIRoleName( xCategories
);
806 aCategories
.m_eCellType
= TEXTORDATE
;
807 m_aColumns
.push_back( aCategories
);
812 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt( xDiagram
, uno::UNO_QUERY
);
813 if( !xCooSysCnt
.is())
815 Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq( xCooSysCnt
->getCoordinateSystems());
816 for( sal_Int32 nCooSysIdx
=0; nCooSysIdx
<aCooSysSeq
.getLength(); ++nCooSysIdx
)
818 Reference
< chart2::XChartTypeContainer
> xCTCnt( aCooSysSeq
[nCooSysIdx
], uno::UNO_QUERY_THROW
);
819 Sequence
< Reference
< chart2::XChartType
> > aChartTypes( xCTCnt
->getChartTypes());
820 sal_Int32 nXAxisNumberFormat
= DataSeriesHelper::getNumberFormatKeyFromAxis( nullptr, aCooSysSeq
[nCooSysIdx
], 0, 0 );
822 for( sal_Int32 nCTIdx
=0; nCTIdx
<aChartTypes
.getLength(); ++nCTIdx
)
824 Reference
< chart2::XDataSeriesContainer
> xSeriesCnt( aChartTypes
[nCTIdx
], uno::UNO_QUERY
);
827 OUString aRoleForDataLabelNumberFormat
= ChartTypeHelper::getRoleOfSequenceForDataLabelNumberFormatDetection( aChartTypes
[nCTIdx
] );
829 Sequence
< Reference
< chart2::XDataSeries
> > aSeries( xSeriesCnt
->getDataSeries());
830 lcl_tSharedSeqVec
aSharedSequences( lcl_getSharedSequences( aSeries
));
831 for (auto const& sharedSequence
: aSharedSequences
)
833 tDataColumn aSharedSequence
;
834 aSharedSequence
.m_xLabeledDataSequence
= sharedSequence
;
835 aSharedSequence
.m_aUIRoleName
= lcl_getUIRoleName(sharedSequence
);
836 aSharedSequence
.m_eCellType
= NUMBER
;
837 // as the sequences are shared it should be ok to take the first series
838 // @todo: dimension index 0 for x-values used here. This is just a guess.
839 // Also, the axis index is 0, as there is usually only one x-axis
840 aSharedSequence
.m_nNumberFormatKey
= nXAxisNumberFormat
;
841 m_aColumns
.push_back( aSharedSequence
);
844 for( sal_Int32 nSeriesIdx
=0; nSeriesIdx
<aSeries
.getLength(); ++nSeriesIdx
)
846 tDataColumnVector::size_type nStartColIndex
= m_aColumns
.size();
847 Reference
< chart2::XDataSeries
> xSeries( aSeries
[nSeriesIdx
] );
848 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY
);
851 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aLSeqs( xSource
->getDataSequences());
852 if( aLSeqs
.getLength() == 0 )
854 nHeaderEnd
= nHeaderStart
;
856 // @todo: dimension index 1 for y-values used here. This is just a guess
857 sal_Int32 nYAxisNumberFormatKey
=
858 DataSeriesHelper::getNumberFormatKeyFromAxis(
859 aSeries
[nSeriesIdx
], aCooSysSeq
[nCooSysIdx
], 1 );
862 for( ; nSeqIdx
<aLSeqs
.getLength(); ++nSeqIdx
)
864 sal_Int32 nSequenceNumberFormatKey
= nYAxisNumberFormatKey
;
865 OUString aRole
= DataSeriesHelper::getRole(aLSeqs
[nSeqIdx
]);
867 if( aRole
== aRoleForDataLabelNumberFormat
)
869 nSequenceNumberFormatKey
= ExplicitValueProvider::getExplicitNumberFormatKeyForDataLabel(
870 Reference
< beans::XPropertySet
>( xSeries
, uno::UNO_QUERY
), xSeries
, -1, xDiagram
);
872 else if( aRole
== "values-x" )
873 nSequenceNumberFormatKey
= nXAxisNumberFormat
;
875 if( std::find_if( aSharedSequences
.begin(), aSharedSequences
.end(),
876 lcl_RepresentationsOfLSeqMatch( aLSeqs
[nSeqIdx
] )) == aSharedSequences
.end())
878 // no shared sequence
879 m_aColumns
.emplace_back(
881 lcl_getUIRoleName( aLSeqs
[nSeqIdx
] ),
884 nSequenceNumberFormatKey
);
889 bool bSwapXAndYAxis
= false;
892 Reference
< beans::XPropertySet
> xProp( aCooSysSeq
[nCooSysIdx
], uno::UNO_QUERY
);
893 xProp
->getPropertyValue( "SwapXAndYAxis" ) >>= bSwapXAndYAxis
;
895 catch( const beans::UnknownPropertyException
& ) {}
897 // add ranges for error bars if present for a series
898 if( StatisticsHelper::usesErrorBarRanges( aSeries
[nSeriesIdx
] ))
899 addErrorBarRanges( aSeries
[nSeriesIdx
], nYAxisNumberFormatKey
, nSeqIdx
, nHeaderEnd
, true );
901 if( StatisticsHelper::usesErrorBarRanges( aSeries
[nSeriesIdx
], /* bYError = */ false ))
902 addErrorBarRanges( aSeries
[nSeriesIdx
], nYAxisNumberFormatKey
, nSeqIdx
, nHeaderEnd
, false );
904 m_aHeaders
.emplace_back(
911 nHeaderStart
= nHeaderEnd
;
913 std::sort( m_aColumns
.begin() + nStartColIndex
, m_aColumns
.end(), implColumnLess() );
921 void DataBrowserModel::addErrorBarRanges(
922 const Reference
< chart2::XDataSeries
> & xDataSeries
,
923 sal_Int32 nNumberFormatKey
,
924 sal_Int32
& rInOutSequenceIndex
,
925 sal_Int32
& rInOutHeaderEnd
, bool bYError
)
929 std::vector
< Reference
< chart2::data::XLabeledDataSequence
> > aSequences
;
931 Reference
< chart2::data::XDataSource
> xErrorSource(
932 StatisticsHelper::getErrorBars( xDataSeries
, bYError
), uno::UNO_QUERY
);
934 Reference
< chart2::data::XLabeledDataSequence
> xErrorLSequence(
935 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
937 /* bPositiveValue = */ true,
939 if( xErrorLSequence
.is())
940 aSequences
.push_back( xErrorLSequence
);
943 StatisticsHelper::getErrorLabeledDataSequenceFromDataSource(
945 /* bPositiveValue = */ false,
947 if( xErrorLSequence
.is())
948 aSequences
.push_back( xErrorLSequence
);
950 for (Reference
<chart2::data::XLabeledDataSequence
> const & rDataSequence
: aSequences
)
952 m_aColumns
.emplace_back(xDataSeries
, lcl_getUIRoleName(rDataSequence
),
953 rDataSequence
, NUMBER
, nNumberFormatKey
);
954 ++rInOutSequenceIndex
;
958 catch( const uno::Exception
& )
960 DBG_UNHANDLED_EXCEPTION("chart2");
966 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */