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