xref: /aoo41x/main/svx/source/svdraw/svdopath.cxx (revision a5258243)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include <tools/bigint.hxx>
32 #include <svx/svdopath.hxx>
33 #include <math.h>
34 #include <svx/xpool.hxx>
35 #include <svx/xpoly.hxx>
36 #include <svx/svdattr.hxx>
37 #include <svx/svdtrans.hxx>
38 #include <svx/svdetc.hxx>
39 #include <svx/svddrag.hxx>
40 #include <svx/svdmodel.hxx>
41 #include <svx/svdpage.hxx>
42 #include <svx/svdhdl.hxx>
43 #include <svx/svdview.hxx>  // fuer MovCreate bei Freihandlinien
44 #include "svx/svdglob.hxx"  // Stringcache
45 #include "svx/svdstr.hrc"   // Objektname
46 
47 #ifdef _MSC_VER
48 #pragma optimize ("",off)
49 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
50 #endif
51 
52 #include <svx/xlnwtit.hxx>
53 #include <svx/xlnclit.hxx>
54 #include <svx/xflclit.hxx>
55 #include <svx/svdogrp.hxx>
56 #include <svx/polypolygoneditor.hxx>
57 #include <svx/xlntrit.hxx>
58 #include <vcl/salbtype.hxx>		// FRound
59 #include "svdoimp.hxx"
60 #include <svx/sdr/contact/viewcontactofsdrpathobj.hxx>
61 #include <basegfx/matrix/b2dhommatrix.hxx>
62 
63 // #104018# replace macros above with type-safe methods
64 inline sal_Int32 ImplTwipsToMM(sal_Int32 nVal) { return ((nVal * 127 + 36) / 72); }
65 inline sal_Int32 ImplMMToTwips(sal_Int32 nVal) { return ((nVal * 72 + 63) / 127); }
66 inline sal_Int64 ImplTwipsToMM(sal_Int64 nVal) { return ((nVal * 127 + 36) / 72); }
67 inline sal_Int64 ImplMMToTwips(sal_Int64 nVal) { return ((nVal * 72 + 63) / 127); }
68 inline double ImplTwipsToMM(double fVal) { return (fVal * (127.0 / 72.0)); }
69 inline double ImplMMToTwips(double fVal) { return (fVal * (72.0 / 127.0)); }
70 #include <basegfx/point/b2dpoint.hxx>
71 #include <basegfx/polygon/b2dpolypolygontools.hxx>
72 #include <basegfx/matrix/b2dhommatrix.hxx>
73 #include <basegfx/range/b2drange.hxx>
74 #include <basegfx/curve/b2dcubicbezier.hxx>
75 #include <basegfx/polygon/b2dpolygontools.hxx>
76 #include <svx/sdr/attribute/sdrtextattribute.hxx>
77 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
78 #include <basegfx/matrix/b2dhommatrixtools.hxx>
79 #include <svx/sdr/attribute/sdrformtextattribute.hxx>
80 
81 using namespace sdr;
82 
83 inline sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, FASTBOOL bClosed)
84 {
85 	if (nPnt>0) {
86 		nPnt--;
87 	} else {
88 		nPnt=nPntMax;
89 		if (bClosed) nPnt--;
90 	}
91 	return nPnt;
92 }
93 
94 inline sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, FASTBOOL bClosed)
95 {
96 	nPnt++;
97 	if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
98 	return nPnt;
99 }
100 
101 struct ImpSdrPathDragData  : public SdrDragStatUserData
102 {
103 	XPolygon					aXP;            // Ausschnitt aud dem Originalpolygon
104 	FASTBOOL					bValid;         // sal_False = zu wenig Punkte
105 	FASTBOOL					bClosed;        // geschlossenes Objekt?
106 	sal_uInt16						nPoly;          // Nummer des Polygons im PolyPolygon
107 	sal_uInt16						nPnt;           // Punktnummer innerhalb des obigen Polygons
108 	sal_uInt16						nPntAnz;        // Punktanzahl des Polygons
109 	sal_uInt16						nPntMax;        // Maximaler Index
110 	FASTBOOL					bBegPnt;        // Gedraggter Punkt ist der Anfangspunkt einer Polyline
111 	FASTBOOL					bEndPnt;        // Gedraggter Punkt ist der Endpunkt einer Polyline
112 	sal_uInt16						nPrevPnt;       // Index des vorherigen Punkts
113 	sal_uInt16						nNextPnt;       // Index des naechsten Punkts
114 	FASTBOOL					bPrevIsBegPnt;  // Vorheriger Punkt ist Anfangspunkt einer Polyline
115 	FASTBOOL					bNextIsEndPnt;  // Folgepunkt ist Endpunkt einer Polyline
116 	sal_uInt16						nPrevPrevPnt;   // Index des vorvorherigen Punkts
117 	sal_uInt16						nNextNextPnt;   // Index des uebernaechsten Punkts
118 	FASTBOOL					bControl;       // Punkt ist ein Kontrollpunkt
119 	FASTBOOL					bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
120 	FASTBOOL					bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
121 	FASTBOOL					bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
122 	FASTBOOL					bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
123 	sal_uInt16						nPrevPrevPnt0;
124 	sal_uInt16						nPrevPnt0;
125 	sal_uInt16						nPnt0;
126 	sal_uInt16						nNextPnt0;
127 	sal_uInt16						nNextNextPnt0;
128 	FASTBOOL					bEliminate;     // Punkt loeschen? (wird von MovDrag gesetzt)
129 
130 	// ##
131 	sal_Bool						mbMultiPointDrag;
132 	const XPolyPolygon			maOrig;
133 	XPolyPolygon				maMove;
134 	Container					maHandles;
135 
136 public:
137 	ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, sal_Bool bMuPoDr, const SdrDragStat& rDrag);
138 	void ResetPoly(const SdrPathObj& rPO);
139 	sal_Bool IsMultiPointDrag() const { return mbMultiPointDrag; }
140 };
141 
142 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, sal_Bool bMuPoDr, const SdrDragStat& rDrag)
143 :	aXP(5),
144 	mbMultiPointDrag(bMuPoDr),
145 	maOrig(rPO.GetPathPoly()),
146 	maHandles(0)
147 {
148 	if(mbMultiPointDrag)
149 	{
150 		const SdrMarkView& rMarkView = *rDrag.GetView();
151 		const SdrHdlList& rHdlList = rMarkView.GetHdlList();
152 		const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
153         const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
154 
155 		for(sal_uInt32 a(0); a < nHdlCount; a++)
156 		{
157 			SdrHdl* pTestHdl = rHdlList.GetHdl(a);
158 
159 			if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
160 			{
161 				maHandles.Insert(pTestHdl, CONTAINER_APPEND);
162 			}
163 		}
164 
165 		maMove = maOrig;
166 		bValid = sal_True;
167 	}
168 	else
169 	{
170 		bValid=sal_False;
171 		bClosed=rPO.IsClosed();          // geschlossenes Objekt?
172 		nPoly=(sal_uInt16)rHdl.GetPolyNum();            // Nummer des Polygons im PolyPolygon
173 		nPnt=(sal_uInt16)rHdl.GetPointNum();            // Punktnummer innerhalb des obigen Polygons
174 		const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
175 		nPntAnz=aTmpXP.GetPointCount();        // Punktanzahl des Polygons
176 		if (nPntAnz==0 || (bClosed && nPntAnz==1)) return; // min. 1Pt bei Line, min. 2 bei Polygon
177 		nPntMax=nPntAnz-1;                  // Maximaler Index
178 		bBegPnt=!bClosed && nPnt==0;        // Gedraggter Punkt ist der Anfangspunkt einer Polyline
179 		bEndPnt=!bClosed && nPnt==nPntMax;  // Gedraggter Punkt ist der Endpunkt einer Polyline
180 		if (bClosed && nPntAnz<=3) {        // Falls Polygon auch nur eine Linie ist
181 			bBegPnt=(nPntAnz<3) || nPnt==0;
182 			bEndPnt=(nPntAnz<3) || nPnt==nPntMax-1;
183 		}
184 		nPrevPnt=nPnt;                      // Index des vorherigen Punkts
185 		nNextPnt=nPnt;                      // Index des naechsten Punkts
186 		if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
187 		if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
188 		bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
189 		bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
190 		nPrevPrevPnt=nPnt;                  // Index des vorvorherigen Punkts
191 		nNextNextPnt=nPnt;                  // Index des uebernaechsten Punkts
192 		if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
193 		if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
194 		bControl=rHdl.IsPlusHdl();          // Punkt ist ein Kontrollpunkt
195 		bIsPrevControl=sal_False;               // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
196 		bIsNextControl=sal_False;               // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
197 		bPrevIsControl=sal_False;               // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
198 		bNextIsControl=sal_False;               // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
199 		if (bControl) {
200 			bIsPrevControl=aTmpXP.IsControl(nPrevPnt);
201 			bIsNextControl=!bIsPrevControl;
202 		} else {
203 			bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==XPOLY_CONTROL;
204 			bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==XPOLY_CONTROL;
205 		}
206 		nPrevPrevPnt0=nPrevPrevPnt;
207 		nPrevPnt0    =nPrevPnt;
208 		nPnt0        =nPnt;
209 		nNextPnt0    =nNextPnt;
210 		nNextNextPnt0=nNextNextPnt;
211 		nPrevPrevPnt=0;
212 		nPrevPnt=1;
213 		nPnt=2;
214 		nNextPnt=3;
215 		nNextNextPnt=4;
216 		bEliminate=sal_False;
217 		ResetPoly(rPO);
218 		bValid=sal_True;
219 	}
220 }
221 
222 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
223 {
224 	const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
225 	aXP[0]=aTmpXP[nPrevPrevPnt0];  aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
226 	aXP[1]=aTmpXP[nPrevPnt0];      aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
227 	aXP[2]=aTmpXP[nPnt0];          aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
228 	aXP[3]=aTmpXP[nNextPnt0];      aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
229 	aXP[4]=aTmpXP[nNextNextPnt0];  aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
230 }
231 
232 /*************************************************************************/
233 
234 struct ImpPathCreateUser  : public SdrDragStatUserData
235 {
236 	Point					aBezControl0;
237 	Point					aBezStart;
238 	Point					aBezCtrl1;
239 	Point					aBezCtrl2;
240 	Point					aBezEnd;
241 	Point					aCircStart;
242 	Point					aCircEnd;
243 	Point					aCircCenter;
244 	Point					aLineStart;
245 	Point					aLineEnd;
246 	Point					aRectP1;
247 	Point					aRectP2;
248 	Point					aRectP3;
249 	long					nCircRadius;
250 	long					nCircStWink;
251 	long					nCircRelWink;
252 	FASTBOOL				bBezier;
253 	FASTBOOL				bBezHasCtrl0;
254 	FASTBOOL				bCurve;
255 	FASTBOOL				bCircle;
256 	FASTBOOL				bAngleSnap;
257 	FASTBOOL				bLine;
258 	FASTBOOL				bLine90;
259 	FASTBOOL				bRect;
260 	FASTBOOL				bMixedCreate;
261 	sal_uInt16					nBezierStartPoint;
262 	SdrObjKind				eStartKind;
263 	SdrObjKind				eAktKind;
264 
265 public:
266 	ImpPathCreateUser(): nCircRadius(0),nCircStWink(0),nCircRelWink(0),
267 		bBezier(sal_False),bBezHasCtrl0(sal_False),bCurve(sal_False),bCircle(sal_False),bAngleSnap(sal_False),bLine(sal_False),bLine90(sal_False),bRect(sal_False),
268 		bMixedCreate(sal_False),nBezierStartPoint(0),eStartKind(OBJ_NONE),eAktKind(OBJ_NONE) { }
269 
270 	void ResetFormFlags() { bBezier=sal_False; bCurve=sal_False; bCircle=sal_False; bLine=sal_False; bRect=sal_False; }
271 	FASTBOOL IsFormFlag() const { return bBezier || bCurve || bCircle || bLine || bRect; }
272 	XPolygon GetFormPoly() const;
273 	FASTBOOL CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown);
274 	XPolygon GetBezierPoly() const;
275 	//int CalcCurve(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView) { return sal_False; }
276 	XPolygon GetCurvePoly() const { return XPolygon(); }
277 	FASTBOOL CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
278 	XPolygon GetCirclePoly() const;
279 	FASTBOOL CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
280 	Point    CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView* pView) const;
281 	XPolygon GetLinePoly() const;
282 	FASTBOOL CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
283 	XPolygon GetRectPoly() const;
284 };
285 
286 XPolygon ImpPathCreateUser::GetFormPoly() const
287 {
288 	if (bBezier) return GetBezierPoly();
289 	if (bCurve)  return GetCurvePoly();
290 	if (bCircle) return GetCirclePoly();
291 	if (bLine)   return GetLinePoly();
292 	if (bRect)   return GetRectPoly();
293 	return XPolygon();
294 }
295 
296 FASTBOOL ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown)
297 {
298 	FASTBOOL bRet=sal_True;
299 	aBezStart=rP1;
300 	aBezCtrl1=rP1+rDir;
301 	aBezCtrl2=rP2;
302 
303 	// #i21479#
304 	// Also copy the end point when no end point is set yet
305 	if (!bMouseDown || (0L == aBezEnd.X() && 0L == aBezEnd.Y())) aBezEnd=rP2;
306 
307 	bBezier=bRet;
308 	return bRet;
309 }
310 
311 XPolygon ImpPathCreateUser::GetBezierPoly() const
312 {
313 	XPolygon aXP(4);
314 	aXP[0]=aBezStart; aXP.SetFlags(0,XPOLY_SMOOTH);
315 	aXP[1]=aBezCtrl1; aXP.SetFlags(1,XPOLY_CONTROL);
316 	aXP[2]=aBezCtrl2; aXP.SetFlags(2,XPOLY_CONTROL);
317 	aXP[3]=aBezEnd;
318 	return aXP;
319 }
320 
321 FASTBOOL ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
322 {
323 	long nTangAngle=GetAngle(rDir);
324 	aCircStart=rP1;
325 	aCircEnd=rP2;
326 	aCircCenter=rP1;
327 	long dx=rP2.X()-rP1.X();
328 	long dy=rP2.Y()-rP1.Y();
329 	long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
330 	dAngle=NormAngle360(dAngle);
331 	long nTmpAngle=NormAngle360(9000-dAngle);
332 	FASTBOOL bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
333 	long nRad=0;
334 	if (bRet) {
335 		double cs=cos(nTmpAngle*nPi180);
336 		double nR=(double)GetLen(Point(dx,dy))/cs/2;
337 		nRad=Abs(Round(nR));
338 	}
339 	if (dAngle<18000) {
340 		nCircStWink=NormAngle360(nTangAngle-9000);
341 		nCircRelWink=NormAngle360(2*dAngle);
342 		aCircCenter.X()+=Round(nRad*cos((nTangAngle+9000)*nPi180));
343 		aCircCenter.Y()-=Round(nRad*sin((nTangAngle+9000)*nPi180));
344 	} else {
345 		nCircStWink=NormAngle360(nTangAngle+9000);
346 		nCircRelWink=-NormAngle360(36000-2*dAngle);
347 		aCircCenter.X()+=Round(nRad*cos((nTangAngle-9000)*nPi180));
348 		aCircCenter.Y()-=Round(nRad*sin((nTangAngle-9000)*nPi180));
349 	}
350 	bAngleSnap=pView!=NULL && pView->IsAngleSnapEnabled();
351 	if (bAngleSnap) {
352 		long nSA=pView->GetSnapAngle();
353 		if (nSA!=0) { // Winkelfang
354 			FASTBOOL bNeg=nCircRelWink<0;
355 			if (bNeg) nCircRelWink=-nCircRelWink;
356 			nCircRelWink+=nSA/2;
357 			nCircRelWink/=nSA;
358 			nCircRelWink*=nSA;
359 			nCircRelWink=NormAngle360(nCircRelWink);
360 			if (bNeg) nCircRelWink=-nCircRelWink;
361 		}
362 	}
363 	nCircRadius=nRad;
364 	if (nRad==0 || Abs(nCircRelWink)<5) bRet=sal_False;
365 	bCircle=bRet;
366 	return bRet;
367 }
368 
369 XPolygon ImpPathCreateUser::GetCirclePoly() const
370 {
371 	if (nCircRelWink>=0) {
372 		XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
373 					 sal_uInt16((nCircStWink+5)/10),sal_uInt16((nCircStWink+nCircRelWink+5)/10),sal_False);
374 		aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
375 		if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
376 		return aXP;
377 	} else {
378 		XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
379 					 sal_uInt16(NormAngle360(nCircStWink+nCircRelWink+5)/10),sal_uInt16((nCircStWink+5)/10),sal_False);
380 		sal_uInt16 nAnz=aXP.GetPointCount();
381 		for (sal_uInt16 nNum=nAnz/2; nNum>0;) {
382 			nNum--; // XPoly Punktreihenfolge umkehren
383 			sal_uInt16 n2=nAnz-nNum-1;
384 			Point aPt(aXP[nNum]);
385 			aXP[nNum]=aXP[n2];
386 			aXP[n2]=aPt;
387 		}
388 		aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
389 		if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
390 		return aXP;
391 	}
392 }
393 
394 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView* pView) const
395 {
396 	long x=aCsr.X(),x1=x,x2=x;
397 	long y=aCsr.Y(),y1=y,y2=y;
398 	FASTBOOL bHLin=nDirY==0;
399 	FASTBOOL bVLin=nDirX==0;
400 	if (bHLin) y=0;
401 	else if (bVLin) x=0;
402 	else {
403 		x1=BigMulDiv(y,nDirX,nDirY);
404 		y2=BigMulDiv(x,nDirY,nDirX);
405 		long l1=Abs(x1)+Abs(y1);
406 		long l2=Abs(x2)+Abs(y2);
407 		if ((l1<=l2) != (pView!=NULL && pView->IsBigOrtho())) {
408 			x=x1; y=y1;
409 		} else {
410 			x=x2; y=y2;
411 		}
412 	}
413 	return Point(x,y);
414 }
415 
416 FASTBOOL ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
417 {
418 	aLineStart=rP1;
419 	aLineEnd=rP2;
420 	bLine90=sal_False;
421 	if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=sal_False; return sal_False; }
422 	Point aTmpPt(rP2-rP1);
423 	long nDirX=rDir.X();
424 	long nDirY=rDir.Y();
425 	Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=Abs(aP1.X())+Abs(aP1.Y());
426 	Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=Abs(aP2.X())+Abs(aP2.Y());
427 	if (pView!=NULL && pView->IsOrtho()) nQ1=0; // Ortho schaltet rechtwinklig aus
428 	bLine90=nQ1>2*nQ2;
429 	if (!bLine90) { // glatter Uebergang
430 		aLineEnd+=aP1;
431 	} else {          // rechtwinkliger Uebergang
432 		aLineEnd+=aP2;
433 	}
434 	bLine=sal_True;
435 	return sal_True;
436 }
437 
438 XPolygon ImpPathCreateUser::GetLinePoly() const
439 {
440 	XPolygon aXP(2);
441 	aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,XPOLY_SMOOTH);
442 	aXP[1]=aLineEnd;
443 	return aXP;
444 }
445 
446 FASTBOOL ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
447 {
448 	aRectP1=rP1;
449 	aRectP2=rP1;
450 	aRectP3=rP2;
451 	if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=sal_False; return sal_False; }
452 	Point aTmpPt(rP2-rP1);
453 	long nDirX=rDir.X();
454 	long nDirY=rDir.Y();
455 	long x=aTmpPt.X();
456 	long y=aTmpPt.Y();
457 	FASTBOOL bHLin=nDirY==0;
458 	FASTBOOL bVLin=nDirX==0;
459 	if (bHLin) y=0;
460 	else if (bVLin) x=0;
461 	else {
462 		y=BigMulDiv(x,nDirY,nDirX);
463 		long nHypLen=aTmpPt.Y()-y;
464 		long nTangAngle=-GetAngle(rDir);
465 		// sin=g/h, g=h*sin
466 		double a=nTangAngle*nPi180;
467 		double sn=sin(a);
468 		double cs=cos(a);
469 		double nGKathLen=nHypLen*sn;
470 		y+=Round(nGKathLen*sn);
471 		x+=Round(nGKathLen*cs);
472 	}
473 	aRectP2.X()+=x;
474 	aRectP2.Y()+=y;
475 	if (pView!=NULL && pView->IsOrtho()) {
476 		long dx1=aRectP2.X()-aRectP1.X(); long dx1a=Abs(dx1);
477 		long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=Abs(dy1);
478 		long dx2=aRectP3.X()-aRectP2.X(); long dx2a=Abs(dx2);
479 		long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=Abs(dy2);
480 		FASTBOOL b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
481 		if (b1MoreThan2 != pView->IsBigOrtho()) {
482 			long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
483 			long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
484 			aRectP2.X()+=xtemp;
485 			aRectP2.Y()+=ytemp;
486 			aRectP3.X()+=xtemp;
487 			aRectP3.Y()+=ytemp;
488 		} else {
489 			long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
490 			long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
491 			aRectP3.X()+=xtemp;
492 			aRectP3.Y()+=ytemp;
493 		}
494 	}
495 	bRect=sal_True;
496 	return sal_True;
497 }
498 
499 XPolygon ImpPathCreateUser::GetRectPoly() const
500 {
501 	XPolygon aXP(3);
502 	aXP[0]=aRectP1; aXP.SetFlags(0,XPOLY_SMOOTH);
503 	aXP[1]=aRectP2;
504 	if (aRectP3!=aRectP2) aXP[2]=aRectP3;
505 	return aXP;
506 }
507 
508 /*************************************************************************/
509 
510 class ImpPathForDragAndCreate
511 {
512 	SdrPathObj&					mrSdrPathObject;
513 	XPolyPolygon				aPathPolygon;
514 	SdrObjKind					meObjectKind;
515     ImpSdrPathDragData*         mpSdrPathDragData;
516 	bool						mbCreating;
517 
518 public:
519 	ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
520 	~ImpPathForDragAndCreate();
521 
522 	// drag stuff
523 	bool beginPathDrag( SdrDragStat& rDrag )  const;
524     bool movePathDrag( SdrDragStat& rDrag ) const;
525     bool endPathDrag( SdrDragStat& rDrag );
526     //void cancelSpecialDrag( SdrDragStat& rDrag ) const;
527 	String getSpecialDragComment(const SdrDragStat& rDrag) const;
528     basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
529 
530 	// create stuff
531 	FASTBOOL BegCreate(SdrDragStat& rStat);
532 	FASTBOOL MovCreate(SdrDragStat& rStat);
533 	FASTBOOL EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
534 	FASTBOOL BckCreate(SdrDragStat& rStat);
535 	void BrkCreate(SdrDragStat& rStat);
536 	Pointer GetCreatePointer() const;
537 
538 	// helping stuff
539 	bool IsClosed(SdrObjKind eKind) const { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
540 	bool IsFreeHand(SdrObjKind eKind) const { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
541 	bool IsBezier(SdrObjKind eKind) const { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
542 	bool IsCreating() const { return mbCreating; }
543 
544 	// get the polygon
545 	basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
546 	basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag) const;
547 	basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return  aPathPolygon.getB2DPolyPolygon(); }
548 };
549 
550 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
551 :	mrSdrPathObject(rSdrPathObject),
552 	aPathPolygon(rSdrPathObject.GetPathPoly()),
553 	meObjectKind(mrSdrPathObject.meKind),
554     mpSdrPathDragData(0),
555 	mbCreating(false)
556 {
557 }
558 
559 ImpPathForDragAndCreate::~ImpPathForDragAndCreate()
560 {
561     if(mpSdrPathDragData)
562     {
563         delete mpSdrPathDragData;
564     }
565 }
566 
567 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat& rDrag )  const
568 {
569 	const SdrHdl* pHdl=rDrag.GetHdl();
570 	if(!pHdl)
571 		return sal_False;
572 
573 	sal_Bool bMultiPointDrag(sal_True);
574 
575 	if(aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()].IsControl((sal_uInt16)pHdl->GetPointNum()))
576 		bMultiPointDrag = sal_False;
577 
578 	if(bMultiPointDrag)
579 	{
580 		const SdrMarkView& rMarkView = *rDrag.GetView();
581 		const SdrHdlList& rHdlList = rMarkView.GetHdlList();
582 		const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
583         const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
584 		sal_uInt32 nSelectedPoints(0);
585 
586 		for(sal_uInt32 a(0); a < nHdlCount; a++)
587 		{
588 			SdrHdl* pTestHdl = rHdlList.GetHdl(a);
589 
590 			if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
591 			{
592 				nSelectedPoints++;
593 			}
594 		}
595 
596 		if(nSelectedPoints <= 1)
597 			bMultiPointDrag = sal_False;
598 	}
599 
600 	((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag);
601 
602     if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
603     {
604 		DBG_ERROR("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData ist ungueltig");
605         delete mpSdrPathDragData;
606         ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = 0;
607 		return false;
608 	}
609 
610 	return true;
611 }
612 
613 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
614 {
615 	if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
616     {
617 		DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
618 		return false;
619 	}
620 
621 	if(mpSdrPathDragData->IsMultiPointDrag())
622 	{
623 		Point aDelta(rDrag.GetNow() - rDrag.GetStart());
624 
625 		if(aDelta.X() || aDelta.Y())
626 		{
627 			for(sal_uInt32 a(0); a < mpSdrPathDragData->maHandles.Count(); a++)
628 			{
629 				SdrHdl* pHandle = (SdrHdl*)mpSdrPathDragData->maHandles.GetObject(a);
630 				const sal_uInt16 nPolyIndex((sal_uInt16)pHandle->GetPolyNum());
631 				const sal_uInt16 nPointIndex((sal_uInt16)pHandle->GetPointNum());
632 				const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
633 				XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
634 				const sal_uInt16 nPointCount(rOrig.GetPointCount());
635 				sal_Bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
636 
637 				// move point itself
638 				rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
639 
640 				// when point is first and poly closed, move close point, too.
641 				if(nPointCount > 0 && !nPointIndex && bClosed)
642 				{
643 					rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
644 
645 					// when moving the last point it may be necessary to move the
646 					// control point in front of this one, too.
647 					if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
648 						rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
649 				}
650 
651 				// is a control point before this?
652 				if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
653 				{
654 					// Yes, move it, too
655 					rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
656 				}
657 
658 				// is a control point after this?
659 				if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
660 				{
661 					// Yes, move it, too
662 					rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
663 				}
664 			}
665 		}
666 	}
667 	else
668 	{
669 		mpSdrPathDragData->ResetPoly(mrSdrPathObject);
670 
671 		// Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
672 		FASTBOOL bClosed       =mpSdrPathDragData->bClosed       ; // geschlossenes Objekt?
673 		sal_uInt16   nPnt          =mpSdrPathDragData->nPnt          ; // Punktnummer innerhalb des obigen Polygons
674 		FASTBOOL bBegPnt       =mpSdrPathDragData->bBegPnt       ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
675 		FASTBOOL bEndPnt       =mpSdrPathDragData->bEndPnt       ; // Gedraggter Punkt ist der Endpunkt einer Polyline
676 		sal_uInt16   nPrevPnt      =mpSdrPathDragData->nPrevPnt      ; // Index des vorherigen Punkts
677 		sal_uInt16   nNextPnt      =mpSdrPathDragData->nNextPnt      ; // Index des naechsten Punkts
678 		FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
679 		FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
680 		sal_uInt16   nPrevPrevPnt  =mpSdrPathDragData->nPrevPrevPnt  ; // Index des vorvorherigen Punkts
681 		sal_uInt16   nNextNextPnt  =mpSdrPathDragData->nNextNextPnt  ; // Index des uebernaechsten Punkts
682 		FASTBOOL bControl      =mpSdrPathDragData->bControl      ; // Punkt ist ein Kontrollpunkt
683 		//int bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
684 		FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
685 		FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
686 		FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
687 
688 		// Ortho bei Linien/Polygonen = Winkel beibehalten
689 		if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsOrtho()) {
690 			FASTBOOL bBigOrtho=rDrag.GetView()->IsBigOrtho();
691 			Point  aPos(rDrag.GetNow());      // die aktuelle Position
692 			Point  aPnt(mpSdrPathDragData->aXP[nPnt]);      // der gedraggte Punkt
693 			sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // seine Nachbarpunkte
694 			Point  aNeuPos1,aNeuPos2;         // die neuen Alternativen fuer aPos
695 			FASTBOOL bPnt1=sal_False,bPnt2=sal_False; // die neuen Alternativen gueltig?
696 			if (!bClosed && mpSdrPathDragData->nPntAnz>=2) { // Mind. 2 Pt bei Linien
697 				if (!bBegPnt) nPnt1=nPrevPnt;
698 				if (!bEndPnt) nPnt2=nNextPnt;
699 			}
700 			if (bClosed && mpSdrPathDragData->nPntAnz>=3) { // Mind. 3 Pt bei Polygon
701 				nPnt1=nPrevPnt;
702 				nPnt2=nNextPnt;
703 			}
704 			if (nPnt1!=0xFFFF && !bPrevIsControl) {
705 				Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
706 				long ndx0=aPnt.X()-aPnt1.X();
707 				long ndy0=aPnt.Y()-aPnt1.Y();
708 				FASTBOOL bHLin=ndy0==0;
709 				FASTBOOL bVLin=ndx0==0;
710 				if (!bHLin || !bVLin) {
711 					long ndx=aPos.X()-aPnt1.X();
712 					long ndy=aPos.Y()-aPnt1.Y();
713 					bPnt1=sal_True;
714 					double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
715 					double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
716 					FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
717 					FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
718 					if (bHor) ndy=long(ndy0*nXFact);
719 					if (bVer) ndx=long(ndx0*nYFact);
720 					aNeuPos1=aPnt1;
721 					aNeuPos1.X()+=ndx;
722 					aNeuPos1.Y()+=ndy;
723 				}
724 			}
725 			if (nPnt2!=0xFFFF && !bNextIsControl) {
726 				Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
727 				long ndx0=aPnt.X()-aPnt2.X();
728 				long ndy0=aPnt.Y()-aPnt2.Y();
729 				FASTBOOL bHLin=ndy0==0;
730 				FASTBOOL bVLin=ndx0==0;
731 				if (!bHLin || !bVLin) {
732 					long ndx=aPos.X()-aPnt2.X();
733 					long ndy=aPos.Y()-aPnt2.Y();
734 					bPnt2=sal_True;
735 					double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
736 					double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
737 					FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
738 					FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
739 					if (bHor) ndy=long(ndy0*nXFact);
740 					if (bVer) ndx=long(ndx0*nYFact);
741 					aNeuPos2=aPnt2;
742 					aNeuPos2.X()+=ndx;
743 					aNeuPos2.Y()+=ndy;
744 				}
745 			}
746 			if (bPnt1 && bPnt2) { // beide Alternativen vorhanden (Konkurenz)
747 				BigInt nX1(aNeuPos1.X()-aPos.X()); nX1*=nX1;
748 				BigInt nY1(aNeuPos1.Y()-aPos.Y()); nY1*=nY1;
749 				BigInt nX2(aNeuPos2.X()-aPos.X()); nX2*=nX2;
750 				BigInt nY2(aNeuPos2.Y()-aPos.Y()); nY2*=nY2;
751 				nX1+=nY1; // Korrekturabstand zum Quadrat
752 				nX2+=nY2; // Korrekturabstand zum Quadrat
753 				// Die Alternative mit dem geringeren Korrekturbedarf gewinnt
754 				if (nX1<nX2) bPnt2=sal_False; else bPnt1=sal_False;
755 			}
756 			if (bPnt1) rDrag.Now()=aNeuPos1;
757 			if (bPnt2) rDrag.Now()=aNeuPos2;
758 		}
759 		rDrag.SetActionRect(Rectangle(rDrag.GetNow(),rDrag.GetNow()));
760 
761 		// IBM Special: Punkte eliminieren, wenn die beiden angrenzenden
762 		//              Linien eh' fast 180 deg sind.
763 		if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsEliminatePolyPoints() &&
764 			!bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
765 		{
766 			Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
767 			aPt-=rDrag.GetNow();
768 			long nWink1=GetAngle(aPt);
769 			aPt=rDrag.GetNow();
770 			aPt-=mpSdrPathDragData->aXP[nPrevPnt];
771 			long nWink2=GetAngle(aPt);
772 			long nDiff=nWink1-nWink2;
773 			nDiff=Abs(nDiff);
774 			mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
775 			if (mpSdrPathDragData->bEliminate) { // Position anpassen, damit Smooth an den Enden stimmt
776 				aPt=mpSdrPathDragData->aXP[nNextPnt];
777 				aPt+=mpSdrPathDragData->aXP[nPrevPnt];
778 				aPt/=2;
779 				rDrag.Now()=aPt;
780 			}
781 		}
782 
783 		// Um diese Entfernung wurde insgesamt gedraggd
784 		Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
785 
786 		// Insgesamt sind 8 Faelle moeglich:
787 		//    X      1. Weder rechts noch links Ctrl.
788 		// o--X--o   2. Rechts und links Ctrl, gedraggd wird St.
789 		// o--X      3. Nur links Ctrl, gedraggd wird St.
790 		//    X--o   4. Nur rechts Ctrl, gedraggd wird St.
791 		// x--O--o   5. Rechts und links Ctrl, gedraggd wird links.
792 		// x--O      6. Nur links Ctrl, gedraggd wird links.
793 		// o--O--x   7. Rechts und links Ctrl, gedraggd wird rechts.
794 		//    O--x   8. Nur rechts Ctrl, gedraggd wird rechts.
795 		// Zusaetzlich ist zu beachten, dass das Veraendern einer Linie (keine Kurve)
796 		// eine evtl. Kurve am anderen Ende der Linie bewirkt, falls dort Smooth
797 		// gesetzt ist (Kontrollpunktausrichtung an Gerade).
798 
799 		mpSdrPathDragData->aXP[nPnt]+=aDiff;
800 
801 		// Nun symmetrische PlusHandles etc. checken
802 		if (bControl) { // Faelle 5,6,7,8
803 			sal_uInt16   nSt=nPnt;   // der zugehoerige Stuetzpunkt
804 			sal_uInt16   nFix=nPnt;  // der gegenueberliegende Kontrollpunkt
805 			if (bIsNextControl) { // Wenn der naechste ein Kontrollpunkt ist, muss der vorh. der Stuetzpunkt sein
806 				nSt=nPrevPnt;
807 				nFix=nPrevPrevPnt;
808 			} else {
809 				nSt=nNextPnt;
810 				nFix=nNextNextPnt;
811 			}
812 			if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
813 				mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
814 			}
815 		}
816 
817 		if (!bControl) { // Faelle 1,2,3,4 wobei bei 1 nix passiert und bei 3+4 unten noch mehr folgt
818 			// die beiden Kontrollpunkte mit verschieben
819 			if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
820 			if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
821 			// Kontrollpunkt ggf. an Gerade ausrichten
822 			if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
823 				if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // Fall 3
824 					mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
825 				}
826 				if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // Fall 4
827 					mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
828 				}
829 			}
830 			// Und nun noch die anderen Enden der Strecken ueberpruefen (nPnt+-1).
831 			// Ist dort eine Kurve (IsControl(nPnt+-2)) mit SmoothJoin (nPnt+-1),
832 			// so muss der entsprechende Kontrollpunkt (nPnt+-2) angepasst werden.
833 			if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
834 				if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
835 					mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
836 				}
837 			}
838 			if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
839 				if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
840 					mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
841 				}
842 			}
843 		}
844 	}
845 
846 	return true;
847 }
848 
849 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat& rDrag)
850 {
851 	Point aLinePt1;
852 	Point aLinePt2;
853 	bool bLineGlueMirror(OBJ_LINE == meObjectKind);
854 	if (bLineGlueMirror) { // #40549#
855 		XPolygon& rXP=aPathPolygon[0];
856 		aLinePt1=rXP[0];
857 		aLinePt2=rXP[1];
858 	}
859 
860     if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
861     {
862 		DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
863 		return false;
864 	}
865 
866 	if(mpSdrPathDragData->IsMultiPointDrag())
867 	{
868 		aPathPolygon = mpSdrPathDragData->maMove;
869 	}
870 	else
871 	{
872 		const SdrHdl* pHdl=rDrag.GetHdl();
873 
874 		// Referenz auf das Polygon
875 		XPolygon& rXP=aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()];
876 
877 		// Die 5 Punkte die sich evtl. geaendert haben
878 		if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
879 		if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
880 		if (!mpSdrPathDragData->bBegPnt)       rXP[mpSdrPathDragData->nPrevPnt0]    =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
881 		if (!mpSdrPathDragData->bEndPnt)       rXP[mpSdrPathDragData->nNextPnt0]    =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
882         rXP[mpSdrPathDragData->nPnt0]        =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
883 
884 		// Letzter Punkt muss beim Geschlossenen immer gleich dem Ersten sein
885 		if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
886 
887 		if (mpSdrPathDragData->bEliminate)
888 		{
889 			basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
890 			sal_uInt32 nPoly,nPnt;
891 
892 			if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
893 			{
894 				basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
895 				aCandidate.remove(nPnt);
896 
897 				if((IsClosed(meObjectKind) && aCandidate.count() < 3L) || aCandidate.count() < 2L)
898 				{
899 					aTempPolyPolygon.remove(nPoly);
900 				}
901 				else
902 				{
903 					aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
904 				}
905 			}
906 
907 			aPathPolygon = XPolyPolygon(aTempPolyPolygon);
908 		}
909 
910 		// Winkel anpassen fuer Text an einfacher Linie
911 		if (bLineGlueMirror)
912 		{ // #40549#
913 			Point aLinePt1_(aPathPolygon[0][0]);
914 			Point aLinePt2_(aPathPolygon[0][1]);
915 			FASTBOOL bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
916 			FASTBOOL bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
917 			if (bXMirr || bYMirr) {
918 				Point aRef1(mrSdrPathObject.GetSnapRect().Center());
919 				if (bXMirr) {
920 					Point aRef2(aRef1);
921 					aRef2.Y()++;
922 					mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
923 				}
924 				if (bYMirr) {
925 					Point aRef2(aRef1);
926 					aRef2.X()++;
927 					mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
928 				}
929 			}
930 		}
931 	}
932 
933 	delete mpSdrPathDragData;
934     mpSdrPathDragData = 0;
935 
936 	return true;
937 }
938 
939 /*void ImpPathForDragAndCreate::cancelSpecialDrag( SdrDragStat& rDrag ) const
940 {
941 	ImpSdrPathDragData* pID=(ImpSdrPathDragData*)rDrag.GetUser();
942 	if (pID!=NULL) {
943 		delete pID;
944 		rDrag.SetUser(NULL);
945 	}
946 }*/
947 
948 String ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
949 {
950 	XubString aStr;
951 	const SdrHdl* pHdl = rDrag.GetHdl();
952     const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
953 
954     if(bCreateComment && rDrag.GetUser())
955     {
956         // #i103058# re-add old creation comment mode
957     	ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
958 		const SdrObjKind eKindMerk(meObjectKind);
959 		mrSdrPathObject.meKind = pU->eAktKind;
960 		mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewCreateObj, aStr);
961 		mrSdrPathObject.meKind = eKindMerk;
962 
963 		Point aPrev(rDrag.GetPrev());
964 		Point aNow(rDrag.GetNow());
965 
966 		if(pU->bLine)
967 			aNow = pU->aLineEnd;
968 
969 		aNow -= aPrev;
970 		aStr.AppendAscii(" (");
971 
972 		XubString aMetr;
973 
974 		if(pU->bCircle)
975 		{
976 			mrSdrPathObject.GetModel()->TakeWinkStr(Abs(pU->nCircRelWink), aMetr);
977 			aStr += aMetr;
978 			aStr.AppendAscii(" r=");
979 			mrSdrPathObject.GetModel()->TakeMetricStr(pU->nCircRadius, aMetr, sal_True);
980 			aStr += aMetr;
981 		}
982 
983 		aStr.AppendAscii("dx=");
984 		mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X(), aMetr, sal_True);
985 		aStr += aMetr;
986 
987 		aStr.AppendAscii(" dy=");
988 		mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y(), aMetr, sal_True);
989 		aStr += aMetr;
990 
991 		if(!IsFreeHand(meObjectKind))
992 		{
993 			sal_Int32 nLen(GetLen(aNow));
994 			aStr.AppendAscii("  l=");
995 			mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
996 			aStr += aMetr;
997 
998 			sal_Int32 nWink(GetAngle(aNow));
999 			aStr += sal_Unicode(' ');
1000 			mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1001 			aStr += aMetr;
1002 		}
1003 
1004 		aStr += sal_Unicode(')');
1005 	}
1006 	else if(!mrSdrPathObject.GetModel() || !pHdl)
1007 	{
1008         // #i103058# fallback when no model and/or Handle, both needed
1009         // for else-path
1010 		mrSdrPathObject.ImpTakeDescriptionStr(STR_DragPathObj, aStr);
1011     }
1012 	else
1013 	{
1014         // #i103058# standard for modification; model and handle needed
1015         ImpSdrPathDragData* pDragData = mpSdrPathDragData;
1016 
1017         if(!pDragData)
1018         {
1019             // getSpecialDragComment is also used from create, so fallback to GetUser()
1020             // when mpSdrPathDragData is not set
1021             pDragData = (ImpSdrPathDragData*)rDrag.GetUser();
1022         }
1023 
1024         if(!pDragData)
1025         {
1026 	        DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
1027 	        return String();
1028         }
1029 
1030 		if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
1031 		{
1032 			// Punkt von ...
1033 			mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewMarkedPoint, aStr);
1034 
1035 			// %O loeschen
1036 			XubString aStr2(ImpGetResStr(STR_EditDelete));
1037 
1038 			// UNICODE: Punkt von ... loeschen
1039 			aStr2.SearchAndReplaceAscii("%1", aStr);
1040 
1041 			return aStr2;
1042 		}
1043 
1044 		// dx=0.00 dy=0.00                // Beide Seiten Bezier
1045 		// dx=0.00 dy=0.00  l=0.00 0.00�  // Anfang oder Ende oder eine Seite Bezier bzw. Hebel
1046 		// dx=0.00 dy=0.00  l=0.00 0.00� / l=0.00 0.00�   // Mittendrin
1047 		XubString aMetr;
1048 		Point aBeg(rDrag.GetStart());
1049 		Point aNow(rDrag.GetNow());
1050 
1051 		aStr = String();
1052 		aStr.AppendAscii("dx=");
1053 		mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X() - aBeg.X(), aMetr, sal_True);
1054 		aStr += aMetr;
1055 
1056 		aStr.AppendAscii(" dy=");
1057 		mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y() - aBeg.Y(), aMetr, sal_True);
1058 		aStr += aMetr;
1059 
1060 		if(!pDragData->IsMultiPointDrag())
1061 		{
1062 			sal_uInt16 nPntNum((sal_uInt16)pHdl->GetPointNum());
1063 			const XPolygon& rXPoly = aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1064 			sal_uInt16 nPntAnz((sal_uInt16)rXPoly.GetPointCount());
1065 			sal_Bool bClose(IsClosed(meObjectKind));
1066 
1067 			if(bClose)
1068 				nPntAnz--;
1069 
1070 			if(pHdl->IsPlusHdl())
1071 			{
1072 				// Hebel
1073 				sal_uInt16 nRef(nPntNum);
1074 
1075 				if(rXPoly.IsControl(nPntNum + 1))
1076 					nRef--;
1077 				else
1078 					nRef++;
1079 
1080 				aNow -= rXPoly[nRef];
1081 
1082 				sal_Int32 nLen(GetLen(aNow));
1083 				aStr.AppendAscii("  l=");
1084 				mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1085 				aStr += aMetr;
1086 
1087 				sal_Int32 nWink(GetAngle(aNow));
1088 				aStr += sal_Unicode(' ');
1089 				mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1090 				aStr += aMetr;
1091 			}
1092 			else if(nPntAnz > 1)
1093 			{
1094 				sal_uInt16 nPntMax(nPntAnz - 1);
1095 				Point aPt1,aPt2;
1096 				sal_Bool bIsClosed(IsClosed(meObjectKind));
1097 				sal_Bool bPt1(nPntNum > 0);
1098 				sal_Bool bPt2(nPntNum < nPntMax);
1099 
1100 				if(bIsClosed && nPntAnz > 2)
1101 				{
1102 					bPt1 = sal_True;
1103 					bPt2 = sal_True;
1104 				}
1105 
1106 				sal_uInt16 nPt1,nPt2;
1107 
1108 				if(nPntNum > 0)
1109 					nPt1 = nPntNum - 1;
1110 				else
1111 					nPt1 = nPntMax;
1112 
1113 				if(nPntNum < nPntMax)
1114 					nPt2 = nPntNum + 1;
1115 				else
1116 					nPt2 = 0;
1117 
1118 				if(bPt1 && rXPoly.IsControl(nPt1))
1119 					bPt1 = sal_False; // Keine Anzeige
1120 
1121 				if(bPt2 && rXPoly.IsControl(nPt2))
1122 					bPt2 = sal_False; // von Bezierdaten
1123 
1124 				if(bPt1)
1125 				{
1126 					Point aPt(aNow);
1127 					aPt -= rXPoly[nPt1];
1128 
1129 					sal_Int32 nLen(GetLen(aPt));
1130 					aStr.AppendAscii("  l=");
1131 					mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1132 					aStr += aMetr;
1133 
1134 					sal_Int32 nWink(GetAngle(aPt));
1135 					aStr += sal_Unicode(' ');
1136 					mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1137 					aStr += aMetr;
1138 				}
1139 
1140 				if(bPt2)
1141 				{
1142 					if(bPt1)
1143 						aStr.AppendAscii(" / ");
1144 					else
1145 						aStr.AppendAscii("  ");
1146 
1147 					Point aPt(aNow);
1148 					aPt -= rXPoly[nPt2];
1149 
1150 					sal_Int32 nLen(GetLen(aPt));
1151 					aStr.AppendAscii("l=");
1152 					mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1153 					aStr += aMetr;
1154 
1155 					sal_Int32 nWink(GetAngle(aPt));
1156 					aStr += sal_Unicode(' ');
1157 					mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1158 					aStr += aMetr;
1159 				}
1160 			}
1161 		}
1162 	}
1163 
1164 	return aStr;
1165 }
1166 
1167 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
1168 {
1169     if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1170     {
1171 		DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
1172 		return basegfx::B2DPolyPolygon();
1173 	}
1174 
1175 	XPolyPolygon aRetval;
1176 
1177     if(mpSdrPathDragData->IsMultiPointDrag())
1178 	{
1179 		aRetval.Insert(mpSdrPathDragData->maMove);
1180 	}
1181 	else
1182 	{
1183 		const XPolygon& rXP=aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1184 		if (rXP.GetPointCount()<=2) { //|| rXPoly.GetFlags(1)==XPOLY_CONTROL && rXPoly.GetPointCount()<=4
1185 			XPolygon aXPoly(rXP);
1186 			aXPoly[(sal_uInt16)rDrag.GetHdl()->GetPointNum()]=rDrag.GetNow();
1187 			aRetval.Insert(aXPoly);
1188 			return aRetval.getB2DPolyPolygon();
1189 		}
1190 		// Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
1191 		FASTBOOL bClosed       =mpSdrPathDragData->bClosed       ; // geschlossenes Objekt?
1192 		sal_uInt16   nPntAnz       =mpSdrPathDragData->nPntAnz       ; // Punktanzahl
1193 		sal_uInt16   nPnt          =mpSdrPathDragData->nPnt          ; // Punktnummer innerhalb des Polygons
1194 		FASTBOOL bBegPnt       =mpSdrPathDragData->bBegPnt       ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
1195 		FASTBOOL bEndPnt       =mpSdrPathDragData->bEndPnt       ; // Gedraggter Punkt ist der Endpunkt einer Polyline
1196 		sal_uInt16   nPrevPnt      =mpSdrPathDragData->nPrevPnt      ; // Index des vorherigen Punkts
1197 		sal_uInt16   nNextPnt      =mpSdrPathDragData->nNextPnt      ; // Index des naechsten Punkts
1198 		FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
1199 		FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
1200 		sal_uInt16   nPrevPrevPnt  =mpSdrPathDragData->nPrevPrevPnt  ; // Index des vorvorherigen Punkts
1201 		sal_uInt16   nNextNextPnt  =mpSdrPathDragData->nNextNextPnt  ; // Index des uebernaechsten Punkts
1202 		FASTBOOL bControl      =mpSdrPathDragData->bControl      ; // Punkt ist ein Kontrollpunkt
1203 		//int bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
1204 		FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
1205 		FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
1206 		FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
1207 		XPolygon aXPoly(mpSdrPathDragData->aXP);
1208 		XPolygon aLine1(2);
1209 		XPolygon aLine2(2);
1210 		XPolygon aLine3(2);
1211 		XPolygon aLine4(2);
1212 		if (bControl) {
1213 			aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1214 			if (bIsNextControl) { // bin ich Kontrollpunkt hinter der Stuetzstelle?
1215 				aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1216 				aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1217 				aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1218 				if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1219 					aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1220 					aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1221 					// Hebellienien fuer das gegenueberliegende Kurvensegment
1222 					aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1223 					aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1224 					aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1225 					aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1226 				} else {
1227 					aXPoly.Remove(0,1);
1228 				}
1229 			} else { // ansonsten bin ich Kontrollpunkt vor der Stuetzstelle
1230 				aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1231 				aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1232 				aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1233 				if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1234 					aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1235 					aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1236 					// Hebellinien fuer das gegenueberliegende Kurvensegment
1237 					aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1238 					aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1239 					aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1240 					aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1241 				} else {
1242 					aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1243 				}
1244 			}
1245 		} else { // ansonsten kein Kontrollpunkt
1246 			if (mpSdrPathDragData->bEliminate) {
1247 				aXPoly.Remove(2,1);
1248 			}
1249 			if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_NORMAL);
1250 			else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1251 				aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1252 				aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1253 			} else {
1254 				aXPoly.Remove(0,1);
1255 				if (bBegPnt) aXPoly.Remove(0,1);
1256 			}
1257 			if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_NORMAL);
1258 			else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1259 				aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1260 				aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1261 			} else {
1262 				aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1263 				if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1264 			}
1265 			if (bClosed) { // "Birnenproblem": 2 Linien, 1 Kurve, alles Smooth, Punkt zw. beiden Linien wird gedraggt
1266 				if (aXPoly.GetPointCount()>nPntAnz && aXPoly.IsControl(1)) {
1267 					sal_uInt16 a=aXPoly.GetPointCount();
1268 					aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1269 					aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1270 					aXPoly.Remove(0,3);
1271 				}
1272 			}
1273 		}
1274 		aRetval.Insert(aXPoly);
1275 		if (aLine1.GetPointCount()>1) aRetval.Insert(aLine1);
1276 		if (aLine2.GetPointCount()>1) aRetval.Insert(aLine2);
1277 		if (aLine3.GetPointCount()>1) aRetval.Insert(aLine3);
1278 		if (aLine4.GetPointCount()>1) aRetval.Insert(aLine4);
1279 	}
1280 
1281 	return aRetval.getB2DPolyPolygon();
1282 }
1283 
1284 FASTBOOL ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
1285 {
1286 	bool bFreeHand(IsFreeHand(meObjectKind));
1287 	rStat.SetNoSnap(bFreeHand);
1288 	rStat.SetOrtho8Possible();
1289 	aPathPolygon.Clear();
1290 	mbCreating=sal_True;
1291 	FASTBOOL bMakeStartPoint=sal_True;
1292 	SdrView* pView=rStat.GetView();
1293 	if (pView!=NULL && pView->IsUseIncompatiblePathCreateInterface() &&
1294 		(meObjectKind==OBJ_POLY || meObjectKind==OBJ_PLIN || meObjectKind==OBJ_PATHLINE || meObjectKind==OBJ_PATHFILL)) {
1295 		bMakeStartPoint=sal_False;
1296 	}
1297 	aPathPolygon.Insert(XPolygon());
1298 	aPathPolygon[0][0]=rStat.GetStart();
1299 	if (bMakeStartPoint) {
1300 		aPathPolygon[0][1]=rStat.GetNow();
1301 	}
1302 	ImpPathCreateUser* pU=new ImpPathCreateUser;
1303 	pU->eStartKind=meObjectKind;
1304 	pU->eAktKind=meObjectKind;
1305 	rStat.SetUser(pU);
1306 	return sal_True;
1307 }
1308 
1309 FASTBOOL ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
1310 {
1311 	ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1312 	SdrView* pView=rStat.GetView();
1313 	XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1314 	if (pView!=NULL && pView->IsCreateMode()) {
1315 		// ggf. auf anderes CreateTool umschalten
1316 		sal_uInt16 nIdent;
1317 		sal_uInt32 nInvent;
1318 		pView->TakeCurrentObj(nIdent,nInvent);
1319 		if (nInvent==SdrInventor && pU->eAktKind!=(SdrObjKind)nIdent) {
1320 			SdrObjKind eNewKind=(SdrObjKind)nIdent;
1321 			switch (eNewKind) {
1322 				case OBJ_CARC: case OBJ_CIRC: case OBJ_CCUT: case OBJ_SECT: eNewKind=OBJ_CARC;
1323 				case OBJ_RECT:
1324 				case OBJ_LINE: case OBJ_PLIN: case OBJ_POLY:
1325 				case OBJ_PATHLINE: case OBJ_PATHFILL:
1326 				case OBJ_FREELINE: case OBJ_FREEFILL:
1327 				case OBJ_SPLNLINE: case OBJ_SPLNFILL: {
1328 					pU->eAktKind=eNewKind;
1329 					pU->bMixedCreate=sal_True;
1330 					pU->nBezierStartPoint=rXPoly.GetPointCount();
1331 					if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1332 				} break;
1333 				default: break;
1334 			} // switch
1335 		}
1336 	}
1337 	sal_uInt16 nActPoint=rXPoly.GetPointCount();
1338 	if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nActPoint<2) {
1339 		rXPoly[0]=rStat.GetPos0();
1340 		rXPoly[1]=rStat.GetNow();
1341 		nActPoint=2;
1342 	}
1343 	if (nActPoint==0) {
1344 		rXPoly[0]=rStat.GetPos0();
1345 	} else nActPoint--;
1346 	FASTBOOL bFreeHand=IsFreeHand(pU->eAktKind);
1347 	rStat.SetNoSnap(bFreeHand /*|| (pU->bMixed && pU->eAktKind==OBJ_LINE)*/);
1348 	rStat.SetOrtho8Possible(pU->eAktKind!=OBJ_CARC && pU->eAktKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eAktKind!=OBJ_LINE));
1349 	Point aActMerk(rXPoly[nActPoint]);
1350 	rXPoly[nActPoint]=rStat.Now();
1351 	if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1352 		Point aPt(rStat.Start());
1353 		if (pView!=NULL && pView->IsCreate1stPointAsCenter()) {
1354 			aPt+=aPt;
1355 			aPt-=rStat.Now();
1356 		}
1357 		rXPoly[0]=aPt;
1358 	}
1359 	OutputDevice* pOut=pView==NULL ? NULL : pView->GetFirstOutputDevice(); // GetWin(0);
1360 	if (bFreeHand) {
1361 		if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1362 		if (rStat.IsMouseDown() && nActPoint>0) {
1363 			// keine aufeinanderfolgenden Punkte an zu Nahe gelegenen Positionen zulassen
1364 			long nMinDist=1;
1365 			if (pView!=NULL) nMinDist=pView->GetFreeHandMinDistPix();
1366 			if (pOut!=NULL) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1367 			if (nMinDist<1) nMinDist=1;
1368 
1369 			Point aPt0(rXPoly[nActPoint-1]);
1370 			Point aPt1(rStat.Now());
1371 			long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1372 			long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1373 			if (dx<nMinDist && dy<nMinDist) return sal_False;
1374 
1375 			// folgendes ist aus EndCreate kopiert (nur kleine Modifikationen)
1376 			// und sollte dann mal in eine Methode zusammengefasst werden:
1377 
1378 			if (nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1379 				rXPoly.PointsToBezier(nActPoint-3);
1380 				rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1381 				rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1382 
1383 				if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1384 					rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1385 					rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1386 				}
1387 			}
1388 			rXPoly[nActPoint+1]=rStat.Now();
1389 			rStat.NextPoint();
1390 		} else {
1391 			pU->nBezierStartPoint=nActPoint;
1392 		}
1393 	}
1394 
1395 	pU->ResetFormFlags();
1396 	if (IsBezier(pU->eAktKind)) {
1397 		if (nActPoint>=2) {
1398 			pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],rStat.IsMouseDown());
1399 		} else if (pU->bBezHasCtrl0) {
1400 			pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],pU->aBezControl0-rXPoly[nActPoint-1],rStat.IsMouseDown());
1401 		}
1402 	}
1403 	if (pU->eAktKind==OBJ_CARC && nActPoint>=2) {
1404 		pU->CalcCircle(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1405 	}
1406 	if (pU->eAktKind==OBJ_LINE && nActPoint>=2) {
1407 		pU->CalcLine(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1408 	}
1409 	if (pU->eAktKind==OBJ_RECT && nActPoint>=2) {
1410 		pU->CalcRect(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1411 	}
1412 
1413 	return sal_True;
1414 }
1415 
1416 FASTBOOL ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
1417 {
1418 	ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1419 	FASTBOOL bRet=sal_False;
1420 	SdrView* pView=rStat.GetView();
1421 	FASTBOOL bIncomp=pView!=NULL && pView->IsUseIncompatiblePathCreateInterface();
1422 	XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1423 	sal_uInt16 nActPoint=rXPoly.GetPointCount()-1;
1424 	Point aAktMerk(rXPoly[nActPoint]);
1425 	rXPoly[nActPoint]=rStat.Now();
1426 	if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1427 		if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1428 		bRet=eCmd==SDRCREATE_FORCEEND;
1429 		if (bRet) {
1430 			mbCreating=sal_False;
1431 			delete pU;
1432 			rStat.SetUser(NULL);
1433 		}
1434 		return bRet;
1435 	}
1436 
1437 	if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1438 		if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1439 		bRet=eCmd==SDRCREATE_FORCEEND;
1440 		if (bRet) {
1441 			mbCreating=sal_False;
1442 			delete pU;
1443 			rStat.SetUser(NULL);
1444 		}
1445 		return bRet;
1446 	}
1447 	if (eCmd==SDRCREATE_NEXTPOINT || eCmd==SDRCREATE_NEXTOBJECT) {
1448 		// keine aufeinanderfolgenden Punkte an identischer Position zulassen
1449 		if (nActPoint==0 || rStat.Now()!=rXPoly[nActPoint-1]) {
1450 			if (bIncomp) {
1451 				if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1452 				if (IsBezier(pU->eAktKind) && nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1453 					rXPoly.PointsToBezier(nActPoint-3);
1454 					rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1455 					rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1456 
1457 					if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1458 						rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1459 						rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1460 					}
1461 				}
1462 			} else {
1463 				if (nActPoint==1 && IsBezier(pU->eAktKind) && !pU->bBezHasCtrl0) {
1464 					pU->aBezControl0=rStat.GetNow();;
1465 					pU->bBezHasCtrl0=sal_True;
1466 					nActPoint--;
1467 				}
1468 				if (pU->IsFormFlag()) {
1469 					sal_uInt16 nPtAnz0=rXPoly.GetPointCount();
1470 					rXPoly.Remove(nActPoint-1,2); // die letzten beiden Punkte entfernen und durch die Form ersetzen
1471 					rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1472 					sal_uInt16 nPtAnz1=rXPoly.GetPointCount();
1473 					for (sal_uInt16 i=nPtAnz0+1; i<nPtAnz1-1; i++) { // Damit BckAction richtig funktioniert
1474 						if (!rXPoly.IsControl(i)) rStat.NextPoint();
1475 					}
1476 					nActPoint=rXPoly.GetPointCount()-1;
1477 				}
1478 			}
1479 			nActPoint++;
1480 			rXPoly[nActPoint]=rStat.GetNow();
1481 		}
1482 		if (eCmd==SDRCREATE_NEXTOBJECT) {
1483 			if (rXPoly.GetPointCount()>=2) {
1484 				pU->bBezHasCtrl0=sal_False;
1485 				// nur einzelnes Polygon kann offen sein, deshalb schliessen
1486 				rXPoly[nActPoint]=rXPoly[0];
1487 				XPolygon aXP;
1488 				aXP[0]=rStat.GetNow();
1489 				aPathPolygon.Insert(aXP);
1490 			}
1491 		}
1492 	}
1493 
1494 	sal_uInt16 nPolyAnz=aPathPolygon.Count();
1495 	if (nPolyAnz!=0) {
1496 		// den letzten Punkt ggf. wieder loeschen
1497 		if (eCmd==SDRCREATE_FORCEEND) {
1498 			XPolygon& rXP=aPathPolygon[nPolyAnz-1];
1499 			sal_uInt16 nPtAnz=rXP.GetPointCount();
1500 			if (nPtAnz>=2) {
1501 				if (!rXP.IsControl(nPtAnz-2)) {
1502 					if (rXP[nPtAnz-1]==rXP[nPtAnz-2]) {
1503 						rXP.Remove(nPtAnz-1,1);
1504 					}
1505 				} else {
1506 					if (rXP[nPtAnz-3]==rXP[nPtAnz-2]) {
1507 						rXP.Remove(nPtAnz-3,3);
1508 					}
1509 				}
1510 			}
1511 		}
1512 		for (sal_uInt16 nPolyNum=nPolyAnz; nPolyNum>0;) {
1513 			nPolyNum--;
1514 			XPolygon& rXP=aPathPolygon[nPolyNum];
1515 			sal_uInt16 nPtAnz=rXP.GetPointCount();
1516 			// Polygone mit zu wenig Punkten werden geloescht
1517 			if (nPolyNum<nPolyAnz-1 || eCmd==SDRCREATE_FORCEEND) {
1518 				if (nPtAnz<2) aPathPolygon.Remove(nPolyNum);
1519 			}
1520 		}
1521 	}
1522 	pU->ResetFormFlags();
1523 	bRet=eCmd==SDRCREATE_FORCEEND;
1524 	if (bRet) {
1525 		mbCreating=sal_False;
1526 		delete pU;
1527 		rStat.SetUser(NULL);
1528 	}
1529 	return bRet;
1530 }
1531 
1532 FASTBOOL ImpPathForDragAndCreate::BckCreate(SdrDragStat& rStat)
1533 {
1534 	ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1535 	if (aPathPolygon.Count()>0) {
1536 		XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1537 		sal_uInt16 nActPoint=rXPoly.GetPointCount();
1538 		if (nActPoint>0) {
1539 			nActPoint--;
1540 			// Das letzte Stueck einer Bezierkurve wird erstmal zu 'ner Linie
1541 			rXPoly.Remove(nActPoint,1);
1542 			if (nActPoint>=3 && rXPoly.IsControl(nActPoint-1)) {
1543 				// Beziersegment am Ende sollte zwar nicht vorkommen, aber falls doch ...
1544 				rXPoly.Remove(nActPoint-1,1);
1545 				if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1546 			}
1547 		}
1548 		nActPoint=rXPoly.GetPointCount();
1549 		if (nActPoint>=4) { // Kein Beziersegment am Ende
1550 			nActPoint--;
1551 			if (rXPoly.IsControl(nActPoint-1)) {
1552 				rXPoly.Remove(nActPoint-1,1);
1553 				if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1554 			}
1555 		}
1556 		if (rXPoly.GetPointCount()<2) {
1557 			aPathPolygon.Remove(aPathPolygon.Count()-1);
1558 		}
1559 		if (aPathPolygon.Count()>0) {
1560 			XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1561 			sal_uInt16 nLocalActPoint=rLocalXPoly.GetPointCount();
1562 			if (nLocalActPoint>0) {
1563 				nLocalActPoint--;
1564 				rLocalXPoly[nLocalActPoint]=rStat.Now();
1565 			}
1566 		}
1567 	}
1568 	pU->ResetFormFlags();
1569 	return aPathPolygon.Count()!=0;
1570 }
1571 
1572 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
1573 {
1574 	ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1575 	aPathPolygon.Clear();
1576 	mbCreating=sal_False;
1577 	delete pU;
1578 	rStat.SetUser(NULL);
1579 }
1580 
1581 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
1582 {
1583 	basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
1584 	SdrView* pView = rDrag.GetView();
1585 
1586 	if(pView && pView->IsUseIncompatiblePathCreateInterface())
1587 		return aRetval;
1588 
1589 	ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1590 	basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1L) : basegfx::B2DPolygon());
1591 
1592 	if(pU->IsFormFlag() && aNewPolygon.count() > 1L)
1593 	{
1594 		// remove last segment and replace with current
1595 		// do not forget to rescue the previous control point which will be lost when
1596 		// the point it's associated with is removed
1597 		const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1598 		const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1599 
1600 		aNewPolygon.remove(nChangeIndex, 2L);
1601 		aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1602 
1603 		if(nChangeIndex < aNewPolygon.count())
1604 		{
1605 			// if really something was added, set the saved prev control point at the
1606 			// point where it belongs
1607 			aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1608 		}
1609 	}
1610 
1611 	if(aRetval.count())
1612 	{
1613 		aRetval.setB2DPolygon(aRetval.count() - 1L, aNewPolygon);
1614 	}
1615 	else
1616 	{
1617 		aRetval.append(aNewPolygon);
1618 	}
1619 
1620 	return aRetval;
1621 }
1622 
1623 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag) const
1624 {
1625 	basegfx::B2DPolyPolygon aRetval;
1626 	SdrView* pView = rDrag.GetView();
1627 
1628 	if(pView && pView->IsUseIncompatiblePathCreateInterface())
1629 		return aRetval;
1630 
1631 	ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1632 
1633 	if(pU && pU->bBezier && rDrag.IsMouseDown())
1634 	{
1635 		// no more XOR, no need for complicated helplines
1636 		basegfx::B2DPolygon aHelpline;
1637 		aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1638 		aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1639 		aRetval.append(aHelpline);
1640 	}
1641 
1642 	return aRetval;
1643 }
1644 
1645 Pointer ImpPathForDragAndCreate::GetCreatePointer() const
1646 {
1647 	switch (meObjectKind) {
1648 		case OBJ_LINE    : return Pointer(POINTER_DRAW_LINE);
1649 		case OBJ_POLY    : return Pointer(POINTER_DRAW_POLYGON);
1650 		case OBJ_PLIN    : return Pointer(POINTER_DRAW_POLYGON);
1651 		case OBJ_PATHLINE: return Pointer(POINTER_DRAW_BEZIER);
1652 		case OBJ_PATHFILL: return Pointer(POINTER_DRAW_BEZIER);
1653 		case OBJ_FREELINE: return Pointer(POINTER_DRAW_FREEHAND);
1654 		case OBJ_FREEFILL: return Pointer(POINTER_DRAW_FREEHAND);
1655 		case OBJ_SPLNLINE: return Pointer(POINTER_DRAW_FREEHAND);
1656 		case OBJ_SPLNFILL: return Pointer(POINTER_DRAW_FREEHAND);
1657 		case OBJ_PATHPOLY: return Pointer(POINTER_DRAW_POLYGON);
1658 		case OBJ_PATHPLIN: return Pointer(POINTER_DRAW_POLYGON);
1659 		default: break;
1660 	} // switch
1661 	return Pointer(POINTER_CROSS);
1662 }
1663 
1664 /*************************************************************************/
1665 
1666 SdrPathObjGeoData::SdrPathObjGeoData()
1667 {
1668 }
1669 
1670 SdrPathObjGeoData::~SdrPathObjGeoData()
1671 {
1672 }
1673 
1674 //////////////////////////////////////////////////////////////////////////////
1675 // DrawContact section
1676 
1677 sdr::contact::ViewContact* SdrPathObj::CreateObjectSpecificViewContact()
1678 {
1679 	return new sdr::contact::ViewContactOfSdrPathObj(*this);
1680 }
1681 
1682 /*************************************************************************/
1683 
1684 TYPEINIT1(SdrPathObj,SdrTextObj);
1685 
1686 SdrPathObj::SdrPathObj(SdrObjKind eNewKind)
1687 :	meKind(eNewKind),
1688 	mpDAC(0L)
1689 {
1690 	bClosedObj = IsClosed();
1691 }
1692 
1693 SdrPathObj::SdrPathObj(SdrObjKind eNewKind, const basegfx::B2DPolyPolygon& rPathPoly)
1694 :	maPathPolygon(rPathPoly),
1695 	meKind(eNewKind),
1696 	mpDAC(0L)
1697 {
1698 	bClosedObj = IsClosed();
1699 	ImpForceKind();
1700 }
1701 
1702 SdrPathObj::~SdrPathObj()
1703 {
1704 	impDeleteDAC();
1705 }
1706 
1707 sal_Bool ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1708 {
1709 	return (1L == rPolyPolygon.count() && 2L == rPolyPolygon.getB2DPolygon(0L).count());
1710 }
1711 
1712 Rectangle ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
1713 {
1714 	basegfx::B2DRange aRange(basegfx::tools::getRange(rPolyPolygon));
1715 
1716 	return Rectangle(
1717 		FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1718 		FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1719 }
1720 
1721 void SdrPathObj::ImpForceLineWink()
1722 {
1723 	if(OBJ_LINE == meKind && ImpIsLine(GetPathPoly()))
1724 	{
1725 		const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1726 		const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1727 		const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1728 		const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1729 		const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1730 		const Point aDelt(aPoint1 - aPoint0);
1731 
1732 		aGeo.nDrehWink=GetAngle(aDelt);
1733 		aGeo.nShearWink=0;
1734 		aGeo.RecalcSinCos();
1735 		aGeo.RecalcTan();
1736 
1737 		// #101412# for SdrTextObj, keep aRect up to date
1738 		aRect = Rectangle(aPoint0, aPoint1);
1739 		aRect.Justify();
1740 	}
1741 }
1742 
1743 void SdrPathObj::ImpForceKind()
1744 {
1745 	if (meKind==OBJ_PATHPLIN) meKind=OBJ_PLIN;
1746 	if (meKind==OBJ_PATHPOLY) meKind=OBJ_POLY;
1747 
1748 	if(GetPathPoly().areControlPointsUsed())
1749 	{
1750 		switch (meKind)
1751 		{
1752 			case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1753 			case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1754 			case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1755 			default: break;
1756 		}
1757 	}
1758 	else
1759 	{
1760 		switch (meKind)
1761 		{
1762 			case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1763 			case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1764 			case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1765 			case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1766 			default: break;
1767 		}
1768 	}
1769 
1770 	if (meKind==OBJ_LINE && !ImpIsLine(GetPathPoly())) meKind=OBJ_PLIN;
1771 	if (meKind==OBJ_PLIN && ImpIsLine(GetPathPoly())) meKind=OBJ_LINE;
1772 
1773 	bClosedObj=IsClosed();
1774 
1775 	if (meKind==OBJ_LINE)
1776 	{
1777 		ImpForceLineWink();
1778 	}
1779 	else
1780 	{
1781 		// #i10659#, similar to #101412# but for polys with more than 2 points.
1782 		//
1783 		// Here i again need to fix something, because when Path-Polys are Copy-Pasted
1784 		// between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1785 		// a scaling loop started from SdrExchangeView::Paste. This is principally nothing
1786 		// wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1787 		// this is the case, some size needs to be set here in aRect to avoid that the cyclus
1788 		// through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1789 		// BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1790 		// from the local Resize() implementation.
1791 		//
1792 		// Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1793 		// text rectangle for the text object itself and methods at SdrTextObj do handle it
1794 		// in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1795 		// which is basically wrong. To make the SdrText methods which deal with aRect directly
1796 		// work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1797 		// command for SdrPathObj. Since adding this update mechanism with #101412# to
1798 		// ImpForceLineWink() for lines was very successful, i add it to where ImpForceLineWink()
1799 		// was called, once here below and once on a 2nd place below.
1800 
1801 		// #i10659# for SdrTextObj, keep aRect up to date
1802 		if(GetPathPoly().count())
1803 		{
1804 			aRect = ImpGetBoundRect(GetPathPoly());
1805 		}
1806 	}
1807 
1808 	// #i75974# adapt polygon state to object type. This may include a reinterpretation
1809 	// of a closed geometry as open one, but with identical first and last point
1810 	for(sal_uInt32 a(0); a < maPathPolygon.count(); a++)
1811 	{
1812 		basegfx::B2DPolygon aCandidate(maPathPolygon.getB2DPolygon(a));
1813 
1814 		if((bool)IsClosed() != aCandidate.isClosed())
1815 		{
1816             // #i80213# really change polygon geometry; else e.g. the last point which
1817             // needs to be identical with the first one will be missing when opening
1818             // due to OBJ_PATH type
1819             if(aCandidate.isClosed())
1820             {
1821                 basegfx::tools::openWithGeometryChange(aCandidate);
1822             }
1823             else
1824             {
1825                 basegfx::tools::closeWithGeometryChange(aCandidate);
1826             }
1827 
1828             maPathPolygon.setB2DPolygon(a, aCandidate);
1829 		}
1830 	}
1831 }
1832 
1833 void SdrPathObj::ImpSetClosed(sal_Bool bClose)
1834 {
1835 	if(bClose)
1836 	{
1837 		switch (meKind)
1838 		{
1839 			case OBJ_LINE    : meKind=OBJ_POLY;     break;
1840 			case OBJ_PLIN    : meKind=OBJ_POLY;     break;
1841 			case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1842 			case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1843 			case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1844 			default: break;
1845 		}
1846 
1847 		bClosedObj = sal_True;
1848 	}
1849 	else
1850 	{
1851 		switch (meKind)
1852 		{
1853 			case OBJ_POLY    : meKind=OBJ_PLIN;     break;
1854 			case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1855 			case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1856 			case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1857 			default: break;
1858 		}
1859 
1860 		bClosedObj = sal_False;
1861 	}
1862 
1863 	ImpForceKind();
1864 }
1865 
1866 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1867 {
1868 	rInfo.bNoContortion=sal_False;
1869 
1870 	FASTBOOL bCanConv = !HasText() || ImpCanConvTextToCurve();
1871 	FASTBOOL bIsPath = IsBezier() || IsSpline();
1872 
1873 	rInfo.bEdgeRadiusAllowed	= sal_False;
1874 	rInfo.bCanConvToPath = bCanConv && !bIsPath;
1875 	rInfo.bCanConvToPoly = bCanConv && bIsPath;
1876 	rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
1877 }
1878 
1879 sal_uInt16 SdrPathObj::GetObjIdentifier() const
1880 {
1881 	return sal_uInt16(meKind);
1882 }
1883 
1884 void SdrPathObj::operator=(const SdrObject& rObj)
1885 {
1886 	SdrTextObj::operator=(rObj);
1887 	SdrPathObj& rPath=(SdrPathObj&)rObj;
1888 	maPathPolygon=rPath.GetPathPoly();
1889 }
1890 
1891 void SdrPathObj::TakeObjNameSingul(XubString& rName) const
1892 {
1893 	if(OBJ_LINE == meKind)
1894 	{
1895 		sal_uInt16 nId(STR_ObjNameSingulLINE);
1896 
1897 		if(ImpIsLine(GetPathPoly()))
1898 		{
1899 			const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1900 			const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1901 			const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1902 			const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1903 			const Point aPoint1(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1904 
1905 			if(aB2DPoint0 != aB2DPoint1)
1906 			{
1907 				if(aB2DPoint0.getY() == aB2DPoint1.getY())
1908 				{
1909 					nId = STR_ObjNameSingulLINE_Hori;
1910 				}
1911 				else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1912 				{
1913 					nId = STR_ObjNameSingulLINE_Vert;
1914 				}
1915 				else
1916 				{
1917 					const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1918 					const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1919 
1920 					if(fDx == fDy)
1921 					{
1922 						nId = STR_ObjNameSingulLINE_Diag;
1923 					}
1924 				}
1925 			}
1926 		}
1927 
1928 		rName = ImpGetResStr(nId);
1929 	}
1930 	else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1931 	{
1932 		const sal_Bool bClosed(OBJ_POLY == meKind);
1933 		sal_uInt16 nId(0);
1934 
1935 		if(mpDAC && mpDAC->IsCreating())
1936 		{
1937 			if(bClosed)
1938 			{
1939 				nId = STR_ObjNameSingulPOLY;
1940 			}
1941 			else
1942 			{
1943 				nId = STR_ObjNameSingulPLIN;
1944 			}
1945 
1946 			rName = ImpGetResStr(nId);
1947 		}
1948 		else
1949 		{
1950 			// get point count
1951 			sal_uInt32 nPointCount(0L);
1952 			const sal_uInt32 nPolyCount(GetPathPoly().count());
1953 
1954 			for(sal_uInt32 a(0L); a < nPolyCount; a++)
1955 			{
1956 				nPointCount += GetPathPoly().getB2DPolygon(a).count();
1957 			}
1958 
1959 			if(bClosed)
1960 			{
1961 				nId = STR_ObjNameSingulPOLY_PntAnz;
1962 			}
1963 			else
1964 			{
1965 				nId = STR_ObjNameSingulPLIN_PntAnz;
1966 			}
1967 
1968 			rName = ImpGetResStr(nId);
1969 			sal_uInt16 nPos(rName.SearchAscii("%2")); // #i96537#
1970 
1971 			if(STRING_NOTFOUND != nPos)
1972 			{
1973 				rName.Erase(nPos, 2);
1974 				rName.Insert(UniString::CreateFromInt32(nPointCount), nPos);
1975 			}
1976 		}
1977 	}
1978 	else
1979 	{
1980 		switch (meKind)
1981 		{
1982 			case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNameSingulPATHLINE); break;
1983 			case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNameSingulFREELINE); break;
1984 			case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNameSingulNATSPLN); break;
1985 			case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNameSingulPATHFILL); break;
1986 			case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNameSingulFREEFILL); break;
1987 			case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNameSingulPERSPLN); break;
1988 			default: break;
1989 		}
1990 	}
1991 
1992 	String aName(GetName());
1993 	if(aName.Len())
1994 	{
1995 		rName += sal_Unicode(' ');
1996 		rName += sal_Unicode('\'');
1997 		rName += aName;
1998 		rName += sal_Unicode('\'');
1999 	}
2000 }
2001 
2002 void SdrPathObj::TakeObjNamePlural(XubString& rName) const
2003 {
2004 	switch(meKind)
2005 	{
2006 		case OBJ_LINE    : rName=ImpGetResStr(STR_ObjNamePluralLINE    ); break;
2007 		case OBJ_PLIN    : rName=ImpGetResStr(STR_ObjNamePluralPLIN    ); break;
2008 		case OBJ_POLY    : rName=ImpGetResStr(STR_ObjNamePluralPOLY    ); break;
2009 		case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNamePluralPATHLINE); break;
2010 		case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNamePluralFREELINE); break;
2011 		case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNamePluralNATSPLN); break;
2012 		case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNamePluralPATHFILL); break;
2013 		case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNamePluralFREEFILL); break;
2014 		case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNamePluralPERSPLN); break;
2015 		default: break;
2016 	}
2017 }
2018 
2019 basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
2020 {
2021 	return GetPathPoly();
2022 }
2023 
2024 sal_uInt32 SdrPathObj::GetHdlCount() const
2025 {
2026 	sal_uInt32 nRetval(0L);
2027 	const sal_uInt32 nPolyCount(GetPathPoly().count());
2028 
2029 	for(sal_uInt32 a(0L); a < nPolyCount; a++)
2030 	{
2031 		nRetval += GetPathPoly().getB2DPolygon(a).count();
2032 	}
2033 
2034 	return nRetval;
2035 }
2036 
2037 SdrHdl* SdrPathObj::GetHdl(sal_uInt32 nHdlNum) const
2038 {
2039 	// #i73248#
2040 	// Warn the user that this is ineffective and show alternatives. Should not be used at all.
2041 	OSL_ENSURE(false, "SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
2042 
2043 	// to have an alternative, get single handle using the ineffective way
2044 	SdrHdl* pRetval = 0;
2045 	SdrHdlList aLocalList(0);
2046 	AddToHdlList(aLocalList);
2047 	const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());
2048 
2049 	if(nHdlCount && nHdlNum < nHdlCount)
2050 	{
2051 		// remove and remember. The other created handles will be deleted again with the
2052 		// destruction of the local list
2053 		pRetval = aLocalList.RemoveHdl(nHdlNum);
2054 	}
2055 
2056 	return pRetval;
2057 }
2058 
2059 void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
2060 {
2061 	// keep old stuff to be able to keep old SdrHdl stuff, too
2062 	const XPolyPolygon aOldPathPolygon(GetPathPoly());
2063 	sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
2064 	FASTBOOL bClosed=IsClosed();
2065 	sal_uInt16 nIdx=0;
2066 
2067 	for (sal_uInt16 i=0; i<nPolyCnt; i++) {
2068 		const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
2069 		sal_uInt16 nPntCnt=rXPoly.GetPointCount();
2070 		if (bClosed && nPntCnt>1) nPntCnt--;
2071 
2072 		for (sal_uInt16 j=0; j<nPntCnt; j++) {
2073 			if (rXPoly.GetFlags(j)!=XPOLY_CONTROL) {
2074 				const Point& rPnt=rXPoly[j];
2075 				SdrHdl* pHdl=new SdrHdl(rPnt,HDL_POLY);
2076 				pHdl->SetPolyNum(i);
2077 				pHdl->SetPointNum(j);
2078 				pHdl->Set1PixMore(j==0);
2079 				pHdl->SetSourceHdlNum(nIdx);
2080 				nIdx++;
2081 				rHdlList.AddHdl(pHdl);
2082 			}
2083 		}
2084 	}
2085 }
2086 
2087 sal_uInt32 SdrPathObj::GetPlusHdlCount(const SdrHdl& rHdl) const
2088 {
2089 	// keep old stuff to be able to keep old SdrHdl stuff, too
2090 	const XPolyPolygon aOldPathPolygon(GetPathPoly());
2091 	sal_uInt16 nCnt = 0;
2092 	sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2093 	sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2094 
2095 	if(nPolyNum < aOldPathPolygon.Count())
2096 	{
2097 		const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2098 		sal_uInt16 nPntMax = rXPoly.GetPointCount();
2099 		if (nPntMax>0)
2100 		{
2101 			nPntMax--;
2102 			if (nPnt<=nPntMax)
2103 			{
2104 				if (rXPoly.GetFlags(nPnt)!=XPOLY_CONTROL)
2105 				{
2106 					if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2107 					if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL) nCnt++;
2108 					if (nPnt==nPntMax && IsClosed()) nPnt=0;
2109 					if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL) nCnt++;
2110 				}
2111 			}
2112 		}
2113 	}
2114 
2115 	return nCnt;
2116 }
2117 
2118 SdrHdl* SdrPathObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlusNum) const
2119 {
2120 	// keep old stuff to be able to keep old SdrHdl stuff, too
2121 	const XPolyPolygon aOldPathPolygon(GetPathPoly());
2122 	SdrHdl* pHdl = 0L;
2123 	sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2124 	sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2125 
2126 	if (nPolyNum<aOldPathPolygon.Count())
2127 	{
2128 		const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2129 		sal_uInt16 nPntMax = rXPoly.GetPointCount();
2130 
2131 		if (nPntMax>0)
2132 		{
2133 			nPntMax--;
2134 			if (nPnt<=nPntMax)
2135 			{
2136 				pHdl=new SdrHdlBezWgt(&rHdl);
2137 				pHdl->SetPolyNum(rHdl.GetPolyNum());
2138 
2139 				if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2140 				if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL && nPlusNum==0)
2141 				{
2142 					pHdl->SetPos(rXPoly[nPnt-1]);
2143 					pHdl->SetPointNum(nPnt-1);
2144 				}
2145 				else
2146 				{
2147 					if (nPnt==nPntMax && IsClosed()) nPnt=0;
2148 					if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL)
2149 					{
2150 						pHdl->SetPos(rXPoly[nPnt+1]);
2151 						pHdl->SetPointNum(nPnt+1);
2152 					}
2153 				}
2154 
2155 				pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2156 				pHdl->SetPlusHdl(sal_True);
2157 			}
2158 		}
2159 	}
2160 	return pHdl;
2161 }
2162 
2163 ////////////////////////////////////////////////////////////////////////////////////////////////////
2164 
2165 bool SdrPathObj::hasSpecialDrag() const
2166 {
2167 	return true;
2168 }
2169 
2170 bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
2171 {
2172 	ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2173 
2174     return aDragAndCreate.beginPathDrag(rDrag);
2175 }
2176 
2177 bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
2178 {
2179 	ImpPathForDragAndCreate aDragAndCreate(*this);
2180     bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2181 
2182     if(bRetval)
2183     {
2184     	bRetval = aDragAndCreate.movePathDrag(rDrag);
2185     }
2186 
2187     if(bRetval)
2188     {
2189     	bRetval = aDragAndCreate.endPathDrag(rDrag);
2190     }
2191 
2192 	if(bRetval)
2193 	{
2194    		NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2195 	}
2196 
2197 	return bRetval;
2198 }
2199 
2200 String SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2201 {
2202     String aRetval;
2203 
2204     if(mpDAC)
2205     {
2206         // #i103058# also get a comment when in creation
2207         const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2208 
2209         if(bCreateComment)
2210         {
2211         	aRetval = mpDAC->getSpecialDragComment(rDrag);
2212         }
2213     }
2214     else
2215     {
2216 	    ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2217         bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2218 
2219         if(bDidWork)
2220         {
2221     	    aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2222         }
2223     }
2224 
2225     return aRetval;
2226 }
2227 
2228 basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
2229 {
2230     basegfx::B2DPolyPolygon aRetval;
2231 	ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2232     bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2233 
2234     if(bDidWork)
2235     {
2236         aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2237     }
2238 
2239     return aRetval;
2240 }
2241 
2242 ////////////////////////////////////////////////////////////////////////////////////////////////////
2243 
2244 FASTBOOL SdrPathObj::BegCreate(SdrDragStat& rStat)
2245 {
2246 	impDeleteDAC();
2247 	return impGetDAC().BegCreate(rStat);
2248 }
2249 
2250 FASTBOOL SdrPathObj::MovCreate(SdrDragStat& rStat)
2251 {
2252 	return impGetDAC().MovCreate(rStat);
2253 }
2254 
2255 FASTBOOL SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
2256 {
2257 	FASTBOOL bRetval(impGetDAC().EndCreate(rStat, eCmd));
2258 
2259 	if(bRetval && mpDAC)
2260 	{
2261 		SetPathPoly(mpDAC->getModifiedPolyPolygon());
2262 
2263 		// #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2264 		// to be able to use the type-changing ImpSetClosed method
2265 		if(!IsClosedObj())
2266 		{
2267 			SdrView* pView = rStat.GetView();
2268 
2269 			if(pView && pView->IsAutoClosePolys() && !pView->IsUseIncompatiblePathCreateInterface())
2270 			{
2271 				OutputDevice* pOut = pView->GetFirstOutputDevice();
2272 
2273 				if(pOut)
2274 				{
2275 					if(GetPathPoly().count())
2276 					{
2277 						const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2278 
2279 						if(aCandidate.count() > 2)
2280 						{
2281 							// check distance of first and last point
2282 							const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2283 							const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2284 
2285 							if(aDistVector.getLength() <= (double)nCloseDist)
2286 							{
2287 								// close it
2288 								ImpSetClosed(true);
2289 							}
2290 						}
2291 					}
2292 				}
2293 			}
2294 		}
2295 
2296 		impDeleteDAC();
2297 	}
2298 
2299 	return bRetval;
2300 }
2301 
2302 FASTBOOL SdrPathObj::BckCreate(SdrDragStat& rStat)
2303 {
2304 	return impGetDAC().BckCreate(rStat);
2305 }
2306 
2307 void SdrPathObj::BrkCreate(SdrDragStat& rStat)
2308 {
2309 	impGetDAC().BrkCreate(rStat);
2310 	impDeleteDAC();
2311 }
2312 
2313 basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
2314 {
2315 	basegfx::B2DPolyPolygon aRetval;
2316 
2317 	if(mpDAC)
2318 	{
2319 		aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2320 		aRetval.append(mpDAC->TakeDragPolyPolygon(rDrag));
2321 	}
2322 
2323 	return aRetval;
2324 }
2325 
2326 // during drag or create, allow accessing the so-far created/modified polyPolygon
2327 basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
2328 {
2329 	basegfx::B2DPolyPolygon aRetval;
2330 
2331 	if(mpDAC)
2332 	{
2333 		aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2334 	}
2335 
2336 	return aRetval;
2337 }
2338 
2339 basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
2340 {
2341 	basegfx::B2DPolyPolygon aRetval;
2342 
2343 	if(mpDAC)
2344 	{
2345 		aRetval = mpDAC->TakeDragPolyPolygon(rDrag);
2346 	}
2347 
2348 	return aRetval;
2349 }
2350 
2351 Pointer SdrPathObj::GetCreatePointer() const
2352 {
2353 	return impGetDAC().GetCreatePointer();
2354 }
2355 
2356 void SdrPathObj::NbcMove(const Size& rSiz)
2357 {
2358     maPathPolygon.transform(basegfx::tools::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
2359 
2360 	// #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2361 	SdrTextObj::NbcMove(rSiz);
2362 }
2363 
2364 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2365 {
2366     basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
2367 	aTrans = basegfx::tools::createScaleTranslateB2DHomMatrix(
2368         double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2369 	maPathPolygon.transform(aTrans);
2370 
2371 	// #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2372 	SdrTextObj::NbcResize(rRef,xFact,yFact);
2373 }
2374 
2375 void SdrPathObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
2376 {
2377     // Thank JOE, the angles are defined mirrored to the mathematical meanings
2378     const basegfx::B2DHomMatrix aTrans(basegfx::tools::createRotateAroundPoint(rRef.X(), rRef.Y(), -nWink * nPi180));
2379 	maPathPolygon.transform(aTrans);
2380 
2381 	// #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2382 	SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
2383 }
2384 
2385 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, FASTBOOL bVShear)
2386 {
2387     basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2388 
2389     if(bVShear)
2390     {
2391         // Thank JOE, the angles are defined mirrored to the mathematical meanings
2392         aTrans.shearY(-fTan);
2393     }
2394     else
2395     {
2396         aTrans.shearX(-fTan);
2397     }
2398 
2399     aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2400 	maPathPolygon.transform(aTrans);
2401 
2402 	// #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2403 	SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2404 }
2405 
2406 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2407 {
2408 	const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2409 	const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2410 	const double fRot(atan2(fDiffY, fDiffX));
2411     basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2412 	aTrans.rotate(-fRot);
2413 	aTrans.scale(1.0, -1.0);
2414 	aTrans.rotate(fRot);
2415 	aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2416 	maPathPolygon.transform(aTrans);
2417 
2418 	// #97538# Do Joe's special handling for lines when mirroring, too
2419 	ImpForceKind();
2420 
2421 	// #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2422 	SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2423 }
2424 
2425 void SdrPathObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
2426 {
2427 	if(!aGeo.nDrehWink)
2428 	{
2429 		rRect = GetSnapRect();
2430 	}
2431 	else
2432 	{
2433 		XPolyPolygon aXPP(GetPathPoly());
2434 		RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2435 		rRect=aXPP.GetBoundRect();
2436 		Point aTmp(rRect.TopLeft());
2437 		RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2438 		aTmp-=rRect.TopLeft();
2439 		rRect.Move(aTmp.X(),aTmp.Y());
2440 	}
2441 }
2442 
2443 void SdrPathObj::RecalcSnapRect()
2444 {
2445 	if(GetPathPoly().count())
2446 	{
2447 		maSnapRect = ImpGetBoundRect(GetPathPoly());
2448 	}
2449 }
2450 
2451 void SdrPathObj::NbcSetSnapRect(const Rectangle& rRect)
2452 {
2453 	Rectangle aOld(GetSnapRect());
2454 
2455 	// #95736# Take RECT_EMPTY into account when calculating scale factors
2456 	long nMulX = (RECT_EMPTY == rRect.Right()) ? 0 : rRect.Right()  - rRect.Left();
2457 
2458 	long nDivX = aOld.Right()   - aOld.Left();
2459 
2460 	// #95736# Take RECT_EMPTY into account when calculating scale factors
2461 	long nMulY = (RECT_EMPTY == rRect.Bottom()) ? 0 : rRect.Bottom() - rRect.Top();
2462 
2463 	long nDivY = aOld.Bottom()  - aOld.Top();
2464 	if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2465 	if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2466 	Fraction aX(nMulX,nDivX);
2467 	Fraction aY(nMulY,nDivY);
2468 	NbcResize(aOld.TopLeft(), aX, aY);
2469 	NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2470 }
2471 
2472 sal_uInt32 SdrPathObj::GetSnapPointCount() const
2473 {
2474 	return GetHdlCount();
2475 }
2476 
2477 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2478 {
2479 	sal_uInt32 nPoly,nPnt;
2480 	if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2481 	{
2482 		DBG_ASSERT(sal_False,"SdrPathObj::GetSnapPoint: Punkt nSnapPnt nicht vorhanden!");
2483 	}
2484 
2485 	const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2486 	return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2487 }
2488 
2489 sal_Bool SdrPathObj::IsPolyObj() const
2490 {
2491 	return sal_True;
2492 }
2493 
2494 sal_uInt32 SdrPathObj::GetPointCount() const
2495 {
2496 	const sal_uInt32 nPolyCount(GetPathPoly().count());
2497 	sal_uInt32 nRetval(0L);
2498 
2499 	for(sal_uInt32 a(0L); a < nPolyCount; a++)
2500 	{
2501 		nRetval += GetPathPoly().getB2DPolygon(a).count();
2502 	}
2503 
2504 	return nRetval;
2505 }
2506 
2507 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2508 {
2509 	Point aRetval;
2510 	sal_uInt32 nPoly,nPnt;
2511 
2512 	if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2513 	{
2514 		const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2515 		const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2516 		aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2517 	}
2518 
2519 	return aRetval;
2520 }
2521 
2522 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2523 {
2524 	sal_uInt32 nPoly,nPnt;
2525 
2526 	if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2527 	{
2528 		basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2529 		aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2530 		maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2531 
2532 		if(meKind==OBJ_LINE)
2533 		{
2534 			ImpForceLineWink();
2535 		}
2536 		else
2537 		{
2538 			if(GetPathPoly().count())
2539 			{
2540 				// #i10659# for SdrTextObj, keep aRect up to date
2541 				aRect = ImpGetBoundRect(GetPathPoly()); // fuer SdrTextObj#
2542 			}
2543 		}
2544 
2545 		SetRectsDirty();
2546 	}
2547 }
2548 
2549 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, sal_Bool bNewObj, sal_Bool bHideHim)
2550 {
2551 	sal_uInt32 nNewHdl;
2552 
2553 	if(bNewObj)
2554 	{
2555 		nNewHdl = NbcInsPoint(0L, rPos, sal_True, bHideHim);
2556 	}
2557 	else
2558 	{
2559 		// look for smallest distance data
2560 		const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2561 		sal_uInt32 nSmallestPolyIndex(0L);
2562 		sal_uInt32 nSmallestEdgeIndex(0L);
2563 		double fSmallestCut;
2564 		basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2565 
2566 		// create old polygon index from it
2567 		sal_uInt32 nPolyIndex(nSmallestEdgeIndex);
2568 
2569 		for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2570 		{
2571 			nPolyIndex += GetPathPoly().getB2DPolygon(a).count();
2572 		}
2573 
2574 		nNewHdl = NbcInsPoint(nPolyIndex, rPos, sal_False, bHideHim);
2575 	}
2576 
2577 	ImpForceKind();
2578 	return nNewHdl;
2579 }
2580 
2581 sal_uInt32 SdrPathObj::NbcInsPoint(sal_uInt32 /*nHdlNum*/, const Point& rPos, sal_Bool bNewObj, sal_Bool /*bHideHim*/)
2582 {
2583 	sal_uInt32 nNewHdl;
2584 
2585 	if(bNewObj)
2586 	{
2587 		basegfx::B2DPolygon aNewPoly;
2588 		const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2589 		aNewPoly.append(aPoint);
2590 		aNewPoly.setClosed(IsClosed());
2591 		maPathPolygon.append(aNewPoly);
2592 		SetRectsDirty();
2593 		nNewHdl = GetHdlCount();
2594 	}
2595 	else
2596 	{
2597 		// look for smallest distance data
2598 		const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2599 		sal_uInt32 nSmallestPolyIndex(0L);
2600 		sal_uInt32 nSmallestEdgeIndex(0L);
2601 		double fSmallestCut;
2602 		basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2603 		basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2604 		const bool bBefore(!aCandidate.isClosed() && 0L == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2605 		const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2L && 1.0 == fSmallestCut);
2606 
2607 		if(bBefore)
2608 		{
2609 			// before first point
2610 			aCandidate.insert(0L, aTestPoint);
2611 
2612 			if(aCandidate.areControlPointsUsed())
2613 			{
2614 				if(aCandidate.isNextControlPointUsed(1))
2615 				{
2616 					aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2617 					aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2618 				}
2619 			}
2620 
2621 			nNewHdl = 0L;
2622 		}
2623 		else if(bAfter)
2624 		{
2625 			// after last point
2626 			aCandidate.append(aTestPoint);
2627 
2628 			if(aCandidate.areControlPointsUsed())
2629 			{
2630 				if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2631 				{
2632 					aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2633 					aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2634 				}
2635 			}
2636 
2637 			nNewHdl = aCandidate.count() - 1L;
2638 		}
2639 		else
2640 		{
2641 			// in between
2642 			bool bSegmentSplit(false);
2643 			const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2644 
2645 			if(aCandidate.areControlPointsUsed())
2646 			{
2647 				if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2648 				{
2649 					bSegmentSplit = true;
2650 				}
2651 			}
2652 
2653 			if(bSegmentSplit)
2654 			{
2655 				// rebuild original segment to get the split data
2656 				basegfx::B2DCubicBezier aBezierA, aBezierB;
2657 				const basegfx::B2DCubicBezier aBezier(
2658 					aCandidate.getB2DPoint(nSmallestEdgeIndex),
2659 					aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2660 					aCandidate.getPrevControlPoint(nNextIndex),
2661 					aCandidate.getB2DPoint(nNextIndex));
2662 
2663 				// split and insert hit point
2664 				aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2665 				aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2666 
2667 				// since we inserted hit point and not split point, we need to add an offset
2668 				// to the control points to get the C1 continuity we want to achieve
2669 				const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2670 				aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2671 				aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2672 				aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2673 				aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2674 			}
2675 			else
2676 			{
2677 				aCandidate.insert(nSmallestEdgeIndex + 1L, aTestPoint);
2678 			}
2679 
2680 			nNewHdl = nSmallestEdgeIndex + 1L;
2681 		}
2682 
2683 		maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2684 
2685 		// create old polygon index from it
2686 		for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2687 		{
2688 			nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2689 		}
2690 	}
2691 
2692 	ImpForceKind();
2693 	return nNewHdl;
2694 }
2695 
2696 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2697 {
2698 	SdrPathObj* pNewObj = 0L;
2699 	const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2700 	sal_uInt32 nPoly, nPnt;
2701 
2702 	if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2703 	{
2704 		if(0L == nPoly)
2705 		{
2706 			const basegfx::B2DPolygon aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2707 			const sal_uInt32 nPointCount(aCandidate.count());
2708 
2709 			if(nPointCount)
2710 			{
2711 				if(IsClosed())
2712 				{
2713 					// when closed, RipPoint means to open the polygon at the selected point. To
2714 					// be able to do that, it is necessary to make the selected point the first one
2715 					basegfx::B2DPolygon aNewPolygon(basegfx::tools::makeStartPoint(aCandidate, nPnt));
2716 					SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2717 					ToggleClosed();
2718 
2719 					// give back new position of old start point (historical reasons)
2720 					rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2721 				}
2722 				else
2723 				{
2724 					if(nPointCount >= 3L && nPnt != 0L && nPnt + 1L < nPointCount)
2725 					{
2726 						// split in two objects at point nPnt
2727 						basegfx::B2DPolygon aSplitPolyA(aCandidate, 0L, nPnt + 1L);
2728 						SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2729 
2730 						pNewObj = (SdrPathObj*)Clone();
2731 						basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2732 						pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2733 					}
2734 				}
2735 			}
2736 		}
2737 	}
2738 
2739 	return pNewObj;
2740 }
2741 
2742 SdrObject* SdrPathObj::DoConvertToPolyObj(sal_Bool bBezier, bool bAddText) const
2743 {
2744     // #i89784# check for FontWork with activated HideContour
2745     const drawinglayer::attribute::SdrTextAttribute aText(
2746 		drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2747     const bool bHideContour(
2748 		!aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2749 
2750     SdrObject* pRet = bHideContour ?
2751         0 :
2752         ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
2753 
2754     SdrPathObj* pPath = PTR_CAST(SdrPathObj, pRet);
2755 
2756 	if(pPath)
2757 	{
2758 		if(pPath->GetPathPoly().areControlPointsUsed())
2759 		{
2760 			if(!bBezier)
2761 			{
2762 				// reduce all bezier curves
2763 				pPath->SetPathPoly(basegfx::tools::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2764 			}
2765 		}
2766 		else
2767 		{
2768 			if(bBezier)
2769 			{
2770 				// create bezier curves
2771 				pPath->SetPathPoly(basegfx::tools::expandToCurve(pPath->GetPathPoly()));
2772 			}
2773 		}
2774 	}
2775 
2776     if(bAddText)
2777     {
2778     	pRet = ImpConvertAddText(pRet, bBezier);
2779     }
2780 
2781 	return pRet;
2782 }
2783 
2784 SdrObjGeoData* SdrPathObj::NewGeoData() const
2785 {
2786 	return new SdrPathObjGeoData;
2787 }
2788 
2789 void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
2790 {
2791 	SdrTextObj::SaveGeoData(rGeo);
2792 	SdrPathObjGeoData& rPGeo = (SdrPathObjGeoData&) rGeo;
2793 	rPGeo.maPathPolygon=GetPathPoly();
2794 	rPGeo.meKind=meKind;
2795 }
2796 
2797 void SdrPathObj::RestGeoData(const SdrObjGeoData& rGeo)
2798 {
2799 	SdrTextObj::RestGeoData(rGeo);
2800 	SdrPathObjGeoData& rPGeo=(SdrPathObjGeoData&)rGeo;
2801 	maPathPolygon=rPGeo.maPathPolygon;
2802 	meKind=rPGeo.meKind;
2803 	ImpForceKind(); // damit u.a. bClosed gesetzt wird
2804 }
2805 
2806 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2807 {
2808 	if(GetPathPoly() != rPathPoly)
2809 	{
2810 		maPathPolygon=rPathPoly;
2811 		ImpForceKind();
2812 		SetRectsDirty();
2813 	}
2814 }
2815 
2816 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2817 {
2818 	if(GetPathPoly() != rPathPoly)
2819 	{
2820 		Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
2821 		NbcSetPathPoly(rPathPoly);
2822 		SetChanged();
2823 		BroadcastObjectChange();
2824 		SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
2825 	}
2826 }
2827 
2828 void SdrPathObj::ToggleClosed() // long nOpenDistance)
2829 {
2830 	Rectangle aBoundRect0;
2831 	if(pUserCall != NULL)
2832 		aBoundRect0 = GetLastBoundRect();
2833 	ImpSetClosed(!IsClosed()); // neuen ObjKind setzen
2834 	ImpForceKind(); // wg. Line->Poly->PolyLine statt Line->Poly->Line
2835 	SetRectsDirty();
2836 	SetChanged();
2837 	BroadcastObjectChange();
2838 	SendUserCall(SDRUSERCALL_RESIZE, aBoundRect0);
2839 }
2840 
2841 // fuer friend class SdrPolyEditView auf einigen Compilern:
2842 void SdrPathObj::SetRectsDirty(sal_Bool bNotMyself)
2843 {
2844 	SdrTextObj::SetRectsDirty(bNotMyself);
2845 }
2846 
2847 ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
2848 {
2849 	if(!mpDAC)
2850 	{
2851 		((SdrPathObj*)this)->mpDAC = new ImpPathForDragAndCreate(*((SdrPathObj*)this));
2852 	}
2853 
2854 	return *mpDAC;
2855 }
2856 
2857 void SdrPathObj::impDeleteDAC() const
2858 {
2859 	if(mpDAC)
2860 	{
2861 		delete mpDAC;
2862 		((SdrPathObj*)this)->mpDAC = 0L;
2863 	}
2864 }
2865 
2866 ////////////////////////////////////////////////////////////////////////////////////////////////////
2867 //
2868 // transformation interface for StarOfficeAPI. This implements support for
2869 // homogen 3x3 matrices containing the transformation of the SdrObject. At the
2870 // moment it contains a shearX, rotation and translation, but for setting all linear
2871 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2872 //
2873 ////////////////////////////////////////////////////////////////////////////////////////////////////
2874 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2875 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2876 sal_Bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2877 {
2878 	double fRotate(0.0);
2879 	double fShearX(0.0);
2880 	basegfx::B2DTuple aScale(1.0, 1.0);
2881 	basegfx::B2DTuple aTranslate(0.0, 0.0);
2882 
2883 	if(GetPathPoly().count())
2884 	{
2885 		// copy geometry
2886 		basegfx::B2DHomMatrix aMoveToZeroMatrix;
2887 		rPolyPolygon = GetPathPoly();
2888 
2889 		if(OBJ_LINE == meKind)
2890 		{
2891 			// ignore shear and rotate, just use scale and translate
2892 			OSL_ENSURE(GetPathPoly().count() > 0L && GetPathPoly().getB2DPolygon(0L).count() > 1L, "OBJ_LINE with too less polygons (!)");
2893 			// #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2894 			// itself, else this method will no longer return the full polygon information (curve will
2895 			// be lost)
2896 			const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2897 			aScale = aPolyRangeNoCurve.getRange();
2898 			aTranslate = aPolyRangeNoCurve.getMinimum();
2899 
2900 			// define matrix for move polygon to zero point
2901 			aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2902 		}
2903 		else
2904 		{
2905 			if(aGeo.nShearWink || aGeo.nDrehWink)
2906 			{
2907 				// get rotate and shear in drawingLayer notation
2908 				fRotate = aGeo.nDrehWink * F_PI18000;
2909 				fShearX = aGeo.nShearWink * F_PI18000;
2910 
2911 				// build mathematically correct (negative shear and rotate) object transform
2912 				// containing shear and rotate to extract unsheared, unrotated polygon
2913 				basegfx::B2DHomMatrix aObjectMatrix;
2914 				aObjectMatrix.shearX(tan((36000 - aGeo.nShearWink) * F_PI18000));
2915 				aObjectMatrix.rotate((36000 - aGeo.nDrehWink) * F_PI18000);
2916 
2917 				// create inverse from it and back-transform polygon
2918 				basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2919 				aInvObjectMatrix.invert();
2920 				rPolyPolygon.transform(aInvObjectMatrix);
2921 
2922 				// get range from unsheared, unrotated polygon and extract scale and translate.
2923 				// transform topLeft from it back to transformed state to get original
2924 				// topLeft (rotation center)
2925 				// #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2926 				// itself, else this method will no longer return the full polygon information (curve will
2927 				// be lost)
2928 				const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2929 				aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2930 				aScale = aCorrectedRangeNoCurve.getRange();
2931 
2932 				// define matrix for move polygon to zero point
2933                 // #i112280# Added missing minus for Y-Translation
2934 				aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2935 			}
2936 			else
2937 			{
2938 				// get scale and translate from unsheared, unrotated polygon
2939 				// #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2940 				// itself, else this method will no longer return the full polygon information (curve will
2941 				// be lost)
2942 				const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2943 				aScale = aPolyRangeNoCurve.getRange();
2944 				aTranslate = aPolyRangeNoCurve.getMinimum();
2945 
2946 				// define matrix for move polygon to zero point
2947 				aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2948 			}
2949 		}
2950 
2951 		// move polygon to zero point with pre-defined matrix
2952 		rPolyPolygon.transform(aMoveToZeroMatrix);
2953 	}
2954 
2955 	// position maybe relative to anchorpos, convert
2956 	if( pModel && pModel->IsWriter() )
2957 	{
2958 		if(GetAnchorPos().X() || GetAnchorPos().Y())
2959 		{
2960 			aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2961 		}
2962 	}
2963 
2964 	// force MapUnit to 100th mm
2965 	SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
2966 	if(eMapUnit != SFX_MAPUNIT_100TH_MM)
2967 	{
2968 		switch(eMapUnit)
2969 		{
2970 			case SFX_MAPUNIT_TWIP :
2971 			{
2972 				// postion
2973 				aTranslate.setX(ImplTwipsToMM(aTranslate.getX()));
2974 				aTranslate.setY(ImplTwipsToMM(aTranslate.getY()));
2975 
2976 				// size
2977 				aScale.setX(ImplTwipsToMM(aScale.getX()));
2978 				aScale.setY(ImplTwipsToMM(aScale.getY()));
2979 
2980 				// polygon
2981 				basegfx::B2DHomMatrix aTwipsToMM;
2982 				const double fFactorTwipsToMM(127.0 / 72.0);
2983 				aTwipsToMM.scale(fFactorTwipsToMM, fFactorTwipsToMM);
2984 				rPolyPolygon.transform(aTwipsToMM);
2985 
2986 				break;
2987 			}
2988 			default:
2989 			{
2990 				DBG_ERROR("TRGetBaseGeometry: Missing unit translation to 100th mm!");
2991 			}
2992 		}
2993 	}
2994 
2995 	// build return value matrix
2996 	rMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
2997 		aScale,
2998 		basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2999 		basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
3000 		aTranslate);
3001 
3002 	return sal_True;
3003 }
3004 
3005 // sets the base geometry of the object using infos contained in the homogen 3x3 matrix.
3006 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
3007 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
3008 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
3009 {
3010 	// break up matrix
3011 	basegfx::B2DTuple aScale;
3012 	basegfx::B2DTuple aTranslate;
3013 	double fRotate, fShearX;
3014 	rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
3015 
3016 	// #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
3017 	// in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
3018 	if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
3019 	{
3020 		aScale.setX(fabs(aScale.getX()));
3021 		aScale.setY(fabs(aScale.getY()));
3022 		fRotate = fmod(fRotate + F_PI, F_2PI);
3023 	}
3024 
3025 	// copy poly
3026 	basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
3027 
3028 	// reset object shear and rotations
3029 	aGeo.nDrehWink = 0;
3030 	aGeo.RecalcSinCos();
3031 	aGeo.nShearWink = 0;
3032 	aGeo.RecalcTan();
3033 
3034 	// force metric to pool metric
3035 	SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
3036 	if(eMapUnit != SFX_MAPUNIT_100TH_MM)
3037 	{
3038 		switch(eMapUnit)
3039 		{
3040 			case SFX_MAPUNIT_TWIP :
3041 			{
3042 				// position
3043 				aTranslate.setX(ImplMMToTwips(aTranslate.getX()));
3044 				aTranslate.setY(ImplMMToTwips(aTranslate.getY()));
3045 
3046 				// size
3047 				aScale.setX(ImplMMToTwips(aScale.getX()));
3048 				aScale.setY(ImplMMToTwips(aScale.getY()));
3049 
3050 				// polygon
3051 				basegfx::B2DHomMatrix aMMToTwips;
3052 				const double fFactorMMToTwips(72.0 / 127.0);
3053 				aMMToTwips.scale(fFactorMMToTwips, fFactorMMToTwips);
3054 				aNewPolyPolygon.transform(aMMToTwips);
3055 
3056 				break;
3057 			}
3058 			default:
3059 			{
3060 				DBG_ERROR("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
3061 			}
3062 		}
3063 	}
3064 
3065 	if( pModel && pModel->IsWriter() )
3066 	{
3067 		// if anchor is used, make position relative to it
3068 		if(GetAnchorPos().X() || GetAnchorPos().Y())
3069 		{
3070 			aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3071 		}
3072 	}
3073 
3074 	// create transformation for polygon, set values at aGeo direct
3075 	basegfx::B2DHomMatrix aTransform;
3076 
3077 	// #i75086#
3078 	// Given polygon is already scaled (for historical reasons), but not mirrored yet.
3079 	// Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
3080 	if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0))
3081 	{
3082 		aTransform.scale(
3083 			basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0,
3084 			basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
3085 	}
3086 
3087 	if(!basegfx::fTools::equalZero(fShearX))
3088 	{
3089 		aTransform.shearX(tan(-atan(fShearX)));
3090 		aGeo.nShearWink = FRound(atan(fShearX) / F_PI18000);
3091 		aGeo.RecalcTan();
3092 	}
3093 
3094 	if(!basegfx::fTools::equalZero(fRotate))
3095 	{
3096         // #i78696#
3097         // fRotate is matematically correct for linear transformations, so it's
3098         // the one to use for the geometry change
3099 		aTransform.rotate(fRotate);
3100 
3101         // #i78696#
3102         // fRotate is matematically correct, but aGeoStat.nDrehWink is
3103         // mirrored -> mirror value here
3104 		aGeo.nDrehWink = NormAngle360(FRound(-fRotate / F_PI18000));
3105 		aGeo.RecalcSinCos();
3106 	}
3107 
3108     if(!aTranslate.equalZero())
3109 	{
3110 		// #i39529# absolute positioning, so get current position (without control points (!))
3111 		const basegfx::B2DRange aCurrentRange(basegfx::tools::getRange(aNewPolyPolygon));
3112 		aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
3113 	}
3114 
3115 	// transform polygon and trigger change
3116 	aNewPolyPolygon.transform(aTransform);
3117 	SetPathPoly(aNewPolyPolygon);
3118 }
3119 
3120 //////////////////////////////////////////////////////////////////////////////
3121 // eof
3122