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>
22 #include <svx/svdglue.hxx>
23 #include <svx/svdobj.hxx>
24 #include <svx/svdtrans.hxx>
26 static const Size
aGlueHalfSize(4,4);
28 void SdrGluePoint::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
30 if ( bReallyAbsolute
!= bOn
)
34 aPos
=GetAbsolutePos(rObj
);
41 SetAbsolutePos(aPt
,rObj
);
46 Point
SdrGluePoint::GetAbsolutePos(const SdrObject
& rObj
) const
48 if (bReallyAbsolute
) return aPos
;
49 Rectangle
aSnap(rObj
.GetSnapRect());
50 Rectangle
aBound(rObj
.GetSnapRect());
53 Point
aOfs(aSnap
.Center());
54 switch (GetHorzAlign()) {
55 case SDRHORZALIGN_LEFT
: aOfs
.X()=aSnap
.Left(); break;
56 case SDRHORZALIGN_RIGHT
: aOfs
.X()=aSnap
.Right(); break;
58 switch (GetVertAlign()) {
59 case SDRVERTALIGN_TOP
: aOfs
.Y()=aSnap
.Top(); break;
60 case SDRVERTALIGN_BOTTOM
: aOfs
.Y()=aSnap
.Bottom(); break;
63 long nXMul
=aSnap
.Right()-aSnap
.Left();
64 long nYMul
=aSnap
.Bottom()-aSnap
.Top();
77 // Now limit to the BoundRect of the object
78 if (aPt
.X()<aBound
.Left ()) aPt
.X()=aBound
.Left ();
79 if (aPt
.X()>aBound
.Right ()) aPt
.X()=aBound
.Right ();
80 if (aPt
.Y()<aBound
.Top ()) aPt
.Y()=aBound
.Top ();
81 if (aPt
.Y()>aBound
.Bottom()) aPt
.Y()=aBound
.Bottom();
85 void SdrGluePoint::SetAbsolutePos(const Point
& rNewPos
, const SdrObject
& rObj
)
87 if (bReallyAbsolute
) {
91 Rectangle
aSnap(rObj
.GetSnapRect());
94 Point
aOfs(aSnap
.Center());
95 switch (GetHorzAlign()) {
96 case SDRHORZALIGN_LEFT
: aOfs
.X()=aSnap
.Left(); break;
97 case SDRHORZALIGN_RIGHT
: aOfs
.X()=aSnap
.Right(); break;
99 switch (GetVertAlign()) {
100 case SDRVERTALIGN_TOP
: aOfs
.Y()=aSnap
.Top(); break;
101 case SDRVERTALIGN_BOTTOM
: aOfs
.Y()=aSnap
.Bottom(); break;
105 long nXMul
=aSnap
.Right()-aSnap
.Left();
106 long nYMul
=aSnap
.Bottom()-aSnap
.Top();
107 if (nXMul
==0) nXMul
=1;
108 if (nYMul
==0) nYMul
=1;
123 long SdrGluePoint::GetAlignAngle() const
126 case SDRHORZALIGN_CENTER
|SDRVERTALIGN_CENTER
: return 0; // Invalid!
127 case SDRHORZALIGN_RIGHT
|SDRVERTALIGN_CENTER
: return 0;
128 case SDRHORZALIGN_RIGHT
|SDRVERTALIGN_TOP
: return 4500;
129 case SDRHORZALIGN_CENTER
|SDRVERTALIGN_TOP
: return 9000;
130 case SDRHORZALIGN_LEFT
|SDRVERTALIGN_TOP
: return 13500;
131 case SDRHORZALIGN_LEFT
|SDRVERTALIGN_CENTER
: return 18000;
132 case SDRHORZALIGN_LEFT
|SDRVERTALIGN_BOTTOM
: return 22500;
133 case SDRHORZALIGN_CENTER
|SDRVERTALIGN_BOTTOM
: return 27000;
134 case SDRHORZALIGN_RIGHT
|SDRVERTALIGN_BOTTOM
: return 31500;
139 void SdrGluePoint::SetAlignAngle(long nWink
)
141 nWink
=NormAngle360(nWink
);
142 if (nWink
>=33750 || nWink
<2250) nAlign
=SDRHORZALIGN_RIGHT
|SDRVERTALIGN_CENTER
;
143 else if (nWink
< 6750) nAlign
=SDRHORZALIGN_RIGHT
|SDRVERTALIGN_TOP
;
144 else if (nWink
<11250) nAlign
=SDRHORZALIGN_CENTER
|SDRVERTALIGN_TOP
;
145 else if (nWink
<15750) nAlign
=SDRHORZALIGN_LEFT
|SDRVERTALIGN_TOP
;
146 else if (nWink
<20250) nAlign
=SDRHORZALIGN_LEFT
|SDRVERTALIGN_CENTER
;
147 else if (nWink
<24750) nAlign
=SDRHORZALIGN_LEFT
|SDRVERTALIGN_BOTTOM
;
148 else if (nWink
<29250) nAlign
=SDRHORZALIGN_CENTER
|SDRVERTALIGN_BOTTOM
;
149 else if (nWink
<33750) nAlign
=SDRHORZALIGN_RIGHT
|SDRVERTALIGN_BOTTOM
;
152 long SdrGluePoint::EscDirToAngle(sal_uInt16 nEsc
) const
155 case SDRESC_RIGHT
: return 0;
156 case SDRESC_TOP
: return 9000;
157 case SDRESC_LEFT
: return 18000;
158 case SDRESC_BOTTOM
: return 27000;
163 sal_uInt16
SdrGluePoint::EscAngleToDir(long nWink
) const
165 nWink
=NormAngle360(nWink
);
166 if (nWink
>=31500 || nWink
<4500) return SDRESC_RIGHT
;
167 if (nWink
<13500) return SDRESC_TOP
;
168 if (nWink
<22500) return SDRESC_LEFT
;
169 if (nWink
<31500) return SDRESC_BOTTOM
;
173 void SdrGluePoint::Rotate(const Point
& rRef
, long nWink
, double sn
, double cs
, const SdrObject
* pObj
)
175 Point
aPt(pObj
!=NULL
? GetAbsolutePos(*pObj
) : GetPos());
176 RotatePoint(aPt
,rRef
,sn
,cs
);
177 // rotate reference edge
178 if(nAlign
!= (SDRHORZALIGN_CENTER
|SDRVERTALIGN_CENTER
))
180 SetAlignAngle(GetAlignAngle()+nWink
);
182 // rotate exit directions
183 sal_uInt16 nEscDir0
=nEscDir
;
184 sal_uInt16 nEscDir1
=0;
185 if ((nEscDir0
&SDRESC_LEFT
)!=0) nEscDir1
|=EscAngleToDir(EscDirToAngle(SDRESC_LEFT
)+nWink
);
186 if ((nEscDir0
&SDRESC_TOP
)!=0) nEscDir1
|=EscAngleToDir(EscDirToAngle(SDRESC_TOP
)+nWink
);
187 if ((nEscDir0
&SDRESC_RIGHT
)!=0) nEscDir1
|=EscAngleToDir(EscDirToAngle(SDRESC_RIGHT
)+nWink
);
188 if ((nEscDir0
&SDRESC_BOTTOM
)!=0) nEscDir1
|=EscAngleToDir(EscDirToAngle(SDRESC_BOTTOM
)+nWink
);
190 if (pObj
!=NULL
) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
193 void SdrGluePoint::Mirror(const Point
& rRef1
, const Point
& rRef2
, long nWink
, const SdrObject
* pObj
)
195 Point
aPt(pObj
!=NULL
? GetAbsolutePos(*pObj
) : GetPos());
196 MirrorPoint(aPt
,rRef1
,rRef2
);
197 // mirror reference edge
198 if(nAlign
!= (SDRHORZALIGN_CENTER
|SDRVERTALIGN_CENTER
))
200 long nAW
=GetAlignAngle();
204 // mirror exit directions
205 sal_uInt16 nEscDir0
=nEscDir
;
206 sal_uInt16 nEscDir1
=0;
207 if ((nEscDir0
&SDRESC_LEFT
)!=0) {
208 long nEW
=EscDirToAngle(SDRESC_LEFT
);
210 nEscDir1
|=EscAngleToDir(nEW
);
212 if ((nEscDir0
&SDRESC_TOP
)!=0) {
213 long nEW
=EscDirToAngle(SDRESC_TOP
);
215 nEscDir1
|=EscAngleToDir(nEW
);
217 if ((nEscDir0
&SDRESC_RIGHT
)!=0) {
218 long nEW
=EscDirToAngle(SDRESC_RIGHT
);
220 nEscDir1
|=EscAngleToDir(nEW
);
222 if ((nEscDir0
&SDRESC_BOTTOM
)!=0) {
223 long nEW
=EscDirToAngle(SDRESC_BOTTOM
);
225 nEscDir1
|=EscAngleToDir(nEW
);
228 if (pObj
!=NULL
) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
231 void SdrGluePoint::Shear(const Point
& rRef
, long /*nWink*/, double tn
, bool bVShear
, const SdrObject
* pObj
)
233 Point
aPt(pObj
!=NULL
? GetAbsolutePos(*pObj
) : GetPos());
234 ShearPoint(aPt
,rRef
,tn
,bVShear
);
235 if (pObj
!=NULL
) SetAbsolutePos(aPt
,*pObj
); else SetPos(aPt
);
238 void SdrGluePoint::Invalidate(Window
& rWin
, const SdrObject
* pObj
) const
240 bool bMapMerk
=rWin
.IsMapModeEnabled();
241 Point
aPt(pObj
!=NULL
? GetAbsolutePos(*pObj
) : GetPos());
242 aPt
=rWin
.LogicToPixel(aPt
);
243 rWin
.EnableMapMode(false);
245 Size
aSiz( aGlueHalfSize
);
246 Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),
247 aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
249 // do not erase background, that causes flicker (!)
250 rWin
.Invalidate(aRect
, INVALIDATE_NOERASE
);
252 rWin
.EnableMapMode(bMapMerk
);
255 bool SdrGluePoint::IsHit(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
) const
257 Point
aPt(pObj
!=NULL
? GetAbsolutePos(*pObj
) : GetPos());
258 Size aSiz
=rOut
.PixelToLogic(aGlueHalfSize
);
259 Rectangle
aRect(aPt
.X()-aSiz
.Width(),aPt
.Y()-aSiz
.Height(),aPt
.X()+aSiz
.Width(),aPt
.Y()+aSiz
.Height());
260 return aRect
.IsInside(rPnt
);
265 void SdrGluePointList::Clear()
267 sal_uInt16 nAnz
=GetCount();
268 for (sal_uInt16 i
=0; i
<nAnz
; i
++) {
274 void SdrGluePointList::operator=(const SdrGluePointList
& rSrcList
)
276 if (GetCount()!=0) Clear();
277 sal_uInt16 nAnz
=rSrcList
.GetCount();
278 for (sal_uInt16 i
=0; i
<nAnz
; i
++) {
283 // The ID's of the glue points always increase monotonously!
284 // If an ID is taken already, the new glue point gets a new ID. ID 0 is reserved.
285 sal_uInt16
SdrGluePointList::Insert(const SdrGluePoint
& rGP
)
287 SdrGluePoint
* pGP
=new SdrGluePoint(rGP
);
288 sal_uInt16 nId
=pGP
->GetId();
289 sal_uInt16 nAnz
=GetCount();
290 sal_uInt16 nInsPos
=nAnz
;
291 sal_uInt16 nLastId
=nAnz
!=0 ? GetObject(nAnz
-1)->GetId() : 0;
292 DBG_ASSERT(nLastId
>=nAnz
,"SdrGluePointList::Insert(): nLastId<nAnz");
293 bool bHole
= nLastId
>nAnz
;
295 if (!bHole
|| nId
==0) {
299 for (sal_uInt16 nNum
=0; nNum
<nAnz
&& !bBrk
; nNum
++) {
300 const SdrGluePoint
* pGP2
=GetObject(nNum
);
301 sal_uInt16 nTmpId
=pGP2
->GetId();
303 nId
=nLastId
+1; // already in use
307 nInsPos
=nNum
; // insert here (sort)
314 aList
.insert(aList
.begin()+nInsPos
, pGP
);
318 void SdrGluePointList::Invalidate(Window
& rWin
, const SdrObject
* pObj
) const
320 sal_uInt16 nAnz
=GetCount();
321 for (sal_uInt16 nNum
=0; nNum
<nAnz
; nNum
++) {
322 GetObject(nNum
)->Invalidate(rWin
,pObj
);
326 sal_uInt16
SdrGluePointList::FindGluePoint(sal_uInt16 nId
) const
328 // TODO: Implement a better search algorithm
329 // List should be sorted at all times!
330 sal_uInt16 nAnz
=GetCount();
331 sal_uInt16 nRet
=SDRGLUEPOINT_NOTFOUND
;
332 for (sal_uInt16 nNum
=0; nNum
<nAnz
&& nRet
==SDRGLUEPOINT_NOTFOUND
; nNum
++) {
333 const SdrGluePoint
* pGP
=GetObject(nNum
);
334 if (pGP
->GetId()==nId
) nRet
=nNum
;
339 sal_uInt16
SdrGluePointList::HitTest(const Point
& rPnt
, const OutputDevice
& rOut
, const SdrObject
* pObj
, bool bBack
, bool bNext
, sal_uInt16 nId0
) const
341 sal_uInt16 nAnz
=GetCount();
342 sal_uInt16 nRet
=SDRGLUEPOINT_NOTFOUND
;
343 sal_uInt16 nNum
=bBack
? 0 : nAnz
;
344 while ((bBack
? nNum
<nAnz
: nNum
>0) && nRet
==SDRGLUEPOINT_NOTFOUND
) {
346 const SdrGluePoint
* pGP
=GetObject(nNum
);
348 if (pGP
->GetId()==nId0
) bNext
=false;
350 if (pGP
->IsHit(rPnt
,rOut
,pObj
)) nRet
=nNum
;
357 void SdrGluePointList::SetReallyAbsolute(bool bOn
, const SdrObject
& rObj
)
359 sal_uInt16 nAnz
=GetCount();
360 for (sal_uInt16 nNum
=0; nNum
<nAnz
; nNum
++) {
361 GetObject(nNum
)->SetReallyAbsolute(bOn
,rObj
);
365 void SdrGluePointList::Rotate(const Point
& rRef
, long nWink
, double sn
, double cs
, const SdrObject
* pObj
)
367 sal_uInt16 nAnz
=GetCount();
368 for (sal_uInt16 nNum
=0; nNum
<nAnz
; nNum
++) {
369 GetObject(nNum
)->Rotate(rRef
,nWink
,sn
,cs
,pObj
);
373 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, const SdrObject
* pObj
)
375 Point
aPt(rRef2
); aPt
-=rRef1
;
376 long nWink
=GetAngle(aPt
);
377 Mirror(rRef1
,rRef2
,nWink
,pObj
);
380 void SdrGluePointList::Mirror(const Point
& rRef1
, const Point
& rRef2
, long nWink
, const SdrObject
* pObj
)
382 sal_uInt16 nAnz
=GetCount();
383 for (sal_uInt16 nNum
=0; nNum
<nAnz
; nNum
++) {
384 GetObject(nNum
)->Mirror(rRef1
,rRef2
,nWink
,pObj
);
388 void SdrGluePointList::Shear(const Point
& rRef
, long nWink
, double tn
, bool bVShear
, const SdrObject
* pObj
)
390 sal_uInt16 nAnz
=GetCount();
391 for (sal_uInt16 nNum
=0; nNum
<nAnz
; nNum
++) {
392 GetObject(nNum
)->Shear(rRef
,nWink
,tn
,bVShear
,pObj
);
396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */