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