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