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