xref: /AOO41X/main/svx/source/svdraw/svdopath.cxx (revision ca62e2c2083b5d0995f1245bad6c2edfb455fbec)
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                 // postion
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