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