Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / svx / source / svdraw / svdopath.cxx
blob25b7fa5a4d728f9615e38225091821516c3d6fc9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <tools/bigint.hxx>
31 #include <svx/svdopath.hxx>
32 #include <math.h>
33 #include <svx/xpool.hxx>
34 #include <svx/xpoly.hxx>
35 #include <svx/svdattr.hxx>
36 #include <svx/svdtrans.hxx>
37 #include <svx/svdetc.hxx>
38 #include <svx/svddrag.hxx>
39 #include <svx/svdmodel.hxx>
40 #include <svx/svdpage.hxx>
41 #include <svx/svdhdl.hxx>
42 #include <svx/svdview.hxx> // for MovCreate when using curves
43 #include "svx/svdglob.hxx" // Stringcache
44 #include "svx/svdstr.hrc" // the object's name
46 #ifdef _MSC_VER
47 #pragma optimize ("",off)
48 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
49 #endif
51 #include <svx/xlnwtit.hxx>
52 #include <svx/xlnclit.hxx>
53 #include <svx/xflclit.hxx>
54 #include <svx/svdogrp.hxx>
55 #include <svx/polypolygoneditor.hxx>
56 #include <svx/xlntrit.hxx>
57 #include <vcl/salbtype.hxx> // FRound
58 #include <svx/sdr/contact/viewcontactofsdrpathobj.hxx>
59 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include "svdconv.hxx"
61 #include <basegfx/point/b2dpoint.hxx>
62 #include <basegfx/polygon/b2dpolypolygontools.hxx>
63 #include <basegfx/range/b2drange.hxx>
64 #include <basegfx/curve/b2dcubicbezier.hxx>
65 #include <basegfx/polygon/b2dpolygontools.hxx>
66 #include <svx/sdr/attribute/sdrtextattribute.hxx>
67 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
68 #include <basegfx/matrix/b2dhommatrixtools.hxx>
69 #include <svx/sdr/attribute/sdrformtextattribute.hxx>
71 using namespace sdr;
73 inline sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
75 if (nPnt>0) {
76 nPnt--;
77 } else {
78 nPnt=nPntMax;
79 if (bClosed) nPnt--;
81 return nPnt;
84 inline sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
86 nPnt++;
87 if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
88 return nPnt;
91 struct ImpSdrPathDragData : public SdrDragStatUserData
93 XPolygon aXP; // section of the original polygon
94 bool bValid; // FALSE = too few points
95 bool bClosed; // closed object?
96 sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
97 sal_uInt16 nPnt; // number of point in the above polygon
98 sal_uInt16 nPntAnz; // number of points of the polygon
99 sal_uInt16 nPntMax; // maximum index
100 bool bBegPnt; // dragged point is first point of a Polyline
101 bool bEndPnt; // dragged point is finishing point of a Polyline
102 sal_uInt16 nPrevPnt; // index of previous point
103 sal_uInt16 nNextPnt; // index of next point
104 bool bPrevIsBegPnt; // previous point is first point of a Polyline
105 bool bNextIsEndPnt; // next point is first point of a Polyline
106 sal_uInt16 nPrevPrevPnt; // index of point before previous point
107 sal_uInt16 nNextNextPnt; // index of point after next point
108 bool bControl; // point is a control point
109 bool bIsPrevControl; // point is a control point before a support point
110 bool bIsNextControl; // point is a control point after a support point
111 bool bPrevIsControl; // if nPnt is a support point: a control point comes before
112 bool bNextIsControl; // if nPnt is a support point: a control point comes after
113 sal_uInt16 nPrevPrevPnt0;
114 sal_uInt16 nPrevPnt0;
115 sal_uInt16 nPnt0;
116 sal_uInt16 nNextPnt0;
117 sal_uInt16 nNextNextPnt0;
118 bool bEliminate; // delete point? (is set by MovDrag)
120 sal_Bool mbMultiPointDrag;
121 const XPolyPolygon maOrig;
122 XPolyPolygon maMove;
123 Container maHandles;
125 public:
126 ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, sal_Bool bMuPoDr, const SdrDragStat& rDrag);
127 void ResetPoly(const SdrPathObj& rPO);
128 sal_Bool IsMultiPointDrag() const { return mbMultiPointDrag; }
131 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, sal_Bool bMuPoDr, const SdrDragStat& rDrag)
132 : aXP(5),
133 mbMultiPointDrag(bMuPoDr),
134 maOrig(rPO.GetPathPoly()),
135 maHandles(0)
137 if(mbMultiPointDrag)
139 const SdrMarkView& rMarkView = *rDrag.GetView();
140 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
141 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
142 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
144 for(sal_uInt32 a(0); a < nHdlCount; a++)
146 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
148 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
150 maHandles.Insert(pTestHdl, CONTAINER_APPEND);
154 maMove = maOrig;
155 bValid = sal_True;
157 else
159 bValid=sal_False;
160 bClosed=rPO.IsClosed(); // closed object?
161 nPoly=(sal_uInt16)rHdl.GetPolyNum(); // number of the polygon in the PolyPolygon
162 nPnt=(sal_uInt16)rHdl.GetPointNum(); // number of points in the above polygon
163 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
164 nPntAnz=aTmpXP.GetPointCount(); // number of point of the polygon
165 if (nPntAnz==0 || (bClosed && nPntAnz==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
166 nPntMax=nPntAnz-1; // maximum index
167 bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
168 bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
169 if (bClosed && nPntAnz<=3) { // if polygon is only a line
170 bBegPnt=(nPntAnz<3) || nPnt==0;
171 bEndPnt=(nPntAnz<3) || nPnt==nPntMax-1;
173 nPrevPnt=nPnt; // index of previous point
174 nNextPnt=nPnt; // index of next point
175 if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
176 if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
177 bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
178 bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
179 nPrevPrevPnt=nPnt; // index of point before previous point
180 nNextNextPnt=nPnt; // index of point after next point
181 if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
182 if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
183 bControl=rHdl.IsPlusHdl(); // point is a control point
184 bIsPrevControl=sal_False; // point is a control point before a support point
185 bIsNextControl=sal_False; // point is a control point after a support point
186 bPrevIsControl=sal_False; // if nPnt is a support point: a control point comes before
187 bNextIsControl=sal_False; // if nPnt is a support point: a control point comes after
188 if (bControl) {
189 bIsPrevControl=aTmpXP.IsControl(nPrevPnt);
190 bIsNextControl=!bIsPrevControl;
191 } else {
192 bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==XPOLY_CONTROL;
193 bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==XPOLY_CONTROL;
195 nPrevPrevPnt0=nPrevPrevPnt;
196 nPrevPnt0 =nPrevPnt;
197 nPnt0 =nPnt;
198 nNextPnt0 =nNextPnt;
199 nNextNextPnt0=nNextNextPnt;
200 nPrevPrevPnt=0;
201 nPrevPnt=1;
202 nPnt=2;
203 nNextPnt=3;
204 nNextNextPnt=4;
205 bEliminate=sal_False;
206 ResetPoly(rPO);
207 bValid=sal_True;
211 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
213 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
214 aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
215 aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
216 aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
217 aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
218 aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
221 /*************************************************************************/
223 struct ImpPathCreateUser : public SdrDragStatUserData
225 Point aBezControl0;
226 Point aBezStart;
227 Point aBezCtrl1;
228 Point aBezCtrl2;
229 Point aBezEnd;
230 Point aCircStart;
231 Point aCircEnd;
232 Point aCircCenter;
233 Point aLineStart;
234 Point aLineEnd;
235 Point aRectP1;
236 Point aRectP2;
237 Point aRectP3;
238 long nCircRadius;
239 long nCircStWink;
240 long nCircRelWink;
241 bool bBezier;
242 bool bBezHasCtrl0;
243 bool bCurve;
244 bool bCircle;
245 bool bAngleSnap;
246 bool bLine;
247 bool bLine90;
248 bool bRect;
249 bool bMixedCreate;
250 sal_uInt16 nBezierStartPoint;
251 SdrObjKind eStartKind;
252 SdrObjKind eAktKind;
254 public:
255 ImpPathCreateUser(): nCircRadius(0),nCircStWink(0),nCircRelWink(0),
256 bBezier(sal_False),bBezHasCtrl0(sal_False),bCurve(sal_False),bCircle(sal_False),bAngleSnap(sal_False),bLine(sal_False),bLine90(sal_False),bRect(sal_False),
257 bMixedCreate(sal_False),nBezierStartPoint(0),eStartKind(OBJ_NONE),eAktKind(OBJ_NONE) { }
259 void ResetFormFlags() { bBezier=sal_False; bCurve=sal_False; bCircle=sal_False; bLine=sal_False; bRect=sal_False; }
260 bool IsFormFlag() const { return bBezier || bCurve || bCircle || bLine || bRect; }
261 XPolygon GetFormPoly() const;
262 bool CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
263 XPolygon GetBezierPoly() const;
264 XPolygon GetCurvePoly() const { return XPolygon(); }
265 bool CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
266 XPolygon GetCirclePoly() const;
267 bool CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
268 Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView* pView) const;
269 XPolygon GetLinePoly() const;
270 bool CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
271 XPolygon GetRectPoly() const;
274 XPolygon ImpPathCreateUser::GetFormPoly() const
276 if (bBezier) return GetBezierPoly();
277 if (bCurve) return GetCurvePoly();
278 if (bCircle) return GetCirclePoly();
279 if (bLine) return GetLinePoly();
280 if (bRect) return GetRectPoly();
281 return XPolygon();
284 bool ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
286 bool bRet = true;
287 aBezStart=rP1;
288 aBezCtrl1=rP1+rDir;
289 aBezCtrl2=rP2;
291 // #i21479#
292 // Also copy the end point when no end point is set yet
293 if (!bMouseDown || (0L == aBezEnd.X() && 0L == aBezEnd.Y())) aBezEnd=rP2;
295 bBezier=bRet;
296 return bRet;
299 XPolygon ImpPathCreateUser::GetBezierPoly() const
301 XPolygon aXP(4);
302 aXP[0]=aBezStart; aXP.SetFlags(0,XPOLY_SMOOTH);
303 aXP[1]=aBezCtrl1; aXP.SetFlags(1,XPOLY_CONTROL);
304 aXP[2]=aBezCtrl2; aXP.SetFlags(2,XPOLY_CONTROL);
305 aXP[3]=aBezEnd;
306 return aXP;
309 bool ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
311 long nTangAngle=GetAngle(rDir);
312 aCircStart=rP1;
313 aCircEnd=rP2;
314 aCircCenter=rP1;
315 long dx=rP2.X()-rP1.X();
316 long dy=rP2.Y()-rP1.Y();
317 long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
318 dAngle=NormAngle360(dAngle);
319 long nTmpAngle=NormAngle360(9000-dAngle);
320 bool bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
321 long nRad=0;
322 if (bRet) {
323 double cs=cos(nTmpAngle*nPi180);
324 double nR=(double)GetLen(Point(dx,dy))/cs/2;
325 nRad=Abs(Round(nR));
327 if (dAngle<18000) {
328 nCircStWink=NormAngle360(nTangAngle-9000);
329 nCircRelWink=NormAngle360(2*dAngle);
330 aCircCenter.X()+=Round(nRad*cos((nTangAngle+9000)*nPi180));
331 aCircCenter.Y()-=Round(nRad*sin((nTangAngle+9000)*nPi180));
332 } else {
333 nCircStWink=NormAngle360(nTangAngle+9000);
334 nCircRelWink=-NormAngle360(36000-2*dAngle);
335 aCircCenter.X()+=Round(nRad*cos((nTangAngle-9000)*nPi180));
336 aCircCenter.Y()-=Round(nRad*sin((nTangAngle-9000)*nPi180));
338 bAngleSnap=pView!=NULL && pView->IsAngleSnapEnabled();
339 if (bAngleSnap) {
340 long nSA=pView->GetSnapAngle();
341 if (nSA!=0) { // angle snapping
342 bool bNeg=nCircRelWink<0;
343 if (bNeg) nCircRelWink=-nCircRelWink;
344 nCircRelWink+=nSA/2;
345 nCircRelWink/=nSA;
346 nCircRelWink*=nSA;
347 nCircRelWink=NormAngle360(nCircRelWink);
348 if (bNeg) nCircRelWink=-nCircRelWink;
351 nCircRadius=nRad;
352 if (nRad==0 || Abs(nCircRelWink)<5) bRet=sal_False;
353 bCircle=bRet;
354 return bRet;
357 XPolygon ImpPathCreateUser::GetCirclePoly() const
359 if (nCircRelWink>=0) {
360 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
361 sal_uInt16((nCircStWink+5)/10),sal_uInt16((nCircStWink+nCircRelWink+5)/10),sal_False);
362 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
363 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
364 return aXP;
365 } else {
366 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
367 sal_uInt16(NormAngle360(nCircStWink+nCircRelWink+5)/10),sal_uInt16((nCircStWink+5)/10),sal_False);
368 sal_uInt16 nAnz=aXP.GetPointCount();
369 for (sal_uInt16 nNum=nAnz/2; nNum>0;) {
370 nNum--; // reverse XPoly's order of points
371 sal_uInt16 n2=nAnz-nNum-1;
372 Point aPt(aXP[nNum]);
373 aXP[nNum]=aXP[n2];
374 aXP[n2]=aPt;
376 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
377 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
378 return aXP;
382 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView* pView) const
384 long x=aCsr.X(),x1=x,x2=x;
385 long y=aCsr.Y(),y1=y,y2=y;
386 bool bHLin=nDirY==0;
387 bool bVLin=nDirX==0;
388 if (bHLin) y=0;
389 else if (bVLin) x=0;
390 else {
391 x1=BigMulDiv(y,nDirX,nDirY);
392 y2=BigMulDiv(x,nDirY,nDirX);
393 long l1=Abs(x1)+Abs(y1);
394 long l2=Abs(x2)+Abs(y2);
395 if ((l1<=l2) != (pView!=NULL && pView->IsBigOrtho())) {
396 x=x1; y=y1;
397 } else {
398 x=x2; y=y2;
401 return Point(x,y);
404 bool ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
406 aLineStart=rP1;
407 aLineEnd=rP2;
408 bLine90=sal_False;
409 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=sal_False; return sal_False; }
410 Point aTmpPt(rP2-rP1);
411 long nDirX=rDir.X();
412 long nDirY=rDir.Y();
413 Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=Abs(aP1.X())+Abs(aP1.Y());
414 Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=Abs(aP2.X())+Abs(aP2.Y());
415 if (pView!=NULL && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
416 bLine90=nQ1>2*nQ2;
417 if (!bLine90) { // smooth transition
418 aLineEnd+=aP1;
419 } else { // rectangular transition
420 aLineEnd+=aP2;
422 bLine=sal_True;
423 return sal_True;
426 XPolygon ImpPathCreateUser::GetLinePoly() const
428 XPolygon aXP(2);
429 aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,XPOLY_SMOOTH);
430 aXP[1]=aLineEnd;
431 return aXP;
434 bool ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
436 aRectP1=rP1;
437 aRectP2=rP1;
438 aRectP3=rP2;
439 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=sal_False; return sal_False; }
440 Point aTmpPt(rP2-rP1);
441 long nDirX=rDir.X();
442 long nDirY=rDir.Y();
443 long x=aTmpPt.X();
444 long y=aTmpPt.Y();
445 bool bHLin=nDirY==0;
446 bool bVLin=nDirX==0;
447 if (bHLin) y=0;
448 else if (bVLin) x=0;
449 else {
450 y=BigMulDiv(x,nDirY,nDirX);
451 long nHypLen=aTmpPt.Y()-y;
452 long nTangAngle=-GetAngle(rDir);
453 // sin=g/h, g=h*sin
454 double a=nTangAngle*nPi180;
455 double sn=sin(a);
456 double cs=cos(a);
457 double nGKathLen=nHypLen*sn;
458 y+=Round(nGKathLen*sn);
459 x+=Round(nGKathLen*cs);
461 aRectP2.X()+=x;
462 aRectP2.Y()+=y;
463 if (pView!=NULL && pView->IsOrtho()) {
464 long dx1=aRectP2.X()-aRectP1.X(); long dx1a=Abs(dx1);
465 long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=Abs(dy1);
466 long dx2=aRectP3.X()-aRectP2.X(); long dx2a=Abs(dx2);
467 long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=Abs(dy2);
468 bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
469 if (b1MoreThan2 != pView->IsBigOrtho()) {
470 long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
471 long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
472 aRectP2.X()+=xtemp;
473 aRectP2.Y()+=ytemp;
474 aRectP3.X()+=xtemp;
475 aRectP3.Y()+=ytemp;
476 } else {
477 long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
478 long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
479 aRectP3.X()+=xtemp;
480 aRectP3.Y()+=ytemp;
483 bRect=sal_True;
484 return sal_True;
487 XPolygon ImpPathCreateUser::GetRectPoly() const
489 XPolygon aXP(3);
490 aXP[0]=aRectP1; aXP.SetFlags(0,XPOLY_SMOOTH);
491 aXP[1]=aRectP2;
492 if (aRectP3!=aRectP2) aXP[2]=aRectP3;
493 return aXP;
496 /*************************************************************************/
498 class ImpPathForDragAndCreate
500 SdrPathObj& mrSdrPathObject;
501 XPolyPolygon aPathPolygon;
502 SdrObjKind meObjectKind;
503 ImpSdrPathDragData* mpSdrPathDragData;
504 bool mbCreating;
506 public:
507 ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
508 ~ImpPathForDragAndCreate();
510 // drag stuff
511 bool beginPathDrag( SdrDragStat& rDrag ) const;
512 bool movePathDrag( SdrDragStat& rDrag ) const;
513 bool endPathDrag( SdrDragStat& rDrag );
514 String getSpecialDragComment(const SdrDragStat& rDrag) const;
515 basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
517 // create stuff
518 bool BegCreate(SdrDragStat& rStat);
519 bool MovCreate(SdrDragStat& rStat);
520 bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
521 bool BckCreate(SdrDragStat& rStat);
522 void BrkCreate(SdrDragStat& rStat);
523 Pointer GetCreatePointer() const;
525 // helping stuff
526 bool IsClosed(SdrObjKind eKind) const { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
527 bool IsFreeHand(SdrObjKind eKind) const { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
528 bool IsBezier(SdrObjKind eKind) const { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
529 bool IsCreating() const { return mbCreating; }
531 // get the polygon
532 basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
533 basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag) const;
534 basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
537 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
538 : mrSdrPathObject(rSdrPathObject),
539 aPathPolygon(rSdrPathObject.GetPathPoly()),
540 meObjectKind(mrSdrPathObject.meKind),
541 mpSdrPathDragData(0),
542 mbCreating(false)
546 ImpPathForDragAndCreate::~ImpPathForDragAndCreate()
548 if(mpSdrPathDragData)
550 delete mpSdrPathDragData;
554 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat& rDrag ) const
556 const SdrHdl* pHdl=rDrag.GetHdl();
557 if(!pHdl)
558 return sal_False;
560 sal_Bool bMultiPointDrag(sal_True);
562 if(aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()].IsControl((sal_uInt16)pHdl->GetPointNum()))
563 bMultiPointDrag = sal_False;
565 if(bMultiPointDrag)
567 const SdrMarkView& rMarkView = *rDrag.GetView();
568 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
569 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
570 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
571 sal_uInt32 nSelectedPoints(0);
573 for(sal_uInt32 a(0); a < nHdlCount; a++)
575 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
577 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
579 nSelectedPoints++;
583 if(nSelectedPoints <= 1)
584 bMultiPointDrag = sal_False;
587 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag);
589 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
591 OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
592 delete mpSdrPathDragData;
593 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = 0;
594 return false;
597 return true;
600 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
602 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
604 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
605 return false;
608 if(mpSdrPathDragData->IsMultiPointDrag())
610 Point aDelta(rDrag.GetNow() - rDrag.GetStart());
612 if(aDelta.X() || aDelta.Y())
614 for(sal_uInt32 a(0); a < mpSdrPathDragData->maHandles.Count(); a++)
616 SdrHdl* pHandle = (SdrHdl*)mpSdrPathDragData->maHandles.GetObject(a);
617 const sal_uInt16 nPolyIndex((sal_uInt16)pHandle->GetPolyNum());
618 const sal_uInt16 nPointIndex((sal_uInt16)pHandle->GetPointNum());
619 const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
620 XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
621 const sal_uInt16 nPointCount(rOrig.GetPointCount());
622 sal_Bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
624 // move point itself
625 rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
627 // when point is first and poly closed, move close point, too.
628 if(nPointCount > 0 && !nPointIndex && bClosed)
630 rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
632 // when moving the last point it may be necessary to move the
633 // control point in front of this one, too.
634 if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
635 rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
638 // is a control point before this?
639 if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
641 // Yes, move it, too
642 rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
645 // is a control point after this?
646 if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
648 // Yes, move it, too
649 rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
654 else
656 mpSdrPathDragData->ResetPoly(mrSdrPathObject);
658 // copy certain data locally to use less code and have faster access times
659 bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
660 sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon
661 bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline
662 bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
663 sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
664 sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point
665 bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
666 bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
667 sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
668 sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point
669 bool bControl =mpSdrPathDragData->bControl ; // point is a control point
670 bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
671 bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
672 bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
674 // Ortho for lines/polygons: keep angle
675 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsOrtho()) {
676 bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
677 Point aPos(rDrag.GetNow()); // current position
678 Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
679 sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
680 Point aNeuPos1,aNeuPos2; // new alternative for aPos
681 bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
682 if (!bClosed && mpSdrPathDragData->nPntAnz>=2) { // minimum of 2 points for lines
683 if (!bBegPnt) nPnt1=nPrevPnt;
684 if (!bEndPnt) nPnt2=nNextPnt;
686 if (bClosed && mpSdrPathDragData->nPntAnz>=3) { // minimum of 3 points for polygon
687 nPnt1=nPrevPnt;
688 nPnt2=nNextPnt;
690 if (nPnt1!=0xFFFF && !bPrevIsControl) {
691 Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
692 long ndx0=aPnt.X()-aPnt1.X();
693 long ndy0=aPnt.Y()-aPnt1.Y();
694 bool bHLin=ndy0==0;
695 bool bVLin=ndx0==0;
696 if (!bHLin || !bVLin) {
697 long ndx=aPos.X()-aPnt1.X();
698 long ndy=aPos.Y()-aPnt1.Y();
699 bPnt1=sal_True;
700 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
701 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
702 bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
703 bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
704 if (bHor) ndy=long(ndy0*nXFact);
705 if (bVer) ndx=long(ndx0*nYFact);
706 aNeuPos1=aPnt1;
707 aNeuPos1.X()+=ndx;
708 aNeuPos1.Y()+=ndy;
711 if (nPnt2!=0xFFFF && !bNextIsControl) {
712 Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
713 long ndx0=aPnt.X()-aPnt2.X();
714 long ndy0=aPnt.Y()-aPnt2.Y();
715 bool bHLin=ndy0==0;
716 bool bVLin=ndx0==0;
717 if (!bHLin || !bVLin) {
718 long ndx=aPos.X()-aPnt2.X();
719 long ndy=aPos.Y()-aPnt2.Y();
720 bPnt2=sal_True;
721 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
722 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
723 bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
724 bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
725 if (bHor) ndy=long(ndy0*nXFact);
726 if (bVer) ndx=long(ndx0*nYFact);
727 aNeuPos2=aPnt2;
728 aNeuPos2.X()+=ndx;
729 aNeuPos2.Y()+=ndy;
732 if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
733 BigInt nX1(aNeuPos1.X()-aPos.X()); nX1*=nX1;
734 BigInt nY1(aNeuPos1.Y()-aPos.Y()); nY1*=nY1;
735 BigInt nX2(aNeuPos2.X()-aPos.X()); nX2*=nX2;
736 BigInt nY2(aNeuPos2.Y()-aPos.Y()); nY2*=nY2;
737 nX1+=nY1; // correction distance to square
738 nX2+=nY2; // correction distance to square
739 // let the alternative that allows fewer correction win
740 if (nX1<nX2) bPnt2=sal_False; else bPnt1=sal_False;
742 if (bPnt1) rDrag.Now()=aNeuPos1;
743 if (bPnt2) rDrag.Now()=aNeuPos2;
745 rDrag.SetActionRect(Rectangle(rDrag.GetNow(),rDrag.GetNow()));
747 // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
748 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsEliminatePolyPoints() &&
749 !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
751 Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
752 aPt-=rDrag.GetNow();
753 long nWink1=GetAngle(aPt);
754 aPt=rDrag.GetNow();
755 aPt-=mpSdrPathDragData->aXP[nPrevPnt];
756 long nWink2=GetAngle(aPt);
757 long nDiff=nWink1-nWink2;
758 nDiff=Abs(nDiff);
759 mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
760 if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
761 aPt=mpSdrPathDragData->aXP[nNextPnt];
762 aPt+=mpSdrPathDragData->aXP[nPrevPnt];
763 aPt/=2;
764 rDrag.Now()=aPt;
768 // we dragged by this distance
769 Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
771 /* There are 8 possible cases:
772 X 1. A control point neither on the left nor on the right.
773 o--X--o 2. There are control points on the left and the right, we are dragging a support point.
774 o--X 3. There is a control point on the left, we are dragging a support point.
775 X--o 4. There is a control point on the right, we are dragging a support point.
776 x--O--o 5. There are control points on the left and the right, we are dragging the left one.
777 x--O 6. There is a control point on the left, we are dragging it.
778 o--O--x 7. There are control points on the left and the right, we are dragging the right one.
779 O--x 8. There is a control point on the right, we are dragging it.
780 Note: modifying a line (not a curve!) might create a curve on the other end of the line
781 if Smooth is set there (with control points aligned to line).
784 mpSdrPathDragData->aXP[nPnt]+=aDiff;
786 // now check symmetric plus handles
787 if (bControl) { // cases 5,6,7,8
788 sal_uInt16 nSt=nPnt; // the associated support point
789 sal_uInt16 nFix=nPnt; // the opposing control point
790 if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
791 nSt=nPrevPnt;
792 nFix=nPrevPrevPnt;
793 } else {
794 nSt=nNextPnt;
795 nFix=nNextNextPnt;
797 if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
798 mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
802 if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
803 // move both control points
804 if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
805 if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
806 // align control point to line, if appropriate
807 if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
808 if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
809 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
811 if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
812 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
815 // Now check the other ends of the line (nPnt+-1). If there is a
816 // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
817 // associated control point (nPnt+-2) has to be adapted.
818 if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
819 if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
820 mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
823 if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
824 if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
825 mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
831 return true;
834 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat& rDrag)
836 Point aLinePt1;
837 Point aLinePt2;
838 bool bLineGlueMirror(OBJ_LINE == meObjectKind);
839 if (bLineGlueMirror) {
840 XPolygon& rXP=aPathPolygon[0];
841 aLinePt1=rXP[0];
842 aLinePt2=rXP[1];
845 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
847 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
848 return false;
851 if(mpSdrPathDragData->IsMultiPointDrag())
853 aPathPolygon = mpSdrPathDragData->maMove;
855 else
857 const SdrHdl* pHdl=rDrag.GetHdl();
859 // reference the polygon
860 XPolygon& rXP=aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()];
862 // the 5 points that might have changed
863 if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
864 if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
865 if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
866 if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
867 rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
869 // for closed objects: last point has to be equal to first point
870 if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
872 if (mpSdrPathDragData->bEliminate)
874 basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
875 sal_uInt32 nPoly,nPnt;
877 if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
879 basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
880 aCandidate.remove(nPnt);
882 if((IsClosed(meObjectKind) && aCandidate.count() < 3L) || aCandidate.count() < 2L)
884 aTempPolyPolygon.remove(nPoly);
886 else
888 aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
892 aPathPolygon = XPolyPolygon(aTempPolyPolygon);
895 // adapt angle for text beneath a simple line
896 if (bLineGlueMirror)
898 Point aLinePt1_(aPathPolygon[0][0]);
899 Point aLinePt2_(aPathPolygon[0][1]);
900 bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
901 bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
902 if (bXMirr || bYMirr) {
903 Point aRef1(mrSdrPathObject.GetSnapRect().Center());
904 if (bXMirr) {
905 Point aRef2(aRef1);
906 aRef2.Y()++;
907 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
909 if (bYMirr) {
910 Point aRef2(aRef1);
911 aRef2.X()++;
912 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
918 delete mpSdrPathDragData;
919 mpSdrPathDragData = 0;
921 return true;
925 String ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
927 XubString aStr;
928 const SdrHdl* pHdl = rDrag.GetHdl();
929 const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
931 if(bCreateComment && rDrag.GetUser())
933 // #i103058# re-add old creation comment mode
934 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
935 const SdrObjKind eKindMerk(meObjectKind);
936 mrSdrPathObject.meKind = pU->eAktKind;
937 rtl::OUString aTmp;
938 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewCreateObj, aTmp);
939 aStr = aTmp;
940 mrSdrPathObject.meKind = eKindMerk;
942 Point aPrev(rDrag.GetPrev());
943 Point aNow(rDrag.GetNow());
945 if(pU->bLine)
946 aNow = pU->aLineEnd;
948 aNow -= aPrev;
949 aStr.AppendAscii(" (");
951 rtl::OUString aMetr;
953 if(pU->bCircle)
955 mrSdrPathObject.GetModel()->TakeWinkStr(Abs(pU->nCircRelWink), aMetr);
956 aStr.Append(aMetr);
957 aStr.AppendAscii(" r=");
958 mrSdrPathObject.GetModel()->TakeMetricStr(pU->nCircRadius, aMetr, sal_True);
959 aStr.Append(aMetr);
962 aStr.AppendAscii("dx=");
963 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X(), aMetr, sal_True);
964 aStr.Append(aMetr);
966 aStr.AppendAscii(" dy=");
967 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y(), aMetr, sal_True);
968 aStr.Append(aMetr);
970 if(!IsFreeHand(meObjectKind))
972 sal_Int32 nLen(GetLen(aNow));
973 aStr.AppendAscii(" l=");
974 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
975 aStr.Append(aMetr);
977 sal_Int32 nWink(GetAngle(aNow));
978 aStr += sal_Unicode(' ');
979 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
980 aStr.Append(aMetr);
983 aStr += sal_Unicode(')');
985 else if(!mrSdrPathObject.GetModel() || !pHdl)
987 // #i103058# fallback when no model and/or Handle, both needed
988 // for else-path
989 rtl::OUString aTmp;
990 mrSdrPathObject.ImpTakeDescriptionStr(STR_DragPathObj, aTmp);
991 aStr = aTmp;
993 else
995 // #i103058# standard for modification; model and handle needed
996 ImpSdrPathDragData* pDragData = mpSdrPathDragData;
998 if(!pDragData)
1000 // getSpecialDragComment is also used from create, so fallback to GetUser()
1001 // when mpSdrPathDragData is not set
1002 pDragData = (ImpSdrPathDragData*)rDrag.GetUser();
1005 if(!pDragData)
1007 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1008 return String();
1011 if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
1013 // point of ...
1014 rtl::OUString aTmp;
1015 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewMarkedPoint, aTmp);
1016 aStr = aTmp;
1018 // delete %O
1019 XubString aStr2(ImpGetResStr(STR_EditDelete));
1021 // UNICODE: delete point of ...
1022 aStr2.SearchAndReplaceAscii("%1", aStr);
1024 return aStr2;
1027 // dx=0.00 dy=0.00 -- both sides bezier
1028 // dx=0.00 dy=0.00 l=0.00 0.00° -- one bezier/lever on one side, a start, or an ending
1029 // dx=0.00 dy=0.00 l=0.00 0.00° / l=0.00 0.00° -- in between
1030 rtl::OUString aMetr;
1031 Point aBeg(rDrag.GetStart());
1032 Point aNow(rDrag.GetNow());
1034 aStr = String();
1035 aStr.AppendAscii("dx=");
1036 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X() - aBeg.X(), aMetr, sal_True);
1037 aStr.Append(aMetr);
1039 aStr.AppendAscii(" dy=");
1040 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y() - aBeg.Y(), aMetr, sal_True);
1041 aStr.Append(aMetr);
1043 if(!pDragData->IsMultiPointDrag())
1045 sal_uInt16 nPntNum((sal_uInt16)pHdl->GetPointNum());
1046 const XPolygon& rXPoly = aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1047 sal_uInt16 nPntAnz((sal_uInt16)rXPoly.GetPointCount());
1048 sal_Bool bClose(IsClosed(meObjectKind));
1050 if(bClose)
1051 nPntAnz--;
1053 if(pHdl->IsPlusHdl())
1055 // lever
1056 sal_uInt16 nRef(nPntNum);
1058 if(rXPoly.IsControl(nPntNum + 1))
1059 nRef--;
1060 else
1061 nRef++;
1063 aNow -= rXPoly[nRef];
1065 sal_Int32 nLen(GetLen(aNow));
1066 aStr.AppendAscii(" l=");
1067 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1068 aStr.Append(aMetr);
1070 sal_Int32 nWink(GetAngle(aNow));
1071 aStr += sal_Unicode(' ');
1072 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1073 aStr.Append(aMetr);
1075 else if(nPntAnz > 1)
1077 sal_uInt16 nPntMax(nPntAnz - 1);
1078 Point aPt1,aPt2;
1079 sal_Bool bIsClosed(IsClosed(meObjectKind));
1080 sal_Bool bPt1(nPntNum > 0);
1081 sal_Bool bPt2(nPntNum < nPntMax);
1083 if(bIsClosed && nPntAnz > 2)
1085 bPt1 = sal_True;
1086 bPt2 = sal_True;
1089 sal_uInt16 nPt1,nPt2;
1091 if(nPntNum > 0)
1092 nPt1 = nPntNum - 1;
1093 else
1094 nPt1 = nPntMax;
1096 if(nPntNum < nPntMax)
1097 nPt2 = nPntNum + 1;
1098 else
1099 nPt2 = 0;
1101 if(bPt1 && rXPoly.IsControl(nPt1))
1102 bPt1 = sal_False; // don't display
1104 if(bPt2 && rXPoly.IsControl(nPt2))
1105 bPt2 = sal_False; // of bezier data
1107 if(bPt1)
1109 Point aPt(aNow);
1110 aPt -= rXPoly[nPt1];
1112 sal_Int32 nLen(GetLen(aPt));
1113 aStr.AppendAscii(" l=");
1114 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1115 aStr += aMetr;
1117 sal_Int32 nWink(GetAngle(aPt));
1118 aStr += sal_Unicode(' ');
1119 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1120 aStr += aMetr;
1123 if(bPt2)
1125 if(bPt1)
1126 aStr.AppendAscii(" / ");
1127 else
1128 aStr.AppendAscii(" ");
1130 Point aPt(aNow);
1131 aPt -= rXPoly[nPt2];
1133 sal_Int32 nLen(GetLen(aPt));
1134 aStr.AppendAscii("l=");
1135 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1136 aStr += aMetr;
1138 sal_Int32 nWink(GetAngle(aPt));
1139 aStr += sal_Unicode(' ');
1140 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1141 aStr += aMetr;
1147 return aStr;
1150 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
1152 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1154 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1155 return basegfx::B2DPolyPolygon();
1158 XPolyPolygon aRetval;
1160 if(mpSdrPathDragData->IsMultiPointDrag())
1162 aRetval.Insert(mpSdrPathDragData->maMove);
1164 else
1166 const XPolygon& rXP=aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1167 if (rXP.GetPointCount()<=2) {
1168 XPolygon aXPoly(rXP);
1169 aXPoly[(sal_uInt16)rDrag.GetHdl()->GetPointNum()]=rDrag.GetNow();
1170 aRetval.Insert(aXPoly);
1171 return aRetval.getB2DPolyPolygon();
1173 // copy certain data locally to use less code and have faster access times
1174 bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
1175 sal_uInt16 nPntAnz =mpSdrPathDragData->nPntAnz ; // number of points
1176 sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
1177 bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
1178 bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
1179 sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
1180 sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
1181 bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
1182 bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
1183 sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
1184 sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
1185 bool bControl =mpSdrPathDragData->bControl ; // point is a control point
1186 bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
1187 bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
1188 bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
1189 XPolygon aXPoly(mpSdrPathDragData->aXP);
1190 XPolygon aLine1(2);
1191 XPolygon aLine2(2);
1192 XPolygon aLine3(2);
1193 XPolygon aLine4(2);
1194 if (bControl) {
1195 aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1196 if (bIsNextControl) { // is this a control point after the support point?
1197 aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1198 aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1199 aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1200 if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1201 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1202 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1203 // leverage lines for the opposing curve segment
1204 aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1205 aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1206 aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1207 aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1208 } else {
1209 aXPoly.Remove(0,1);
1211 } else { // else this is a control point before a support point
1212 aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1213 aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1214 aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1215 if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1216 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1217 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1218 // leverage lines for the opposing curve segment
1219 aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1220 aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1221 aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1222 aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1223 } else {
1224 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1227 } else { // else is not a control point
1228 if (mpSdrPathDragData->bEliminate) {
1229 aXPoly.Remove(2,1);
1231 if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_NORMAL);
1232 else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1233 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1234 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1235 } else {
1236 aXPoly.Remove(0,1);
1237 if (bBegPnt) aXPoly.Remove(0,1);
1239 if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_NORMAL);
1240 else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1241 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1242 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1243 } else {
1244 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1245 if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1247 if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1248 if (aXPoly.GetPointCount()>nPntAnz && aXPoly.IsControl(1)) {
1249 sal_uInt16 a=aXPoly.GetPointCount();
1250 aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1251 aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1252 aXPoly.Remove(0,3);
1256 aRetval.Insert(aXPoly);
1257 if (aLine1.GetPointCount()>1) aRetval.Insert(aLine1);
1258 if (aLine2.GetPointCount()>1) aRetval.Insert(aLine2);
1259 if (aLine3.GetPointCount()>1) aRetval.Insert(aLine3);
1260 if (aLine4.GetPointCount()>1) aRetval.Insert(aLine4);
1263 return aRetval.getB2DPolyPolygon();
1266 bool ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
1268 bool bFreeHand(IsFreeHand(meObjectKind));
1269 rStat.SetNoSnap(bFreeHand);
1270 rStat.SetOrtho8Possible();
1271 aPathPolygon.Clear();
1272 mbCreating=sal_True;
1273 bool bMakeStartPoint = true;
1274 SdrView* pView=rStat.GetView();
1275 if (pView!=NULL && pView->IsUseIncompatiblePathCreateInterface() &&
1276 (meObjectKind==OBJ_POLY || meObjectKind==OBJ_PLIN || meObjectKind==OBJ_PATHLINE || meObjectKind==OBJ_PATHFILL)) {
1277 bMakeStartPoint = false;
1279 aPathPolygon.Insert(XPolygon());
1280 aPathPolygon[0][0]=rStat.GetStart();
1281 if (bMakeStartPoint) {
1282 aPathPolygon[0][1]=rStat.GetNow();
1284 ImpPathCreateUser* pU=new ImpPathCreateUser;
1285 pU->eStartKind=meObjectKind;
1286 pU->eAktKind=meObjectKind;
1287 rStat.SetUser(pU);
1288 return true;
1291 bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
1293 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1294 SdrView* pView=rStat.GetView();
1295 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1296 if (pView!=NULL && pView->IsCreateMode()) {
1297 // switch to different CreateTool, if appropriate
1298 sal_uInt16 nIdent;
1299 sal_uInt32 nInvent;
1300 pView->TakeCurrentObj(nIdent,nInvent);
1301 if (nInvent==SdrInventor && pU->eAktKind!=(SdrObjKind)nIdent) {
1302 SdrObjKind eNewKind=(SdrObjKind)nIdent;
1303 switch (eNewKind) {
1304 case OBJ_CARC: case OBJ_CIRC: case OBJ_CCUT: case OBJ_SECT: eNewKind=OBJ_CARC;
1305 case OBJ_RECT:
1306 case OBJ_LINE: case OBJ_PLIN: case OBJ_POLY:
1307 case OBJ_PATHLINE: case OBJ_PATHFILL:
1308 case OBJ_FREELINE: case OBJ_FREEFILL:
1309 case OBJ_SPLNLINE: case OBJ_SPLNFILL: {
1310 pU->eAktKind=eNewKind;
1311 pU->bMixedCreate=sal_True;
1312 pU->nBezierStartPoint=rXPoly.GetPointCount();
1313 if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1314 } break;
1315 default: break;
1316 } // switch
1319 sal_uInt16 nActPoint=rXPoly.GetPointCount();
1320 if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nActPoint<2) {
1321 rXPoly[0]=rStat.GetPos0();
1322 rXPoly[1]=rStat.GetNow();
1323 nActPoint=2;
1325 if (nActPoint==0) {
1326 rXPoly[0]=rStat.GetPos0();
1327 } else nActPoint--;
1328 bool bFreeHand=IsFreeHand(pU->eAktKind);
1329 rStat.SetNoSnap(bFreeHand);
1330 rStat.SetOrtho8Possible(pU->eAktKind!=OBJ_CARC && pU->eAktKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eAktKind!=OBJ_LINE));
1331 Point aActMerk(rXPoly[nActPoint]);
1332 rXPoly[nActPoint]=rStat.Now();
1333 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1334 Point aPt(rStat.Start());
1335 if (pView!=NULL && pView->IsCreate1stPointAsCenter()) {
1336 aPt+=aPt;
1337 aPt-=rStat.Now();
1339 rXPoly[0]=aPt;
1341 OutputDevice* pOut=pView==NULL ? NULL : pView->GetFirstOutputDevice();
1342 if (bFreeHand) {
1343 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1344 if (rStat.IsMouseDown() && nActPoint>0) {
1345 // don't allow two consecutive points to occupy too similar positions
1346 long nMinDist=1;
1347 if (pView!=NULL) nMinDist=pView->GetFreeHandMinDistPix();
1348 if (pOut!=NULL) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1349 if (nMinDist<1) nMinDist=1;
1351 Point aPt0(rXPoly[nActPoint-1]);
1352 Point aPt1(rStat.Now());
1353 long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1354 long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1355 if (dx<nMinDist && dy<nMinDist) return sal_False;
1357 // TODO: the following is copied from EndCreate (with a few smaller modifications)
1358 // and should be combined into a method with the code there.
1360 if (nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1361 rXPoly.PointsToBezier(nActPoint-3);
1362 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1363 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1365 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1366 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1367 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1370 rXPoly[nActPoint+1]=rStat.Now();
1371 rStat.NextPoint();
1372 } else {
1373 pU->nBezierStartPoint=nActPoint;
1377 pU->ResetFormFlags();
1378 if (IsBezier(pU->eAktKind)) {
1379 if (nActPoint>=2) {
1380 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],rStat.IsMouseDown());
1381 } else if (pU->bBezHasCtrl0) {
1382 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],pU->aBezControl0-rXPoly[nActPoint-1],rStat.IsMouseDown());
1385 if (pU->eAktKind==OBJ_CARC && nActPoint>=2) {
1386 pU->CalcCircle(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1388 if (pU->eAktKind==OBJ_LINE && nActPoint>=2) {
1389 pU->CalcLine(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1391 if (pU->eAktKind==OBJ_RECT && nActPoint>=2) {
1392 pU->CalcRect(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1395 return true;
1398 bool ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
1400 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1401 bool bRet = false;
1402 SdrView* pView=rStat.GetView();
1403 bool bIncomp=pView!=NULL && pView->IsUseIncompatiblePathCreateInterface();
1404 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1405 sal_uInt16 nActPoint=rXPoly.GetPointCount()-1;
1406 Point aAktMerk(rXPoly[nActPoint]);
1407 rXPoly[nActPoint]=rStat.Now();
1408 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1409 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1410 bRet = eCmd==SDRCREATE_FORCEEND;
1411 if (bRet) {
1412 mbCreating = sal_False;
1413 delete pU;
1414 rStat.SetUser(NULL);
1416 return bRet;
1419 if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1420 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1421 bRet=eCmd==SDRCREATE_FORCEEND;
1422 if (bRet) {
1423 mbCreating=sal_False;
1424 delete pU;
1425 rStat.SetUser(NULL);
1427 return bRet;
1429 if (eCmd==SDRCREATE_NEXTPOINT || eCmd==SDRCREATE_NEXTOBJECT) {
1430 // don't allow two consecutive points to occupy the same position
1431 if (nActPoint==0 || rStat.Now()!=rXPoly[nActPoint-1]) {
1432 if (bIncomp) {
1433 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1434 if (IsBezier(pU->eAktKind) && nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1435 rXPoly.PointsToBezier(nActPoint-3);
1436 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1437 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1439 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1440 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1441 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1444 } else {
1445 if (nActPoint==1 && IsBezier(pU->eAktKind) && !pU->bBezHasCtrl0) {
1446 pU->aBezControl0=rStat.GetNow();
1447 pU->bBezHasCtrl0=sal_True;
1448 nActPoint--;
1450 if (pU->IsFormFlag()) {
1451 sal_uInt16 nPtAnz0=rXPoly.GetPointCount();
1452 rXPoly.Remove(nActPoint-1,2); // remove last two points and replace by form
1453 rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1454 sal_uInt16 nPtAnz1=rXPoly.GetPointCount();
1455 for (sal_uInt16 i=nPtAnz0+1; i<nPtAnz1-1; i++) { // to make BckAction work
1456 if (!rXPoly.IsControl(i)) rStat.NextPoint();
1458 nActPoint=rXPoly.GetPointCount()-1;
1461 nActPoint++;
1462 rXPoly[nActPoint]=rStat.GetNow();
1464 if (eCmd==SDRCREATE_NEXTOBJECT) {
1465 if (rXPoly.GetPointCount()>=2) {
1466 pU->bBezHasCtrl0=sal_False;
1467 // only a singular polygon may be opened, so close this
1468 rXPoly[nActPoint]=rXPoly[0];
1469 XPolygon aXP;
1470 aXP[0]=rStat.GetNow();
1471 aPathPolygon.Insert(aXP);
1476 sal_uInt16 nPolyAnz=aPathPolygon.Count();
1477 if (nPolyAnz!=0) {
1478 // delete last point, if necessary
1479 if (eCmd==SDRCREATE_FORCEEND) {
1480 XPolygon& rXP=aPathPolygon[nPolyAnz-1];
1481 sal_uInt16 nPtAnz=rXP.GetPointCount();
1482 if (nPtAnz>=2) {
1483 if (!rXP.IsControl(nPtAnz-2)) {
1484 if (rXP[nPtAnz-1]==rXP[nPtAnz-2]) {
1485 rXP.Remove(nPtAnz-1,1);
1487 } else {
1488 if (rXP[nPtAnz-3]==rXP[nPtAnz-2]) {
1489 rXP.Remove(nPtAnz-3,3);
1494 for (sal_uInt16 nPolyNum=nPolyAnz; nPolyNum>0;) {
1495 nPolyNum--;
1496 XPolygon& rXP=aPathPolygon[nPolyNum];
1497 sal_uInt16 nPtAnz=rXP.GetPointCount();
1498 // delete polygons with too few points
1499 if (nPolyNum<nPolyAnz-1 || eCmd==SDRCREATE_FORCEEND) {
1500 if (nPtAnz<2) aPathPolygon.Remove(nPolyNum);
1504 pU->ResetFormFlags();
1505 bRet=eCmd==SDRCREATE_FORCEEND;
1506 if (bRet) {
1507 mbCreating=sal_False;
1508 delete pU;
1509 rStat.SetUser(NULL);
1511 return bRet;
1514 bool ImpPathForDragAndCreate::BckCreate(SdrDragStat& rStat)
1516 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1517 if (aPathPolygon.Count()>0) {
1518 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1519 sal_uInt16 nActPoint=rXPoly.GetPointCount();
1520 if (nActPoint>0) {
1521 nActPoint--;
1522 // make the last part of a bezier curve a line
1523 rXPoly.Remove(nActPoint,1);
1524 if (nActPoint>=3 && rXPoly.IsControl(nActPoint-1)) {
1525 // there should never be a bezier segment at the end, so this is just in case...
1526 rXPoly.Remove(nActPoint-1,1);
1527 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1530 nActPoint=rXPoly.GetPointCount();
1531 if (nActPoint>=4) { // no bezier segment at the end
1532 nActPoint--;
1533 if (rXPoly.IsControl(nActPoint-1)) {
1534 rXPoly.Remove(nActPoint-1,1);
1535 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1538 if (rXPoly.GetPointCount()<2) {
1539 aPathPolygon.Remove(aPathPolygon.Count()-1);
1541 if (aPathPolygon.Count()>0) {
1542 XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1543 sal_uInt16 nLocalActPoint=rLocalXPoly.GetPointCount();
1544 if (nLocalActPoint>0) {
1545 nLocalActPoint--;
1546 rLocalXPoly[nLocalActPoint]=rStat.Now();
1550 pU->ResetFormFlags();
1551 return aPathPolygon.Count()!=0;
1554 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
1556 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1557 aPathPolygon.Clear();
1558 mbCreating=sal_False;
1559 delete pU;
1560 rStat.SetUser(NULL);
1563 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
1565 basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
1566 SdrView* pView = rDrag.GetView();
1568 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1569 return aRetval;
1571 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1572 basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1L) : basegfx::B2DPolygon());
1574 if(pU->IsFormFlag() && aNewPolygon.count() > 1L)
1576 // remove last segment and replace with current
1577 // do not forget to rescue the previous control point which will be lost when
1578 // the point it's associated with is removed
1579 const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1580 const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1582 aNewPolygon.remove(nChangeIndex, 2L);
1583 aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1585 if(nChangeIndex < aNewPolygon.count())
1587 // if really something was added, set the saved previous control point to the
1588 // point where it belongs
1589 aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1593 if(aRetval.count())
1595 aRetval.setB2DPolygon(aRetval.count() - 1L, aNewPolygon);
1597 else
1599 aRetval.append(aNewPolygon);
1602 return aRetval;
1605 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag) const
1607 basegfx::B2DPolyPolygon aRetval;
1608 SdrView* pView = rDrag.GetView();
1610 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1611 return aRetval;
1613 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1615 if(pU && pU->bBezier && rDrag.IsMouseDown())
1617 // no more XOR, no need for complicated helplines
1618 basegfx::B2DPolygon aHelpline;
1619 aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1620 aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1621 aRetval.append(aHelpline);
1624 return aRetval;
1627 Pointer ImpPathForDragAndCreate::GetCreatePointer() const
1629 switch (meObjectKind) {
1630 case OBJ_LINE : return Pointer(POINTER_DRAW_LINE);
1631 case OBJ_POLY : return Pointer(POINTER_DRAW_POLYGON);
1632 case OBJ_PLIN : return Pointer(POINTER_DRAW_POLYGON);
1633 case OBJ_PATHLINE: return Pointer(POINTER_DRAW_BEZIER);
1634 case OBJ_PATHFILL: return Pointer(POINTER_DRAW_BEZIER);
1635 case OBJ_FREELINE: return Pointer(POINTER_DRAW_FREEHAND);
1636 case OBJ_FREEFILL: return Pointer(POINTER_DRAW_FREEHAND);
1637 case OBJ_SPLNLINE: return Pointer(POINTER_DRAW_FREEHAND);
1638 case OBJ_SPLNFILL: return Pointer(POINTER_DRAW_FREEHAND);
1639 case OBJ_PATHPOLY: return Pointer(POINTER_DRAW_POLYGON);
1640 case OBJ_PATHPLIN: return Pointer(POINTER_DRAW_POLYGON);
1641 default: break;
1642 } // switch
1643 return Pointer(POINTER_CROSS);
1646 /*************************************************************************/
1648 SdrPathObjGeoData::SdrPathObjGeoData()
1652 SdrPathObjGeoData::~SdrPathObjGeoData()
1656 //////////////////////////////////////////////////////////////////////////////
1657 // DrawContact section
1659 sdr::contact::ViewContact* SdrPathObj::CreateObjectSpecificViewContact()
1661 return new sdr::contact::ViewContactOfSdrPathObj(*this);
1664 /*************************************************************************/
1666 TYPEINIT1(SdrPathObj,SdrTextObj);
1668 SdrPathObj::SdrPathObj(SdrObjKind eNewKind)
1669 : meKind(eNewKind),
1670 mpDAC(0L),
1671 mdBrightness(1.0)
1673 bClosedObj = IsClosed();
1676 SdrPathObj::SdrPathObj(SdrObjKind eNewKind, const basegfx::B2DPolyPolygon& rPathPoly, double dBrightness)
1677 : maPathPolygon(rPathPoly),
1678 meKind(eNewKind),
1679 mpDAC(0L),
1680 mdBrightness(dBrightness)
1682 bClosedObj = IsClosed();
1683 ImpForceKind();
1686 SdrPathObj::~SdrPathObj()
1688 impDeleteDAC();
1691 sal_Bool ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1693 return (1L == rPolyPolygon.count() && 2L == rPolyPolygon.getB2DPolygon(0L).count());
1696 Rectangle ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
1698 basegfx::B2DRange aRange(basegfx::tools::getRange(rPolyPolygon));
1700 return Rectangle(
1701 FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1702 FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1705 void SdrPathObj::ImpForceLineWink()
1707 if(OBJ_LINE == meKind && ImpIsLine(GetPathPoly()))
1709 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1710 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1711 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1712 const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1713 const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1714 const Point aDelt(aPoint1 - aPoint0);
1716 aGeo.nDrehWink=GetAngle(aDelt);
1717 aGeo.nShearWink=0;
1718 aGeo.RecalcSinCos();
1719 aGeo.RecalcTan();
1721 // for SdrTextObj, keep aRect up to date
1722 aRect = Rectangle(aPoint0, aPoint1);
1723 aRect.Justify();
1727 void SdrPathObj::ImpForceKind()
1729 if (meKind==OBJ_PATHPLIN) meKind=OBJ_PLIN;
1730 if (meKind==OBJ_PATHPOLY) meKind=OBJ_POLY;
1732 if(GetPathPoly().areControlPointsUsed())
1734 switch (meKind)
1736 case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1737 case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1738 case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1739 default: break;
1742 else
1744 switch (meKind)
1746 case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1747 case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1748 case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1749 case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1750 default: break;
1754 if (meKind==OBJ_LINE && !ImpIsLine(GetPathPoly())) meKind=OBJ_PLIN;
1755 if (meKind==OBJ_PLIN && ImpIsLine(GetPathPoly())) meKind=OBJ_LINE;
1757 bClosedObj=IsClosed();
1759 if (meKind==OBJ_LINE)
1761 ImpForceLineWink();
1763 else
1765 // #i10659#, for polys with more than 2 points.
1767 // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1768 // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1769 // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1770 // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1771 // this is the case, some size needs to be set here in aRect to avoid that the cycle
1772 // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1773 // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1774 // from the local Resize() implementation.
1776 // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1777 // text rectangle for the text object itself and methods at SdrTextObj do handle it
1778 // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1779 // which is basically wrong. To make the SdrText methods which deal with aRect directly
1780 // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1781 // command for SdrPathObj. Since adding this update mechanism with #101412# to
1782 // ImpForceLineWink() for lines was very successful, i add it to where ImpForceLineWink()
1783 // was called, once here below and once on a 2nd place below.
1785 // #i10659# for SdrTextObj, keep aRect up to date
1786 if(GetPathPoly().count())
1788 aRect = ImpGetBoundRect(GetPathPoly());
1792 // #i75974# adapt polygon state to object type. This may include a reinterpretation
1793 // of a closed geometry as open one, but with identical first and last point
1794 for(sal_uInt32 a(0); a < maPathPolygon.count(); a++)
1796 basegfx::B2DPolygon aCandidate(maPathPolygon.getB2DPolygon(a));
1798 if((bool)IsClosed() != aCandidate.isClosed())
1800 // #i80213# really change polygon geometry; else e.g. the last point which
1801 // needs to be identical with the first one will be missing when opening
1802 // due to OBJ_PATH type
1803 if(aCandidate.isClosed())
1805 basegfx::tools::openWithGeometryChange(aCandidate);
1807 else
1809 basegfx::tools::closeWithGeometryChange(aCandidate);
1812 maPathPolygon.setB2DPolygon(a, aCandidate);
1817 void SdrPathObj::ImpSetClosed(sal_Bool bClose)
1819 if(bClose)
1821 switch (meKind)
1823 case OBJ_LINE : meKind=OBJ_POLY; break;
1824 case OBJ_PLIN : meKind=OBJ_POLY; break;
1825 case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1826 case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1827 case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1828 default: break;
1831 bClosedObj = sal_True;
1833 else
1835 switch (meKind)
1837 case OBJ_POLY : meKind=OBJ_PLIN; break;
1838 case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1839 case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1840 case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1841 default: break;
1844 bClosedObj = sal_False;
1847 ImpForceKind();
1850 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1852 rInfo.bNoContortion=sal_False;
1854 bool bCanConv = !HasText() || ImpCanConvTextToCurve();
1855 bool bIsPath = IsBezier() || IsSpline();
1857 rInfo.bEdgeRadiusAllowed = sal_False;
1858 rInfo.bCanConvToPath = bCanConv && !bIsPath;
1859 rInfo.bCanConvToPoly = bCanConv && bIsPath;
1860 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
1863 sal_uInt16 SdrPathObj::GetObjIdentifier() const
1865 return sal_uInt16(meKind);
1868 SdrPathObj* SdrPathObj::Clone() const
1870 return CloneHelper< SdrPathObj >();
1873 SdrPathObj& SdrPathObj::operator=(const SdrPathObj& rObj)
1875 if( this == &rObj )
1876 return *this;
1877 SdrTextObj::operator=(rObj);
1878 maPathPolygon=rObj.GetPathPoly();
1879 return *this;
1882 void SdrPathObj::TakeObjNameSingul(XubString& rName) const
1884 if(OBJ_LINE == meKind)
1886 sal_uInt16 nId(STR_ObjNameSingulLINE);
1888 if(ImpIsLine(GetPathPoly()))
1890 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1891 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1892 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1893 const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1894 const Point aPoint1(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1896 if(aB2DPoint0 != aB2DPoint1)
1898 if(aB2DPoint0.getY() == aB2DPoint1.getY())
1900 nId = STR_ObjNameSingulLINE_Hori;
1902 else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1904 nId = STR_ObjNameSingulLINE_Vert;
1906 else
1908 const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1909 const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1911 if(fDx == fDy)
1913 nId = STR_ObjNameSingulLINE_Diag;
1919 rName = ImpGetResStr(nId);
1921 else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1923 const sal_Bool bClosed(OBJ_POLY == meKind);
1924 sal_uInt16 nId(0);
1926 if(mpDAC && mpDAC->IsCreating())
1928 if(bClosed)
1930 nId = STR_ObjNameSingulPOLY;
1932 else
1934 nId = STR_ObjNameSingulPLIN;
1937 rName = ImpGetResStr(nId);
1939 else
1941 // get point count
1942 sal_uInt32 nPointCount(0L);
1943 const sal_uInt32 nPolyCount(GetPathPoly().count());
1945 for(sal_uInt32 a(0L); a < nPolyCount; a++)
1947 nPointCount += GetPathPoly().getB2DPolygon(a).count();
1950 if(bClosed)
1952 nId = STR_ObjNameSingulPOLY_PntAnz;
1954 else
1956 nId = STR_ObjNameSingulPLIN_PntAnz;
1959 rName = ImpGetResStr(nId);
1960 sal_uInt16 nPos(rName.SearchAscii("%2")); // #i96537#
1962 if(STRING_NOTFOUND != nPos)
1964 rName.Erase(nPos, 2);
1965 rName.Insert(rtl::OUString::valueOf(static_cast<sal_Int32>(nPointCount)), nPos);
1969 else
1971 switch (meKind)
1973 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNameSingulPATHLINE); break;
1974 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNameSingulFREELINE); break;
1975 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNameSingulNATSPLN); break;
1976 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNameSingulPATHFILL); break;
1977 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNameSingulFREEFILL); break;
1978 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNameSingulPERSPLN); break;
1979 default: break;
1983 String aName(GetName());
1984 if(aName.Len())
1986 rName += sal_Unicode(' ');
1987 rName += sal_Unicode('\'');
1988 rName += aName;
1989 rName += sal_Unicode('\'');
1993 void SdrPathObj::TakeObjNamePlural(XubString& rName) const
1995 switch(meKind)
1997 case OBJ_LINE : rName=ImpGetResStr(STR_ObjNamePluralLINE ); break;
1998 case OBJ_PLIN : rName=ImpGetResStr(STR_ObjNamePluralPLIN ); break;
1999 case OBJ_POLY : rName=ImpGetResStr(STR_ObjNamePluralPOLY ); break;
2000 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNamePluralPATHLINE); break;
2001 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNamePluralFREELINE); break;
2002 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNamePluralNATSPLN); break;
2003 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNamePluralPATHFILL); break;
2004 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNamePluralFREEFILL); break;
2005 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNamePluralPERSPLN); break;
2006 default: break;
2010 basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
2012 return GetPathPoly();
2015 sal_uInt32 SdrPathObj::GetHdlCount() const
2017 sal_uInt32 nRetval(0L);
2018 const sal_uInt32 nPolyCount(GetPathPoly().count());
2020 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2022 nRetval += GetPathPoly().getB2DPolygon(a).count();
2025 return nRetval;
2028 SdrHdl* SdrPathObj::GetHdl(sal_uInt32 nHdlNum) const
2030 // #i73248#
2031 // Warn the user that this is ineffective and show alternatives. Should not be used at all.
2032 OSL_FAIL("SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
2034 // to have an alternative, get single handle using the ineffective way
2035 SdrHdl* pRetval = 0;
2036 SdrHdlList aLocalList(0);
2037 AddToHdlList(aLocalList);
2038 const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());
2040 if(nHdlCount && nHdlNum < nHdlCount)
2042 // remove and remember. The other created handles will be deleted again with the
2043 // destruction of the local list
2044 pRetval = aLocalList.RemoveHdl(nHdlNum);
2047 return pRetval;
2050 void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
2052 // keep old stuff to be able to keep old SdrHdl stuff, too
2053 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2054 sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
2055 bool bClosed=IsClosed();
2056 sal_uInt16 nIdx=0;
2058 for (sal_uInt16 i=0; i<nPolyCnt; i++) {
2059 const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
2060 sal_uInt16 nPntCnt=rXPoly.GetPointCount();
2061 if (bClosed && nPntCnt>1) nPntCnt--;
2063 for (sal_uInt16 j=0; j<nPntCnt; j++) {
2064 if (rXPoly.GetFlags(j)!=XPOLY_CONTROL) {
2065 const Point& rPnt=rXPoly[j];
2066 SdrHdl* pHdl=new SdrHdl(rPnt,HDL_POLY);
2067 pHdl->SetPolyNum(i);
2068 pHdl->SetPointNum(j);
2069 pHdl->Set1PixMore(j==0);
2070 pHdl->SetSourceHdlNum(nIdx);
2071 nIdx++;
2072 rHdlList.AddHdl(pHdl);
2078 sal_uInt32 SdrPathObj::GetPlusHdlCount(const SdrHdl& rHdl) const
2080 // keep old stuff to be able to keep old SdrHdl stuff, too
2081 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2082 sal_uInt16 nCnt = 0;
2083 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2084 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2086 if(nPolyNum < aOldPathPolygon.Count())
2088 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2089 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2090 if (nPntMax>0)
2092 nPntMax--;
2093 if (nPnt<=nPntMax)
2095 if (rXPoly.GetFlags(nPnt)!=XPOLY_CONTROL)
2097 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2098 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL) nCnt++;
2099 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2100 if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL) nCnt++;
2106 return nCnt;
2109 SdrHdl* SdrPathObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlusNum) const
2111 // keep old stuff to be able to keep old SdrHdl stuff, too
2112 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2113 SdrHdl* pHdl = 0L;
2114 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2115 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2117 if (nPolyNum<aOldPathPolygon.Count())
2119 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2120 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2122 if (nPntMax>0)
2124 nPntMax--;
2125 if (nPnt<=nPntMax)
2127 pHdl=new SdrHdlBezWgt(&rHdl);
2128 pHdl->SetPolyNum(rHdl.GetPolyNum());
2130 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2131 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL && nPlusNum==0)
2133 pHdl->SetPos(rXPoly[nPnt-1]);
2134 pHdl->SetPointNum(nPnt-1);
2136 else
2138 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2139 if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL)
2141 pHdl->SetPos(rXPoly[nPnt+1]);
2142 pHdl->SetPointNum(nPnt+1);
2146 pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2147 pHdl->SetPlusHdl(sal_True);
2151 return pHdl;
2154 ////////////////////////////////////////////////////////////////////////////////////////////////////
2156 bool SdrPathObj::hasSpecialDrag() const
2158 return true;
2161 bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
2163 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2165 return aDragAndCreate.beginPathDrag(rDrag);
2168 bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
2170 ImpPathForDragAndCreate aDragAndCreate(*this);
2171 bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2173 if(bRetval)
2175 bRetval = aDragAndCreate.movePathDrag(rDrag);
2178 if(bRetval)
2180 bRetval = aDragAndCreate.endPathDrag(rDrag);
2183 if(bRetval)
2185 NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2188 return bRetval;
2191 String SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2193 String aRetval;
2195 if(mpDAC)
2197 // #i103058# also get a comment when in creation
2198 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2200 if(bCreateComment)
2202 aRetval = mpDAC->getSpecialDragComment(rDrag);
2205 else
2207 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2208 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2210 if(bDidWork)
2212 aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2216 return aRetval;
2219 basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
2221 basegfx::B2DPolyPolygon aRetval;
2222 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2223 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2225 if(bDidWork)
2227 aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2230 return aRetval;
2233 ////////////////////////////////////////////////////////////////////////////////////////////////////
2235 bool SdrPathObj::BegCreate(SdrDragStat& rStat)
2237 impDeleteDAC();
2238 return impGetDAC().BegCreate(rStat);
2241 bool SdrPathObj::MovCreate(SdrDragStat& rStat)
2243 return impGetDAC().MovCreate(rStat);
2246 bool SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
2248 bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2250 if(bRetval && mpDAC)
2252 SetPathPoly(mpDAC->getModifiedPolyPolygon());
2254 // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2255 // to be able to use the type-changing ImpSetClosed method
2256 if(!IsClosedObj())
2258 SdrView* pView = rStat.GetView();
2260 if(pView && pView->IsAutoClosePolys() && !pView->IsUseIncompatiblePathCreateInterface())
2262 OutputDevice* pOut = pView->GetFirstOutputDevice();
2264 if(pOut)
2266 if(GetPathPoly().count())
2268 const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2270 if(aCandidate.count() > 2)
2272 // check distance of first and last point
2273 const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2274 const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2276 if(aDistVector.getLength() <= (double)nCloseDist)
2278 // close it
2279 ImpSetClosed(true);
2287 impDeleteDAC();
2290 return bRetval;
2293 bool SdrPathObj::BckCreate(SdrDragStat& rStat)
2295 return impGetDAC().BckCreate(rStat);
2298 void SdrPathObj::BrkCreate(SdrDragStat& rStat)
2300 impGetDAC().BrkCreate(rStat);
2301 impDeleteDAC();
2304 basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
2306 basegfx::B2DPolyPolygon aRetval;
2308 if(mpDAC)
2310 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2311 aRetval.append(mpDAC->TakeDragPolyPolygon(rDrag));
2314 return aRetval;
2317 // during drag or create, allow accessing the so-far created/modified polyPolygon
2318 basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
2320 basegfx::B2DPolyPolygon aRetval;
2322 if(mpDAC)
2324 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2327 return aRetval;
2330 basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
2332 basegfx::B2DPolyPolygon aRetval;
2334 if(mpDAC)
2336 aRetval = mpDAC->TakeDragPolyPolygon(rDrag);
2339 return aRetval;
2342 Pointer SdrPathObj::GetCreatePointer() const
2344 return impGetDAC().GetCreatePointer();
2347 void SdrPathObj::NbcMove(const Size& rSiz)
2349 maPathPolygon.transform(basegfx::tools::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
2351 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2352 SdrTextObj::NbcMove(rSiz);
2355 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2357 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
2358 aTrans = basegfx::tools::createScaleTranslateB2DHomMatrix(
2359 double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2360 maPathPolygon.transform(aTrans);
2362 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2363 SdrTextObj::NbcResize(rRef,xFact,yFact);
2366 void SdrPathObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
2368 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2369 const basegfx::B2DHomMatrix aTrans(basegfx::tools::createRotateAroundPoint(rRef.X(), rRef.Y(), -nWink * nPi180));
2370 maPathPolygon.transform(aTrans);
2372 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2373 SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
2376 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, bool bVShear)
2378 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2380 if(bVShear)
2382 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2383 aTrans.shearY(-fTan);
2385 else
2387 aTrans.shearX(-fTan);
2390 aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2391 maPathPolygon.transform(aTrans);
2393 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2394 SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2397 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2399 const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2400 const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2401 const double fRot(atan2(fDiffY, fDiffX));
2402 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2403 aTrans.rotate(-fRot);
2404 aTrans.scale(1.0, -1.0);
2405 aTrans.rotate(fRot);
2406 aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2407 maPathPolygon.transform(aTrans);
2409 // Do Joe's special handling for lines when mirroring, too
2410 ImpForceKind();
2412 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2413 SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2416 void SdrPathObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
2418 if(!aGeo.nDrehWink)
2420 rRect = GetSnapRect();
2422 else
2424 XPolyPolygon aXPP(GetPathPoly());
2425 RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2426 rRect=aXPP.GetBoundRect();
2427 Point aTmp(rRect.TopLeft());
2428 RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2429 aTmp-=rRect.TopLeft();
2430 rRect.Move(aTmp.X(),aTmp.Y());
2434 void SdrPathObj::RecalcSnapRect()
2436 if(GetPathPoly().count())
2438 maSnapRect = ImpGetBoundRect(GetPathPoly());
2442 void SdrPathObj::NbcSetSnapRect(const Rectangle& rRect)
2444 Rectangle aOld(GetSnapRect());
2446 // Take RECT_EMPTY into account when calculating scale factors
2447 long nMulX = (RECT_EMPTY == rRect.Right()) ? 0 : rRect.Right() - rRect.Left();
2449 long nDivX = aOld.Right() - aOld.Left();
2451 // Take RECT_EMPTY into account when calculating scale factors
2452 long nMulY = (RECT_EMPTY == rRect.Bottom()) ? 0 : rRect.Bottom() - rRect.Top();
2454 long nDivY = aOld.Bottom() - aOld.Top();
2455 if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2456 if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2457 Fraction aX(nMulX,nDivX);
2458 Fraction aY(nMulY,nDivY);
2459 NbcResize(aOld.TopLeft(), aX, aY);
2460 NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2463 sal_uInt32 SdrPathObj::GetSnapPointCount() const
2465 return GetHdlCount();
2468 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2470 sal_uInt32 nPoly,nPnt;
2471 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2473 DBG_ASSERT(sal_False,"SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2476 const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2477 return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2480 sal_Bool SdrPathObj::IsPolyObj() const
2482 return sal_True;
2485 sal_uInt32 SdrPathObj::GetPointCount() const
2487 const sal_uInt32 nPolyCount(GetPathPoly().count());
2488 sal_uInt32 nRetval(0L);
2490 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2492 nRetval += GetPathPoly().getB2DPolygon(a).count();
2495 return nRetval;
2498 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2500 Point aRetval;
2501 sal_uInt32 nPoly,nPnt;
2503 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2505 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2506 const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2507 aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2510 return aRetval;
2513 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2515 sal_uInt32 nPoly,nPnt;
2517 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2519 basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2520 aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2521 maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2523 if(meKind==OBJ_LINE)
2525 ImpForceLineWink();
2527 else
2529 if(GetPathPoly().count())
2531 // #i10659# for SdrTextObj, keep aRect up to date
2532 aRect = ImpGetBoundRect(GetPathPoly());
2536 SetRectsDirty();
2540 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, sal_Bool bNewObj, sal_Bool bHideHim)
2542 sal_uInt32 nNewHdl;
2544 if(bNewObj)
2546 nNewHdl = NbcInsPoint(0L, rPos, sal_True, bHideHim);
2548 else
2550 // look for smallest distance data
2551 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2552 sal_uInt32 nSmallestPolyIndex(0L);
2553 sal_uInt32 nSmallestEdgeIndex(0L);
2554 double fSmallestCut;
2555 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2557 // create old polygon index from it
2558 sal_uInt32 nPolyIndex(nSmallestEdgeIndex);
2560 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2562 nPolyIndex += GetPathPoly().getB2DPolygon(a).count();
2565 nNewHdl = NbcInsPoint(nPolyIndex, rPos, sal_False, bHideHim);
2568 ImpForceKind();
2569 return nNewHdl;
2572 sal_uInt32 SdrPathObj::NbcInsPoint(sal_uInt32 /*nHdlNum*/, const Point& rPos, sal_Bool bNewObj, sal_Bool /*bHideHim*/)
2574 sal_uInt32 nNewHdl;
2576 if(bNewObj)
2578 basegfx::B2DPolygon aNewPoly;
2579 const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2580 aNewPoly.append(aPoint);
2581 aNewPoly.setClosed(IsClosed());
2582 maPathPolygon.append(aNewPoly);
2583 SetRectsDirty();
2584 nNewHdl = GetHdlCount();
2586 else
2588 // look for smallest distance data
2589 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2590 sal_uInt32 nSmallestPolyIndex(0L);
2591 sal_uInt32 nSmallestEdgeIndex(0L);
2592 double fSmallestCut;
2593 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2594 basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2595 const bool bBefore(!aCandidate.isClosed() && 0L == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2596 const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2L && 1.0 == fSmallestCut);
2598 if(bBefore)
2600 // before first point
2601 aCandidate.insert(0L, aTestPoint);
2603 if(aCandidate.areControlPointsUsed())
2605 if(aCandidate.isNextControlPointUsed(1))
2607 aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2608 aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2612 nNewHdl = 0L;
2614 else if(bAfter)
2616 // after last point
2617 aCandidate.append(aTestPoint);
2619 if(aCandidate.areControlPointsUsed())
2621 if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2623 aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2624 aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2628 nNewHdl = aCandidate.count() - 1L;
2630 else
2632 // in between
2633 bool bSegmentSplit(false);
2634 const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2636 if(aCandidate.areControlPointsUsed())
2638 if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2640 bSegmentSplit = true;
2644 if(bSegmentSplit)
2646 // rebuild original segment to get the split data
2647 basegfx::B2DCubicBezier aBezierA, aBezierB;
2648 const basegfx::B2DCubicBezier aBezier(
2649 aCandidate.getB2DPoint(nSmallestEdgeIndex),
2650 aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2651 aCandidate.getPrevControlPoint(nNextIndex),
2652 aCandidate.getB2DPoint(nNextIndex));
2654 // split and insert hit point
2655 aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2656 aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2658 // since we inserted hit point and not split point, we need to add an offset
2659 // to the control points to get the C1 continuity we want to achieve
2660 const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2661 aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2662 aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2663 aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2664 aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2666 else
2668 aCandidate.insert(nSmallestEdgeIndex + 1L, aTestPoint);
2671 nNewHdl = nSmallestEdgeIndex + 1L;
2674 maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2676 // create old polygon index from it
2677 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2679 nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2683 ImpForceKind();
2684 return nNewHdl;
2687 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2689 SdrPathObj* pNewObj = 0L;
2690 const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2691 sal_uInt32 nPoly, nPnt;
2693 if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2695 if(0L == nPoly)
2697 const basegfx::B2DPolygon aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2698 const sal_uInt32 nPointCount(aCandidate.count());
2700 if(nPointCount)
2702 if(IsClosed())
2704 // when closed, RipPoint means to open the polygon at the selected point. To
2705 // be able to do that, it is necessary to make the selected point the first one
2706 basegfx::B2DPolygon aNewPolygon(basegfx::tools::makeStartPoint(aCandidate, nPnt));
2707 SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2708 ToggleClosed();
2710 // give back new position of old start point (historical reasons)
2711 rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2713 else
2715 if(nPointCount >= 3L && nPnt != 0L && nPnt + 1L < nPointCount)
2717 // split in two objects at point nPnt
2718 basegfx::B2DPolygon aSplitPolyA(aCandidate, 0L, nPnt + 1L);
2719 SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2721 pNewObj = (SdrPathObj*)Clone();
2722 basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2723 pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2730 return pNewObj;
2733 SdrObject* SdrPathObj::DoConvertToPolyObj(sal_Bool bBezier) const
2735 // #i89784# check for FontWork with activated HideContour
2736 const drawinglayer::attribute::SdrTextAttribute aText(
2737 drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2738 const bool bHideContour(
2739 !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2741 SdrObject* pRet = bHideContour ?
2743 ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
2745 SdrPathObj* pPath = PTR_CAST(SdrPathObj, pRet);
2747 if(pPath)
2749 if(pPath->GetPathPoly().areControlPointsUsed())
2751 if(!bBezier)
2753 // reduce all bezier curves
2754 pPath->SetPathPoly(basegfx::tools::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2757 else
2759 if(bBezier)
2761 // create bezier curves
2762 pPath->SetPathPoly(basegfx::tools::expandToCurve(pPath->GetPathPoly()));
2767 pRet = ImpConvertAddText(pRet, bBezier);
2769 return pRet;
2772 SdrObjGeoData* SdrPathObj::NewGeoData() const
2774 return new SdrPathObjGeoData;
2777 void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
2779 SdrTextObj::SaveGeoData(rGeo);
2780 SdrPathObjGeoData& rPGeo = (SdrPathObjGeoData&) rGeo;
2781 rPGeo.maPathPolygon=GetPathPoly();
2782 rPGeo.meKind=meKind;
2785 void SdrPathObj::RestGeoData(const SdrObjGeoData& rGeo)
2787 SdrTextObj::RestGeoData(rGeo);
2788 SdrPathObjGeoData& rPGeo=(SdrPathObjGeoData&)rGeo;
2789 maPathPolygon=rPGeo.maPathPolygon;
2790 meKind=rPGeo.meKind;
2791 ImpForceKind(); // to set bClosed (among other things)
2794 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2796 if(GetPathPoly() != rPathPoly)
2798 maPathPolygon=rPathPoly;
2799 ImpForceKind();
2800 SetRectsDirty();
2804 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2806 if(GetPathPoly() != rPathPoly)
2808 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
2809 NbcSetPathPoly(rPathPoly);
2810 SetChanged();
2811 BroadcastObjectChange();
2812 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
2816 void SdrPathObj::ToggleClosed()
2818 Rectangle aBoundRect0;
2819 if(pUserCall != NULL)
2820 aBoundRect0 = GetLastBoundRect();
2821 ImpSetClosed(!IsClosed()); // set new ObjKind
2822 ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2823 SetRectsDirty();
2824 SetChanged();
2825 BroadcastObjectChange();
2826 SendUserCall(SDRUSERCALL_RESIZE, aBoundRect0);
2829 // for friend class SdrPolyEditView in some compilers:
2830 void SdrPathObj::SetRectsDirty(sal_Bool bNotMyself)
2832 SdrTextObj::SetRectsDirty(bNotMyself);
2835 ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
2837 if(!mpDAC)
2839 ((SdrPathObj*)this)->mpDAC = new ImpPathForDragAndCreate(*((SdrPathObj*)this));
2842 return *mpDAC;
2845 void SdrPathObj::impDeleteDAC() const
2847 if(mpDAC)
2849 delete mpDAC;
2850 ((SdrPathObj*)this)->mpDAC = 0L;
2854 ////////////////////////////////////////////////////////////////////////////////////////////////////
2856 // transformation interface for StarOfficeAPI. This implements support for
2857 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2858 // moment it contains a shearX, rotation and translation, but for setting all linear
2859 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2861 ////////////////////////////////////////////////////////////////////////////////////////////////////
2862 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2863 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2864 sal_Bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2866 double fRotate(0.0);
2867 double fShearX(0.0);
2868 basegfx::B2DTuple aScale(1.0, 1.0);
2869 basegfx::B2DTuple aTranslate(0.0, 0.0);
2871 if(GetPathPoly().count())
2873 // copy geometry
2874 basegfx::B2DHomMatrix aMoveToZeroMatrix;
2875 rPolyPolygon = GetPathPoly();
2877 if(OBJ_LINE == meKind)
2879 // ignore shear and rotate, just use scale and translate
2880 OSL_ENSURE(GetPathPoly().count() > 0L && GetPathPoly().getB2DPolygon(0L).count() > 1L, "OBJ_LINE with too few polygons (!)");
2881 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2882 // itself, else this method will no longer return the full polygon information (curve will
2883 // be lost)
2884 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2885 aScale = aPolyRangeNoCurve.getRange();
2886 aTranslate = aPolyRangeNoCurve.getMinimum();
2888 // define matrix for move polygon to zero point
2889 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2891 else
2893 if(aGeo.nShearWink || aGeo.nDrehWink)
2895 // get rotate and shear in drawingLayer notation
2896 fRotate = aGeo.nDrehWink * F_PI18000;
2897 fShearX = aGeo.nShearWink * F_PI18000;
2899 // build mathematically correct (negative shear and rotate) object transform
2900 // containing shear and rotate to extract unsheared, unrotated polygon
2901 basegfx::B2DHomMatrix aObjectMatrix;
2902 aObjectMatrix.shearX(tan((36000 - aGeo.nShearWink) * F_PI18000));
2903 aObjectMatrix.rotate((36000 - aGeo.nDrehWink) * F_PI18000);
2905 // create inverse from it and back-transform polygon
2906 basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2907 aInvObjectMatrix.invert();
2908 rPolyPolygon.transform(aInvObjectMatrix);
2910 // get range from unsheared, unrotated polygon and extract scale and translate.
2911 // transform topLeft from it back to transformed state to get original
2912 // topLeft (rotation center)
2913 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2914 // itself, else this method will no longer return the full polygon information (curve will
2915 // be lost)
2916 const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2917 aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2918 aScale = aCorrectedRangeNoCurve.getRange();
2920 // define matrix for move polygon to zero point
2921 // #i112280# Added missing minus for Y-Translation
2922 aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2924 else
2926 // get scale and translate from unsheared, unrotated polygon
2927 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2928 // itself, else this method will no longer return the full polygon information (curve will
2929 // be lost)
2930 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2931 aScale = aPolyRangeNoCurve.getRange();
2932 aTranslate = aPolyRangeNoCurve.getMinimum();
2934 // define matrix for move polygon to zero point
2935 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2939 // move polygon to zero point with pre-defined matrix
2940 rPolyPolygon.transform(aMoveToZeroMatrix);
2943 // position maybe relative to anchorpos, convert
2944 if( pModel && pModel->IsWriter() )
2946 if(GetAnchorPos().X() || GetAnchorPos().Y())
2948 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2952 // force MapUnit to 100th mm
2953 SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
2954 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
2956 switch(eMapUnit)
2958 case SFX_MAPUNIT_TWIP :
2960 // position
2961 aTranslate.setX(ImplTwipsToMM(aTranslate.getX()));
2962 aTranslate.setY(ImplTwipsToMM(aTranslate.getY()));
2964 // size
2965 aScale.setX(ImplTwipsToMM(aScale.getX()));
2966 aScale.setY(ImplTwipsToMM(aScale.getY()));
2968 // polygon
2969 basegfx::B2DHomMatrix aTwipsToMM;
2970 const double fFactorTwipsToMM(127.0 / 72.0);
2971 aTwipsToMM.scale(fFactorTwipsToMM, fFactorTwipsToMM);
2972 rPolyPolygon.transform(aTwipsToMM);
2974 break;
2976 default:
2978 OSL_FAIL("TRGetBaseGeometry: Missing unit translation to 100th mm!");
2983 // build return value matrix
2984 rMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
2985 aScale,
2986 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2987 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
2988 aTranslate);
2990 return sal_True;
2993 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2994 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2995 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2996 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
2998 // break up matrix
2999 basegfx::B2DTuple aScale;
3000 basegfx::B2DTuple aTranslate;
3001 double fRotate, fShearX;
3002 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
3004 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
3005 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
3006 if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
3008 aScale.setX(fabs(aScale.getX()));
3009 aScale.setY(fabs(aScale.getY()));
3010 fRotate = fmod(fRotate + F_PI, F_2PI);
3013 // copy poly
3014 basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
3016 // reset object shear and rotations
3017 aGeo.nDrehWink = 0;
3018 aGeo.RecalcSinCos();
3019 aGeo.nShearWink = 0;
3020 aGeo.RecalcTan();
3022 // force metric to pool metric
3023 SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
3024 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
3026 switch(eMapUnit)
3028 case SFX_MAPUNIT_TWIP :
3030 // position
3031 aTranslate.setX(ImplMMToTwips(aTranslate.getX()));
3032 aTranslate.setY(ImplMMToTwips(aTranslate.getY()));
3034 // size
3035 aScale.setX(ImplMMToTwips(aScale.getX()));
3036 aScale.setY(ImplMMToTwips(aScale.getY()));
3038 // polygon
3039 basegfx::B2DHomMatrix aMMToTwips;
3040 const double fFactorMMToTwips(72.0 / 127.0);
3041 aMMToTwips.scale(fFactorMMToTwips, fFactorMMToTwips);
3042 aNewPolyPolygon.transform(aMMToTwips);
3044 break;
3046 default:
3048 OSL_FAIL("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
3053 if( pModel && pModel->IsWriter() )
3055 // if anchor is used, make position relative to it
3056 if(GetAnchorPos().X() || GetAnchorPos().Y())
3058 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3062 // create transformation for polygon, set values at aGeo direct
3063 basegfx::B2DHomMatrix aTransform;
3065 // #i75086#
3066 // Given polygon is already scaled (for historical reasons), but not mirrored yet.
3067 // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
3068 if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0))
3070 aTransform.scale(
3071 basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0,
3072 basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
3075 if(!basegfx::fTools::equalZero(fShearX))
3077 aTransform.shearX(tan(-atan(fShearX)));
3078 aGeo.nShearWink = FRound(atan(fShearX) / F_PI18000);
3079 aGeo.RecalcTan();
3082 if(!basegfx::fTools::equalZero(fRotate))
3084 // #i78696#
3085 // fRotate is mathematically correct for linear transformations, so it's
3086 // the one to use for the geometry change
3087 aTransform.rotate(fRotate);
3089 // #i78696#
3090 // fRotate is mathematically correct, but aGeoStat.nDrehWink is
3091 // mirrored -> mirror value here
3092 aGeo.nDrehWink = NormAngle360(FRound(-fRotate / F_PI18000));
3093 aGeo.RecalcSinCos();
3096 if(!aTranslate.equalZero())
3098 // #i39529# absolute positioning, so get current position (without control points (!))
3099 const basegfx::B2DRange aCurrentRange(basegfx::tools::getRange(aNewPolyPolygon));
3100 aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
3103 // transform polygon and trigger change
3104 aNewPolyPolygon.transform(aTransform);
3105 SetPathPoly(aNewPolyPolygon);
3108 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */