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 <DiagramHelper.hxx>
21 #include <Diagram.hxx>
22 #include <DataSeries.hxx>
23 #include <DataSeriesHelper.hxx>
25 #include <AxisHelper.hxx>
26 #include <ChartType.hxx>
27 #include <ChartTypeHelper.hxx>
28 #include <ChartTypeManager.hxx>
29 #include <ChartTypeTemplate.hxx>
30 #include <ChartModel.hxx>
31 #include <ChartModelHelper.hxx>
32 #include <ExplicitCategoriesProvider.hxx>
33 #include <servicenames_charttypes.hxx>
34 #include <RelativePositionHelper.hxx>
35 #include <ControllerLockGuard.hxx>
36 #include <NumberFormatterWrapper.hxx>
37 #include <unonames.hxx>
38 #include <BaseCoordinateSystem.hxx>
40 #include <com/sun/star/chart/MissingValueTreatment.hpp>
41 #include <com/sun/star/chart/XDiagramPositioning.hpp>
42 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
43 #include <com/sun/star/chart2/AxisType.hpp>
44 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
45 #include <com/sun/star/chart2/RelativePosition.hpp>
46 #include <com/sun/star/chart2/RelativeSize.hpp>
47 #include <com/sun/star/chart2/StackingDirection.hpp>
49 #include <com/sun/star/util/CloseVetoException.hpp>
50 #include <com/sun/star/util/NumberFormat.hpp>
51 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
53 #include <o3tl/safeint.hxx>
54 #include <unotools/saveopt.hxx>
55 #include <rtl/math.hxx>
56 #include <svl/numformat.hxx>
57 #include <svl/zforlist.hxx>
58 #include <vcl/svapp.hxx>
59 #include <vcl/settings.hxx>
60 #include <comphelper/sequence.hxx>
61 #include <comphelper/diagnose_ex.hxx>
62 #include <sal/log.hxx>
67 using namespace ::com::sun::star
;
68 using namespace ::com::sun::star::chart2
;
70 using ::com::sun::star::uno::Reference
;
71 using ::com::sun::star::uno::Sequence
;
72 using ::com::sun::star::uno::Any
;
73 using ::com::sun::star::chart2::XAnyDescriptionAccess
;
78 StackMode
DiagramHelper::getStackModeFromChartType(
79 const rtl::Reference
< ChartType
> & xChartType
,
80 bool& rbFound
, bool& rbAmbiguous
,
81 const rtl::Reference
< BaseCoordinateSystem
> & xCorrespondingCoordinateSystem
)
83 StackMode eStackMode
= StackMode::NONE
;
89 const std::vector
< rtl::Reference
< DataSeries
> > & aSeries
= xChartType
->getDataSeries2();
91 chart2::StackingDirection eCommonDirection
= chart2::StackingDirection_NO_STACKING
;
92 bool bDirectionInitialized
= false;
94 // first series is irrelevant for stacking, start with second, unless
95 // there is only one series
96 const sal_Int32 nSeriesCount
= aSeries
.size();
97 sal_Int32 i
= (nSeriesCount
== 1) ? 0: 1;
98 for( ; i
<nSeriesCount
; ++i
)
101 chart2::StackingDirection eCurrentDirection
= eCommonDirection
;
102 // property is not MAYBEVOID
103 bool bSuccess
= ( aSeries
[i
]->getPropertyValue( "StackingDirection" ) >>= eCurrentDirection
);
104 OSL_ASSERT( bSuccess
);
105 if( ! bDirectionInitialized
)
107 eCommonDirection
= eCurrentDirection
;
108 bDirectionInitialized
= true;
112 if( eCommonDirection
!= eCurrentDirection
)
122 if( eCommonDirection
== chart2::StackingDirection_Z_STACKING
)
123 eStackMode
= StackMode::ZStacked
;
124 else if( eCommonDirection
== chart2::StackingDirection_Y_STACKING
)
126 eStackMode
= StackMode::YStacked
;
129 if( xCorrespondingCoordinateSystem
.is() )
131 if( 1 < xCorrespondingCoordinateSystem
->getDimension() )
133 sal_Int32 nAxisIndex
= 0;
135 nAxisIndex
= DataSeriesHelper::getAttachedAxisIndex(aSeries
[0]);
137 rtl::Reference
< Axis
> xAxis
=
138 xCorrespondingCoordinateSystem
->getAxisByDimension2( 1,nAxisIndex
);
141 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
142 if( aScaleData
.AxisType
==chart2::AxisType::PERCENT
)
143 eStackMode
= StackMode::YStackedPercent
;
150 catch( const uno::Exception
& )
152 DBG_UNHANDLED_EXCEPTION("chart2");
158 bool DiagramHelper::isSeriesAttachedToMainAxis(
159 const rtl::Reference
< ::chart::DataSeries
>& xDataSeries
)
161 sal_Int32 nAxisIndex
= DataSeriesHelper::getAttachedAxisIndex(xDataSeries
);
162 return (nAxisIndex
==0);
165 static void lcl_generateAutomaticCategoriesFromChartType(
166 Sequence
< OUString
>& rRet
,
167 const rtl::Reference
< ChartType
>& xChartType
)
171 OUString
aMainSeq( xChartType
->getRoleOfSequenceForSeriesLabel() );
173 const std::vector
< rtl::Reference
< DataSeries
> > & aSeriesSeq
= xChartType
->getDataSeries2();
174 for( rtl::Reference
< DataSeries
> const & dataSeries
: aSeriesSeq
)
176 uno::Reference
< data::XLabeledDataSequence
> xLabeledSeq
=
177 ::chart::DataSeriesHelper::getDataSequenceByRole( dataSeries
, aMainSeq
);
178 if( !xLabeledSeq
.is() )
180 Reference
< chart2::data::XDataSequence
> xValueSeq( xLabeledSeq
->getValues() );
181 if( !xValueSeq
.is() )
183 rRet
= xValueSeq
->generateLabel( chart2::data::LabelOrigin_LONG_SIDE
);
184 if( rRet
.hasElements() )
189 Sequence
< OUString
> DiagramHelper::generateAutomaticCategoriesFromCooSys( const rtl::Reference
< BaseCoordinateSystem
> & xCooSys
)
191 Sequence
< OUString
> aRet
;
195 const std::vector
< rtl::Reference
< ChartType
> > & aChartTypes( xCooSys
->getChartTypes2() );
196 for( rtl::Reference
< ChartType
> const & chartType
: aChartTypes
)
198 lcl_generateAutomaticCategoriesFromChartType( aRet
, chartType
);
199 if( aRet
.hasElements() )
206 Sequence
< OUString
> DiagramHelper::getExplicitSimpleCategories(
209 rtl::Reference
< BaseCoordinateSystem
> xCooSys( ChartModelHelper::getFirstCoordinateSystem( &rModel
) );
210 ExplicitCategoriesProvider
aExplicitCategoriesProvider( xCooSys
, rModel
);
211 return aExplicitCategoriesProvider
.getSimpleCategories();
216 void lcl_switchToDateCategories( const rtl::Reference
< ChartModel
>& xChartDoc
, const Reference
< XAxis
>& xAxis
)
220 if( !xChartDoc
.is() )
223 ScaleData
aScale( xAxis
->getScaleData() );
224 if( xChartDoc
->hasInternalDataProvider() )
226 //remove all content the is not of type double and remove multiple level
227 Reference
< XAnyDescriptionAccess
> xDataAccess( xChartDoc
->getDataProvider(), uno::UNO_QUERY
);
228 if( xDataAccess
.is() )
230 Sequence
< Sequence
< Any
> > aAnyCategories( xDataAccess
->getAnyRowDescriptions() );
231 auto aAnyCategoriesRange
= asNonConstRange(aAnyCategories
);
233 sal_Int32 nN
= aAnyCategories
.getLength();
236 Sequence
< Any
>& rCat
= aAnyCategoriesRange
[nN
];
237 if( rCat
.getLength() > 1 )
239 if( rCat
.getLength() == 1 )
241 Any
& rAny
= rCat
.getArray()[0];
242 if( !(rAny
>>=fTest
) )
244 rAny
<<= std::numeric_limits
<double>::quiet_NaN();
248 xDataAccess
->setAnyRowDescriptions( aAnyCategories
);
250 //check the numberformat at the axis
251 Reference
< beans::XPropertySet
> xAxisProps( xAxis
, uno::UNO_QUERY
);
252 if( xAxisProps
.is() )
254 sal_Int32 nNumberFormat
= -1;
255 xAxisProps
->getPropertyValue(CHART_UNONAME_NUMFMT
) >>= nNumberFormat
;
257 Reference
< util::XNumberFormats
> xNumberFormats( xChartDoc
->getNumberFormats() );
258 if( xNumberFormats
.is() )
260 Reference
< beans::XPropertySet
> xKeyProps
;
263 xKeyProps
= xNumberFormats
->getByKey( nNumberFormat
);
265 catch( const uno::Exception
& )
267 DBG_UNHANDLED_EXCEPTION("chart2");
269 sal_Int32 nType
= util::NumberFormat::UNDEFINED
;
271 xKeyProps
->getPropertyValue( "Type" ) >>= nType
;
272 if( !( nType
& util::NumberFormat::DATE
) )
274 //set a date format to the axis
275 const LocaleDataWrapper
& rLocaleDataWrapper
= Application::GetSettings().GetLocaleDataWrapper();
276 Sequence
<sal_Int32
> aKeySeq
= xNumberFormats
->queryKeys( util::NumberFormat::DATE
, rLocaleDataWrapper
.getLanguageTag().getLocale(), true/*bCreate*/ );
277 if( aKeySeq
.hasElements() )
279 xAxisProps
->setPropertyValue(CHART_UNONAME_NUMFMT
, uno::Any(aKeySeq
[0]));
285 if( aScale
.AxisType
!= chart2::AxisType::DATE
)
286 AxisHelper::removeExplicitScaling( aScale
);
287 aScale
.AxisType
= chart2::AxisType::DATE
;
288 xAxis
->setScaleData( aScale
);
291 void lcl_switchToTextCategories( const rtl::Reference
< ChartModel
>& xChartDoc
, const Reference
< XAxis
>& xAxis
)
295 if( !xChartDoc
.is() )
297 ScaleData
aScale( xAxis
->getScaleData() );
298 if( aScale
.AxisType
!= chart2::AxisType::CATEGORY
)
299 AxisHelper::removeExplicitScaling( aScale
);
300 //todo migrate dates to text?
301 aScale
.AxisType
= chart2::AxisType::CATEGORY
;
302 aScale
.AutoDateAxis
= false;
303 xAxis
->setScaleData( aScale
);
308 void DiagramHelper::switchToDateCategories( const rtl::Reference
<::chart::ChartModel
>& xChartDoc
)
312 ControllerLockGuardUNO
aCtrlLockGuard( xChartDoc
);
314 rtl::Reference
< BaseCoordinateSystem
> xCooSys
= ChartModelHelper::getFirstCoordinateSystem( xChartDoc
);
317 rtl::Reference
< Axis
> xAxis
= xCooSys
->getAxisByDimension2(0,0);
318 lcl_switchToDateCategories( xChartDoc
, xAxis
);
323 void DiagramHelper::switchToTextCategories( const rtl::Reference
<::chart::ChartModel
>& xChartDoc
)
327 ControllerLockGuardUNO
aCtrlLockGuard( xChartDoc
);
329 rtl::Reference
< BaseCoordinateSystem
> xCooSys
= ChartModelHelper::getFirstCoordinateSystem( xChartDoc
);
332 rtl::Reference
< Axis
> xAxis
= xCooSys
->getAxisByDimension2(0,0);
333 lcl_switchToTextCategories( xChartDoc
, xAxis
);
338 bool DiagramHelper::isDateNumberFormat( sal_Int32 nNumberFormat
, const Reference
< util::XNumberFormats
>& xNumberFormats
)
340 bool bIsDate
= false;
341 if( !xNumberFormats
.is() )
344 Reference
< beans::XPropertySet
> xKeyProps
= xNumberFormats
->getByKey( nNumberFormat
);
347 sal_Int32 nType
= util::NumberFormat::UNDEFINED
;
348 xKeyProps
->getPropertyValue( "Type" ) >>= nType
;
349 bIsDate
= nType
& util::NumberFormat::DATE
;
354 sal_Int32
DiagramHelper::getDateNumberFormat( const Reference
< util::XNumberFormatsSupplier
>& xNumberFormatsSupplier
)
358 //try to get a date format with full year display
359 const LanguageTag
& rLanguageTag
= Application::GetSettings().GetLanguageTag();
360 NumberFormatterWrapper
aNumberFormatterWrapper( xNumberFormatsSupplier
);
361 SvNumberFormatter
* pNumFormatter
= aNumberFormatterWrapper
.getSvNumberFormatter();
364 nRet
= pNumFormatter
->GetFormatIndex( NF_DATE_SYS_DDMMYYYY
, rLanguageTag
.getLanguageType() );
368 Reference
< util::XNumberFormats
> xNumberFormats( xNumberFormatsSupplier
->getNumberFormats() );
369 if( xNumberFormats
.is() )
371 Sequence
<sal_Int32
> aKeySeq
= xNumberFormats
->queryKeys( util::NumberFormat::DATE
,
372 rLanguageTag
.getLocale(), true/*bCreate */);
373 if( aKeySeq
.hasElements() )
382 sal_Int32
DiagramHelper::getDateTimeInputNumberFormat( const Reference
< util::XNumberFormatsSupplier
>& xNumberFormatsSupplier
, double fNumber
)
386 // Get the most detailed date/time format according to fNumber.
387 NumberFormatterWrapper
aNumberFormatterWrapper( xNumberFormatsSupplier
);
388 SvNumberFormatter
* pNumFormatter
= aNumberFormatterWrapper
.getSvNumberFormatter();
390 SAL_WARN("chart2", "DiagramHelper::getDateTimeInputNumberFormat - no SvNumberFormatter");
393 SvNumFormatType nType
;
394 // Obtain best matching date, time or datetime format.
395 nRet
= pNumFormatter
->GuessDateTimeFormat( nType
, fNumber
, LANGUAGE_SYSTEM
);
396 // Obtain the corresponding edit format.
397 nRet
= pNumFormatter
->GetEditFormat( fNumber
, nRet
, nType
, nullptr);
402 sal_Int32
DiagramHelper::getPercentNumberFormat( const Reference
< util::XNumberFormatsSupplier
>& xNumberFormatsSupplier
)
405 const LanguageTag
& rLanguageTag
= Application::GetSettings().GetLanguageTag();
406 NumberFormatterWrapper
aNumberFormatterWrapper( xNumberFormatsSupplier
);
407 SvNumberFormatter
* pNumFormatter
= aNumberFormatterWrapper
.getSvNumberFormatter();
410 nRet
= pNumFormatter
->GetFormatIndex( NF_PERCENT_INT
, rLanguageTag
.getLanguageType() );
414 Reference
< util::XNumberFormats
> xNumberFormats( xNumberFormatsSupplier
->getNumberFormats() );
415 if( xNumberFormats
.is() )
417 Sequence
<sal_Int32
> aKeySeq
= xNumberFormats
->queryKeys( util::NumberFormat::PERCENT
,
418 rLanguageTag
.getLocale(), true/*bCreate*/ );
419 if( aKeySeq
.hasElements() )
421 // This *assumes* the sequence is sorted as in
422 // NfIndexTableOffset and the first format is the integer 0%
423 // format by chance... which usually is the case, but... anyway,
424 // we usually also have a number formatter so don't reach here.
432 bool DiagramHelper::areChartTypesCompatible( const rtl::Reference
< ChartType
>& xFirstType
,
433 const rtl::Reference
< ChartType
>& xSecondType
)
435 if( !xFirstType
.is() || !xSecondType
.is() )
438 auto aFirstRoles( comphelper::sequenceToContainer
<std::vector
< OUString
>>( xFirstType
->getSupportedMandatoryRoles() ) );
439 auto aSecondRoles( comphelper::sequenceToContainer
<std::vector
< OUString
>>( xSecondType
->getSupportedMandatoryRoles() ) );
440 std::sort( aFirstRoles
.begin(), aFirstRoles
.end() );
441 std::sort( aSecondRoles
.begin(), aSecondRoles
.end() );
442 return ( aFirstRoles
== aSecondRoles
);
445 static void lcl_ensureRange0to1( double& rValue
)
453 bool DiagramHelper::setDiagramPositioning( const rtl::Reference
<::chart::ChartModel
>& xChartModel
,
454 const awt::Rectangle
& rPosRect
/*100th mm*/ )
456 ControllerLockGuardUNO
aCtrlLockGuard( xChartModel
);
458 bool bChanged
= false;
459 awt::Size
aPageSize( ChartModelHelper::getPageSize(xChartModel
) );
460 rtl::Reference
< Diagram
> xDiagram
= xChartModel
->getFirstChartDiagram();
464 RelativePosition aOldPos
;
465 RelativeSize aOldSize
;
466 xDiagram
->getPropertyValue("RelativePosition" ) >>= aOldPos
;
467 xDiagram
->getPropertyValue("RelativeSize" ) >>= aOldSize
;
469 RelativePosition aNewPos
;
470 aNewPos
.Anchor
= drawing::Alignment_TOP_LEFT
;
471 aNewPos
.Primary
= double(rPosRect
.X
)/double(aPageSize
.Width
);
472 aNewPos
.Secondary
= double(rPosRect
.Y
)/double(aPageSize
.Height
);
474 chart2::RelativeSize aNewSize
;
475 aNewSize
.Primary
= double(rPosRect
.Width
)/double(aPageSize
.Width
);
476 aNewSize
.Secondary
= double(rPosRect
.Height
)/double(aPageSize
.Height
);
478 lcl_ensureRange0to1( aNewPos
.Primary
);
479 lcl_ensureRange0to1( aNewPos
.Secondary
);
480 lcl_ensureRange0to1( aNewSize
.Primary
);
481 lcl_ensureRange0to1( aNewSize
.Secondary
);
482 if( (aNewPos
.Primary
+ aNewSize
.Primary
) > 1.0 )
483 aNewPos
.Primary
= 1.0 - aNewSize
.Primary
;
484 if( (aNewPos
.Secondary
+ aNewSize
.Secondary
) > 1.0 )
485 aNewPos
.Secondary
= 1.0 - aNewSize
.Secondary
;
487 xDiagram
->setPropertyValue( "RelativePosition", uno::Any(aNewPos
) );
488 xDiagram
->setPropertyValue( "RelativeSize", uno::Any(aNewSize
) );
490 bChanged
= (aOldPos
.Anchor
!=aNewPos
.Anchor
) ||
491 (aOldPos
.Primary
!=aNewPos
.Primary
) ||
492 (aOldPos
.Secondary
!=aNewPos
.Secondary
) ||
493 (aOldSize
.Primary
!=aNewSize
.Primary
) ||
494 (aOldSize
.Secondary
!=aNewSize
.Secondary
);
498 awt::Rectangle
DiagramHelper::getDiagramRectangleFromModel( const rtl::Reference
<::chart::ChartModel
>& xChartModel
)
500 awt::Rectangle
aRet(-1,-1,-1,-1);
502 rtl::Reference
< Diagram
> xDiagram
= xChartModel
->getFirstChartDiagram();
506 awt::Size
aPageSize( ChartModelHelper::getPageSize(xChartModel
) );
508 RelativePosition aRelPos
;
509 RelativeSize aRelSize
;
510 xDiagram
->getPropertyValue("RelativePosition" ) >>= aRelPos
;
511 xDiagram
->getPropertyValue("RelativeSize" ) >>= aRelSize
;
514 static_cast< sal_Int32
>( aRelSize
.Primary
* aPageSize
.Width
),
515 static_cast< sal_Int32
>( aRelSize
.Secondary
* aPageSize
.Height
));
518 static_cast< sal_Int32
>( aRelPos
.Primary
* aPageSize
.Width
),
519 static_cast< sal_Int32
>( aRelPos
.Secondary
* aPageSize
.Height
));
521 awt::Point aAbsPosLeftTop
= RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( aAbsPos
, aAbsSize
, aRelPos
.Anchor
);
523 aRet
= awt::Rectangle(aAbsPosLeftTop
.X
, aAbsPosLeftTop
.Y
, aAbsSize
.Width
, aAbsSize
.Height
);
528 bool DiagramHelper::switchDiagramPositioningToExcludingPositioning(
529 ChartModel
& rModel
, bool bResetModifiedState
, bool bConvertAlsoFromAutoPositioning
)
531 //return true if something was changed
532 const SvtSaveOptions::ODFSaneDefaultVersion
nCurrentODFVersion(GetODFSaneDefaultVersion());
533 if (SvtSaveOptions::ODFSVER_012
< nCurrentODFVersion
)
535 uno::Reference
< css::chart::XDiagramPositioning
> xDiagramPositioning( rModel
.getFirstDiagram(), uno::UNO_QUERY
);
536 if( xDiagramPositioning
.is() && ( bConvertAlsoFromAutoPositioning
|| !xDiagramPositioning
->isAutomaticDiagramPositioning() )
537 && !xDiagramPositioning
->isExcludingDiagramPositioning() )
539 ControllerLockGuard
aCtrlLockGuard( rModel
);
540 bool bModelWasModified
= rModel
.isModified();
541 xDiagramPositioning
->setDiagramPositionExcludingAxes( xDiagramPositioning
->calculateDiagramPositionExcludingAxes() );
542 if(bResetModifiedState
&& !bModelWasModified
)
543 rModel
.setModified(false);
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */