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