Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/svdglue.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 <tools/debug.hxx>
21
#include <vcl/window.hxx>
22
23
#include <svx/svdglue.hxx>
24
#include <svx/svdobj.hxx>
25
#include <svx/svdtrans.hxx>
26
#include <comphelper/lok.hxx>
27
28
const Size aGlueHalfSize(4,4);
29
30
void SdrGluePoint::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
31
732k
{
32
732k
   if ( m_bReallyAbsolute == bOn )
33
366k
       return;
34
35
366k
   if ( bOn )
36
366k
   {
37
366k
       m_aPos=GetAbsolutePos(rObj);
38
366k
       m_bReallyAbsolute=bOn;
39
366k
   }
40
0
   else
41
0
   {
42
0
       m_bReallyAbsolute=bOn;
43
0
       Point aPt(m_aPos);
44
0
       SetAbsolutePos(aPt,rObj);
45
0
   }
46
366k
}
47
48
Point SdrGluePoint::GetAbsolutePos(const SdrObject& rObj) const
49
732k
{
50
732k
    if (m_bReallyAbsolute) return m_aPos;
51
732k
    tools::Rectangle aSnap(rObj.GetSnapRect());
52
732k
    tools::Rectangle aBound(rObj.GetSnapRect());
53
732k
    Point aPt(m_aPos);
54
55
732k
    Point aOfs(aSnap.Center());
56
732k
    switch (GetHorzAlign()) {
57
732k
        case SdrAlign::HORZ_LEFT  : aOfs.setX(aSnap.Left() ); break;
58
0
        case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
59
225
        default: break;
60
732k
    }
61
732k
    switch (GetVertAlign()) {
62
732k
        case SdrAlign::VERT_TOP   : aOfs.setY(aSnap.Top() ); break;
63
0
        case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
64
225
        default: break;
65
732k
    }
66
732k
    if (!m_bNoPercent) {
67
0
        tools::Long nXMul=aSnap.Right()-aSnap.Left();
68
0
        tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
69
0
        tools::Long nXDiv=10000;
70
0
        tools::Long nYDiv=10000;
71
0
        if (nXMul!=nXDiv) {
72
0
            aPt.setX( aPt.X() * nXMul );
73
0
            aPt.setX( aPt.X() / nXDiv );
74
0
        }
75
0
        if (nYMul!=nYDiv) {
76
0
            aPt.setY( aPt.Y() * nYMul );
77
0
            aPt.setY( aPt.Y() / nYDiv );
78
0
        }
79
0
    }
80
732k
    aPt+=aOfs;
81
    // Now limit to the BoundRect of the object
82
732k
    if (aPt.X()<aBound.Left  ()) aPt.setX(aBound.Left  () );
83
732k
    if (aPt.X()>aBound.Right ()) aPt.setX(aBound.Right () );
84
732k
    if (aPt.Y()<aBound.Top   ()) aPt.setY(aBound.Top   () );
85
732k
    if (aPt.Y()>aBound.Bottom()) aPt.setY(aBound.Bottom() );
86
732k
    return aPt;
87
732k
}
88
89
void SdrGluePoint::SetAbsolutePos(const Point& rNewPos, const SdrObject& rObj)
90
366k
{
91
366k
    if (m_bReallyAbsolute) {
92
0
        m_aPos=rNewPos;
93
0
        return;
94
0
    }
95
366k
    tools::Rectangle aSnap(rObj.GetSnapRect());
96
366k
    Point aPt(rNewPos);
97
98
366k
    Point aOfs(aSnap.Center());
99
366k
    switch (GetHorzAlign()) {
100
315k
        case SdrAlign::HORZ_LEFT  : aOfs.setX(aSnap.Left() ); break;
101
46.0k
        case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
102
4.62k
        default: break;
103
366k
    }
104
366k
    switch (GetVertAlign()) {
105
233k
        case SdrAlign::VERT_TOP   : aOfs.setY(aSnap.Top() ); break;
106
78.5k
        case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
107
54.1k
        default: break;
108
366k
    }
109
366k
    aPt-=aOfs;
110
366k
    if (!m_bNoPercent) {
111
0
        tools::Long nXMul=aSnap.Right()-aSnap.Left();
112
0
        tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
113
0
        if (nXMul==0) nXMul=1;
114
0
        if (nYMul==0) nYMul=1;
115
0
        tools::Long nXDiv=10000;
116
0
        tools::Long nYDiv=10000;
117
0
        if (nXMul!=nXDiv) {
118
0
            aPt.setX( aPt.X() * nXDiv );
119
0
            aPt.setX( aPt.X() / nXMul );
120
0
        }
121
0
        if (nYMul!=nYDiv) {
122
0
            aPt.setY( aPt.Y() * nYDiv );
123
0
            aPt.setY( aPt.Y() / nYMul );
124
0
        }
125
0
    }
126
366k
    m_aPos=aPt;
127
366k
}
128
129
Degree100 SdrGluePoint::GetAlignAngle() const
130
366k
{
131
366k
    if (m_nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
132
0
        return 0_deg100; // Invalid!
133
366k
    else if (m_nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER))
134
0
        return 0_deg100;
135
366k
    else if (m_nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP))
136
0
        return 4500_deg100;
137
366k
    else if (m_nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP))
138
0
        return 9000_deg100;
139
366k
    else if (m_nAlign == (SdrAlign::HORZ_LEFT  |SdrAlign::VERT_TOP))
140
366k
        return 13500_deg100;
141
0
    else if (m_nAlign == (SdrAlign::HORZ_LEFT  |SdrAlign::VERT_CENTER))
142
0
        return 18000_deg100;
143
0
    else if (m_nAlign == (SdrAlign::HORZ_LEFT  |SdrAlign::VERT_BOTTOM))
144
0
        return 22500_deg100;
145
0
    else if (m_nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM))
146
0
        return 27000_deg100;
147
0
    else if (m_nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM))
148
0
        return 31500_deg100;
149
0
    return 0_deg100;
150
366k
}
151
152
void SdrGluePoint::SetAlignAngle(Degree100 nAngle)
153
366k
{
154
366k
    nAngle=NormAngle36000(nAngle);
155
366k
    if (nAngle>=33750_deg100 || nAngle<2250_deg100) m_nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER;
156
365k
    else if (nAngle< 6750_deg100) m_nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP   ;
157
324k
    else if (nAngle<11250_deg100) m_nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP   ;
158
321k
    else if (nAngle<15750_deg100) m_nAlign=SdrAlign::HORZ_LEFT  |SdrAlign::VERT_TOP   ;
159
132k
    else if (nAngle<20250_deg100) m_nAlign=SdrAlign::HORZ_LEFT  |SdrAlign::VERT_CENTER;
160
78.5k
    else if (nAngle<24750_deg100) m_nAlign=SdrAlign::HORZ_LEFT  |SdrAlign::VERT_BOTTOM;
161
5.58k
    else if (nAngle<29250_deg100) m_nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM;
162
4.84k
    else if (nAngle<33750_deg100) m_nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM;
163
366k
}
164
165
Degree100 SdrGluePoint::EscDirToAngle(SdrEscapeDirection nEsc)
166
0
{
167
0
    switch (nEsc) {
168
0
        case SdrEscapeDirection::RIGHT : return 0_deg100;
169
0
        case SdrEscapeDirection::TOP   : return 9000_deg100;
170
0
        case SdrEscapeDirection::LEFT  : return 18000_deg100;
171
0
        case SdrEscapeDirection::BOTTOM: return 27000_deg100;
172
0
        default: break;
173
0
    } // switch
174
0
    return 0_deg100;
175
0
}
176
177
SdrEscapeDirection SdrGluePoint::EscAngleToDir(Degree100 nAngle)
178
0
{
179
0
    nAngle=NormAngle36000(nAngle);
180
0
    if (nAngle>=31500_deg100 || nAngle<4500_deg100)
181
0
        return SdrEscapeDirection::RIGHT;
182
0
    if (nAngle<13500_deg100)
183
0
        return SdrEscapeDirection::TOP;
184
0
    if (nAngle<22500_deg100)
185
0
        return SdrEscapeDirection::LEFT;
186
    /* (nAngle<31500)*/
187
0
    return SdrEscapeDirection::BOTTOM;
188
0
}
189
190
void SdrGluePoint::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
191
265k
{
192
265k
    Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
193
265k
    RotatePoint(aPt,rRef,sn,cs);
194
    // rotate reference edge
195
265k
    if(m_nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
196
265k
    {
197
265k
        SetAlignAngle(GetAlignAngle()+nAngle);
198
265k
    }
199
    // rotate exit directions
200
265k
    SdrEscapeDirection nEscDir0=m_nEscDir;
201
265k
    SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
202
265k
    if (nEscDir0&SdrEscapeDirection::LEFT  ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::LEFT  )+nAngle);
203
265k
    if (nEscDir0&SdrEscapeDirection::TOP   ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::TOP   )+nAngle);
204
265k
    if (nEscDir0&SdrEscapeDirection::RIGHT ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::RIGHT )+nAngle);
205
265k
    if (nEscDir0&SdrEscapeDirection::BOTTOM) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::BOTTOM)+nAngle);
206
265k
    m_nEscDir=nEscDir1;
207
265k
    if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
208
265k
}
209
210
void SdrGluePoint::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
211
100k
{
212
100k
    Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
213
100k
    MirrorPoint(aPt,rRef1,rRef2);
214
    // mirror reference edge
215
100k
    if(m_nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
216
100k
    {
217
100k
        Degree100 nAW=GetAlignAngle();
218
100k
        nAW+=2_deg100*(nAngle-nAW);
219
100k
        SetAlignAngle(nAW);
220
100k
    }
221
    // mirror exit directions
222
100k
    SdrEscapeDirection nEscDir0=m_nEscDir;
223
100k
    SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
224
100k
    if (nEscDir0&SdrEscapeDirection::LEFT) {
225
0
        Degree100 nEW=EscDirToAngle(SdrEscapeDirection::LEFT);
226
0
        nEW+=2_deg100*(nAngle-nEW);
227
0
        nEscDir1|=EscAngleToDir(nEW);
228
0
    }
229
100k
    if (nEscDir0&SdrEscapeDirection::TOP) {
230
0
        Degree100 nEW=EscDirToAngle(SdrEscapeDirection::TOP);
231
0
        nEW+=2_deg100*(nAngle-nEW);
232
0
        nEscDir1|=EscAngleToDir(nEW);
233
0
    }
234
100k
    if (nEscDir0&SdrEscapeDirection::RIGHT) {
235
0
        Degree100 nEW=EscDirToAngle(SdrEscapeDirection::RIGHT);
236
0
        nEW+=2_deg100*(nAngle-nEW);
237
0
        nEscDir1|=EscAngleToDir(nEW);
238
0
    }
239
100k
    if (nEscDir0&SdrEscapeDirection::BOTTOM) {
240
0
        Degree100 nEW=EscDirToAngle(SdrEscapeDirection::BOTTOM);
241
0
        nEW+=2_deg100*(nAngle-nEW);
242
0
        nEscDir1|=EscAngleToDir(nEW);
243
0
    }
244
100k
    m_nEscDir=nEscDir1;
245
100k
    if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
246
100k
}
247
248
void SdrGluePoint::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
249
0
{
250
0
    Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
251
0
    ShearPoint(aPt,rRef,tn,bVShear);
252
0
    if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
253
0
}
254
255
void SdrGluePoint::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
256
0
{
257
0
    if (comphelper::LibreOfficeKit::isActive())
258
0
        return;
259
0
    bool bMapMode=rWin.IsMapModeEnabled();
260
0
    Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
261
0
    aPt=rWin.LogicToPixel(aPt);
262
0
    rWin.EnableMapMode(false);
263
264
0
    Size aSiz( aGlueHalfSize );
265
0
    tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),
266
0
                    aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
267
268
    // do not erase background, that causes flicker (!)
269
0
    rWin.Invalidate(aRect, InvalidateFlags::NoErase);
270
271
0
    rWin.EnableMapMode(bMapMode);
272
0
}
273
274
bool SdrGluePoint::IsHit(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
275
0
{
276
0
    Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
277
0
    Size aSiz=rOut.PixelToLogic(aGlueHalfSize);
278
0
    tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
279
0
    return aRect.Contains(rPnt);
280
0
}
281
282
283
SdrGluePointList& SdrGluePointList::operator=(const SdrGluePointList& rSrcList)
284
45.0k
{
285
45.0k
    if (GetCount()!=0) m_aList.clear();
286
45.0k
    sal_uInt16 nCount=rSrcList.GetCount();
287
2.24M
    for (sal_uInt16 i=0; i<nCount; i++) {
288
2.19M
        Insert(rSrcList[i]);
289
2.19M
    }
290
45.0k
    return *this;
291
45.0k
}
292
293
// The ID's of the gluepoints always increase monotonously!
294
// If an ID is taken already, the new gluepoint gets a new ID. ID 0 is reserved.
295
sal_uInt16 SdrGluePointList::Insert(const SdrGluePoint& rGP)
296
4.58M
{
297
4.58M
    SdrGluePoint aGP(rGP);
298
4.58M
    sal_uInt16 nId=aGP.GetId();
299
4.58M
    sal_uInt16 nCount=GetCount();
300
4.58M
    sal_uInt16 nInsPos=nCount;
301
4.58M
    sal_uInt16 nLastId=nCount!=0 ? m_aList[nCount-1].GetId() : 0;
302
4.58M
    DBG_ASSERT(nLastId>=nCount,"SdrGluePointList::Insert(): nLastId<nCount");
303
4.58M
    bool bHole = nLastId>nCount;
304
4.58M
    if (nId<=nLastId) {
305
189k
        if (!bHole || nId==0) {
306
189k
            nId=nLastId+1;
307
189k
        } else {
308
0
            bool bBrk = false;
309
0
            for (sal_uInt16 nNum=0; nNum<nCount && !bBrk; nNum++) {
310
0
                const auto& pGP2=m_aList[nNum];
311
0
                sal_uInt16 nTmpId=pGP2.GetId();
312
0
                if (nTmpId==nId) {
313
0
                    nId=nLastId+1; // already in use
314
0
                    bBrk = true;
315
0
                }
316
0
                if (nTmpId>nId) {
317
0
                    nInsPos=nNum; // insert here (sort)
318
0
                    bBrk = true;
319
0
                }
320
0
            }
321
0
        }
322
189k
        aGP.SetId(nId);
323
189k
    }
324
4.58M
    m_aList.emplace(m_aList.begin()+nInsPos, aGP);
325
4.58M
    return nInsPos;
326
4.58M
}
327
328
void SdrGluePointList::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
329
0
{
330
0
    if (comphelper::LibreOfficeKit::isActive())
331
0
        return;
332
0
    for (auto& xGP : m_aList)
333
0
        xGP.Invalidate(rWin,pObj);
334
0
}
335
336
sal_uInt16 SdrGluePointList::FindGluePoint(sal_uInt16 nId) const
337
303
{
338
    // TODO: Implement a better search algorithm
339
    // List should be sorted at all times!
340
303
    sal_uInt16 nCount=GetCount();
341
303
    sal_uInt16 nRet=SDRGLUEPOINT_NOTFOUND;
342
957
    for (sal_uInt16 nNum=0; nNum<nCount && nRet==SDRGLUEPOINT_NOTFOUND; nNum++) {
343
654
        const auto& pGP=m_aList[nNum];
344
654
        if (pGP.GetId()==nId) nRet=nNum;
345
654
    }
346
303
    return nRet;
347
303
}
348
349
sal_uInt16 SdrGluePointList::HitTest(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
350
0
{
351
0
    sal_uInt16 nCount = GetCount();
352
0
    sal_uInt16 nRet = SDRGLUEPOINT_NOTFOUND;
353
0
    sal_uInt16 nNum = nCount;
354
0
    while ((nNum>0) && nRet==SDRGLUEPOINT_NOTFOUND) {
355
0
        nNum--;
356
0
        const auto& pGP = m_aList[nNum];
357
0
        if (pGP.IsHit(rPnt,rOut,pObj))
358
0
            nRet = nNum;
359
0
    }
360
0
    return nRet;
361
0
}
362
363
void SdrGluePointList::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
364
14.8k
{
365
14.8k
    for (auto& xGP : m_aList)
366
732k
        xGP.SetReallyAbsolute(bOn,rObj);
367
14.8k
}
368
369
void SdrGluePointList::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
370
5.27k
{
371
5.27k
    for (auto& xGP : m_aList)
372
265k
        xGP.Rotate(rRef,nAngle,sn,cs,pObj);
373
5.27k
}
374
375
void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, const SdrObject* pObj)
376
2.13k
{
377
2.13k
    Point aPt(rRef2); aPt-=rRef1;
378
2.13k
    Degree100 nAngle=GetAngle(aPt);
379
2.13k
    Mirror(rRef1,rRef2,nAngle,pObj);
380
2.13k
}
381
382
void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
383
2.13k
{
384
2.13k
    for (auto& xGP : m_aList)
385
100k
        xGP.Mirror(rRef1,rRef2,nAngle,pObj);
386
2.13k
}
387
388
void SdrGluePointList::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
389
0
{
390
0
    for (auto& xGP : m_aList)
391
0
        xGP.Shear(rRef,tn,bVShear,pObj);
392
0
}
393
394
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */