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