LibreOffice Module svx (master)  1
svdopath.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <tools/bigint.hxx>
21 #include <tools/helpers.hxx>
22 #include <rtl/ustrbuf.hxx>
23 #include <svx/svdopath.hxx>
24 #include <math.h>
25 #include <svx/xpoly.hxx>
26 #include <svx/svdtrans.hxx>
27 #include <svx/svddrag.hxx>
28 #include <svx/svdmodel.hxx>
29 #include <svx/svdhdl.hxx>
30 #include <svx/svdview.hxx>
31 #include <svx/dialmgr.hxx>
32 #include <svx/strings.hrc>
33 
46 #include <vcl/ptrstyle.hxx>
47 #include <memory>
48 #include <sal/log.hxx>
49 
50 using namespace sdr;
51 
52 static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
53 {
54  if (nPnt>0) {
55  nPnt--;
56  } else {
57  nPnt=nPntMax;
58  if (bClosed) nPnt--;
59  }
60  return nPnt;
61 }
62 
63 static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
64 {
65  nPnt++;
66  if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
67  return nPnt;
68 }
69 
70 namespace {
71 
72 struct ImpSdrPathDragData : public SdrDragStatUserData
73 {
74  XPolygon aXP; // section of the original polygon
75  bool bValid; // FALSE = too few points
76  bool bClosed; // closed object?
77  sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
78  sal_uInt16 nPnt; // number of point in the above polygon
79  sal_uInt16 nPointCount; // number of points of the polygon
80  bool bBegPnt; // dragged point is first point of a Polyline
81  bool bEndPnt; // dragged point is finishing point of a Polyline
82  sal_uInt16 nPrevPnt; // index of previous point
83  sal_uInt16 nNextPnt; // index of next point
84  bool bPrevIsBegPnt; // previous point is first point of a Polyline
85  bool bNextIsEndPnt; // next point is first point of a Polyline
86  sal_uInt16 nPrevPrevPnt; // index of point before previous point
87  sal_uInt16 nNextNextPnt; // index of point after next point
88  bool bControl; // point is a control point
89  bool bIsNextControl; // point is a control point after a support point
90  bool bPrevIsControl; // if nPnt is a support point: a control point comes before
91  bool bNextIsControl; // if nPnt is a support point: a control point comes after
92  sal_uInt16 nPrevPrevPnt0;
93  sal_uInt16 nPrevPnt0;
94  sal_uInt16 nPnt0;
95  sal_uInt16 nNextPnt0;
96  sal_uInt16 nNextNextPnt0;
97  bool bEliminate; // delete point? (is set by MovDrag)
98 
99  bool mbMultiPointDrag;
100  const XPolyPolygon maOrig;
101  XPolyPolygon maMove;
102  std::vector<SdrHdl*> maHandles;
103 
104 public:
105  ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
106  void ResetPoly(const SdrPathObj& rPO);
107  bool IsMultiPointDrag() const { return mbMultiPointDrag; }
108 };
109 
110 }
111 
112 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
113  : aXP(5)
114  , bValid(false)
115  , bClosed(false)
116  , nPoly(0)
117  , nPnt(0)
118  , nPointCount(0)
119  , bBegPnt(false)
120  , bEndPnt(false)
121  , nPrevPnt(0)
122  , nNextPnt(0)
123  , bPrevIsBegPnt(false)
124  , bNextIsEndPnt(false)
125  , nPrevPrevPnt(0)
126  , nNextNextPnt(0)
127  , bControl(false)
128  , bIsNextControl(false)
129  , bPrevIsControl(false)
130  , bNextIsControl(false)
131  , nPrevPrevPnt0(0)
132  , nPrevPnt0(0)
133  , nPnt0(0)
134  , nNextPnt0(0)
135  , nNextNextPnt0(0)
136  , bEliminate(false)
137  , mbMultiPointDrag(bMuPoDr)
138  , maOrig(rPO.GetPathPoly())
139  , maHandles(0)
140 {
141  if(mbMultiPointDrag)
142  {
143  const SdrMarkView& rMarkView = *rDrag.GetView();
144  const SdrHdlList& rHdlList = rMarkView.GetHdlList();
145  const size_t nHdlCount = rHdlList.GetHdlCount();
146  const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
147 
148  for(size_t a = 0; a < nHdlCount; ++a)
149  {
150  SdrHdl* pTestHdl = rHdlList.GetHdl(a);
151 
152  if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
153  {
154  maHandles.push_back(pTestHdl);
155  }
156  }
157 
158  maMove = maOrig;
159  bValid = true;
160  }
161  else
162  {
163  sal_uInt16 nPntMax = 0; // maximum index
164  bValid=false;
165  bClosed=rPO.IsClosed(); // closed object?
166  nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum()); // number of the polygon in the PolyPolygon
167  nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum()); // number of points in the above polygon
168  const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
169  nPointCount=aTmpXP.GetPointCount(); // number of point of the polygon
170  if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
171  nPntMax=nPointCount-1; // maximum index
172  bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
173  bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
174  if (bClosed && nPointCount<=3) { // if polygon is only a line
175  bBegPnt=(nPointCount<3) || nPnt==0;
176  bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
177  }
178  nPrevPnt=nPnt; // index of previous point
179  nNextPnt=nPnt; // index of next point
180  if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
181  if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
182  bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
183  bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
184  nPrevPrevPnt=nPnt; // index of point before previous point
185  nNextNextPnt=nPnt; // index of point after next point
186  if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
187  if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
188  bControl=rHdl.IsPlusHdl(); // point is a control point
189  bIsNextControl=false; // point is a control point after a support point
190  bPrevIsControl=false; // if nPnt is a support point: a control point comes before
191  bNextIsControl=false; // if nPnt is a support point: a control point comes after
192  if (bControl) {
193  bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
194  } else {
195  bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
196  bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
197  }
198  nPrevPrevPnt0=nPrevPrevPnt;
199  nPrevPnt0 =nPrevPnt;
200  nPnt0 =nPnt;
201  nNextPnt0 =nNextPnt;
202  nNextNextPnt0=nNextNextPnt;
203  nPrevPrevPnt=0;
204  nPrevPnt=1;
205  nPnt=2;
206  nNextPnt=3;
207  nNextNextPnt=4;
208  bEliminate=false;
209  ResetPoly(rPO);
210  bValid=true;
211  }
212 }
213 
214 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
215 {
216  const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
217  aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
218  aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
219  aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
220  aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
221  aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
222 }
223 
224 namespace {
225 
226 struct ImpPathCreateUser : public SdrDragStatUserData
227 {
228  Point aBezControl0;
229  Point aBezStart;
230  Point aBezCtrl1;
231  Point aBezCtrl2;
232  Point aBezEnd;
233  Point aCircStart;
234  Point aCircEnd;
235  Point aCircCenter;
236  Point aLineStart;
237  Point aLineEnd;
238  Point aRectP1;
239  Point aRectP2;
240  Point aRectP3;
241  long nCircRadius;
242  long nCircStAngle;
243  long nCircRelAngle;
244  bool bBezier;
245  bool bBezHasCtrl0;
246  bool bCircle;
247  bool bAngleSnap;
248  bool bLine;
249  bool bLine90;
250  bool bRect;
251  bool bMixedCreate;
252  sal_uInt16 nBezierStartPoint;
253  SdrObjKind eStartKind;
254  SdrObjKind eCurrentKind;
255 
256 public:
257  ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
258  bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
259  bMixedCreate(false),nBezierStartPoint(0),eStartKind(OBJ_NONE),eCurrentKind(OBJ_NONE) { }
260 
261  void ResetFormFlags() { bBezier=false; bCircle=false; bLine=false; bRect=false; }
262  bool IsFormFlag() const { return bBezier || bCircle || bLine || bRect; }
263  XPolygon GetFormPoly() const;
264  void CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
265  XPolygon GetBezierPoly() const;
266  void CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
267  XPolygon GetCirclePoly() const;
268  void CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
269  static Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView const * pView);
270  XPolygon GetLinePoly() const;
271  void CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
272  XPolygon GetRectPoly() const;
273 };
274 
275 }
276 
277 XPolygon ImpPathCreateUser::GetFormPoly() const
278 {
279  if (bBezier) return GetBezierPoly();
280  if (bCircle) return GetCirclePoly();
281  if (bLine) return GetLinePoly();
282  if (bRect) return GetRectPoly();
283  return XPolygon();
284 }
285 
286 void ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
287 {
288  aBezStart=rP1;
289  aBezCtrl1=rP1+rDir;
290  aBezCtrl2=rP2;
291 
292  // #i21479#
293  // Also copy the end point when no end point is set yet
294  if (!bMouseDown || (0 == aBezEnd.X() && 0 == aBezEnd.Y())) aBezEnd=rP2;
295 
296  bBezier=true;
297 }
298 
299 XPolygon ImpPathCreateUser::GetBezierPoly() const
300 {
301  XPolygon aXP(4);
302  aXP[0]=aBezStart; aXP.SetFlags(0,PolyFlags::Smooth);
303  aXP[1]=aBezCtrl1; aXP.SetFlags(1,PolyFlags::Control);
304  aXP[2]=aBezCtrl2; aXP.SetFlags(2,PolyFlags::Control);
305  aXP[3]=aBezEnd;
306  return aXP;
307 }
308 
309 void ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
310 {
311  long nTangAngle=GetAngle(rDir);
312  aCircStart=rP1;
313  aCircEnd=rP2;
314  aCircCenter=rP1;
315  long dx=rP2.X()-rP1.X();
316  long dy=rP2.Y()-rP1.Y();
317  long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
318  dAngle=NormAngle36000(dAngle);
319  long nTmpAngle=NormAngle36000(9000-dAngle);
320  bool bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
321  long nRad=0;
322  if (bRet) {
323  double cs = cos(nTmpAngle * F_PI18000);
324  double nR=static_cast<double>(GetLen(Point(dx,dy)))/cs/2;
325  nRad=std::abs(FRound(nR));
326  }
327  if (dAngle<18000) {
328  nCircStAngle=NormAngle36000(nTangAngle-9000);
329  nCircRelAngle=NormAngle36000(2*dAngle);
330  aCircCenter.AdjustX(FRound(nRad * cos((nTangAngle + 9000) * F_PI18000)));
331  aCircCenter.AdjustY(-(FRound(nRad * sin((nTangAngle + 9000) * F_PI18000))));
332  } else {
333  nCircStAngle=NormAngle36000(nTangAngle+9000);
334  nCircRelAngle=-NormAngle36000(36000-2*dAngle);
335  aCircCenter.AdjustX(FRound(nRad * cos((nTangAngle - 9000) * F_PI18000)));
336  aCircCenter.AdjustY(-(FRound(nRad * sin((nTangAngle - 9000) * F_PI18000))));
337  }
338  bAngleSnap=pView!=nullptr && pView->IsAngleSnapEnabled();
339  if (bAngleSnap) {
340  long nSA=pView->GetSnapAngle();
341  if (nSA!=0) { // angle snapping
342  bool bNeg=nCircRelAngle<0;
343  if (bNeg) nCircRelAngle=-nCircRelAngle;
344  nCircRelAngle+=nSA/2;
345  nCircRelAngle/=nSA;
346  nCircRelAngle*=nSA;
347  nCircRelAngle=NormAngle36000(nCircRelAngle);
348  if (bNeg) nCircRelAngle=-nCircRelAngle;
349  }
350  }
351  nCircRadius=nRad;
352  if (nRad==0 || std::abs(nCircRelAngle)<5) bRet=false;
353  bCircle=bRet;
354 }
355 
356 XPolygon ImpPathCreateUser::GetCirclePoly() const
357 {
358  if (nCircRelAngle>=0) {
359  XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
360  sal_uInt16((nCircStAngle+5)/10),sal_uInt16((nCircStAngle+nCircRelAngle+5)/10),false);
361  aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
362  if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
363  return aXP;
364  } else {
365  XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
366  sal_uInt16(NormAngle36000(nCircStAngle+nCircRelAngle+5)/10),sal_uInt16((nCircStAngle+5)/10),false);
367  sal_uInt16 nCount=aXP.GetPointCount();
368  for (sal_uInt16 nNum=nCount/2; nNum>0;) {
369  nNum--; // reverse XPoly's order of points
370  sal_uInt16 n2=nCount-nNum-1;
371  Point aPt(aXP[nNum]);
372  aXP[nNum]=aXP[n2];
373  aXP[n2]=aPt;
374  }
375  aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
376  if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
377  return aXP;
378  }
379 }
380 
381 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView const * pView)
382 {
383  long x=aCsr.X();
384  long y=aCsr.Y();
385  bool bHLin=nDirY==0;
386  bool bVLin=nDirX==0;
387  if (bHLin) y=0;
388  else if (bVLin) x=0;
389  else {
390  long x1=BigMulDiv(y,nDirX,nDirY);
391  long y1=y;
392  long x2=x;
393  long y2=BigMulDiv(x,nDirY,nDirX);
394  long l1=std::abs(x1)+std::abs(y1);
395  long l2=std::abs(x2)+std::abs(y2);
396  if ((l1<=l2) != (pView!=nullptr && pView->IsBigOrtho())) {
397  x=x1; y=y1;
398  } else {
399  x=x2; y=y2;
400  }
401  }
402  return Point(x,y);
403 }
404 
405 void ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
406 {
407  aLineStart=rP1;
408  aLineEnd=rP2;
409  bLine90=false;
410  if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=false; return; }
411  Point aTmpPt(rP2-rP1);
412  long nDirX=rDir.X();
413  long nDirY=rDir.Y();
414  Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
415  Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
416  if (pView!=nullptr && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
417  bLine90=nQ1>2*nQ2;
418  if (!bLine90) { // smooth transition
419  aLineEnd+=aP1;
420  } else { // rectangular transition
421  aLineEnd+=aP2;
422  }
423  bLine=true;
424 }
425 
426 XPolygon ImpPathCreateUser::GetLinePoly() const
427 {
428  XPolygon aXP(2);
429  aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,PolyFlags::Smooth);
430  aXP[1]=aLineEnd;
431  return aXP;
432 }
433 
434 void ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
435 {
436  aRectP1=rP1;
437  aRectP2=rP1;
438  aRectP3=rP2;
439  if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=false; return; }
440  Point aTmpPt(rP2-rP1);
441  long nDirX=rDir.X();
442  long nDirY=rDir.Y();
443  long x=aTmpPt.X();
444  long y=aTmpPt.Y();
445  bool bHLin=nDirY==0;
446  bool bVLin=nDirX==0;
447  if (bHLin) y=0;
448  else if (bVLin) x=0;
449  else {
450  y=BigMulDiv(x,nDirY,nDirX);
451  long nHypLen=aTmpPt.Y()-y;
452  long nTangAngle=-GetAngle(rDir);
453  // sin=g/h, g=h*sin
454  double a = nTangAngle * F_PI18000;
455  double sn=sin(a);
456  double cs=cos(a);
457  double nGKathLen=nHypLen*sn;
458  y+=FRound(nGKathLen*sn);
459  x+=FRound(nGKathLen*cs);
460  }
461  aRectP2.AdjustX(x );
462  aRectP2.AdjustY(y );
463  if (pView!=nullptr && pView->IsOrtho()) {
464  long dx1=aRectP2.X()-aRectP1.X(); long dx1a=std::abs(dx1);
465  long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=std::abs(dy1);
466  long dx2=aRectP3.X()-aRectP2.X(); long dx2a=std::abs(dx2);
467  long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=std::abs(dy2);
468  bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
469  if (b1MoreThan2 != pView->IsBigOrtho()) {
470  long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
471  long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
472  aRectP2.AdjustX(xtemp );
473  aRectP2.AdjustY(ytemp );
474  aRectP3.AdjustX(xtemp );
475  aRectP3.AdjustY(ytemp );
476  } else {
477  long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
478  long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
479  aRectP3.AdjustX(xtemp );
480  aRectP3.AdjustY(ytemp );
481  }
482  }
483  bRect=true;
484 }
485 
486 XPolygon ImpPathCreateUser::GetRectPoly() const
487 {
488  XPolygon aXP(3);
489  aXP[0]=aRectP1; aXP.SetFlags(0,PolyFlags::Smooth);
490  aXP[1]=aRectP2;
491  if (aRectP3!=aRectP2) aXP[2]=aRectP3;
492  return aXP;
493 }
494 
496 {
500  std::unique_ptr<ImpSdrPathDragData>
503 
504 public:
505  explicit ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
506 
507  // drag stuff
508  bool beginPathDrag( SdrDragStat const & rDrag ) const;
509  bool movePathDrag( SdrDragStat& rDrag ) const;
510  bool endPathDrag( SdrDragStat const & rDrag );
511  OUString getSpecialDragComment(const SdrDragStat& rDrag) const;
513 
514  // create stuff
515  void BegCreate(SdrDragStat& rStat);
516  bool MovCreate(SdrDragStat& rStat);
517  bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
518  bool BckCreate(SdrDragStat const & rStat);
519  void BrkCreate(SdrDragStat& rStat);
521 
522  // helping stuff
523  static bool IsClosed(SdrObjKind eKind) { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
524  static bool IsFreeHand(SdrObjKind eKind) { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
525  static bool IsBezier(SdrObjKind eKind) { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
526  bool IsCreating() const { return mbCreating; }
527 
528  // get the polygon
532 };
533 
535 : mrSdrPathObject(rSdrPathObject),
536  aPathPolygon(rSdrPathObject.GetPathPoly()),
537  meObjectKind(mrSdrPathObject.meKind),
538  mbCreating(false)
539 {
540 }
541 
543 {
544  const SdrHdl* pHdl=rDrag.GetHdl();
545  if(!pHdl)
546  return false;
547 
548  bool bMultiPointDrag(true);
549 
550  if(aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())].IsControl(static_cast<sal_uInt16>(pHdl->GetPointNum())))
551  bMultiPointDrag = false;
552 
553  if(bMultiPointDrag)
554  {
555  const SdrMarkView& rMarkView = *rDrag.GetView();
556  const SdrHdlList& rHdlList = rMarkView.GetHdlList();
557  const size_t nHdlCount = rHdlList.GetHdlCount();
558  const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
559  sal_uInt32 nSelectedPoints(0);
560 
561  for(size_t a = 0; a < nHdlCount; ++a)
562  {
563  SdrHdl* pTestHdl = rHdlList.GetHdl(a);
564 
565  if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
566  {
567  nSelectedPoints++;
568  }
569  }
570 
571  if(nSelectedPoints <= 1)
572  bMultiPointDrag = false;
573  }
574 
575  const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset( new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag) );
576 
577  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
578  {
579  OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
580  const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset();
581  return false;
582  }
583 
584  return true;
585 }
586 
588 {
589  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
590  {
591  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
592  return false;
593  }
594 
595  if(mpSdrPathDragData->IsMultiPointDrag())
596  {
597  Point aDelta(rDrag.GetNow() - rDrag.GetStart());
598 
599  if(aDelta.X() || aDelta.Y())
600  {
601  for(SdrHdl* pHandle : mpSdrPathDragData->maHandles)
602  {
603  const sal_uInt16 nPolyIndex(static_cast<sal_uInt16>(pHandle->GetPolyNum()));
604  const sal_uInt16 nPointIndex(static_cast<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]);
609 
610  // move point itself
611  rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
612 
613  // when point is first and poly closed, move close point, too.
614  if(nPointCount > 0 && !nPointIndex && bClosed)
615  {
616  rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
617 
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;
622  }
623 
624  // is a control point before this?
625  if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
626  {
627  // Yes, move it, too
628  rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
629  }
630 
631  // is a control point after this?
632  if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
633  {
634  // Yes, move it, too
635  rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
636  }
637  }
638  }
639  }
640  else
641  {
643 
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
659 
660  // Ortho for lines/polygons: keep angle
661  if (!bControl && rDrag.GetView()!=nullptr && 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 aNewPos1,aNewPos2; // new alternative for aPos
667  bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
668  if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines
669  if (!bBegPnt) nPnt1=nPrevPnt;
670  if (!bEndPnt) nPnt2=nNextPnt;
671  }
672  if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
673  nPnt1=nPrevPnt;
674  nPnt2=nNextPnt;
675  }
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=true;
686  double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
687  double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<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  aNewPos1=aPnt1;
693  aNewPos1.AdjustX(ndx );
694  aNewPos1.AdjustY(ndy );
695  }
696  }
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=true;
707  double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
708  double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<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  aNewPos2=aPnt2;
714  aNewPos2.AdjustX(ndx );
715  aNewPos2.AdjustY(ndy );
716  }
717  }
718  if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
719  BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
720  BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
721  BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
722  BigInt nY2(aNewPos2.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=false; else bPnt1=false;
727  }
728  if (bPnt1) rDrag.SetNow(aNewPos1);
729  if (bPnt2) rDrag.SetNow(aNewPos2);
730  }
731  rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
732 
733  // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
734  if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
735  !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
736  {
737  Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
738  aPt-=rDrag.GetNow();
739  long nAngle1=GetAngle(aPt);
740  aPt=rDrag.GetNow();
741  aPt-=mpSdrPathDragData->aXP[nPrevPnt];
742  long nAngle2=GetAngle(aPt);
743  long nDiff=nAngle1-nAngle2;
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.SetNow(aPt);
751  }
752  }
753 
754  // we dragged by this distance
755  Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
756 
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).
768  */
769 
770  mpSdrPathDragData->aXP[nPnt]+=aDiff;
771 
772  // now check symmetric plus handles
773  if (bControl) { // cases 5,6,7,8
774  sal_uInt16 nSt; // the associated support point
775  sal_uInt16 nFix; // 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;
782  }
783  if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
784  mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
785  }
786  }
787 
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);
796  }
797  if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
798  mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
799  }
800  }
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);
807  }
808  }
809  if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
810  if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
811  mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
812  }
813  }
814  }
815  }
816 
817  return true;
818 }
819 
821 {
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];
829  }
830 
831  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
832  {
833  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
834  return false;
835  }
836 
837  if(mpSdrPathDragData->IsMultiPointDrag())
838  {
840  }
841  else
842  {
843  const SdrHdl* pHdl=rDrag.GetHdl();
844 
845  // reference the polygon
846  XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
847 
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];
854 
855  // for closed objects: last point has to be equal to first point
856  if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
857 
858  if (mpSdrPathDragData->bEliminate)
859  {
861  sal_uInt32 nPoly,nPnt;
862 
863  if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
864  {
865  basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
866  aCandidate.remove(nPnt);
867 
868  if(aCandidate.count() < 2)
869  {
870  aTempPolyPolygon.remove(nPoly);
871  }
872  else
873  {
874  aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
875  }
876  }
877 
878  aPathPolygon = XPolyPolygon(aTempPolyPolygon);
879  }
880 
881  // adapt angle for text beneath a simple line
882  if (bLineGlueMirror)
883  {
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) {
890  if (bXMirr) {
891  Point aRef2(aRef1);
892  aRef2.AdjustY( 1 );
894  }
895  if (bYMirr) {
896  Point aRef2(aRef1);
897  aRef2.AdjustX( 1 );
899  }
900  }
901  }
902  }
903 
904  mpSdrPathDragData.reset();
905 
906  return true;
907 }
908 
910 {
911  OUString aStr;
912  const SdrHdl* pHdl = rDrag.GetHdl();
913  const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
914 
915  if(bCreateComment && rDrag.GetUser())
916  {
917  // #i103058# re-add old creation comment mode
918  const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
919  const SdrObjKind eOriginalKind(meObjectKind);
920  mrSdrPathObject.meKind = pU->eCurrentKind;
921  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewCreateObj);
922  mrSdrPathObject.meKind = eOriginalKind;
923 
924  Point aPrev(rDrag.GetPrev());
925  Point aNow(rDrag.GetNow());
926 
927  if(pU->bLine)
928  aNow = pU->aLineEnd;
929 
930  aNow -= aPrev;
931  aStr += " (";
932 
933  if(pU->bCircle)
934  {
935  aStr += SdrModel::GetAngleString(std::abs(pU->nCircRelAngle))
936  + " r="
937  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(pU->nCircRadius, true);
938  }
939 
940  aStr += "dx="
942  + " dy="
944 
946  {
947  sal_Int32 nLen(GetLen(aNow));
948  sal_Int32 nAngle(GetAngle(aNow));
949  aStr += " l="
951  + " "
952  + SdrModel::GetAngleString(nAngle);
953  }
954 
955  aStr += ")";
956  }
957  else if(!pHdl)
958  {
959  // #i103058# fallback when no model and/or Handle, both needed
960  // for else-path
961  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
962  }
963  else
964  {
965  // #i103058# standard for modification; model and handle needed
966  ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
967 
968  if(!pDragData)
969  {
970  // getSpecialDragComment is also used from create, so fallback to GetUser()
971  // when mpSdrPathDragData is not set
972  pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
973  }
974 
975  if(!pDragData)
976  {
977  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
978  return OUString();
979  }
980 
981  if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
982  {
983  // point of ...
984  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
985 
986  // delete %O
987  OUString aStr2(SvxResId(STR_EditDelete));
988 
989  // UNICODE: delete point of ...
990  aStr2 = aStr2.replaceFirst("%1", aStr);
991 
992  return aStr2;
993  }
994 
995  // dx=0.00 dy=0.00 -- both sides bezier
996  // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
997  // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
998  Point aBeg(rDrag.GetStart());
999  Point aNow(rDrag.GetNow());
1000 
1001  aStr.clear();
1002  aStr += "dx="
1003  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X() - aBeg.X(), true)
1004  + " dy="
1005  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y() - aBeg.Y(), true);
1006 
1007  if(!pDragData->IsMultiPointDrag())
1008  {
1009  sal_uInt16 nPntNum(static_cast<sal_uInt16>(pHdl->GetPointNum()));
1010  const XPolygon& rXPoly = aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1011  sal_uInt16 nPointCount(rXPoly.GetPointCount());
1012  bool bClose(IsClosed(meObjectKind));
1013 
1014  if(bClose)
1015  nPointCount--;
1016 
1017  if(pHdl->IsPlusHdl())
1018  {
1019  // lever
1020  sal_uInt16 nRef(nPntNum);
1021 
1022  if(rXPoly.IsControl(nPntNum + 1))
1023  nRef--;
1024  else
1025  nRef++;
1026 
1027  aNow -= rXPoly[nRef];
1028 
1029  sal_Int32 nLen(GetLen(aNow));
1030  sal_Int32 nAngle(GetAngle(aNow));
1031  aStr += " l="
1033  + " "
1034  + SdrModel::GetAngleString(nAngle);
1035  }
1036  else if(nPointCount > 1)
1037  {
1038  sal_uInt16 nPntMax(nPointCount - 1);
1039  bool bIsClosed(IsClosed(meObjectKind));
1040  bool bPt1(nPntNum > 0);
1041  bool bPt2(nPntNum < nPntMax);
1042 
1043  if(bIsClosed && nPointCount > 2)
1044  {
1045  bPt1 = true;
1046  bPt2 = true;
1047  }
1048 
1049  sal_uInt16 nPt1,nPt2;
1050 
1051  if(nPntNum > 0)
1052  nPt1 = nPntNum - 1;
1053  else
1054  nPt1 = nPntMax;
1055 
1056  if(nPntNum < nPntMax)
1057  nPt2 = nPntNum + 1;
1058  else
1059  nPt2 = 0;
1060 
1061  if(bPt1 && rXPoly.IsControl(nPt1))
1062  bPt1 = false; // don't display
1063 
1064  if(bPt2 && rXPoly.IsControl(nPt2))
1065  bPt2 = false; // of bezier data
1066 
1067  if(bPt1)
1068  {
1069  Point aPt(aNow);
1070  aPt -= rXPoly[nPt1];
1071 
1072  sal_Int32 nLen(GetLen(aPt));
1073  sal_Int32 nAngle(GetAngle(aPt));
1074  aStr += " l="
1076  + " "
1077  + SdrModel::GetAngleString(nAngle);
1078  }
1079 
1080  if(bPt2)
1081  {
1082  if(bPt1)
1083  aStr += " / ";
1084  else
1085  aStr += " ";
1086 
1087  Point aPt(aNow);
1088  aPt -= rXPoly[nPt2];
1089 
1090  sal_Int32 nLen(GetLen(aPt));
1091  sal_Int32 nAngle(GetAngle(aPt));
1092  aStr += "l="
1094  + " "
1095  + SdrModel::GetAngleString(nAngle);
1096  }
1097  }
1098  }
1099  }
1100 
1101  return aStr;
1102 }
1103 
1105 {
1106  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1107  {
1108  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1109  return basegfx::B2DPolyPolygon();
1110  }
1111 
1112  XPolyPolygon aRetval;
1113 
1114  if(mpSdrPathDragData->IsMultiPointDrag())
1115  {
1116  aRetval.Insert(mpSdrPathDragData->maMove);
1117  }
1118  else
1119  {
1120  const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1121  if (rXP.GetPointCount()<=2) {
1122  XPolygon aXPoly(rXP);
1123  aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
1124  aRetval.Insert(std::move(aXPoly));
1125  return aRetval.getB2DPolyPolygon();
1126  }
1127  // copy certain data locally to use less code and have faster access times
1128  bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
1129  sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
1130  sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
1131  bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
1132  bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
1133  sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
1134  sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
1135  bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
1136  bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
1137  sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
1138  sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
1139  bool bControl =mpSdrPathDragData->bControl ; // point is a control point
1140  bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
1141  bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
1142  bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
1143  XPolygon aXPoly(mpSdrPathDragData->aXP);
1144  XPolygon aLine1(2);
1145  XPolygon aLine2(2);
1146  XPolygon aLine3(2);
1147  XPolygon aLine4(2);
1148  if (bControl) {
1149  aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1150  if (bIsNextControl) { // is this a control point after the support point?
1151  aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1152  aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1153  aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1154  if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1155  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1156  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1157  // leverage lines for the opposing curve segment
1158  aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1159  aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1160  aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1161  aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1162  } else {
1163  aXPoly.Remove(0,1);
1164  }
1165  } else { // else this is a control point before a support point
1166  aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1167  aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1168  aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1169  if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1170  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1171  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1172  // leverage lines for the opposing curve segment
1173  aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1174  aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1175  aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1176  aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1177  } else {
1178  aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1179  }
1180  }
1181  } else { // else is not a control point
1182  if (mpSdrPathDragData->bEliminate) {
1183  aXPoly.Remove(2,1);
1184  }
1185  if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal);
1186  else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1187  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1188  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1189  } else {
1190  aXPoly.Remove(0,1);
1191  if (bBegPnt) aXPoly.Remove(0,1);
1192  }
1193  if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal);
1194  else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1195  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1196  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1197  } else {
1198  aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1199  if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1200  }
1201  if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1202  if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
1203  sal_uInt16 a=aXPoly.GetPointCount();
1204  aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1205  aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1206  aXPoly.Remove(0,3);
1207  }
1208  }
1209  }
1210  aRetval.Insert(std::move(aXPoly));
1211  if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1));
1212  if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2));
1213  if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3));
1214  if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
1215  }
1216 
1217  return aRetval.getB2DPolyPolygon();
1218 }
1219 
1221 {
1222  bool bFreeHand(IsFreeHand(meObjectKind));
1223  rStat.SetNoSnap(bFreeHand);
1224  rStat.SetOrtho8Possible();
1225  aPathPolygon.Clear();
1226  mbCreating=true;
1227  bool bMakeStartPoint = true;
1228  SdrView* pView=rStat.GetView();
1229  if (pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface() &&
1231  bMakeStartPoint = false;
1232  }
1234  aPathPolygon[0][0]=rStat.GetStart();
1235  if (bMakeStartPoint) {
1236  aPathPolygon[0][1]=rStat.GetNow();
1237  }
1238  std::unique_ptr<ImpPathCreateUser> pU(new ImpPathCreateUser);
1239  pU->eStartKind=meObjectKind;
1240  pU->eCurrentKind=meObjectKind;
1241  rStat.SetUser(std::move(pU));
1242 }
1243 
1245 {
1246  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1247  SdrView* pView=rStat.GetView();
1248  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1249  if (pView!=nullptr && pView->IsCreateMode()) {
1250  // switch to different CreateTool, if appropriate
1251  sal_uInt16 nIdent;
1252  SdrInventor nInvent;
1253  pView->TakeCurrentObj(nIdent,nInvent);
1254  if (nInvent==SdrInventor::Default && pU->eCurrentKind!=static_cast<SdrObjKind>(nIdent)) {
1255  SdrObjKind eNewKind=static_cast<SdrObjKind>(nIdent);
1256  switch (eNewKind) {
1257  case OBJ_CARC:
1258  case OBJ_CIRC:
1259  case OBJ_CCUT:
1260  case OBJ_SECT:
1261  eNewKind=OBJ_CARC;
1262  [[fallthrough]];
1263  case OBJ_RECT:
1264  case OBJ_LINE:
1265  case OBJ_PLIN:
1266  case OBJ_POLY:
1267  case OBJ_PATHLINE:
1268  case OBJ_PATHFILL:
1269  case OBJ_FREELINE:
1270  case OBJ_FREEFILL:
1271  case OBJ_SPLNLINE:
1272  case OBJ_SPLNFILL: {
1273  pU->eCurrentKind=eNewKind;
1274  pU->bMixedCreate=true;
1275  pU->nBezierStartPoint=rXPoly.GetPointCount();
1276  if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1277  } break;
1278  default: break;
1279  } // switch
1280  }
1281  }
1282  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1283  if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
1284  rXPoly[0]=rStat.GetPos0();
1285  rXPoly[1]=rStat.GetNow();
1286  nCurrentPoint=2;
1287  }
1288  if (nCurrentPoint==0) {
1289  rXPoly[0]=rStat.GetPos0();
1290  } else nCurrentPoint--;
1291  bool bFreeHand=IsFreeHand(pU->eCurrentKind);
1292  rStat.SetNoSnap(bFreeHand);
1293  rStat.SetOrtho8Possible(pU->eCurrentKind!=OBJ_CARC && pU->eCurrentKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eCurrentKind!=OBJ_LINE));
1294  rXPoly[nCurrentPoint]=rStat.GetNow();
1295  if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1296  Point aPt(rStat.GetStart());
1297  if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
1298  aPt+=aPt;
1299  aPt-=rStat.GetNow();
1300  }
1301  rXPoly[0]=aPt;
1302  }
1303  OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice();
1304  if (bFreeHand) {
1305  if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1306  if (rStat.IsMouseDown() && nCurrentPoint>0) {
1307  // don't allow two consecutive points to occupy too similar positions
1308  long nMinDist=1;
1309  if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix();
1310  if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1311  if (nMinDist<1) nMinDist=1;
1312 
1313  Point aPt0(rXPoly[nCurrentPoint-1]);
1314  Point aPt1(rStat.GetNow());
1315  long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1316  long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1317  if (dx<nMinDist && dy<nMinDist) return false;
1318 
1319  // TODO: the following is copied from EndCreate (with a few smaller modifications)
1320  // and should be combined into a method with the code there.
1321 
1322  if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1323  rXPoly.PointsToBezier(nCurrentPoint-3);
1324  rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1325  rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1326 
1327  if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1328  rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1329  rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1330  }
1331  }
1332  rXPoly[nCurrentPoint+1]=rStat.GetNow();
1333  rStat.NextPoint();
1334  } else {
1335  pU->nBezierStartPoint=nCurrentPoint;
1336  }
1337  }
1338 
1339  pU->ResetFormFlags();
1340  if (IsBezier(pU->eCurrentKind)) {
1341  if (nCurrentPoint>=2) {
1342  pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],rStat.IsMouseDown());
1343  } else if (pU->bBezHasCtrl0) {
1344  pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],pU->aBezControl0-rXPoly[nCurrentPoint-1],rStat.IsMouseDown());
1345  }
1346  }
1347  if (pU->eCurrentKind==OBJ_CARC && nCurrentPoint>=2) {
1348  pU->CalcCircle(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1349  }
1350  if (pU->eCurrentKind==OBJ_LINE && nCurrentPoint>=2) {
1351  pU->CalcLine(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1352  }
1353  if (pU->eCurrentKind==OBJ_RECT && nCurrentPoint>=2) {
1354  pU->CalcRect(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1355  }
1356 
1357  return true;
1358 }
1359 
1361 {
1362  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1363  bool bRet = false;
1364  SdrView* pView=rStat.GetView();
1365  bool bIncomp=pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface();
1366  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1367  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount()-1;
1368  rXPoly[nCurrentPoint]=rStat.GetNow();
1369  if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1370  if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1371  bRet = eCmd==SdrCreateCmd::ForceEnd;
1372  if (bRet) {
1373  mbCreating = false;
1374  rStat.SetUser(nullptr);
1375  }
1376  return bRet;
1377  }
1378 
1379  if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1380  if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1381  bRet=eCmd==SdrCreateCmd::ForceEnd;
1382  if (bRet) {
1383  mbCreating=false;
1384  rStat.SetUser(nullptr);
1385  }
1386  return bRet;
1387  }
1389  // don't allow two consecutive points to occupy the same position
1390  if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) {
1391  if (bIncomp) {
1392  if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1393  if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1394  rXPoly.PointsToBezier(nCurrentPoint-3);
1395  rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1396  rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1397 
1398  if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1399  rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1400  rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1401  }
1402  }
1403  } else {
1404  if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
1405  pU->aBezControl0=rStat.GetNow();
1406  pU->bBezHasCtrl0=true;
1407  nCurrentPoint--;
1408  }
1409  if (pU->IsFormFlag()) {
1410  sal_uInt16 nPointCount0=rXPoly.GetPointCount();
1411  rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
1412  rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1413  sal_uInt16 nPointCount1=rXPoly.GetPointCount();
1414  for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work
1415  if (!rXPoly.IsControl(i)) rStat.NextPoint();
1416  }
1417  nCurrentPoint=rXPoly.GetPointCount()-1;
1418  }
1419  }
1420  nCurrentPoint++;
1421  rXPoly[nCurrentPoint]=rStat.GetNow();
1422  }
1423  if (eCmd==SdrCreateCmd::NextObject) {
1424  if (rXPoly.GetPointCount()>=2) {
1425  pU->bBezHasCtrl0=false;
1426  // only a singular polygon may be opened, so close this
1427  rXPoly[nCurrentPoint]=rXPoly[0];
1428  XPolygon aXP;
1429  aXP[0]=rStat.GetNow();
1430  aPathPolygon.Insert(std::move(aXP));
1431  }
1432  }
1433  }
1434 
1435  sal_uInt16 nPolyCount=aPathPolygon.Count();
1436  if (nPolyCount!=0) {
1437  // delete last point, if necessary
1438  if (eCmd==SdrCreateCmd::ForceEnd) {
1439  XPolygon& rXP=aPathPolygon[nPolyCount-1];
1440  sal_uInt16 nPointCount=rXP.GetPointCount();
1441  if (nPointCount>=2) {
1442  if (!rXP.IsControl(nPointCount-2)) {
1443  if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
1444  rXP.Remove(nPointCount-1,1);
1445  }
1446  } else {
1447  if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
1448  rXP.Remove(nPointCount-3,3);
1449  }
1450  }
1451  }
1452  }
1453  for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
1454  nPolyNum--;
1455  XPolygon& rXP=aPathPolygon[nPolyNum];
1456  sal_uInt16 nPointCount=rXP.GetPointCount();
1457  // delete polygons with too few points
1458  if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) {
1459  if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
1460  }
1461  }
1462  }
1463  pU->ResetFormFlags();
1464  bRet=eCmd==SdrCreateCmd::ForceEnd;
1465  if (bRet) {
1466  mbCreating=false;
1467  rStat.SetUser(nullptr);
1468  }
1469  return bRet;
1470 }
1471 
1473 {
1474  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1475  if (aPathPolygon.Count()>0) {
1476  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1477  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1478  if (nCurrentPoint>0) {
1479  nCurrentPoint--;
1480  // make the last part of a bezier curve a line
1481  rXPoly.Remove(nCurrentPoint,1);
1482  if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) {
1483  // there should never be a bezier segment at the end, so this is just in case...
1484  rXPoly.Remove(nCurrentPoint-1,1);
1485  if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1486  }
1487  }
1488  nCurrentPoint=rXPoly.GetPointCount();
1489  if (nCurrentPoint>=4) { // no bezier segment at the end
1490  nCurrentPoint--;
1491  if (rXPoly.IsControl(nCurrentPoint-1)) {
1492  rXPoly.Remove(nCurrentPoint-1,1);
1493  if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1494  }
1495  }
1496  if (rXPoly.GetPointCount()<2) {
1498  }
1499  if (aPathPolygon.Count()>0) {
1500  XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1501  sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount();
1502  if (nLocalCurrentPoint>0) {
1503  nLocalCurrentPoint--;
1504  rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
1505  }
1506  }
1507  }
1508  pU->ResetFormFlags();
1509  return aPathPolygon.Count()!=0;
1510 }
1511 
1513 {
1514  aPathPolygon.Clear();
1515  mbCreating=false;
1516  rStat.SetUser(nullptr);
1517 }
1518 
1520 {
1522  SdrView* pView = rDrag.GetView();
1523 
1524  if(pView && pView->IsUseIncompatiblePathCreateInterface())
1525  return aRetval;
1526 
1527  ImpPathCreateUser* pU = static_cast<ImpPathCreateUser*>(rDrag.GetUser());
1528  basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1) : basegfx::B2DPolygon());
1529 
1530  if(pU->IsFormFlag() && aNewPolygon.count() > 1)
1531  {
1532  // remove last segment and replace with current
1533  // do not forget to rescue the previous control point which will be lost when
1534  // the point it's associated with is removed
1535  const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1536  const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1537 
1538  aNewPolygon.remove(nChangeIndex, 2);
1539  aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1540 
1541  if(nChangeIndex < aNewPolygon.count())
1542  {
1543  // if really something was added, set the saved previous control point to the
1544  // point where it belongs
1545  aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1546  }
1547  }
1548 
1549  if(aRetval.count())
1550  {
1551  aRetval.setB2DPolygon(aRetval.count() - 1, aNewPolygon);
1552  }
1553  else
1554  {
1555  aRetval.append(aNewPolygon);
1556  }
1557 
1558  return aRetval;
1559 }
1560 
1562 {
1563  basegfx::B2DPolyPolygon aRetval;
1564  SdrView* pView = rDrag.GetView();
1565 
1566  if(pView && pView->IsUseIncompatiblePathCreateInterface())
1567  return aRetval;
1568 
1569  const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
1570 
1571  if(pU && pU->bBezier && rDrag.IsMouseDown())
1572  {
1573  // no more XOR, no need for complicated helplines
1574  basegfx::B2DPolygon aHelpline;
1575  aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1576  aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1577  aRetval.append(aHelpline);
1578  }
1579 
1580  return aRetval;
1581 }
1582 
1584 {
1585  switch (meObjectKind) {
1586  case OBJ_LINE : return PointerStyle::DrawLine;
1587  case OBJ_POLY : return PointerStyle::DrawPolygon;
1588  case OBJ_PLIN : return PointerStyle::DrawPolygon;
1589  case OBJ_PATHLINE: return PointerStyle::DrawBezier;
1590  case OBJ_PATHFILL: return PointerStyle::DrawBezier;
1591  case OBJ_FREELINE: return PointerStyle::DrawFreehand;
1592  case OBJ_FREEFILL: return PointerStyle::DrawFreehand;
1593  case OBJ_SPLNLINE: return PointerStyle::DrawFreehand;
1594  case OBJ_SPLNFILL: return PointerStyle::DrawFreehand;
1595  case OBJ_PATHPOLY: return PointerStyle::DrawPolygon;
1596  case OBJ_PATHPLIN: return PointerStyle::DrawPolygon;
1597  default: break;
1598  } // switch
1599  return PointerStyle::Cross;
1600 }
1601 
1603  : meKind(OBJ_NONE)
1604 {
1605 }
1606 
1608 {
1609 }
1610 
1611 // DrawContact section
1612 
1613 std::unique_ptr<sdr::contact::ViewContact> SdrPathObj::CreateObjectSpecificViewContact()
1614 {
1615  return std::make_unique<sdr::contact::ViewContactOfSdrPathObj>(*this);
1616 }
1617 
1618 
1620  SdrModel& rSdrModel,
1621  SdrObjKind eNewKind)
1622 : SdrTextObj(rSdrModel),
1623  meKind(eNewKind)
1624 {
1625  bClosedObj = IsClosed();
1626 }
1627 
1629  SdrModel& rSdrModel,
1630  SdrObjKind eNewKind,
1631  const basegfx::B2DPolyPolygon& rPathPoly)
1632 : SdrTextObj(rSdrModel),
1633  maPathPolygon(rPathPoly),
1634  meKind(eNewKind)
1635 {
1636  bClosedObj = IsClosed();
1637  ImpForceKind();
1638 }
1639 
1640 SdrPathObj::~SdrPathObj() = default;
1641 
1642 static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1643 {
1644  return (1 == rPolyPolygon.count() && 2 == rPolyPolygon.getB2DPolygon(0).count());
1645 }
1646 
1648 {
1649  basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
1650 
1651  if (aRange.isEmpty())
1652  return tools::Rectangle();
1653 
1654  return tools::Rectangle(
1655  FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1656  FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1657 }
1658 
1660 {
1662  return;
1663 
1664  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1665  const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1666  const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1667  const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1668  const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1669  const basegfx::B2DPoint aB2DDelt(aB2DPoint1 - aB2DPoint0);
1670  const Point aDelt(FRound(aB2DDelt.getX()), FRound(aB2DDelt.getY()));
1671 
1672  aGeo.nRotationAngle=GetAngle(aDelt);
1673  aGeo.nShearAngle=0;
1674  aGeo.RecalcSinCos();
1675  aGeo.RecalcTan();
1676 
1677  // for SdrTextObj, keep aRect up to date
1678  maRect = tools::Rectangle::Justify(aPoint0, aPoint1);
1679 }
1680 
1682 {
1685 
1686  if(GetPathPoly().areControlPointsUsed())
1687  {
1688  switch (meKind)
1689  {
1690  case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1691  case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1692  case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1693  default: break;
1694  }
1695  }
1696  else
1697  {
1698  switch (meKind)
1699  {
1700  case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1701  case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1702  case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1703  case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1704  default: break;
1705  }
1706  }
1707 
1710 
1711  bClosedObj=IsClosed();
1712 
1713  if (meKind==OBJ_LINE)
1714  {
1716  }
1717  else
1718  {
1719  // #i10659#, for polys with more than 2 points.
1720 
1721  // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1722  // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1723  // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1724  // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1725  // this is the case, some size needs to be set here in aRect to avoid that the cycle
1726  // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1727  // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1728  // from the local Resize() implementation.
1729 
1730  // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1731  // text rectangle for the text object itself and methods at SdrTextObj do handle it
1732  // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1733  // which is basically wrong. To make the SdrText methods which deal with aRect directly
1734  // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1735  // command for SdrPathObj. Since adding this update mechanism with #101412# to
1736  // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
1737  // was called, once here below and once on a 2nd place below.
1738 
1739  // #i10659# for SdrTextObj, keep aRect up to date
1740  if(GetPathPoly().count())
1741  {
1743  }
1744  }
1745 
1746  // #i75974# adapt polygon state to object type. This may include a reinterpretation
1747  // of a closed geometry as open one, but with identical first and last point
1748  for(auto& rPolygon : maPathPolygon)
1749  {
1750  if(IsClosed() != rPolygon.isClosed())
1751  {
1752  // #i80213# really change polygon geometry; else e.g. the last point which
1753  // needs to be identical with the first one will be missing when opening
1754  // due to OBJ_PATH type
1755  if(rPolygon.isClosed())
1756  {
1758  }
1759  else
1760  {
1762  }
1763  }
1764  }
1765 }
1766 
1767 void SdrPathObj::ImpSetClosed(bool bClose)
1768 {
1769  if(bClose)
1770  {
1771  switch (meKind)
1772  {
1773  case OBJ_LINE : meKind=OBJ_POLY; break;
1774  case OBJ_PLIN : meKind=OBJ_POLY; break;
1775  case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1776  case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1777  case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1778  default: break;
1779  }
1780 
1781  bClosedObj = true;
1782  }
1783  else
1784  {
1785  switch (meKind)
1786  {
1787  case OBJ_POLY : meKind=OBJ_PLIN; break;
1788  case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1789  case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1790  case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1791  default: break;
1792  }
1793 
1794  bClosedObj = false;
1795  }
1796 
1797  ImpForceKind();
1798 }
1799 
1801 {
1802  rInfo.bNoContortion=false;
1803 
1804  bool bCanConv = !HasText() || ImpCanConvTextToCurve();
1805  bool bIsPath = IsBezier() || IsSpline();
1806 
1807  rInfo.bEdgeRadiusAllowed = false;
1808  rInfo.bCanConvToPath = bCanConv && !bIsPath;
1809  rInfo.bCanConvToPoly = bCanConv && bIsPath;
1811 }
1812 
1814 {
1815  return sal_uInt16(meKind);
1816 }
1817 
1819 {
1820  return CloneHelper< SdrPathObj >(rTargetModel);
1821 }
1822 
1824 {
1825  if( this == &rObj )
1826  return *this;
1827  SdrTextObj::operator=(rObj);
1828  maPathPolygon=rObj.GetPathPoly();
1829  return *this;
1830 }
1831 
1833 {
1834  OUStringBuffer sName;
1835 
1836  if(OBJ_LINE == meKind)
1837  {
1838  const char* pId(STR_ObjNameSingulLINE);
1839 
1840  if(lcl_ImpIsLine(GetPathPoly()))
1841  {
1842  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1843  const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1844  const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1845 
1846  if(aB2DPoint0 != aB2DPoint1)
1847  {
1848  if(aB2DPoint0.getY() == aB2DPoint1.getY())
1849  {
1850  pId = STR_ObjNameSingulLINE_Hori;
1851  }
1852  else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1853  {
1854  pId = STR_ObjNameSingulLINE_Vert;
1855  }
1856  else
1857  {
1858  const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1859  const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1860 
1861  if(fDx == fDy)
1862  {
1863  pId = STR_ObjNameSingulLINE_Diag;
1864  }
1865  }
1866  }
1867  }
1868 
1869  sName.append(SvxResId(pId));
1870  }
1871  else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1872  {
1873  const bool bClosed(OBJ_POLY == meKind);
1874  const char* pId(nullptr);
1875 
1876  if(mpDAC && mpDAC->IsCreating())
1877  {
1878  if(bClosed)
1879  {
1880  pId = STR_ObjNameSingulPOLY;
1881  }
1882  else
1883  {
1884  pId = STR_ObjNameSingulPLIN;
1885  }
1886 
1887  sName.append(SvxResId(pId));
1888  }
1889  else
1890  {
1891  // get point count
1892  sal_uInt32 nPointCount(0);
1893 
1894  for(auto const& rPolygon : GetPathPoly())
1895  {
1896  nPointCount += rPolygon.count();
1897  }
1898 
1899  if(bClosed)
1900  {
1901  pId = STR_ObjNameSingulPOLY_PointCount;
1902  }
1903  else
1904  {
1905  pId = STR_ObjNameSingulPLIN_PointCount;
1906  }
1907 
1908  OUString sTemp(SvxResId(pId));
1909  // #i96537#
1910  sName.append(sTemp.replaceFirst("%2", OUString::number(nPointCount)));
1911  }
1912  }
1913  else
1914  {
1915  switch (meKind)
1916  {
1917  case OBJ_PATHLINE: sName.append(SvxResId(STR_ObjNameSingulPATHLINE)); break;
1918  case OBJ_FREELINE: sName.append(SvxResId(STR_ObjNameSingulFREELINE)); break;
1919  case OBJ_SPLNLINE: sName.append(SvxResId(STR_ObjNameSingulNATSPLN)); break;
1920  case OBJ_PATHFILL: sName.append(SvxResId(STR_ObjNameSingulPATHFILL)); break;
1921  case OBJ_FREEFILL: sName.append(SvxResId(STR_ObjNameSingulFREEFILL)); break;
1922  case OBJ_SPLNFILL: sName.append(SvxResId(STR_ObjNameSingulPERSPLN)); break;
1923  default: break;
1924  }
1925  }
1926 
1927  OUString aName(GetName());
1928  if (!aName.isEmpty())
1929  {
1930  sName.append(' ');
1931  sName.append('\'');
1932  sName.append(aName);
1933  sName.append('\'');
1934  }
1935 
1936  return sName.makeStringAndClear();
1937 }
1938 
1940 {
1941  OUString sName;
1942  switch(meKind)
1943  {
1944  case OBJ_LINE : sName=SvxResId(STR_ObjNamePluralLINE ); break;
1945  case OBJ_PLIN : sName=SvxResId(STR_ObjNamePluralPLIN ); break;
1946  case OBJ_POLY : sName=SvxResId(STR_ObjNamePluralPOLY ); break;
1947  case OBJ_PATHLINE: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
1948  case OBJ_FREELINE: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
1949  case OBJ_SPLNLINE: sName=SvxResId(STR_ObjNamePluralNATSPLN); break;
1950  case OBJ_PATHFILL: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
1951  case OBJ_FREEFILL: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
1952  case OBJ_SPLNFILL: sName=SvxResId(STR_ObjNamePluralPERSPLN); break;
1953  default: break;
1954  }
1955  return sName;
1956 }
1957 
1959 {
1960  return GetPathPoly();
1961 }
1962 
1963 sal_uInt32 SdrPathObj::GetHdlCount() const
1964 {
1965  sal_uInt32 nRetval(0);
1966 
1967  for(auto const& rPolygon : GetPathPoly())
1968  {
1969  nRetval += rPolygon.count();
1970  }
1971 
1972  return nRetval;
1973 }
1974 
1976 {
1977  // keep old stuff to be able to keep old SdrHdl stuff, too
1978  const XPolyPolygon aOldPathPolygon(GetPathPoly());
1979  sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
1980  bool bClosed=IsClosed();
1981  sal_uInt16 nIdx=0;
1982 
1983  for (sal_uInt16 i=0; i<nPolyCnt; i++) {
1984  const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
1985  sal_uInt16 nPntCnt=rXPoly.GetPointCount();
1986  if (bClosed && nPntCnt>1) nPntCnt--;
1987 
1988  for (sal_uInt16 j=0; j<nPntCnt; j++) {
1989  if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
1990  const Point& rPnt=rXPoly[j];
1991  std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
1992  pHdl->SetPolyNum(i);
1993  pHdl->SetPointNum(j);
1994  pHdl->Set1PixMore(j==0);
1995  pHdl->SetSourceHdlNum(nIdx);
1996  nIdx++;
1997  rHdlList.AddHdl(std::move(pHdl));
1998  }
1999  }
2000  }
2001 }
2002 
2003 void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
2004 {
2005  // keep old stuff to be able to keep old SdrHdl stuff, too
2006  const XPolyPolygon aOldPathPolygon(GetPathPoly());
2007  sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2008  sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
2009 
2010  if (nPolyNum>=aOldPathPolygon.Count())
2011  return;
2012 
2013  const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2014  sal_uInt16 nPntMax = rXPoly.GetPointCount();
2015 
2016  if (nPntMax<=0)
2017  return;
2018  nPntMax--;
2019  if (nPnt>nPntMax)
2020  return;
2021 
2022  // calculate the number of plus points
2023  sal_uInt16 nCnt = 0;
2024  if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
2025  {
2026  if (nPnt==0 && IsClosed())
2027  nPnt=nPntMax;
2028  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
2029  nCnt++;
2030  if (nPnt==nPntMax && IsClosed())
2031  nPnt=0;
2032  if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2033  nCnt++;
2034  }
2035 
2036  // construct the plus points
2037  for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
2038  {
2039  nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2040  std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
2041  pHdl->SetPolyNum(rHdl.GetPolyNum());
2042 
2043  if (nPnt==0 && IsClosed())
2044  nPnt=nPntMax;
2045  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
2046  {
2047  pHdl->SetPos(rXPoly[nPnt-1]);
2048  pHdl->SetPointNum(nPnt-1);
2049  }
2050  else
2051  {
2052  if (nPnt==nPntMax && IsClosed())
2053  nPnt=0;
2054  if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2055  {
2056  pHdl->SetPos(rXPoly[nPnt+1]);
2057  pHdl->SetPointNum(nPnt+1);
2058  }
2059  }
2060 
2061  pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2062  pHdl->SetPlusHdl(true);
2063  rHdlList.AddHdl(std::move(pHdl));
2064  }
2065 }
2066 
2067 // dragging
2068 
2070 {
2071  return true;
2072 }
2073 
2075 {
2076  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2077 
2078  return aDragAndCreate.beginPathDrag(rDrag);
2079 }
2080 
2082 {
2083  ImpPathForDragAndCreate aDragAndCreate(*this);
2084  bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2085 
2086  if(bRetval)
2087  {
2088  bRetval = aDragAndCreate.movePathDrag(rDrag);
2089  }
2090 
2091  if(bRetval)
2092  {
2093  bRetval = aDragAndCreate.endPathDrag(rDrag);
2094  }
2095 
2096  if(bRetval)
2097  {
2098  NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2099  }
2100 
2101  return bRetval;
2102 }
2103 
2105 {
2106  OUString aRetval;
2107 
2108  if(mpDAC)
2109  {
2110  // #i103058# also get a comment when in creation
2111  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2112 
2113  if(bCreateComment)
2114  {
2115  aRetval = mpDAC->getSpecialDragComment(rDrag);
2116  }
2117  }
2118  else
2119  {
2120  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2121  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2122 
2123  if(bDidWork)
2124  {
2125  aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2126  }
2127  }
2128 
2129  return aRetval;
2130 }
2131 
2133 {
2134  basegfx::B2DPolyPolygon aRetval;
2135  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2136  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2137 
2138  if(bDidWork)
2139  {
2140  aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2141  }
2142 
2143  return aRetval;
2144 }
2145 
2146 // creation
2147 
2149 {
2150  mpDAC.reset();
2151  impGetDAC().BegCreate(rStat);
2152  return true;
2153 }
2154 
2156 {
2157  return impGetDAC().MovCreate(rStat);
2158 }
2159 
2161 {
2162  bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2163 
2164  if(bRetval && mpDAC)
2165  {
2166  SetPathPoly(mpDAC->getModifiedPolyPolygon());
2167 
2168  // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2169  // to be able to use the type-changing ImpSetClosed method
2170  if(!IsClosedObj())
2171  {
2172  SdrView* pView = rStat.GetView();
2173 
2174  if(pView && !pView->IsUseIncompatiblePathCreateInterface())
2175  {
2177 
2178  if(pOut)
2179  {
2180  if(GetPathPoly().count())
2181  {
2182  const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2183 
2184  if(aCandidate.count() > 2)
2185  {
2186  // check distance of first and last point
2187  const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2188  const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2189 
2190  if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
2191  {
2192  // close it
2193  ImpSetClosed(true);
2194  }
2195  }
2196  }
2197  }
2198  }
2199  }
2200 
2201  mpDAC.reset();
2202  }
2203 
2204  return bRetval;
2205 }
2206 
2208 {
2209  return impGetDAC().BckCreate(rStat);
2210 }
2211 
2213 {
2214  impGetDAC().BrkCreate(rStat);
2215  mpDAC.reset();
2216 }
2217 
2218 // polygons
2219 
2221 {
2222  basegfx::B2DPolyPolygon aRetval;
2223 
2224  if(mpDAC)
2225  {
2226  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2228  }
2229 
2230  return aRetval;
2231 }
2232 
2233 // during drag or create, allow accessing the so-far created/modified polyPolygon
2235 {
2236  basegfx::B2DPolyPolygon aRetval;
2237 
2238  if(mpDAC)
2239  {
2240  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2241  }
2242 
2243  return aRetval;
2244 }
2245 
2247 {
2248  basegfx::B2DPolyPolygon aRetval;
2249 
2250  if(mpDAC)
2251  {
2253  }
2254 
2255  return aRetval;
2256 }
2257 
2259 {
2260  return impGetDAC().GetCreatePointer();
2261 }
2262 
2263 void SdrPathObj::NbcMove(const Size& rSiz)
2264 {
2266 
2267  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2268  SdrTextObj::NbcMove(rSiz);
2269 }
2270 
2271 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2272 {
2273  const double fResizeX(xFact);
2274  const double fResizeY(yFact);
2275 
2276  if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
2277  {
2278  // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
2279  // manipulate at all - that may change aGeo rapidly (and wrongly) in
2280  // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
2281  // a rotation but to manipulate the existing one, this is fatal. So just
2282  // avoid this error as long as we have to deal with imprecise geometry
2283  // manipulations
2284  return;
2285  }
2286 
2289  double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2290  maPathPolygon.transform(aTrans);
2291 
2292  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2293  SdrTextObj::NbcResize(rRef,xFact,yFact);
2294 }
2295 
2296 void SdrPathObj::NbcRotate(const Point& rRef, long nAngle, double sn, double cs)
2297 {
2298  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2299  const basegfx::B2DHomMatrix aTrans(
2300  basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -nAngle * F_PI18000));
2301  maPathPolygon.transform(aTrans);
2302 
2303  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2304  SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
2305 }
2306 
2307 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, bool bVShear)
2308 {
2309  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2310 
2311  if(bVShear)
2312  {
2313  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2314  aTrans.shearY(-fTan);
2315  }
2316  else
2317  {
2318  aTrans.shearX(-fTan);
2319  }
2320 
2321  aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2322  maPathPolygon.transform(aTrans);
2323 
2324  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2325  SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2326 }
2327 
2328 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2329 {
2330  const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2331  const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2332  const double fRot(atan2(fDiffY, fDiffX));
2333  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2334  aTrans.rotate(-fRot);
2335  aTrans.scale(1.0, -1.0);
2336  aTrans.rotate(fRot);
2337  aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2338  maPathPolygon.transform(aTrans);
2339 
2340  // Do Joe's special handling for lines when mirroring, too
2341  ImpForceKind();
2342 
2343  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2344  SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2345 }
2346 
2348 {
2349  if(!aGeo.nRotationAngle)
2350  {
2351  rRect = GetSnapRect();
2352  }
2353  else
2354  {
2355  XPolyPolygon aXPP(GetPathPoly());
2356  RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2357  rRect=aXPP.GetBoundRect();
2358  Point aTmp(rRect.TopLeft());
2359  RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2360  aTmp-=rRect.TopLeft();
2361  rRect.Move(aTmp.X(),aTmp.Y());
2362  }
2363 }
2364 
2366 {
2367  if(GetPathPoly().count())
2368  {
2370  }
2371 }
2372 
2374 {
2375  tools::Rectangle aOld(GetSnapRect());
2376  if (aOld.IsEmpty())
2377  {
2378  Fraction aX(1,1);
2379  Fraction aY(1,1);
2380  NbcResize(aOld.TopLeft(), aX, aY);
2381  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2382  return;
2383  }
2384 
2385  // Take empty into account when calculating scale factors
2386  long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left();
2387 
2388  long nDivX = aOld.Right() - aOld.Left();
2389 
2390  // Take empty into account when calculating scale factors
2391  long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
2392 
2393  long nDivY = aOld.Bottom() - aOld.Top();
2394  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2395  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2396  if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
2397  if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
2398  Fraction aX(nMulX,nDivX);
2399  Fraction aY(nMulY,nDivY);
2400  NbcResize(aOld.TopLeft(), aX, aY);
2401  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2402 }
2403 
2405 {
2406  return GetHdlCount();
2407 }
2408 
2409 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2410 {
2411  sal_uInt32 nPoly,nPnt;
2412  if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2413  {
2414  SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2415  }
2416 
2417  const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2418  return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2419 }
2420 
2422 {
2423  return true;
2424 }
2425 
2426 sal_uInt32 SdrPathObj::GetPointCount() const
2427 {
2428  sal_uInt32 nRetval(0);
2429 
2430  for(auto const& rPolygon : GetPathPoly())
2431  {
2432  nRetval += rPolygon.count();
2433  }
2434 
2435  return nRetval;
2436 }
2437 
2438 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2439 {
2440  Point aRetval;
2441  sal_uInt32 nPoly,nPnt;
2442 
2443  if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2444  {
2445  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2446  const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2447  aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2448  }
2449 
2450  return aRetval;
2451 }
2452 
2453 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2454 {
2455  sal_uInt32 nPoly,nPnt;
2456 
2457  if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2458  return;
2459 
2460  basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2461  aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2462  maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2463 
2464  if(meKind==OBJ_LINE)
2465  {
2467  }
2468  else
2469  {
2470  if(GetPathPoly().count())
2471  {
2472  // #i10659# for SdrTextObj, keep aRect up to date
2474  }
2475  }
2476 
2477  SetRectsDirty();
2478 }
2479 
2480 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
2481 {
2482  sal_uInt32 nNewHdl;
2483 
2484  if(bNewObj)
2485  {
2486  nNewHdl = NbcInsPoint(rPos, true);
2487  }
2488  else
2489  {
2490  // look for smallest distance data
2491  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2492  sal_uInt32 nSmallestPolyIndex(0);
2493  sal_uInt32 nSmallestEdgeIndex(0);
2494  double fSmallestCut;
2495  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2496 
2497  nNewHdl = NbcInsPoint(rPos, false);
2498  }
2499 
2500  ImpForceKind();
2501  return nNewHdl;
2502 }
2503 
2504 sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
2505 {
2506  sal_uInt32 nNewHdl;
2507 
2508  if(bNewObj)
2509  {
2510  basegfx::B2DPolygon aNewPoly;
2511  const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2512  aNewPoly.append(aPoint);
2513  aNewPoly.setClosed(IsClosed());
2514  maPathPolygon.append(aNewPoly);
2515  SetRectsDirty();
2516  nNewHdl = GetHdlCount();
2517  }
2518  else
2519  {
2520  // look for smallest distance data
2521  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2522  sal_uInt32 nSmallestPolyIndex(0);
2523  sal_uInt32 nSmallestEdgeIndex(0);
2524  double fSmallestCut;
2525  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2526  basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2527  const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2528  const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
2529 
2530  if(bBefore)
2531  {
2532  // before first point
2533  aCandidate.insert(0, aTestPoint);
2534 
2535  if(aCandidate.areControlPointsUsed())
2536  {
2537  if(aCandidate.isNextControlPointUsed(1))
2538  {
2539  aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2540  aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2541  }
2542  }
2543 
2544  nNewHdl = 0;
2545  }
2546  else if(bAfter)
2547  {
2548  // after last point
2549  aCandidate.append(aTestPoint);
2550 
2551  if(aCandidate.areControlPointsUsed())
2552  {
2553  if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2554  {
2555  aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2556  aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2557  }
2558  }
2559 
2560  nNewHdl = aCandidate.count() - 1;
2561  }
2562  else
2563  {
2564  // in between
2565  bool bSegmentSplit(false);
2566  const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2567 
2568  if(aCandidate.areControlPointsUsed())
2569  {
2570  if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2571  {
2572  bSegmentSplit = true;
2573  }
2574  }
2575 
2576  if(bSegmentSplit)
2577  {
2578  // rebuild original segment to get the split data
2579  basegfx::B2DCubicBezier aBezierA, aBezierB;
2580  const basegfx::B2DCubicBezier aBezier(
2581  aCandidate.getB2DPoint(nSmallestEdgeIndex),
2582  aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2583  aCandidate.getPrevControlPoint(nNextIndex),
2584  aCandidate.getB2DPoint(nNextIndex));
2585 
2586  // split and insert hit point
2587  aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2588  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2589 
2590  // since we inserted hit point and not split point, we need to add an offset
2591  // to the control points to get the C1 continuity we want to achieve
2592  const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2593  aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2594  aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2595  aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2596  aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2597  }
2598  else
2599  {
2600  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2601  }
2602 
2603  nNewHdl = nSmallestEdgeIndex + 1;
2604  }
2605 
2606  maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2607 
2608  // create old polygon index from it
2609  for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
2610  {
2611  nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2612  }
2613  }
2614 
2615  ImpForceKind();
2616  return nNewHdl;
2617 }
2618 
2619 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2620 {
2621  SdrPathObj* pNewObj = nullptr;
2622  const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2623  sal_uInt32 nPoly, nPnt;
2624 
2625  if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2626  {
2627  if(0 == nPoly)
2628  {
2629  const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2630  const sal_uInt32 nPointCount(aCandidate.count());
2631 
2632  if(nPointCount)
2633  {
2634  if(IsClosed())
2635  {
2636  // when closed, RipPoint means to open the polygon at the selected point. To
2637  // be able to do that, it is necessary to make the selected point the first one
2638  basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
2639  SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2640  ToggleClosed();
2641 
2642  // give back new position of old start point (historical reasons)
2643  rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2644  }
2645  else
2646  {
2647  if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
2648  {
2649  // split in two objects at point nPnt
2650  basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
2651  SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2652 
2654  basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2655  pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2656  }
2657  }
2658  }
2659  }
2660  }
2661 
2662  return pNewObj;
2663 }
2664 
2665 SdrObjectUniquePtr SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2666 {
2667  // #i89784# check for FontWork with activated HideContour
2670  const bool bHideContour(
2671  !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2672 
2673  SdrObjectUniquePtr pRet;
2674 
2675  if(!bHideContour)
2676  {
2678 
2679  if(pPath->GetPathPoly().areControlPointsUsed())
2680  {
2681  if(!bBezier)
2682  {
2683  // reduce all bezier curves
2684  pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2685  }
2686  }
2687  else
2688  {
2689  if(bBezier)
2690  {
2691  // create bezier curves
2692  pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
2693  }
2694  }
2695  pRet = std::move(pPath);
2696  }
2697 
2698  if(bAddText)
2699  {
2700  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2701  }
2702 
2703  return pRet;
2704 }
2705 
2707 {
2708  return new SdrPathObjGeoData;
2709 }
2710 
2712 {
2714  SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
2715  rPGeo.maPathPolygon=GetPathPoly();
2716  rPGeo.meKind=meKind;
2717 }
2718 
2720 {
2722  const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
2724  meKind=rPGeo.meKind;
2725  ImpForceKind(); // to set bClosed (among other things)
2726 }
2727 
2729 {
2730  if(GetPathPoly() != rPathPoly)
2731  {
2732  maPathPolygon=rPathPoly;
2733  ImpForceKind();
2734  SetRectsDirty();
2735  }
2736 }
2737 
2739 {
2740  if(GetPathPoly() != rPathPoly)
2741  {
2742  tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
2743  NbcSetPathPoly(rPathPoly);
2744  SetChanged();
2747  }
2748 }
2749 
2751 {
2752  tools::Rectangle aBoundRect0;
2753  if(pUserCall != nullptr)
2754  aBoundRect0 = GetLastBoundRect();
2755  ImpSetClosed(!IsClosed()); // set new ObjKind
2756  ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2757  SetRectsDirty();
2758  SetChanged();
2760  SendUserCall(SdrUserCallType::Resize, aBoundRect0);
2761 }
2762 
2764 {
2765  if(!mpDAC)
2766  {
2767  const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
2768  }
2769 
2770  return *mpDAC;
2771 }
2772 
2773 
2774 // transformation interface for StarOfficeAPI. This implements support for
2775 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2776 // moment it contains a shearX, rotation and translation, but for setting all linear
2777 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2778 
2779 
2780 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2781 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2783 {
2784  double fRotate(0.0);
2785  double fShearX(0.0);
2786  basegfx::B2DTuple aScale(1.0, 1.0);
2787  basegfx::B2DTuple aTranslate(0.0, 0.0);
2788 
2789  if(GetPathPoly().count())
2790  {
2791  // copy geometry
2792  basegfx::B2DHomMatrix aMoveToZeroMatrix;
2793  rPolyPolygon = GetPathPoly();
2794 
2795  if(OBJ_LINE == meKind)
2796  {
2797  // ignore shear and rotate, just use scale and translate
2798  OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2799  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2800  // itself, else this method will no longer return the full polygon information (curve will
2801  // be lost)
2802  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2803  aScale = aPolyRangeNoCurve.getRange();
2804  aTranslate = aPolyRangeNoCurve.getMinimum();
2805 
2806  // define matrix for move polygon to zero point
2807  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2808  }
2809  else
2810  {
2812  {
2813  // get rotate and shear in drawingLayer notation
2814  fRotate = aGeo.nRotationAngle * F_PI18000;
2815  fShearX = aGeo.nShearAngle * F_PI18000;
2816 
2817  // build mathematically correct (negative shear and rotate) object transform
2818  // containing shear and rotate to extract unsheared, unrotated polygon
2819  basegfx::B2DHomMatrix aObjectMatrix;
2820  aObjectMatrix.shearX(tan((36000 - aGeo.nShearAngle) * F_PI18000));
2821  aObjectMatrix.rotate((36000 - aGeo.nRotationAngle) * F_PI18000);
2822 
2823  // create inverse from it and back-transform polygon
2824  basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2825  aInvObjectMatrix.invert();
2826  rPolyPolygon.transform(aInvObjectMatrix);
2827 
2828  // get range from unsheared, unrotated polygon and extract scale and translate.
2829  // transform topLeft from it back to transformed state to get original
2830  // topLeft (rotation center)
2831  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2832  // itself, else this method will no longer return the full polygon information (curve will
2833  // be lost)
2834  const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2835  aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2836  aScale = aCorrectedRangeNoCurve.getRange();
2837 
2838  // define matrix for move polygon to zero point
2839  // #i112280# Added missing minus for Y-Translation
2840  aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2841  }
2842  else
2843  {
2844  // get scale and translate from unsheared, unrotated polygon
2845  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2846  // itself, else this method will no longer return the full polygon information (curve will
2847  // be lost)
2848  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2849  aScale = aPolyRangeNoCurve.getRange();
2850  aTranslate = aPolyRangeNoCurve.getMinimum();
2851 
2852  // define matrix for move polygon to zero point
2853  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2854  }
2855  }
2856 
2857  // move polygon to zero point with pre-defined matrix
2858  rPolyPolygon.transform(aMoveToZeroMatrix);
2859  }
2860 
2861  // position maybe relative to anchorpos, convert
2862  if( getSdrModelFromSdrObject().IsWriter() )
2863  {
2864  if(GetAnchorPos().X() || GetAnchorPos().Y())
2865  {
2866  aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2867  }
2868  }
2869 
2870  // build return value matrix
2872  aScale,
2873  basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2874  basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
2875  aTranslate);
2876 
2877  return true;
2878 }
2879 
2880 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2881 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2882 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2884 {
2885  // break up matrix
2886  basegfx::B2DTuple aScale;
2887  basegfx::B2DTuple aTranslate;
2888  double fRotate, fShearX;
2889  rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2890 
2891  // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2892  // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2893  if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
2894  {
2895  aScale.setX(fabs(aScale.getX()));
2896  aScale.setY(fabs(aScale.getY()));
2897  fRotate = fmod(fRotate + F_PI, F_2PI);
2898  }
2899 
2900  // copy poly
2901  basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
2902 
2903  // reset object shear and rotations
2904  aGeo.nRotationAngle = 0;
2905  aGeo.RecalcSinCos();
2906  aGeo.nShearAngle = 0;
2907  aGeo.RecalcTan();
2908 
2909  if( getSdrModelFromSdrObject().IsWriter() )
2910  {
2911  // if anchor is used, make position relative to it
2912  if(GetAnchorPos().X() || GetAnchorPos().Y())
2913  {
2914  aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2915  }
2916  }
2917 
2918  // create transformation for polygon, set values at aGeo direct
2919  basegfx::B2DHomMatrix aTransform;
2920 
2921  // #i75086#
2922  // Given polygon is already scaled (for historical reasons), but not mirrored yet.
2923  // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
2924  double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0);
2925  double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
2926 
2927  // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
2928  // the polygon. But draw:transform might introduce additional scaling factors, which need to
2929  // be applied to the polygon too, so aScale cannot be ignored while loading.
2930  // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
2931  // idea? The behavior in other cases is the same as it was before this fix.
2932  if (maSnapRect.IsEmpty() && GetPathPoly().count())
2933  {
2934  // In case of a Writer document, the scaling factors were converted to twips. That is not
2935  // correct here, because width and height are already in the points coordinates and aScale
2936  // is no length but only a factor here. Convert back.
2938  {
2939  aScale.setX(aScale.getX() * 127.0 / 72.0);
2940  aScale.setY(aScale.getY() * 127.0 / 72.0);
2941  }
2942  fScaleX *= fabs(aScale.getX());
2943  fScaleY *= fabs(aScale.getY());
2944  }
2945 
2946  if (fScaleX != 1.0 || fScaleY != 1.0)
2947  aTransform.scale(fScaleX, fScaleY);
2948 
2949  if(!basegfx::fTools::equalZero(fShearX))
2950  {
2951  aTransform.shearX(tan(-atan(fShearX)));
2952  aGeo.nShearAngle = FRound(atan(fShearX) / F_PI18000);
2953  aGeo.RecalcTan();
2954  }
2955 
2956  if(!basegfx::fTools::equalZero(fRotate))
2957  {
2958  // #i78696#
2959  // fRotate is mathematically correct for linear transformations, so it's
2960  // the one to use for the geometry change
2961  aTransform.rotate(fRotate);
2962 
2963  // #i78696#
2964  // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
2965  // mirrored -> mirror value here
2967  aGeo.RecalcSinCos();
2968  }
2969 
2970  if(!aTranslate.equalZero())
2971  {
2972  // #i39529# absolute positioning, so get current position (without control points (!))
2973  const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
2974  aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
2975  }
2976 
2977  // transform polygon and trigger change
2978  aNewPolyPolygon.transform(aTransform);
2979  SetPathPoly(aNewPolyPolygon);
2980 }
2981 
2982 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdopath.cxx:1647
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:861
virtual ~SdrPathObj() override
void setX(double fX)
const size_t count(pCandidateA->getBorderLines().size())
bool IsEliminatePolyPoints() const
Definition: svdsnpv.hxx:282
virtual void NbcResize(const Point &rRef, const Fraction &xFact, const Fraction &yFact) override
Definition: svdotxtr.cxx:102
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
virtual SdrPathObj * CloneSdrObject(SdrModel &rTargetModel) const override
Definition: svdopath.cxx:1818
sal_uInt32 NbcInsPointOld(const Point &rPos, bool bNewObj)
Definition: svdopath.cxx:2480
virtual OUString TakeObjNamePlural() const override
Definition: svdopath.cxx:1939
void remove(sal_uInt32 nIndex, sal_uInt32 nCount=1)
sal_uInt32 GetPolyNum() const
Definition: svdhdl.hxx:219
bool IsHeightEmpty() const
bool IsClosedObj() const
Definition: svdobj.hxx:789
sal_uInt16 Count() const
Definition: _xpoly.cxx:890
std::unique_ptr< ImpSdrPathDragData > mpSdrPathDragData
Definition: svdopath.cxx:501
void BrkCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1512
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
virtual basegfx::B2DPolyPolygon TakeCreatePoly(const SdrDragStat &rDrag) const override
Polygon dragged by the user when creating the object.
Definition: svdopath.cxx:2220
SdrObjUserCall * pUserCall
Definition: svdobj.hxx:927
virtual SdrObjGeoData * NewGeoData() const override
A derived class must override these 3 methods if it has own geometric data that must be saved for Und...
Definition: svdopath.cxx:2706
natural cubic Spline (ni)
Definition: svdobj.hxx:133
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:35
static bool equal(const double &rfValA, const double &rfValB)
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:452
double nSin
Definition: svdtrans.hxx:219
const Point & GetStart() const
Definition: svddrag.hxx:92
virtual SdrText * getText(sal_Int32 nIndex) const override
returns the nth available text.
Definition: svdotext.cxx:2057
GeoStat aGeo
Definition: svdotext.hxx:185
B2DPolygon expandToCurve(const B2DPolygon &rCandidate)
static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdopath.cxx:1642
void ImpSetClosed(bool bClose)
Definition: svdopath.cxx:1767
bool ImpCanConvTextToCurve() const
Definition: svdotxtr.cxx:424
virtual Point GetPoint(sal_uInt32 nHdlNum) const override
Definition: svdopath.cxx:2438
SdrPathObj & mrSdrPathObject
Definition: svdopath.cxx:497
virtual bool hasSpecialDrag() const override
The standard transformations (Move,Resize,Rotate,Mirror,Shear) are taken over by the View (TakeXorPol...
Definition: svdopath.cxx:2069
long nRotationAngle
Definition: svdtrans.hxx:216
double getX() const
bool IsUseIncompatiblePathCreateInterface() const
Definition: svdcrtv.hxx:148
caption object
Definition: svdobj.hxx:141
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:935
void setB2DPolygon(sal_uInt32 nIndex, const B2DPolygon &rPolygon)
sal_uInt32 GetPointNum() const
Definition: svdhdl.hxx:222
PointerStyle GetCreatePointer() const
Definition: svdopath.cxx:1583
bool areControlPointsUsed() const
double getY() const
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
B2DVector getRange() const
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:463
SdrInventor
Definition: svdobj.hxx:153
void TakeCurrentObj(sal_uInt16 &nIdent, SdrInventor &nInvent) const
Definition: svdcrtv.hxx:104
void NbcMirrorGluePoints(const Point &rRef1, const Point &rRef2)
Definition: svdobj.cxx:2240
virtual bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd) override
Definition: svdopath.cxx:2160
circle cut
Definition: svdobj.hxx:126
void shearY(double fSy)
virtual void BrkCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2212
void RecalcTan()
Definition: svdtrans.cxx:463
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 nHdlNum) override
Definition: svdopath.cxx:2453
open free-hand line
Definition: svdobj.hxx:131
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:48
float x
bool MovCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1244
virtual void NbcResize(const Point &rRefPnt, const Fraction &aXFact, const Fraction &aYFact) override
Definition: svdopath.cxx:2271
All geometrical data of an arbitrary object for use in undo/redo.
Definition: svdobj.hxx:227
double getMaxX() const
long NormAngle36000(long a)
Normalize angle to -180.00..179.99.
Definition: svdtrans.cxx:408
std::unique_ptr< SdrPathObj, SdrObjectFreeOp > SdrPathObjUniquePtr
Definition: svdotext.hxx:135
polygon, PolyPolygon
Definition: svdobj.hxx:127
long GetEliminatePolyPointLimitAngle() const
Definition: svdsnpv.hxx:284
line
Definition: svdobj.hxx:121
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdopath.cxx:2373
virtual bool HasText() const override
Definition: svdotxat.cxx:412
void SetOrtho8Possible(bool bOn=true)
Definition: svddrag.hxx:129
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdotxtr.cxx:232
long GetLen(const Point &rPnt)
Determine sector within the cartesian coordinate system.
Definition: svdtrans.cxx:425
HSLColor interpolate(const HSLColor &rFrom, const HSLColor &rTo, double t, bool bCCW)
bool IsEmpty() const
SdrObjKind meKind
Definition: svdopath.hxx:36
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdopath.cxx:2719
Provides information about various ZObject properties.
Definition: svdobj.hxx:249
const SdrHdl * GetHdl() const
Definition: svddrag.hxx:101
static OUString GetAngleString(long nAngle)
Definition: svdmodel.cxx:1205
void ImpForceKind()
Definition: svdopath.cxx:1681
Polygon/PolyPolygon represented by SdrPathObj.
Definition: svdobj.hxx:142
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
ImpPathForDragAndCreate(SdrPathObj &rSdrPathObject)
Definition: svdopath.cxx:534
const XPolygon & GetObject(sal_uInt16 nPos) const
Definition: _xpoly.cxx:880
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2155
SdrPathObj(SdrModel &rSdrModel, SdrObjKind eNewKind)
Definition: svdopath.cxx:1619
int n2
bool IsOrtho() const
Definition: svdsnpv.hxx:246
tools::Long Left() const
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:28
virtual void AddToPlusHdlList(SdrHdlList &rHdlList, SdrHdl &rHdl) const override
Definition: svdopath.cxx:2003
#define X
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1440
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
bool isPrevControlPointUsed(sal_uInt32 nIndex) const
void CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
Calculate tangent between two Bézier curves.
Definition: _xpoly.cxx:648
int nCount
bool IsClosed() const
Definition: svdopath.hxx:144
tools::Long Bottom() const
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1885
oslFileHandle & pOut
tools::Rectangle maRect
Definition: svdotext.hxx:182
void insert(sal_uInt32 nIndex, const basegfx::B2DPoint &rPoint, sal_uInt32 nCount=1)
void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrObject * RipPoint(sal_uInt32 nHdlNum, sal_uInt32 &rNewPt0Index)
Definition: svdopath.cxx:2619
PolyLine.
Definition: svdobj.hxx:128
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
const Point & GetPos0() const
Definition: svddrag.hxx:94
bool bClosedObj
Definition: svdobj.hxx:945
OutputDevice * GetFirstOutputDevice() const
Definition: svdpntv.cxx:87
virtual void NbcRotate(const Point &rRefPnt, long nAngle, double fSin, double fCos) override
Definition: svdopath.cxx:2296
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:42
double getMaxY() const
circle, ellipse
Definition: svdobj.hxx:123
virtual ~SdrPathObjGeoData() override
Definition: svdopath.cxx:1607
#define XPOLY_APPEND
Definition: xpoly.hxx:37
virtual bool IsPolyObj() const override
Definition: svdopath.cxx:2421
bool isClosed() const
sal_uInt16 GetFreeHandMinDistPix() const
Definition: svdcrtv.hxx:138
static bool less(const double &rfValA, const double &rfValB)
void shearX(double fSx)
float y
const B2DPoint & getEndPoint() const
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdopath.cxx:2365
bool isEmpty() const
sal_uInt16 GetAutoCloseDistPix() const
Definition: svdcrtv.hxx:133
void SetNoSnap(bool bOn=true)
Definition: svddrag.hxx:121
object group
Definition: svdobj.hxx:120
sal_Int32 nRef
attribute::SdrTextAttribute createNewSdrTextAttribute(const SfxItemSet &rSet, const SdrText &rText, const sal_Int32 *pLeft, const sal_Int32 *pUpper, const sal_Int32 *pRight, const sal_Int32 *pLower)
#define F_PI
double nCos
Definition: svdtrans.hxx:220
int i
void SetPathPoly(const basegfx::B2DPolyPolygon &rPathPoly)
Definition: svdopath.cxx:2738
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdopath.cxx:2409
uno_Any a
void Clear()
Definition: _xpoly.cxx:885
bool IsWriter() const
Definition: svdmodel.hxx:575
const B2DPoint & getControlPointA() const
bool IsCreating() const
Definition: svdopath.cxx:526
OUString sName
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void rotate(double fRadiant)
SdrObject * GetCreateObj() const
Definition: svdcrtv.hxx:116
tools::Long FRound(double fVal)
static basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat &rDrag)
Definition: svdopath.cxx:1561
SdrPathObjUniquePtr ImpConvertMakeObj(const basegfx::B2DPolyPolygon &rPolyPolygon, bool bClosed, bool bBezier) const
Definition: svdotxtr.cxx:429
B2DPolygon makeStartPoint(const B2DPolygon &rCandidate, sal_uInt32 nIndexOfNewStatPoint)
virtual void AddToHdlList(SdrHdlList &rHdlList) const override
Definition: svdopath.cxx:1975
void NbcSetPathPoly(const basegfx::B2DPolyPolygon &rPathPoly)
Definition: svdopath.cxx:2728
bool IsSelected() const
Definition: svdhdl.hxx:206
void openWithGeometryChange(B2DPolygon &rCandidate)
std::unique_ptr< ImpPathForDragAndCreate > mpDAC
Definition: svdopath.hxx:54
bool IsSpline() const
Definition: svdopath.hxx:147
void BroadcastObjectChange() const
Definition: svdobj.cxx:905
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:91
#define F_2PI
SdrTextObj & operator=(const SdrTextObj &rObj)
Definition: svdotext.cxx:1007
SdrView * GetView() const
Definition: svddrag.hxx:86
bool IsAngleSnapEnabled() const
Definition: svdsnpv.hxx:222
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
static bool equalZero(const double &rfVal)
tools::Long Width() const
double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon &rCandidate, const B2DPoint &rTestPoint, sal_uInt32 &rPolygonIndex, sal_uInt32 &rEdgeIndex, double &rCut)
virtual bool BegCreate(SdrDragStat &rStat) override
Every object must be able to create itself interactively.
Definition: svdopath.cxx:2148
static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
Definition: svdopath.cxx:63
virtual SdrObjectUniquePtr DoConvertToPolyObj(bool bBezier, bool bAddText) const override
Definition: svdopath.cxx:2665
void scale(double fX, double fY)
void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:273
void transform(const basegfx::B2DHomMatrix &rMatrix)
static bool IsBezier(SdrObjKind eKind)
Definition: svdopath.cxx:525
bool BckCreate(SdrDragStat const &rStat)
Definition: svdopath.cxx:1472
long GetSnapAngle() const
Definition: svdsnpv.hxx:224
static bool IsFreeHand(SdrObjKind eKind)
Definition: svdopath.cxx:524
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const override
Definition: svdopath.cxx:2782
void NextPoint()
Definition: svddrag.cxx:73
virtual void NbcMove(const Size &rSiz) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdotxtr.cxx:94
bool IsCreate1stPointAsCenter() const
Definition: svdcrtv.hxx:129
basegfx::B2DPolyPolygon getDragPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:2246
SdrObjKind meKind
Definition: svdopath.hxx:51
basegfx::B2DPolyPolygon getObjectPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:2234
bool movePathDrag(SdrDragStat &rDrag) const
Definition: svdopath.cxx:587
tools::Long Top() const
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdopath.cxx:2263
Abstract DrawObject.
Definition: svdobj.hxx:313
bool IsCreateMode() const
Definition: svdmrkv.hxx:230
virtual void TakeObjInfo(SdrObjTransformInfoRec &rInfo) const override
Definition: svdopath.cxx:1800
OUString GetName() const
Definition: svdobj.cxx:698
long nShearAngle
Definition: svdtrans.hxx:217
#define Y
virtual void NbcShear(const Point &rRefPnt, long nAngle, double fTan, bool bVShear) override
Definition: svdopath.cxx:2307
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdopath.cxx:2711
B2DRange getRange(const B2DPolygon &rCandidate)
basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:1519
long BigMulDiv(long nVal, long nMul, long nDiv)
Definition: svdtrans.cxx:559
virtual sal_uInt32 GetHdlCount() const override
Via GetHdlCount the number of Handles can be retrieved.
Definition: svdopath.cxx:1963
bool endPathDrag(SdrDragStat const &rDrag)
Definition: svdopath.cxx:820
OUString GetMetricString(long nVal, bool bNoUnitChars=false, sal_Int32 nNumDigits=-1) const
Definition: svdmodel.cxx:1083
sal_uInt32 NbcInsPoint(const Point &rPos, bool bNewObj)
Definition: svdopath.cxx:2504
const Point & GetPrev() const
Definition: svddrag.hxx:93
Point PixelToLogic(const Point &rDevicePt) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:352
closed Bezier-curve
Definition: svdobj.hxx:130
virtual const tools::Rectangle & GetLastBoundRect() const
Definition: svdobj.cxx:868
size_t GetHdlCount() const
Definition: svdhdl.hxx:461
basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:1104
circle section
Definition: svdobj.hxx:124
SdrHdl * GetHdl(size_t nNum) const
Definition: svdhdl.hxx:462
void setClosed(bool bNew)
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:114
void SetActionRect(const tools::Rectangle &rR)
Definition: svddrag.hxx:157
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon) override
Definition: svdopath.cxx:2883
void closeWithGeometryChange(B2DPolygon &rCandidate)
bool isNextControlPointUsed(sal_uInt32 nIndex) const
double getMinY() const
void split(double t, B2DCubicBezier *pBezierA, B2DCubicBezier *pBezierB) const
sal_uInt32 count() const
virtual void TakeUnrotatedSnapRect(tools::Rectangle &rRect) const override
Definition: svdopath.cxx:2347
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1448
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:376
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
virtual void NbcMirror(const Point &rRefPnt1, const Point &rRefPnt2) override
Definition: svdopath.cxx:2328
circle arc
Definition: svdobj.hxx:125
void setY(double fY)
SdrObjKind
Definition: svdobj.hxx:117
bool equalZero() const
PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdopath.cxx:2258
void RecalcSinCos()
Definition: svdtrans.cxx:451
bool IsControl(sal_uInt16 nPos) const
short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is...
Definition: _xpoly.cxx:466
OUString ImpGetDescriptionStr(const char *pStrCacheID) const
Definition: svdobj.cxx:1038
virtual OUString TakeObjNameSingul() const override
Definition: svdopath.cxx:1832
ImpPathForDragAndCreate & impGetDAC() const
Definition: svdopath.cxx:2763
SdrPathObj & operator=(const SdrPathObj &rObj)
Definition: svdopath.cxx:1823
virtual sal_uInt32 GetPointCount() const override
Definition: svdopath.cxx:2426
void ImpForceLineAngle()
Definition: svdopath.cxx:1659
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:459
B2DHomMatrix createRotateAroundPoint(double fPointX, double fPointY, double fRadiant)
OUString aName
void SetUser(std::unique_ptr< SdrDragStatUserData > pU)
Definition: svddrag.hxx:104
bool IsPlusHdl() const
Definition: svdhdl.hxx:225
#define F_PI18000
tools::Long Height() const
void SetNow(Point const &pt)
Definition: svddrag.hxx:96
B2DPoint getMinimum() const
XPolyPolygon aPathPolygon
Definition: svdopath.cxx:498
virtual sal_uInt16 GetObjIdentifier() const override
Definition: svdopath.cxx:1813
bool IsBezier() const
Definition: svdopath.hxx:146
virtual bool applySpecialDrag(SdrDragStat &rDrag) override
Definition: svdopath.cxx:2081
bool IsMouseDown() const
Definition: svddrag.hxx:140
std::unique_ptr< SdrObject, SdrObjectFreeOp > SdrObjectUniquePtr
Definition: svdobj.hxx:115
bool IsWidthEmpty() const
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdopath.cxx:2404
virtual basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2132
const Point & GetNow() const
Definition: svddrag.hxx:95
bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd)
Definition: svdopath.cxx:1360
long GetAngle(const Point &rPnt)
The Y axis points down! The function negates the Y axis, when calculating the angle, such that GetAngle(Point(0,-1))=90 deg.
Definition: svdtrans.cxx:386
virtual std::unique_ptr< sdr::contact::ViewContact > CreateObjectSpecificViewContact() override
Definition: svdopath.cxx:1613
void translate(double fX, double fY)
PointerStyle
const Point & GetAnchorPos() const
Definition: svdobj.cxx:1589
const basegfx::B2DPolyPolygon & GetPathPoly() const
Definition: svdopath.hxx:139
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:435
static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
Definition: svdopath.cxx:52
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
void BegCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1220
void RotateXPoly(XPolygon &rPoly, const Point &rRef, double sn, double cs)
Definition: svdtrans.cxx:88
virtual bool BckCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2207
const SdrFormTextAttribute & getSdrFormTextAttribute() const
#define SAL_WARN(area, stream)
virtual OUString getSpecialDragComment(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2104
double getMinX() const
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:962
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2074
const SdrHdlList & GetHdlList() const
Definition: svdmrkv.hxx:353
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdopath.cxx:1958
virtual void NbcRotate(const Point &rRef, long nAngle, double sn, double cs) override
Definition: svdotxtr.cxx:186
SdrDragStatUserData * GetUser() const
Definition: svddrag.hxx:103
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
SdrCreateCmd
Definition: svdtypes.hxx:27
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
bool IsBigOrtho() const
Definition: svdsnpv.hxx:258
virtual void SetChanged()
Definition: svdobj.cxx:929
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:875
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:50
virtual void NbcShear(const Point &rRef, long nAngle, double tn, bool bVShear) override
Definition: svdotxtr.cxx:210
void PointsToBezier(sal_uInt16 nFirst)
convert four polygon points into a Bézier curve
Definition: _xpoly.cxx:675
void ToggleClosed()
Definition: svdopath.cxx:2750
void setPrevControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
basegfx::B2DPolyPolygon getModifiedPolyPolygon() const
Definition: svdopath.cxx:531
aStr
sal_uInt32 GetSourceHdlNum() const
Definition: svdhdl.hxx:228
open Bezier-curve
Definition: svdobj.hxx:129
tools::Long Right() const
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2655
closed free-hand line
Definition: svdobj.hxx:132
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:895
static bool IsClosed(SdrObjKind eKind)
Definition: svdopath.cxx:523
Point Center() const
void AddHdl(std::unique_ptr< SdrHdl > pHdl)
Definition: svdhdl.cxx:2289
virtual bool IsFontwork() const
Definition: svdotext.cxx:1732
const B2DPoint & getControlPointB() const
sal_uInt32 count() const
rectangle (round corners optional)
Definition: svdobj.hxx:122
OUString getSpecialDragComment(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:909
bool beginPathDrag(SdrDragStat const &rDrag) const
Definition: svdopath.cxx:542
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const