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