Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */