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