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