bump product version to 4.1.6.2
[LibreOffice.git] / svx / source / svdraw / svdopath.cxx
blobdbbaaef0cd99735767426312d468d14382eed8b9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <tools/bigint.hxx>
22 #include <tools/helpers.hxx>
23 #include <svx/svdopath.hxx>
24 #include <math.h>
25 #include <svx/xpool.hxx>
26 #include <svx/xpoly.hxx>
27 #include <svx/svdattr.hxx>
28 #include <svx/svdtrans.hxx>
29 #include <svx/svdetc.hxx>
30 #include <svx/svddrag.hxx>
31 #include <svx/svdmodel.hxx>
32 #include <svx/svdpage.hxx>
33 #include <svx/svdhdl.hxx>
34 #include <svx/svdview.hxx> // for MovCreate when using curves
35 #include "svx/svdglob.hxx" // Stringcache
36 #include "svx/svdstr.hrc" // the object's name
38 #include <svx/xlnwtit.hxx>
39 #include <svx/xlnclit.hxx>
40 #include <svx/xflclit.hxx>
41 #include <svx/svdogrp.hxx>
42 #include <svx/polypolygoneditor.hxx>
43 #include <svx/xlntrit.hxx>
44 #include <svx/sdr/contact/viewcontactofsdrpathobj.hxx>
45 #include <basegfx/matrix/b2dhommatrix.hxx>
46 #include "svdconv.hxx"
47 #include <basegfx/point/b2dpoint.hxx>
48 #include <basegfx/polygon/b2dpolypolygontools.hxx>
49 #include <basegfx/range/b2drange.hxx>
50 #include <basegfx/curve/b2dcubicbezier.hxx>
51 #include <basegfx/polygon/b2dpolygontools.hxx>
52 #include <svx/sdr/attribute/sdrtextattribute.hxx>
53 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
54 #include <basegfx/matrix/b2dhommatrixtools.hxx>
55 #include <svx/sdr/attribute/sdrformtextattribute.hxx>
57 using namespace sdr;
59 inline sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
61 if (nPnt>0) {
62 nPnt--;
63 } else {
64 nPnt=nPntMax;
65 if (bClosed) nPnt--;
67 return nPnt;
70 inline sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
72 nPnt++;
73 if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
74 return nPnt;
77 struct ImpSdrPathDragData : public SdrDragStatUserData
79 XPolygon aXP; // section of the original polygon
80 bool bValid; // FALSE = too few points
81 bool bClosed; // closed object?
82 sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
83 sal_uInt16 nPnt; // number of point in the above polygon
84 sal_uInt16 nPntAnz; // number of points of the polygon
85 sal_uInt16 nPntMax; // maximum index
86 bool bBegPnt; // dragged point is first point of a Polyline
87 bool bEndPnt; // dragged point is finishing point of a Polyline
88 sal_uInt16 nPrevPnt; // index of previous point
89 sal_uInt16 nNextPnt; // index of next point
90 bool bPrevIsBegPnt; // previous point is first point of a Polyline
91 bool bNextIsEndPnt; // next point is first point of a Polyline
92 sal_uInt16 nPrevPrevPnt; // index of point before previous point
93 sal_uInt16 nNextNextPnt; // index of point after next point
94 bool bControl; // point is a control point
95 bool bIsPrevControl; // point is a control point before a support point
96 bool bIsNextControl; // point is a control point after a support point
97 bool bPrevIsControl; // if nPnt is a support point: a control point comes before
98 bool bNextIsControl; // if nPnt is a support point: a control point comes after
99 sal_uInt16 nPrevPrevPnt0;
100 sal_uInt16 nPrevPnt0;
101 sal_uInt16 nPnt0;
102 sal_uInt16 nNextPnt0;
103 sal_uInt16 nNextNextPnt0;
104 bool bEliminate; // delete point? (is set by MovDrag)
106 bool mbMultiPointDrag;
107 const XPolyPolygon maOrig;
108 XPolyPolygon maMove;
109 std::vector<SdrHdl*> maHandles;
111 public:
112 ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
113 void ResetPoly(const SdrPathObj& rPO);
114 bool IsMultiPointDrag() const { return mbMultiPointDrag; }
117 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
118 : aXP(5),
119 mbMultiPointDrag(bMuPoDr),
120 maOrig(rPO.GetPathPoly()),
121 maHandles(0)
123 if(mbMultiPointDrag)
125 const SdrMarkView& rMarkView = *rDrag.GetView();
126 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
127 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
128 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
130 for(sal_uInt32 a(0); a < nHdlCount; a++)
132 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
134 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
136 maHandles.push_back(pTestHdl);
140 maMove = maOrig;
141 bValid = sal_True;
143 else
145 bValid=sal_False;
146 bClosed=rPO.IsClosed(); // closed object?
147 nPoly=(sal_uInt16)rHdl.GetPolyNum(); // number of the polygon in the PolyPolygon
148 nPnt=(sal_uInt16)rHdl.GetPointNum(); // number of points in the above polygon
149 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
150 nPntAnz=aTmpXP.GetPointCount(); // number of point of the polygon
151 if (nPntAnz==0 || (bClosed && nPntAnz==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
152 nPntMax=nPntAnz-1; // maximum index
153 bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
154 bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
155 if (bClosed && nPntAnz<=3) { // if polygon is only a line
156 bBegPnt=(nPntAnz<3) || nPnt==0;
157 bEndPnt=(nPntAnz<3) || nPnt==nPntMax-1;
159 nPrevPnt=nPnt; // index of previous point
160 nNextPnt=nPnt; // index of next point
161 if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
162 if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
163 bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
164 bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
165 nPrevPrevPnt=nPnt; // index of point before previous point
166 nNextNextPnt=nPnt; // index of point after next point
167 if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
168 if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
169 bControl=rHdl.IsPlusHdl(); // point is a control point
170 bIsPrevControl=sal_False; // point is a control point before a support point
171 bIsNextControl=sal_False; // point is a control point after a support point
172 bPrevIsControl=sal_False; // if nPnt is a support point: a control point comes before
173 bNextIsControl=sal_False; // if nPnt is a support point: a control point comes after
174 if (bControl) {
175 bIsPrevControl=aTmpXP.IsControl(nPrevPnt);
176 bIsNextControl=!bIsPrevControl;
177 } else {
178 bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==XPOLY_CONTROL;
179 bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==XPOLY_CONTROL;
181 nPrevPrevPnt0=nPrevPrevPnt;
182 nPrevPnt0 =nPrevPnt;
183 nPnt0 =nPnt;
184 nNextPnt0 =nNextPnt;
185 nNextNextPnt0=nNextNextPnt;
186 nPrevPrevPnt=0;
187 nPrevPnt=1;
188 nPnt=2;
189 nNextPnt=3;
190 nNextNextPnt=4;
191 bEliminate=sal_False;
192 ResetPoly(rPO);
193 bValid=sal_True;
197 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
199 const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
200 aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
201 aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
202 aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
203 aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
204 aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
207 /*************************************************************************/
209 struct ImpPathCreateUser : public SdrDragStatUserData
211 Point aBezControl0;
212 Point aBezStart;
213 Point aBezCtrl1;
214 Point aBezCtrl2;
215 Point aBezEnd;
216 Point aCircStart;
217 Point aCircEnd;
218 Point aCircCenter;
219 Point aLineStart;
220 Point aLineEnd;
221 Point aRectP1;
222 Point aRectP2;
223 Point aRectP3;
224 long nCircRadius;
225 long nCircStWink;
226 long nCircRelWink;
227 bool bBezier;
228 bool bBezHasCtrl0;
229 bool bCurve;
230 bool bCircle;
231 bool bAngleSnap;
232 bool bLine;
233 bool bLine90;
234 bool bRect;
235 bool bMixedCreate;
236 sal_uInt16 nBezierStartPoint;
237 SdrObjKind eStartKind;
238 SdrObjKind eAktKind;
240 public:
241 ImpPathCreateUser(): nCircRadius(0),nCircStWink(0),nCircRelWink(0),
242 bBezier(sal_False),bBezHasCtrl0(sal_False),bCurve(sal_False),bCircle(sal_False),bAngleSnap(sal_False),bLine(sal_False),bLine90(sal_False),bRect(sal_False),
243 bMixedCreate(sal_False),nBezierStartPoint(0),eStartKind(OBJ_NONE),eAktKind(OBJ_NONE) { }
245 void ResetFormFlags() { bBezier=sal_False; bCurve=sal_False; bCircle=sal_False; bLine=sal_False; bRect=sal_False; }
246 bool IsFormFlag() const { return bBezier || bCurve || bCircle || bLine || bRect; }
247 XPolygon GetFormPoly() const;
248 bool CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
249 XPolygon GetBezierPoly() const;
250 XPolygon GetCurvePoly() const { return XPolygon(); }
251 bool CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
252 XPolygon GetCirclePoly() const;
253 bool CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
254 Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView* pView) const;
255 XPolygon GetLinePoly() const;
256 bool CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
257 XPolygon GetRectPoly() const;
260 XPolygon ImpPathCreateUser::GetFormPoly() const
262 if (bBezier) return GetBezierPoly();
263 if (bCurve) return GetCurvePoly();
264 if (bCircle) return GetCirclePoly();
265 if (bLine) return GetLinePoly();
266 if (bRect) return GetRectPoly();
267 return XPolygon();
270 bool ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
272 bool bRet = true;
273 aBezStart=rP1;
274 aBezCtrl1=rP1+rDir;
275 aBezCtrl2=rP2;
277 // #i21479#
278 // Also copy the end point when no end point is set yet
279 if (!bMouseDown || (0L == aBezEnd.X() && 0L == aBezEnd.Y())) aBezEnd=rP2;
281 bBezier=bRet;
282 return bRet;
285 XPolygon ImpPathCreateUser::GetBezierPoly() const
287 XPolygon aXP(4);
288 aXP[0]=aBezStart; aXP.SetFlags(0,XPOLY_SMOOTH);
289 aXP[1]=aBezCtrl1; aXP.SetFlags(1,XPOLY_CONTROL);
290 aXP[2]=aBezCtrl2; aXP.SetFlags(2,XPOLY_CONTROL);
291 aXP[3]=aBezEnd;
292 return aXP;
295 bool ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
297 long nTangAngle=GetAngle(rDir);
298 aCircStart=rP1;
299 aCircEnd=rP2;
300 aCircCenter=rP1;
301 long dx=rP2.X()-rP1.X();
302 long dy=rP2.Y()-rP1.Y();
303 long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
304 dAngle=NormAngle360(dAngle);
305 long nTmpAngle=NormAngle360(9000-dAngle);
306 bool bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
307 long nRad=0;
308 if (bRet) {
309 double cs=cos(nTmpAngle*nPi180);
310 double nR=(double)GetLen(Point(dx,dy))/cs/2;
311 nRad=std::abs(Round(nR));
313 if (dAngle<18000) {
314 nCircStWink=NormAngle360(nTangAngle-9000);
315 nCircRelWink=NormAngle360(2*dAngle);
316 aCircCenter.X()+=Round(nRad*cos((nTangAngle+9000)*nPi180));
317 aCircCenter.Y()-=Round(nRad*sin((nTangAngle+9000)*nPi180));
318 } else {
319 nCircStWink=NormAngle360(nTangAngle+9000);
320 nCircRelWink=-NormAngle360(36000-2*dAngle);
321 aCircCenter.X()+=Round(nRad*cos((nTangAngle-9000)*nPi180));
322 aCircCenter.Y()-=Round(nRad*sin((nTangAngle-9000)*nPi180));
324 bAngleSnap=pView!=NULL && pView->IsAngleSnapEnabled();
325 if (bAngleSnap) {
326 long nSA=pView->GetSnapAngle();
327 if (nSA!=0) { // angle snapping
328 bool bNeg=nCircRelWink<0;
329 if (bNeg) nCircRelWink=-nCircRelWink;
330 nCircRelWink+=nSA/2;
331 nCircRelWink/=nSA;
332 nCircRelWink*=nSA;
333 nCircRelWink=NormAngle360(nCircRelWink);
334 if (bNeg) nCircRelWink=-nCircRelWink;
337 nCircRadius=nRad;
338 if (nRad==0 || std::abs(nCircRelWink)<5) bRet=sal_False;
339 bCircle=bRet;
340 return bRet;
343 XPolygon ImpPathCreateUser::GetCirclePoly() const
345 if (nCircRelWink>=0) {
346 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
347 sal_uInt16((nCircStWink+5)/10),sal_uInt16((nCircStWink+nCircRelWink+5)/10),sal_False);
348 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
349 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
350 return aXP;
351 } else {
352 XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
353 sal_uInt16(NormAngle360(nCircStWink+nCircRelWink+5)/10),sal_uInt16((nCircStWink+5)/10),sal_False);
354 sal_uInt16 nAnz=aXP.GetPointCount();
355 for (sal_uInt16 nNum=nAnz/2; nNum>0;) {
356 nNum--; // reverse XPoly's order of points
357 sal_uInt16 n2=nAnz-nNum-1;
358 Point aPt(aXP[nNum]);
359 aXP[nNum]=aXP[n2];
360 aXP[n2]=aPt;
362 aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
363 if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
364 return aXP;
368 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView* pView) const
370 long x=aCsr.X(),x1=x,x2=x;
371 long y=aCsr.Y(),y1=y,y2=y;
372 bool bHLin=nDirY==0;
373 bool bVLin=nDirX==0;
374 if (bHLin) y=0;
375 else if (bVLin) x=0;
376 else {
377 x1=BigMulDiv(y,nDirX,nDirY);
378 y2=BigMulDiv(x,nDirY,nDirX);
379 long l1=std::abs(x1)+std::abs(y1);
380 long l2=std::abs(x2)+std::abs(y2);
381 if ((l1<=l2) != (pView!=NULL && pView->IsBigOrtho())) {
382 x=x1; y=y1;
383 } else {
384 x=x2; y=y2;
387 return Point(x,y);
390 bool ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
392 aLineStart=rP1;
393 aLineEnd=rP2;
394 bLine90=sal_False;
395 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=sal_False; return sal_False; }
396 Point aTmpPt(rP2-rP1);
397 long nDirX=rDir.X();
398 long nDirY=rDir.Y();
399 Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
400 Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
401 if (pView!=NULL && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
402 bLine90=nQ1>2*nQ2;
403 if (!bLine90) { // smooth transition
404 aLineEnd+=aP1;
405 } else { // rectangular transition
406 aLineEnd+=aP2;
408 bLine=sal_True;
409 return sal_True;
412 XPolygon ImpPathCreateUser::GetLinePoly() const
414 XPolygon aXP(2);
415 aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,XPOLY_SMOOTH);
416 aXP[1]=aLineEnd;
417 return aXP;
420 bool ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
422 aRectP1=rP1;
423 aRectP2=rP1;
424 aRectP3=rP2;
425 if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=sal_False; return sal_False; }
426 Point aTmpPt(rP2-rP1);
427 long nDirX=rDir.X();
428 long nDirY=rDir.Y();
429 long x=aTmpPt.X();
430 long y=aTmpPt.Y();
431 bool bHLin=nDirY==0;
432 bool bVLin=nDirX==0;
433 if (bHLin) y=0;
434 else if (bVLin) x=0;
435 else {
436 y=BigMulDiv(x,nDirY,nDirX);
437 long nHypLen=aTmpPt.Y()-y;
438 long nTangAngle=-GetAngle(rDir);
439 // sin=g/h, g=h*sin
440 double a=nTangAngle*nPi180;
441 double sn=sin(a);
442 double cs=cos(a);
443 double nGKathLen=nHypLen*sn;
444 y+=Round(nGKathLen*sn);
445 x+=Round(nGKathLen*cs);
447 aRectP2.X()+=x;
448 aRectP2.Y()+=y;
449 if (pView!=NULL && pView->IsOrtho()) {
450 long dx1=aRectP2.X()-aRectP1.X(); long dx1a=std::abs(dx1);
451 long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=std::abs(dy1);
452 long dx2=aRectP3.X()-aRectP2.X(); long dx2a=std::abs(dx2);
453 long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=std::abs(dy2);
454 bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
455 if (b1MoreThan2 != pView->IsBigOrtho()) {
456 long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
457 long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
458 aRectP2.X()+=xtemp;
459 aRectP2.Y()+=ytemp;
460 aRectP3.X()+=xtemp;
461 aRectP3.Y()+=ytemp;
462 } else {
463 long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
464 long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
465 aRectP3.X()+=xtemp;
466 aRectP3.Y()+=ytemp;
469 bRect=sal_True;
470 return sal_True;
473 XPolygon ImpPathCreateUser::GetRectPoly() const
475 XPolygon aXP(3);
476 aXP[0]=aRectP1; aXP.SetFlags(0,XPOLY_SMOOTH);
477 aXP[1]=aRectP2;
478 if (aRectP3!=aRectP2) aXP[2]=aRectP3;
479 return aXP;
482 /*************************************************************************/
484 class ImpPathForDragAndCreate
486 SdrPathObj& mrSdrPathObject;
487 XPolyPolygon aPathPolygon;
488 SdrObjKind meObjectKind;
489 ImpSdrPathDragData* mpSdrPathDragData;
490 bool mbCreating;
492 public:
493 ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
494 ~ImpPathForDragAndCreate();
496 // drag stuff
497 bool beginPathDrag( SdrDragStat& rDrag ) const;
498 bool movePathDrag( SdrDragStat& rDrag ) const;
499 bool endPathDrag( SdrDragStat& rDrag );
500 String getSpecialDragComment(const SdrDragStat& rDrag) const;
501 basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
503 // create stuff
504 bool BegCreate(SdrDragStat& rStat);
505 bool MovCreate(SdrDragStat& rStat);
506 bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
507 bool BckCreate(SdrDragStat& rStat);
508 void BrkCreate(SdrDragStat& rStat);
509 Pointer GetCreatePointer() const;
511 // helping stuff
512 bool IsClosed(SdrObjKind eKind) const { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
513 bool IsFreeHand(SdrObjKind eKind) const { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
514 bool IsBezier(SdrObjKind eKind) const { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
515 bool IsCreating() const { return mbCreating; }
517 // get the polygon
518 basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
519 basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag) const;
520 basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
523 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
524 : mrSdrPathObject(rSdrPathObject),
525 aPathPolygon(rSdrPathObject.GetPathPoly()),
526 meObjectKind(mrSdrPathObject.meKind),
527 mpSdrPathDragData(0),
528 mbCreating(false)
532 ImpPathForDragAndCreate::~ImpPathForDragAndCreate()
534 if(mpSdrPathDragData)
536 delete mpSdrPathDragData;
540 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat& rDrag ) const
542 const SdrHdl* pHdl=rDrag.GetHdl();
543 if(!pHdl)
544 return sal_False;
546 bool bMultiPointDrag(true);
548 if(aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()].IsControl((sal_uInt16)pHdl->GetPointNum()))
549 bMultiPointDrag = false;
551 if(bMultiPointDrag)
553 const SdrMarkView& rMarkView = *rDrag.GetView();
554 const SdrHdlList& rHdlList = rMarkView.GetHdlList();
555 const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
556 const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
557 sal_uInt32 nSelectedPoints(0);
559 for(sal_uInt32 a(0); a < nHdlCount; a++)
561 SdrHdl* pTestHdl = rHdlList.GetHdl(a);
563 if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
565 nSelectedPoints++;
569 if(nSelectedPoints <= 1)
570 bMultiPointDrag = false;
573 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag);
575 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
577 OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
578 delete mpSdrPathDragData;
579 ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = 0;
580 return false;
583 return true;
586 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
588 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
590 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
591 return false;
594 if(mpSdrPathDragData->IsMultiPointDrag())
596 Point aDelta(rDrag.GetNow() - rDrag.GetStart());
598 if(aDelta.X() || aDelta.Y())
600 for(sal_uInt32 a(0); a < mpSdrPathDragData->maHandles.size(); a++)
602 SdrHdl* pHandle = mpSdrPathDragData->maHandles[a];
603 const sal_uInt16 nPolyIndex((sal_uInt16)pHandle->GetPolyNum());
604 const sal_uInt16 nPointIndex((sal_uInt16)pHandle->GetPointNum());
605 const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
606 XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
607 const sal_uInt16 nPointCount(rOrig.GetPointCount());
608 bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
610 // move point itself
611 rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
613 // when point is first and poly closed, move close point, too.
614 if(nPointCount > 0 && !nPointIndex && bClosed)
616 rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
618 // when moving the last point it may be necessary to move the
619 // control point in front of this one, too.
620 if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
621 rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
624 // is a control point before this?
625 if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
627 // Yes, move it, too
628 rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
631 // is a control point after this?
632 if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
634 // Yes, move it, too
635 rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
640 else
642 mpSdrPathDragData->ResetPoly(mrSdrPathObject);
644 // copy certain data locally to use less code and have faster access times
645 bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
646 sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon
647 bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline
648 bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
649 sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
650 sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point
651 bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
652 bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
653 sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
654 sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point
655 bool bControl =mpSdrPathDragData->bControl ; // point is a control point
656 bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
657 bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
658 bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
660 // Ortho for lines/polygons: keep angle
661 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsOrtho()) {
662 bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
663 Point aPos(rDrag.GetNow()); // current position
664 Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
665 sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
666 Point aNeuPos1,aNeuPos2; // new alternative for aPos
667 bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
668 if (!bClosed && mpSdrPathDragData->nPntAnz>=2) { // minimum of 2 points for lines
669 if (!bBegPnt) nPnt1=nPrevPnt;
670 if (!bEndPnt) nPnt2=nNextPnt;
672 if (bClosed && mpSdrPathDragData->nPntAnz>=3) { // minimum of 3 points for polygon
673 nPnt1=nPrevPnt;
674 nPnt2=nNextPnt;
676 if (nPnt1!=0xFFFF && !bPrevIsControl) {
677 Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
678 long ndx0=aPnt.X()-aPnt1.X();
679 long ndy0=aPnt.Y()-aPnt1.Y();
680 bool bHLin=ndy0==0;
681 bool bVLin=ndx0==0;
682 if (!bHLin || !bVLin) {
683 long ndx=aPos.X()-aPnt1.X();
684 long ndy=aPos.Y()-aPnt1.Y();
685 bPnt1=sal_True;
686 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
687 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
688 bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
689 bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
690 if (bHor) ndy=long(ndy0*nXFact);
691 if (bVer) ndx=long(ndx0*nYFact);
692 aNeuPos1=aPnt1;
693 aNeuPos1.X()+=ndx;
694 aNeuPos1.Y()+=ndy;
697 if (nPnt2!=0xFFFF && !bNextIsControl) {
698 Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
699 long ndx0=aPnt.X()-aPnt2.X();
700 long ndy0=aPnt.Y()-aPnt2.Y();
701 bool bHLin=ndy0==0;
702 bool bVLin=ndx0==0;
703 if (!bHLin || !bVLin) {
704 long ndx=aPos.X()-aPnt2.X();
705 long ndy=aPos.Y()-aPnt2.Y();
706 bPnt2=sal_True;
707 double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
708 double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
709 bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
710 bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
711 if (bHor) ndy=long(ndy0*nXFact);
712 if (bVer) ndx=long(ndx0*nYFact);
713 aNeuPos2=aPnt2;
714 aNeuPos2.X()+=ndx;
715 aNeuPos2.Y()+=ndy;
718 if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
719 BigInt nX1(aNeuPos1.X()-aPos.X()); nX1*=nX1;
720 BigInt nY1(aNeuPos1.Y()-aPos.Y()); nY1*=nY1;
721 BigInt nX2(aNeuPos2.X()-aPos.X()); nX2*=nX2;
722 BigInt nY2(aNeuPos2.Y()-aPos.Y()); nY2*=nY2;
723 nX1+=nY1; // correction distance to square
724 nX2+=nY2; // correction distance to square
725 // let the alternative that allows fewer correction win
726 if (nX1<nX2) bPnt2=sal_False; else bPnt1=sal_False;
728 if (bPnt1) rDrag.Now()=aNeuPos1;
729 if (bPnt2) rDrag.Now()=aNeuPos2;
731 rDrag.SetActionRect(Rectangle(rDrag.GetNow(),rDrag.GetNow()));
733 // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
734 if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsEliminatePolyPoints() &&
735 !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
737 Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
738 aPt-=rDrag.GetNow();
739 long nWink1=GetAngle(aPt);
740 aPt=rDrag.GetNow();
741 aPt-=mpSdrPathDragData->aXP[nPrevPnt];
742 long nWink2=GetAngle(aPt);
743 long nDiff=nWink1-nWink2;
744 nDiff=std::abs(nDiff);
745 mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
746 if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
747 aPt=mpSdrPathDragData->aXP[nNextPnt];
748 aPt+=mpSdrPathDragData->aXP[nPrevPnt];
749 aPt/=2;
750 rDrag.Now()=aPt;
754 // we dragged by this distance
755 Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
757 /* There are 8 possible cases:
758 X 1. A control point neither on the left nor on the right.
759 o--X--o 2. There are control points on the left and the right, we are dragging a support point.
760 o--X 3. There is a control point on the left, we are dragging a support point.
761 X--o 4. There is a control point on the right, we are dragging a support point.
762 x--O--o 5. There are control points on the left and the right, we are dragging the left one.
763 x--O 6. There is a control point on the left, we are dragging it.
764 o--O--x 7. There are control points on the left and the right, we are dragging the right one.
765 O--x 8. There is a control point on the right, we are dragging it.
766 Note: modifying a line (not a curve!) might create a curve on the other end of the line
767 if Smooth is set there (with control points aligned to line).
770 mpSdrPathDragData->aXP[nPnt]+=aDiff;
772 // now check symmetric plus handles
773 if (bControl) { // cases 5,6,7,8
774 sal_uInt16 nSt=nPnt; // the associated support point
775 sal_uInt16 nFix=nPnt; // the opposing control point
776 if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
777 nSt=nPrevPnt;
778 nFix=nPrevPrevPnt;
779 } else {
780 nSt=nNextPnt;
781 nFix=nNextNextPnt;
783 if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
784 mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
788 if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
789 // move both control points
790 if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
791 if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
792 // align control point to line, if appropriate
793 if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
794 if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
795 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
797 if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
798 mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
801 // Now check the other ends of the line (nPnt+-1). If there is a
802 // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
803 // associated control point (nPnt+-2) has to be adapted.
804 if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
805 if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
806 mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
809 if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
810 if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
811 mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
817 return true;
820 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat& rDrag)
822 Point aLinePt1;
823 Point aLinePt2;
824 bool bLineGlueMirror(OBJ_LINE == meObjectKind);
825 if (bLineGlueMirror) {
826 XPolygon& rXP=aPathPolygon[0];
827 aLinePt1=rXP[0];
828 aLinePt2=rXP[1];
831 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
833 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
834 return false;
837 if(mpSdrPathDragData->IsMultiPointDrag())
839 aPathPolygon = mpSdrPathDragData->maMove;
841 else
843 const SdrHdl* pHdl=rDrag.GetHdl();
845 // reference the polygon
846 XPolygon& rXP=aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()];
848 // the 5 points that might have changed
849 if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
850 if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
851 if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
852 if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
853 rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
855 // for closed objects: last point has to be equal to first point
856 if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
858 if (mpSdrPathDragData->bEliminate)
860 basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
861 sal_uInt32 nPoly,nPnt;
863 if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
865 basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
866 aCandidate.remove(nPnt);
868 if((IsClosed(meObjectKind) && aCandidate.count() < 3L) || aCandidate.count() < 2L)
870 aTempPolyPolygon.remove(nPoly);
872 else
874 aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
878 aPathPolygon = XPolyPolygon(aTempPolyPolygon);
881 // adapt angle for text beneath a simple line
882 if (bLineGlueMirror)
884 Point aLinePt1_(aPathPolygon[0][0]);
885 Point aLinePt2_(aPathPolygon[0][1]);
886 bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
887 bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
888 if (bXMirr || bYMirr) {
889 Point aRef1(mrSdrPathObject.GetSnapRect().Center());
890 if (bXMirr) {
891 Point aRef2(aRef1);
892 aRef2.Y()++;
893 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
895 if (bYMirr) {
896 Point aRef2(aRef1);
897 aRef2.X()++;
898 mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
904 delete mpSdrPathDragData;
905 mpSdrPathDragData = 0;
907 return true;
911 String ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
913 XubString aStr;
914 const SdrHdl* pHdl = rDrag.GetHdl();
915 const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
917 if(bCreateComment && rDrag.GetUser())
919 // #i103058# re-add old creation comment mode
920 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
921 const SdrObjKind eKindMerk(meObjectKind);
922 mrSdrPathObject.meKind = pU->eAktKind;
923 OUString aTmp;
924 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewCreateObj, aTmp);
925 aStr = aTmp;
926 mrSdrPathObject.meKind = eKindMerk;
928 Point aPrev(rDrag.GetPrev());
929 Point aNow(rDrag.GetNow());
931 if(pU->bLine)
932 aNow = pU->aLineEnd;
934 aNow -= aPrev;
935 aStr.AppendAscii(" (");
937 OUString aMetr;
939 if(pU->bCircle)
941 mrSdrPathObject.GetModel()->TakeWinkStr(std::abs(pU->nCircRelWink), aMetr);
942 aStr.Append(aMetr);
943 aStr.AppendAscii(" r=");
944 mrSdrPathObject.GetModel()->TakeMetricStr(pU->nCircRadius, aMetr, sal_True);
945 aStr.Append(aMetr);
948 aStr.AppendAscii("dx=");
949 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X(), aMetr, sal_True);
950 aStr.Append(aMetr);
952 aStr.AppendAscii(" dy=");
953 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y(), aMetr, sal_True);
954 aStr.Append(aMetr);
956 if(!IsFreeHand(meObjectKind))
958 sal_Int32 nLen(GetLen(aNow));
959 aStr.AppendAscii(" l=");
960 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
961 aStr.Append(aMetr);
963 sal_Int32 nWink(GetAngle(aNow));
964 aStr += sal_Unicode(' ');
965 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
966 aStr.Append(aMetr);
969 aStr += sal_Unicode(')');
971 else if(!mrSdrPathObject.GetModel() || !pHdl)
973 // #i103058# fallback when no model and/or Handle, both needed
974 // for else-path
975 OUString aTmp;
976 mrSdrPathObject.ImpTakeDescriptionStr(STR_DragPathObj, aTmp);
977 aStr = aTmp;
979 else
981 // #i103058# standard for modification; model and handle needed
982 ImpSdrPathDragData* pDragData = mpSdrPathDragData;
984 if(!pDragData)
986 // getSpecialDragComment is also used from create, so fallback to GetUser()
987 // when mpSdrPathDragData is not set
988 pDragData = (ImpSdrPathDragData*)rDrag.GetUser();
991 if(!pDragData)
993 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
994 return String();
997 if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
999 // point of ...
1000 OUString aTmp;
1001 mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewMarkedPoint, aTmp);
1002 aStr = aTmp;
1004 // delete %O
1005 XubString aStr2(ImpGetResStr(STR_EditDelete));
1007 // UNICODE: delete point of ...
1008 aStr2.SearchAndReplaceAscii("%1", aStr);
1010 return aStr2;
1013 // dx=0.00 dy=0.00 -- both sides bezier
1014 // dx=0.00 dy=0.00 l=0.00 0.00° -- one bezier/lever on one side, a start, or an ending
1015 // dx=0.00 dy=0.00 l=0.00 0.00° / l=0.00 0.00° -- in between
1016 OUString aMetr;
1017 Point aBeg(rDrag.GetStart());
1018 Point aNow(rDrag.GetNow());
1020 aStr = String();
1021 aStr.AppendAscii("dx=");
1022 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X() - aBeg.X(), aMetr, sal_True);
1023 aStr.Append(aMetr);
1025 aStr.AppendAscii(" dy=");
1026 mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y() - aBeg.Y(), aMetr, sal_True);
1027 aStr.Append(aMetr);
1029 if(!pDragData->IsMultiPointDrag())
1031 sal_uInt16 nPntNum((sal_uInt16)pHdl->GetPointNum());
1032 const XPolygon& rXPoly = aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1033 sal_uInt16 nPntAnz((sal_uInt16)rXPoly.GetPointCount());
1034 sal_Bool bClose(IsClosed(meObjectKind));
1036 if(bClose)
1037 nPntAnz--;
1039 if(pHdl->IsPlusHdl())
1041 // lever
1042 sal_uInt16 nRef(nPntNum);
1044 if(rXPoly.IsControl(nPntNum + 1))
1045 nRef--;
1046 else
1047 nRef++;
1049 aNow -= rXPoly[nRef];
1051 sal_Int32 nLen(GetLen(aNow));
1052 aStr.AppendAscii(" l=");
1053 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1054 aStr.Append(aMetr);
1056 sal_Int32 nWink(GetAngle(aNow));
1057 aStr += sal_Unicode(' ');
1058 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1059 aStr.Append(aMetr);
1061 else if(nPntAnz > 1)
1063 sal_uInt16 nPntMax(nPntAnz - 1);
1064 sal_Bool bIsClosed(IsClosed(meObjectKind));
1065 bool bPt1(nPntNum > 0);
1066 bool bPt2(nPntNum < nPntMax);
1068 if(bIsClosed && nPntAnz > 2)
1070 bPt1 = true;
1071 bPt2 = true;
1074 sal_uInt16 nPt1,nPt2;
1076 if(nPntNum > 0)
1077 nPt1 = nPntNum - 1;
1078 else
1079 nPt1 = nPntMax;
1081 if(nPntNum < nPntMax)
1082 nPt2 = nPntNum + 1;
1083 else
1084 nPt2 = 0;
1086 if(bPt1 && rXPoly.IsControl(nPt1))
1087 bPt1 = false; // don't display
1089 if(bPt2 && rXPoly.IsControl(nPt2))
1090 bPt2 = false; // of bezier data
1092 if(bPt1)
1094 Point aPt(aNow);
1095 aPt -= rXPoly[nPt1];
1097 sal_Int32 nLen(GetLen(aPt));
1098 aStr.AppendAscii(" l=");
1099 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1100 aStr += aMetr;
1102 sal_Int32 nWink(GetAngle(aPt));
1103 aStr += sal_Unicode(' ');
1104 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1105 aStr += aMetr;
1108 if(bPt2)
1110 if(bPt1)
1111 aStr.AppendAscii(" / ");
1112 else
1113 aStr.AppendAscii(" ");
1115 Point aPt(aNow);
1116 aPt -= rXPoly[nPt2];
1118 sal_Int32 nLen(GetLen(aPt));
1119 aStr.AppendAscii("l=");
1120 mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, sal_True);
1121 aStr += aMetr;
1123 sal_Int32 nWink(GetAngle(aPt));
1124 aStr += sal_Unicode(' ');
1125 mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
1126 aStr += aMetr;
1132 return aStr;
1135 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
1137 if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1139 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1140 return basegfx::B2DPolyPolygon();
1143 XPolyPolygon aRetval;
1145 if(mpSdrPathDragData->IsMultiPointDrag())
1147 aRetval.Insert(mpSdrPathDragData->maMove);
1149 else
1151 const XPolygon& rXP=aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
1152 if (rXP.GetPointCount()<=2) {
1153 XPolygon aXPoly(rXP);
1154 aXPoly[(sal_uInt16)rDrag.GetHdl()->GetPointNum()]=rDrag.GetNow();
1155 aRetval.Insert(aXPoly);
1156 return aRetval.getB2DPolyPolygon();
1158 // copy certain data locally to use less code and have faster access times
1159 bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
1160 sal_uInt16 nPntAnz =mpSdrPathDragData->nPntAnz ; // number of points
1161 sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
1162 bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
1163 bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
1164 sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
1165 sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
1166 bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
1167 bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
1168 sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
1169 sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
1170 bool bControl =mpSdrPathDragData->bControl ; // point is a control point
1171 bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
1172 bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
1173 bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
1174 XPolygon aXPoly(mpSdrPathDragData->aXP);
1175 XPolygon aLine1(2);
1176 XPolygon aLine2(2);
1177 XPolygon aLine3(2);
1178 XPolygon aLine4(2);
1179 if (bControl) {
1180 aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1181 if (bIsNextControl) { // is this a control point after the support point?
1182 aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1183 aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1184 aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1185 if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1186 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1187 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1188 // leverage lines for the opposing curve segment
1189 aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1190 aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1191 aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1192 aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1193 } else {
1194 aXPoly.Remove(0,1);
1196 } else { // else this is a control point before a support point
1197 aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1198 aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1199 aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1200 if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1201 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1202 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1203 // leverage lines for the opposing curve segment
1204 aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1205 aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1206 aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1207 aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1208 } else {
1209 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1212 } else { // else is not a control point
1213 if (mpSdrPathDragData->bEliminate) {
1214 aXPoly.Remove(2,1);
1216 if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_NORMAL);
1217 else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1218 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
1219 aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
1220 } else {
1221 aXPoly.Remove(0,1);
1222 if (bBegPnt) aXPoly.Remove(0,1);
1224 if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_NORMAL);
1225 else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1226 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
1227 aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
1228 } else {
1229 aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1230 if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1232 if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1233 if (aXPoly.GetPointCount()>nPntAnz && aXPoly.IsControl(1)) {
1234 sal_uInt16 a=aXPoly.GetPointCount();
1235 aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1236 aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1237 aXPoly.Remove(0,3);
1241 aRetval.Insert(aXPoly);
1242 if (aLine1.GetPointCount()>1) aRetval.Insert(aLine1);
1243 if (aLine2.GetPointCount()>1) aRetval.Insert(aLine2);
1244 if (aLine3.GetPointCount()>1) aRetval.Insert(aLine3);
1245 if (aLine4.GetPointCount()>1) aRetval.Insert(aLine4);
1248 return aRetval.getB2DPolyPolygon();
1251 bool ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
1253 bool bFreeHand(IsFreeHand(meObjectKind));
1254 rStat.SetNoSnap(bFreeHand);
1255 rStat.SetOrtho8Possible();
1256 aPathPolygon.Clear();
1257 mbCreating=sal_True;
1258 bool bMakeStartPoint = true;
1259 SdrView* pView=rStat.GetView();
1260 if (pView!=NULL && pView->IsUseIncompatiblePathCreateInterface() &&
1261 (meObjectKind==OBJ_POLY || meObjectKind==OBJ_PLIN || meObjectKind==OBJ_PATHLINE || meObjectKind==OBJ_PATHFILL)) {
1262 bMakeStartPoint = false;
1264 aPathPolygon.Insert(XPolygon());
1265 aPathPolygon[0][0]=rStat.GetStart();
1266 if (bMakeStartPoint) {
1267 aPathPolygon[0][1]=rStat.GetNow();
1269 ImpPathCreateUser* pU=new ImpPathCreateUser;
1270 pU->eStartKind=meObjectKind;
1271 pU->eAktKind=meObjectKind;
1272 rStat.SetUser(pU);
1273 return true;
1276 bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
1278 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1279 SdrView* pView=rStat.GetView();
1280 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1281 if (pView!=NULL && pView->IsCreateMode()) {
1282 // switch to different CreateTool, if appropriate
1283 sal_uInt16 nIdent;
1284 sal_uInt32 nInvent;
1285 pView->TakeCurrentObj(nIdent,nInvent);
1286 if (nInvent==SdrInventor && pU->eAktKind!=(SdrObjKind)nIdent) {
1287 SdrObjKind eNewKind=(SdrObjKind)nIdent;
1288 switch (eNewKind) {
1289 case OBJ_CARC: case OBJ_CIRC: case OBJ_CCUT: case OBJ_SECT: eNewKind=OBJ_CARC;
1290 case OBJ_RECT:
1291 case OBJ_LINE: case OBJ_PLIN: case OBJ_POLY:
1292 case OBJ_PATHLINE: case OBJ_PATHFILL:
1293 case OBJ_FREELINE: case OBJ_FREEFILL:
1294 case OBJ_SPLNLINE: case OBJ_SPLNFILL: {
1295 pU->eAktKind=eNewKind;
1296 pU->bMixedCreate=sal_True;
1297 pU->nBezierStartPoint=rXPoly.GetPointCount();
1298 if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1299 } break;
1300 default: break;
1301 } // switch
1304 sal_uInt16 nActPoint=rXPoly.GetPointCount();
1305 if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nActPoint<2) {
1306 rXPoly[0]=rStat.GetPos0();
1307 rXPoly[1]=rStat.GetNow();
1308 nActPoint=2;
1310 if (nActPoint==0) {
1311 rXPoly[0]=rStat.GetPos0();
1312 } else nActPoint--;
1313 bool bFreeHand=IsFreeHand(pU->eAktKind);
1314 rStat.SetNoSnap(bFreeHand);
1315 rStat.SetOrtho8Possible(pU->eAktKind!=OBJ_CARC && pU->eAktKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eAktKind!=OBJ_LINE));
1316 rXPoly[nActPoint]=rStat.Now();
1317 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1318 Point aPt(rStat.Start());
1319 if (pView!=NULL && pView->IsCreate1stPointAsCenter()) {
1320 aPt+=aPt;
1321 aPt-=rStat.Now();
1323 rXPoly[0]=aPt;
1325 OutputDevice* pOut=pView==NULL ? NULL : pView->GetFirstOutputDevice();
1326 if (bFreeHand) {
1327 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1328 if (rStat.IsMouseDown() && nActPoint>0) {
1329 // don't allow two consecutive points to occupy too similar positions
1330 long nMinDist=1;
1331 if (pView!=NULL) nMinDist=pView->GetFreeHandMinDistPix();
1332 if (pOut!=NULL) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1333 if (nMinDist<1) nMinDist=1;
1335 Point aPt0(rXPoly[nActPoint-1]);
1336 Point aPt1(rStat.Now());
1337 long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1338 long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1339 if (dx<nMinDist && dy<nMinDist) return sal_False;
1341 // TODO: the following is copied from EndCreate (with a few smaller modifications)
1342 // and should be combined into a method with the code there.
1344 if (nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1345 rXPoly.PointsToBezier(nActPoint-3);
1346 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1347 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1349 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1350 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1351 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1354 rXPoly[nActPoint+1]=rStat.Now();
1355 rStat.NextPoint();
1356 } else {
1357 pU->nBezierStartPoint=nActPoint;
1361 pU->ResetFormFlags();
1362 if (IsBezier(pU->eAktKind)) {
1363 if (nActPoint>=2) {
1364 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],rStat.IsMouseDown());
1365 } else if (pU->bBezHasCtrl0) {
1366 pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],pU->aBezControl0-rXPoly[nActPoint-1],rStat.IsMouseDown());
1369 if (pU->eAktKind==OBJ_CARC && nActPoint>=2) {
1370 pU->CalcCircle(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1372 if (pU->eAktKind==OBJ_LINE && nActPoint>=2) {
1373 pU->CalcLine(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1375 if (pU->eAktKind==OBJ_RECT && nActPoint>=2) {
1376 pU->CalcRect(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
1379 return true;
1382 bool ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
1384 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1385 bool bRet = false;
1386 SdrView* pView=rStat.GetView();
1387 bool bIncomp=pView!=NULL && pView->IsUseIncompatiblePathCreateInterface();
1388 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1389 sal_uInt16 nActPoint=rXPoly.GetPointCount()-1;
1390 rXPoly[nActPoint]=rStat.Now();
1391 if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1392 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1393 bRet = eCmd==SDRCREATE_FORCEEND;
1394 if (bRet) {
1395 mbCreating = sal_False;
1396 delete pU;
1397 rStat.SetUser(NULL);
1399 return bRet;
1402 if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1403 if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
1404 bRet=eCmd==SDRCREATE_FORCEEND;
1405 if (bRet) {
1406 mbCreating=sal_False;
1407 delete pU;
1408 rStat.SetUser(NULL);
1410 return bRet;
1412 if (eCmd==SDRCREATE_NEXTPOINT || eCmd==SDRCREATE_NEXTOBJECT) {
1413 // don't allow two consecutive points to occupy the same position
1414 if (nActPoint==0 || rStat.Now()!=rXPoly[nActPoint-1]) {
1415 if (bIncomp) {
1416 if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
1417 if (IsBezier(pU->eAktKind) && nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
1418 rXPoly.PointsToBezier(nActPoint-3);
1419 rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
1420 rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
1422 if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
1423 rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
1424 rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
1427 } else {
1428 if (nActPoint==1 && IsBezier(pU->eAktKind) && !pU->bBezHasCtrl0) {
1429 pU->aBezControl0=rStat.GetNow();
1430 pU->bBezHasCtrl0=sal_True;
1431 nActPoint--;
1433 if (pU->IsFormFlag()) {
1434 sal_uInt16 nPtAnz0=rXPoly.GetPointCount();
1435 rXPoly.Remove(nActPoint-1,2); // remove last two points and replace by form
1436 rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1437 sal_uInt16 nPtAnz1=rXPoly.GetPointCount();
1438 for (sal_uInt16 i=nPtAnz0+1; i<nPtAnz1-1; i++) { // to make BckAction work
1439 if (!rXPoly.IsControl(i)) rStat.NextPoint();
1441 nActPoint=rXPoly.GetPointCount()-1;
1444 nActPoint++;
1445 rXPoly[nActPoint]=rStat.GetNow();
1447 if (eCmd==SDRCREATE_NEXTOBJECT) {
1448 if (rXPoly.GetPointCount()>=2) {
1449 pU->bBezHasCtrl0=sal_False;
1450 // only a singular polygon may be opened, so close this
1451 rXPoly[nActPoint]=rXPoly[0];
1452 XPolygon aXP;
1453 aXP[0]=rStat.GetNow();
1454 aPathPolygon.Insert(aXP);
1459 sal_uInt16 nPolyAnz=aPathPolygon.Count();
1460 if (nPolyAnz!=0) {
1461 // delete last point, if necessary
1462 if (eCmd==SDRCREATE_FORCEEND) {
1463 XPolygon& rXP=aPathPolygon[nPolyAnz-1];
1464 sal_uInt16 nPtAnz=rXP.GetPointCount();
1465 if (nPtAnz>=2) {
1466 if (!rXP.IsControl(nPtAnz-2)) {
1467 if (rXP[nPtAnz-1]==rXP[nPtAnz-2]) {
1468 rXP.Remove(nPtAnz-1,1);
1470 } else {
1471 if (rXP[nPtAnz-3]==rXP[nPtAnz-2]) {
1472 rXP.Remove(nPtAnz-3,3);
1477 for (sal_uInt16 nPolyNum=nPolyAnz; nPolyNum>0;) {
1478 nPolyNum--;
1479 XPolygon& rXP=aPathPolygon[nPolyNum];
1480 sal_uInt16 nPtAnz=rXP.GetPointCount();
1481 // delete polygons with too few points
1482 if (nPolyNum<nPolyAnz-1 || eCmd==SDRCREATE_FORCEEND) {
1483 if (nPtAnz<2) aPathPolygon.Remove(nPolyNum);
1487 pU->ResetFormFlags();
1488 bRet=eCmd==SDRCREATE_FORCEEND;
1489 if (bRet) {
1490 mbCreating=sal_False;
1491 delete pU;
1492 rStat.SetUser(NULL);
1494 return bRet;
1497 bool ImpPathForDragAndCreate::BckCreate(SdrDragStat& rStat)
1499 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1500 if (aPathPolygon.Count()>0) {
1501 XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1502 sal_uInt16 nActPoint=rXPoly.GetPointCount();
1503 if (nActPoint>0) {
1504 nActPoint--;
1505 // make the last part of a bezier curve a line
1506 rXPoly.Remove(nActPoint,1);
1507 if (nActPoint>=3 && rXPoly.IsControl(nActPoint-1)) {
1508 // there should never be a bezier segment at the end, so this is just in case...
1509 rXPoly.Remove(nActPoint-1,1);
1510 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1513 nActPoint=rXPoly.GetPointCount();
1514 if (nActPoint>=4) { // no bezier segment at the end
1515 nActPoint--;
1516 if (rXPoly.IsControl(nActPoint-1)) {
1517 rXPoly.Remove(nActPoint-1,1);
1518 if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
1521 if (rXPoly.GetPointCount()<2) {
1522 aPathPolygon.Remove(aPathPolygon.Count()-1);
1524 if (aPathPolygon.Count()>0) {
1525 XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1526 sal_uInt16 nLocalActPoint=rLocalXPoly.GetPointCount();
1527 if (nLocalActPoint>0) {
1528 nLocalActPoint--;
1529 rLocalXPoly[nLocalActPoint]=rStat.Now();
1533 pU->ResetFormFlags();
1534 return aPathPolygon.Count()!=0;
1537 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
1539 ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
1540 aPathPolygon.Clear();
1541 mbCreating=sal_False;
1542 delete pU;
1543 rStat.SetUser(NULL);
1546 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
1548 basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
1549 SdrView* pView = rDrag.GetView();
1551 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1552 return aRetval;
1554 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1555 basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1L) : basegfx::B2DPolygon());
1557 if(pU->IsFormFlag() && aNewPolygon.count() > 1L)
1559 // remove last segment and replace with current
1560 // do not forget to rescue the previous control point which will be lost when
1561 // the point it's associated with is removed
1562 const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1563 const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1565 aNewPolygon.remove(nChangeIndex, 2L);
1566 aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1568 if(nChangeIndex < aNewPolygon.count())
1570 // if really something was added, set the saved previous control point to the
1571 // point where it belongs
1572 aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1576 if(aRetval.count())
1578 aRetval.setB2DPolygon(aRetval.count() - 1L, aNewPolygon);
1580 else
1582 aRetval.append(aNewPolygon);
1585 return aRetval;
1588 basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag) const
1590 basegfx::B2DPolyPolygon aRetval;
1591 SdrView* pView = rDrag.GetView();
1593 if(pView && pView->IsUseIncompatiblePathCreateInterface())
1594 return aRetval;
1596 ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
1598 if(pU && pU->bBezier && rDrag.IsMouseDown())
1600 // no more XOR, no need for complicated helplines
1601 basegfx::B2DPolygon aHelpline;
1602 aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1603 aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1604 aRetval.append(aHelpline);
1607 return aRetval;
1610 Pointer ImpPathForDragAndCreate::GetCreatePointer() const
1612 switch (meObjectKind) {
1613 case OBJ_LINE : return Pointer(POINTER_DRAW_LINE);
1614 case OBJ_POLY : return Pointer(POINTER_DRAW_POLYGON);
1615 case OBJ_PLIN : return Pointer(POINTER_DRAW_POLYGON);
1616 case OBJ_PATHLINE: return Pointer(POINTER_DRAW_BEZIER);
1617 case OBJ_PATHFILL: return Pointer(POINTER_DRAW_BEZIER);
1618 case OBJ_FREELINE: return Pointer(POINTER_DRAW_FREEHAND);
1619 case OBJ_FREEFILL: return Pointer(POINTER_DRAW_FREEHAND);
1620 case OBJ_SPLNLINE: return Pointer(POINTER_DRAW_FREEHAND);
1621 case OBJ_SPLNFILL: return Pointer(POINTER_DRAW_FREEHAND);
1622 case OBJ_PATHPOLY: return Pointer(POINTER_DRAW_POLYGON);
1623 case OBJ_PATHPLIN: return Pointer(POINTER_DRAW_POLYGON);
1624 default: break;
1625 } // switch
1626 return Pointer(POINTER_CROSS);
1629 /*************************************************************************/
1631 SdrPathObjGeoData::SdrPathObjGeoData()
1635 SdrPathObjGeoData::~SdrPathObjGeoData()
1639 //////////////////////////////////////////////////////////////////////////////
1640 // DrawContact section
1642 sdr::contact::ViewContact* SdrPathObj::CreateObjectSpecificViewContact()
1644 return new sdr::contact::ViewContactOfSdrPathObj(*this);
1647 /*************************************************************************/
1649 TYPEINIT1(SdrPathObj,SdrTextObj);
1651 SdrPathObj::SdrPathObj(SdrObjKind eNewKind)
1652 : meKind(eNewKind),
1653 mpDAC(0L),
1654 mdBrightness(1.0)
1656 bClosedObj = IsClosed();
1659 SdrPathObj::SdrPathObj(SdrObjKind eNewKind, const basegfx::B2DPolyPolygon& rPathPoly, double dBrightness)
1660 : maPathPolygon(rPathPoly),
1661 meKind(eNewKind),
1662 mpDAC(0L),
1663 mdBrightness(dBrightness)
1665 bClosedObj = IsClosed();
1666 ImpForceKind();
1669 SdrPathObj::~SdrPathObj()
1671 impDeleteDAC();
1674 static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1676 return (1L == rPolyPolygon.count() && 2L == rPolyPolygon.getB2DPolygon(0L).count());
1679 static Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
1681 basegfx::B2DRange aRange(basegfx::tools::getRange(rPolyPolygon));
1683 return Rectangle(
1684 FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1685 FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1688 void SdrPathObj::ImpForceLineWink()
1690 if(OBJ_LINE == meKind && lcl_ImpIsLine(GetPathPoly()))
1692 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1693 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1694 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1695 const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1696 const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1697 const Point aDelt(aPoint1 - aPoint0);
1699 aGeo.nDrehWink=GetAngle(aDelt);
1700 aGeo.nShearWink=0;
1701 aGeo.RecalcSinCos();
1702 aGeo.RecalcTan();
1704 // for SdrTextObj, keep aRect up to date
1705 aRect = Rectangle(aPoint0, aPoint1);
1706 aRect.Justify();
1710 void SdrPathObj::ImpForceKind()
1712 if (meKind==OBJ_PATHPLIN) meKind=OBJ_PLIN;
1713 if (meKind==OBJ_PATHPOLY) meKind=OBJ_POLY;
1715 if(GetPathPoly().areControlPointsUsed())
1717 switch (meKind)
1719 case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1720 case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1721 case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1722 default: break;
1725 else
1727 switch (meKind)
1729 case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1730 case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1731 case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1732 case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1733 default: break;
1737 if (meKind==OBJ_LINE && !lcl_ImpIsLine(GetPathPoly())) meKind=OBJ_PLIN;
1738 if (meKind==OBJ_PLIN && lcl_ImpIsLine(GetPathPoly())) meKind=OBJ_LINE;
1740 bClosedObj=IsClosed();
1742 if (meKind==OBJ_LINE)
1744 ImpForceLineWink();
1746 else
1748 // #i10659#, for polys with more than 2 points.
1750 // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1751 // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1752 // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1753 // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1754 // this is the case, some size needs to be set here in aRect to avoid that the cycle
1755 // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1756 // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1757 // from the local Resize() implementation.
1759 // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1760 // text rectangle for the text object itself and methods at SdrTextObj do handle it
1761 // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1762 // which is basically wrong. To make the SdrText methods which deal with aRect directly
1763 // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1764 // command for SdrPathObj. Since adding this update mechanism with #101412# to
1765 // ImpForceLineWink() for lines was very successful, i add it to where ImpForceLineWink()
1766 // was called, once here below and once on a 2nd place below.
1768 // #i10659# for SdrTextObj, keep aRect up to date
1769 if(GetPathPoly().count())
1771 aRect = lcl_ImpGetBoundRect(GetPathPoly());
1774 // #i116244# reset rotation
1775 aGeo.nDrehWink = aGeo.nShearWink = 0;
1776 aGeo.RecalcSinCos(); aGeo.RecalcTan();
1779 // #i75974# adapt polygon state to object type. This may include a reinterpretation
1780 // of a closed geometry as open one, but with identical first and last point
1781 for(sal_uInt32 a(0); a < maPathPolygon.count(); a++)
1783 basegfx::B2DPolygon aCandidate(maPathPolygon.getB2DPolygon(a));
1785 if((bool)IsClosed() != aCandidate.isClosed())
1787 // #i80213# really change polygon geometry; else e.g. the last point which
1788 // needs to be identical with the first one will be missing when opening
1789 // due to OBJ_PATH type
1790 if(aCandidate.isClosed())
1792 basegfx::tools::openWithGeometryChange(aCandidate);
1794 else
1796 basegfx::tools::closeWithGeometryChange(aCandidate);
1799 maPathPolygon.setB2DPolygon(a, aCandidate);
1804 void SdrPathObj::ImpSetClosed(sal_Bool bClose)
1806 if(bClose)
1808 switch (meKind)
1810 case OBJ_LINE : meKind=OBJ_POLY; break;
1811 case OBJ_PLIN : meKind=OBJ_POLY; break;
1812 case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1813 case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1814 case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1815 default: break;
1818 bClosedObj = sal_True;
1820 else
1822 switch (meKind)
1824 case OBJ_POLY : meKind=OBJ_PLIN; break;
1825 case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1826 case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1827 case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1828 default: break;
1831 bClosedObj = sal_False;
1834 ImpForceKind();
1837 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
1839 rInfo.bNoContortion=sal_False;
1841 bool bCanConv = !HasText() || ImpCanConvTextToCurve();
1842 bool bIsPath = IsBezier() || IsSpline();
1844 rInfo.bEdgeRadiusAllowed = sal_False;
1845 rInfo.bCanConvToPath = bCanConv && !bIsPath;
1846 rInfo.bCanConvToPoly = bCanConv && bIsPath;
1847 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
1850 sal_uInt16 SdrPathObj::GetObjIdentifier() const
1852 return sal_uInt16(meKind);
1855 SdrPathObj* SdrPathObj::Clone() const
1857 return CloneHelper< SdrPathObj >();
1860 SdrPathObj& SdrPathObj::operator=(const SdrPathObj& rObj)
1862 if( this == &rObj )
1863 return *this;
1864 SdrTextObj::operator=(rObj);
1865 maPathPolygon=rObj.GetPathPoly();
1866 return *this;
1869 void SdrPathObj::TakeObjNameSingul(XubString& rName) const
1871 if(OBJ_LINE == meKind)
1873 sal_uInt16 nId(STR_ObjNameSingulLINE);
1875 if(lcl_ImpIsLine(GetPathPoly()))
1877 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
1878 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
1879 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
1881 if(aB2DPoint0 != aB2DPoint1)
1883 if(aB2DPoint0.getY() == aB2DPoint1.getY())
1885 nId = STR_ObjNameSingulLINE_Hori;
1887 else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1889 nId = STR_ObjNameSingulLINE_Vert;
1891 else
1893 const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1894 const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1896 if(fDx == fDy)
1898 nId = STR_ObjNameSingulLINE_Diag;
1904 rName = ImpGetResStr(nId);
1906 else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1908 const bool bClosed(OBJ_POLY == meKind);
1909 sal_uInt16 nId(0);
1911 if(mpDAC && mpDAC->IsCreating())
1913 if(bClosed)
1915 nId = STR_ObjNameSingulPOLY;
1917 else
1919 nId = STR_ObjNameSingulPLIN;
1922 rName = ImpGetResStr(nId);
1924 else
1926 // get point count
1927 sal_uInt32 nPointCount(0L);
1928 const sal_uInt32 nPolyCount(GetPathPoly().count());
1930 for(sal_uInt32 a(0L); a < nPolyCount; a++)
1932 nPointCount += GetPathPoly().getB2DPolygon(a).count();
1935 if(bClosed)
1937 nId = STR_ObjNameSingulPOLY_PntAnz;
1939 else
1941 nId = STR_ObjNameSingulPLIN_PntAnz;
1944 rName = ImpGetResStr(nId);
1945 sal_uInt16 nPos(rName.SearchAscii("%2")); // #i96537#
1947 if(STRING_NOTFOUND != nPos)
1949 rName.Erase(nPos, 2);
1950 rName.Insert(OUString::number(nPointCount), nPos);
1954 else
1956 switch (meKind)
1958 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNameSingulPATHLINE); break;
1959 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNameSingulFREELINE); break;
1960 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNameSingulNATSPLN); break;
1961 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNameSingulPATHFILL); break;
1962 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNameSingulFREEFILL); break;
1963 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNameSingulPERSPLN); break;
1964 default: break;
1968 String aName(GetName());
1969 if(aName.Len())
1971 rName += sal_Unicode(' ');
1972 rName += sal_Unicode('\'');
1973 rName += aName;
1974 rName += sal_Unicode('\'');
1978 void SdrPathObj::TakeObjNamePlural(XubString& rName) const
1980 switch(meKind)
1982 case OBJ_LINE : rName=ImpGetResStr(STR_ObjNamePluralLINE ); break;
1983 case OBJ_PLIN : rName=ImpGetResStr(STR_ObjNamePluralPLIN ); break;
1984 case OBJ_POLY : rName=ImpGetResStr(STR_ObjNamePluralPOLY ); break;
1985 case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNamePluralPATHLINE); break;
1986 case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNamePluralFREELINE); break;
1987 case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNamePluralNATSPLN); break;
1988 case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNamePluralPATHFILL); break;
1989 case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNamePluralFREEFILL); break;
1990 case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNamePluralPERSPLN); break;
1991 default: break;
1995 basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
1997 return GetPathPoly();
2000 sal_uInt32 SdrPathObj::GetHdlCount() const
2002 sal_uInt32 nRetval(0L);
2003 const sal_uInt32 nPolyCount(GetPathPoly().count());
2005 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2007 nRetval += GetPathPoly().getB2DPolygon(a).count();
2010 return nRetval;
2013 SdrHdl* SdrPathObj::GetHdl(sal_uInt32 nHdlNum) const
2015 // #i73248#
2016 // Warn the user that this is ineffective and show alternatives. Should not be used at all.
2017 OSL_FAIL("SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
2019 // to have an alternative, get single handle using the ineffective way
2020 SdrHdl* pRetval = 0;
2021 SdrHdlList aLocalList(0);
2022 AddToHdlList(aLocalList);
2023 const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());
2025 if(nHdlCount && nHdlNum < nHdlCount)
2027 // remove and remember. The other created handles will be deleted again with the
2028 // destruction of the local list
2029 pRetval = aLocalList.RemoveHdl(nHdlNum);
2032 return pRetval;
2035 void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
2037 // keep old stuff to be able to keep old SdrHdl stuff, too
2038 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2039 sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
2040 bool bClosed=IsClosed();
2041 sal_uInt16 nIdx=0;
2043 for (sal_uInt16 i=0; i<nPolyCnt; i++) {
2044 const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
2045 sal_uInt16 nPntCnt=rXPoly.GetPointCount();
2046 if (bClosed && nPntCnt>1) nPntCnt--;
2048 for (sal_uInt16 j=0; j<nPntCnt; j++) {
2049 if (rXPoly.GetFlags(j)!=XPOLY_CONTROL) {
2050 const Point& rPnt=rXPoly[j];
2051 SdrHdl* pHdl=new SdrHdl(rPnt,HDL_POLY);
2052 pHdl->SetPolyNum(i);
2053 pHdl->SetPointNum(j);
2054 pHdl->Set1PixMore(j==0);
2055 pHdl->SetSourceHdlNum(nIdx);
2056 nIdx++;
2057 rHdlList.AddHdl(pHdl);
2063 sal_uInt32 SdrPathObj::GetPlusHdlCount(const SdrHdl& rHdl) const
2065 // keep old stuff to be able to keep old SdrHdl stuff, too
2066 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2067 sal_uInt16 nCnt = 0;
2068 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2069 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2071 if(nPolyNum < aOldPathPolygon.Count())
2073 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2074 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2075 if (nPntMax>0)
2077 nPntMax--;
2078 if (nPnt<=nPntMax)
2080 if (rXPoly.GetFlags(nPnt)!=XPOLY_CONTROL)
2082 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2083 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL) nCnt++;
2084 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2085 if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL) nCnt++;
2091 return nCnt;
2094 SdrHdl* SdrPathObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlusNum) const
2096 // keep old stuff to be able to keep old SdrHdl stuff, too
2097 const XPolyPolygon aOldPathPolygon(GetPathPoly());
2098 SdrHdl* pHdl = 0L;
2099 sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
2100 sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
2102 if (nPolyNum<aOldPathPolygon.Count())
2104 const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2105 sal_uInt16 nPntMax = rXPoly.GetPointCount();
2107 if (nPntMax>0)
2109 nPntMax--;
2110 if (nPnt<=nPntMax)
2112 pHdl=new SdrHdlBezWgt(&rHdl);
2113 pHdl->SetPolyNum(rHdl.GetPolyNum());
2115 if (nPnt==0 && IsClosed()) nPnt=nPntMax;
2116 if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL && nPlusNum==0)
2118 pHdl->SetPos(rXPoly[nPnt-1]);
2119 pHdl->SetPointNum(nPnt-1);
2121 else
2123 if (nPnt==nPntMax && IsClosed()) nPnt=0;
2124 if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL)
2126 pHdl->SetPos(rXPoly[nPnt+1]);
2127 pHdl->SetPointNum(nPnt+1);
2131 pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2132 pHdl->SetPlusHdl(sal_True);
2136 return pHdl;
2139 ////////////////////////////////////////////////////////////////////////////////////////////////////
2141 bool SdrPathObj::hasSpecialDrag() const
2143 return true;
2146 bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
2148 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2150 return aDragAndCreate.beginPathDrag(rDrag);
2153 bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
2155 ImpPathForDragAndCreate aDragAndCreate(*this);
2156 bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2158 if(bRetval)
2160 bRetval = aDragAndCreate.movePathDrag(rDrag);
2163 if(bRetval)
2165 bRetval = aDragAndCreate.endPathDrag(rDrag);
2168 if(bRetval)
2170 NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2173 return bRetval;
2176 String SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
2178 String aRetval;
2180 if(mpDAC)
2182 // #i103058# also get a comment when in creation
2183 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2185 if(bCreateComment)
2187 aRetval = mpDAC->getSpecialDragComment(rDrag);
2190 else
2192 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2193 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2195 if(bDidWork)
2197 aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2201 return aRetval;
2204 basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
2206 basegfx::B2DPolyPolygon aRetval;
2207 ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
2208 bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
2210 if(bDidWork)
2212 aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2215 return aRetval;
2218 ////////////////////////////////////////////////////////////////////////////////////////////////////
2220 bool SdrPathObj::BegCreate(SdrDragStat& rStat)
2222 impDeleteDAC();
2223 return impGetDAC().BegCreate(rStat);
2226 bool SdrPathObj::MovCreate(SdrDragStat& rStat)
2228 return impGetDAC().MovCreate(rStat);
2231 bool SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
2233 bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2235 if(bRetval && mpDAC)
2237 SetPathPoly(mpDAC->getModifiedPolyPolygon());
2239 // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2240 // to be able to use the type-changing ImpSetClosed method
2241 if(!IsClosedObj())
2243 SdrView* pView = rStat.GetView();
2245 if(pView && pView->IsAutoClosePolys() && !pView->IsUseIncompatiblePathCreateInterface())
2247 OutputDevice* pOut = pView->GetFirstOutputDevice();
2249 if(pOut)
2251 if(GetPathPoly().count())
2253 const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2255 if(aCandidate.count() > 2)
2257 // check distance of first and last point
2258 const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2259 const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2261 if(aDistVector.getLength() <= (double)nCloseDist)
2263 // close it
2264 ImpSetClosed(true);
2272 impDeleteDAC();
2275 return bRetval;
2278 bool SdrPathObj::BckCreate(SdrDragStat& rStat)
2280 return impGetDAC().BckCreate(rStat);
2283 void SdrPathObj::BrkCreate(SdrDragStat& rStat)
2285 impGetDAC().BrkCreate(rStat);
2286 impDeleteDAC();
2289 basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
2291 basegfx::B2DPolyPolygon aRetval;
2293 if(mpDAC)
2295 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2296 aRetval.append(mpDAC->TakeDragPolyPolygon(rDrag));
2299 return aRetval;
2302 // during drag or create, allow accessing the so-far created/modified polyPolygon
2303 basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
2305 basegfx::B2DPolyPolygon aRetval;
2307 if(mpDAC)
2309 aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2312 return aRetval;
2315 basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
2317 basegfx::B2DPolyPolygon aRetval;
2319 if(mpDAC)
2321 aRetval = mpDAC->TakeDragPolyPolygon(rDrag);
2324 return aRetval;
2327 Pointer SdrPathObj::GetCreatePointer() const
2329 return impGetDAC().GetCreatePointer();
2332 void SdrPathObj::NbcMove(const Size& rSiz)
2334 maPathPolygon.transform(basegfx::tools::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
2336 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2337 SdrTextObj::NbcMove(rSiz);
2340 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2342 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
2343 aTrans = basegfx::tools::createScaleTranslateB2DHomMatrix(
2344 double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2345 maPathPolygon.transform(aTrans);
2347 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2348 SdrTextObj::NbcResize(rRef,xFact,yFact);
2351 void SdrPathObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
2353 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2354 const basegfx::B2DHomMatrix aTrans(basegfx::tools::createRotateAroundPoint(rRef.X(), rRef.Y(), -nWink * nPi180));
2355 maPathPolygon.transform(aTrans);
2357 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2358 SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
2361 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, bool bVShear)
2363 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2365 if(bVShear)
2367 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2368 aTrans.shearY(-fTan);
2370 else
2372 aTrans.shearX(-fTan);
2375 aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2376 maPathPolygon.transform(aTrans);
2378 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2379 SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2382 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2384 const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2385 const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2386 const double fRot(atan2(fDiffY, fDiffX));
2387 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2388 aTrans.rotate(-fRot);
2389 aTrans.scale(1.0, -1.0);
2390 aTrans.rotate(fRot);
2391 aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2392 maPathPolygon.transform(aTrans);
2394 // Do Joe's special handling for lines when mirroring, too
2395 ImpForceKind();
2397 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2398 SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2401 void SdrPathObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
2403 if(!aGeo.nDrehWink)
2405 rRect = GetSnapRect();
2407 else
2409 XPolyPolygon aXPP(GetPathPoly());
2410 RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2411 rRect=aXPP.GetBoundRect();
2412 Point aTmp(rRect.TopLeft());
2413 RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2414 aTmp-=rRect.TopLeft();
2415 rRect.Move(aTmp.X(),aTmp.Y());
2419 void SdrPathObj::RecalcSnapRect()
2421 if(GetPathPoly().count())
2423 maSnapRect = lcl_ImpGetBoundRect(GetPathPoly());
2427 void SdrPathObj::NbcSetSnapRect(const Rectangle& rRect)
2429 Rectangle aOld(GetSnapRect());
2431 // Take RECT_EMPTY into account when calculating scale factors
2432 long nMulX = (RECT_EMPTY == rRect.Right()) ? 0 : rRect.Right() - rRect.Left();
2434 long nDivX = aOld.Right() - aOld.Left();
2436 // Take RECT_EMPTY into account when calculating scale factors
2437 long nMulY = (RECT_EMPTY == rRect.Bottom()) ? 0 : rRect.Bottom() - rRect.Top();
2439 long nDivY = aOld.Bottom() - aOld.Top();
2440 if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2441 if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2442 Fraction aX(nMulX,nDivX);
2443 Fraction aY(nMulY,nDivY);
2444 NbcResize(aOld.TopLeft(), aX, aY);
2445 NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2448 sal_uInt32 SdrPathObj::GetSnapPointCount() const
2450 return GetHdlCount();
2453 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2455 sal_uInt32 nPoly,nPnt;
2456 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2458 DBG_ASSERT(sal_False,"SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2461 const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2462 return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2465 sal_Bool SdrPathObj::IsPolyObj() const
2467 return sal_True;
2470 sal_uInt32 SdrPathObj::GetPointCount() const
2472 const sal_uInt32 nPolyCount(GetPathPoly().count());
2473 sal_uInt32 nRetval(0L);
2475 for(sal_uInt32 a(0L); a < nPolyCount; a++)
2477 nRetval += GetPathPoly().getB2DPolygon(a).count();
2480 return nRetval;
2483 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2485 Point aRetval;
2486 sal_uInt32 nPoly,nPnt;
2488 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2490 const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2491 const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2492 aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2495 return aRetval;
2498 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2500 sal_uInt32 nPoly,nPnt;
2502 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2504 basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2505 aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2506 maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2508 if(meKind==OBJ_LINE)
2510 ImpForceLineWink();
2512 else
2514 if(GetPathPoly().count())
2516 // #i10659# for SdrTextObj, keep aRect up to date
2517 aRect = lcl_ImpGetBoundRect(GetPathPoly());
2521 SetRectsDirty();
2525 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, sal_Bool bNewObj, sal_Bool bHideHim)
2527 sal_uInt32 nNewHdl;
2529 if(bNewObj)
2531 nNewHdl = NbcInsPoint(0L, rPos, sal_True, bHideHim);
2533 else
2535 // look for smallest distance data
2536 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2537 sal_uInt32 nSmallestPolyIndex(0L);
2538 sal_uInt32 nSmallestEdgeIndex(0L);
2539 double fSmallestCut;
2540 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2542 // create old polygon index from it
2543 sal_uInt32 nPolyIndex(nSmallestEdgeIndex);
2545 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2547 nPolyIndex += GetPathPoly().getB2DPolygon(a).count();
2550 nNewHdl = NbcInsPoint(nPolyIndex, rPos, sal_False, bHideHim);
2553 ImpForceKind();
2554 return nNewHdl;
2557 sal_uInt32 SdrPathObj::NbcInsPoint(sal_uInt32 /*nHdlNum*/, const Point& rPos, sal_Bool bNewObj, sal_Bool /*bHideHim*/)
2559 sal_uInt32 nNewHdl;
2561 if(bNewObj)
2563 basegfx::B2DPolygon aNewPoly;
2564 const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2565 aNewPoly.append(aPoint);
2566 aNewPoly.setClosed(IsClosed());
2567 maPathPolygon.append(aNewPoly);
2568 SetRectsDirty();
2569 nNewHdl = GetHdlCount();
2571 else
2573 // look for smallest distance data
2574 const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2575 sal_uInt32 nSmallestPolyIndex(0L);
2576 sal_uInt32 nSmallestEdgeIndex(0L);
2577 double fSmallestCut;
2578 basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2579 basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2580 const bool bBefore(!aCandidate.isClosed() && 0L == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2581 const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2L && 1.0 == fSmallestCut);
2583 if(bBefore)
2585 // before first point
2586 aCandidate.insert(0L, aTestPoint);
2588 if(aCandidate.areControlPointsUsed())
2590 if(aCandidate.isNextControlPointUsed(1))
2592 aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2593 aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2597 nNewHdl = 0L;
2599 else if(bAfter)
2601 // after last point
2602 aCandidate.append(aTestPoint);
2604 if(aCandidate.areControlPointsUsed())
2606 if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2608 aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2609 aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2613 nNewHdl = aCandidate.count() - 1L;
2615 else
2617 // in between
2618 bool bSegmentSplit(false);
2619 const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2621 if(aCandidate.areControlPointsUsed())
2623 if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2625 bSegmentSplit = true;
2629 if(bSegmentSplit)
2631 // rebuild original segment to get the split data
2632 basegfx::B2DCubicBezier aBezierA, aBezierB;
2633 const basegfx::B2DCubicBezier aBezier(
2634 aCandidate.getB2DPoint(nSmallestEdgeIndex),
2635 aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2636 aCandidate.getPrevControlPoint(nNextIndex),
2637 aCandidate.getB2DPoint(nNextIndex));
2639 // split and insert hit point
2640 aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2641 aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2643 // since we inserted hit point and not split point, we need to add an offset
2644 // to the control points to get the C1 continuity we want to achieve
2645 const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2646 aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2647 aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2648 aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2649 aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2651 else
2653 aCandidate.insert(nSmallestEdgeIndex + 1L, aTestPoint);
2656 nNewHdl = nSmallestEdgeIndex + 1L;
2659 maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2661 // create old polygon index from it
2662 for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
2664 nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2668 ImpForceKind();
2669 return nNewHdl;
2672 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2674 SdrPathObj* pNewObj = 0L;
2675 const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2676 sal_uInt32 nPoly, nPnt;
2678 if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2680 if(0L == nPoly)
2682 const basegfx::B2DPolygon aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2683 const sal_uInt32 nPointCount(aCandidate.count());
2685 if(nPointCount)
2687 if(IsClosed())
2689 // when closed, RipPoint means to open the polygon at the selected point. To
2690 // be able to do that, it is necessary to make the selected point the first one
2691 basegfx::B2DPolygon aNewPolygon(basegfx::tools::makeStartPoint(aCandidate, nPnt));
2692 SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2693 ToggleClosed();
2695 // give back new position of old start point (historical reasons)
2696 rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2698 else
2700 if(nPointCount >= 3L && nPnt != 0L && nPnt + 1L < nPointCount)
2702 // split in two objects at point nPnt
2703 basegfx::B2DPolygon aSplitPolyA(aCandidate, 0L, nPnt + 1L);
2704 SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2706 pNewObj = (SdrPathObj*)Clone();
2707 basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2708 pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2715 return pNewObj;
2718 SdrObject* SdrPathObj::DoConvertToPolyObj(sal_Bool bBezier, bool bAddText) const
2720 // #i89784# check for FontWork with activated HideContour
2721 const drawinglayer::attribute::SdrTextAttribute aText(
2722 drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2723 const bool bHideContour(
2724 !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2726 SdrObject* pRet = bHideContour ?
2728 ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
2730 SdrPathObj* pPath = PTR_CAST(SdrPathObj, pRet);
2732 if(pPath)
2734 if(pPath->GetPathPoly().areControlPointsUsed())
2736 if(!bBezier)
2738 // reduce all bezier curves
2739 pPath->SetPathPoly(basegfx::tools::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2742 else
2744 if(bBezier)
2746 // create bezier curves
2747 pPath->SetPathPoly(basegfx::tools::expandToCurve(pPath->GetPathPoly()));
2752 if(bAddText)
2754 pRet = ImpConvertAddText(pRet, bBezier);
2757 return pRet;
2760 SdrObjGeoData* SdrPathObj::NewGeoData() const
2762 return new SdrPathObjGeoData;
2765 void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
2767 SdrTextObj::SaveGeoData(rGeo);
2768 SdrPathObjGeoData& rPGeo = (SdrPathObjGeoData&) rGeo;
2769 rPGeo.maPathPolygon=GetPathPoly();
2770 rPGeo.meKind=meKind;
2773 void SdrPathObj::RestGeoData(const SdrObjGeoData& rGeo)
2775 SdrTextObj::RestGeoData(rGeo);
2776 SdrPathObjGeoData& rPGeo=(SdrPathObjGeoData&)rGeo;
2777 maPathPolygon=rPGeo.maPathPolygon;
2778 meKind=rPGeo.meKind;
2779 ImpForceKind(); // to set bClosed (among other things)
2782 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2784 if(GetPathPoly() != rPathPoly)
2786 maPathPolygon=rPathPoly;
2787 ImpForceKind();
2788 SetRectsDirty();
2792 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
2794 if(GetPathPoly() != rPathPoly)
2796 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
2797 NbcSetPathPoly(rPathPoly);
2798 SetChanged();
2799 BroadcastObjectChange();
2800 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
2804 void SdrPathObj::ToggleClosed()
2806 Rectangle aBoundRect0;
2807 if(pUserCall != NULL)
2808 aBoundRect0 = GetLastBoundRect();
2809 ImpSetClosed(!IsClosed()); // set new ObjKind
2810 ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2811 SetRectsDirty();
2812 SetChanged();
2813 BroadcastObjectChange();
2814 SendUserCall(SDRUSERCALL_RESIZE, aBoundRect0);
2817 // for friend class SdrPolyEditView in some compilers:
2818 void SdrPathObj::SetRectsDirty(sal_Bool bNotMyself)
2820 SdrTextObj::SetRectsDirty(bNotMyself);
2823 ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
2825 if(!mpDAC)
2827 ((SdrPathObj*)this)->mpDAC = new ImpPathForDragAndCreate(*((SdrPathObj*)this));
2830 return *mpDAC;
2833 void SdrPathObj::impDeleteDAC() const
2835 if(mpDAC)
2837 delete mpDAC;
2838 ((SdrPathObj*)this)->mpDAC = 0L;
2842 ////////////////////////////////////////////////////////////////////////////////////////////////////
2844 // transformation interface for StarOfficeAPI. This implements support for
2845 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2846 // moment it contains a shearX, rotation and translation, but for setting all linear
2847 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2849 ////////////////////////////////////////////////////////////////////////////////////////////////////
2850 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2851 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2852 sal_Bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
2854 double fRotate(0.0);
2855 double fShearX(0.0);
2856 basegfx::B2DTuple aScale(1.0, 1.0);
2857 basegfx::B2DTuple aTranslate(0.0, 0.0);
2859 if(GetPathPoly().count())
2861 // copy geometry
2862 basegfx::B2DHomMatrix aMoveToZeroMatrix;
2863 rPolyPolygon = GetPathPoly();
2865 if(OBJ_LINE == meKind)
2867 // ignore shear and rotate, just use scale and translate
2868 OSL_ENSURE(GetPathPoly().count() > 0L && GetPathPoly().getB2DPolygon(0L).count() > 1L, "OBJ_LINE with too few polygons (!)");
2869 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2870 // itself, else this method will no longer return the full polygon information (curve will
2871 // be lost)
2872 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2873 aScale = aPolyRangeNoCurve.getRange();
2874 aTranslate = aPolyRangeNoCurve.getMinimum();
2876 // define matrix for move polygon to zero point
2877 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2879 else
2881 if(aGeo.nShearWink || aGeo.nDrehWink)
2883 // get rotate and shear in drawingLayer notation
2884 fRotate = aGeo.nDrehWink * F_PI18000;
2885 fShearX = aGeo.nShearWink * F_PI18000;
2887 // build mathematically correct (negative shear and rotate) object transform
2888 // containing shear and rotate to extract unsheared, unrotated polygon
2889 basegfx::B2DHomMatrix aObjectMatrix;
2890 aObjectMatrix.shearX(tan((36000 - aGeo.nShearWink) * F_PI18000));
2891 aObjectMatrix.rotate((36000 - aGeo.nDrehWink) * F_PI18000);
2893 // create inverse from it and back-transform polygon
2894 basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2895 aInvObjectMatrix.invert();
2896 rPolyPolygon.transform(aInvObjectMatrix);
2898 // get range from unsheared, unrotated polygon and extract scale and translate.
2899 // transform topLeft from it back to transformed state to get original
2900 // topLeft (rotation center)
2901 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2902 // itself, else this method will no longer return the full polygon information (curve will
2903 // be lost)
2904 const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2905 aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2906 aScale = aCorrectedRangeNoCurve.getRange();
2908 // define matrix for move polygon to zero point
2909 // #i112280# Added missing minus for Y-Translation
2910 aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2912 else
2914 // get scale and translate from unsheared, unrotated polygon
2915 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2916 // itself, else this method will no longer return the full polygon information (curve will
2917 // be lost)
2918 const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
2919 aScale = aPolyRangeNoCurve.getRange();
2920 aTranslate = aPolyRangeNoCurve.getMinimum();
2922 // define matrix for move polygon to zero point
2923 aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2927 // move polygon to zero point with pre-defined matrix
2928 rPolyPolygon.transform(aMoveToZeroMatrix);
2931 // position maybe relative to anchorpos, convert
2932 if( pModel && pModel->IsWriter() )
2934 if(GetAnchorPos().X() || GetAnchorPos().Y())
2936 aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2940 // force MapUnit to 100th mm
2941 const SfxMapUnit eMapUnit(GetObjectMapUnit());
2942 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
2944 switch(eMapUnit)
2946 case SFX_MAPUNIT_TWIP :
2948 // position
2949 aTranslate.setX(ImplTwipsToMM(aTranslate.getX()));
2950 aTranslate.setY(ImplTwipsToMM(aTranslate.getY()));
2952 // size
2953 aScale.setX(ImplTwipsToMM(aScale.getX()));
2954 aScale.setY(ImplTwipsToMM(aScale.getY()));
2956 // polygon
2957 basegfx::B2DHomMatrix aTwipsToMM;
2958 const double fFactorTwipsToMM(127.0 / 72.0);
2959 aTwipsToMM.scale(fFactorTwipsToMM, fFactorTwipsToMM);
2960 rPolyPolygon.transform(aTwipsToMM);
2962 break;
2964 default:
2966 OSL_FAIL("TRGetBaseGeometry: Missing unit translation to 100th mm!");
2971 // build return value matrix
2972 rMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
2973 aScale,
2974 basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2975 basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
2976 aTranslate);
2978 return sal_True;
2981 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2982 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2983 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2984 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
2986 // break up matrix
2987 basegfx::B2DTuple aScale;
2988 basegfx::B2DTuple aTranslate;
2989 double fRotate, fShearX;
2990 rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2992 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2993 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2994 if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
2996 aScale.setX(fabs(aScale.getX()));
2997 aScale.setY(fabs(aScale.getY()));
2998 fRotate = fmod(fRotate + F_PI, F_2PI);
3001 // copy poly
3002 basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
3004 // reset object shear and rotations
3005 aGeo.nDrehWink = 0;
3006 aGeo.RecalcSinCos();
3007 aGeo.nShearWink = 0;
3008 aGeo.RecalcTan();
3010 // force metric to pool metric
3011 const SfxMapUnit eMapUnit(GetObjectMapUnit());
3012 if(eMapUnit != SFX_MAPUNIT_100TH_MM)
3014 switch(eMapUnit)
3016 case SFX_MAPUNIT_TWIP :
3018 // position
3019 aTranslate.setX(ImplMMToTwips(aTranslate.getX()));
3020 aTranslate.setY(ImplMMToTwips(aTranslate.getY()));
3022 // size
3023 aScale.setX(ImplMMToTwips(aScale.getX()));
3024 aScale.setY(ImplMMToTwips(aScale.getY()));
3026 // polygon
3027 basegfx::B2DHomMatrix aMMToTwips;
3028 const double fFactorMMToTwips(72.0 / 127.0);
3029 aMMToTwips.scale(fFactorMMToTwips, fFactorMMToTwips);
3030 aNewPolyPolygon.transform(aMMToTwips);
3032 break;
3034 default:
3036 OSL_FAIL("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
3041 if( pModel && pModel->IsWriter() )
3043 // if anchor is used, make position relative to it
3044 if(GetAnchorPos().X() || GetAnchorPos().Y())
3046 aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3050 // create transformation for polygon, set values at aGeo direct
3051 basegfx::B2DHomMatrix aTransform;
3053 // #i75086#
3054 // Given polygon is already scaled (for historical reasons), but not mirrored yet.
3055 // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
3056 if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0))
3058 aTransform.scale(
3059 basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0,
3060 basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
3063 if(!basegfx::fTools::equalZero(fShearX))
3065 aTransform.shearX(tan(-atan(fShearX)));
3066 aGeo.nShearWink = FRound(atan(fShearX) / F_PI18000);
3067 aGeo.RecalcTan();
3070 if(!basegfx::fTools::equalZero(fRotate))
3072 // #i78696#
3073 // fRotate is mathematically correct for linear transformations, so it's
3074 // the one to use for the geometry change
3075 aTransform.rotate(fRotate);
3077 // #i78696#
3078 // fRotate is mathematically correct, but aGeoStat.nDrehWink is
3079 // mirrored -> mirror value here
3080 aGeo.nDrehWink = NormAngle360(FRound(-fRotate / F_PI18000));
3081 aGeo.RecalcSinCos();
3084 if(!aTranslate.equalZero())
3086 // #i39529# absolute positioning, so get current position (without control points (!))
3087 const basegfx::B2DRange aCurrentRange(basegfx::tools::getRange(aNewPolyPolygon));
3088 aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
3091 // transform polygon and trigger change
3092 aNewPolyPolygon.transform(aTransform);
3093 SetPathPoly(aNewPolyPolygon);
3096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */