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