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