merge the formfield patch from ooo-build
[ooovba.git] / svx / source / svdraw / svdopath.cxx
blob5558f69a01fd8a6097a99657271f8da7f33c06dc
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: svdopath.cxx,v $
10 * $Revision: 1.51.18.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include <tools/bigint.hxx>
35 #include <svx/svdopath.hxx>
36 #include <math.h>
37 #include <svx/xpool.hxx>
38 #include <svx/xpoly.hxx>
39 #include <svx/svdattr.hxx>
40 #include <svx/svdtrans.hxx>
41 #include <svx/svdetc.hxx>
42 #include <svx/svddrag.hxx>
43 #include <svx/svdmodel.hxx>
44 #include <svx/svdpage.hxx>
45 #include <svx/svdhdl.hxx>
46 #include <svx/svdview.hxx> // fuer MovCreate bei Freihandlinien
47 #include "svdglob.hxx" // Stringcache
48 #include "svdstr.hrc" // Objektname
50 #ifdef _MSC_VER
51 #pragma optimize ("",off)
52 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
53 #endif
55 #include <svx/xlnwtit.hxx>
56 #include <svx/xlnclit.hxx>
57 #include <svx/xflclit.hxx>
58 #include <svx/svdogrp.hxx>
60 #include <svx/polypolygoneditor.hxx>
61 #include <svx/xlntrit.hxx>
62 #include <vcl/salbtype.hxx> // FRound
63 #include "svdoimp.hxx"
64 #include <svx/sdr/contact/viewcontactofsdrpathobj.hxx>
65 #include <basegfx/matrix/b2dhommatrix.hxx>
67 // #104018# replace macros above with type-safe methods
68 inline sal_Int32 ImplTwipsToMM(sal_Int32 nVal) { return ((nVal * 127 + 36) / 72); }
69 inline sal_Int32 ImplMMToTwips(sal_Int32 nVal) { return ((nVal * 72 + 63) / 127); }
70 inline sal_Int64 ImplTwipsToMM(sal_Int64 nVal) { return ((nVal * 127 + 36) / 72); }
71 inline sal_Int64 ImplMMToTwips(sal_Int64 nVal) { return ((nVal * 72 + 63) / 127); }
72 inline double ImplTwipsToMM(double fVal) { return (fVal * (127.0 / 72.0)); }
73 inline double ImplMMToTwips(double fVal) { return (fVal * (72.0 / 127.0)); }
74 #include <basegfx/point/b2dpoint.hxx>
75 #include <basegfx/polygon/b2dpolypolygontools.hxx>
76 #include <basegfx/matrix/b2dhommatrix.hxx>
77 #include <basegfx/range/b2drange.hxx>
78 #include <basegfx/curve/b2dcubicbezier.hxx>
79 #include <basegfx/polygon/b2dpolygontools.hxx>
81 // #i89784#
82 #include <svx/sdr/attribute/sdrtextattribute.hxx>
83 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
85 using namespace sdr;
87 inline USHORT GetPrevPnt(USHORT nPnt, USHORT nPntMax, FASTBOOL bClosed)
89 if (nPnt>0) {
90 nPnt--;
91 } else {
92 nPnt=nPntMax;
93 if (bClosed) nPnt--;
95 return nPnt;
98 inline USHORT GetNextPnt(USHORT nPnt, USHORT nPntMax, FASTBOOL bClosed)
100 nPnt++;
101 if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
102 return nPnt;
105 struct ImpSdrPathDragData : public SdrDragStatUserData
107 XPolygon aXP; // Ausschnitt aud dem Originalpolygon
108 FASTBOOL bValid; // FALSE = zu wenig Punkte
109 FASTBOOL bClosed; // geschlossenes Objekt?
110 USHORT nPoly; // Nummer des Polygons im PolyPolygon
111 USHORT nPnt; // Punktnummer innerhalb des obigen Polygons
112 USHORT nPntAnz; // Punktanzahl des Polygons
113 USHORT nPntMax; // Maximaler Index
114 FASTBOOL bBegPnt; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
115 FASTBOOL bEndPnt; // Gedraggter Punkt ist der Endpunkt einer Polyline
116 USHORT nPrevPnt; // Index des vorherigen Punkts
117 USHORT nNextPnt; // Index des naechsten Punkts
118 FASTBOOL bPrevIsBegPnt; // Vorheriger Punkt ist Anfangspunkt einer Polyline
119 FASTBOOL bNextIsEndPnt; // Folgepunkt ist Endpunkt einer Polyline
120 USHORT nPrevPrevPnt; // Index des vorvorherigen Punkts
121 USHORT nNextNextPnt; // Index des uebernaechsten Punkts
122 FASTBOOL bControl; // Punkt ist ein Kontrollpunkt
123 FASTBOOL bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
124 FASTBOOL bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
125 FASTBOOL bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
126 FASTBOOL bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
127 USHORT nPrevPrevPnt0;
128 USHORT nPrevPnt0;
129 USHORT nPnt0;
130 USHORT nNextPnt0;
131 USHORT nNextNextPnt0;
132 FASTBOOL bEliminate; // Punkt loeschen? (wird von MovDrag gesetzt)
134 // ##
135 BOOL mbMultiPointDrag;
136 const XPolyPolygon maOrig;
137 XPolyPolygon maMove;
138 Container maHandles;
140 public:
141 ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, BOOL bMuPoDr, const SdrDragStat& rDrag);
142 void ResetPoly(const SdrPathObj& rPO);
143 BOOL IsMultiPointDrag() const { return mbMultiPointDrag; }
146 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, BOOL bMuPoDr, const SdrDragStat& rDrag)
147 : aXP(5),
148 mbMultiPointDrag(bMuPoDr),
149 maOrig(rPO.GetPathPoly()),
150 maHandles(0)
152 if(mbMultiPointDrag)
154 const SdrMarkView& rMarkView = *rDrag.GetView();
155 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
156 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
157 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
159 for(sal_uInt32 a(0); a < nHdlCount; a++)
161 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
163 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
165 maHandles.Insert(pTestHdl, CONTAINER_APPEND);
169 maMove = maOrig;
170 bValid = TRUE;
172 else
174 bValid=FALSE;
175 bClosed=rPO.IsClosed(); // geschlossenes Objekt?
176 nPoly=(sal_uInt16)rHdl.GetPolyNum(); // Nummer des Polygons im PolyPolygon
177 nPnt=(sal_uInt16)rHdl.GetPointNum(); // Punktnummer innerhalb des obigen Polygons
178 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
179 nPntAnz=aTmpXP.GetPointCount(); // Punktanzahl des Polygons
180 if (nPntAnz==0 || (bClosed && nPntAnz==1)) return; // min. 1Pt bei Line, min. 2 bei Polygon
181 nPntMax=nPntAnz-1; // Maximaler Index
182 bBegPnt=!bClosed && nPnt==0; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
183 bEndPnt=!bClosed && nPnt==nPntMax; // Gedraggter Punkt ist der Endpunkt einer Polyline
184 if (bClosed && nPntAnz<=3) { // Falls Polygon auch nur eine Linie ist
185 bBegPnt=(nPntAnz<3) || nPnt==0;
186 bEndPnt=(nPntAnz<3) || nPnt==nPntMax-1;
188 nPrevPnt=nPnt; // Index des vorherigen Punkts
189 nNextPnt=nPnt; // Index des naechsten Punkts
190 if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
191 if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
192 bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
193 bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
194 nPrevPrevPnt=nPnt; // Index des vorvorherigen Punkts
195 nNextNextPnt=nPnt; // Index des uebernaechsten Punkts
196 if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
197 if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
198 bControl=rHdl.IsPlusHdl(); // Punkt ist ein Kontrollpunkt
199 bIsPrevControl=FALSE; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
200 bIsNextControl=FALSE; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
201 bPrevIsControl=FALSE; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
202 bNextIsControl=FALSE; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
203 if (bControl) {
204 bIsPrevControl=aTmpXP.IsControl(nPrevPnt);
205 bIsNextControl=!bIsPrevControl;
206 } else {
207 bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==XPOLY_CONTROL;
208 bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==XPOLY_CONTROL;
210 nPrevPrevPnt0=nPrevPrevPnt;
211 nPrevPnt0 =nPrevPnt;
212 nPnt0 =nPnt;
213 nNextPnt0 =nNextPnt;
214 nNextNextPnt0=nNextNextPnt;
215 nPrevPrevPnt=0;
216 nPrevPnt=1;
217 nPnt=2;
218 nNextPnt=3;
219 nNextNextPnt=4;
220 bEliminate=FALSE;
221 ResetPoly(rPO);
222 bValid=TRUE;
226 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
228 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
229 aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
230 aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
231 aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
232 aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
233 aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
236 /*************************************************************************/
238 struct ImpPathCreateUser : public SdrDragStatUserData
240 Point aBezControl0;
241 Point aBezStart;
242 Point aBezCtrl1;
243 Point aBezCtrl2;
244 Point aBezEnd;
245 Point aCircStart;
246 Point aCircEnd;
247 Point aCircCenter;
248 Point aLineStart;
249 Point aLineEnd;
250 Point aRectP1;
251 Point aRectP2;
252 Point aRectP3;
253 long nCircRadius;
254 long nCircStWink;
255 long nCircRelWink;
256 FASTBOOL bBezier;
257 FASTBOOL bBezHasCtrl0;
258 FASTBOOL bCurve;
259 FASTBOOL bCircle;
260 FASTBOOL bAngleSnap;
261 FASTBOOL bLine;
262 FASTBOOL bLine90;
263 FASTBOOL bRect;
264 FASTBOOL bMixedCreate;
265 USHORT nBezierStartPoint;
266 SdrObjKind eStartKind;
267 SdrObjKind eAktKind;
269 public:
270 ImpPathCreateUser(): nCircRadius(0),nCircStWink(0),nCircRelWink(0),
271 bBezier(FALSE),bBezHasCtrl0(FALSE),bCurve(FALSE),bCircle(FALSE),bAngleSnap(FALSE),bLine(FALSE),bLine90(FALSE),bRect(FALSE),
272 bMixedCreate(FALSE),nBezierStartPoint(0),eStartKind(OBJ_NONE),eAktKind(OBJ_NONE) { }
274 void ResetFormFlags() { bBezier=FALSE; bCurve=FALSE; bCircle=FALSE; bLine=FALSE; bRect=FALSE; }
275 FASTBOOL IsFormFlag() const { return bBezier || bCurve || bCircle || bLine || bRect; }
276 XPolygon GetFormPoly() const;
277 FASTBOOL CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown);
278 XPolygon GetBezierPoly() const;
279 //FASTBOOL CalcCurve(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView) { return FALSE; }
280 XPolygon GetCurvePoly() const { return XPolygon(); }
281 FASTBOOL CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
282 XPolygon GetCirclePoly() const;
283 FASTBOOL CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
284 Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView* pView) const;
285 XPolygon GetLinePoly() const;
286 FASTBOOL CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
287 XPolygon GetRectPoly() const;
290 XPolygon ImpPathCreateUser::GetFormPoly() const
292 if (bBezier) return GetBezierPoly();
293 if (bCurve) return GetCurvePoly();
294 if (bCircle) return GetCirclePoly();
295 if (bLine) return GetLinePoly();
296 if (bRect) return GetRectPoly();
297 return XPolygon();
300 FASTBOOL ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown)
302 FASTBOOL bRet=TRUE;
303 aBezStart=rP1;
304 aBezCtrl1=rP1+rDir;
305 aBezCtrl2=rP2;
307 // #i21479#
308 // Also copy the end point when no end point is set yet
309 if (!bMouseDown || (0L == aBezEnd.X() && 0L == aBezEnd.Y())) aBezEnd=rP2;
311 bBezier=bRet;
312 return bRet;
315 XPolygon ImpPathCreateUser::GetBezierPoly() const
317 XPolygon aXP(4);
318 aXP[0]=aBezStart; aXP.SetFlags(0,XPOLY_SMOOTH);
319 aXP[1]=aBezCtrl1; aXP.SetFlags(1,XPOLY_CONTROL);
320 aXP[2]=aBezCtrl2; aXP.SetFlags(2,XPOLY_CONTROL);
321 aXP[3]=aBezEnd;
322 return aXP;
325 FASTBOOL ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
327 long nTangAngle=GetAngle(rDir);
328 aCircStart=rP1;
329 aCircEnd=rP2;
330 aCircCenter=rP1;
331 long dx=rP2.X()-rP1.X();
332 long dy=rP2.Y()-rP1.Y();
333 long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
334 dAngle=NormAngle360(dAngle);
335 long nTmpAngle=NormAngle360(9000-dAngle);
336 FASTBOOL bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
337 long nRad=0;
338 if (bRet) {
339 double cs=cos(nTmpAngle*nPi180);
340 double nR=(double)GetLen(Point(dx,dy))/cs/2;
341 nRad=Abs(Round(nR));
343 if (dAngle<18000) {
344 nCircStWink=NormAngle360(nTangAngle-9000);
345 nCircRelWink=NormAngle360(2*dAngle);
346 aCircCenter.X()+=Round(nRad*cos((nTangAngle+9000)*nPi180));
347 aCircCenter.Y()-=Round(nRad*sin((nTangAngle+9000)*nPi180));
348 } else {
349 nCircStWink=NormAngle360(nTangAngle+9000);
350 nCircRelWink=-NormAngle360(36000-2*dAngle);
351 aCircCenter.X()+=Round(nRad*cos((nTangAngle-9000)*nPi180));
352 aCircCenter.Y()-=Round(nRad*sin((nTangAngle-9000)*nPi180));
354 bAngleSnap=pView!=NULL && pView->IsAngleSnapEnabled();
355 if (bAngleSnap) {
356 long nSA=pView->GetSnapAngle();
357 if (nSA!=0) { // Winkelfang
358 FASTBOOL bNeg=nCircRelWink<0;
359 if (bNeg) nCircRelWink=-nCircRelWink;
360 nCircRelWink+=nSA/2;
361 nCircRelWink/=nSA;
362 nCircRelWink*=nSA;
363 nCircRelWink=NormAngle360(nCircRelWink);
364 if (bNeg) nCircRelWink=-nCircRelWink;
367 nCircRadius=nRad;
368 if (nRad==0 || Abs(nCircRelWink)<5) bRet=FALSE;
369 bCircle=bRet;
370 return bRet;
373 XPolygon ImpPathCreateUser::GetCirclePoly() const
375 if (nCircRelWink>=0) {
376 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
377 USHORT((nCircStWink+5)/10),USHORT((nCircStWink+nCircRelWink+5)/10),FALSE);
378 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
379 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
380 return aXP;
381 } else {
382 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
383 USHORT(NormAngle360(nCircStWink+nCircRelWink+5)/10),USHORT((nCircStWink+5)/10),FALSE);
384 USHORT nAnz=aXP.GetPointCount();
385 for (USHORT nNum=nAnz/2; nNum>0;) {
386 nNum--; // XPoly Punktreihenfolge umkehren
387 USHORT n2=nAnz-nNum-1;
388 Point aPt(aXP[nNum]);
389 aXP[nNum]=aXP[n2];
390 aXP[n2]=aPt;
392 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
393 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
394 return aXP;
398 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView* pView) const
400 long x=aCsr.X(),x1=x,x2=x;
401 long y=aCsr.Y(),y1=y,y2=y;
402 FASTBOOL bHLin=nDirY==0;
403 FASTBOOL bVLin=nDirX==0;
404 if (bHLin) y=0;
405 else if (bVLin) x=0;
406 else {
407 x1=BigMulDiv(y,nDirX,nDirY);
408 y2=BigMulDiv(x,nDirY,nDirX);
409 long l1=Abs(x1)+Abs(y1);
410 long l2=Abs(x2)+Abs(y2);
411 if (l1<=l2 !=(pView!=NULL && pView->IsBigOrtho())) {
412 x=x1; y=y1;
413 } else {
414 x=x2; y=y2;
417 return Point(x,y);
420 FASTBOOL ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
422 aLineStart=rP1;
423 aLineEnd=rP2;
424 bLine90=FALSE;
425 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=FALSE; return FALSE; }
426 Point aTmpPt(rP2-rP1);
427 long nDirX=rDir.X();
428 long nDirY=rDir.Y();
429 Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=Abs(aP1.X())+Abs(aP1.Y());
430 Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=Abs(aP2.X())+Abs(aP2.Y());
431 if (pView!=NULL && pView->IsOrtho()) nQ1=0; // Ortho schaltet rechtwinklig aus
432 bLine90=nQ1>2*nQ2;
433 if (!bLine90) { // glatter Uebergang
434 aLineEnd+=aP1;
435 } else { // rechtwinkliger Uebergang
436 aLineEnd+=aP2;
438 bLine=TRUE;
439 return TRUE;
442 XPolygon ImpPathCreateUser::GetLinePoly() const
444 XPolygon aXP(2);
445 aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,XPOLY_SMOOTH);
446 aXP[1]=aLineEnd;
447 return aXP;
450 FASTBOOL ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
452 aRectP1=rP1;
453 aRectP2=rP1;
454 aRectP3=rP2;
455 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=FALSE; return FALSE; }
456 Point aTmpPt(rP2-rP1);
457 long nDirX=rDir.X();
458 long nDirY=rDir.Y();
459 long x=aTmpPt.X();
460 long y=aTmpPt.Y();
461 FASTBOOL bHLin=nDirY==0;
462 FASTBOOL bVLin=nDirX==0;
463 if (bHLin) y=0;
464 else if (bVLin) x=0;
465 else {
466 y=BigMulDiv(x,nDirY,nDirX);
467 long nHypLen=aTmpPt.Y()-y;
468 long nTangAngle=-GetAngle(rDir);
469 // sin=g/h, g=h*sin
470 double a=nTangAngle*nPi180;
471 double sn=sin(a);
472 double cs=cos(a);
473 double nGKathLen=nHypLen*sn;
474 y+=Round(nGKathLen*sn);
475 x+=Round(nGKathLen*cs);
477 aRectP2.X()+=x;
478 aRectP2.Y()+=y;
479 if (pView!=NULL && pView->IsOrtho()) {
480 long dx1=aRectP2.X()-aRectP1.X(); long dx1a=Abs(dx1);
481 long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=Abs(dy1);
482 long dx2=aRectP3.X()-aRectP2.X(); long dx2a=Abs(dx2);
483 long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=Abs(dy2);
484 FASTBOOL b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
485 if (b1MoreThan2 != pView->IsBigOrtho()) {
486 long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
487 long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
488 aRectP2.X()+=xtemp;
489 aRectP2.Y()+=ytemp;
490 aRectP3.X()+=xtemp;
491 aRectP3.Y()+=ytemp;
492 } else {
493 long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
494 long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
495 aRectP3.X()+=xtemp;
496 aRectP3.Y()+=ytemp;
499 bRect=TRUE;
500 return TRUE;
503 XPolygon ImpPathCreateUser::GetRectPoly() const
505 XPolygon aXP(3);
506 aXP[0]=aRectP1; aXP.SetFlags(0,XPOLY_SMOOTH);
507 aXP[1]=aRectP2;
508 if (aRectP3!=aRectP2) aXP[2]=aRectP3;
509 return aXP;
512 /*************************************************************************/
514 class ImpPathForDragAndCreate
516 SdrPathObj& mrSdrPathObject;
517 XPolyPolygon aPathPolygon;
518 SdrObjKind meObjectKind;
519 ImpSdrPathDragData* mpSdrPathDragData;
520 bool mbCreating;
522 public:
523 ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
524 ~ImpPathForDragAndCreate();
526 // drag stuff
527 bool beginPathDrag( SdrDragStat& rDrag ) const;
528 bool movePathDrag( SdrDragStat& rDrag ) const;
529 bool endPathDrag( SdrDragStat& rDrag );
530 //void cancelSpecialDrag( SdrDragStat& rDrag ) const;
531 String getSpecialDragComment(const SdrDragStat& rDrag) const;
532 basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
534 // create stuff
535 FASTBOOL BegCreate(SdrDragStat& rStat);
536 FASTBOOL MovCreate(SdrDragStat& rStat);
537 FASTBOOL EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
538 FASTBOOL BckCreate(SdrDragStat& rStat);
539 void BrkCreate(SdrDragStat& rStat);
540 Pointer GetCreatePointer() const;
542 // helping stuff
543 bool IsClosed(SdrObjKind eKind) const { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
544 bool IsFreeHand(SdrObjKind eKind) const { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
545 bool IsBezier(SdrObjKind eKind) const { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
546 bool IsCreating() const { return mbCreating; }
548 // get the polygon
549 basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
550 basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag) const;
551 basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
554 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
555 : mrSdrPathObject(rSdrPathObject),
556 aPathPolygon(rSdrPathObject.GetPathPoly()),
557 meObjectKind(mrSdrPathObject.meKind),
558 mpSdrPathDragData(0),
559 mbCreating(false)
563 ImpPathForDragAndCreate::~ImpPathForDragAndCreate()
565 if(mpSdrPathDragData)
567 delete mpSdrPathDragData;
571 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat& rDrag ) const
573 const SdrHdl* pHdl=rDrag.GetHdl();
574 if(!pHdl)
575 return FALSE;
577 BOOL bMultiPointDrag(TRUE);
579 if(aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()].IsControl((sal_uInt16)pHdl->GetPointNum()))
580 bMultiPointDrag = FALSE;
582 if(bMultiPointDrag)
584 const SdrMarkView& rMarkView = *rDrag.GetView();
585 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
586 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
587 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
588 sal_uInt32 nSelectedPoints(0);
590 for(sal_uInt32 a(0); a < nHdlCount; a++)
592 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
594 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
596 nSelectedPoints++;
600 if(nSelectedPoints <= 1)
601 bMultiPointDrag = FALSE;
604 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag);
606 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
608 DBG_ERROR("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData ist ungueltig");
609 delete mpSdrPathDragData;
610 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = 0;
611 return false;
614 return true;
617 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
619 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
621 DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
622 return false;
625 if(mpSdrPathDragData->IsMultiPointDrag())
627 Point aDelta(rDrag.GetNow() - rDrag.GetStart());
629 if(aDelta.X() || aDelta.Y())
631 for(sal_uInt32 a(0); a < mpSdrPathDragData->maHandles.Count(); a++)
633 SdrHdl* pHandle = (SdrHdl*)mpSdrPathDragData->maHandles.GetObject(a);
634 const sal_uInt16 nPolyIndex((sal_uInt16)pHandle->GetPolyNum());
635 const sal_uInt16 nPointIndex((sal_uInt16)pHandle->GetPointNum());
636 const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
637 XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
638 const sal_uInt16 nPointCount(rOrig.GetPointCount());
639 BOOL bClosed(rOrig[0] == rOrig[nPointCount-1]);
641 // move point itself
642 rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
644 // when point is first and poly closed, move close point, too.
645 if(nPointCount > 0 && !nPointIndex && bClosed)
647 rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
649 // when moving the last point it may be necessary to move the
650 // control point in front of this one, too.
651 if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
652 rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
655 // is a control point before this?
656 if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
658 // Yes, move it, too
659 rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
662 // is a control point after this?
663 if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
665 // Yes, move it, too
666 rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
671 else
673 mpSdrPathDragData->ResetPoly(mrSdrPathObject);
675 // Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
676 FASTBOOL bClosed =mpSdrPathDragData->bClosed ; // geschlossenes Objekt?
677 USHORT nPnt =mpSdrPathDragData->nPnt ; // Punktnummer innerhalb des obigen Polygons
678 FASTBOOL bBegPnt =mpSdrPathDragData->bBegPnt ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
679 FASTBOOL bEndPnt =mpSdrPathDragData->bEndPnt ; // Gedraggter Punkt ist der Endpunkt einer Polyline
680 USHORT nPrevPnt =mpSdrPathDragData->nPrevPnt ; // Index des vorherigen Punkts
681 USHORT nNextPnt =mpSdrPathDragData->nNextPnt ; // Index des naechsten Punkts
682 FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
683 FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
684 USHORT nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // Index des vorvorherigen Punkts
685 USHORT nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // Index des uebernaechsten Punkts
686 FASTBOOL bControl =mpSdrPathDragData->bControl ; // Punkt ist ein Kontrollpunkt
687 //FASTBOOL bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
688 FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
689 FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
690 FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
692 // Ortho bei Linien/Polygonen = Winkel beibehalten
693 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsOrtho()) {
694 FASTBOOL bBigOrtho=rDrag.GetView()->IsBigOrtho();
695 Point aPos(rDrag.GetNow()); // die aktuelle Position
696 Point aPnt(mpSdrPathDragData->aXP[nPnt]); // der gedraggte Punkt
697 USHORT nPnt1=0xFFFF,nPnt2=0xFFFF; // seine Nachbarpunkte
698 Point aNeuPos1,aNeuPos2; // die neuen Alternativen fuer aPos
699 FASTBOOL bPnt1=FALSE,bPnt2=FALSE; // die neuen Alternativen gueltig?
700 if (!bClosed && mpSdrPathDragData->nPntAnz>=2) { // Mind. 2 Pt bei Linien
701 if (!bBegPnt) nPnt1=nPrevPnt;
702 if (!bEndPnt) nPnt2=nNextPnt;
704 if (bClosed && mpSdrPathDragData->nPntAnz>=3) { // Mind. 3 Pt bei Polygon
705 nPnt1=nPrevPnt;
706 nPnt2=nNextPnt;
708 if (nPnt1!=0xFFFF && !bPrevIsControl) {
709 Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
710 long ndx0=aPnt.X()-aPnt1.X();
711 long ndy0=aPnt.Y()-aPnt1.Y();
712 FASTBOOL bHLin=ndy0==0;
713 FASTBOOL bVLin=ndx0==0;
714 if (!bHLin || !bVLin) {
715 long ndx=aPos.X()-aPnt1.X();
716 long ndy=aPos.Y()-aPnt1.Y();
717 bPnt1=TRUE;
718 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
719 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
720 FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
721 FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
722 if (bHor) ndy=long(ndy0*nXFact);
723 if (bVer) ndx=long(ndx0*nYFact);
724 aNeuPos1=aPnt1;
725 aNeuPos1.X()+=ndx;
726 aNeuPos1.Y()+=ndy;
729 if (nPnt2!=0xFFFF && !bNextIsControl) {
730 Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
731 long ndx0=aPnt.X()-aPnt2.X();
732 long ndy0=aPnt.Y()-aPnt2.Y();
733 FASTBOOL bHLin=ndy0==0;
734 FASTBOOL bVLin=ndx0==0;
735 if (!bHLin || !bVLin) {
736 long ndx=aPos.X()-aPnt2.X();
737 long ndy=aPos.Y()-aPnt2.Y();
738 bPnt2=TRUE;
739 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
740 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
741 FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
742 FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
743 if (bHor) ndy=long(ndy0*nXFact);
744 if (bVer) ndx=long(ndx0*nYFact);
745 aNeuPos2=aPnt2;
746 aNeuPos2.X()+=ndx;
747 aNeuPos2.Y()+=ndy;
750 if (bPnt1 && bPnt2) { // beide Alternativen vorhanden (Konkurenz)
751 BigInt nX1(aNeuPos1.X()-aPos.X()); nX1*=nX1;
752 BigInt nY1(aNeuPos1.Y()-aPos.Y()); nY1*=nY1;
753 BigInt nX2(aNeuPos2.X()-aPos.X()); nX2*=nX2;
754 BigInt nY2(aNeuPos2.Y()-aPos.Y()); nY2*=nY2;
755 nX1+=nY1; // Korrekturabstand zum Quadrat
756 nX2+=nY2; // Korrekturabstand zum Quadrat
757 // Die Alternative mit dem geringeren Korrekturbedarf gewinnt
758 if (nX1<nX2) bPnt2=FALSE; else bPnt1=FALSE;
760 if (bPnt1) rDrag.Now()=aNeuPos1;
761 if (bPnt2) rDrag.Now()=aNeuPos2;
763 rDrag.SetActionRect(Rectangle(rDrag.GetNow(),rDrag.GetNow()));
765 // IBM Special: Punkte eliminieren, wenn die beiden angrenzenden
766 // Linien eh' fast 180 deg sind.
767 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsEliminatePolyPoints() &&
768 !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
770 Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
771 aPt-=rDrag.GetNow();
772 long nWink1=GetAngle(aPt);
773 aPt=rDrag.GetNow();
774 aPt-=mpSdrPathDragData->aXP[nPrevPnt];
775 long nWink2=GetAngle(aPt);
776 long nDiff=nWink1-nWink2;
777 nDiff=Abs(nDiff);
778 mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
779 if (mpSdrPathDragData->bEliminate) { // Position anpassen, damit Smooth an den Enden stimmt
780 aPt=mpSdrPathDragData->aXP[nNextPnt];
781 aPt+=mpSdrPathDragData->aXP[nPrevPnt];
782 aPt/=2;
783 rDrag.Now()=aPt;
787 // Um diese Entfernung wurde insgesamt gedraggd
788 Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
790 // Insgesamt sind 8 Faelle moeglich:
791 // X 1. Weder rechts noch links Ctrl.
792 // o--X--o 2. Rechts und links Ctrl, gedraggd wird St.
793 // o--X 3. Nur links Ctrl, gedraggd wird St.
794 // X--o 4. Nur rechts Ctrl, gedraggd wird St.
795 // x--O--o 5. Rechts und links Ctrl, gedraggd wird links.
796 // x--O 6. Nur links Ctrl, gedraggd wird links.
797 // o--O--x 7. Rechts und links Ctrl, gedraggd wird rechts.
798 // O--x 8. Nur rechts Ctrl, gedraggd wird rechts.
799 // Zusaetzlich ist zu beachten, dass das Veraendern einer Linie (keine Kurve)
800 // eine evtl. Kurve am anderen Ende der Linie bewirkt, falls dort Smooth
801 // gesetzt ist (Kontrollpunktausrichtung an Gerade).
803 mpSdrPathDragData->aXP[nPnt]+=aDiff;
805 // Nun symmetrische PlusHandles etc. checken
806 if (bControl) { // Faelle 5,6,7,8
807 USHORT nSt=nPnt; // der zugehoerige Stuetzpunkt
808 USHORT nFix=nPnt; // der gegenueberliegende Kontrollpunkt
809 if (bIsNextControl) { // Wenn der naechste ein Kontrollpunkt ist, muss der vorh. der Stuetzpunkt sein
810 nSt=nPrevPnt;
811 nFix=nPrevPrevPnt;
812 } else {
813 nSt=nNextPnt;
814 nFix=nNextNextPnt;
816 if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
817 mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
821 if (!bControl) { // Faelle 1,2,3,4 wobei bei 1 nix passiert und bei 3+4 unten noch mehr folgt
822 // die beiden Kontrollpunkte mit verschieben
823 if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
824 if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
825 // Kontrollpunkt ggf. an Gerade ausrichten
826 if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
827 if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // Fall 3
828 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
830 if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // Fall 4
831 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
834 // Und nun noch die anderen Enden der Strecken ueberpruefen (nPnt+-1).
835 // Ist dort eine Kurve (IsControl(nPnt+-2)) mit SmoothJoin (nPnt+-1),
836 // so muss der entsprechende Kontrollpunkt (nPnt+-2) angepasst werden.
837 if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
838 if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
839 mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
842 if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
843 if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
844 mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
850 return true;
853 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat& rDrag)
855 Point aLinePt1;
856 Point aLinePt2;
857 bool bLineGlueMirror(OBJ_LINE == meObjectKind);
858 if (bLineGlueMirror) { // #40549#
859 XPolygon& rXP=aPathPolygon[0];
860 aLinePt1=rXP[0];
861 aLinePt2=rXP[1];
864 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
866 DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
867 return false;
870 if(mpSdrPathDragData->IsMultiPointDrag())
872 aPathPolygon = mpSdrPathDragData->maMove;
874 else
876 const SdrHdl* pHdl=rDrag.GetHdl();
878 // Referenz auf das Polygon
879 XPolygon& rXP=aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()];
881 // Die 5 Punkte die sich evtl. geaendert haben
882 if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
883 if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
884 if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
885 if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
886 rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
888 // Letzter Punkt muss beim Geschlossenen immer gleich dem Ersten sein
889 if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
891 if (mpSdrPathDragData->bEliminate)
893 basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
894 sal_uInt32 nPoly,nPnt;
896 if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
898 basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
899 aCandidate.remove(nPnt);
901 if((IsClosed(meObjectKind) && aCandidate.count() < 3L) || aCandidate.count() < 2L)
903 aTempPolyPolygon.remove(nPoly);
905 else
907 aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
911 aPathPolygon = XPolyPolygon(aTempPolyPolygon);
914 // Winkel anpassen fuer Text an einfacher Linie
915 if (bLineGlueMirror)
916 { // #40549#
917 Point aLinePt1_(aPathPolygon[0][0]);
918 Point aLinePt2_(aPathPolygon[0][1]);
919 FASTBOOL bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
920 FASTBOOL bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
921 if (bXMirr || bYMirr) {
922 Point aRef1(mrSdrPathObject.GetSnapRect().Center());
923 if (bXMirr) {
924 Point aRef2(aRef1);
925 aRef2.Y()++;
926 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
928 if (bYMirr) {
929 Point aRef2(aRef1);
930 aRef2.X()++;
931 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
937 delete mpSdrPathDragData;
938 mpSdrPathDragData = 0;
940 return true;
943 /*void ImpPathForDragAndCreate::cancelSpecialDrag( SdrDragStat& rDrag ) const
945 ImpSdrPathDragData* pID=(ImpSdrPathDragData*)rDrag.GetUser();
946 if (pID!=NULL) {
947 delete pID;
948 rDrag.SetUser(NULL);
952 String ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
954 XubString aStr;
955 const SdrHdl* pHdl = rDrag.GetHdl();
956 const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
958 if(bCreateComment && rDrag.GetUser())
960 // #i103058# re-add old creation comment mode
961 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
962 const SdrObjKind eKindMerk(meObjectKind);
963 mrSdrPathObject.meKind = pU->eAktKind;
964 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewCreateObj, aStr);
965 mrSdrPathObject.meKind = eKindMerk;
967 Point aPrev(rDrag.GetPrev());
968 Point aNow(rDrag.GetNow());
970 if(pU->bLine)
971 aNow = pU->aLineEnd;
973 aNow -= aPrev;
974 aStr.AppendAscii(" (");
976 XubString aMetr;
978 if(pU->bCircle)
980 mrSdrPathObject.GetModel()->TakeWinkStr(Abs(pU->nCircRelWink), aMetr);
981 aStr += aMetr;
982 aStr.AppendAscii(" r=");
983 mrSdrPathObject.GetModel()->TakeMetricStr(pU->nCircRadius, aMetr, TRUE);
984 aStr += aMetr;
987 aStr.AppendAscii("dx=");
988 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X(), aMetr, TRUE);
989 aStr += aMetr;
991 aStr.AppendAscii(" dy=");
992 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y(), aMetr, TRUE);
993 aStr += aMetr;
995 if(!IsFreeHand(meObjectKind))
997 INT32 nLen(GetLen(aNow));
998 aStr.AppendAscii(" l=");
999 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
1000 aStr += aMetr;
1002 INT32 nWink(GetAngle(aNow));
1003 aStr += sal_Unicode(' ');
1004 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1005 aStr += aMetr;
1008 aStr += sal_Unicode(')');
1010 else if(!mrSdrPathObject.GetModel() || !pHdl)
1012 // #i103058# fallback when no model and/or Handle, both needed
1013 // for else-path
1014 mrSdrPathObject.ImpTakeDescriptionStr(STR_DragPathObj, aStr);
1016 else
1018 // #i103058# standard for modification; model and handle needed
1019 ImpSdrPathDragData* pDragData = mpSdrPathDragData;
1021 if(!pDragData)
1023 // getSpecialDragComment is also used from create, so fallback to GetUser()
1024 // when mpSdrPathDragData is not set
1025 pDragData = (ImpSdrPathDragData*)rDrag.GetUser();
1028 if(!pDragData)
1030 DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
1031 return String();
1034 if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
1036 // Punkt von ...
1037 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewMarkedPoint, aStr);
1039 // %O loeschen
1040 XubString aStr2(ImpGetResStr(STR_EditDelete));
1042 // UNICODE: Punkt von ... loeschen
1043 aStr2.SearchAndReplaceAscii("%1", aStr);
1045 return aStr2;
1048 // dx=0.00 dy=0.00 // Beide Seiten Bezier
1049 // dx=0.00 dy=0.00 l=0.00 0.00ø // Anfang oder Ende oder eine Seite Bezier bzw. Hebel
1050 // dx=0.00 dy=0.00 l=0.00 0.00ø / l=0.00 0.00ø // Mittendrin
1051 XubString aMetr;
1052 Point aBeg(rDrag.GetStart());
1053 Point aNow(rDrag.GetNow());
1055 aStr = String();
1056 aStr.AppendAscii("dx=");
1057 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X() - aBeg.X(), aMetr, TRUE);
1058 aStr += aMetr;
1060 aStr.AppendAscii(" dy=");
1061 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y() - aBeg.Y(), aMetr, TRUE);
1062 aStr += aMetr;
1064 if(!pDragData->IsMultiPointDrag())
1066 UINT16 nPntNum((sal_uInt16)pHdl->GetPointNum());
1067 const XPolygon& rXPoly = aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1068 UINT16 nPntAnz((sal_uInt16)rXPoly.GetPointCount());
1069 BOOL bClose(IsClosed(meObjectKind));
1071 if(bClose)
1072 nPntAnz--;
1074 if(pHdl->IsPlusHdl())
1076 // Hebel
1077 UINT16 nRef(nPntNum);
1079 if(rXPoly.IsControl(nPntNum + 1))
1080 nRef--;
1081 else
1082 nRef++;
1084 aNow -= rXPoly[nRef];
1086 INT32 nLen(GetLen(aNow));
1087 aStr.AppendAscii(" l=");
1088 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
1089 aStr += aMetr;
1091 INT32 nWink(GetAngle(aNow));
1092 aStr += sal_Unicode(' ');
1093 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1094 aStr += aMetr;
1096 else if(nPntAnz > 1)
1098 UINT16 nPntMax(nPntAnz - 1);
1099 Point aPt1,aPt2;
1100 BOOL bIsClosed(IsClosed(meObjectKind));
1101 BOOL bPt1(nPntNum > 0);
1102 BOOL bPt2(nPntNum < nPntMax);
1104 if(bIsClosed && nPntAnz > 2)
1106 bPt1 = TRUE;
1107 bPt2 = TRUE;
1110 UINT16 nPt1,nPt2;
1112 if(nPntNum > 0)
1113 nPt1 = nPntNum - 1;
1114 else
1115 nPt1 = nPntMax;
1117 if(nPntNum < nPntMax)
1118 nPt2 = nPntNum + 1;
1119 else
1120 nPt2 = 0;
1122 if(bPt1 && rXPoly.IsControl(nPt1))
1123 bPt1 = FALSE; // Keine Anzeige
1125 if(bPt2 && rXPoly.IsControl(nPt2))
1126 bPt2 = FALSE; // von Bezierdaten
1128 if(bPt1)
1130 Point aPt(aNow);
1131 aPt -= rXPoly[nPt1];
1133 INT32 nLen(GetLen(aPt));
1134 aStr.AppendAscii(" l=");
1135 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
1136 aStr += aMetr;
1138 INT32 nWink(GetAngle(aPt));
1139 aStr += sal_Unicode(' ');
1140 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1141 aStr += aMetr;
1144 if(bPt2)
1146 if(bPt1)
1147 aStr.AppendAscii(" / ");
1148 else
1149 aStr.AppendAscii(" ");
1151 Point aPt(aNow);
1152 aPt -= rXPoly[nPt2];
1154 INT32 nLen(GetLen(aPt));
1155 aStr.AppendAscii("l=");
1156 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
1157 aStr += aMetr;
1159 INT32 nWink(GetAngle(aPt));
1160 aStr += sal_Unicode(' ');
1161 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1162 aStr += aMetr;
1168 return aStr;
1171 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
1173 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1175 DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
1176 return basegfx::B2DPolyPolygon();
1179 XPolyPolygon aRetval;
1181 if(mpSdrPathDragData->IsMultiPointDrag())
1183 aRetval.Insert(mpSdrPathDragData->maMove);
1185 else
1187 const XPolygon& rXP=aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1188 if (rXP.GetPointCount()<=2) { //|| rXPoly.GetFlags(1)==XPOLY_CONTROL && rXPoly.GetPointCount()<=4
1189 XPolygon aXPoly(rXP);
1190 aXPoly[(sal_uInt16)rDrag.GetHdl()->GetPointNum()]=rDrag.GetNow();
1191 aRetval.Insert(aXPoly);
1192 return aRetval.getB2DPolyPolygon();
1194 // Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
1195 FASTBOOL bClosed =mpSdrPathDragData->bClosed ; // geschlossenes Objekt?
1196 USHORT nPntAnz =mpSdrPathDragData->nPntAnz ; // Punktanzahl
1197 USHORT nPnt =mpSdrPathDragData->nPnt ; // Punktnummer innerhalb des Polygons
1198 FASTBOOL bBegPnt =mpSdrPathDragData->bBegPnt ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
1199 FASTBOOL bEndPnt =mpSdrPathDragData->bEndPnt ; // Gedraggter Punkt ist der Endpunkt einer Polyline
1200 USHORT nPrevPnt =mpSdrPathDragData->nPrevPnt ; // Index des vorherigen Punkts
1201 USHORT nNextPnt =mpSdrPathDragData->nNextPnt ; // Index des naechsten Punkts
1202 FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
1203 FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
1204 USHORT nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // Index des vorvorherigen Punkts
1205 USHORT nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // Index des uebernaechsten Punkts
1206 FASTBOOL bControl =mpSdrPathDragData->bControl ; // Punkt ist ein Kontrollpunkt
1207 //FASTBOOL bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
1208 FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
1209 FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
1210 FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
1211 XPolygon aXPoly(mpSdrPathDragData->aXP);
1212 XPolygon aLine1(2);
1213 XPolygon aLine2(2);
1214 XPolygon aLine3(2);
1215 XPolygon aLine4(2);
1216 if (bControl) {
1217 aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1218 if (bIsNextControl) { // bin ich Kontrollpunkt hinter der Stuetzstelle?
1219 aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1220 aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1221 aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1222 if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1223 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1224 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1225 // Hebellienien fuer das gegenueberliegende Kurvensegment
1226 aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1227 aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1228 aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1229 aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1230 } else {
1231 aXPoly.Remove(0,1);
1233 } else { // ansonsten bin ich Kontrollpunkt vor der Stuetzstelle
1234 aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1235 aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1236 aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1237 if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1238 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1239 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1240 // Hebellinien fuer das gegenueberliegende Kurvensegment
1241 aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1242 aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1243 aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1244 aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1245 } else {
1246 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1249 } else { // ansonsten kein Kontrollpunkt
1250 if (mpSdrPathDragData->bEliminate) {
1251 aXPoly.Remove(2,1);
1253 if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_NORMAL);
1254 else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1255 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1256 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1257 } else {
1258 aXPoly.Remove(0,1);
1259 if (bBegPnt) aXPoly.Remove(0,1);
1261 if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_NORMAL);
1262 else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1263 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1264 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1265 } else {
1266 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1267 if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1269 if (bClosed) { // "Birnenproblem": 2 Linien, 1 Kurve, alles Smooth, Punkt zw. beiden Linien wird gedraggt
1270 if (aXPoly.GetPointCount()>nPntAnz && aXPoly.IsControl(1)) {
1271 USHORT a=aXPoly.GetPointCount();
1272 aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1273 aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1274 aXPoly.Remove(0,3);
1278 aRetval.Insert(aXPoly);
1279 if (aLine1.GetPointCount()>1) aRetval.Insert(aLine1);
1280 if (aLine2.GetPointCount()>1) aRetval.Insert(aLine2);
1281 if (aLine3.GetPointCount()>1) aRetval.Insert(aLine3);
1282 if (aLine4.GetPointCount()>1) aRetval.Insert(aLine4);
1285 return aRetval.getB2DPolyPolygon();
1288 FASTBOOL ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
1290 bool bFreeHand(IsFreeHand(meObjectKind));
1291 rStat.SetNoSnap(bFreeHand);
1292 rStat.SetOrtho8Possible();
1293 aPathPolygon.Clear();
1294 mbCreating=TRUE;
1295 FASTBOOL bMakeStartPoint=TRUE;
1296 SdrView* pView=rStat.GetView();
1297 if (pView!=NULL && pView->IsUseIncompatiblePathCreateInterface() &&
1298 (meObjectKind==OBJ_POLY || meObjectKind==OBJ_PLIN || meObjectKind==OBJ_PATHLINE || meObjectKind==OBJ_PATHFILL)) {
1299 bMakeStartPoint=FALSE;
1301 aPathPolygon.Insert(XPolygon());
1302 aPathPolygon[0][0]=rStat.GetStart();
1303 if (bMakeStartPoint) {
1304 aPathPolygon[0][1]=rStat.GetNow();
1306 ImpPathCreateUser* pU=new ImpPathCreateUser;
1307 pU->eStartKind=meObjectKind;
1308 pU->eAktKind=meObjectKind;
1309 rStat.SetUser(pU);
1310 return TRUE;
1313 FASTBOOL ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
1315 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1316 SdrView* pView=rStat.GetView();
1317 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1318 if (pView!=NULL && pView->IsCreateMode()) {
1319 // ggf. auf anderes CreateTool umschalten
1320 UINT16 nIdent;
1321 UINT32 nInvent;
1322 pView->TakeCurrentObj(nIdent,nInvent);
1323 if (nInvent==SdrInventor && pU->eAktKind!=(SdrObjKind)nIdent) {
1324 SdrObjKind eNewKind=(SdrObjKind)nIdent;
1325 switch (eNewKind) {
1326 case OBJ_CARC: case OBJ_CIRC: case OBJ_CCUT: case OBJ_SECT: eNewKind=OBJ_CARC;
1327 case OBJ_RECT:
1328 case OBJ_LINE: case OBJ_PLIN: case OBJ_POLY:
1329 case OBJ_PATHLINE: case OBJ_PATHFILL:
1330 case OBJ_FREELINE: case OBJ_FREEFILL:
1331 case OBJ_SPLNLINE: case OBJ_SPLNFILL: {
1332 pU->eAktKind=eNewKind;
1333 pU->bMixedCreate=TRUE;
1334 pU->nBezierStartPoint=rXPoly.GetPointCount();
1335 if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1336 } break;
1337 default: break;
1338 } // switch
1341 USHORT nActPoint=rXPoly.GetPointCount();
1342 if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nActPoint<2) {
1343 rXPoly[0]=rStat.GetPos0();
1344 rXPoly[1]=rStat.GetNow();
1345 nActPoint=2;
1347 if (nActPoint==0) {
1348 rXPoly[0]=rStat.GetPos0();
1349 } else nActPoint--;
1350 FASTBOOL bFreeHand=IsFreeHand(pU->eAktKind);
1351 rStat.SetNoSnap(bFreeHand /*|| (pU->bMixed && pU->eAktKind==OBJ_LINE)*/);
1352 rStat.SetOrtho8Possible(pU->eAktKind!=OBJ_CARC && pU->eAktKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eAktKind!=OBJ_LINE));
1353 Point aActMerk(rXPoly[nActPoint]);
1354 rXPoly[nActPoint]=rStat.Now();
1355 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1356 Point aPt(rStat.Start());
1357 if (pView!=NULL && pView->IsCreate1stPointAsCenter()) {
1358 aPt+=aPt;
1359 aPt-=rStat.Now();
1361 rXPoly[0]=aPt;
1363 OutputDevice* pOut=pView==NULL ? NULL : pView->GetFirstOutputDevice(); // GetWin(0);
1364 if (bFreeHand) {
1365 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1366 if (rStat.IsMouseDown() && nActPoint>0) {
1367 // keine aufeinanderfolgenden Punkte an zu Nahe gelegenen Positionen zulassen
1368 long nMinDist=1;
1369 if (pView!=NULL) nMinDist=pView->GetFreeHandMinDistPix();
1370 if (pOut!=NULL) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1371 if (nMinDist<1) nMinDist=1;
1373 Point aPt0(rXPoly[nActPoint-1]);
1374 Point aPt1(rStat.Now());
1375 long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1376 long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1377 if (dx<nMinDist && dy<nMinDist) return FALSE;
1379 // folgendes ist aus EndCreate kopiert (nur kleine Modifikationen)
1380 // und sollte dann mal in eine Methode zusammengefasst werden:
1382 if (nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1383 rXPoly.PointsToBezier(nActPoint-3);
1384 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1385 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1387 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1388 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1389 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1392 rXPoly[nActPoint+1]=rStat.Now();
1393 rStat.NextPoint();
1394 } else {
1395 pU->nBezierStartPoint=nActPoint;
1399 pU->ResetFormFlags();
1400 if (IsBezier(pU->eAktKind)) {
1401 if (nActPoint>=2) {
1402 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],rStat.IsMouseDown());
1403 } else if (pU->bBezHasCtrl0) {
1404 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],pU->aBezControl0-rXPoly[nActPoint-1],rStat.IsMouseDown());
1407 if (pU->eAktKind==OBJ_CARC && nActPoint>=2) {
1408 pU->CalcCircle(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1410 if (pU->eAktKind==OBJ_LINE && nActPoint>=2) {
1411 pU->CalcLine(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1413 if (pU->eAktKind==OBJ_RECT && nActPoint>=2) {
1414 pU->CalcRect(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1417 return TRUE;
1420 FASTBOOL ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
1422 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1423 FASTBOOL bRet=FALSE;
1424 SdrView* pView=rStat.GetView();
1425 FASTBOOL bIncomp=pView!=NULL && pView->IsUseIncompatiblePathCreateInterface();
1426 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1427 USHORT nActPoint=rXPoly.GetPointCount()-1;
1428 Point aAktMerk(rXPoly[nActPoint]);
1429 rXPoly[nActPoint]=rStat.Now();
1430 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1431 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1432 bRet=eCmd==SDRCREATE_FORCEEND;
1433 if (bRet) {
1434 mbCreating=FALSE;
1435 delete pU;
1436 rStat.SetUser(NULL);
1438 return bRet;
1441 if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1442 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1443 bRet=eCmd==SDRCREATE_FORCEEND;
1444 if (bRet) {
1445 mbCreating=FALSE;
1446 delete pU;
1447 rStat.SetUser(NULL);
1449 return bRet;
1451 if (eCmd==SDRCREATE_NEXTPOINT || eCmd==SDRCREATE_NEXTOBJECT) {
1452 // keine aufeinanderfolgenden Punkte an identischer Position zulassen
1453 if (nActPoint==0 || rStat.Now()!=rXPoly[nActPoint-1]) {
1454 if (bIncomp) {
1455 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1456 if (IsBezier(pU->eAktKind) && nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1457 rXPoly.PointsToBezier(nActPoint-3);
1458 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1459 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1461 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1462 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1463 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1466 } else {
1467 if (nActPoint==1 && IsBezier(pU->eAktKind) && !pU->bBezHasCtrl0) {
1468 pU->aBezControl0=rStat.GetNow();;
1469 pU->bBezHasCtrl0=TRUE;
1470 nActPoint--;
1472 if (pU->IsFormFlag()) {
1473 USHORT nPtAnz0=rXPoly.GetPointCount();
1474 rXPoly.Remove(nActPoint-1,2); // die letzten beiden Punkte entfernen und durch die Form ersetzen
1475 rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1476 USHORT nPtAnz1=rXPoly.GetPointCount();
1477 for (USHORT i=nPtAnz0+1; i<nPtAnz1-1; i++) { // Damit BckAction richtig funktioniert
1478 if (!rXPoly.IsControl(i)) rStat.NextPoint();
1480 nActPoint=rXPoly.GetPointCount()-1;
1483 nActPoint++;
1484 rXPoly[nActPoint]=rStat.GetNow();
1486 if (eCmd==SDRCREATE_NEXTOBJECT) {
1487 if (rXPoly.GetPointCount()>=2) {
1488 pU->bBezHasCtrl0=FALSE;
1489 // nur einzelnes Polygon kann offen sein, deshalb schliessen
1490 rXPoly[nActPoint]=rXPoly[0];
1491 XPolygon aXP;
1492 aXP[0]=rStat.GetNow();
1493 aPathPolygon.Insert(aXP);
1498 USHORT nPolyAnz=aPathPolygon.Count();
1499 if (nPolyAnz!=0) {
1500 // den letzten Punkt ggf. wieder loeschen
1501 if (eCmd==SDRCREATE_FORCEEND) {
1502 XPolygon& rXP=aPathPolygon[nPolyAnz-1];
1503 USHORT nPtAnz=rXP.GetPointCount();
1504 if (nPtAnz>=2) {
1505 if (!rXP.IsControl(nPtAnz-2)) {
1506 if (rXP[nPtAnz-1]==rXP[nPtAnz-2]) {
1507 rXP.Remove(nPtAnz-1,1);
1509 } else {
1510 if (rXP[nPtAnz-3]==rXP[nPtAnz-2]) {
1511 rXP.Remove(nPtAnz-3,3);
1516 for (USHORT nPolyNum=nPolyAnz; nPolyNum>0;) {
1517 nPolyNum--;
1518 XPolygon& rXP=aPathPolygon[nPolyNum];
1519 USHORT nPtAnz=rXP.GetPointCount();
1520 // Polygone mit zu wenig Punkten werden geloescht
1521 if (nPolyNum<nPolyAnz-1 || eCmd==SDRCREATE_FORCEEND) {
1522 if (nPtAnz<2) aPathPolygon.Remove(nPolyNum);
1526 pU->ResetFormFlags();
1527 bRet=eCmd==SDRCREATE_FORCEEND;
1528 if (bRet) {
1529 mbCreating=FALSE;
1530 delete pU;
1531 rStat.SetUser(NULL);
1533 return bRet;
1536 FASTBOOL ImpPathForDragAndCreate::BckCreate(SdrDragStat& rStat)
1538 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1539 if (aPathPolygon.Count()>0) {
1540 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1541 USHORT nActPoint=rXPoly.GetPointCount();
1542 if (nActPoint>0) {
1543 nActPoint--;
1544 // Das letzte Stueck einer Bezierkurve wird erstmal zu 'ner Linie
1545 rXPoly.Remove(nActPoint,1);
1546 if (nActPoint>=3 && rXPoly.IsControl(nActPoint-1)) {
1547 // Beziersegment am Ende sollte zwar nicht vorkommen, aber falls doch ...
1548 rXPoly.Remove(nActPoint-1,1);
1549 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1552 nActPoint=rXPoly.GetPointCount();
1553 if (nActPoint>=4) { // Kein Beziersegment am Ende
1554 nActPoint--;
1555 if (rXPoly.IsControl(nActPoint-1)) {
1556 rXPoly.Remove(nActPoint-1,1);
1557 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1560 if (rXPoly.GetPointCount()<2) {
1561 aPathPolygon.Remove(aPathPolygon.Count()-1);
1563 if (aPathPolygon.Count()>0) {
1564 XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1565 USHORT nLocalActPoint=rLocalXPoly.GetPointCount();
1566 if (nLocalActPoint>0) {
1567 nLocalActPoint--;
1568 rLocalXPoly[nLocalActPoint]=rStat.Now();
1572 pU->ResetFormFlags();
1573 return aPathPolygon.Count()!=0;
1576 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
1578 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1579 aPathPolygon.Clear();
1580 mbCreating=FALSE;
1581 delete pU;
1582 rStat.SetUser(NULL);
1585 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
1587 basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
1588 SdrView* pView = rDrag.GetView();
1590 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1591 return aRetval;
1593 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1594 basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1L) : basegfx::B2DPolygon());
1596 if(pU->IsFormFlag() && aNewPolygon.count() > 1L)
1598 // remove last segment and replace with current
1599 // do not forget to rescue the previous control point which will be lost when
1600 // the point it's associated with is removed
1601 const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1602 const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1604 aNewPolygon.remove(nChangeIndex, 2L);
1605 aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1607 if(nChangeIndex < aNewPolygon.count())
1609 // if really something was added, set the saved prev control point at the
1610 // point where it belongs
1611 aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1615 if(aRetval.count())
1617 aRetval.setB2DPolygon(aRetval.count() - 1L, aNewPolygon);
1619 else
1621 aRetval.append(aNewPolygon);
1624 return aRetval;
1627 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag) const
1629 basegfx::B2DPolyPolygon aRetval;
1630 SdrView* pView = rDrag.GetView();
1632 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1633 return aRetval;
1635 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1637 if(pU && pU->bBezier && rDrag.IsMouseDown())
1639 // no more XOR, no need for complicated helplines
1640 basegfx::B2DPolygon aHelpline;
1641 aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1642 aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1643 aRetval.append(aHelpline);
1646 return aRetval;
1649 Pointer ImpPathForDragAndCreate::GetCreatePointer() const
1651 switch (meObjectKind) {
1652 case OBJ_LINE : return Pointer(POINTER_DRAW_LINE);
1653 case OBJ_POLY : return Pointer(POINTER_DRAW_POLYGON);
1654 case OBJ_PLIN : return Pointer(POINTER_DRAW_POLYGON);
1655 case OBJ_PATHLINE: return Pointer(POINTER_DRAW_BEZIER);
1656 case OBJ_PATHFILL: return Pointer(POINTER_DRAW_BEZIER);
1657 case OBJ_FREELINE: return Pointer(POINTER_DRAW_FREEHAND);
1658 case OBJ_FREEFILL: return Pointer(POINTER_DRAW_FREEHAND);
1659 case OBJ_SPLNLINE: return Pointer(POINTER_DRAW_FREEHAND);
1660 case OBJ_SPLNFILL: return Pointer(POINTER_DRAW_FREEHAND);
1661 case OBJ_PATHPOLY: return Pointer(POINTER_DRAW_POLYGON);
1662 case OBJ_PATHPLIN: return Pointer(POINTER_DRAW_POLYGON);
1663 default: break;
1664 } // switch
1665 return Pointer(POINTER_CROSS);
1668 /*************************************************************************/
1670 SdrPathObjGeoData::SdrPathObjGeoData()
1674 SdrPathObjGeoData::~SdrPathObjGeoData()
1678 //////////////////////////////////////////////////////////////////////////////
1679 // DrawContact section
1681 sdr::contact::ViewContact* SdrPathObj::CreateObjectSpecificViewContact()
1683 return new sdr::contact::ViewContactOfSdrPathObj(*this);
1686 /*************************************************************************/
1688 TYPEINIT1(SdrPathObj,SdrTextObj);
1690 SdrPathObj::SdrPathObj(SdrObjKind eNewKind)
1691 : meKind(eNewKind),
1692 mpDAC(0L)
1694 bClosedObj = IsClosed();
1697 SdrPathObj::SdrPathObj(SdrObjKind eNewKind, const basegfx::B2DPolyPolygon& rPathPoly)
1698 : maPathPolygon(rPathPoly),
1699 meKind(eNewKind),
1700 mpDAC(0L)
1702 bClosedObj = IsClosed();
1703 ImpForceKind();
1706 SdrPathObj::~SdrPathObj()
1708 impDeleteDAC();
1711 sal_Bool ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1713 return (1L == rPolyPolygon.count() && 2L == rPolyPolygon.getB2DPolygon(0L).count());
1716 Rectangle ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
1718 basegfx::B2DRange aRange(basegfx::tools::getRange(rPolyPolygon));
1720 return Rectangle(
1721 FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1722 FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1725 void SdrPathObj::ImpForceLineWink()
1727 if(OBJ_LINE == meKind && ImpIsLine(GetPathPoly()))
1729 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1730 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1731 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1732 const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1733 const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1734 const Point aDelt(aPoint1 - aPoint0);
1736 aGeo.nDrehWink=GetAngle(aDelt);
1737 aGeo.nShearWink=0;
1738 aGeo.RecalcSinCos();
1739 aGeo.RecalcTan();
1741 // #101412# for SdrTextObj, keep aRect up to date
1742 aRect = Rectangle(aPoint0, aPoint1);
1743 aRect.Justify();
1747 void SdrPathObj::ImpForceKind()
1749 if (meKind==OBJ_PATHPLIN) meKind=OBJ_PLIN;
1750 if (meKind==OBJ_PATHPOLY) meKind=OBJ_POLY;
1752 if(GetPathPoly().areControlPointsUsed())
1754 switch (meKind)
1756 case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1757 case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1758 case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1759 default: break;
1762 else
1764 switch (meKind)
1766 case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1767 case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1768 case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1769 case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1770 default: break;
1774 if (meKind==OBJ_LINE && !ImpIsLine(GetPathPoly())) meKind=OBJ_PLIN;
1775 if (meKind==OBJ_PLIN && ImpIsLine(GetPathPoly())) meKind=OBJ_LINE;
1777 bClosedObj=IsClosed();
1779 if (meKind==OBJ_LINE)
1781 ImpForceLineWink();
1783 else
1785 // #i10659#, similar to #101412# but for polys with more than 2 points.
1787 // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1788 // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1789 // a scaling loop started from SdrExchangeView::Paste. This is principally nothing
1790 // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1791 // this is the case, some size needs to be set here in aRect to avoid that the cyclus
1792 // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1793 // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1794 // from the local Resize() implementation.
1796 // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1797 // text rectangle for the text object itself and methods at SdrTextObj do handle it
1798 // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1799 // which is basically wrong. To make the SdrText methods which deal with aRect directly
1800 // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1801 // command for SdrPathObj. Since adding this update mechanism with #101412# to
1802 // ImpForceLineWink() for lines was very successful, i add it to where ImpForceLineWink()
1803 // was called, once here below and once on a 2nd place below.
1805 // #i10659# for SdrTextObj, keep aRect up to date
1806 if(GetPathPoly().count())
1808 aRect = ImpGetBoundRect(GetPathPoly());
1812 // #i75974# adapt polygon state to object type. This may include a reinterpretation
1813 // of a closed geometry as open one, but with identical first and last point
1814 for(sal_uInt32 a(0); a < maPathPolygon.count(); a++)
1816 basegfx::B2DPolygon aCandidate(maPathPolygon.getB2DPolygon(a));
1818 if((bool)IsClosed() != aCandidate.isClosed())
1820 // #i80213# really change polygon geometry; else e.g. the last point which
1821 // needs to be identical with the first one will be missing when opening
1822 // due to OBJ_PATH type
1823 if(aCandidate.isClosed())
1825 basegfx::tools::openWithGeometryChange(aCandidate);
1827 else
1829 basegfx::tools::closeWithGeometryChange(aCandidate);
1832 maPathPolygon.setB2DPolygon(a, aCandidate);
1837 void SdrPathObj::ImpSetClosed(sal_Bool bClose)
1839 if(bClose)
1841 switch (meKind)
1843 case OBJ_LINE : meKind=OBJ_POLY; break;
1844 case OBJ_PLIN : meKind=OBJ_POLY; break;
1845 case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1846 case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1847 case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1848 default: break;
1851 bClosedObj = TRUE;
1853 else
1855 switch (meKind)
1857 case OBJ_POLY : meKind=OBJ_PLIN; break;
1858 case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1859 case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1860 case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1861 default: break;
1864 bClosedObj = FALSE;
1867 ImpForceKind();
1870 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1872 rInfo.bNoContortion=FALSE;
1874 FASTBOOL bCanConv = !HasText() || ImpCanConvTextToCurve();
1875 FASTBOOL bIsPath = IsBezier() || IsSpline();
1877 rInfo.bEdgeRadiusAllowed = FALSE;
1878 rInfo.bCanConvToPath = bCanConv && !bIsPath;
1879 rInfo.bCanConvToPoly = bCanConv && bIsPath;
1880 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
1883 UINT16 SdrPathObj::GetObjIdentifier() const
1885 return USHORT(meKind);
1888 void SdrPathObj::operator=(const SdrObject& rObj)
1890 SdrTextObj::operator=(rObj);
1891 SdrPathObj& rPath=(SdrPathObj&)rObj;
1892 maPathPolygon=rPath.GetPathPoly();
1895 void SdrPathObj::TakeObjNameSingul(XubString& rName) const
1897 if(OBJ_LINE == meKind)
1899 sal_uInt16 nId(STR_ObjNameSingulLINE);
1901 if(ImpIsLine(GetPathPoly()))
1903 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1904 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1905 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1906 const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1907 const Point aPoint1(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1909 if(aB2DPoint0 != aB2DPoint1)
1911 if(aB2DPoint0.getY() == aB2DPoint1.getY())
1913 nId = STR_ObjNameSingulLINE_Hori;
1915 else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1917 nId = STR_ObjNameSingulLINE_Vert;
1919 else
1921 const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1922 const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1924 if(fDx == fDy)
1926 nId = STR_ObjNameSingulLINE_Diag;
1932 rName = ImpGetResStr(nId);
1934 else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1936 const sal_Bool bClosed(OBJ_POLY == meKind);
1937 sal_uInt16 nId(0);
1939 if(mpDAC && mpDAC->IsCreating())
1941 if(bClosed)
1943 nId = STR_ObjNameSingulPOLY;
1945 else
1947 nId = STR_ObjNameSingulPLIN;
1950 rName = ImpGetResStr(nId);
1952 else
1954 // get point count
1955 sal_uInt32 nPointCount(0L);
1956 const sal_uInt32 nPolyCount(GetPathPoly().count());
1958 for(sal_uInt32 a(0L); a < nPolyCount; a++)
1960 nPointCount += GetPathPoly().getB2DPolygon(a).count();
1963 if(bClosed)
1965 nId = STR_ObjNameSingulPOLY_PntAnz;
1967 else
1969 nId = STR_ObjNameSingulPLIN_PntAnz;
1972 rName = ImpGetResStr(nId);
1973 sal_uInt16 nPos(rName.SearchAscii("%2")); // #i96537#
1975 if(STRING_NOTFOUND != nPos)
1977 rName.Erase(nPos, 2);
1978 rName.Insert(UniString::CreateFromInt32(nPointCount), nPos);
1982 else
1984 switch (meKind)
1986 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNameSingulPATHLINE); break;
1987 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNameSingulFREELINE); break;
1988 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNameSingulNATSPLN); break;
1989 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNameSingulPATHFILL); break;
1990 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNameSingulFREEFILL); break;
1991 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNameSingulPERSPLN); break;
1992 default: break;
1996 String aName(GetName());
1997 if(aName.Len())
1999 rName += sal_Unicode(' ');
2000 rName += sal_Unicode('\'');
2001 rName += aName;
2002 rName += sal_Unicode('\'');
2006 void SdrPathObj::TakeObjNamePlural(XubString& rName) const
2008 switch(meKind)
2010 case OBJ_LINE : rName=ImpGetResStr(STR_ObjNamePluralLINE ); break;
2011 case OBJ_PLIN : rName=ImpGetResStr(STR_ObjNamePluralPLIN ); break;
2012 case OBJ_POLY : rName=ImpGetResStr(STR_ObjNamePluralPOLY ); break;
2013 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNamePluralPATHLINE); break;
2014 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNamePluralFREELINE); break;
2015 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNamePluralNATSPLN); break;
2016 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNamePluralPATHFILL); break;
2017 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNamePluralFREEFILL); break;
2018 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNamePluralPERSPLN); break;
2019 default: break;
2023 basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
2025 return GetPathPoly();
2028 sal_uInt32 SdrPathObj::GetHdlCount() const
2030 sal_uInt32 nRetval(0L);
2031 const sal_uInt32 nPolyCount(GetPathPoly().count());
2033 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2035 nRetval += GetPathPoly().getB2DPolygon(a).count();
2038 return nRetval;
2041 SdrHdl* SdrPathObj::GetHdl(sal_uInt32 nHdlNum) const
2043 // #i73248#
2044 // Warn the user that this is ineffective and show alternatives. Should not be used at all.
2045 OSL_ENSURE(false, "SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
2047 // to have an alternative, get single handle using the ineffective way
2048 SdrHdl* pRetval = 0;
2049 SdrHdlList aLocalList(0);
2050 AddToHdlList(aLocalList);
2051 const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());
2053 if(nHdlCount && nHdlNum < nHdlCount)
2055 // remove and remember. The other created handles will be deleted again with the
2056 // destruction of the local list
2057 pRetval = aLocalList.RemoveHdl(nHdlNum);
2060 return pRetval;
2063 void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
2065 // keep old stuff to be able to keep old SdrHdl stuff, too
2066 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2067 USHORT nPolyCnt=aOldPathPolygon.Count();
2068 FASTBOOL bClosed=IsClosed();
2069 USHORT nIdx=0;
2071 for (USHORT i=0; i<nPolyCnt; i++) {
2072 const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
2073 USHORT nPntCnt=rXPoly.GetPointCount();
2074 if (bClosed && nPntCnt>1) nPntCnt--;
2076 for (USHORT j=0; j<nPntCnt; j++) {
2077 if (rXPoly.GetFlags(j)!=XPOLY_CONTROL) {
2078 const Point& rPnt=rXPoly[j];
2079 SdrHdl* pHdl=new SdrHdl(rPnt,HDL_POLY);
2080 pHdl->SetPolyNum(i);
2081 pHdl->SetPointNum(j);
2082 pHdl->Set1PixMore(j==0);
2083 pHdl->SetSourceHdlNum(nIdx);
2084 nIdx++;
2085 rHdlList.AddHdl(pHdl);
2091 sal_uInt32 SdrPathObj::GetPlusHdlCount(const SdrHdl& rHdl) const
2093 // keep old stuff to be able to keep old SdrHdl stuff, too
2094 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2095 sal_uInt16 nCnt = 0;
2096 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2097 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2099 if(nPolyNum < aOldPathPolygon.Count())
2101 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2102 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2103 if (nPntMax>0)
2105 nPntMax--;
2106 if (nPnt<=nPntMax)
2108 if (rXPoly.GetFlags(nPnt)!=XPOLY_CONTROL)
2110 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2111 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL) nCnt++;
2112 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2113 if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL) nCnt++;
2119 return nCnt;
2122 SdrHdl* SdrPathObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlusNum) const
2124 // keep old stuff to be able to keep old SdrHdl stuff, too
2125 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2126 SdrHdl* pHdl = 0L;
2127 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2128 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2130 if (nPolyNum<aOldPathPolygon.Count())
2132 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2133 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2135 if (nPntMax>0)
2137 nPntMax--;
2138 if (nPnt<=nPntMax)
2140 pHdl=new SdrHdlBezWgt(&rHdl);
2141 pHdl->SetPolyNum(rHdl.GetPolyNum());
2143 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2144 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL && nPlusNum==0)
2146 pHdl->SetPos(rXPoly[nPnt-1]);
2147 pHdl->SetPointNum(nPnt-1);
2149 else
2151 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2152 if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL)
2154 pHdl->SetPos(rXPoly[nPnt+1]);
2155 pHdl->SetPointNum(nPnt+1);
2159 pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2160 pHdl->SetPlusHdl(TRUE);
2164 return pHdl;
2167 ////////////////////////////////////////////////////////////////////////////////////////////////////
2169 bool SdrPathObj::hasSpecialDrag() const
2171 return true;
2174 bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
2176 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2178 return aDragAndCreate.beginPathDrag(rDrag);
2181 bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
2183 ImpPathForDragAndCreate aDragAndCreate(*this);
2184 bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2186 if(bRetval)
2188 bRetval = aDragAndCreate.movePathDrag(rDrag);
2191 if(bRetval)
2193 bRetval = aDragAndCreate.endPathDrag(rDrag);
2196 if(bRetval)
2198 NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2201 return bRetval;
2204 String SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2206 String aRetval;
2208 if(mpDAC)
2210 // #i103058# also get a comment when in creation
2211 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2213 if(bCreateComment)
2215 aRetval = mpDAC->getSpecialDragComment(rDrag);
2218 else
2220 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2221 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2223 if(bDidWork)
2225 aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2229 return aRetval;
2232 basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
2234 basegfx::B2DPolyPolygon aRetval;
2235 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2236 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2238 if(bDidWork)
2240 aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2243 return aRetval;
2246 ////////////////////////////////////////////////////////////////////////////////////////////////////
2248 FASTBOOL SdrPathObj::BegCreate(SdrDragStat& rStat)
2250 impDeleteDAC();
2251 return impGetDAC().BegCreate(rStat);
2254 FASTBOOL SdrPathObj::MovCreate(SdrDragStat& rStat)
2256 return impGetDAC().MovCreate(rStat);
2259 FASTBOOL SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
2261 FASTBOOL bRetval(impGetDAC().EndCreate(rStat, eCmd));
2263 if(bRetval && mpDAC)
2265 SetPathPoly(mpDAC->getModifiedPolyPolygon());
2267 // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2268 // to be able to use the type-changing ImpSetClosed method
2269 if(!IsClosedObj())
2271 SdrView* pView = rStat.GetView();
2273 if(pView && pView->IsAutoClosePolys() && !pView->IsUseIncompatiblePathCreateInterface())
2275 OutputDevice* pOut = pView->GetFirstOutputDevice();
2277 if(pOut)
2279 if(GetPathPoly().count())
2281 const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2283 if(aCandidate.count() > 2)
2285 // check distance of first and last point
2286 const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2287 const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2289 if(aDistVector.getLength() <= (double)nCloseDist)
2291 // close it
2292 ImpSetClosed(true);
2300 impDeleteDAC();
2303 return bRetval;
2306 FASTBOOL SdrPathObj::BckCreate(SdrDragStat& rStat)
2308 return impGetDAC().BckCreate(rStat);
2311 void SdrPathObj::BrkCreate(SdrDragStat& rStat)
2313 impGetDAC().BrkCreate(rStat);
2314 impDeleteDAC();
2317 basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
2319 basegfx::B2DPolyPolygon aRetval;
2321 if(mpDAC)
2323 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2324 aRetval.append(mpDAC->TakeDragPolyPolygon(rDrag));
2327 return aRetval;
2330 // during drag or create, allow accessing the so-far created/modified polyPolygon
2331 basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
2333 basegfx::B2DPolyPolygon aRetval;
2335 if(mpDAC)
2337 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2340 return aRetval;
2343 basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
2345 basegfx::B2DPolyPolygon aRetval;
2347 if(mpDAC)
2349 aRetval = mpDAC->TakeDragPolyPolygon(rDrag);
2352 return aRetval;
2355 Pointer SdrPathObj::GetCreatePointer() const
2357 return impGetDAC().GetCreatePointer();
2360 void SdrPathObj::NbcMove(const Size& rSiz)
2362 basegfx::B2DHomMatrix aTrans;
2363 aTrans.translate(rSiz.Width(), rSiz.Height());
2364 maPathPolygon.transform(aTrans);
2366 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2367 SdrTextObj::NbcMove(rSiz);
2370 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2372 basegfx::B2DHomMatrix aTrans;
2373 aTrans.translate(-rRef.X(), -rRef.Y());
2374 aTrans.scale(double(xFact), double(yFact));
2375 aTrans.translate(rRef.X(), rRef.Y());
2376 maPathPolygon.transform(aTrans);
2378 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2379 SdrTextObj::NbcResize(rRef,xFact,yFact);
2382 void SdrPathObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
2384 basegfx::B2DHomMatrix aTrans;
2385 aTrans.translate(-rRef.X(), -rRef.Y());
2386 aTrans.rotate(-nWink * nPi180); // Thank JOE, the angles are defined mirrored to the mathematical meanings
2387 aTrans.translate(rRef.X(), rRef.Y());
2388 maPathPolygon.transform(aTrans);
2390 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2391 SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
2394 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, FASTBOOL bVShear)
2396 basegfx::B2DHomMatrix aTrans;
2397 aTrans.translate(-rRefPnt.X(), -rRefPnt.Y());
2399 if(bVShear)
2401 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2402 aTrans.shearY(-fTan);
2404 else
2406 aTrans.shearX(-fTan);
2409 aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2410 maPathPolygon.transform(aTrans);
2412 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2413 SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2416 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2418 basegfx::B2DHomMatrix aTrans;
2419 const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2420 const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2421 const double fRot(atan2(fDiffY, fDiffX));
2422 aTrans.translate(-rRefPnt1.X(), -rRefPnt1.Y());
2423 aTrans.rotate(-fRot);
2424 aTrans.scale(1.0, -1.0);
2425 aTrans.rotate(fRot);
2426 aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2427 maPathPolygon.transform(aTrans);
2429 // #97538# Do Joe's special handling for lines when mirroring, too
2430 ImpForceKind();
2432 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2433 SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2436 void SdrPathObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
2438 if(!aGeo.nDrehWink)
2440 rRect = GetSnapRect();
2442 else
2444 XPolyPolygon aXPP(GetPathPoly());
2445 RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2446 rRect=aXPP.GetBoundRect();
2447 Point aTmp(rRect.TopLeft());
2448 RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2449 aTmp-=rRect.TopLeft();
2450 rRect.Move(aTmp.X(),aTmp.Y());
2454 void SdrPathObj::RecalcSnapRect()
2456 if(GetPathPoly().count())
2458 maSnapRect = ImpGetBoundRect(GetPathPoly());
2462 void SdrPathObj::NbcSetSnapRect(const Rectangle& rRect)
2464 Rectangle aOld(GetSnapRect());
2466 // #95736# Take RECT_EMPTY into account when calculating scale factors
2467 long nMulX = (RECT_EMPTY == rRect.Right()) ? 0 : rRect.Right() - rRect.Left();
2469 long nDivX = aOld.Right() - aOld.Left();
2471 // #95736# Take RECT_EMPTY into account when calculating scale factors
2472 long nMulY = (RECT_EMPTY == rRect.Bottom()) ? 0 : rRect.Bottom() - rRect.Top();
2474 long nDivY = aOld.Bottom() - aOld.Top();
2475 if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2476 if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2477 Fraction aX(nMulX,nDivX);
2478 Fraction aY(nMulY,nDivY);
2479 NbcResize(aOld.TopLeft(), aX, aY);
2480 NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2483 sal_uInt32 SdrPathObj::GetSnapPointCount() const
2485 return GetHdlCount();
2488 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2490 sal_uInt32 nPoly,nPnt;
2491 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2493 DBG_ASSERT(FALSE,"SdrPathObj::GetSnapPoint: Punkt nSnapPnt nicht vorhanden!");
2496 const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2497 return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2500 sal_Bool SdrPathObj::IsPolyObj() const
2502 return sal_True;
2505 sal_uInt32 SdrPathObj::GetPointCount() const
2507 const sal_uInt32 nPolyCount(GetPathPoly().count());
2508 sal_uInt32 nRetval(0L);
2510 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2512 nRetval += GetPathPoly().getB2DPolygon(a).count();
2515 return nRetval;
2518 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2520 Point aRetval;
2521 sal_uInt32 nPoly,nPnt;
2523 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2525 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2526 const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2527 aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2530 return aRetval;
2533 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2535 sal_uInt32 nPoly,nPnt;
2537 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2539 basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2540 aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2541 maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2543 if(meKind==OBJ_LINE)
2545 ImpForceLineWink();
2547 else
2549 if(GetPathPoly().count())
2551 // #i10659# for SdrTextObj, keep aRect up to date
2552 aRect = ImpGetBoundRect(GetPathPoly()); // fuer SdrTextObj#
2556 SetRectsDirty();
2560 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, sal_Bool bNewObj, sal_Bool bHideHim)
2562 sal_uInt32 nNewHdl;
2564 if(bNewObj)
2566 nNewHdl = NbcInsPoint(0L, rPos, sal_True, bHideHim);
2568 else
2570 // look for smallest distance data
2571 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2572 sal_uInt32 nSmallestPolyIndex(0L);
2573 sal_uInt32 nSmallestEdgeIndex(0L);
2574 double fSmallestCut;
2575 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2577 // create old polygon index from it
2578 sal_uInt32 nPolyIndex(nSmallestEdgeIndex);
2580 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2582 nPolyIndex += GetPathPoly().getB2DPolygon(a).count();
2585 nNewHdl = NbcInsPoint(nPolyIndex, rPos, sal_False, bHideHim);
2588 ImpForceKind();
2589 return nNewHdl;
2592 sal_uInt32 SdrPathObj::NbcInsPoint(sal_uInt32 /*nHdlNum*/, const Point& rPos, sal_Bool bNewObj, sal_Bool /*bHideHim*/)
2594 sal_uInt32 nNewHdl;
2596 if(bNewObj)
2598 basegfx::B2DPolygon aNewPoly;
2599 const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2600 aNewPoly.append(aPoint);
2601 aNewPoly.setClosed(IsClosed());
2602 maPathPolygon.append(aNewPoly);
2603 SetRectsDirty();
2604 nNewHdl = GetHdlCount();
2606 else
2608 // look for smallest distance data
2609 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2610 sal_uInt32 nSmallestPolyIndex(0L);
2611 sal_uInt32 nSmallestEdgeIndex(0L);
2612 double fSmallestCut;
2613 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2614 basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2615 const bool bBefore(!aCandidate.isClosed() && 0L == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2616 const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2L && 1.0 == fSmallestCut);
2618 if(bBefore)
2620 // before first point
2621 aCandidate.insert(0L, aTestPoint);
2623 if(aCandidate.areControlPointsUsed())
2625 if(aCandidate.isNextControlPointUsed(1))
2627 aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2628 aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2632 nNewHdl = 0L;
2634 else if(bAfter)
2636 // after last point
2637 aCandidate.append(aTestPoint);
2639 if(aCandidate.areControlPointsUsed())
2641 if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2643 aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2644 aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2648 nNewHdl = aCandidate.count() - 1L;
2650 else
2652 // in between
2653 bool bSegmentSplit(false);
2654 const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2656 if(aCandidate.areControlPointsUsed())
2658 if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2660 bSegmentSplit = true;
2664 if(bSegmentSplit)
2666 // rebuild original segment to get the split data
2667 basegfx::B2DCubicBezier aBezierA, aBezierB;
2668 const basegfx::B2DCubicBezier aBezier(
2669 aCandidate.getB2DPoint(nSmallestEdgeIndex),
2670 aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2671 aCandidate.getPrevControlPoint(nNextIndex),
2672 aCandidate.getB2DPoint(nNextIndex));
2674 // split and insert hit point
2675 aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2676 aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2678 // since we inserted hit point and not split point, we need to add an offset
2679 // to the control points to get the C1 continuity we want to achieve
2680 const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2681 aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2682 aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2683 aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2684 aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2686 else
2688 aCandidate.insert(nSmallestEdgeIndex + 1L, aTestPoint);
2691 nNewHdl = nSmallestEdgeIndex + 1L;
2694 maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2696 // create old polygon index from it
2697 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2699 nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2703 ImpForceKind();
2704 return nNewHdl;
2707 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2709 SdrPathObj* pNewObj = 0L;
2710 const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2711 sal_uInt32 nPoly, nPnt;
2713 if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2715 if(0L == nPoly)
2717 const basegfx::B2DPolygon aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2718 const sal_uInt32 nPointCount(aCandidate.count());
2720 if(nPointCount)
2722 if(IsClosed())
2724 // when closed, RipPoint means to open the polygon at the selected point. To
2725 // be able to do that, it is necessary to make the selected point the first one
2726 basegfx::B2DPolygon aNewPolygon(basegfx::tools::makeStartPoint(aCandidate, nPnt));
2727 SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2728 ToggleClosed();
2730 // give back new position of old start point (historical reasons)
2731 rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2733 else
2735 if(nPointCount >= 3L && nPnt != 0L && nPnt + 1L < nPointCount)
2737 // split in two objects at point nPnt
2738 basegfx::B2DPolygon aSplitPolyA(aCandidate, 0L, nPnt + 1L);
2739 SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2741 pNewObj = (SdrPathObj*)Clone();
2742 basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2743 pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2750 return pNewObj;
2753 SdrObject* SdrPathObj::DoConvertToPolyObj(BOOL bBezier) const
2755 // #i89784# check for FontWork with activated HideContour
2756 bool bHideContour(false);
2759 drawinglayer::attribute::SdrTextAttribute* pText = drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0));
2760 bHideContour = pText && pText->getSdrFormTextAttribute() && pText->isHideContour();
2761 delete pText;
2764 SdrObject* pRet = bHideContour ?
2765 0 :
2766 ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
2768 SdrPathObj* pPath = PTR_CAST(SdrPathObj, pRet);
2770 if(pPath)
2772 if(pPath->GetPathPoly().areControlPointsUsed())
2774 if(!bBezier)
2776 // reduce all bezier curves
2777 pPath->SetPathPoly(basegfx::tools::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2780 else
2782 if(bBezier)
2784 // create bezier curves
2785 pPath->SetPathPoly(basegfx::tools::expandToCurve(pPath->GetPathPoly()));
2790 pRet = ImpConvertAddText(pRet, bBezier);
2792 return pRet;
2795 SdrObjGeoData* SdrPathObj::NewGeoData() const
2797 return new SdrPathObjGeoData;
2800 void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
2802 SdrTextObj::SaveGeoData(rGeo);
2803 SdrPathObjGeoData& rPGeo = (SdrPathObjGeoData&) rGeo;
2804 rPGeo.maPathPolygon=GetPathPoly();
2805 rPGeo.meKind=meKind;
2808 void SdrPathObj::RestGeoData(const SdrObjGeoData& rGeo)
2810 SdrTextObj::RestGeoData(rGeo);
2811 SdrPathObjGeoData& rPGeo=(SdrPathObjGeoData&)rGeo;
2812 maPathPolygon=rPGeo.maPathPolygon;
2813 meKind=rPGeo.meKind;
2814 ImpForceKind(); // damit u.a. bClosed gesetzt wird
2817 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2819 if(GetPathPoly() != rPathPoly)
2821 maPathPolygon=rPathPoly;
2822 ImpForceKind();
2823 SetRectsDirty();
2827 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2829 if(GetPathPoly() != rPathPoly)
2831 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
2832 NbcSetPathPoly(rPathPoly);
2833 SetChanged();
2834 BroadcastObjectChange();
2835 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
2839 void SdrPathObj::ToggleClosed() // long nOpenDistance)
2841 Rectangle aBoundRect0;
2842 if(pUserCall != NULL)
2843 aBoundRect0 = GetLastBoundRect();
2844 ImpSetClosed(!IsClosed()); // neuen ObjKind setzen
2845 ImpForceKind(); // wg. Line->Poly->PolyLine statt Line->Poly->Line
2846 SetRectsDirty();
2847 SetChanged();
2848 BroadcastObjectChange();
2849 SendUserCall(SDRUSERCALL_RESIZE, aBoundRect0);
2852 // fuer friend class SdrPolyEditView auf einigen Compilern:
2853 void SdrPathObj::SetRectsDirty(sal_Bool bNotMyself)
2855 SdrTextObj::SetRectsDirty(bNotMyself);
2858 ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
2860 if(!mpDAC)
2862 ((SdrPathObj*)this)->mpDAC = new ImpPathForDragAndCreate(*((SdrPathObj*)this));
2865 return *mpDAC;
2868 void SdrPathObj::impDeleteDAC() const
2870 if(mpDAC)
2872 delete mpDAC;
2873 ((SdrPathObj*)this)->mpDAC = 0L;
2877 ////////////////////////////////////////////////////////////////////////////////////////////////////
2879 // transformation interface for StarOfficeAPI. This implements support for
2880 // homogen 3x3 matrices containing the transformation of the SdrObject. At the
2881 // moment it contains a shearX, rotation and translation, but for setting all linear
2882 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2884 ////////////////////////////////////////////////////////////////////////////////////////////////////
2885 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2886 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2887 sal_Bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2889 double fRotate(0.0);
2890 double fShearX(0.0);
2891 basegfx::B2DTuple aScale(1.0, 1.0);
2892 basegfx::B2DTuple aTranslate(0.0, 0.0);
2894 if(GetPathPoly().count())
2896 // copy geometry
2897 basegfx::B2DHomMatrix aMoveToZeroMatrix;
2898 rPolyPolygon = GetPathPoly();
2900 if(OBJ_LINE == meKind)
2902 // ignore shear and rotate, just use scale and translate
2903 OSL_ENSURE(GetPathPoly().count() > 0L && GetPathPoly().getB2DPolygon(0L).count() > 1L, "OBJ_LINE with too less polygons (!)");
2904 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2905 // itself, else this method will no longer return the full polygon information (curve will
2906 // be lost)
2907 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2908 aScale = aPolyRangeNoCurve.getRange();
2909 aTranslate = aPolyRangeNoCurve.getMinimum();
2911 // define matrix for move polygon to zero point
2912 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2914 else
2916 if(aGeo.nShearWink || aGeo.nDrehWink)
2918 // get rotate and shear in drawingLayer notation
2919 fRotate = aGeo.nDrehWink * F_PI18000;
2920 fShearX = aGeo.nShearWink * F_PI18000;
2922 // build mathematically correct (negative shear and rotate) object transform
2923 // containing shear and rotate to extract unsheared, unrotated polygon
2924 basegfx::B2DHomMatrix aObjectMatrix;
2925 aObjectMatrix.shearX(tan((36000 - aGeo.nShearWink) * F_PI18000));
2926 aObjectMatrix.rotate((36000 - aGeo.nDrehWink) * F_PI18000);
2928 // create inverse from it and back-transform polygon
2929 basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2930 aInvObjectMatrix.invert();
2931 rPolyPolygon.transform(aInvObjectMatrix);
2933 // get range from unsheared, unrotated polygon and extract scale and translate.
2934 // transform topLeft from it back to transformed state to get original
2935 // topLeft (rotation center)
2936 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2937 // itself, else this method will no longer return the full polygon information (curve will
2938 // be lost)
2939 const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2940 aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2941 aScale = aCorrectedRangeNoCurve.getRange();
2943 // define matrix for move polygon to zero point
2944 aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), aCorrectedRangeNoCurve.getMinY());
2946 else
2948 // get scale and translate from unsheared, unrotated polygon
2949 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2950 // itself, else this method will no longer return the full polygon information (curve will
2951 // be lost)
2952 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2953 aScale = aPolyRangeNoCurve.getRange();
2954 aTranslate = aPolyRangeNoCurve.getMinimum();
2956 // define matrix for move polygon to zero point
2957 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2961 // move polygon to zero point with pre-defined matrix
2962 rPolyPolygon.transform(aMoveToZeroMatrix);
2965 // position maybe relative to anchorpos, convert
2966 if( pModel && pModel->IsWriter() )
2968 if(GetAnchorPos().X() || GetAnchorPos().Y())
2970 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2974 // force MapUnit to 100th mm
2975 SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
2976 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
2978 switch(eMapUnit)
2980 case SFX_MAPUNIT_TWIP :
2982 // postion
2983 aTranslate.setX(ImplTwipsToMM(aTranslate.getX()));
2984 aTranslate.setY(ImplTwipsToMM(aTranslate.getY()));
2986 // size
2987 aScale.setX(ImplTwipsToMM(aScale.getX()));
2988 aScale.setY(ImplTwipsToMM(aScale.getY()));
2990 // polygon
2991 basegfx::B2DHomMatrix aTwipsToMM;
2992 const double fFactorTwipsToMM(127.0 / 72.0);
2993 aTwipsToMM.scale(fFactorTwipsToMM, fFactorTwipsToMM);
2994 rPolyPolygon.transform(aTwipsToMM);
2996 break;
2998 default:
3000 DBG_ERROR("TRGetBaseGeometry: Missing unit translation to 100th mm!");
3005 // build return value matrix
3006 rMatrix.identity();
3008 if(!basegfx::fTools::equal(aScale.getX(), 1.0) || !basegfx::fTools::equal(aScale.getY(), 1.0))
3010 rMatrix.scale(aScale.getX(), aScale.getY());
3013 if(!basegfx::fTools::equalZero(fShearX))
3015 rMatrix.shearX(tan(fShearX));
3018 if(!basegfx::fTools::equalZero(fRotate))
3020 // #i78696#
3021 // fRotate is from the old GeoStat and thus mathematically wrong orientated. For
3022 // the linear combination of matrices it needed to be fixed in the API, so it needs to
3023 // be mirrored here
3024 rMatrix.rotate(-fRotate);
3027 if(!aTranslate.equalZero())
3029 rMatrix.translate(aTranslate.getX(), aTranslate.getY());
3032 return sal_True;
3035 // sets the base geometry of the object using infos contained in the homogen 3x3 matrix.
3036 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
3037 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
3038 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
3040 // break up matrix
3041 basegfx::B2DTuple aScale;
3042 basegfx::B2DTuple aTranslate;
3043 double fRotate, fShearX;
3044 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
3046 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
3047 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
3048 if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
3050 aScale.setX(fabs(aScale.getX()));
3051 aScale.setY(fabs(aScale.getY()));
3052 fRotate = fmod(fRotate + F_PI, F_2PI);
3055 // copy poly
3056 basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
3058 // reset object shear and rotations
3059 aGeo.nDrehWink = 0;
3060 aGeo.RecalcSinCos();
3061 aGeo.nShearWink = 0;
3062 aGeo.RecalcTan();
3064 // force metric to pool metric
3065 SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
3066 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
3068 switch(eMapUnit)
3070 case SFX_MAPUNIT_TWIP :
3072 // position
3073 aTranslate.setX(ImplMMToTwips(aTranslate.getX()));
3074 aTranslate.setY(ImplMMToTwips(aTranslate.getY()));
3076 // size
3077 aScale.setX(ImplMMToTwips(aScale.getX()));
3078 aScale.setY(ImplMMToTwips(aScale.getY()));
3080 // polygon
3081 basegfx::B2DHomMatrix aMMToTwips;
3082 const double fFactorMMToTwips(72.0 / 127.0);
3083 aMMToTwips.scale(fFactorMMToTwips, fFactorMMToTwips);
3084 aNewPolyPolygon.transform(aMMToTwips);
3086 break;
3088 default:
3090 DBG_ERROR("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
3095 if( pModel && pModel->IsWriter() )
3097 // if anchor is used, make position relative to it
3098 if(GetAnchorPos().X() || GetAnchorPos().Y())
3100 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3104 // create transformation for polygon, set values at aGeo direct
3105 basegfx::B2DHomMatrix aTransform;
3107 // #i75086#
3108 // Given polygon is already scaled (for historical reasons), but not mirrored yet.
3109 // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
3110 if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0))
3112 aTransform.scale(
3113 basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0,
3114 basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
3117 if(!basegfx::fTools::equalZero(fShearX))
3119 aTransform.shearX(tan(-atan(fShearX)));
3120 aGeo.nShearWink = FRound(atan(fShearX) / F_PI18000);
3121 aGeo.RecalcTan();
3124 if(!basegfx::fTools::equalZero(fRotate))
3126 // #i78696#
3127 // fRotate is matematically correct for linear transformations, so it's
3128 // the one to use for the geometry change
3129 aTransform.rotate(fRotate);
3131 // #i78696#
3132 // fRotate is matematically correct, but aGeoStat.nDrehWink is
3133 // mirrored -> mirror value here
3134 aGeo.nDrehWink = NormAngle360(FRound(-fRotate / F_PI18000));
3135 aGeo.RecalcSinCos();
3138 if(!aTranslate.equalZero())
3140 // #i39529# absolute positioning, so get current position (without control points (!))
3141 const basegfx::B2DRange aCurrentRange(basegfx::tools::getRange(aNewPolyPolygon));
3142 aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
3145 // transform polygon and trigger change
3146 aNewPolyPolygon.transform(aTransform);
3147 SetPathPoly(aNewPolyPolygon);
3150 //////////////////////////////////////////////////////////////////////////////
3151 // eof