xref: /core/chart2/source/view/axes/VAxisProperties.cxx (revision 76d296f7600bddf0b52fe2bd5522594551f10218)
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