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  aGeo.nRotationAngle=GetAngle(aDelt);
1683  aGeo.nShearAngle=0_deg100;
1684  aGeo.RecalcSinCos();
1685  aGeo.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  OUStringBuffer sName;
1836 
1837  if(OBJ_LINE == meKind)
1838  {
1839  const char* 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.append(SvxResId(pId));
1871  }
1872  else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1873  {
1874  const bool bClosed(OBJ_POLY == meKind);
1875  const char* pId(nullptr);
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.append(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  OUString sTemp(SvxResId(pId));
1910  // #i96537#
1911  sName.append(sTemp.replaceFirst("%2", OUString::number(nPointCount)));
1912  }
1913  }
1914  else
1915  {
1916  switch (meKind)
1917  {
1918  case OBJ_PATHLINE: sName.append(SvxResId(STR_ObjNameSingulPATHLINE)); break;
1919  case OBJ_FREELINE: sName.append(SvxResId(STR_ObjNameSingulFREELINE)); break;
1920  case OBJ_SPLNLINE: sName.append(SvxResId(STR_ObjNameSingulNATSPLN)); break;
1921  case OBJ_PATHFILL: sName.append(SvxResId(STR_ObjNameSingulPATHFILL)); break;
1922  case OBJ_FREEFILL: sName.append(SvxResId(STR_ObjNameSingulFREEFILL)); break;
1923  case OBJ_SPLNFILL: sName.append(SvxResId(STR_ObjNameSingulPERSPLN)); break;
1924  default: break;
1925  }
1926  }
1927 
1928  OUString aName(GetName());
1929  if (!aName.isEmpty())
1930  {
1931  sName.append(' ');
1932  sName.append('\'');
1933  sName.append(aName);
1934  sName.append('\'');
1935  }
1936 
1937  return sName.makeStringAndClear();
1938 }
1939 
1941 {
1942  OUString sName;
1943  switch(meKind)
1944  {
1945  case OBJ_LINE : sName=SvxResId(STR_ObjNamePluralLINE ); break;
1946  case OBJ_PLIN : sName=SvxResId(STR_ObjNamePluralPLIN ); break;
1947  case OBJ_POLY : sName=SvxResId(STR_ObjNamePluralPOLY ); break;
1948  case OBJ_PATHLINE: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
1949  case OBJ_FREELINE: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
1950  case OBJ_SPLNLINE: sName=SvxResId(STR_ObjNamePluralNATSPLN); break;
1951  case OBJ_PATHFILL: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
1952  case OBJ_FREEFILL: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
1953  case OBJ_SPLNFILL: sName=SvxResId(STR_ObjNamePluralPERSPLN); break;
1954  default: break;
1955  }
1956  return sName;
1957 }
1958 
1960 {
1961  return GetPathPoly();
1962 }
1963 
1964 sal_uInt32 SdrPathObj::GetHdlCount() const
1965 {
1966  sal_uInt32 nRetval(0);
1967 
1968  for(auto const& rPolygon : GetPathPoly())
1969  {
1970  nRetval += rPolygon.count();
1971  }
1972 
1973  return nRetval;
1974 }
1975 
1977 {
1978  // keep old stuff to be able to keep old SdrHdl stuff, too
1979  const XPolyPolygon aOldPathPolygon(GetPathPoly());
1980  sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
1981  bool bClosed=IsClosed();
1982  sal_uInt16 nIdx=0;
1983 
1984  for (sal_uInt16 i=0; i<nPolyCnt; i++) {
1985  const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
1986  sal_uInt16 nPntCnt=rXPoly.GetPointCount();
1987  if (bClosed && nPntCnt>1) nPntCnt--;
1988 
1989  for (sal_uInt16 j=0; j<nPntCnt; j++) {
1990  if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
1991  const Point& rPnt=rXPoly[j];
1992  std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
1993  pHdl->SetPolyNum(i);
1994  pHdl->SetPointNum(j);
1995  pHdl->Set1PixMore(j==0);
1996  pHdl->SetSourceHdlNum(nIdx);
1997  nIdx++;
1998  rHdlList.AddHdl(std::move(pHdl));
1999  }
2000  }
2001  }
2002 }
2003 
2004 void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
2005 {
2006  // keep old stuff to be able to keep old SdrHdl stuff, too
2007  const XPolyPolygon aOldPathPolygon(GetPathPoly());
2008  sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2009  sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
2010 
2011  if (nPolyNum>=aOldPathPolygon.Count())
2012  return;
2013 
2014  const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2015  sal_uInt16 nPntMax = rXPoly.GetPointCount();
2016 
2017  if (nPntMax<=0)
2018  return;
2019  nPntMax--;
2020  if (nPnt>nPntMax)
2021  return;
2022 
2023  // calculate the number of plus points
2024  sal_uInt16 nCnt = 0;
2025  if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
2026  {
2027  if (nPnt==0 && IsClosed())
2028  nPnt=nPntMax;
2029  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
2030  nCnt++;
2031  if (nPnt==nPntMax && IsClosed())
2032  nPnt=0;
2033  if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2034  nCnt++;
2035  }
2036 
2037  // construct the plus points
2038  for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
2039  {
2040  nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2041  std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
2042  pHdl->SetPolyNum(rHdl.GetPolyNum());
2043 
2044  if (nPnt==0 && IsClosed())
2045  nPnt=nPntMax;
2046  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
2047  {
2048  pHdl->SetPos(rXPoly[nPnt-1]);
2049  pHdl->SetPointNum(nPnt-1);
2050  }
2051  else
2052  {
2053  if (nPnt==nPntMax && IsClosed())
2054  nPnt=0;
2055  if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2056  {
2057  pHdl->SetPos(rXPoly[nPnt+1]);
2058  pHdl->SetPointNum(nPnt+1);
2059  }
2060  }
2061 
2062  pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2063  pHdl->SetPlusHdl(true);
2064  rHdlList.AddHdl(std::move(pHdl));
2065  }
2066 }
2067 
2068 // dragging
2069 
2071 {
2072  return true;
2073 }
2074 
2076 {
2077  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2078 
2079  return aDragAndCreate.beginPathDrag(rDrag);
2080 }
2081 
2083 {
2084  ImpPathForDragAndCreate aDragAndCreate(*this);
2085  bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2086 
2087  if(bRetval)
2088  {
2089  bRetval = aDragAndCreate.movePathDrag(rDrag);
2090  }
2091 
2092  if(bRetval)
2093  {
2094  bRetval = aDragAndCreate.endPathDrag(rDrag);
2095  }
2096 
2097  if(bRetval)
2098  {
2099  NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2100  }
2101 
2102  return bRetval;
2103 }
2104 
2106 {
2107  OUString aRetval;
2108 
2109  if(mpDAC)
2110  {
2111  // #i103058# also get a comment when in creation
2112  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2113 
2114  if(bCreateComment)
2115  {
2116  aRetval = mpDAC->getSpecialDragComment(rDrag);
2117  }
2118  }
2119  else
2120  {
2121  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2122  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2123 
2124  if(bDidWork)
2125  {
2126  aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2127  }
2128  }
2129 
2130  return aRetval;
2131 }
2132 
2134 {
2135  basegfx::B2DPolyPolygon aRetval;
2136  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2137  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2138 
2139  if(bDidWork)
2140  {
2141  aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2142  }
2143 
2144  return aRetval;
2145 }
2146 
2147 // creation
2148 
2150 {
2151  mpDAC.reset();
2152  impGetDAC().BegCreate(rStat);
2153  return true;
2154 }
2155 
2157 {
2158  return impGetDAC().MovCreate(rStat);
2159 }
2160 
2162 {
2163  bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2164 
2165  if(bRetval && mpDAC)
2166  {
2167  SetPathPoly(mpDAC->getModifiedPolyPolygon());
2168 
2169  // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2170  // to be able to use the type-changing ImpSetClosed method
2171  if(!IsClosedObj())
2172  {
2173  SdrView* pView = rStat.GetView();
2174 
2175  if(pView && !pView->IsUseIncompatiblePathCreateInterface())
2176  {
2178 
2179  if(pOut)
2180  {
2181  if(GetPathPoly().count())
2182  {
2183  const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2184 
2185  if(aCandidate.count() > 2)
2186  {
2187  // check distance of first and last point
2188  const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2189  const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2190 
2191  if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
2192  {
2193  // close it
2194  ImpSetClosed(true);
2195  }
2196  }
2197  }
2198  }
2199  }
2200  }
2201 
2202  mpDAC.reset();
2203  }
2204 
2205  return bRetval;
2206 }
2207 
2209 {
2210  return impGetDAC().BckCreate(rStat);
2211 }
2212 
2214 {
2215  impGetDAC().BrkCreate(rStat);
2216  mpDAC.reset();
2217 }
2218 
2219 // polygons
2220 
2222 {
2223  basegfx::B2DPolyPolygon aRetval;
2224 
2225  if(mpDAC)
2226  {
2227  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2229  }
2230 
2231  return aRetval;
2232 }
2233 
2234 // during drag or create, allow accessing the so-far created/modified polyPolygon
2236 {
2237  basegfx::B2DPolyPolygon aRetval;
2238 
2239  if(mpDAC)
2240  {
2241  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2242  }
2243 
2244  return aRetval;
2245 }
2246 
2248 {
2249  basegfx::B2DPolyPolygon aRetval;
2250 
2251  if(mpDAC)
2252  {
2254  }
2255 
2256  return aRetval;
2257 }
2258 
2260 {
2261  return impGetDAC().GetCreatePointer();
2262 }
2263 
2264 void SdrPathObj::NbcMove(const Size& rSiz)
2265 {
2267 
2268  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2269  SdrTextObj::NbcMove(rSiz);
2270 }
2271 
2272 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2273 {
2274  const double fResizeX(xFact);
2275  const double fResizeY(yFact);
2276 
2277  if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
2278  {
2279  // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
2280  // manipulate at all - that may change aGeo rapidly (and wrongly) in
2281  // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
2282  // a rotation but to manipulate the existing one, this is fatal. So just
2283  // avoid this error as long as we have to deal with imprecise geometry
2284  // manipulations
2285  return;
2286  }
2287 
2290  double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2291  maPathPolygon.transform(aTrans);
2292 
2293  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2294  SdrTextObj::NbcResize(rRef,xFact,yFact);
2295 }
2296 
2297 void SdrPathObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
2298 {
2299  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2300  const basegfx::B2DHomMatrix aTrans(
2301  basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -nAngle.get() * F_PI18000));
2302  maPathPolygon.transform(aTrans);
2303 
2304  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2305  SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
2306 }
2307 
2308 void SdrPathObj::NbcShear(const Point& rRefPnt, Degree100 nAngle, double fTan, bool bVShear)
2309 {
2310  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2311 
2312  if(bVShear)
2313  {
2314  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2315  aTrans.shearY(-fTan);
2316  }
2317  else
2318  {
2319  aTrans.shearX(-fTan);
2320  }
2321 
2322  aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2323  maPathPolygon.transform(aTrans);
2324 
2325  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2326  SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2327 }
2328 
2329 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2330 {
2331  const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2332  const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2333  const double fRot(atan2(fDiffY, fDiffX));
2334  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2335  aTrans.rotate(-fRot);
2336  aTrans.scale(1.0, -1.0);
2337  aTrans.rotate(fRot);
2338  aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2339  maPathPolygon.transform(aTrans);
2340 
2341  // Do Joe's special handling for lines when mirroring, too
2342  ImpForceKind();
2343 
2344  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2345  SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2346 }
2347 
2349 {
2350  if(!aGeo.nRotationAngle)
2351  {
2352  rRect = GetSnapRect();
2353  }
2354  else
2355  {
2356  XPolyPolygon aXPP(GetPathPoly());
2358  rRect=aXPP.GetBoundRect();
2359  Point aTmp(rRect.TopLeft());
2361  aTmp-=rRect.TopLeft();
2362  rRect.Move(aTmp.X(),aTmp.Y());
2363  }
2364 }
2365 
2367 {
2368  if(GetPathPoly().count())
2369  {
2371  }
2372 }
2373 
2375 {
2376  tools::Rectangle aOld(GetSnapRect());
2377  if (aOld.IsEmpty())
2378  {
2379  Fraction aX(1,1);
2380  Fraction aY(1,1);
2381  NbcResize(aOld.TopLeft(), aX, aY);
2382  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2383  return;
2384  }
2385 
2386  // Take empty into account when calculating scale factors
2387  tools::Long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left();
2388 
2389  tools::Long nDivX = aOld.Right() - aOld.Left();
2390 
2391  // Take empty into account when calculating scale factors
2392  tools::Long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
2393 
2394  tools::Long nDivY = aOld.Bottom() - aOld.Top();
2395  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2396  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2397  if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
2398  if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
2399  Fraction aX(nMulX,nDivX);
2400  Fraction aY(nMulY,nDivY);
2401  NbcResize(aOld.TopLeft(), aX, aY);
2402  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2403 }
2404 
2406 {
2407  return GetHdlCount();
2408 }
2409 
2410 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2411 {
2412  sal_uInt32 nPoly,nPnt;
2413  if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2414  {
2415  SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2416  }
2417 
2418  const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2419  return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2420 }
2421 
2423 {
2424  return true;
2425 }
2426 
2427 sal_uInt32 SdrPathObj::GetPointCount() const
2428 {
2429  sal_uInt32 nRetval(0);
2430 
2431  for(auto const& rPolygon : GetPathPoly())
2432  {
2433  nRetval += rPolygon.count();
2434  }
2435 
2436  return nRetval;
2437 }
2438 
2439 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2440 {
2441  Point aRetval;
2442  sal_uInt32 nPoly,nPnt;
2443 
2444  if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2445  {
2446  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2447  const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2448  aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2449  }
2450 
2451  return aRetval;
2452 }
2453 
2454 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2455 {
2456  sal_uInt32 nPoly,nPnt;
2457 
2458  if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2459  return;
2460 
2461  basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2462  aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2463  maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2464 
2465  if(meKind==OBJ_LINE)
2466  {
2468  }
2469  else
2470  {
2471  if(GetPathPoly().count())
2472  {
2473  // #i10659# for SdrTextObj, keep aRect up to date
2475  }
2476  }
2477 
2478  SetRectsDirty();
2479 }
2480 
2481 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
2482 {
2483  sal_uInt32 nNewHdl;
2484 
2485  if(bNewObj)
2486  {
2487  nNewHdl = NbcInsPoint(rPos, true);
2488  }
2489  else
2490  {
2491  // look for smallest distance data
2492  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2493  sal_uInt32 nSmallestPolyIndex(0);
2494  sal_uInt32 nSmallestEdgeIndex(0);
2495  double fSmallestCut;
2496  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2497 
2498  nNewHdl = NbcInsPoint(rPos, false);
2499  }
2500 
2501  ImpForceKind();
2502  return nNewHdl;
2503 }
2504 
2505 sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
2506 {
2507  sal_uInt32 nNewHdl;
2508 
2509  if(bNewObj)
2510  {
2511  basegfx::B2DPolygon aNewPoly;
2512  const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2513  aNewPoly.append(aPoint);
2514  aNewPoly.setClosed(IsClosed());
2515  maPathPolygon.append(aNewPoly);
2516  SetRectsDirty();
2517  nNewHdl = GetHdlCount();
2518  }
2519  else
2520  {
2521  // look for smallest distance data
2522  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2523  sal_uInt32 nSmallestPolyIndex(0);
2524  sal_uInt32 nSmallestEdgeIndex(0);
2525  double fSmallestCut;
2526  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2527  basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2528  const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2529  const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
2530 
2531  if(bBefore)
2532  {
2533  // before first point
2534  aCandidate.insert(0, aTestPoint);
2535 
2536  if(aCandidate.areControlPointsUsed())
2537  {
2538  if(aCandidate.isNextControlPointUsed(1))
2539  {
2540  aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2541  aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2542  }
2543  }
2544 
2545  nNewHdl = 0;
2546  }
2547  else if(bAfter)
2548  {
2549  // after last point
2550  aCandidate.append(aTestPoint);
2551 
2552  if(aCandidate.areControlPointsUsed())
2553  {
2554  if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2555  {
2556  aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2557  aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2558  }
2559  }
2560 
2561  nNewHdl = aCandidate.count() - 1;
2562  }
2563  else
2564  {
2565  // in between
2566  bool bSegmentSplit(false);
2567  const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2568 
2569  if(aCandidate.areControlPointsUsed())
2570  {
2571  if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2572  {
2573  bSegmentSplit = true;
2574  }
2575  }
2576 
2577  if(bSegmentSplit)
2578  {
2579  // rebuild original segment to get the split data
2580  basegfx::B2DCubicBezier aBezierA, aBezierB;
2581  const basegfx::B2DCubicBezier aBezier(
2582  aCandidate.getB2DPoint(nSmallestEdgeIndex),
2583  aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2584  aCandidate.getPrevControlPoint(nNextIndex),
2585  aCandidate.getB2DPoint(nNextIndex));
2586 
2587  // split and insert hit point
2588  aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2589  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2590 
2591  // since we inserted hit point and not split point, we need to add an offset
2592  // to the control points to get the C1 continuity we want to achieve
2593  const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2594  aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2595  aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2596  aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2597  aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2598  }
2599  else
2600  {
2601  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2602  }
2603 
2604  nNewHdl = nSmallestEdgeIndex + 1;
2605  }
2606 
2607  maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2608 
2609  // create old polygon index from it
2610  for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
2611  {
2612  nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2613  }
2614  }
2615 
2616  ImpForceKind();
2617  return nNewHdl;
2618 }
2619 
2620 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2621 {
2622  SdrPathObj* pNewObj = nullptr;
2623  const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2624  sal_uInt32 nPoly, nPnt;
2625 
2626  if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2627  {
2628  if(0 == nPoly)
2629  {
2630  const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2631  const sal_uInt32 nPointCount(aCandidate.count());
2632 
2633  if(nPointCount)
2634  {
2635  if(IsClosed())
2636  {
2637  // when closed, RipPoint means to open the polygon at the selected point. To
2638  // be able to do that, it is necessary to make the selected point the first one
2639  basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
2640  SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2641  ToggleClosed();
2642 
2643  // give back new position of old start point (historical reasons)
2644  rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2645  }
2646  else
2647  {
2648  if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
2649  {
2650  // split in two objects at point nPnt
2651  basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
2652  SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2653 
2655  basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2656  pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2657  }
2658  }
2659  }
2660  }
2661  }
2662 
2663  return pNewObj;
2664 }
2665 
2666 SdrObjectUniquePtr SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2667 {
2668  // #i89784# check for FontWork with activated HideContour
2671  const bool bHideContour(
2672  !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2673 
2674  SdrObjectUniquePtr pRet;
2675 
2676  if(!bHideContour)
2677  {
2679 
2680  if(pPath->GetPathPoly().areControlPointsUsed())
2681  {
2682  if(!bBezier)
2683  {
2684  // reduce all bezier curves
2685  pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2686  }
2687  }
2688  else
2689  {
2690  if(bBezier)
2691  {
2692  // create bezier curves
2693  pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
2694  }
2695  }
2696  pRet = std::move(pPath);
2697  }
2698 
2699  if(bAddText)
2700  {
2701  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2702  }
2703 
2704  return pRet;
2705 }
2706 
2707 std::unique_ptr<SdrObjGeoData> SdrPathObj::NewGeoData() const
2708 {
2709  return std::make_unique<SdrPathObjGeoData>();
2710 }
2711 
2713 {
2715  SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
2716  rPGeo.maPathPolygon=GetPathPoly();
2717  rPGeo.meKind=meKind;
2718 }
2719 
2721 {
2723  const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
2725  meKind=rPGeo.meKind;
2726  ImpForceKind(); // to set bClosed (among other things)
2727 }
2728 
2730 {
2731  if(GetPathPoly() != rPathPoly)
2732  {
2733  maPathPolygon=rPathPoly;
2734  ImpForceKind();
2735  SetRectsDirty();
2736  }
2737 }
2738 
2740 {
2741  if(GetPathPoly() != rPathPoly)
2742  {
2743  tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
2744  NbcSetPathPoly(rPathPoly);
2745  SetChanged();
2748  }
2749 }
2750 
2752 {
2753  tools::Rectangle aBoundRect0;
2754  if(m_pUserCall != nullptr)
2755  aBoundRect0 = GetLastBoundRect();
2756  ImpSetClosed(!IsClosed()); // set new ObjKind
2757  ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2758  SetRectsDirty();
2759  SetChanged();
2761  SendUserCall(SdrUserCallType::Resize, aBoundRect0);
2762 }
2763 
2765 {
2766  if(!mpDAC)
2767  {
2768  const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
2769  }
2770 
2771  return *mpDAC;
2772 }
2773 
2774 
2775 // transformation interface for StarOfficeAPI. This implements support for
2776 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2777 // moment it contains a shearX, rotation and translation, but for setting all linear
2778 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2779 
2780 
2781 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2782 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2784 {
2785  double fRotate(0.0);
2786  double fShearX(0.0);
2787  basegfx::B2DTuple aScale(1.0, 1.0);
2788  basegfx::B2DTuple aTranslate(0.0, 0.0);
2789 
2790  if(GetPathPoly().count())
2791  {
2792  // copy geometry
2793  basegfx::B2DHomMatrix aMoveToZeroMatrix;
2794  rPolyPolygon = GetPathPoly();
2795 
2796  if(OBJ_LINE == meKind)
2797  {
2798  // ignore shear and rotate, just use scale and translate
2799  OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2800  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2801  // itself, else this method will no longer return the full polygon information (curve will
2802  // be lost)
2803  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2804  aScale = aPolyRangeNoCurve.getRange();
2805  aTranslate = aPolyRangeNoCurve.getMinimum();
2806 
2807  // define matrix for move polygon to zero point
2808  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2809  }
2810  else
2811  {
2813  {
2814  // get rotate and shear in drawingLayer notation
2815  fRotate = aGeo.nRotationAngle.get() * F_PI18000;
2816  fShearX = aGeo.nShearAngle.get() * F_PI18000;
2817 
2818  // build mathematically correct (negative shear and rotate) object transform
2819  // containing shear and rotate to extract unsheared, unrotated polygon
2820  basegfx::B2DHomMatrix aObjectMatrix;
2821  aObjectMatrix.shearX(-aGeo.mfTanShearAngle);
2822  aObjectMatrix.rotate((36000 - aGeo.nRotationAngle.get()) * F_PI18000);
2823 
2824  // create inverse from it and back-transform polygon
2825  basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2826  aInvObjectMatrix.invert();
2827  rPolyPolygon.transform(aInvObjectMatrix);
2828 
2829  // get range from unsheared, unrotated polygon and extract scale and translate.
2830  // transform topLeft from it back to transformed state to get original
2831  // topLeft (rotation center)
2832  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2833  // itself, else this method will no longer return the full polygon information (curve will
2834  // be lost)
2835  const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2836  aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2837  aScale = aCorrectedRangeNoCurve.getRange();
2838 
2839  // define matrix for move polygon to zero point
2840  // #i112280# Added missing minus for Y-Translation
2841  aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2842  }
2843  else
2844  {
2845  // get scale and translate from unsheared, unrotated polygon
2846  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2847  // itself, else this method will no longer return the full polygon information (curve will
2848  // be lost)
2849  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2850  aScale = aPolyRangeNoCurve.getRange();
2851  aTranslate = aPolyRangeNoCurve.getMinimum();
2852 
2853  // define matrix for move polygon to zero point
2854  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2855  }
2856  }
2857 
2858  // move polygon to zero point with pre-defined matrix
2859  rPolyPolygon.transform(aMoveToZeroMatrix);
2860  }
2861 
2862  // position maybe relative to anchorpos, convert
2863  if( getSdrModelFromSdrObject().IsWriter() )
2864  {
2865  if(GetAnchorPos().X() || GetAnchorPos().Y())
2866  {
2867  aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2868  }
2869  }
2870 
2871  // build return value matrix
2873  aScale,
2874  basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2875  basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
2876  aTranslate);
2877 
2878  return true;
2879 }
2880 
2881 void SdrPathObj::SetHandleScale(bool bHandleScale)
2882 {
2883  mbHandleScale = bHandleScale;
2884 }
2885 
2886 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2887 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2888 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2890 {
2891  // break up matrix
2892  basegfx::B2DTuple aScale;
2893  basegfx::B2DTuple aTranslate;
2894  double fRotate, fShearX;
2895  rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2896 
2897  // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2898  // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2899  if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
2900  {
2901  aScale.setX(fabs(aScale.getX()));
2902  aScale.setY(fabs(aScale.getY()));
2903  fRotate = fmod(fRotate + F_PI, F_2PI);
2904  }
2905 
2906  // copy poly
2907  basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
2908 
2909  // reset object shear and rotations
2910  aGeo.nRotationAngle = 0_deg100;
2911  aGeo.RecalcSinCos();
2912  aGeo.nShearAngle = 0_deg100;
2913  aGeo.RecalcTan();
2914 
2915  if( getSdrModelFromSdrObject().IsWriter() )
2916  {
2917  // if anchor is used, make position relative to it
2918  if(GetAnchorPos().X() || GetAnchorPos().Y())
2919  {
2920  aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2921  }
2922  }
2923 
2924  // create transformation for polygon, set values at aGeo direct
2925  basegfx::B2DHomMatrix aTransform;
2926 
2927  // #i75086#
2928  // Given polygon is already scaled (for historical reasons), but not mirrored yet.
2929  // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
2930  double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0);
2931  double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
2932 
2933  // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
2934  // the polygon. But draw:transform might introduce additional scaling factors, which need to
2935  // be applied to the polygon too, so aScale cannot be ignored while loading.
2936  // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
2937  // idea? The behavior in other cases is the same as it was before this fix.
2939  {
2940  // In case of a Writer document, the scaling factors were converted to twips. That is not
2941  // correct here, because width and height are already in the points coordinates and aScale
2942  // is no length but only a factor here. Convert back.
2944  {
2947  }
2948  fScaleX *= fabs(aScale.getX());
2949  fScaleY *= fabs(aScale.getY());
2950  }
2951 
2952  if (fScaleX != 1.0 || fScaleY != 1.0)
2953  aTransform.scale(fScaleX, fScaleY);
2954 
2955  if(!basegfx::fTools::equalZero(fShearX))
2956  {
2957  aTransform.shearX(tan(-atan(fShearX)));
2958  aGeo.nShearAngle = Degree100(FRound(atan(fShearX) / F_PI18000));
2959  aGeo.RecalcTan();
2960  }
2961 
2962  if(!basegfx::fTools::equalZero(fRotate))
2963  {
2964  // #i78696#
2965  // fRotate is mathematically correct for linear transformations, so it's
2966  // the one to use for the geometry change
2967  aTransform.rotate(fRotate);
2968 
2969  // #i78696#
2970  // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
2971  // mirrored -> mirror value here
2973  aGeo.RecalcSinCos();
2974  }
2975 
2976  if(!aTranslate.equalZero())
2977  {
2978  // #i39529# absolute positioning, so get current position (without control points (!))
2979  const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
2980  aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
2981  }
2982 
2983  // transform polygon and trigger change
2984  aNewPolyPolygon.transform(aTransform);
2985  SetPathPoly(aNewPolyPolygon);
2986 }
2987 
2988 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdopath.cxx:1657
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:861
virtual void NbcShear(const Point &rRefPnt, Degree100 nAngle, double fTan, bool bVShear) override
Definition: svdopath.cxx:2308
virtual ~SdrPathObj() override
void setX(double fX)
const size_t count(pCandidateA->getBorderLines().size())
bool IsEliminatePolyPoints() const
Definition: svdsnpv.hxx:283
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:2481
virtual OUString TakeObjNamePlural() const override
Definition: svdopath.cxx:1940
void remove(sal_uInt32 nIndex, sal_uInt32 nCount=1)
line
Definition: svdobjkind.hxx:29
sal_uInt32 GetPolyNum() const
Definition: svdhdl.hxx:219
bool IsHeightEmpty() const
bool IsClosedObj() const
Definition: svdobj.hxx:736
Polygon/PolyPolygon represented by SdrPathObj.
Definition: svdobjkind.hxx:50
sal_uInt16 Count() const
Definition: _xpoly.cxx:890
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:2221
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:35
bool m_bClosedObj
Definition: svdobj.hxx:892
static bool equal(const double &rfValA, const double &rfValB)
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:452
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:2060
GeoStat aGeo
Definition: svdotext.hxx:185
static OUString GetAngleString(Degree100 nAngle)
Definition: svdmodel.cxx:1143
Degree100 GetEliminatePolyPointLimitAngle() const
Definition: svdsnpv.hxx:285
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:2439
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:2070
double getX() const
double mfSinRotationAngle
Definition: svdtrans.hxx:220
bool IsUseIncompatiblePathCreateInterface() const
Definition: svdcrtv.hxx:148
tools::Long GetLen(const Point &rPnt)
Determine sector within the cartesian coordinate system.
Definition: svdtrans.cxx:427
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:935
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
double getY() const
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
B2DVector getRange() const
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:463
SdrInventor
Definition: svdobj.hxx:98
void SetHandleScale(bool bHandleScale)
Scaling is ignored from the transform matrix by default, to not break compatibility.
Definition: svdopath.cxx:2881
void NbcMirrorGluePoints(const Point &rRef1, const Point &rRef2)
Definition: svdobj.cxx:2303
virtual bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd) override
Definition: svdopath.cxx:2161
void shearY(double fSy)
virtual void BrkCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2213
void RecalcTan()
Definition: svdtrans.cxx:465
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 nHdlNum) override
Definition: svdopath.cxx:2454
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:2272
All geometrical data of an arbitrary object for use in undo/redo.
Definition: svdobj.hxx:172
double getMaxX() const
SdrObjKind
Definition: svdobjkind.hxx:24
std::unique_ptr< SdrPathObj, SdrObjectFreeOp > SdrPathObjUniquePtr
Definition: svdotext.hxx:135
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdopath.cxx:2374
virtual bool HasText() const override
Definition: svdotxat.cxx:416
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)
bool IsEmpty() const
SdrObjKind meKind
Definition: svdopath.hxx:36
constexpr tools::Long Width() const
Provides information about various ZObject properties.
Definition: svdobj.hxx:194
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:880
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2156
SdrPathObj(SdrModel &rSdrModel, SdrObjKind eNewKind)
Definition: svdopath.cxx:1621
int n2
bool IsOrtho() const
Definition: svdsnpv.hxx:247
tools::Long Left() const
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:25
virtual void AddToPlusHdlList(SdrHdlList &rHdlList, SdrHdl &rHdl) const override
Definition: svdopath.cxx:2004
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1443
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
bool isPrevControlPointUsed(sal_uInt32 nIndex) const
void CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
Calculate tangent between two Bézier curves.
Definition: _xpoly.cxx:648
int nCount
bool IsClosed() const
Definition: svdopath.hxx:146
tools::Long Bottom() const
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1952
oslFileHandle & pOut
void TakeCurrentObj(SdrObjKind &nIdent, SdrInventor &nInvent) const
Definition: svdcrtv.hxx:104
tools::Rectangle maRect
Definition: svdotext.hxx:182
void insert(sal_uInt32 nIndex, const basegfx::B2DPoint &rPoint, sal_uInt32 nCount=1)
void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrObject * RipPoint(sal_uInt32 nHdlNum, sal_uInt32 &rNewPt0Index)
Definition: svdopath.cxx:2620
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:88
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:42
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:2422
bool isClosed() const
double mfTanShearAngle
Definition: svdtrans.hxx:219
constexpr auto convert(N n, sal_Int64 mul, sal_Int64 div)
virtual void RestoreGeoData(const SdrObjGeoData &rGeo) override
Definition: svdopath.cxx:2720
sal_uInt16 GetFreeHandMinDistPix() const
Definition: svdcrtv.hxx:138
static bool less(const double &rfValA, const double &rfValB)
void shearX(double fSx)
float y
const B2DPoint & getEndPoint() const
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdopath.cxx:2366
bool isEmpty() const
bool mbHandleScale
Definition: svdopath.hxx:52
sal_uInt16 GetAutoCloseDistPix() const
Definition: svdcrtv.hxx:133
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:2739
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdopath.cxx:2410
uno_Any a
void Clear()
Definition: _xpoly.cxx:885
bool IsWriter() const
Definition: svdmodel.hxx:576
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:116
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:2707
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:1976
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:2729
bool IsSelected() const
Definition: svdhdl.hxx:206
void openWithGeometryChange(B2DPolygon &rCandidate)
std::unique_ptr< ImpPathForDragAndCreate > mpDAC
Definition: svdopath.hxx:55
bool IsSpline() const
Definition: svdopath.hxx:149
void BroadcastObjectChange() const
Definition: svdobj.cxx:982
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:101
#define F_2PI
SdrView * GetView() const
Definition: svddrag.hxx:96
bool IsAngleSnapEnabled() const
Definition: svdsnpv.hxx:223
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
static bool equalZero(const double &rfVal)
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:2149
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:2666
void scale(double fX, double fY)
void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:276
SdrObjUserCall * m_pUserCall
Definition: svdobj.hxx:874
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
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:2783
void NextPoint()
Definition: svddrag.cxx:73
virtual void NbcMove(const Size &rSiz) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdotxtr.cxx:94
bool IsCreate1stPointAsCenter() const
Definition: svdcrtv.hxx:129
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:2247
SdrObjKind meKind
Definition: svdopath.hxx:51
basegfx::B2DPolyPolygon getObjectPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:2235
bool movePathDrag(SdrDragStat &rDrag) const
Definition: svdopath.cxx:589
Degree100 GetSnapAngle() const
Definition: svdsnpv.hxx:225
tools::Long Top() const
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdopath.cxx:2264
Abstract DrawObject.
Definition: svdobj.hxx:258
bool IsCreateMode() const
Definition: svdmrkv.hxx:231
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:775
#define Y
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdopath.cxx:2712
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:1964
bool endPathDrag(SdrDragStat const &rDrag)
Definition: svdopath.cxx:822
sal_uInt32 NbcInsPoint(const Point &rPos, bool bNewObj)
Definition: svdopath.cxx:2505
const Point & GetPrev() const
Definition: svddrag.hxx:103
Point PixelToLogic(const Point &rDevicePt) const
natural cubic Spline (ni)
Definition: svdobjkind.hxx:41
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 & GetLastBoundRect() const
Definition: svdobj.cxx:945
size_t GetHdlCount() const
Definition: svdhdl.hxx:461
basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:1106
SdrHdl * GetHdl(size_t nNum) const
Definition: svdhdl.hxx:462
void setClosed(bool bNew)
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:115
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:2889
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:2348
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:2329
closed free-hand line
Definition: svdobjkind.hxx:40
void setY(double fY)
bool equalZero() const
PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdopath.cxx:2259
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
OUString ImpGetDescriptionStr(const char *pStrCacheID) const
Definition: svdobj.cxx:1076
virtual OUString TakeObjNameSingul() const override
Definition: svdopath.cxx:1833
ImpPathForDragAndCreate & impGetDAC() const
Definition: svdopath.cxx:2764
virtual sal_uInt32 GetPointCount() const override
Definition: svdopath.cxx:2427
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:217
OUString aName
void SetUser(std::unique_ptr< SdrDragStatUserData > pU)
Definition: svddrag.hxx:114
double mfCosRotationAngle
Definition: svdtrans.hxx:221
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:2082
bool IsMouseDown() const
Definition: svddrag.hxx:150
virtual void RestoreGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1451
std::unique_ptr< SdrObject, SdrObjectFreeOp > SdrObjectUniquePtr
Definition: svdobj.hxx:96
bool IsWidthEmpty() const
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdopath.cxx:2405
virtual basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2133
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:1636
const basegfx::B2DPolyPolygon & GetPathPoly() const
Definition: svdopath.hxx:141
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:509
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:2208
const SdrFormTextAttribute & getSdrFormTextAttribute() const
#define SAL_WARN(area, stream)
virtual OUString getSpecialDragComment(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2105
double getMinX() const
OUString GetMetricString(tools::Long nVal, bool bNoUnitChars=false, sal_Int32 nNumDigits=-1) const
Definition: svdmodel.cxx:1021
Degree100 abs(Degree100 x)
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:1039
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2075
const SdrHdlList & GetHdlList() const
Definition: svdmrkv.hxx:354
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdopath.cxx:1959
SdrDragStatUserData * GetUser() const
Definition: svddrag.hxx:113
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
SdrCreateCmd
Definition: svdtypes.hxx:27
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
bool IsBigOrtho() const
Definition: svdsnpv.hxx:259
virtual void SetChanged()
Definition: svdobj.cxx:1006
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:875
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:50
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:2751
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:2297
sal_uInt32 GetSourceHdlNum() const
Definition: svdhdl.hxx:228
Degree100 nShearAngle
Definition: svdtrans.hxx:218
tools::Long Right() const
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2718
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:895
static bool IsClosed(SdrObjKind eKind)
Definition: svdopath.cxx:525
Point Center() const
void AddHdl(std::unique_ptr< SdrHdl > pHdl)
Definition: svdhdl.cxx:2289
virtual bool IsFontwork() const
Definition: svdotext.cxx:1735
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