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>
27 const Size
aGlueHalfSize(4,4);
29 void SdrGluePoint::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
31 if ( bReallyAbsolute
== bOn
)
36 aPos
=GetAbsolutePos(rObj
);
43 SetAbsolutePos(aPt
,rObj
);
47 Point
SdrGluePoint::GetAbsolutePos(const SdrObject
& rObj
) const
49 if (bReallyAbsolute
) return aPos
;
50 tools::Rectangle
aSnap(rObj
.GetSnapRect());
51 tools::Rectangle
aBound(rObj
.GetSnapRect());
54 Point
aOfs(aSnap
.Center());
55 switch (GetHorzAlign()) {
56 case SdrAlign::HORZ_LEFT
: aOfs
.setX(aSnap
.Left() ); break;
57 case SdrAlign::HORZ_RIGHT
: aOfs
.setX(aSnap
.Right() ); break;
60 switch (GetVertAlign()) {
61 case SdrAlign::VERT_TOP
: aOfs
.setY(aSnap
.Top() ); break;
62 case SdrAlign::VERT_BOTTOM
: aOfs
.setY(aSnap
.Bottom() ); break;
66 tools::Long nXMul
=aSnap
.Right()-aSnap
.Left();
67 tools::Long nYMul
=aSnap
.Bottom()-aSnap
.Top();
68 tools::Long nXDiv
=10000;
69 tools::Long nYDiv
=10000;
71 aPt
.setX( aPt
.X() * nXMul
);
72 aPt
.setX( aPt
.X() / nXDiv
);
75 aPt
.setY( aPt
.Y() * nYMul
);
76 aPt
.setY( aPt
.Y() / nYDiv
);
80 // Now limit to the BoundRect of the object
81 if (aPt
.X()<aBound
.Left ()) aPt
.setX(aBound
.Left () );
82 if (aPt
.X()>aBound
.Right ()) aPt
.setX(aBound
.Right () );
83 if (aPt
.Y()<aBound
.Top ()) aPt
.setY(aBound
.Top () );
84 if (aPt
.Y()>aBound
.Bottom()) aPt
.setY(aBound
.Bottom() );
88 void SdrGluePoint::SetAbsolutePos(const Point
& rNewPos
, const SdrObject
& rObj
)
90 if (bReallyAbsolute
) {
94 tools::Rectangle
aSnap(rObj
.GetSnapRect());
97 Point
aOfs(aSnap
.Center());
98 switch (GetHorzAlign()) {
99 case SdrAlign::HORZ_LEFT
: aOfs
.setX(aSnap
.Left() ); break;
100 case SdrAlign::HORZ_RIGHT
: aOfs
.setX(aSnap
.Right() ); break;
103 switch (GetVertAlign()) {
104 case SdrAlign::VERT_TOP
: aOfs
.setY(aSnap
.Top() ); break;
105 case SdrAlign::VERT_BOTTOM
: aOfs
.setY(aSnap
.Bottom() ); break;
110 tools::Long nXMul
=aSnap
.Right()-aSnap
.Left();
111 tools::Long nYMul
=aSnap
.Bottom()-aSnap
.Top();
112 if (nXMul
==0) nXMul
=1;
113 if (nYMul
==0) nYMul
=1;
114 tools::Long nXDiv
=10000;
115 tools::Long nYDiv
=10000;
117 aPt
.setX( aPt
.X() * nXDiv
);
118 aPt
.setX( aPt
.X() / nXMul
);
121 aPt
.setY( aPt
.Y() * nYDiv
);
122 aPt
.setY( aPt
.Y() / nYMul
);
128 tools::Long
SdrGluePoint::GetAlignAngle() const
130 if (nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
131 return 0; // Invalid!
132 else if (nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_CENTER
))
134 else if (nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_TOP
))
136 else if (nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_TOP
))
138 else if (nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_TOP
))
140 else if (nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_CENTER
))
142 else if (nAlign
== (SdrAlign::HORZ_LEFT
|SdrAlign::VERT_BOTTOM
))
144 else if (nAlign
== (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_BOTTOM
))
146 else if (nAlign
== (SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_BOTTOM
))
151 void SdrGluePoint::SetAlignAngle(tools::Long nAngle
)
153 nAngle
=NormAngle36000(nAngle
);
154 if (nAngle
>=33750 || nAngle
<2250) nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_CENTER
;
155 else if (nAngle
< 6750) nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_TOP
;
156 else if (nAngle
<11250) nAlign
=SdrAlign::HORZ_CENTER
|SdrAlign::VERT_TOP
;
157 else if (nAngle
<15750) nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_TOP
;
158 else if (nAngle
<20250) nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_CENTER
;
159 else if (nAngle
<24750) nAlign
=SdrAlign::HORZ_LEFT
|SdrAlign::VERT_BOTTOM
;
160 else if (nAngle
<29250) nAlign
=SdrAlign::HORZ_CENTER
|SdrAlign::VERT_BOTTOM
;
161 else if (nAngle
<33750) nAlign
=SdrAlign::HORZ_RIGHT
|SdrAlign::VERT_BOTTOM
;
164 tools::Long
SdrGluePoint::EscDirToAngle(SdrEscapeDirection nEsc
)
167 case SdrEscapeDirection::RIGHT
: return 0;
168 case SdrEscapeDirection::TOP
: return 9000;
169 case SdrEscapeDirection::LEFT
: return 18000;
170 case SdrEscapeDirection::BOTTOM
: return 27000;
176 SdrEscapeDirection
SdrGluePoint::EscAngleToDir(tools::Long nAngle
)
178 nAngle
=NormAngle36000(nAngle
);
179 if (nAngle
>=31500 || nAngle
<4500)
180 return SdrEscapeDirection::RIGHT
;
182 return SdrEscapeDirection::TOP
;
184 return SdrEscapeDirection::LEFT
;
186 return SdrEscapeDirection::BOTTOM
;
189 void SdrGluePoint::Rotate(const Point
& rRef
, tools::Long nAngle
, double sn
, double cs
, const SdrObject
* pObj
)
191 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
192 RotatePoint(aPt
,rRef
,sn
,cs
);
193 // rotate reference edge
194 if(nAlign
!= (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
196 SetAlignAngle(GetAlignAngle()+nAngle
);
198 // rotate exit directions
199 SdrEscapeDirection nEscDir0
=nEscDir
;
200 SdrEscapeDirection nEscDir1
=SdrEscapeDirection::SMART
;
201 if (nEscDir0
&SdrEscapeDirection::LEFT
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::LEFT
)+nAngle
);
202 if (nEscDir0
&SdrEscapeDirection::TOP
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::TOP
)+nAngle
);
203 if (nEscDir0
&SdrEscapeDirection::RIGHT
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::RIGHT
)+nAngle
);
204 if (nEscDir0
&SdrEscapeDirection::BOTTOM
) nEscDir1
|= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::BOTTOM
)+nAngle
);
206 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
209 void SdrGluePoint::Mirror(const Point
& rRef1
, const Point
& rRef2
, tools::Long nAngle
, const SdrObject
* pObj
)
211 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
212 MirrorPoint(aPt
,rRef1
,rRef2
);
213 // mirror reference edge
214 if(nAlign
!= (SdrAlign::HORZ_CENTER
|SdrAlign::VERT_CENTER
))
216 tools::Long nAW
=GetAlignAngle();
220 // mirror exit directions
221 SdrEscapeDirection nEscDir0
=nEscDir
;
222 SdrEscapeDirection nEscDir1
=SdrEscapeDirection::SMART
;
223 if (nEscDir0
&SdrEscapeDirection::LEFT
) {
224 tools::Long nEW
=EscDirToAngle(SdrEscapeDirection::LEFT
);
226 nEscDir1
|=EscAngleToDir(nEW
);
228 if (nEscDir0
&SdrEscapeDirection::TOP
) {
229 tools::Long nEW
=EscDirToAngle(SdrEscapeDirection::TOP
);
231 nEscDir1
|=EscAngleToDir(nEW
);
233 if (nEscDir0
&SdrEscapeDirection::RIGHT
) {
234 tools::Long nEW
=EscDirToAngle(SdrEscapeDirection::RIGHT
);
236 nEscDir1
|=EscAngleToDir(nEW
);
238 if (nEscDir0
&SdrEscapeDirection::BOTTOM
) {
239 tools::Long nEW
=EscDirToAngle(SdrEscapeDirection::BOTTOM
);
241 nEscDir1
|=EscAngleToDir(nEW
);
244 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
247 void SdrGluePoint::Shear(const Point
& rRef
, double tn
, bool bVShear
, const SdrObject
* pObj
)
249 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
250 ShearPoint(aPt
,rRef
,tn
,bVShear
);
251 if (pObj
!=nullptr) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
254 void SdrGluePoint::Invalidate(vcl::Window
& rWin
, const SdrObject
* pObj
) const
256 bool bMapMode
=rWin
.IsMapModeEnabled();
257 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
258 aPt
=rWin
.LogicToPixel(aPt
);
259 rWin
.EnableMapMode(false);
261 Size
aSiz( aGlueHalfSize
);
262 tools::Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),
263 aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
265 // do not erase background, that causes flicker (!)
266 rWin
.Invalidate(aRect
, InvalidateFlags::NoErase
);
268 rWin
.EnableMapMode(bMapMode
);
271 bool SdrGluePoint::IsHit(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
) const
273 Point
aPt(pObj
!=nullptr ? GetAbsolutePos(*pObj
) : GetPos());
274 Size aSiz
=rOut
.PixelToLogic(aGlueHalfSize
);
275 tools::Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
276 return aRect
.IsInside(rPnt
);
280 SdrGluePointList
& SdrGluePointList::operator=(const SdrGluePointList
& rSrcList
)
282 if (GetCount()!=0) aList
.clear();
283 sal_uInt16 nCount
=rSrcList
.GetCount();
284 for (sal_uInt16 i
=0; i
<nCount
; i
++) {
290 // The ID's of the glue points always increase monotonously!
291 // If an ID is taken already, the new glue point gets a new ID. ID 0 is reserved.
292 sal_uInt16
SdrGluePointList::Insert(const SdrGluePoint
& rGP
)
294 SdrGluePoint
* pGP
=new SdrGluePoint(rGP
);
295 sal_uInt16 nId
=pGP
->GetId();
296 sal_uInt16 nCount
=GetCount();
297 sal_uInt16 nInsPos
=nCount
;
298 sal_uInt16 nLastId
=nCount
!=0 ? aList
[nCount
-1]->GetId() : 0;
299 DBG_ASSERT(nLastId
>=nCount
,"SdrGluePointList::Insert(): nLastId<nCount");
300 bool bHole
= nLastId
>nCount
;
302 if (!bHole
|| nId
==0) {
306 for (sal_uInt16 nNum
=0; nNum
<nCount
&& !bBrk
; nNum
++) {
307 const auto& pGP2
=aList
[nNum
];
308 sal_uInt16 nTmpId
=pGP2
->GetId();
310 nId
=nLastId
+1; // already in use
314 nInsPos
=nNum
; // insert here (sort)
321 aList
.emplace(aList
.begin()+nInsPos
, pGP
);
325 void SdrGluePointList::Invalidate(vcl::Window
& rWin
, const SdrObject
* pObj
) const
327 for (auto& xGP
: aList
)
328 xGP
->Invalidate(rWin
,pObj
);
331 sal_uInt16
SdrGluePointList::FindGluePoint(sal_uInt16 nId
) const
333 // TODO: Implement a better search algorithm
334 // List should be sorted at all times!
335 sal_uInt16 nCount
=GetCount();
336 sal_uInt16 nRet
=SDRGLUEPOINT_NOTFOUND
;
337 for (sal_uInt16 nNum
=0; nNum
<nCount
&& nRet
==SDRGLUEPOINT_NOTFOUND
; nNum
++) {
338 const auto& pGP
=aList
[nNum
];
339 if (pGP
->GetId()==nId
) nRet
=nNum
;
344 sal_uInt16
SdrGluePointList::HitTest(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
) const
346 sal_uInt16 nCount
= GetCount();
347 sal_uInt16 nRet
= SDRGLUEPOINT_NOTFOUND
;
348 sal_uInt16 nNum
= nCount
;
349 while ((nNum
>0) && nRet
==SDRGLUEPOINT_NOTFOUND
) {
351 const auto& pGP
= aList
[nNum
];
352 if (pGP
->IsHit(rPnt
,rOut
,pObj
))
358 void SdrGluePointList::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
360 for (auto& xGP
: aList
)
361 xGP
->SetReallyAbsolute(bOn
,rObj
);
364 void SdrGluePointList::Rotate(const Point
& rRef
, tools::Long nAngle
, double sn
, double cs
, const SdrObject
* pObj
)
366 for (auto& xGP
: aList
)
367 xGP
->Rotate(rRef
,nAngle
,sn
,cs
,pObj
);
370 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, const SdrObject
* pObj
)
372 Point
aPt(rRef2
); aPt
-=rRef1
;
373 tools::Long nAngle
=GetAngle(aPt
);
374 Mirror(rRef1
,rRef2
,nAngle
,pObj
);
377 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, tools::Long nAngle
, const SdrObject
* pObj
)
379 for (auto& xGP
: aList
)
380 xGP
->Mirror(rRef1
,rRef2
,nAngle
,pObj
);
383 void SdrGluePointList::Shear(const Point
& rRef
, double tn
, bool bVShear
, const SdrObject
* pObj
)
385 for (auto& xGP
: aList
)
386 xGP
->Shear(rRef
,tn
,bVShear
,pObj
);
389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */