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