1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/debug.hxx>
21 #include <vcl/window.hxx>
23 #include <svx/svdglue.hxx>
24 #include <svx/svdobj.hxx>
25 #include <svx/svdtrans.hxx>
26 #include <comphelper/lok.hxx>
28 const Size
aGlueHalfSize(4,4);
30 void SdrGluePoint::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
32 if ( m_bReallyAbsolute
== bOn
)
37 m_aPos
=GetAbsolutePos(rObj
);
38 m_bReallyAbsolute
=bOn
;
42 m_bReallyAbsolute
=bOn
;
44 SetAbsolutePos(aPt
,rObj
);
48 Point
SdrGluePoint::GetAbsolutePos(const SdrObject
& rObj
) const
50 if (m_bReallyAbsolute
) return m_aPos
;
51 tools::Rectangle
aSnap(rObj
.GetSnapRect());
52 tools::Rectangle
aBound(rObj
.GetSnapRect());
55 Point
aOfs(aSnap
.Center());
56 switch (GetHorzAlign()) {
57 case SdrAlign::HORZ_LEFT
: aOfs
.setX(aSnap
.Left() ); break;
58 case SdrAlign::HORZ_RIGHT
: aOfs
.setX(aSnap
.Right() ); break;
61 switch (GetVertAlign()) {
62 case SdrAlign::VERT_TOP
: aOfs
.setY(aSnap
.Top() ); break;
63 case SdrAlign::VERT_BOTTOM
: aOfs
.setY(aSnap
.Bottom() ); break;
67 tools::Long nXMul
=aSnap
.Right()-aSnap
.Left();
68 tools::Long nYMul
=aSnap
.Bottom()-aSnap
.Top();
69 tools::Long nXDiv
=10000;
70 tools::Long nYDiv
=10000;
72 aPt
.setX( aPt
.X() * nXMul
);
73 aPt
.setX( aPt
.X() / nXDiv
);
76 aPt
.setY( aPt
.Y() * nYMul
);
77 aPt
.setY( aPt
.Y() / nYDiv
);
81 // Now limit to the BoundRect of the object
82 if (aPt
.X()<aBound
.Left ()) aPt
.setX(aBound
.Left () );
83 if (aPt
.X()>aBound
.Right ()) aPt
.setX(aBound
.Right () );
84 if (aPt
.Y()<aBound
.Top ()) aPt
.setY(aBound
.Top () );
85 if (aPt
.Y()>aBound
.Bottom()) aPt
.setY(aBound
.Bottom() );
89 void SdrGluePoint::SetAbsolutePos(const Point
& rNewPos
, const SdrObject
& rObj
)
91 if (m_bReallyAbsolute
) {
95 tools::Rectangle
aSnap(rObj
.GetSnapRect());
98 Point
aOfs(aSnap
.Center());
99 switch (GetHorzAlign()) {
100 case SdrAlign::HORZ_LEFT
: aOfs
.setX(aSnap
.Left() ); break;
101 case SdrAlign::HORZ_RIGHT
: aOfs
.setX(aSnap
.Right() ); break;
104 switch (GetVertAlign()) {
105 case SdrAlign::VERT_TOP
: aOfs
.setY(aSnap
.Top() ); break;
106 case SdrAlign::VERT_BOTTOM
: aOfs
.setY(aSnap
.Bottom() ); break;
111 tools::Long nXMul
=aSnap
.Right()-aSnap
.Left();
112 tools::Long nYMul
=aSnap
.Bottom()-aSnap
.Top();
113 if (nXMul
==0) nXMul
=1;
114 if (nYMul
==0) nYMul
=1;
115 tools::Long nXDiv
=10000;
116 tools::Long nYDiv
=10000;
118 aPt
.setX( aPt
.X() * nXDiv
);
119 aPt
.setX( aPt
.X() / nXMul
);
122 aPt
.setY( aPt
.Y() * nYDiv
);
123 aPt
.setY( aPt
.Y() / nYMul
);
129 Degree100
SdrGluePoint::GetAlignAngle() const
131 if (m_nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
132 return 0_deg100
; // Invalid!
133 else if (m_nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_CENTER
))
135 else if (m_nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_TOP
))
137 else if (m_nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_TOP
))
139 else if (m_nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_TOP
))
141 else if (m_nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_CENTER
))
143 else if (m_nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_BOTTOM
))
145 else if (m_nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_BOTTOM
))
147 else if (m_nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_BOTTOM
))
152 void SdrGluePoint::SetAlignAngle(Degree100 nAngle
)
154 nAngle
=NormAngle36000(nAngle
);
155 if (nAngle
>=33750_deg100
|| nAngle
<2250_deg100
) m_nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_CENTER
;
156 else if (nAngle
< 6750_deg100
) m_nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_TOP
;
157 else if (nAngle
<11250_deg100
) m_nAlign
=SdrAlign::HORZ_CENTER
|SdrAlign::VERT_TOP
;
158 else if (nAngle
<15750_deg100
) m_nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_TOP
;
159 else if (nAngle
<20250_deg100
) m_nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_CENTER
;
160 else if (nAngle
<24750_deg100
) m_nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_BOTTOM
;
161 else if (nAngle
<29250_deg100
) m_nAlign
=SdrAlign::HORZ_CENTER
|SdrAlign::VERT_BOTTOM
;
162 else if (nAngle
<33750_deg100
) m_nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_BOTTOM
;
165 Degree100
SdrGluePoint::EscDirToAngle(SdrEscapeDirection nEsc
)
168 case SdrEscapeDirection::RIGHT
: return 0_deg100
;
169 case SdrEscapeDirection::TOP
: return 9000_deg100
;
170 case SdrEscapeDirection::LEFT
: return 18000_deg100
;
171 case SdrEscapeDirection::BOTTOM
: return 27000_deg100
;
177 SdrEscapeDirection
SdrGluePoint::EscAngleToDir(Degree100 nAngle
)
179 nAngle
=NormAngle36000(nAngle
);
180 if (nAngle
>=31500_deg100
|| nAngle
<4500_deg100
)
181 return SdrEscapeDirection::RIGHT
;
182 if (nAngle
<13500_deg100
)
183 return SdrEscapeDirection::TOP
;
184 if (nAngle
<22500_deg100
)
185 return SdrEscapeDirection::LEFT
;
187 return SdrEscapeDirection::BOTTOM
;
190 void SdrGluePoint::Rotate(const Point
& rRef
, Degree100 nAngle
, double sn
, double cs
, const SdrObject
* pObj
)
192 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
193 RotatePoint(aPt
,rRef
,sn
,cs
);
194 // rotate reference edge
195 if(m_nAlign
!= (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
197 SetAlignAngle(GetAlignAngle()+nAngle
);
199 // rotate exit directions
200 SdrEscapeDirection nEscDir0
=m_nEscDir
;
201 SdrEscapeDirection nEscDir1
=SdrEscapeDirection::SMART
;
202 if (nEscDir0
&SdrEscapeDirection::LEFT
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::LEFT
)+nAngle
);
203 if (nEscDir0
&SdrEscapeDirection::TOP
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::TOP
)+nAngle
);
204 if (nEscDir0
&SdrEscapeDirection::RIGHT
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::RIGHT
)+nAngle
);
205 if (nEscDir0
&SdrEscapeDirection::BOTTOM
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::BOTTOM
)+nAngle
);
207 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
210 void SdrGluePoint::Mirror(const Point
& rRef1
, const Point
& rRef2
, Degree100 nAngle
, const SdrObject
* pObj
)
212 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
213 MirrorPoint(aPt
,rRef1
,rRef2
);
214 // mirror reference edge
215 if(m_nAlign
!= (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
217 Degree100 nAW
=GetAlignAngle();
218 nAW
+=2_deg100
*(nAngle
-nAW
);
221 // mirror exit directions
222 SdrEscapeDirection nEscDir0
=m_nEscDir
;
223 SdrEscapeDirection nEscDir1
=SdrEscapeDirection::SMART
;
224 if (nEscDir0
&SdrEscapeDirection::LEFT
) {
225 Degree100 nEW
=EscDirToAngle(SdrEscapeDirection::LEFT
);
226 nEW
+=2_deg100
*(nAngle
-nEW
);
227 nEscDir1
|=EscAngleToDir(nEW
);
229 if (nEscDir0
&SdrEscapeDirection::TOP
) {
230 Degree100 nEW
=EscDirToAngle(SdrEscapeDirection::TOP
);
231 nEW
+=2_deg100
*(nAngle
-nEW
);
232 nEscDir1
|=EscAngleToDir(nEW
);
234 if (nEscDir0
&SdrEscapeDirection::RIGHT
) {
235 Degree100 nEW
=EscDirToAngle(SdrEscapeDirection::RIGHT
);
236 nEW
+=2_deg100
*(nAngle
-nEW
);
237 nEscDir1
|=EscAngleToDir(nEW
);
239 if (nEscDir0
&SdrEscapeDirection::BOTTOM
) {
240 Degree100 nEW
=EscDirToAngle(SdrEscapeDirection::BOTTOM
);
241 nEW
+=2_deg100
*(nAngle
-nEW
);
242 nEscDir1
|=EscAngleToDir(nEW
);
245 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
248 void SdrGluePoint::Shear(const Point
& rRef
, double tn
, bool bVShear
, const SdrObject
* pObj
)
250 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
251 ShearPoint(aPt
,rRef
,tn
,bVShear
);
252 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
255 void SdrGluePoint::Invalidate(vcl::Window
& rWin
, const SdrObject
* pObj
) const
257 if (comphelper::LibreOfficeKit::isActive())
259 bool bMapMode
=rWin
.IsMapModeEnabled();
260 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
261 aPt
=rWin
.LogicToPixel(aPt
);
262 rWin
.EnableMapMode(false);
264 Size
aSiz( aGlueHalfSize
);
265 tools::Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),
266 aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
268 // do not erase background, that causes flicker (!)
269 rWin
.Invalidate(aRect
, InvalidateFlags::NoErase
);
271 rWin
.EnableMapMode(bMapMode
);
274 bool SdrGluePoint::IsHit(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
) const
276 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
277 Size aSiz
=rOut
.PixelToLogic(aGlueHalfSize
);
278 tools::Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
279 return aRect
.Contains(rPnt
);
283 SdrGluePointList
& SdrGluePointList::operator=(const SdrGluePointList
& rSrcList
)
285 if (GetCount()!=0) m_aList
.clear();
286 sal_uInt16 nCount
=rSrcList
.GetCount();
287 for (sal_uInt16 i
=0; i
<nCount
; i
++) {
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
)
297 SdrGluePoint
aGP(rGP
);
298 sal_uInt16 nId
=aGP
.GetId();
299 sal_uInt16 nCount
=GetCount();
300 sal_uInt16 nInsPos
=nCount
;
301 sal_uInt16 nLastId
=nCount
!=0 ? m_aList
[nCount
-1].GetId() : 0;
302 DBG_ASSERT(nLastId
>=nCount
,"SdrGluePointList::Insert(): nLastId<nCount");
303 bool bHole
= nLastId
>nCount
;
305 if (!bHole
|| nId
==0) {
309 for (sal_uInt16 nNum
=0; nNum
<nCount
&& !bBrk
; nNum
++) {
310 const auto& pGP2
=m_aList
[nNum
];
311 sal_uInt16 nTmpId
=pGP2
.GetId();
313 nId
=nLastId
+1; // already in use
317 nInsPos
=nNum
; // insert here (sort)
324 m_aList
.emplace(m_aList
.begin()+nInsPos
, aGP
);
328 void SdrGluePointList::Invalidate(vcl::Window
& rWin
, const SdrObject
* pObj
) const
330 if (comphelper::LibreOfficeKit::isActive())
332 for (auto& xGP
: m_aList
)
333 xGP
.Invalidate(rWin
,pObj
);
336 sal_uInt16
SdrGluePointList::FindGluePoint(sal_uInt16 nId
) const
338 // TODO: Implement a better search algorithm
339 // List should be sorted at all times!
340 sal_uInt16 nCount
=GetCount();
341 sal_uInt16 nRet
=SDRGLUEPOINT_NOTFOUND
;
342 for (sal_uInt16 nNum
=0; nNum
<nCount
&& nRet
==SDRGLUEPOINT_NOTFOUND
; nNum
++) {
343 const auto& pGP
=m_aList
[nNum
];
344 if (pGP
.GetId()==nId
) nRet
=nNum
;
349 sal_uInt16
SdrGluePointList::HitTest(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
) const
351 sal_uInt16 nCount
= GetCount();
352 sal_uInt16 nRet
= SDRGLUEPOINT_NOTFOUND
;
353 sal_uInt16 nNum
= nCount
;
354 while ((nNum
>0) && nRet
==SDRGLUEPOINT_NOTFOUND
) {
356 const auto& pGP
= m_aList
[nNum
];
357 if (pGP
.IsHit(rPnt
,rOut
,pObj
))
363 void SdrGluePointList::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
365 for (auto& xGP
: m_aList
)
366 xGP
.SetReallyAbsolute(bOn
,rObj
);
369 void SdrGluePointList::Rotate(const Point
& rRef
, Degree100 nAngle
, double sn
, double cs
, const SdrObject
* pObj
)
371 for (auto& xGP
: m_aList
)
372 xGP
.Rotate(rRef
,nAngle
,sn
,cs
,pObj
);
375 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, const SdrObject
* pObj
)
377 Point
aPt(rRef2
); aPt
-=rRef1
;
378 Degree100 nAngle
=GetAngle(aPt
);
379 Mirror(rRef1
,rRef2
,nAngle
,pObj
);
382 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, Degree100 nAngle
, const SdrObject
* pObj
)
384 for (auto& xGP
: m_aList
)
385 xGP
.Mirror(rRef1
,rRef2
,nAngle
,pObj
);
388 void SdrGluePointList::Shear(const Point
& rRef
, double tn
, bool bVShear
, const SdrObject
* pObj
)
390 for (auto& xGP
: m_aList
)
391 xGP
.Shear(rRef
,tn
,bVShear
,pObj
);
394 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */