tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / chart2 / source / tools / ExplicitCategoriesProvider.cxx
blobd543b2b365fbff372ec8853ec5b5a701e1526689
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ExplicitCategoriesProvider.hxx>
21 #include <DiagramHelper.hxx>
22 #include <ChartType.hxx>
23 #include <ChartTypeHelper.hxx>
24 #include <Axis.hxx>
25 #include <AxisHelper.hxx>
26 #include <DataSourceHelper.hxx>
27 #include <ChartModel.hxx>
28 #include <ChartModelHelper.hxx>
29 #include <NumberFormatterWrapper.hxx>
30 #include <unonames.hxx>
31 #include <BaseCoordinateSystem.hxx>
32 #include <DataSeries.hxx>
34 #include <com/sun/star/chart2/AxisType.hpp>
35 #include <com/sun/star/chart/ChartDataRowSource.hpp>
36 #include <o3tl/compare.hxx>
37 #include <o3tl/safeint.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <comphelper/diagnose_ex.hxx>
41 #include <limits>
43 namespace chart
46 using namespace ::com::sun::star;
47 using namespace ::com::sun::star::chart2;
48 using ::com::sun::star::uno::Reference;
49 using ::com::sun::star::uno::Sequence;
50 using std::vector;
52 ExplicitCategoriesProvider::ExplicitCategoriesProvider( const rtl::Reference< BaseCoordinateSystem >& xCooSysModel
53 , ChartModel& rModel )
54 : m_bDirty(true)
55 , m_xCooSysModel( xCooSysModel.get() )
56 , mrModel(rModel)
57 , m_bIsExplicitCategoriesInited(false)
58 , m_bIsDateAxis(false)
59 , m_bIsAutoDate(false)
61 try
63 if( xCooSysModel.is() )
65 // TODO: handle different category names on the primary and secondary category axis.
66 rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
67 if( xAxis.is() )
69 ScaleData aScale( xAxis->getScaleData() );
70 m_xOriginalCategories = aScale.Categories;
71 m_bIsAutoDate = (aScale.AutoDateAxis && aScale.AxisType==chart2::AxisType::CATEGORY);
72 m_bIsDateAxis = (aScale.AxisType == chart2::AxisType::DATE || m_bIsAutoDate);
76 if( m_xOriginalCategories.is() )
78 uno::Reference< data::XDataProvider > xDataProvider( mrModel.getDataProvider() );
80 OUString aCategoriesRange( DataSourceHelper::getRangeFromValues( m_xOriginalCategories ) );
81 if( xDataProvider.is() && !aCategoriesRange.isEmpty() )
83 const bool bFirstCellAsLabel = false;
84 const bool bHasCategories = false;
85 const uno::Sequence< sal_Int32 > aSequenceMapping;
87 uno::Reference< data::XDataSource > xColumnCategoriesSource( xDataProvider->createDataSource(
88 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, true /*bUseColumns*/
89 , bFirstCellAsLabel, bHasCategories ) ) );
91 uno::Reference< data::XDataSource > xRowCategoriesSource( xDataProvider->createDataSource(
92 DataSourceHelper::createArguments( aCategoriesRange, aSequenceMapping, false /*bUseColumns*/
93 , bFirstCellAsLabel, bHasCategories ) ) );
95 if( xColumnCategoriesSource.is() && xRowCategoriesSource.is() )
97 Sequence< Reference< data::XLabeledDataSequence> > aColumns = xColumnCategoriesSource->getDataSequences();
98 Sequence< Reference< data::XLabeledDataSequence> > aRows = xRowCategoriesSource->getDataSequences();
100 sal_Int32 nColumnCount = aColumns.getLength();
101 sal_Int32 nRowCount = aRows.getLength();
102 if( nColumnCount>1 && nRowCount>1 )
104 //we have complex categories
105 //->split them in the direction of the first series
106 //detect whether the first series is a row or a column
107 bool bSeriesUsesColumns = true;
108 std::vector< rtl::Reference< DataSeries > > aSeries = ChartModelHelper::getDataSeries( &mrModel );
109 if( !aSeries.empty() )
111 const rtl::Reference< DataSeries >& xSeriesSource = aSeries.front();
112 for(const auto& rArgument : xDataProvider->detectArguments( xSeriesSource))
114 if ( rArgument.Name == "DataRowSource" )
116 css::chart::ChartDataRowSource eRowSource;
117 if( rArgument.Value >>= eRowSource )
119 bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
120 break;
125 if( bSeriesUsesColumns )
126 m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aColumns);
127 else
128 m_aSplitCategoriesList = comphelper::sequenceToContainer<std::vector<Reference<data::XLabeledDataSequence>>>(aRows);
132 if( m_aSplitCategoriesList.empty() )
134 m_aSplitCategoriesList = { m_xOriginalCategories };
138 catch( const uno::Exception & )
140 DBG_UNHANDLED_EXCEPTION("chart2");
144 ExplicitCategoriesProvider::~ExplicitCategoriesProvider()
148 Reference< chart2::data::XDataSequence > ExplicitCategoriesProvider::getOriginalCategories()
150 if( m_xOriginalCategories.is() )
151 return m_xOriginalCategories->getValues();
152 return nullptr;
155 bool ExplicitCategoriesProvider::hasComplexCategories() const
157 return m_aSplitCategoriesList.size() > 1;
160 sal_Int32 ExplicitCategoriesProvider::getCategoryLevelCount() const
162 sal_Int32 nCount = m_aSplitCategoriesList.size();
163 if(!nCount)
164 nCount = 1;
165 return nCount;
168 static std::vector<sal_Int32> lcl_getLimitingBorders( const std::vector< ComplexCategory >& rComplexCategories )
170 std::vector<sal_Int32> aLimitingBorders;
171 sal_Int32 nBorderIndex = 0; /*border below the index*/
172 for (auto const& complexCategory : rComplexCategories)
174 nBorderIndex += complexCategory.Count;
175 aLimitingBorders.push_back(nBorderIndex);
177 return aLimitingBorders;
180 void ExplicitCategoriesProvider::convertCategoryAnysToText( uno::Sequence< OUString >& rOutTexts, const uno::Sequence< uno::Any >& rInAnys, ChartModel& rModel )
182 sal_Int32 nCount = rInAnys.getLength();
183 if(!nCount)
184 return;
185 rOutTexts.realloc(nCount);
186 auto pOutTexts = rOutTexts.getArray();
188 sal_Int32 nAxisNumberFormat = 0;
189 rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
190 if( xCooSysModel.is() )
192 rtl::Reference< Axis > xAxis = xCooSysModel->getAxisByDimension2(0,0);
193 nAxisNumberFormat = AxisHelper::getExplicitNumberFormatKeyForAxis(
194 xAxis, xCooSysModel, &rModel, false );
197 Color nLabelColor;
198 bool bColorChanged = false;
200 NumberFormatterWrapper aNumberFormatterWrapper( rModel.getNumberFormatsSupplier() );
202 for(sal_Int32 nN=0;nN<nCount;nN++)
204 OUString aText;
205 const uno::Any& aAny = rInAnys[nN];
206 if( aAny.hasValue() )
208 double fDouble = 0;
209 if( aAny>>=fDouble )
211 if( !std::isnan(fDouble) )
212 aText = aNumberFormatterWrapper.getFormattedString(
213 nAxisNumberFormat, fDouble, nLabelColor, bColorChanged );
215 else
217 aAny>>=aText;
220 pOutTexts[nN] = aText;
224 SplitCategoriesProvider::~SplitCategoriesProvider()
228 namespace {
230 class SplitCategoriesProvider_ForLabeledDataSequences : public SplitCategoriesProvider
232 public:
234 explicit SplitCategoriesProvider_ForLabeledDataSequences(
235 const std::vector< Reference< data::XLabeledDataSequence> >& rSplitCategoriesList
236 , ChartModel& rModel )
237 : m_rSplitCategoriesList( rSplitCategoriesList )
238 , mrModel( rModel )
241 virtual sal_Int32 getLevelCount() const override;
242 virtual uno::Sequence< OUString > getStringsForLevel( sal_Int32 nIndex ) const override;
244 private:
245 const std::vector< Reference< data::XLabeledDataSequence> >& m_rSplitCategoriesList;
247 ChartModel& mrModel;
252 sal_Int32 SplitCategoriesProvider_ForLabeledDataSequences::getLevelCount() const
254 return m_rSplitCategoriesList.size();
256 uno::Sequence< OUString > SplitCategoriesProvider_ForLabeledDataSequences::getStringsForLevel( sal_Int32 nLevel ) const
258 uno::Sequence< OUString > aRet;
259 Reference< data::XLabeledDataSequence > xLabeledDataSequence( m_rSplitCategoriesList[nLevel] );
260 if( xLabeledDataSequence.is() )
262 uno::Reference< data::XDataSequence > xDataSequence( xLabeledDataSequence->getValues() );
263 if( xDataSequence.is() )
264 ExplicitCategoriesProvider::convertCategoryAnysToText( aRet, xDataSequence->getData(), mrModel );
266 return aRet;
269 static std::vector< ComplexCategory > lcl_DataSequenceToComplexCategoryVector(
270 const uno::Sequence< OUString >& rStrings
271 , const std::vector<sal_Int32>& rLimitingBorders, bool bCreateSingleCategories )
273 std::vector< ComplexCategory > aResult;
275 sal_Int32 nMaxCount = rStrings.getLength();
276 OUString aPrevious;
277 sal_Int32 nCurrentCount=0;
278 for( sal_Int32 nN=0; nN<nMaxCount; nN++ )
280 const OUString& aCurrent = rStrings[nN];
281 if( bCreateSingleCategories || std::find( rLimitingBorders.begin(), rLimitingBorders.end(), nN ) != rLimitingBorders.end() )
283 aResult.emplace_back(aPrevious,nCurrentCount );
284 nCurrentCount=1;
285 aPrevious = aCurrent;
287 else
289 // Empty value is interpreted as a continuation of the previous
290 // category. Note that having the same value as the previous one
291 // does not equate to a continuation of the category.
293 if (aCurrent.isEmpty())
294 ++nCurrentCount;
295 else
297 aResult.emplace_back(aPrevious,nCurrentCount );
298 nCurrentCount=1;
299 aPrevious = aCurrent;
303 if( nCurrentCount )
304 aResult.emplace_back(aPrevious,nCurrentCount );
306 return aResult;
309 static sal_Int32 lcl_getCategoryCount( std::vector< ComplexCategory >& rComplexCategories )
311 sal_Int32 nCount = 0;
312 for (auto const& complexCategory : rComplexCategories)
313 nCount+=complexCategory.Count;
314 return nCount;
317 static Sequence< OUString > lcl_getExplicitSimpleCategories(
318 const SplitCategoriesProvider& rSplitCategoriesProvider,
319 std::vector< std::vector< ComplexCategory > >& rComplexCats )
321 Sequence< OUString > aRet;
323 rComplexCats.clear();
324 sal_Int32 nLCount = rSplitCategoriesProvider.getLevelCount();
325 for( sal_Int32 nL = 0; nL < nLCount; nL++ )
327 std::vector<sal_Int32> aLimitingBorders;
328 if(nL>0)
329 aLimitingBorders = lcl_getLimitingBorders( rComplexCats.back() );
330 rComplexCats.push_back( lcl_DataSequenceToComplexCategoryVector(
331 rSplitCategoriesProvider.getStringsForLevel(nL), aLimitingBorders, nL==(nLCount-1) ) );
334 //ensure that the category count is the same on each level
335 sal_Int32 nMaxCategoryCount = 0;
337 for (auto & complexCat : rComplexCats)
339 sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
340 nMaxCategoryCount = std::max( nCurrentCount, nMaxCategoryCount );
342 for (auto & complexCat : rComplexCats)
344 if ( !complexCat.empty() )
346 sal_Int32 nCurrentCount = lcl_getCategoryCount(complexCat);
347 if( nCurrentCount< nMaxCategoryCount )
349 ComplexCategory& rComplexCategory = complexCat.back();
350 rComplexCategory.Count += (nMaxCategoryCount-nCurrentCount);
356 //create a list with an element for every index
357 std::vector< std::vector< ComplexCategory > > aComplexCatsPerIndex;
358 for (auto const& complexCat : rComplexCats)
360 std::vector< ComplexCategory > aSingleLevel;
361 for (auto const& elem : complexCat)
363 sal_Int32 nCount = elem.Count;
364 while( nCount-- )
365 aSingleLevel.push_back(elem);
367 aComplexCatsPerIndex.push_back( aSingleLevel );
370 if(nMaxCategoryCount)
372 aRet.realloc(nMaxCategoryCount);
373 auto pRet = aRet.getArray();
374 for(sal_Int32 nN=0; nN<nMaxCategoryCount; nN++)
376 OUStringBuffer aText;
377 for (auto const& complexCatPerIndex : aComplexCatsPerIndex)
379 if ( o3tl::make_unsigned(nN) < complexCatPerIndex.size() )
381 OUString aAddText = complexCatPerIndex[nN].Text;
382 if( !aAddText.isEmpty() )
384 if(!aText.isEmpty())
385 aText.append(" ");
386 aText.append(aAddText);
390 pRet[nN]=aText.makeStringAndClear();
393 return aRet;
396 Sequence< OUString > ExplicitCategoriesProvider::getExplicitSimpleCategories(
397 const SplitCategoriesProvider& rSplitCategoriesProvider )
399 vector< vector< ComplexCategory > > aComplexCats;
400 return lcl_getExplicitSimpleCategories( rSplitCategoriesProvider, aComplexCats );
403 static bool lcl_fillDateCategories( const uno::Reference< data::XDataSequence >& xDataSequence, std::vector< double >& rDateCategories, bool bIsAutoDate, ChartModel& rModel )
405 bool bOnlyDatesFound = true;
406 bool bAnyDataFound = false;
408 if( xDataSequence.is() )
410 uno::Sequence< uno::Any > aValues = xDataSequence->getData();
411 sal_Int32 nCount = aValues.getLength();
412 rDateCategories.reserve(nCount);
413 Reference< util::XNumberFormats > xNumberFormats( rModel.getNumberFormats() );
415 bool bOwnData = false;
416 bool bOwnDataAnddAxisHasAnyFormat = false;
417 bool bOwnDataAnddAxisHasDateFormat = false;
418 rtl::Reference< BaseCoordinateSystem > xCooSysModel( ChartModelHelper::getFirstCoordinateSystem( &rModel ) );
419 if( xCooSysModel.is() )
421 if( rModel.hasInternalDataProvider() )
423 bOwnData = true;
424 rtl::Reference< Axis > xAxisProps = xCooSysModel->getAxisByDimension2(0,0);
425 sal_Int32 nAxisNumberFormat = 0;
426 if (xAxisProps.is() && (xAxisProps->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nAxisNumberFormat))
428 bOwnDataAnddAxisHasAnyFormat = true;
429 bOwnDataAnddAxisHasDateFormat = DiagramHelper::isDateNumberFormat( nAxisNumberFormat, xNumberFormats );
434 for(sal_Int32 nN=0;nN<nCount;nN++)
436 bool bIsDate = false;
437 if( bIsAutoDate )
439 if( bOwnData )
440 bIsDate = !bOwnDataAnddAxisHasAnyFormat || bOwnDataAnddAxisHasDateFormat;
441 else
442 bIsDate = DiagramHelper::isDateNumberFormat( xDataSequence->getNumberFormatKeyByIndex( nN ), xNumberFormats );
444 else
445 bIsDate = true;
447 bool bContainsEmptyString = false;
448 const uno::Any& aAny = aValues[nN];
449 if( aAny.hasValue() )
451 OUString aTest;
452 double fTest = 0;
453 bool bContainsNan = false;
454 if( (aAny>>=aTest) && aTest.isEmpty() ) //empty String
455 bContainsEmptyString = true;
456 else if( (aAny>>=fTest) && std::isnan(fTest) )
457 bContainsNan = true;
459 if( !bContainsEmptyString && !bContainsNan )
460 bAnyDataFound = true;
462 double aDate( 1.0 );
463 if( bIsDate && (aAny >>= aDate) )
464 rDateCategories.push_back( aDate );
465 else
467 if( aAny.hasValue() && !bContainsEmptyString )//empty string does not count as non date value!
468 bOnlyDatesFound=false;
469 rDateCategories.push_back( std::numeric_limits<double>::quiet_NaN() );
472 std::sort(
473 rDateCategories.begin(), rDateCategories.end(),
474 [](auto x, auto y) { return o3tl::strong_order(x, y) < 0; } );
477 return bAnyDataFound && bOnlyDatesFound;
480 void ExplicitCategoriesProvider::init()
482 if( !m_bDirty )
483 return;
485 m_aComplexCats.clear();//not one per index
486 m_aDateCategories.clear();
488 if( m_xOriginalCategories.is() )
490 if( !hasComplexCategories() )
492 if(m_bIsDateAxis)
494 if( ChartTypeHelper::isSupportingDateAxis( AxisHelper::getChartTypeByIndex( m_xCooSysModel.get(), 0 ), 0 ) )
495 m_bIsDateAxis = lcl_fillDateCategories( m_xOriginalCategories->getValues(), m_aDateCategories, m_bIsAutoDate, mrModel );
496 else
497 m_bIsDateAxis = false;
500 else
502 m_bIsDateAxis = false;
505 else
506 m_bIsDateAxis=false;
507 m_bDirty = false;
510 Sequence< OUString > const & ExplicitCategoriesProvider::getSimpleCategories()
512 if( !m_bIsExplicitCategoriesInited )
514 init();
515 m_aExplicitCategories.realloc(0);
516 if( m_xOriginalCategories.is() )
518 if( !hasComplexCategories() )
520 uno::Reference< data::XDataSequence > xDataSequence( m_xOriginalCategories->getValues() );
521 if( xDataSequence.is() )
522 ExplicitCategoriesProvider::convertCategoryAnysToText( m_aExplicitCategories, xDataSequence->getData(), mrModel );
524 else
526 m_aExplicitCategories = lcl_getExplicitSimpleCategories(
527 SplitCategoriesProvider_ForLabeledDataSequences( m_aSplitCategoriesList, mrModel ), m_aComplexCats );
530 if(!m_aExplicitCategories.hasElements())
531 m_aExplicitCategories = DiagramHelper::generateAutomaticCategoriesFromCooSys( m_xCooSysModel.get() );
532 m_bIsExplicitCategoriesInited = true;
534 return m_aExplicitCategories;
537 const std::vector<ComplexCategory>* ExplicitCategoriesProvider::getCategoriesByLevel( sal_Int32 nLevel )
539 init();
540 sal_Int32 nMaxIndex = m_aComplexCats.size()-1;
541 if (nLevel >= 0 && nLevel <= nMaxIndex)
542 return &m_aComplexCats[nMaxIndex-nLevel];
543 return nullptr;
546 OUString ExplicitCategoriesProvider::getCategoryByIndex(
547 const rtl::Reference< BaseCoordinateSystem >& xCooSysModel
548 , ChartModel& rModel
549 , sal_Int32 nIndex )
551 if( xCooSysModel.is())
553 ExplicitCategoriesProvider aExplicitCategoriesProvider( xCooSysModel, rModel );
554 Sequence< OUString > aCategories( aExplicitCategoriesProvider.getSimpleCategories());
555 if( nIndex < aCategories.getLength())
556 return aCategories[ nIndex ];
558 return OUString();
561 bool ExplicitCategoriesProvider::isDateAxis()
563 init();
564 return m_bIsDateAxis;
567 const std::vector< double >& ExplicitCategoriesProvider::getDateCategories()
569 init();
570 return m_aDateCategories;
573 } //namespace chart
575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */