Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/tools/RelativePositionHelper.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 <RelativePositionHelper.hxx>
21
#include <com/sun/star/chart2/RelativeSize.hpp>
22
#include <com/sun/star/awt/Size.hpp>
23
#include <rtl/math.hxx>
24
#include <osl/diagnose.h>
25
26
using namespace ::com::sun::star;
27
28
namespace chart
29
{
30
31
chart2::RelativePosition RelativePositionHelper::getReanchoredPosition(
32
    const chart2::RelativePosition & rPosition,
33
    const chart2::RelativeSize & rObjectSize,
34
    drawing::Alignment aNewAnchor )
35
0
{
36
0
    chart2::RelativePosition aResult( rPosition );
37
0
    if( rPosition.Anchor != aNewAnchor )
38
0
    {
39
0
        sal_Int32 nShiftHalfWidths  = 0;
40
0
        sal_Int32 nShiftHalfHeights = 0;
41
42
        // normalize to top-left
43
0
        switch( rPosition.Anchor )
44
0
        {
45
0
            case drawing::Alignment_TOP_LEFT:
46
0
                break;
47
0
            case drawing::Alignment_LEFT:
48
0
                nShiftHalfHeights -= 1;
49
0
                break;
50
0
            case drawing::Alignment_BOTTOM_LEFT:
51
0
                nShiftHalfHeights -= 2;
52
0
                break;
53
0
            case drawing::Alignment_TOP:
54
0
                nShiftHalfWidths  -= 1;
55
0
                break;
56
0
            case drawing::Alignment_CENTER:
57
0
                nShiftHalfWidths  -= 1;
58
0
                nShiftHalfHeights -= 1;
59
0
                break;
60
0
            case drawing::Alignment_BOTTOM:
61
0
                nShiftHalfWidths  -= 1;
62
0
                nShiftHalfHeights -= 2;
63
0
                break;
64
0
            case drawing::Alignment_TOP_RIGHT:
65
0
                nShiftHalfWidths  -= 2;
66
0
                break;
67
0
            case drawing::Alignment_RIGHT:
68
0
                nShiftHalfWidths  -= 2;
69
0
                nShiftHalfHeights -= 1;
70
0
                break;
71
0
            case drawing::Alignment_BOTTOM_RIGHT:
72
0
                nShiftHalfWidths  -= 2;
73
0
                nShiftHalfHeights -= 2;
74
0
                break;
75
0
            case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
76
0
                break;
77
0
        }
78
79
        // transform
80
0
        switch( aNewAnchor )
81
0
        {
82
0
            case drawing::Alignment_TOP_LEFT:
83
0
                break;
84
0
            case drawing::Alignment_LEFT:
85
0
                nShiftHalfHeights += 1;
86
0
                break;
87
0
            case drawing::Alignment_BOTTOM_LEFT:
88
0
                nShiftHalfHeights += 2;
89
0
                break;
90
0
            case drawing::Alignment_TOP:
91
0
                nShiftHalfWidths  += 1;
92
0
                break;
93
0
            case drawing::Alignment_CENTER:
94
0
                nShiftHalfWidths  += 1;
95
0
                nShiftHalfHeights += 1;
96
0
                break;
97
0
            case drawing::Alignment_BOTTOM:
98
0
                nShiftHalfWidths  += 1;
99
0
                nShiftHalfHeights += 2;
100
0
                break;
101
0
            case drawing::Alignment_TOP_RIGHT:
102
0
                nShiftHalfWidths  += 2;
103
0
                break;
104
0
            case drawing::Alignment_RIGHT:
105
0
                nShiftHalfWidths  += 2;
106
0
                nShiftHalfHeights += 1;
107
0
                break;
108
0
            case drawing::Alignment_BOTTOM_RIGHT:
109
0
                nShiftHalfWidths  += 2;
110
0
                nShiftHalfHeights += 2;
111
0
                break;
112
0
            case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
113
0
                break;
114
0
        }
115
116
0
        if( nShiftHalfWidths != 0 )
117
0
            aResult.Primary += (rObjectSize.Primary / 2.0) * nShiftHalfWidths;
118
0
        if( nShiftHalfHeights != 0 )
119
0
            aResult.Secondary += (rObjectSize.Secondary / 2.0) * nShiftHalfHeights;
120
0
    }
121
122
0
    return aResult;
123
0
}
124
125
awt::Point RelativePositionHelper::getUpperLeftCornerOfAnchoredObject(
126
      awt::Point aPoint
127
    , awt::Size aObjectSize
128
    , drawing::Alignment aAnchor )
129
0
{
130
0
    awt::Point aResult( aPoint );
131
132
0
    double fXDelta = 0.0;
133
0
    double fYDelta = 0.0;
134
135
    // adapt x-value
136
0
    switch( aAnchor )
137
0
    {
138
0
        case drawing::Alignment_TOP:
139
0
        case drawing::Alignment_CENTER:
140
0
        case drawing::Alignment_BOTTOM:
141
0
            fXDelta -= static_cast< double >( aObjectSize.Width ) / 2.0;
142
0
            break;
143
0
        case drawing::Alignment_TOP_RIGHT:
144
0
        case drawing::Alignment_RIGHT:
145
0
        case drawing::Alignment_BOTTOM_RIGHT:
146
0
            fXDelta -= aObjectSize.Width;
147
0
            break;
148
0
        case drawing::Alignment_TOP_LEFT:
149
0
        case drawing::Alignment_LEFT:
150
0
        case drawing::Alignment_BOTTOM_LEFT:
151
0
        default:
152
            // nothing to do
153
0
            break;
154
0
    }
155
156
    // adapt y-value
157
0
    switch( aAnchor )
158
0
    {
159
0
        case drawing::Alignment_LEFT:
160
0
        case drawing::Alignment_CENTER:
161
0
        case drawing::Alignment_RIGHT:
162
0
            fYDelta -= static_cast< double >( aObjectSize.Height ) / 2.0;
163
0
            break;
164
0
        case drawing::Alignment_BOTTOM_LEFT:
165
0
        case drawing::Alignment_BOTTOM:
166
0
        case drawing::Alignment_BOTTOM_RIGHT:
167
0
            fYDelta -= aObjectSize.Height;
168
0
            break;
169
0
        case drawing::Alignment_TOP_LEFT:
170
0
        case drawing::Alignment_TOP:
171
0
        case drawing::Alignment_TOP_RIGHT:
172
0
        default:
173
            // nothing to do
174
0
            break;
175
0
    }
176
177
0
    aResult.X += static_cast< sal_Int32 >( ::rtl::math::round( fXDelta ));
178
0
    aResult.Y += static_cast< sal_Int32 >( ::rtl::math::round( fYDelta ));
179
180
0
    return aResult;
181
0
}
182
183
awt::Point RelativePositionHelper::getCenterOfAnchoredObject(
184
      awt::Point aPoint
185
    , awt::Size aUnrotatedObjectSize
186
    , drawing::Alignment aAnchor
187
    , double fAnglePi )
188
0
{
189
0
    awt::Point aResult( aPoint );
190
191
0
    double fXDelta = 0.0;
192
0
    double fYDelta = 0.0;
193
194
    // adapt x-value
195
0
    switch( aAnchor )
196
0
    {
197
0
        case drawing::Alignment_TOP:
198
0
        case drawing::Alignment_CENTER:
199
0
        case drawing::Alignment_BOTTOM:
200
            // nothing to do
201
0
            break;
202
0
        case drawing::Alignment_TOP_RIGHT:
203
0
        case drawing::Alignment_RIGHT:
204
0
        case drawing::Alignment_BOTTOM_RIGHT:
205
0
            fXDelta -= aUnrotatedObjectSize.Width/2;
206
0
            break;
207
0
        case drawing::Alignment_TOP_LEFT:
208
0
        case drawing::Alignment_LEFT:
209
0
        case drawing::Alignment_BOTTOM_LEFT:
210
0
        default:
211
0
            fXDelta += aUnrotatedObjectSize.Width/2;
212
0
            break;
213
0
    }
214
215
    // adapt y-value
216
0
    switch( aAnchor )
217
0
    {
218
0
        case drawing::Alignment_LEFT:
219
0
        case drawing::Alignment_CENTER:
220
0
        case drawing::Alignment_RIGHT:
221
            // nothing to do
222
0
            break;
223
0
        case drawing::Alignment_BOTTOM_LEFT:
224
0
        case drawing::Alignment_BOTTOM:
225
0
        case drawing::Alignment_BOTTOM_RIGHT:
226
0
            fYDelta -= aUnrotatedObjectSize.Height/2;
227
0
            break;
228
0
        case drawing::Alignment_TOP_LEFT:
229
0
        case drawing::Alignment_TOP:
230
0
        case drawing::Alignment_TOP_RIGHT:
231
0
            fYDelta += aUnrotatedObjectSize.Height/2;
232
0
            break;
233
0
        default:
234
            // nothing to do
235
0
            break;
236
0
    }
237
238
    //take rotation into account:
239
0
    aResult.X += static_cast< sal_Int32 >(
240
0
        ::rtl::math::round(    fXDelta * std::cos( fAnglePi ) + fYDelta * std::sin( fAnglePi ) ) );
241
0
    aResult.Y += static_cast< sal_Int32 >(
242
0
        ::rtl::math::round(  - fXDelta * std::sin( fAnglePi ) + fYDelta * std::cos( fAnglePi ) ) );
243
244
0
    return aResult;
245
0
}
246
247
bool RelativePositionHelper::centerGrow(
248
    chart2::RelativePosition & rInOutPosition,
249
    chart2::RelativeSize & rInOutSize,
250
    double fAmountX, double fAmountY )
251
0
{
252
0
    chart2::RelativePosition aPos( rInOutPosition );
253
0
    chart2::RelativeSize     aSize( rInOutSize );
254
0
    const double fPosCheckThreshold = 0.02;
255
0
    const double fSizeCheckThreshold = 0.1;
256
257
    // grow/shrink, back to relative
258
0
    aSize.Primary += fAmountX;
259
0
    aSize.Secondary += fAmountY;
260
261
0
    double fShiftAmountX = fAmountX / 2.0;
262
0
    double fShiftAmountY = fAmountY / 2.0;
263
264
    // shift X
265
0
    switch( rInOutPosition.Anchor )
266
0
    {
267
0
        case drawing::Alignment_TOP_LEFT:
268
0
        case drawing::Alignment_LEFT:
269
0
        case drawing::Alignment_BOTTOM_LEFT:
270
0
            aPos.Primary -= fShiftAmountX;
271
0
            break;
272
0
        case drawing::Alignment_TOP:
273
0
        case drawing::Alignment_CENTER:
274
0
        case drawing::Alignment_BOTTOM:
275
            // nothing
276
0
            break;
277
0
        case drawing::Alignment_TOP_RIGHT:
278
0
        case drawing::Alignment_RIGHT:
279
0
        case drawing::Alignment_BOTTOM_RIGHT:
280
0
            aPos.Primary += fShiftAmountX;
281
0
            break;
282
0
        case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
283
0
            break;
284
0
    }
285
286
    // shift Y
287
0
    switch( rInOutPosition.Anchor )
288
0
    {
289
0
        case drawing::Alignment_TOP:
290
0
        case drawing::Alignment_TOP_LEFT:
291
0
        case drawing::Alignment_TOP_RIGHT:
292
0
            aPos.Secondary -= fShiftAmountY;
293
0
            break;
294
0
        case drawing::Alignment_CENTER:
295
0
        case drawing::Alignment_LEFT:
296
0
        case drawing::Alignment_RIGHT:
297
            // nothing
298
0
            break;
299
0
        case drawing::Alignment_BOTTOM:
300
0
        case drawing::Alignment_BOTTOM_LEFT:
301
0
        case drawing::Alignment_BOTTOM_RIGHT:
302
0
            aPos.Secondary += fShiftAmountY;
303
0
            break;
304
0
        case drawing::Alignment::Alignment_MAKE_FIXED_SIZE:
305
0
            break;
306
0
    }
307
308
    // anchor must not be changed
309
0
    OSL_ASSERT( rInOutPosition.Anchor == aPos.Anchor );
310
311
0
    if( rInOutPosition.Primary == aPos.Primary &&
312
0
        rInOutPosition.Secondary == aPos.Secondary &&
313
0
        rInOutSize.Primary == aSize.Primary &&
314
0
        rInOutSize.Secondary == aSize.Secondary )
315
0
        return false;
316
317
    // Note: this somewhat complicated check allows the output being
318
    // out-of-bounds if the input was also out-of-bounds, and the change is
319
    // for "advantage".  E.g., you have a chart that laps out on the left
320
    // side. If you shrink it, this should be possible, also if it still
321
    // laps out on the left side afterwards. But you shouldn't be able to
322
    // grow it then.
323
324
0
    chart2::RelativePosition aUpperLeft(
325
0
        RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_TOP_LEFT ));
326
0
    chart2::RelativePosition aLowerRight(
327
0
        RelativePositionHelper::getReanchoredPosition( aPos, aSize, drawing::Alignment_BOTTOM_RIGHT ));
328
329
    // Do not grow, if this leads to corners being off-screen
330
0
    if( fAmountX > 0.0 &&
331
0
        ( (aUpperLeft.Primary < fPosCheckThreshold) ||
332
0
          (aLowerRight.Primary > (1.0 - fPosCheckThreshold)) ))
333
0
        return false;
334
0
    if( fAmountY > 0.0 &&
335
0
        ( (aUpperLeft.Secondary < fPosCheckThreshold) ||
336
0
          (aLowerRight.Secondary > (1.0 - fPosCheckThreshold)) ))
337
0
        return false;
338
339
    // Do not shrink, if this leads to a size too small
340
0
    if( fAmountX < 0.0 &&
341
0
        ( aSize.Primary < fSizeCheckThreshold ))
342
0
        return false;
343
0
    if( fAmountY < 0.0 &&
344
0
        ( aSize.Secondary < fSizeCheckThreshold ))
345
0
        return false;
346
347
0
    rInOutPosition = aPos;
348
0
    rInOutSize = aSize;
349
0
    return true;
350
0
}
351
352
bool RelativePositionHelper::moveObject(
353
    chart2::RelativePosition & rInOutPosition,
354
    const chart2::RelativeSize & rObjectSize,
355
    double fAmountX, double fAmountY )
356
0
{
357
0
    chart2::RelativePosition aPos( rInOutPosition );
358
0
    aPos.Primary += fAmountX;
359
0
    aPos.Secondary += fAmountY;
360
0
    const double fPosCheckThreshold = 0.02;
361
362
0
    chart2::RelativePosition aUpperLeft(
363
0
        RelativePositionHelper::getReanchoredPosition( aPos, rObjectSize, drawing::Alignment_TOP_LEFT ));
364
0
    chart2::RelativePosition aLowerRight( aUpperLeft );
365
0
    aLowerRight.Primary += rObjectSize.Primary;
366
0
    aLowerRight.Secondary += rObjectSize.Secondary;
367
368
0
    const double fFarEdgeThreshold = 1.0 - fPosCheckThreshold;
369
0
    if( ( fAmountX > 0.0 && (aLowerRight.Primary > fFarEdgeThreshold)) ||
370
0
        ( fAmountX < 0.0 && (aUpperLeft.Primary < fPosCheckThreshold)) ||
371
0
        ( fAmountY > 0.0 && (aLowerRight.Secondary > fFarEdgeThreshold)) ||
372
0
        ( fAmountY < 0.0 && (aUpperLeft.Secondary < fPosCheckThreshold)) )
373
0
        return false;
374
375
0
    rInOutPosition = aPos;
376
0
    return true;
377
0
}
378
379
} //  namespace chart
380
381
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */