1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include "VAxisProperties.hxx"
21 #include <ViewDefines.hxx>
22 #include <Axis.hxx>
23 #include <AxisHelper.hxx>
24 #include <ChartModel.hxx>
25 #include <ExplicitCategoriesProvider.hxx>
26
27 #include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
28 #include <com/sun/star/chart2/AxisType.hpp>
29
30 #include <comphelper/diagnose_ex.hxx>
31 #include <rtl/math.hxx>
32 #include <utility>
33
34 using namespace ::com::sun::star;
35 using namespace ::com::sun::star::chart2;
36
37 namespace chart {
38
AxisLabelAlignment()39 AxisLabelAlignment::AxisLabelAlignment() :
40 mfLabelDirection(1.0),
41 mfInnerTickDirection(1.0),
42 meAlignment(LABEL_ALIGN_RIGHT_TOP) {}
43
lcl_calcTickLengthForDepth(sal_Int32 nDepth,sal_Int32 nTickmarkStyle)44 static sal_Int32 lcl_calcTickLengthForDepth(sal_Int32 nDepth,sal_Int32 nTickmarkStyle)
45 {
46 sal_Int32 const nWidth = AXIS2D_TICKLENGTH; //@maybefuturetodo this length could be offered by the model
47 double fPercent = 1.0;
48 switch(nDepth)
49 {
50 case 0:
51 fPercent = 1.0;
52 break;
53 case 1:
54 fPercent = 0.75;//percentage like in the old chart
55 break;
56 case 2:
57 fPercent = 0.5;
58 break;
59 default:
60 fPercent = 0.3;
61 break;
62 }
63 if(nTickmarkStyle==3)//inner and outer tickmarks
64 fPercent*=2.0;
65 return static_cast<sal_Int32>(nWidth*fPercent);
66 }
67
lcl_getTickOffset(sal_Int32 nLength,sal_Int32 nTickmarkStyle)68 static double lcl_getTickOffset(sal_Int32 nLength,sal_Int32 nTickmarkStyle)
69 {
70 double fPercent = 0.0; //0<=fPercent<=1
71 //0.0: completely inner
72 //1.0: completely outer
73 //0.5: half and half
74
75 /*
76 nTickmarkStyle:
77 1: inner tickmarks
78 2: outer tickmarks
79 3: inner and outer tickmarks
80 */
81 switch(nTickmarkStyle)
82 {
83 case 1:
84 fPercent = 0.0;
85 break;
86 case 2:
87 fPercent = 1.0;
88 break;
89 default:
90 fPercent = 0.5;
91 break;
92 }
93 return fPercent*nLength;
94 }
95
makeTickmarkProperties(sal_Int32 nDepth) const96 TickmarkProperties AxisProperties::makeTickmarkProperties(
97 sal_Int32 nDepth ) const
98 {
99 /*
100 nTickmarkStyle:
101 1: inner tickmarks
102 2: outer tickmarks
103 3: inner and outer tickmarks
104 */
105 sal_Int32 nTickmarkStyle = 1;
106 if(nDepth==0)
107 {
108 nTickmarkStyle = m_nMajorTickmarks;
109 if(!nTickmarkStyle)
110 {
111 //create major tickmarks as if they were minor tickmarks
112 nDepth = 1;
113 nTickmarkStyle = m_nMinorTickmarks;
114 }
115 }
116 else if( nDepth==1)
117 {
118 nTickmarkStyle = m_nMinorTickmarks;
119 }
120
121 if (maLabelAlignment.mfInnerTickDirection == 0.0)
122 {
123 if( nTickmarkStyle != 0 )
124 nTickmarkStyle = 3; //inner and outer tickmarks
125 }
126
127 TickmarkProperties aTickmarkProperties;
128 aTickmarkProperties.Length = lcl_calcTickLengthForDepth(nDepth,nTickmarkStyle);
129 aTickmarkProperties.RelativePos = static_cast<sal_Int32>(lcl_getTickOffset(aTickmarkProperties.Length,nTickmarkStyle));
130 aTickmarkProperties.aLineProperties = makeLinePropertiesForDepth();
131 return aTickmarkProperties;
132 }
133
makeTickmarkPropertiesForComplexCategories(sal_Int32 nTickLength,sal_Int32 nTickStartDistanceToAxis) const134 TickmarkProperties AxisProperties::makeTickmarkPropertiesForComplexCategories(
135 sal_Int32 nTickLength, sal_Int32 nTickStartDistanceToAxis ) const
136 {
137 sal_Int32 nTickmarkStyle = (maLabelAlignment.mfLabelDirection == maLabelAlignment.mfInnerTickDirection) ? 2/*outside*/ : 1/*inside*/;
138
139 TickmarkProperties aTickmarkProperties;
140 aTickmarkProperties.Length = nTickLength;// + nTextLevel*( lcl_calcTickLengthForDepth(0,nTickmarkStyle) );
141 aTickmarkProperties.RelativePos = static_cast<sal_Int32>(lcl_getTickOffset(aTickmarkProperties.Length+nTickStartDistanceToAxis,nTickmarkStyle));
142 aTickmarkProperties.aLineProperties = makeLinePropertiesForDepth();
143 return aTickmarkProperties;
144 }
145
getBiggestTickmarkProperties()146 TickmarkProperties AxisProperties::getBiggestTickmarkProperties()
147 {
148 TickmarkProperties aTickmarkProperties;
149 sal_Int32 nTickmarkStyle = 3;//inner and outer tickmarks
150 aTickmarkProperties.Length = lcl_calcTickLengthForDepth( 0/*nDepth*/,nTickmarkStyle );
151 aTickmarkProperties.RelativePos = static_cast<sal_Int32>( lcl_getTickOffset( aTickmarkProperties.Length, nTickmarkStyle ) );
152 return aTickmarkProperties;
153 }
154
AxisProperties(rtl::Reference<::chart::Axis> xAxisModel,ExplicitCategoriesProvider * pExplicitCategoriesProvider,rtl::Reference<::chart::DataTable> const & xDataTableModel)155 AxisProperties::AxisProperties(rtl::Reference<::chart::Axis> xAxisModel,
156 ExplicitCategoriesProvider* pExplicitCategoriesProvider,
157 rtl::Reference<::chart::DataTable> const& xDataTableModel)
158 : m_xAxisModel(std::move(xAxisModel))
159 , m_nDimensionIndex(0)
160 , m_bIsMainAxis(true)
161 , m_bSwapXAndY(false)
162 , m_eCrossoverType( css::chart::ChartAxisPosition_ZERO )
163 , m_eLabelPos( css::chart::ChartAxisLabelPosition_NEAR_AXIS )
164 , m_eTickmarkPos( css::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS )
165 , m_bCrossingAxisHasReverseDirection(false)
166 , m_bCrossingAxisIsCategoryAxes(false)
167 , m_bDisplayDataTable(false)
168 , m_bDataTableAlignAxisValuesWithColumns(false)
169 , m_bDisplayLabels( true )
170 , m_bTryStaggeringFirst( false )
171 , m_nNumberFormatKey(0)
172 , m_nMajorTickmarks(1)
173 , m_nMinorTickmarks(1)
174 , m_nAxisType(AxisType::REALNUMBER)
175 , m_bComplexCategories(false)
176 , m_pExplicitCategoriesProvider(pExplicitCategoriesProvider)
177 , m_bLimitSpaceForLabels(false)
178 , m_xDataTableModel(xDataTableModel)
179 {
180 }
181
lcl_getLabelAlignmentForZAxis(const AxisProperties & rAxisProperties)182 static LabelAlignment lcl_getLabelAlignmentForZAxis( const AxisProperties& rAxisProperties )
183 {
184 LabelAlignment aRet( LABEL_ALIGN_RIGHT );
185 if (rAxisProperties.maLabelAlignment.mfLabelDirection < 0)
186 aRet = LABEL_ALIGN_LEFT;
187 return aRet;
188 }
189
lcl_getLabelAlignmentForYAxis(const AxisProperties & rAxisProperties)190 static LabelAlignment lcl_getLabelAlignmentForYAxis( const AxisProperties& rAxisProperties )
191 {
192 LabelAlignment aRet( LABEL_ALIGN_RIGHT );
193 if (rAxisProperties.maLabelAlignment.mfLabelDirection < 0)
194 aRet = LABEL_ALIGN_LEFT;
195 return aRet;
196 }
197
lcl_getLabelAlignmentForXAxis(const AxisProperties & rAxisProperties)198 static LabelAlignment lcl_getLabelAlignmentForXAxis( const AxisProperties& rAxisProperties )
199 {
200 LabelAlignment aRet( LABEL_ALIGN_BOTTOM );
201 if (rAxisProperties.maLabelAlignment.mfLabelDirection < 0)
202 aRet = LABEL_ALIGN_TOP;
203 return aRet;
204 }
205
initAxisPositioning(const uno::Reference<beans::XPropertySet> & xAxisProp)206 void AxisProperties::initAxisPositioning( const uno::Reference< beans::XPropertySet >& xAxisProp )
207 {
208 if( !xAxisProp.is() )
209 return;
210 try
211 {
212 if( AxisHelper::isAxisPositioningEnabled() )
213 {
214 xAxisProp->getPropertyValue(u"CrossoverPosition"_ustr) >>= m_eCrossoverType;
215 if( m_eCrossoverType == css::chart::ChartAxisPosition_VALUE )
216 {
217 double fValue = 0.0;
218 xAxisProp->getPropertyValue(u"CrossoverValue"_ustr) >>= fValue;
219
220 if( m_bCrossingAxisIsCategoryAxes )
221 fValue = ::rtl::math::round(fValue);
222 m_pfMainLinePositionAtOtherAxis = fValue;
223 }
224 else if( m_eCrossoverType == css::chart::ChartAxisPosition_ZERO )
225 m_pfMainLinePositionAtOtherAxis = 0.0;
226
227 xAxisProp->getPropertyValue(u"LabelPosition"_ustr) >>= m_eLabelPos;
228 xAxisProp->getPropertyValue(u"MarkPosition"_ustr) >>= m_eTickmarkPos;
229 }
230 else
231 {
232 m_eCrossoverType = css::chart::ChartAxisPosition_START;
233 if( m_bIsMainAxis == m_bCrossingAxisHasReverseDirection )
234 m_eCrossoverType = css::chart::ChartAxisPosition_END;
235 m_eLabelPos = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
236 m_eTickmarkPos = css::chart::ChartAxisMarkPosition_AT_LABELS;
237 }
238 }
239 catch( const uno::Exception& )
240 {
241 TOOLS_WARN_EXCEPTION("chart2", "" );
242 }
243 }
244
init(bool bCartesian)245 void AxisProperties::init( bool bCartesian )
246 {
247 if( !m_xAxisModel.is() )
248 return;
249
250 if( m_nDimensionIndex<2 )
251 initAxisPositioning( m_xAxisModel );
252
253 ScaleData aScaleData = m_xAxisModel->getScaleData();
254 if( m_nDimensionIndex==0 )
255 AxisHelper::checkDateAxis( aScaleData, m_pExplicitCategoriesProvider, bCartesian );
256 m_nAxisType = aScaleData.AxisType;
257
258 if( bCartesian )
259 {
260 if ((!m_bSwapXAndY && m_nDimensionIndex == 0) || (m_bSwapXAndY && m_nDimensionIndex == 1))
261 {
262 m_bDisplayDataTable = m_xDataTableModel.is();
263 }
264
265 if( m_nDimensionIndex == 0 && m_nAxisType == AxisType::CATEGORY
266 && m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->hasComplexCategories() )
267 m_bComplexCategories = true;
268
269 if( m_eCrossoverType == css::chart::ChartAxisPosition_END )
270 maLabelAlignment.mfInnerTickDirection = m_bCrossingAxisHasReverseDirection ? 1.0 : -1.0;
271 else
272 maLabelAlignment.mfInnerTickDirection = m_bCrossingAxisHasReverseDirection ? -1.0 : 1.0;
273
274 if( m_eLabelPos == css::chart::ChartAxisLabelPosition_NEAR_AXIS )
275 maLabelAlignment.mfLabelDirection = maLabelAlignment.mfInnerTickDirection;
276 else if( m_eLabelPos == css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE )
277 maLabelAlignment.mfLabelDirection = -maLabelAlignment.mfInnerTickDirection;
278 else if( m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_START )
279 maLabelAlignment.mfLabelDirection = m_bCrossingAxisHasReverseDirection ? -1 : 1;
280 else if( m_eLabelPos == css::chart::ChartAxisLabelPosition_OUTSIDE_END )
281 maLabelAlignment.mfLabelDirection = m_bCrossingAxisHasReverseDirection ? 1 : -1;
282
283 if( m_nDimensionIndex==2 )
284 maLabelAlignment.meAlignment = lcl_getLabelAlignmentForZAxis(*this);
285 else
286 {
287 bool bIsYAxisPosition = (m_nDimensionIndex==1 && !m_bSwapXAndY)
288 || (m_nDimensionIndex==0 && m_bSwapXAndY);
289 if( bIsYAxisPosition )
290 {
291 maLabelAlignment.mfLabelDirection *= -1.0;
292 maLabelAlignment.mfInnerTickDirection *= -1.0;
293
294 maLabelAlignment.meAlignment = lcl_getLabelAlignmentForYAxis(*this);
295 }
296 else
297 maLabelAlignment.meAlignment = lcl_getLabelAlignmentForXAxis(*this);
298 }
299 }
300
301 try
302 {
303 //init LineProperties
304 m_aLineProperties.initFromPropertySet( m_xAxisModel );
305
306 //init display labels
307 m_xAxisModel->getPropertyValue( u"DisplayLabels"_ustr ) >>= m_bDisplayLabels;
308
309 // Init layout strategy hint for axis labels.
310 // Compatibility option: starting from LibreOffice 5.1 the rotated
311 // layout is preferred to staggering for axis labels.
312 m_xAxisModel->getPropertyValue( u"TryStaggeringFirst"_ustr ) >>= m_bTryStaggeringFirst;
313
314 //init TickmarkProperties
315 m_xAxisModel->getPropertyValue( u"MajorTickmarks"_ustr ) >>= m_nMajorTickmarks;
316 m_xAxisModel->getPropertyValue( u"MinorTickmarks"_ustr ) >>= m_nMinorTickmarks;
317
318 sal_Int32 nMaxDepth = 0;
319 if(m_nMinorTickmarks!=0)
320 nMaxDepth=2;
321 else if(m_nMajorTickmarks!=0)
322 nMaxDepth=1;
323
324 m_aTickmarkPropertiesList.clear();
325 for( sal_Int32 nDepth=0; nDepth<nMaxDepth; nDepth++ )
326 {
327 TickmarkProperties aTickmarkProperties = makeTickmarkProperties( nDepth );
328 m_aTickmarkPropertiesList.push_back( aTickmarkProperties );
329 }
330 }
331 catch( const uno::Exception& )
332 {
333 TOOLS_WARN_EXCEPTION("chart2", "" );
334 }
335
336 if (m_bDisplayDataTable)
337 {
338 m_bDataTableAlignAxisValuesWithColumns = (m_nDimensionIndex == 0);
339
340 if (m_nDimensionIndex == 0)
341 {
342 m_bDisplayLabels = false;
343 }
344
345 }
346 }
347
AxisLabelProperties()348 AxisLabelProperties::AxisLabelProperties()
349 : m_aFontReferenceSize( ChartModel::getDefaultPageSize() )
350 , m_aMaximumSpaceForLabels( 0 , 0, m_aFontReferenceSize.Width, m_aFontReferenceSize.Height )
351 , m_nNumberFormatKey(0)
352 , m_eStaggering( AxisLabelStaggering::SideBySide )
353 , m_bLineBreakAllowed( false )
354 , m_bOverlapAllowed( false )
355 , m_bStackCharacters( false )
356 , m_fRotationAngleDegree( 0.0 )
357 , m_nRhythm( 1 )
358 {
359
360 }
361
init(const rtl::Reference<Axis> & xAxisModel)362 void AxisLabelProperties::init( const rtl::Reference< Axis >& xAxisModel )
363 {
364 if(!xAxisModel.is())
365 return;
366
367 try
368 {
369 xAxisModel->getPropertyValue( u"TextBreak"_ustr ) >>= m_bLineBreakAllowed;
370 xAxisModel->getPropertyValue( u"TextOverlap"_ustr ) >>= m_bOverlapAllowed;
371 xAxisModel->getPropertyValue( u"StackCharacters"_ustr ) >>= m_bStackCharacters;
372 xAxisModel->getPropertyValue( u"TextRotation"_ustr ) >>= m_fRotationAngleDegree;
373
374 css::chart::ChartAxisArrangeOrderType eArrangeOrder;
375 xAxisModel->getPropertyValue( u"ArrangeOrder"_ustr ) >>= eArrangeOrder;
376 switch(eArrangeOrder)
377 {
378 case css::chart::ChartAxisArrangeOrderType_SIDE_BY_SIDE:
379 m_eStaggering = AxisLabelStaggering::SideBySide;
380 break;
381 case css::chart::ChartAxisArrangeOrderType_STAGGER_EVEN:
382 m_eStaggering = AxisLabelStaggering::StaggerEven;
383 break;
384 case css::chart::ChartAxisArrangeOrderType_STAGGER_ODD:
385 m_eStaggering = AxisLabelStaggering::StaggerOdd;
386 break;
387 default:
388 m_eStaggering = AxisLabelStaggering::StaggerAuto;
389 break;
390 }
391 }
392 catch( const uno::Exception& )
393 {
394 TOOLS_WARN_EXCEPTION("chart2", "" );
395 }
396 }
397
isStaggered() const398 bool AxisLabelProperties::isStaggered() const
399 {
400 return ( m_eStaggering == AxisLabelStaggering::StaggerOdd || m_eStaggering == AxisLabelStaggering::StaggerEven );
401 }
402
autoRotate45()403 void AxisLabelProperties::autoRotate45()
404 {
405 m_fRotationAngleDegree = 45;
406 m_bLineBreakAllowed = false;
407 m_eStaggering = AxisLabelStaggering::SideBySide;
408 }
409
410 } //namespace chart
411
412 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
413