summaryrefslogtreecommitdiff
path: root/svx/source/svdraw/svdopath.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'svx/source/svdraw/svdopath.cxx')
-rw-r--r--svx/source/svdraw/svdopath.cxx3118
1 files changed, 3118 insertions, 0 deletions
diff --git a/svx/source/svdraw/svdopath.cxx b/svx/source/svdraw/svdopath.cxx
new file mode 100644
index 000000000000..9d136ef82eae
--- /dev/null
+++ b/svx/source/svdraw/svdopath.cxx
@@ -0,0 +1,3118 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_svx.hxx"
+
+#include <tools/bigint.hxx>
+#include <svx/svdopath.hxx>
+#include <math.h>
+#include <svx/xpool.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/svdattr.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdview.hxx> // fuer MovCreate bei Freihandlinien
+#include "svdglob.hxx" // Stringcache
+#include "svdstr.hrc" // Objektname
+
+#ifdef _MSC_VER
+#pragma optimize ("",off)
+#pragma warning(disable: 4748) // "... because optimizations are disabled ..."
+#endif
+
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/polypolygoneditor.hxx>
+#include <svx/xlntrit.hxx>
+#include <vcl/salbtype.hxx> // FRound
+#include "svdoimp.hxx"
+#include <svx/sdr/contact/viewcontactofsdrpathobj.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+// #104018# replace macros above with type-safe methods
+inline sal_Int32 ImplTwipsToMM(sal_Int32 nVal) { return ((nVal * 127 + 36) / 72); }
+inline sal_Int32 ImplMMToTwips(sal_Int32 nVal) { return ((nVal * 72 + 63) / 127); }
+inline sal_Int64 ImplTwipsToMM(sal_Int64 nVal) { return ((nVal * 127 + 36) / 72); }
+inline sal_Int64 ImplMMToTwips(sal_Int64 nVal) { return ((nVal * 72 + 63) / 127); }
+inline double ImplTwipsToMM(double fVal) { return (fVal * (127.0 / 72.0)); }
+inline double ImplMMToTwips(double fVal) { return (fVal * (72.0 / 127.0)); }
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/attribute/sdrtextattribute.hxx>
+#include <svx/sdr/primitive2d/sdrattributecreator.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/attribute/sdrformtextattribute.hxx>
+
+using namespace sdr;
+
+inline USHORT GetPrevPnt(USHORT nPnt, USHORT nPntMax, FASTBOOL bClosed)
+{
+ if (nPnt>0) {
+ nPnt--;
+ } else {
+ nPnt=nPntMax;
+ if (bClosed) nPnt--;
+ }
+ return nPnt;
+}
+
+inline USHORT GetNextPnt(USHORT nPnt, USHORT nPntMax, FASTBOOL bClosed)
+{
+ nPnt++;
+ if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
+ return nPnt;
+}
+
+struct ImpSdrPathDragData : public SdrDragStatUserData
+{
+ XPolygon aXP; // Ausschnitt aud dem Originalpolygon
+ FASTBOOL bValid; // FALSE = zu wenig Punkte
+ FASTBOOL bClosed; // geschlossenes Objekt?
+ USHORT nPoly; // Nummer des Polygons im PolyPolygon
+ USHORT nPnt; // Punktnummer innerhalb des obigen Polygons
+ USHORT nPntAnz; // Punktanzahl des Polygons
+ USHORT nPntMax; // Maximaler Index
+ FASTBOOL bBegPnt; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
+ FASTBOOL bEndPnt; // Gedraggter Punkt ist der Endpunkt einer Polyline
+ USHORT nPrevPnt; // Index des vorherigen Punkts
+ USHORT nNextPnt; // Index des naechsten Punkts
+ FASTBOOL bPrevIsBegPnt; // Vorheriger Punkt ist Anfangspunkt einer Polyline
+ FASTBOOL bNextIsEndPnt; // Folgepunkt ist Endpunkt einer Polyline
+ USHORT nPrevPrevPnt; // Index des vorvorherigen Punkts
+ USHORT nNextNextPnt; // Index des uebernaechsten Punkts
+ FASTBOOL bControl; // Punkt ist ein Kontrollpunkt
+ FASTBOOL bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
+ FASTBOOL bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
+ FASTBOOL bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
+ FASTBOOL bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
+ USHORT nPrevPrevPnt0;
+ USHORT nPrevPnt0;
+ USHORT nPnt0;
+ USHORT nNextPnt0;
+ USHORT nNextNextPnt0;
+ FASTBOOL bEliminate; // Punkt loeschen? (wird von MovDrag gesetzt)
+
+ // ##
+ BOOL mbMultiPointDrag;
+ const XPolyPolygon maOrig;
+ XPolyPolygon maMove;
+ Container maHandles;
+
+public:
+ ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, BOOL bMuPoDr, const SdrDragStat& rDrag);
+ void ResetPoly(const SdrPathObj& rPO);
+ BOOL IsMultiPointDrag() const { return mbMultiPointDrag; }
+};
+
+ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, BOOL bMuPoDr, const SdrDragStat& rDrag)
+: aXP(5),
+ mbMultiPointDrag(bMuPoDr),
+ maOrig(rPO.GetPathPoly()),
+ maHandles(0)
+{
+ if(mbMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
+
+ for(sal_uInt32 a(0); a < nHdlCount; a++)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ maHandles.Insert(pTestHdl, CONTAINER_APPEND);
+ }
+ }
+
+ maMove = maOrig;
+ bValid = TRUE;
+ }
+ else
+ {
+ bValid=FALSE;
+ bClosed=rPO.IsClosed(); // geschlossenes Objekt?
+ nPoly=(sal_uInt16)rHdl.GetPolyNum(); // Nummer des Polygons im PolyPolygon
+ nPnt=(sal_uInt16)rHdl.GetPointNum(); // Punktnummer innerhalb des obigen Polygons
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ nPntAnz=aTmpXP.GetPointCount(); // Punktanzahl des Polygons
+ if (nPntAnz==0 || (bClosed && nPntAnz==1)) return; // min. 1Pt bei Line, min. 2 bei Polygon
+ nPntMax=nPntAnz-1; // Maximaler Index
+ bBegPnt=!bClosed && nPnt==0; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
+ bEndPnt=!bClosed && nPnt==nPntMax; // Gedraggter Punkt ist der Endpunkt einer Polyline
+ if (bClosed && nPntAnz<=3) { // Falls Polygon auch nur eine Linie ist
+ bBegPnt=(nPntAnz<3) || nPnt==0;
+ bEndPnt=(nPntAnz<3) || nPnt==nPntMax-1;
+ }
+ nPrevPnt=nPnt; // Index des vorherigen Punkts
+ nNextPnt=nPnt; // Index des naechsten Punkts
+ if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
+ if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
+ bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
+ bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
+ nPrevPrevPnt=nPnt; // Index des vorvorherigen Punkts
+ nNextNextPnt=nPnt; // Index des uebernaechsten Punkts
+ if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
+ if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
+ bControl=rHdl.IsPlusHdl(); // Punkt ist ein Kontrollpunkt
+ bIsPrevControl=FALSE; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
+ bIsNextControl=FALSE; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
+ bPrevIsControl=FALSE; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
+ bNextIsControl=FALSE; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
+ if (bControl) {
+ bIsPrevControl=aTmpXP.IsControl(nPrevPnt);
+ bIsNextControl=!bIsPrevControl;
+ } else {
+ bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==XPOLY_CONTROL;
+ bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==XPOLY_CONTROL;
+ }
+ nPrevPrevPnt0=nPrevPrevPnt;
+ nPrevPnt0 =nPrevPnt;
+ nPnt0 =nPnt;
+ nNextPnt0 =nNextPnt;
+ nNextNextPnt0=nNextNextPnt;
+ nPrevPrevPnt=0;
+ nPrevPnt=1;
+ nPnt=2;
+ nNextPnt=3;
+ nNextNextPnt=4;
+ bEliminate=FALSE;
+ ResetPoly(rPO);
+ bValid=TRUE;
+ }
+}
+
+void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
+{
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
+ aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
+ aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
+ aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
+ aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
+}
+
+/*************************************************************************/
+
+struct ImpPathCreateUser : public SdrDragStatUserData
+{
+ Point aBezControl0;
+ Point aBezStart;
+ Point aBezCtrl1;
+ Point aBezCtrl2;
+ Point aBezEnd;
+ Point aCircStart;
+ Point aCircEnd;
+ Point aCircCenter;
+ Point aLineStart;
+ Point aLineEnd;
+ Point aRectP1;
+ Point aRectP2;
+ Point aRectP3;
+ long nCircRadius;
+ long nCircStWink;
+ long nCircRelWink;
+ FASTBOOL bBezier;
+ FASTBOOL bBezHasCtrl0;
+ FASTBOOL bCurve;
+ FASTBOOL bCircle;
+ FASTBOOL bAngleSnap;
+ FASTBOOL bLine;
+ FASTBOOL bLine90;
+ FASTBOOL bRect;
+ FASTBOOL bMixedCreate;
+ USHORT nBezierStartPoint;
+ SdrObjKind eStartKind;
+ SdrObjKind eAktKind;
+
+public:
+ ImpPathCreateUser(): nCircRadius(0),nCircStWink(0),nCircRelWink(0),
+ bBezier(FALSE),bBezHasCtrl0(FALSE),bCurve(FALSE),bCircle(FALSE),bAngleSnap(FALSE),bLine(FALSE),bLine90(FALSE),bRect(FALSE),
+ bMixedCreate(FALSE),nBezierStartPoint(0),eStartKind(OBJ_NONE),eAktKind(OBJ_NONE) { }
+
+ void ResetFormFlags() { bBezier=FALSE; bCurve=FALSE; bCircle=FALSE; bLine=FALSE; bRect=FALSE; }
+ FASTBOOL IsFormFlag() const { return bBezier || bCurve || bCircle || bLine || bRect; }
+ XPolygon GetFormPoly() const;
+ FASTBOOL CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown);
+ XPolygon GetBezierPoly() const;
+ //FASTBOOL CalcCurve(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView) { return FALSE; }
+ XPolygon GetCurvePoly() const { return XPolygon(); }
+ FASTBOOL CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
+ XPolygon GetCirclePoly() const;
+ FASTBOOL CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
+ Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView* pView) const;
+ XPolygon GetLinePoly() const;
+ FASTBOOL CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView);
+ XPolygon GetRectPoly() const;
+};
+
+XPolygon ImpPathCreateUser::GetFormPoly() const
+{
+ if (bBezier) return GetBezierPoly();
+ if (bCurve) return GetCurvePoly();
+ if (bCircle) return GetCirclePoly();
+ if (bLine) return GetLinePoly();
+ if (bRect) return GetRectPoly();
+ return XPolygon();
+}
+
+FASTBOOL ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, FASTBOOL bMouseDown)
+{
+ FASTBOOL bRet=TRUE;
+ aBezStart=rP1;
+ aBezCtrl1=rP1+rDir;
+ aBezCtrl2=rP2;
+
+ // #i21479#
+ // Also copy the end point when no end point is set yet
+ if (!bMouseDown || (0L == aBezEnd.X() && 0L == aBezEnd.Y())) aBezEnd=rP2;
+
+ bBezier=bRet;
+ return bRet;
+}
+
+XPolygon ImpPathCreateUser::GetBezierPoly() const
+{
+ XPolygon aXP(4);
+ aXP[0]=aBezStart; aXP.SetFlags(0,XPOLY_SMOOTH);
+ aXP[1]=aBezCtrl1; aXP.SetFlags(1,XPOLY_CONTROL);
+ aXP[2]=aBezCtrl2; aXP.SetFlags(2,XPOLY_CONTROL);
+ aXP[3]=aBezEnd;
+ return aXP;
+}
+
+FASTBOOL ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
+{
+ long nTangAngle=GetAngle(rDir);
+ aCircStart=rP1;
+ aCircEnd=rP2;
+ aCircCenter=rP1;
+ long dx=rP2.X()-rP1.X();
+ long dy=rP2.Y()-rP1.Y();
+ long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
+ dAngle=NormAngle360(dAngle);
+ long nTmpAngle=NormAngle360(9000-dAngle);
+ FASTBOOL bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
+ long nRad=0;
+ if (bRet) {
+ double cs=cos(nTmpAngle*nPi180);
+ double nR=(double)GetLen(Point(dx,dy))/cs/2;
+ nRad=Abs(Round(nR));
+ }
+ if (dAngle<18000) {
+ nCircStWink=NormAngle360(nTangAngle-9000);
+ nCircRelWink=NormAngle360(2*dAngle);
+ aCircCenter.X()+=Round(nRad*cos((nTangAngle+9000)*nPi180));
+ aCircCenter.Y()-=Round(nRad*sin((nTangAngle+9000)*nPi180));
+ } else {
+ nCircStWink=NormAngle360(nTangAngle+9000);
+ nCircRelWink=-NormAngle360(36000-2*dAngle);
+ aCircCenter.X()+=Round(nRad*cos((nTangAngle-9000)*nPi180));
+ aCircCenter.Y()-=Round(nRad*sin((nTangAngle-9000)*nPi180));
+ }
+ bAngleSnap=pView!=NULL && pView->IsAngleSnapEnabled();
+ if (bAngleSnap) {
+ long nSA=pView->GetSnapAngle();
+ if (nSA!=0) { // Winkelfang
+ FASTBOOL bNeg=nCircRelWink<0;
+ if (bNeg) nCircRelWink=-nCircRelWink;
+ nCircRelWink+=nSA/2;
+ nCircRelWink/=nSA;
+ nCircRelWink*=nSA;
+ nCircRelWink=NormAngle360(nCircRelWink);
+ if (bNeg) nCircRelWink=-nCircRelWink;
+ }
+ }
+ nCircRadius=nRad;
+ if (nRad==0 || Abs(nCircRelWink)<5) bRet=FALSE;
+ bCircle=bRet;
+ return bRet;
+}
+
+XPolygon ImpPathCreateUser::GetCirclePoly() const
+{
+ if (nCircRelWink>=0) {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ USHORT((nCircStWink+5)/10),USHORT((nCircStWink+nCircRelWink+5)/10),FALSE);
+ aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ } else {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ USHORT(NormAngle360(nCircStWink+nCircRelWink+5)/10),USHORT((nCircStWink+5)/10),FALSE);
+ USHORT nAnz=aXP.GetPointCount();
+ for (USHORT nNum=nAnz/2; nNum>0;) {
+ nNum--; // XPoly Punktreihenfolge umkehren
+ USHORT n2=nAnz-nNum-1;
+ Point aPt(aXP[nNum]);
+ aXP[nNum]=aXP[n2];
+ aXP[n2]=aPt;
+ }
+ aXP[0]=aCircStart; aXP.SetFlags(0,XPOLY_SMOOTH);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ }
+}
+
+Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView* pView) const
+{
+ long x=aCsr.X(),x1=x,x2=x;
+ long y=aCsr.Y(),y1=y,y2=y;
+ FASTBOOL bHLin=nDirY==0;
+ FASTBOOL bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ x1=BigMulDiv(y,nDirX,nDirY);
+ y2=BigMulDiv(x,nDirY,nDirX);
+ long l1=Abs(x1)+Abs(y1);
+ long l2=Abs(x2)+Abs(y2);
+ if ((l1<=l2) != (pView!=NULL && pView->IsBigOrtho())) {
+ x=x1; y=y1;
+ } else {
+ x=x2; y=y2;
+ }
+ }
+ return Point(x,y);
+}
+
+FASTBOOL ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
+{
+ aLineStart=rP1;
+ aLineEnd=rP2;
+ bLine90=FALSE;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=FALSE; return FALSE; }
+ Point aTmpPt(rP2-rP1);
+ long nDirX=rDir.X();
+ long nDirY=rDir.Y();
+ Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=Abs(aP1.X())+Abs(aP1.Y());
+ Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=Abs(aP2.X())+Abs(aP2.Y());
+ if (pView!=NULL && pView->IsOrtho()) nQ1=0; // Ortho schaltet rechtwinklig aus
+ bLine90=nQ1>2*nQ2;
+ if (!bLine90) { // glatter Uebergang
+ aLineEnd+=aP1;
+ } else { // rechtwinkliger Uebergang
+ aLineEnd+=aP2;
+ }
+ bLine=TRUE;
+ return TRUE;
+}
+
+XPolygon ImpPathCreateUser::GetLinePoly() const
+{
+ XPolygon aXP(2);
+ aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,XPOLY_SMOOTH);
+ aXP[1]=aLineEnd;
+ return aXP;
+}
+
+FASTBOOL ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView* pView)
+{
+ aRectP1=rP1;
+ aRectP2=rP1;
+ aRectP3=rP2;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=FALSE; return FALSE; }
+ Point aTmpPt(rP2-rP1);
+ long nDirX=rDir.X();
+ long nDirY=rDir.Y();
+ long x=aTmpPt.X();
+ long y=aTmpPt.Y();
+ FASTBOOL bHLin=nDirY==0;
+ FASTBOOL bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ y=BigMulDiv(x,nDirY,nDirX);
+ long nHypLen=aTmpPt.Y()-y;
+ long nTangAngle=-GetAngle(rDir);
+ // sin=g/h, g=h*sin
+ double a=nTangAngle*nPi180;
+ double sn=sin(a);
+ double cs=cos(a);
+ double nGKathLen=nHypLen*sn;
+ y+=Round(nGKathLen*sn);
+ x+=Round(nGKathLen*cs);
+ }
+ aRectP2.X()+=x;
+ aRectP2.Y()+=y;
+ if (pView!=NULL && pView->IsOrtho()) {
+ long dx1=aRectP2.X()-aRectP1.X(); long dx1a=Abs(dx1);
+ long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=Abs(dy1);
+ long dx2=aRectP3.X()-aRectP2.X(); long dx2a=Abs(dx2);
+ long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=Abs(dy2);
+ FASTBOOL b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
+ if (b1MoreThan2 != pView->IsBigOrtho()) {
+ long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
+ long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
+ aRectP2.X()+=xtemp;
+ aRectP2.Y()+=ytemp;
+ aRectP3.X()+=xtemp;
+ aRectP3.Y()+=ytemp;
+ } else {
+ long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
+ long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
+ aRectP3.X()+=xtemp;
+ aRectP3.Y()+=ytemp;
+ }
+ }
+ bRect=TRUE;
+ return TRUE;
+}
+
+XPolygon ImpPathCreateUser::GetRectPoly() const
+{
+ XPolygon aXP(3);
+ aXP[0]=aRectP1; aXP.SetFlags(0,XPOLY_SMOOTH);
+ aXP[1]=aRectP2;
+ if (aRectP3!=aRectP2) aXP[2]=aRectP3;
+ return aXP;
+}
+
+/*************************************************************************/
+
+class ImpPathForDragAndCreate
+{
+ SdrPathObj& mrSdrPathObject;
+ XPolyPolygon aPathPolygon;
+ SdrObjKind meObjectKind;
+ ImpSdrPathDragData* mpSdrPathDragData;
+ bool mbCreating;
+
+public:
+ ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
+ ~ImpPathForDragAndCreate();
+
+ // drag stuff
+ bool beginPathDrag( SdrDragStat& rDrag ) const;
+ bool movePathDrag( SdrDragStat& rDrag ) const;
+ bool endPathDrag( SdrDragStat& rDrag );
+ //void cancelSpecialDrag( SdrDragStat& rDrag ) const;
+ String getSpecialDragComment(const SdrDragStat& rDrag) const;
+ basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
+
+ // create stuff
+ FASTBOOL BegCreate(SdrDragStat& rStat);
+ FASTBOOL MovCreate(SdrDragStat& rStat);
+ FASTBOOL EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
+ FASTBOOL BckCreate(SdrDragStat& rStat);
+ void BrkCreate(SdrDragStat& rStat);
+ Pointer GetCreatePointer() const;
+
+ // helping stuff
+ bool IsClosed(SdrObjKind eKind) const { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
+ bool IsFreeHand(SdrObjKind eKind) const { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
+ bool IsBezier(SdrObjKind eKind) const { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
+ bool IsCreating() const { return mbCreating; }
+
+ // get the polygon
+ basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
+ basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag) const;
+ basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
+};
+
+ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
+: mrSdrPathObject(rSdrPathObject),
+ aPathPolygon(rSdrPathObject.GetPathPoly()),
+ meObjectKind(mrSdrPathObject.meKind),
+ mpSdrPathDragData(0),
+ mbCreating(false)
+{
+}
+
+ImpPathForDragAndCreate::~ImpPathForDragAndCreate()
+{
+ if(mpSdrPathDragData)
+ {
+ delete mpSdrPathDragData;
+ }
+}
+
+bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat& rDrag ) const
+{
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ if(!pHdl)
+ return FALSE;
+
+ BOOL bMultiPointDrag(TRUE);
+
+ if(aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()].IsControl((sal_uInt16)pHdl->GetPointNum()))
+ bMultiPointDrag = FALSE;
+
+ if(bMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const sal_uInt32 nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : 0);
+ sal_uInt32 nSelectedPoints(0);
+
+ for(sal_uInt32 a(0); a < nHdlCount; a++)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ nSelectedPoints++;
+ }
+ }
+
+ if(nSelectedPoints <= 1)
+ bMultiPointDrag = FALSE;
+ }
+
+ ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag);
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ DBG_ERROR("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData ist ungueltig");
+ delete mpSdrPathDragData;
+ ((ImpPathForDragAndCreate*)this)->mpSdrPathDragData = 0;
+ return false;
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ Point aDelta(rDrag.GetNow() - rDrag.GetStart());
+
+ if(aDelta.X() || aDelta.Y())
+ {
+ for(sal_uInt32 a(0); a < mpSdrPathDragData->maHandles.Count(); a++)
+ {
+ SdrHdl* pHandle = (SdrHdl*)mpSdrPathDragData->maHandles.GetObject(a);
+ const sal_uInt16 nPolyIndex((sal_uInt16)pHandle->GetPolyNum());
+ const sal_uInt16 nPointIndex((sal_uInt16)pHandle->GetPointNum());
+ const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
+ XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
+ const sal_uInt16 nPointCount(rOrig.GetPointCount());
+ BOOL bClosed(rOrig[0] == rOrig[nPointCount-1]);
+
+ // move point itself
+ rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
+
+ // when point is first and poly closed, move close point, too.
+ if(nPointCount > 0 && !nPointIndex && bClosed)
+ {
+ rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
+
+ // when moving the last point it may be necessary to move the
+ // control point in front of this one, too.
+ if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
+ rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
+ }
+
+ // is a control point before this?
+ if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
+ }
+
+ // is a control point after this?
+ if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
+ }
+ }
+ }
+ }
+ else
+ {
+ mpSdrPathDragData->ResetPoly(mrSdrPathObject);
+
+ // Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
+ FASTBOOL bClosed =mpSdrPathDragData->bClosed ; // geschlossenes Objekt?
+ USHORT nPnt =mpSdrPathDragData->nPnt ; // Punktnummer innerhalb des obigen Polygons
+ FASTBOOL bBegPnt =mpSdrPathDragData->bBegPnt ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
+ FASTBOOL bEndPnt =mpSdrPathDragData->bEndPnt ; // Gedraggter Punkt ist der Endpunkt einer Polyline
+ USHORT nPrevPnt =mpSdrPathDragData->nPrevPnt ; // Index des vorherigen Punkts
+ USHORT nNextPnt =mpSdrPathDragData->nNextPnt ; // Index des naechsten Punkts
+ FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
+ FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
+ USHORT nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // Index des vorvorherigen Punkts
+ USHORT nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // Index des uebernaechsten Punkts
+ FASTBOOL bControl =mpSdrPathDragData->bControl ; // Punkt ist ein Kontrollpunkt
+ //FASTBOOL bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
+ FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
+ FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
+ FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
+
+ // Ortho bei Linien/Polygonen = Winkel beibehalten
+ if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsOrtho()) {
+ FASTBOOL bBigOrtho=rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow()); // die aktuelle Position
+ Point aPnt(mpSdrPathDragData->aXP[nPnt]); // der gedraggte Punkt
+ USHORT nPnt1=0xFFFF,nPnt2=0xFFFF; // seine Nachbarpunkte
+ Point aNeuPos1,aNeuPos2; // die neuen Alternativen fuer aPos
+ FASTBOOL bPnt1=FALSE,bPnt2=FALSE; // die neuen Alternativen gueltig?
+ if (!bClosed && mpSdrPathDragData->nPntAnz>=2) { // Mind. 2 Pt bei Linien
+ if (!bBegPnt) nPnt1=nPrevPnt;
+ if (!bEndPnt) nPnt2=nNextPnt;
+ }
+ if (bClosed && mpSdrPathDragData->nPntAnz>=3) { // Mind. 3 Pt bei Polygon
+ nPnt1=nPrevPnt;
+ nPnt2=nNextPnt;
+ }
+ if (nPnt1!=0xFFFF && !bPrevIsControl) {
+ Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
+ long ndx0=aPnt.X()-aPnt1.X();
+ long ndy0=aPnt.Y()-aPnt1.Y();
+ FASTBOOL bHLin=ndy0==0;
+ FASTBOOL bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ long ndx=aPos.X()-aPnt1.X();
+ long ndy=aPos.Y()-aPnt1.Y();
+ bPnt1=TRUE;
+ double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
+ double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
+ FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=long(ndy0*nXFact);
+ if (bVer) ndx=long(ndx0*nYFact);
+ aNeuPos1=aPnt1;
+ aNeuPos1.X()+=ndx;
+ aNeuPos1.Y()+=ndy;
+ }
+ }
+ if (nPnt2!=0xFFFF && !bNextIsControl) {
+ Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
+ long ndx0=aPnt.X()-aPnt2.X();
+ long ndy0=aPnt.Y()-aPnt2.Y();
+ FASTBOOL bHLin=ndy0==0;
+ FASTBOOL bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ long ndx=aPos.X()-aPnt2.X();
+ long ndy=aPos.Y()-aPnt2.Y();
+ bPnt2=TRUE;
+ double nXFact=0; if (!bVLin) nXFact=(double)ndx/(double)ndx0;
+ double nYFact=0; if (!bHLin) nYFact=(double)ndy/(double)ndy0;
+ FASTBOOL bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ FASTBOOL bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=long(ndy0*nXFact);
+ if (bVer) ndx=long(ndx0*nYFact);
+ aNeuPos2=aPnt2;
+ aNeuPos2.X()+=ndx;
+ aNeuPos2.Y()+=ndy;
+ }
+ }
+ if (bPnt1 && bPnt2) { // beide Alternativen vorhanden (Konkurenz)
+ BigInt nX1(aNeuPos1.X()-aPos.X()); nX1*=nX1;
+ BigInt nY1(aNeuPos1.Y()-aPos.Y()); nY1*=nY1;
+ BigInt nX2(aNeuPos2.X()-aPos.X()); nX2*=nX2;
+ BigInt nY2(aNeuPos2.Y()-aPos.Y()); nY2*=nY2;
+ nX1+=nY1; // Korrekturabstand zum Quadrat
+ nX2+=nY2; // Korrekturabstand zum Quadrat
+ // Die Alternative mit dem geringeren Korrekturbedarf gewinnt
+ if (nX1<nX2) bPnt2=FALSE; else bPnt1=FALSE;
+ }
+ if (bPnt1) rDrag.Now()=aNeuPos1;
+ if (bPnt2) rDrag.Now()=aNeuPos2;
+ }
+ rDrag.SetActionRect(Rectangle(rDrag.GetNow(),rDrag.GetNow()));
+
+ // IBM Special: Punkte eliminieren, wenn die beiden angrenzenden
+ // Linien eh' fast 180 deg sind.
+ if (!bControl && rDrag.GetView()!=NULL && rDrag.GetView()->IsEliminatePolyPoints() &&
+ !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
+ {
+ Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
+ aPt-=rDrag.GetNow();
+ long nWink1=GetAngle(aPt);
+ aPt=rDrag.GetNow();
+ aPt-=mpSdrPathDragData->aXP[nPrevPnt];
+ long nWink2=GetAngle(aPt);
+ long nDiff=nWink1-nWink2;
+ nDiff=Abs(nDiff);
+ mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
+ if (mpSdrPathDragData->bEliminate) { // Position anpassen, damit Smooth an den Enden stimmt
+ aPt=mpSdrPathDragData->aXP[nNextPnt];
+ aPt+=mpSdrPathDragData->aXP[nPrevPnt];
+ aPt/=2;
+ rDrag.Now()=aPt;
+ }
+ }
+
+ // Um diese Entfernung wurde insgesamt gedraggd
+ Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
+
+ // Insgesamt sind 8 Faelle moeglich:
+ // X 1. Weder rechts noch links Ctrl.
+ // o--X--o 2. Rechts und links Ctrl, gedraggd wird St.
+ // o--X 3. Nur links Ctrl, gedraggd wird St.
+ // X--o 4. Nur rechts Ctrl, gedraggd wird St.
+ // x--O--o 5. Rechts und links Ctrl, gedraggd wird links.
+ // x--O 6. Nur links Ctrl, gedraggd wird links.
+ // o--O--x 7. Rechts und links Ctrl, gedraggd wird rechts.
+ // O--x 8. Nur rechts Ctrl, gedraggd wird rechts.
+ // Zusaetzlich ist zu beachten, dass das Veraendern einer Linie (keine Kurve)
+ // eine evtl. Kurve am anderen Ende der Linie bewirkt, falls dort Smooth
+ // gesetzt ist (Kontrollpunktausrichtung an Gerade).
+
+ mpSdrPathDragData->aXP[nPnt]+=aDiff;
+
+ // Nun symmetrische PlusHandles etc. checken
+ if (bControl) { // Faelle 5,6,7,8
+ USHORT nSt=nPnt; // der zugehoerige Stuetzpunkt
+ USHORT nFix=nPnt; // der gegenueberliegende Kontrollpunkt
+ if (bIsNextControl) { // Wenn der naechste ein Kontrollpunkt ist, muss der vorh. der Stuetzpunkt sein
+ nSt=nPrevPnt;
+ nFix=nPrevPrevPnt;
+ } else {
+ nSt=nNextPnt;
+ nFix=nNextNextPnt;
+ }
+ if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
+ }
+ }
+
+ if (!bControl) { // Faelle 1,2,3,4 wobei bei 1 nix passiert und bei 3+4 unten noch mehr folgt
+ // die beiden Kontrollpunkte mit verschieben
+ if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
+ if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
+ // Kontrollpunkt ggf. an Gerade ausrichten
+ if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
+ if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // Fall 3
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
+ }
+ if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // Fall 4
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
+ }
+ }
+ // Und nun noch die anderen Enden der Strecken ueberpruefen (nPnt+-1).
+ // Ist dort eine Kurve (IsControl(nPnt+-2)) mit SmoothJoin (nPnt+-1),
+ // so muss der entsprechende Kontrollpunkt (nPnt+-2) angepasst werden.
+ if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
+ }
+ }
+ if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat& rDrag)
+{
+ Point aLinePt1;
+ Point aLinePt2;
+ bool bLineGlueMirror(OBJ_LINE == meObjectKind);
+ if (bLineGlueMirror) { // #40549#
+ XPolygon& rXP=aPathPolygon[0];
+ aLinePt1=rXP[0];
+ aLinePt2=rXP[1];
+ }
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aPathPolygon = mpSdrPathDragData->maMove;
+ }
+ else
+ {
+ const SdrHdl* pHdl=rDrag.GetHdl();
+
+ // Referenz auf das Polygon
+ XPolygon& rXP=aPathPolygon[(sal_uInt16)pHdl->GetPolyNum()];
+
+ // Die 5 Punkte die sich evtl. geaendert haben
+ if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
+ if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
+ if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
+ if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
+ rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
+
+ // Letzter Punkt muss beim Geschlossenen immer gleich dem Ersten sein
+ if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
+
+ if (mpSdrPathDragData->bEliminate)
+ {
+ basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
+ {
+ basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
+ aCandidate.remove(nPnt);
+
+ if((IsClosed(meObjectKind) && aCandidate.count() < 3L) || aCandidate.count() < 2L)
+ {
+ aTempPolyPolygon.remove(nPoly);
+ }
+ else
+ {
+ aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
+ }
+ }
+
+ aPathPolygon = XPolyPolygon(aTempPolyPolygon);
+ }
+
+ // Winkel anpassen fuer Text an einfacher Linie
+ if (bLineGlueMirror)
+ { // #40549#
+ Point aLinePt1_(aPathPolygon[0][0]);
+ Point aLinePt2_(aPathPolygon[0][1]);
+ FASTBOOL bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
+ FASTBOOL bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
+ if (bXMirr || bYMirr) {
+ Point aRef1(mrSdrPathObject.GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.Y()++;
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.X()++;
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ }
+ }
+
+ delete mpSdrPathDragData;
+ mpSdrPathDragData = 0;
+
+ return true;
+}
+
+/*void ImpPathForDragAndCreate::cancelSpecialDrag( SdrDragStat& rDrag ) const
+{
+ ImpSdrPathDragData* pID=(ImpSdrPathDragData*)rDrag.GetUser();
+ if (pID!=NULL) {
+ delete pID;
+ rDrag.SetUser(NULL);
+ }
+}*/
+
+String ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ XubString aStr;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment && rDrag.GetUser())
+ {
+ // #i103058# re-add old creation comment mode
+ ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
+ const SdrObjKind eKindMerk(meObjectKind);
+ mrSdrPathObject.meKind = pU->eAktKind;
+ mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewCreateObj, aStr);
+ mrSdrPathObject.meKind = eKindMerk;
+
+ Point aPrev(rDrag.GetPrev());
+ Point aNow(rDrag.GetNow());
+
+ if(pU->bLine)
+ aNow = pU->aLineEnd;
+
+ aNow -= aPrev;
+ aStr.AppendAscii(" (");
+
+ XubString aMetr;
+
+ if(pU->bCircle)
+ {
+ mrSdrPathObject.GetModel()->TakeWinkStr(Abs(pU->nCircRelWink), aMetr);
+ aStr += aMetr;
+ aStr.AppendAscii(" r=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(pU->nCircRadius, aMetr, TRUE);
+ aStr += aMetr;
+ }
+
+ aStr.AppendAscii("dx=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X(), aMetr, TRUE);
+ aStr += aMetr;
+
+ aStr.AppendAscii(" dy=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y(), aMetr, TRUE);
+ aStr += aMetr;
+
+ if(!IsFreeHand(meObjectKind))
+ {
+ INT32 nLen(GetLen(aNow));
+ aStr.AppendAscii(" l=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
+ aStr += aMetr;
+
+ INT32 nWink(GetAngle(aNow));
+ aStr += sal_Unicode(' ');
+ mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
+ aStr += aMetr;
+ }
+
+ aStr += sal_Unicode(')');
+ }
+ else if(!mrSdrPathObject.GetModel() || !pHdl)
+ {
+ // #i103058# fallback when no model and/or Handle, both needed
+ // for else-path
+ mrSdrPathObject.ImpTakeDescriptionStr(STR_DragPathObj, aStr);
+ }
+ else
+ {
+ // #i103058# standard for modification; model and handle needed
+ ImpSdrPathDragData* pDragData = mpSdrPathDragData;
+
+ if(!pDragData)
+ {
+ // getSpecialDragComment is also used from create, so fallback to GetUser()
+ // when mpSdrPathDragData is not set
+ pDragData = (ImpSdrPathDragData*)rDrag.GetUser();
+ }
+
+ if(!pDragData)
+ {
+ DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
+ return String();
+ }
+
+ if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
+ {
+ // Punkt von ...
+ mrSdrPathObject.ImpTakeDescriptionStr(STR_ViewMarkedPoint, aStr);
+
+ // %O loeschen
+ XubString aStr2(ImpGetResStr(STR_EditDelete));
+
+ // UNICODE: Punkt von ... loeschen
+ aStr2.SearchAndReplaceAscii("%1", aStr);
+
+ return aStr2;
+ }
+
+ // dx=0.00 dy=0.00 // Beide Seiten Bezier
+ // dx=0.00 dy=0.00 l=0.00 0.00 // Anfang oder Ende oder eine Seite Bezier bzw. Hebel
+ // dx=0.00 dy=0.00 l=0.00 0.00 / l=0.00 0.00 // Mittendrin
+ XubString aMetr;
+ Point aBeg(rDrag.GetStart());
+ Point aNow(rDrag.GetNow());
+
+ aStr = String();
+ aStr.AppendAscii("dx=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(aNow.X() - aBeg.X(), aMetr, TRUE);
+ aStr += aMetr;
+
+ aStr.AppendAscii(" dy=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(aNow.Y() - aBeg.Y(), aMetr, TRUE);
+ aStr += aMetr;
+
+ if(!pDragData->IsMultiPointDrag())
+ {
+ UINT16 nPntNum((sal_uInt16)pHdl->GetPointNum());
+ const XPolygon& rXPoly = aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
+ UINT16 nPntAnz((sal_uInt16)rXPoly.GetPointCount());
+ BOOL bClose(IsClosed(meObjectKind));
+
+ if(bClose)
+ nPntAnz--;
+
+ if(pHdl->IsPlusHdl())
+ {
+ // Hebel
+ UINT16 nRef(nPntNum);
+
+ if(rXPoly.IsControl(nPntNum + 1))
+ nRef--;
+ else
+ nRef++;
+
+ aNow -= rXPoly[nRef];
+
+ INT32 nLen(GetLen(aNow));
+ aStr.AppendAscii(" l=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
+ aStr += aMetr;
+
+ INT32 nWink(GetAngle(aNow));
+ aStr += sal_Unicode(' ');
+ mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
+ aStr += aMetr;
+ }
+ else if(nPntAnz > 1)
+ {
+ UINT16 nPntMax(nPntAnz - 1);
+ Point aPt1,aPt2;
+ BOOL bIsClosed(IsClosed(meObjectKind));
+ BOOL bPt1(nPntNum > 0);
+ BOOL bPt2(nPntNum < nPntMax);
+
+ if(bIsClosed && nPntAnz > 2)
+ {
+ bPt1 = TRUE;
+ bPt2 = TRUE;
+ }
+
+ UINT16 nPt1,nPt2;
+
+ if(nPntNum > 0)
+ nPt1 = nPntNum - 1;
+ else
+ nPt1 = nPntMax;
+
+ if(nPntNum < nPntMax)
+ nPt2 = nPntNum + 1;
+ else
+ nPt2 = 0;
+
+ if(bPt1 && rXPoly.IsControl(nPt1))
+ bPt1 = FALSE; // Keine Anzeige
+
+ if(bPt2 && rXPoly.IsControl(nPt2))
+ bPt2 = FALSE; // von Bezierdaten
+
+ if(bPt1)
+ {
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt1];
+
+ INT32 nLen(GetLen(aPt));
+ aStr.AppendAscii(" l=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
+ aStr += aMetr;
+
+ INT32 nWink(GetAngle(aPt));
+ aStr += sal_Unicode(' ');
+ mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
+ aStr += aMetr;
+ }
+
+ if(bPt2)
+ {
+ if(bPt1)
+ aStr.AppendAscii(" / ");
+ else
+ aStr.AppendAscii(" ");
+
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt2];
+
+ INT32 nLen(GetLen(aPt));
+ aStr.AppendAscii("l=");
+ mrSdrPathObject.GetModel()->TakeMetricStr(nLen, aMetr, TRUE);
+ aStr += aMetr;
+
+ INT32 nWink(GetAngle(aPt));
+ aStr += sal_Unicode(' ');
+ mrSdrPathObject.GetModel()->TakeWinkStr(nWink, aMetr);
+ aStr += aMetr;
+ }
+ }
+ }
+ }
+
+ return aStr;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ DBG_ERROR("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData ist ungueltig");
+ return basegfx::B2DPolyPolygon();
+ }
+
+ XPolyPolygon aRetval;
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aRetval.Insert(mpSdrPathDragData->maMove);
+ }
+ else
+ {
+ const XPolygon& rXP=aPathPolygon[(sal_uInt16)rDrag.GetHdl()->GetPolyNum()];
+ if (rXP.GetPointCount()<=2) { //|| rXPoly.GetFlags(1)==XPOLY_CONTROL && rXPoly.GetPointCount()<=4
+ XPolygon aXPoly(rXP);
+ aXPoly[(sal_uInt16)rDrag.GetHdl()->GetPointNum()]=rDrag.GetNow();
+ aRetval.Insert(aXPoly);
+ return aRetval.getB2DPolyPolygon();
+ }
+ // Div. Daten lokal Kopieren fuer weniger Code und schnelleren Zugriff
+ FASTBOOL bClosed =mpSdrPathDragData->bClosed ; // geschlossenes Objekt?
+ USHORT nPntAnz =mpSdrPathDragData->nPntAnz ; // Punktanzahl
+ USHORT nPnt =mpSdrPathDragData->nPnt ; // Punktnummer innerhalb des Polygons
+ FASTBOOL bBegPnt =mpSdrPathDragData->bBegPnt ; // Gedraggter Punkt ist der Anfangspunkt einer Polyline
+ FASTBOOL bEndPnt =mpSdrPathDragData->bEndPnt ; // Gedraggter Punkt ist der Endpunkt einer Polyline
+ USHORT nPrevPnt =mpSdrPathDragData->nPrevPnt ; // Index des vorherigen Punkts
+ USHORT nNextPnt =mpSdrPathDragData->nNextPnt ; // Index des naechsten Punkts
+ FASTBOOL bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // Vorheriger Punkt ist Anfangspunkt einer Polyline
+ FASTBOOL bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // Folgepunkt ist Endpunkt einer Polyline
+ USHORT nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // Index des vorvorherigen Punkts
+ USHORT nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // Index des uebernaechsten Punkts
+ FASTBOOL bControl =mpSdrPathDragData->bControl ; // Punkt ist ein Kontrollpunkt
+ //FASTBOOL bIsPrevControl=mpSdrPathDragData->bIsPrevControl; // Punkt ist Kontrollpunkt vor einem Stuetzpunkt
+ FASTBOOL bIsNextControl=mpSdrPathDragData->bIsNextControl; // Punkt ist Kontrollpunkt hinter einem Stuetzpunkt
+ FASTBOOL bPrevIsControl=mpSdrPathDragData->bPrevIsControl; // Falls nPnt ein StPnt: Davor ist ein Kontrollpunkt
+ FASTBOOL bNextIsControl=mpSdrPathDragData->bNextIsControl; // Falls nPnt ein StPnt: Dahinter ist ein Kontrollpunkt
+ XPolygon aXPoly(mpSdrPathDragData->aXP);
+ XPolygon aLine1(2);
+ XPolygon aLine2(2);
+ XPolygon aLine3(2);
+ XPolygon aLine4(2);
+ if (bControl) {
+ aLine1[1]=mpSdrPathDragData->aXP[nPnt];
+ if (bIsNextControl) { // bin ich Kontrollpunkt hinter der Stuetzstelle?
+ aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
+ // Hebellienien fuer das gegenueberliegende Kurvensegment
+ aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
+ aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
+ } else {
+ aXPoly.Remove(0,1);
+ }
+ } else { // ansonsten bin ich Kontrollpunkt vor der Stuetzstelle
+ aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
+ // Hebellinien fuer das gegenueberliegende Kurvensegment
+ aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
+ aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ }
+ } else { // ansonsten kein Kontrollpunkt
+ if (mpSdrPathDragData->bEliminate) {
+ aXPoly.Remove(2,1);
+ }
+ if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_NORMAL);
+ else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],XPOLY_CONTROL);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],XPOLY_NORMAL);
+ } else {
+ aXPoly.Remove(0,1);
+ if (bBegPnt) aXPoly.Remove(0,1);
+ }
+ if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_NORMAL);
+ else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],XPOLY_CONTROL);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],XPOLY_NORMAL);
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ if (bClosed) { // "Birnenproblem": 2 Linien, 1 Kurve, alles Smooth, Punkt zw. beiden Linien wird gedraggt
+ if (aXPoly.GetPointCount()>nPntAnz && aXPoly.IsControl(1)) {
+ USHORT a=aXPoly.GetPointCount();
+ aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
+ aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
+ aXPoly.Remove(0,3);
+ }
+ }
+ }
+ aRetval.Insert(aXPoly);
+ if (aLine1.GetPointCount()>1) aRetval.Insert(aLine1);
+ if (aLine2.GetPointCount()>1) aRetval.Insert(aLine2);
+ if (aLine3.GetPointCount()>1) aRetval.Insert(aLine3);
+ if (aLine4.GetPointCount()>1) aRetval.Insert(aLine4);
+ }
+
+ return aRetval.getB2DPolyPolygon();
+}
+
+FASTBOOL ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
+{
+ bool bFreeHand(IsFreeHand(meObjectKind));
+ rStat.SetNoSnap(bFreeHand);
+ rStat.SetOrtho8Possible();
+ aPathPolygon.Clear();
+ mbCreating=TRUE;
+ FASTBOOL bMakeStartPoint=TRUE;
+ SdrView* pView=rStat.GetView();
+ if (pView!=NULL && pView->IsUseIncompatiblePathCreateInterface() &&
+ (meObjectKind==OBJ_POLY || meObjectKind==OBJ_PLIN || meObjectKind==OBJ_PATHLINE || meObjectKind==OBJ_PATHFILL)) {
+ bMakeStartPoint=FALSE;
+ }
+ aPathPolygon.Insert(XPolygon());
+ aPathPolygon[0][0]=rStat.GetStart();
+ if (bMakeStartPoint) {
+ aPathPolygon[0][1]=rStat.GetNow();
+ }
+ ImpPathCreateUser* pU=new ImpPathCreateUser;
+ pU->eStartKind=meObjectKind;
+ pU->eAktKind=meObjectKind;
+ rStat.SetUser(pU);
+ return TRUE;
+}
+
+FASTBOOL ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
+{
+ ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
+ SdrView* pView=rStat.GetView();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ if (pView!=NULL && pView->IsCreateMode()) {
+ // ggf. auf anderes CreateTool umschalten
+ UINT16 nIdent;
+ UINT32 nInvent;
+ pView->TakeCurrentObj(nIdent,nInvent);
+ if (nInvent==SdrInventor && pU->eAktKind!=(SdrObjKind)nIdent) {
+ SdrObjKind eNewKind=(SdrObjKind)nIdent;
+ switch (eNewKind) {
+ case OBJ_CARC: case OBJ_CIRC: case OBJ_CCUT: case OBJ_SECT: eNewKind=OBJ_CARC;
+ case OBJ_RECT:
+ case OBJ_LINE: case OBJ_PLIN: case OBJ_POLY:
+ case OBJ_PATHLINE: case OBJ_PATHFILL:
+ case OBJ_FREELINE: case OBJ_FREEFILL:
+ case OBJ_SPLNLINE: case OBJ_SPLNFILL: {
+ pU->eAktKind=eNewKind;
+ pU->bMixedCreate=TRUE;
+ pU->nBezierStartPoint=rXPoly.GetPointCount();
+ if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
+ } break;
+ default: break;
+ } // switch
+ }
+ }
+ USHORT nActPoint=rXPoly.GetPointCount();
+ if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nActPoint<2) {
+ rXPoly[0]=rStat.GetPos0();
+ rXPoly[1]=rStat.GetNow();
+ nActPoint=2;
+ }
+ if (nActPoint==0) {
+ rXPoly[0]=rStat.GetPos0();
+ } else nActPoint--;
+ FASTBOOL bFreeHand=IsFreeHand(pU->eAktKind);
+ rStat.SetNoSnap(bFreeHand /*|| (pU->bMixed && pU->eAktKind==OBJ_LINE)*/);
+ rStat.SetOrtho8Possible(pU->eAktKind!=OBJ_CARC && pU->eAktKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eAktKind!=OBJ_LINE));
+ Point aActMerk(rXPoly[nActPoint]);
+ rXPoly[nActPoint]=rStat.Now();
+ if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
+ Point aPt(rStat.Start());
+ if (pView!=NULL && pView->IsCreate1stPointAsCenter()) {
+ aPt+=aPt;
+ aPt-=rStat.Now();
+ }
+ rXPoly[0]=aPt;
+ }
+ OutputDevice* pOut=pView==NULL ? NULL : pView->GetFirstOutputDevice(); // GetWin(0);
+ if (bFreeHand) {
+ if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
+ if (rStat.IsMouseDown() && nActPoint>0) {
+ // keine aufeinanderfolgenden Punkte an zu Nahe gelegenen Positionen zulassen
+ long nMinDist=1;
+ if (pView!=NULL) nMinDist=pView->GetFreeHandMinDistPix();
+ if (pOut!=NULL) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
+ if (nMinDist<1) nMinDist=1;
+
+ Point aPt0(rXPoly[nActPoint-1]);
+ Point aPt1(rStat.Now());
+ long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
+ long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
+ if (dx<nMinDist && dy<nMinDist) return FALSE;
+
+ // folgendes ist aus EndCreate kopiert (nur kleine Modifikationen)
+ // und sollte dann mal in eine Methode zusammengefasst werden:
+
+ if (nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nActPoint-3);
+ rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
+ rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
+
+ if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
+ rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
+ rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
+ }
+ }
+ rXPoly[nActPoint+1]=rStat.Now();
+ rStat.NextPoint();
+ } else {
+ pU->nBezierStartPoint=nActPoint;
+ }
+ }
+
+ pU->ResetFormFlags();
+ if (IsBezier(pU->eAktKind)) {
+ if (nActPoint>=2) {
+ pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],rStat.IsMouseDown());
+ } else if (pU->bBezHasCtrl0) {
+ pU->CalcBezier(rXPoly[nActPoint-1],rXPoly[nActPoint],pU->aBezControl0-rXPoly[nActPoint-1],rStat.IsMouseDown());
+ }
+ }
+ if (pU->eAktKind==OBJ_CARC && nActPoint>=2) {
+ pU->CalcCircle(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
+ }
+ if (pU->eAktKind==OBJ_LINE && nActPoint>=2) {
+ pU->CalcLine(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
+ }
+ if (pU->eAktKind==OBJ_RECT && nActPoint>=2) {
+ pU->CalcRect(rXPoly[nActPoint-1],rXPoly[nActPoint],rXPoly[nActPoint-1]-rXPoly[nActPoint-2],pView);
+ }
+
+ return TRUE;
+}
+
+FASTBOOL ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
+ FASTBOOL bRet=FALSE;
+ SdrView* pView=rStat.GetView();
+ FASTBOOL bIncomp=pView!=NULL && pView->IsUseIncompatiblePathCreateInterface();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ USHORT nActPoint=rXPoly.GetPointCount()-1;
+ Point aAktMerk(rXPoly[nActPoint]);
+ rXPoly[nActPoint]=rStat.Now();
+ if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
+ if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
+ bRet=eCmd==SDRCREATE_FORCEEND;
+ if (bRet) {
+ mbCreating=FALSE;
+ delete pU;
+ rStat.SetUser(NULL);
+ }
+ return bRet;
+ }
+
+ if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
+ if (rStat.GetPointAnz()>=2) eCmd=SDRCREATE_FORCEEND;
+ bRet=eCmd==SDRCREATE_FORCEEND;
+ if (bRet) {
+ mbCreating=FALSE;
+ delete pU;
+ rStat.SetUser(NULL);
+ }
+ return bRet;
+ }
+ if (eCmd==SDRCREATE_NEXTPOINT || eCmd==SDRCREATE_NEXTOBJECT) {
+ // keine aufeinanderfolgenden Punkte an identischer Position zulassen
+ if (nActPoint==0 || rStat.Now()!=rXPoly[nActPoint-1]) {
+ if (bIncomp) {
+ if (pU->nBezierStartPoint>nActPoint) pU->nBezierStartPoint=nActPoint;
+ if (IsBezier(pU->eAktKind) && nActPoint-pU->nBezierStartPoint>=3 && ((nActPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nActPoint-3);
+ rXPoly.SetFlags(nActPoint-1,XPOLY_CONTROL);
+ rXPoly.SetFlags(nActPoint-2,XPOLY_CONTROL);
+
+ if (nActPoint>=6 && rXPoly.IsControl(nActPoint-4)) {
+ rXPoly.CalcTangent(nActPoint-3,nActPoint-4,nActPoint-2);
+ rXPoly.SetFlags(nActPoint-3,XPOLY_SMOOTH);
+ }
+ }
+ } else {
+ if (nActPoint==1 && IsBezier(pU->eAktKind) && !pU->bBezHasCtrl0) {
+ pU->aBezControl0=rStat.GetNow();;
+ pU->bBezHasCtrl0=TRUE;
+ nActPoint--;
+ }
+ if (pU->IsFormFlag()) {
+ USHORT nPtAnz0=rXPoly.GetPointCount();
+ rXPoly.Remove(nActPoint-1,2); // die letzten beiden Punkte entfernen und durch die Form ersetzen
+ rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
+ USHORT nPtAnz1=rXPoly.GetPointCount();
+ for (USHORT i=nPtAnz0+1; i<nPtAnz1-1; i++) { // Damit BckAction richtig funktioniert
+ if (!rXPoly.IsControl(i)) rStat.NextPoint();
+ }
+ nActPoint=rXPoly.GetPointCount()-1;
+ }
+ }
+ nActPoint++;
+ rXPoly[nActPoint]=rStat.GetNow();
+ }
+ if (eCmd==SDRCREATE_NEXTOBJECT) {
+ if (rXPoly.GetPointCount()>=2) {
+ pU->bBezHasCtrl0=FALSE;
+ // nur einzelnes Polygon kann offen sein, deshalb schliessen
+ rXPoly[nActPoint]=rXPoly[0];
+ XPolygon aXP;
+ aXP[0]=rStat.GetNow();
+ aPathPolygon.Insert(aXP);
+ }
+ }
+ }
+
+ USHORT nPolyAnz=aPathPolygon.Count();
+ if (nPolyAnz!=0) {
+ // den letzten Punkt ggf. wieder loeschen
+ if (eCmd==SDRCREATE_FORCEEND) {
+ XPolygon& rXP=aPathPolygon[nPolyAnz-1];
+ USHORT nPtAnz=rXP.GetPointCount();
+ if (nPtAnz>=2) {
+ if (!rXP.IsControl(nPtAnz-2)) {
+ if (rXP[nPtAnz-1]==rXP[nPtAnz-2]) {
+ rXP.Remove(nPtAnz-1,1);
+ }
+ } else {
+ if (rXP[nPtAnz-3]==rXP[nPtAnz-2]) {
+ rXP.Remove(nPtAnz-3,3);
+ }
+ }
+ }
+ }
+ for (USHORT nPolyNum=nPolyAnz; nPolyNum>0;) {
+ nPolyNum--;
+ XPolygon& rXP=aPathPolygon[nPolyNum];
+ USHORT nPtAnz=rXP.GetPointCount();
+ // Polygone mit zu wenig Punkten werden geloescht
+ if (nPolyNum<nPolyAnz-1 || eCmd==SDRCREATE_FORCEEND) {
+ if (nPtAnz<2) aPathPolygon.Remove(nPolyNum);
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ bRet=eCmd==SDRCREATE_FORCEEND;
+ if (bRet) {
+ mbCreating=FALSE;
+ delete pU;
+ rStat.SetUser(NULL);
+ }
+ return bRet;
+}
+
+FASTBOOL ImpPathForDragAndCreate::BckCreate(SdrDragStat& rStat)
+{
+ ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ USHORT nActPoint=rXPoly.GetPointCount();
+ if (nActPoint>0) {
+ nActPoint--;
+ // Das letzte Stueck einer Bezierkurve wird erstmal zu 'ner Linie
+ rXPoly.Remove(nActPoint,1);
+ if (nActPoint>=3 && rXPoly.IsControl(nActPoint-1)) {
+ // Beziersegment am Ende sollte zwar nicht vorkommen, aber falls doch ...
+ rXPoly.Remove(nActPoint-1,1);
+ if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
+ }
+ }
+ nActPoint=rXPoly.GetPointCount();
+ if (nActPoint>=4) { // Kein Beziersegment am Ende
+ nActPoint--;
+ if (rXPoly.IsControl(nActPoint-1)) {
+ rXPoly.Remove(nActPoint-1,1);
+ if (rXPoly.IsControl(nActPoint-2)) rXPoly.Remove(nActPoint-2,1);
+ }
+ }
+ if (rXPoly.GetPointCount()<2) {
+ aPathPolygon.Remove(aPathPolygon.Count()-1);
+ }
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ USHORT nLocalActPoint=rLocalXPoly.GetPointCount();
+ if (nLocalActPoint>0) {
+ nLocalActPoint--;
+ rLocalXPoly[nLocalActPoint]=rStat.Now();
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ return aPathPolygon.Count()!=0;
+}
+
+void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
+{
+ ImpPathCreateUser* pU=(ImpPathCreateUser*)rStat.GetUser();
+ aPathPolygon.Clear();
+ mbCreating=FALSE;
+ delete pU;
+ rStat.SetUser(NULL);
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
+ basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1L) : basegfx::B2DPolygon());
+
+ if(pU->IsFormFlag() && aNewPolygon.count() > 1L)
+ {
+ // remove last segment and replace with current
+ // do not forget to rescue the previous control point which will be lost when
+ // the point it's associated with is removed
+ const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
+ const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
+
+ aNewPolygon.remove(nChangeIndex, 2L);
+ aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
+
+ if(nChangeIndex < aNewPolygon.count())
+ {
+ // if really something was added, set the saved prev control point at the
+ // point where it belongs
+ aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
+ }
+ }
+
+ if(aRetval.count())
+ {
+ aRetval.setB2DPolygon(aRetval.count() - 1L, aNewPolygon);
+ }
+ else
+ {
+ aRetval.append(aNewPolygon);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ ImpPathCreateUser* pU = (ImpPathCreateUser*)rDrag.GetUser();
+
+ if(pU && pU->bBezier && rDrag.IsMouseDown())
+ {
+ // no more XOR, no need for complicated helplines
+ basegfx::B2DPolygon aHelpline;
+ aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
+ aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
+ aRetval.append(aHelpline);
+ }
+
+ return aRetval;
+}
+
+Pointer ImpPathForDragAndCreate::GetCreatePointer() const
+{
+ switch (meObjectKind) {
+ case OBJ_LINE : return Pointer(POINTER_DRAW_LINE);
+ case OBJ_POLY : return Pointer(POINTER_DRAW_POLYGON);
+ case OBJ_PLIN : return Pointer(POINTER_DRAW_POLYGON);
+ case OBJ_PATHLINE: return Pointer(POINTER_DRAW_BEZIER);
+ case OBJ_PATHFILL: return Pointer(POINTER_DRAW_BEZIER);
+ case OBJ_FREELINE: return Pointer(POINTER_DRAW_FREEHAND);
+ case OBJ_FREEFILL: return Pointer(POINTER_DRAW_FREEHAND);
+ case OBJ_SPLNLINE: return Pointer(POINTER_DRAW_FREEHAND);
+ case OBJ_SPLNFILL: return Pointer(POINTER_DRAW_FREEHAND);
+ case OBJ_PATHPOLY: return Pointer(POINTER_DRAW_POLYGON);
+ case OBJ_PATHPLIN: return Pointer(POINTER_DRAW_POLYGON);
+ default: break;
+ } // switch
+ return Pointer(POINTER_CROSS);
+}
+
+/*************************************************************************/
+
+SdrPathObjGeoData::SdrPathObjGeoData()
+{
+}
+
+SdrPathObjGeoData::~SdrPathObjGeoData()
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// DrawContact section
+
+sdr::contact::ViewContact* SdrPathObj::CreateObjectSpecificViewContact()
+{
+ return new sdr::contact::ViewContactOfSdrPathObj(*this);
+}
+
+/*************************************************************************/
+
+TYPEINIT1(SdrPathObj,SdrTextObj);
+
+SdrPathObj::SdrPathObj(SdrObjKind eNewKind)
+: meKind(eNewKind),
+ mpDAC(0L)
+{
+ bClosedObj = IsClosed();
+}
+
+SdrPathObj::SdrPathObj(SdrObjKind eNewKind, const basegfx::B2DPolyPolygon& rPathPoly)
+: maPathPolygon(rPathPoly),
+ meKind(eNewKind),
+ mpDAC(0L)
+{
+ bClosedObj = IsClosed();
+ ImpForceKind();
+}
+
+SdrPathObj::~SdrPathObj()
+{
+ impDeleteDAC();
+}
+
+sal_Bool ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ return (1L == rPolyPolygon.count() && 2L == rPolyPolygon.getB2DPolygon(0L).count());
+}
+
+Rectangle ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ basegfx::B2DRange aRange(basegfx::tools::getRange(rPolyPolygon));
+
+ return Rectangle(
+ FRound(aRange.getMinX()), FRound(aRange.getMinY()),
+ FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
+}
+
+void SdrPathObj::ImpForceLineWink()
+{
+ if(OBJ_LINE == meKind && ImpIsLine(GetPathPoly()))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
+ const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
+ const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
+ const Point aDelt(aPoint1 - aPoint0);
+
+ aGeo.nDrehWink=GetAngle(aDelt);
+ aGeo.nShearWink=0;
+ aGeo.RecalcSinCos();
+ aGeo.RecalcTan();
+
+ // #101412# for SdrTextObj, keep aRect up to date
+ aRect = Rectangle(aPoint0, aPoint1);
+ aRect.Justify();
+ }
+}
+
+void SdrPathObj::ImpForceKind()
+{
+ if (meKind==OBJ_PATHPLIN) meKind=OBJ_PLIN;
+ if (meKind==OBJ_PATHPOLY) meKind=OBJ_POLY;
+
+ if(GetPathPoly().areControlPointsUsed())
+ {
+ switch (meKind)
+ {
+ case OBJ_LINE: meKind=OBJ_PATHLINE; break;
+ case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
+ case OBJ_POLY: meKind=OBJ_PATHFILL; break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
+ case OBJ_FREELINE: meKind=OBJ_PLIN; break;
+ case OBJ_PATHFILL: meKind=OBJ_POLY; break;
+ case OBJ_FREEFILL: meKind=OBJ_POLY; break;
+ default: break;
+ }
+ }
+
+ if (meKind==OBJ_LINE && !ImpIsLine(GetPathPoly())) meKind=OBJ_PLIN;
+ if (meKind==OBJ_PLIN && ImpIsLine(GetPathPoly())) meKind=OBJ_LINE;
+
+ bClosedObj=IsClosed();
+
+ if (meKind==OBJ_LINE)
+ {
+ ImpForceLineWink();
+ }
+ else
+ {
+ // #i10659#, similar to #101412# but for polys with more than 2 points.
+ //
+ // Here i again need to fix something, because when Path-Polys are Copy-Pasted
+ // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
+ // a scaling loop started from SdrExchangeView::Paste. This is principally nothing
+ // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
+ // this is the case, some size needs to be set here in aRect to avoid that the cyclus
+ // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
+ // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
+ // from the local Resize() implementation.
+ //
+ // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
+ // text rectangle for the text object itself and methods at SdrTextObj do handle it
+ // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
+ // which is basically wrong. To make the SdrText methods which deal with aRect directly
+ // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
+ // command for SdrPathObj. Since adding this update mechanism with #101412# to
+ // ImpForceLineWink() for lines was very successful, i add it to where ImpForceLineWink()
+ // was called, once here below and once on a 2nd place below.
+
+ // #i10659# for SdrTextObj, keep aRect up to date
+ if(GetPathPoly().count())
+ {
+ aRect = ImpGetBoundRect(GetPathPoly());
+ }
+ }
+
+ // #i75974# adapt polygon state to object type. This may include a reinterpretation
+ // of a closed geometry as open one, but with identical first and last point
+ for(sal_uInt32 a(0); a < maPathPolygon.count(); a++)
+ {
+ basegfx::B2DPolygon aCandidate(maPathPolygon.getB2DPolygon(a));
+
+ if((bool)IsClosed() != aCandidate.isClosed())
+ {
+ // #i80213# really change polygon geometry; else e.g. the last point which
+ // needs to be identical with the first one will be missing when opening
+ // due to OBJ_PATH type
+ if(aCandidate.isClosed())
+ {
+ basegfx::tools::openWithGeometryChange(aCandidate);
+ }
+ else
+ {
+ basegfx::tools::closeWithGeometryChange(aCandidate);
+ }
+
+ maPathPolygon.setB2DPolygon(a, aCandidate);
+ }
+ }
+}
+
+void SdrPathObj::ImpSetClosed(sal_Bool bClose)
+{
+ if(bClose)
+ {
+ switch (meKind)
+ {
+ case OBJ_LINE : meKind=OBJ_POLY; break;
+ case OBJ_PLIN : meKind=OBJ_POLY; break;
+ case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
+ case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
+ case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
+ default: break;
+ }
+
+ bClosedObj = TRUE;
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case OBJ_POLY : meKind=OBJ_PLIN; break;
+ case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
+ case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
+ case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
+ default: break;
+ }
+
+ bClosedObj = FALSE;
+ }
+
+ ImpForceKind();
+}
+
+void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bNoContortion=FALSE;
+
+ FASTBOOL bCanConv = !HasText() || ImpCanConvTextToCurve();
+ FASTBOOL bIsPath = IsBezier() || IsSpline();
+
+ rInfo.bEdgeRadiusAllowed = FALSE;
+ rInfo.bCanConvToPath = bCanConv && !bIsPath;
+ rInfo.bCanConvToPoly = bCanConv && bIsPath;
+ rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+UINT16 SdrPathObj::GetObjIdentifier() const
+{
+ return USHORT(meKind);
+}
+
+void SdrPathObj::operator=(const SdrObject& rObj)
+{
+ SdrTextObj::operator=(rObj);
+ SdrPathObj& rPath=(SdrPathObj&)rObj;
+ maPathPolygon=rPath.GetPathPoly();
+}
+
+void SdrPathObj::TakeObjNameSingul(XubString& rName) const
+{
+ if(OBJ_LINE == meKind)
+ {
+ sal_uInt16 nId(STR_ObjNameSingulLINE);
+
+ if(ImpIsLine(GetPathPoly()))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0L));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0L));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1L));
+ const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
+ const Point aPoint1(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
+
+ if(aB2DPoint0 != aB2DPoint1)
+ {
+ if(aB2DPoint0.getY() == aB2DPoint1.getY())
+ {
+ nId = STR_ObjNameSingulLINE_Hori;
+ }
+ else if(aB2DPoint0.getX() == aB2DPoint1.getX())
+ {
+ nId = STR_ObjNameSingulLINE_Vert;
+ }
+ else
+ {
+ const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
+ const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
+
+ if(fDx == fDy)
+ {
+ nId = STR_ObjNameSingulLINE_Diag;
+ }
+ }
+ }
+ }
+
+ rName = ImpGetResStr(nId);
+ }
+ else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
+ {
+ const sal_Bool bClosed(OBJ_POLY == meKind);
+ sal_uInt16 nId(0);
+
+ if(mpDAC && mpDAC->IsCreating())
+ {
+ if(bClosed)
+ {
+ nId = STR_ObjNameSingulPOLY;
+ }
+ else
+ {
+ nId = STR_ObjNameSingulPLIN;
+ }
+
+ rName = ImpGetResStr(nId);
+ }
+ else
+ {
+ // get point count
+ sal_uInt32 nPointCount(0L);
+ const sal_uInt32 nPolyCount(GetPathPoly().count());
+
+ for(sal_uInt32 a(0L); a < nPolyCount; a++)
+ {
+ nPointCount += GetPathPoly().getB2DPolygon(a).count();
+ }
+
+ if(bClosed)
+ {
+ nId = STR_ObjNameSingulPOLY_PntAnz;
+ }
+ else
+ {
+ nId = STR_ObjNameSingulPLIN_PntAnz;
+ }
+
+ rName = ImpGetResStr(nId);
+ sal_uInt16 nPos(rName.SearchAscii("%2")); // #i96537#
+
+ if(STRING_NOTFOUND != nPos)
+ {
+ rName.Erase(nPos, 2);
+ rName.Insert(UniString::CreateFromInt32(nPointCount), nPos);
+ }
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNameSingulPATHLINE); break;
+ case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNameSingulFREELINE); break;
+ case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNameSingulNATSPLN); break;
+ case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNameSingulPATHFILL); break;
+ case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNameSingulFREEFILL); break;
+ case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNameSingulPERSPLN); break;
+ default: break;
+ }
+ }
+
+ String aName(GetName());
+ if(aName.Len())
+ {
+ rName += sal_Unicode(' ');
+ rName += sal_Unicode('\'');
+ rName += aName;
+ rName += sal_Unicode('\'');
+ }
+}
+
+void SdrPathObj::TakeObjNamePlural(XubString& rName) const
+{
+ switch(meKind)
+ {
+ case OBJ_LINE : rName=ImpGetResStr(STR_ObjNamePluralLINE ); break;
+ case OBJ_PLIN : rName=ImpGetResStr(STR_ObjNamePluralPLIN ); break;
+ case OBJ_POLY : rName=ImpGetResStr(STR_ObjNamePluralPOLY ); break;
+ case OBJ_PATHLINE: rName=ImpGetResStr(STR_ObjNamePluralPATHLINE); break;
+ case OBJ_FREELINE: rName=ImpGetResStr(STR_ObjNamePluralFREELINE); break;
+ case OBJ_SPLNLINE: rName=ImpGetResStr(STR_ObjNamePluralNATSPLN); break;
+ case OBJ_PATHFILL: rName=ImpGetResStr(STR_ObjNamePluralPATHFILL); break;
+ case OBJ_FREEFILL: rName=ImpGetResStr(STR_ObjNamePluralFREEFILL); break;
+ case OBJ_SPLNFILL: rName=ImpGetResStr(STR_ObjNamePluralPERSPLN); break;
+ default: break;
+ }
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
+{
+ return GetPathPoly();
+}
+
+sal_uInt32 SdrPathObj::GetHdlCount() const
+{
+ sal_uInt32 nRetval(0L);
+ const sal_uInt32 nPolyCount(GetPathPoly().count());
+
+ for(sal_uInt32 a(0L); a < nPolyCount; a++)
+ {
+ nRetval += GetPathPoly().getB2DPolygon(a).count();
+ }
+
+ return nRetval;
+}
+
+SdrHdl* SdrPathObj::GetHdl(sal_uInt32 nHdlNum) const
+{
+ // #i73248#
+ // Warn the user that this is ineffective and show alternatives. Should not be used at all.
+ OSL_ENSURE(false, "SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
+
+ // to have an alternative, get single handle using the ineffective way
+ SdrHdl* pRetval = 0;
+ SdrHdlList aLocalList(0);
+ AddToHdlList(aLocalList);
+ const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());
+
+ if(nHdlCount && nHdlNum < nHdlCount)
+ {
+ // remove and remember. The other created handles will be deleted again with the
+ // destruction of the local list
+ pRetval = aLocalList.RemoveHdl(nHdlNum);
+ }
+
+ return pRetval;
+}
+
+void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ USHORT nPolyCnt=aOldPathPolygon.Count();
+ FASTBOOL bClosed=IsClosed();
+ USHORT nIdx=0;
+
+ for (USHORT i=0; i<nPolyCnt; i++) {
+ const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
+ USHORT nPntCnt=rXPoly.GetPointCount();
+ if (bClosed && nPntCnt>1) nPntCnt--;
+
+ for (USHORT j=0; j<nPntCnt; j++) {
+ if (rXPoly.GetFlags(j)!=XPOLY_CONTROL) {
+ const Point& rPnt=rXPoly[j];
+ SdrHdl* pHdl=new SdrHdl(rPnt,HDL_POLY);
+ pHdl->SetPolyNum(i);
+ pHdl->SetPointNum(j);
+ pHdl->Set1PixMore(j==0);
+ pHdl->SetSourceHdlNum(nIdx);
+ nIdx++;
+ rHdlList.AddHdl(pHdl);
+ }
+ }
+ }
+}
+
+sal_uInt32 SdrPathObj::GetPlusHdlCount(const SdrHdl& rHdl) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ sal_uInt16 nCnt = 0;
+ sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
+ sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
+
+ if(nPolyNum < aOldPathPolygon.Count())
+ {
+ const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
+ sal_uInt16 nPntMax = rXPoly.GetPointCount();
+ if (nPntMax>0)
+ {
+ nPntMax--;
+ if (nPnt<=nPntMax)
+ {
+ if (rXPoly.GetFlags(nPnt)!=XPOLY_CONTROL)
+ {
+ if (nPnt==0 && IsClosed()) nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL) nCnt++;
+ if (nPnt==nPntMax && IsClosed()) nPnt=0;
+ if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL) nCnt++;
+ }
+ }
+ }
+ }
+
+ return nCnt;
+}
+
+SdrHdl* SdrPathObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlusNum) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ SdrHdl* pHdl = 0L;
+ sal_uInt16 nPnt = (sal_uInt16)rHdl.GetPointNum();
+ sal_uInt16 nPolyNum = (sal_uInt16)rHdl.GetPolyNum();
+
+ if (nPolyNum<aOldPathPolygon.Count())
+ {
+ const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
+ sal_uInt16 nPntMax = rXPoly.GetPointCount();
+
+ if (nPntMax>0)
+ {
+ nPntMax--;
+ if (nPnt<=nPntMax)
+ {
+ pHdl=new SdrHdlBezWgt(&rHdl);
+ pHdl->SetPolyNum(rHdl.GetPolyNum());
+
+ if (nPnt==0 && IsClosed()) nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==XPOLY_CONTROL && nPlusNum==0)
+ {
+ pHdl->SetPos(rXPoly[nPnt-1]);
+ pHdl->SetPointNum(nPnt-1);
+ }
+ else
+ {
+ if (nPnt==nPntMax && IsClosed()) nPnt=0;
+ if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==XPOLY_CONTROL)
+ {
+ pHdl->SetPos(rXPoly[nPnt+1]);
+ pHdl->SetPointNum(nPnt+1);
+ }
+ }
+
+ pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
+ pHdl->SetPlusHdl(TRUE);
+ }
+ }
+ }
+ return pHdl;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SdrPathObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
+
+ return aDragAndCreate.beginPathDrag(rDrag);
+}
+
+bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ ImpPathForDragAndCreate aDragAndCreate(*this);
+ bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.movePathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.endPathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
+ }
+
+ return bRetval;
+}
+
+String SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ String aRetval;
+
+ if(mpDAC)
+ {
+ // #i103058# also get a comment when in creation
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ aRetval = mpDAC->getSpecialDragComment(rDrag);
+ }
+ }
+ else
+ {
+ ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
+ bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ ImpPathForDragAndCreate aDragAndCreate(*((SdrPathObj*)this));
+ bool bDidWork(aDragAndCreate.beginPathDrag((SdrDragStat&)rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
+ }
+
+ return aRetval;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+FASTBOOL SdrPathObj::BegCreate(SdrDragStat& rStat)
+{
+ impDeleteDAC();
+ return impGetDAC().BegCreate(rStat);
+}
+
+FASTBOOL SdrPathObj::MovCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().MovCreate(rStat);
+}
+
+FASTBOOL SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ FASTBOOL bRetval(impGetDAC().EndCreate(rStat, eCmd));
+
+ if(bRetval && mpDAC)
+ {
+ SetPathPoly(mpDAC->getModifiedPolyPolygon());
+
+ // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
+ // to be able to use the type-changing ImpSetClosed method
+ if(!IsClosedObj())
+ {
+ SdrView* pView = rStat.GetView();
+
+ if(pView && pView->IsAutoClosePolys() && !pView->IsUseIncompatiblePathCreateInterface())
+ {
+ OutputDevice* pOut = pView->GetFirstOutputDevice();
+
+ if(pOut)
+ {
+ if(GetPathPoly().count())
+ {
+ const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
+
+ if(aCandidate.count() > 2)
+ {
+ // check distance of first and last point
+ const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
+ const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
+
+ if(aDistVector.getLength() <= (double)nCloseDist)
+ {
+ // close it
+ ImpSetClosed(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ impDeleteDAC();
+ }
+
+ return bRetval;
+}
+
+FASTBOOL SdrPathObj::BckCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().BckCreate(rStat);
+}
+
+void SdrPathObj::BrkCreate(SdrDragStat& rStat)
+{
+ impGetDAC().BrkCreate(rStat);
+ impDeleteDAC();
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ aRetval.append(mpDAC->TakeDragPolyPolygon(rDrag));
+ }
+
+ return aRetval;
+}
+
+// during drag or create, allow accessing the so-far created/modified polyPolygon
+basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeDragPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+Pointer SdrPathObj::GetCreatePointer() const
+{
+ return impGetDAC().GetCreatePointer();
+}
+
+void SdrPathObj::NbcMove(const Size& rSiz)
+{
+ maPathPolygon.transform(basegfx::tools::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMove(rSiz);
+}
+
+void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
+ aTrans = basegfx::tools::createScaleTranslateB2DHomMatrix(
+ double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+}
+
+void SdrPathObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
+{
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ const basegfx::B2DHomMatrix aTrans(basegfx::tools::createRotateAroundPoint(rRef.X(), rRef.Y(), -nWink * nPi180));
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
+}
+
+void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, FASTBOOL bVShear)
+{
+ basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
+
+ if(bVShear)
+ {
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ aTrans.shearY(-fTan);
+ }
+ else
+ {
+ aTrans.shearX(-fTan);
+ }
+
+ aTrans.translate(rRefPnt.X(), rRefPnt.Y());
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
+}
+
+void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
+{
+ const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
+ const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
+ const double fRot(atan2(fDiffY, fDiffX));
+ basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
+ aTrans.rotate(-fRot);
+ aTrans.scale(1.0, -1.0);
+ aTrans.rotate(fRot);
+ aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
+ maPathPolygon.transform(aTrans);
+
+ // #97538# Do Joe's special handling for lines when mirroring, too
+ ImpForceKind();
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
+}
+
+void SdrPathObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
+{
+ if(!aGeo.nDrehWink)
+ {
+ rRect = GetSnapRect();
+ }
+ else
+ {
+ XPolyPolygon aXPP(GetPathPoly());
+ RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
+ rRect=aXPP.GetBoundRect();
+ Point aTmp(rRect.TopLeft());
+ RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
+ aTmp-=rRect.TopLeft();
+ rRect.Move(aTmp.X(),aTmp.Y());
+ }
+}
+
+void SdrPathObj::RecalcSnapRect()
+{
+ if(GetPathPoly().count())
+ {
+ maSnapRect = ImpGetBoundRect(GetPathPoly());
+ }
+}
+
+void SdrPathObj::NbcSetSnapRect(const Rectangle& rRect)
+{
+ Rectangle aOld(GetSnapRect());
+
+ // #95736# Take RECT_EMPTY into account when calculating scale factors
+ long nMulX = (RECT_EMPTY == rRect.Right()) ? 0 : rRect.Right() - rRect.Left();
+
+ long nDivX = aOld.Right() - aOld.Left();
+
+ // #95736# Take RECT_EMPTY into account when calculating scale factors
+ long nMulY = (RECT_EMPTY == rRect.Bottom()) ? 0 : rRect.Bottom() - rRect.Top();
+
+ long nDivY = aOld.Bottom() - aOld.Top();
+ if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+}
+
+sal_uInt32 SdrPathObj::GetSnapPointCount() const
+{
+ return GetHdlCount();
+}
+
+Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
+{
+ sal_uInt32 nPoly,nPnt;
+ if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
+ {
+ DBG_ASSERT(FALSE,"SdrPathObj::GetSnapPoint: Punkt nSnapPnt nicht vorhanden!");
+ }
+
+ const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
+ return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
+}
+
+sal_Bool SdrPathObj::IsPolyObj() const
+{
+ return sal_True;
+}
+
+sal_uInt32 SdrPathObj::GetPointCount() const
+{
+ const sal_uInt32 nPolyCount(GetPathPoly().count());
+ sal_uInt32 nRetval(0L);
+
+ for(sal_uInt32 a(0L); a < nPolyCount; a++)
+ {
+ nRetval += GetPathPoly().getB2DPolygon(a).count();
+ }
+
+ return nRetval;
+}
+
+Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
+{
+ Point aRetval;
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
+ const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
+ aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
+ }
+
+ return aRetval;
+}
+
+void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
+{
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ {
+ basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
+ aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
+ maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
+
+ if(meKind==OBJ_LINE)
+ {
+ ImpForceLineWink();
+ }
+ else
+ {
+ if(GetPathPoly().count())
+ {
+ // #i10659# for SdrTextObj, keep aRect up to date
+ aRect = ImpGetBoundRect(GetPathPoly()); // fuer SdrTextObj#
+ }
+ }
+
+ SetRectsDirty();
+ }
+}
+
+sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, sal_Bool bNewObj, sal_Bool bHideHim)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ nNewHdl = NbcInsPoint(0L, rPos, sal_True, bHideHim);
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0L);
+ sal_uInt32 nSmallestEdgeIndex(0L);
+ double fSmallestCut;
+ basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+
+ // create old polygon index from it
+ sal_uInt32 nPolyIndex(nSmallestEdgeIndex);
+
+ for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
+ {
+ nPolyIndex += GetPathPoly().getB2DPolygon(a).count();
+ }
+
+ nNewHdl = NbcInsPoint(nPolyIndex, rPos, sal_False, bHideHim);
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+sal_uInt32 SdrPathObj::NbcInsPoint(sal_uInt32 /*nHdlNum*/, const Point& rPos, sal_Bool bNewObj, sal_Bool /*bHideHim*/)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ basegfx::B2DPolygon aNewPoly;
+ const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
+ aNewPoly.append(aPoint);
+ aNewPoly.setClosed(IsClosed());
+ maPathPolygon.append(aNewPoly);
+ SetRectsDirty();
+ nNewHdl = GetHdlCount();
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0L);
+ sal_uInt32 nSmallestEdgeIndex(0L);
+ double fSmallestCut;
+ basegfx::tools::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+ basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
+ const bool bBefore(!aCandidate.isClosed() && 0L == nSmallestEdgeIndex && 0.0 == fSmallestCut);
+ const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2L && 1.0 == fSmallestCut);
+
+ if(bBefore)
+ {
+ // before first point
+ aCandidate.insert(0L, aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(1))
+ {
+ aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = 0L;
+ }
+ else if(bAfter)
+ {
+ // after last point
+ aCandidate.append(aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
+ {
+ aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = aCandidate.count() - 1L;
+ }
+ else
+ {
+ // in between
+ bool bSegmentSplit(false);
+ const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
+ {
+ bSegmentSplit = true;
+ }
+ }
+
+ if(bSegmentSplit)
+ {
+ // rebuild original segment to get the split data
+ basegfx::B2DCubicBezier aBezierA, aBezierB;
+ const basegfx::B2DCubicBezier aBezier(
+ aCandidate.getB2DPoint(nSmallestEdgeIndex),
+ aCandidate.getNextControlPoint(nSmallestEdgeIndex),
+ aCandidate.getPrevControlPoint(nNextIndex),
+ aCandidate.getB2DPoint(nNextIndex));
+
+ // split and insert hit point
+ aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
+ aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
+
+ // since we inserted hit point and not split point, we need to add an offset
+ // to the control points to get the C1 continuity we want to achieve
+ const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
+ }
+ else
+ {
+ aCandidate.insert(nSmallestEdgeIndex + 1L, aTestPoint);
+ }
+
+ nNewHdl = nSmallestEdgeIndex + 1L;
+ }
+
+ maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
+
+ // create old polygon index from it
+ for(sal_uInt32 a(0L); a < nSmallestPolyIndex; a++)
+ {
+ nNewHdl += GetPathPoly().getB2DPolygon(a).count();
+ }
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
+{
+ SdrPathObj* pNewObj = 0L;
+ const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
+ sal_uInt32 nPoly, nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
+ {
+ if(0L == nPoly)
+ {
+ const basegfx::B2DPolygon aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ if(IsClosed())
+ {
+ // when closed, RipPoint means to open the polygon at the selected point. To
+ // be able to do that, it is necessary to make the selected point the first one
+ basegfx::B2DPolygon aNewPolygon(basegfx::tools::makeStartPoint(aCandidate, nPnt));
+ SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
+ ToggleClosed();
+
+ // give back new position of old start point (historical reasons)
+ rNewPt0Index = (nPointCount - nPnt) % nPointCount;
+ }
+ else
+ {
+ if(nPointCount >= 3L && nPnt != 0L && nPnt + 1L < nPointCount)
+ {
+ // split in two objects at point nPnt
+ basegfx::B2DPolygon aSplitPolyA(aCandidate, 0L, nPnt + 1L);
+ SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
+
+ pNewObj = (SdrPathObj*)Clone();
+ basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
+ pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
+ }
+ }
+ }
+ }
+ }
+
+ return pNewObj;
+}
+
+SdrObject* SdrPathObj::DoConvertToPolyObj(BOOL bBezier) const
+{
+ // #i89784# check for FontWork with activated HideContour
+ const drawinglayer::attribute::SdrTextAttribute aText(
+ drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
+ const bool bHideContour(
+ !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
+
+ SdrObject* pRet = bHideContour ?
+ 0 :
+ ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
+
+ SdrPathObj* pPath = PTR_CAST(SdrPathObj, pRet);
+
+ if(pPath)
+ {
+ if(pPath->GetPathPoly().areControlPointsUsed())
+ {
+ if(!bBezier)
+ {
+ // reduce all bezier curves
+ pPath->SetPathPoly(basegfx::tools::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
+ }
+ }
+ else
+ {
+ if(bBezier)
+ {
+ // create bezier curves
+ pPath->SetPathPoly(basegfx::tools::expandToCurve(pPath->GetPathPoly()));
+ }
+ }
+ }
+
+ pRet = ImpConvertAddText(pRet, bBezier);
+
+ return pRet;
+}
+
+SdrObjGeoData* SdrPathObj::NewGeoData() const
+{
+ return new SdrPathObjGeoData;
+}
+
+void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrPathObjGeoData& rPGeo = (SdrPathObjGeoData&) rGeo;
+ rPGeo.maPathPolygon=GetPathPoly();
+ rPGeo.meKind=meKind;
+}
+
+void SdrPathObj::RestGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestGeoData(rGeo);
+ SdrPathObjGeoData& rPGeo=(SdrPathObjGeoData&)rGeo;
+ maPathPolygon=rPGeo.maPathPolygon;
+ meKind=rPGeo.meKind;
+ ImpForceKind(); // damit u.a. bClosed gesetzt wird
+}
+
+void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ maPathPolygon=rPathPoly;
+ ImpForceKind();
+ SetRectsDirty();
+ }
+}
+
+void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect();
+ NbcSetPathPoly(rPathPoly);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
+ }
+}
+
+void SdrPathObj::ToggleClosed() // long nOpenDistance)
+{
+ Rectangle aBoundRect0;
+ if(pUserCall != NULL)
+ aBoundRect0 = GetLastBoundRect();
+ ImpSetClosed(!IsClosed()); // neuen ObjKind setzen
+ ImpForceKind(); // wg. Line->Poly->PolyLine statt Line->Poly->Line
+ SetRectsDirty();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SDRUSERCALL_RESIZE, aBoundRect0);
+}
+
+// fuer friend class SdrPolyEditView auf einigen Compilern:
+void SdrPathObj::SetRectsDirty(sal_Bool bNotMyself)
+{
+ SdrTextObj::SetRectsDirty(bNotMyself);
+}
+
+ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
+{
+ if(!mpDAC)
+ {
+ ((SdrPathObj*)this)->mpDAC = new ImpPathForDragAndCreate(*((SdrPathObj*)this));
+ }
+
+ return *mpDAC;
+}
+
+void SdrPathObj::impDeleteDAC() const
+{
+ if(mpDAC)
+ {
+ delete mpDAC;
+ ((SdrPathObj*)this)->mpDAC = 0L;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// transformation interface for StarOfficeAPI. This implements support for
+// homogen 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+//
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+sal_Bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
+{
+ double fRotate(0.0);
+ double fShearX(0.0);
+ basegfx::B2DTuple aScale(1.0, 1.0);
+ basegfx::B2DTuple aTranslate(0.0, 0.0);
+
+ if(GetPathPoly().count())
+ {
+ // copy geometry
+ basegfx::B2DHomMatrix aMoveToZeroMatrix;
+ rPolyPolygon = GetPathPoly();
+
+ if(OBJ_LINE == meKind)
+ {
+ // ignore shear and rotate, just use scale and translate
+ OSL_ENSURE(GetPathPoly().count() > 0L && GetPathPoly().getB2DPolygon(0L).count() > 1L, "OBJ_LINE with too less polygons (!)");
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ else
+ {
+ if(aGeo.nShearWink || aGeo.nDrehWink)
+ {
+ // get rotate and shear in drawingLayer notation
+ fRotate = aGeo.nDrehWink * F_PI18000;
+ fShearX = aGeo.nShearWink * F_PI18000;
+
+ // build mathematically correct (negative shear and rotate) object transform
+ // containing shear and rotate to extract unsheared, unrotated polygon
+ basegfx::B2DHomMatrix aObjectMatrix;
+ aObjectMatrix.shearX(tan((36000 - aGeo.nShearWink) * F_PI18000));
+ aObjectMatrix.rotate((36000 - aGeo.nDrehWink) * F_PI18000);
+
+ // create inverse from it and back-transform polygon
+ basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
+ aInvObjectMatrix.invert();
+ rPolyPolygon.transform(aInvObjectMatrix);
+
+ // get range from unsheared, unrotated polygon and extract scale and translate.
+ // transform topLeft from it back to transformed state to get original
+ // topLeft (rotation center)
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
+ aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
+ aScale = aCorrectedRangeNoCurve.getRange();
+
+ // define matrix for move polygon to zero point
+ // #i112280# Added missing minus for Y-Translation
+ aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
+ }
+ else
+ {
+ // get scale and translate from unsheared, unrotated polygon
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::tools::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ }
+
+ // move polygon to zero point with pre-defined matrix
+ rPolyPolygon.transform(aMoveToZeroMatrix);
+ }
+
+ // position maybe relative to anchorpos, convert
+ if( pModel && pModel->IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // force MapUnit to 100th mm
+ SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
+ if(eMapUnit != SFX_MAPUNIT_100TH_MM)
+ {
+ switch(eMapUnit)
+ {
+ case SFX_MAPUNIT_TWIP :
+ {
+ // postion
+ aTranslate.setX(ImplTwipsToMM(aTranslate.getX()));
+ aTranslate.setY(ImplTwipsToMM(aTranslate.getY()));
+
+ // size
+ aScale.setX(ImplTwipsToMM(aScale.getX()));
+ aScale.setY(ImplTwipsToMM(aScale.getY()));
+
+ // polygon
+ basegfx::B2DHomMatrix aTwipsToMM;
+ const double fFactorTwipsToMM(127.0 / 72.0);
+ aTwipsToMM.scale(fFactorTwipsToMM, fFactorTwipsToMM);
+ rPolyPolygon.transform(aTwipsToMM);
+
+ break;
+ }
+ default:
+ {
+ DBG_ERROR("TRGetBaseGeometry: Missing unit translation to 100th mm!");
+ }
+ }
+ }
+
+ // build return value matrix
+ rMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return sal_True;
+}
+
+// sets the base geometry of the object using infos contained in the homogen 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale.setX(fabs(aScale.getX()));
+ aScale.setY(fabs(aScale.getY()));
+ fRotate = fmod(fRotate + F_PI, F_2PI);
+ }
+
+ // copy poly
+ basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
+
+ // reset object shear and rotations
+ aGeo.nDrehWink = 0;
+ aGeo.RecalcSinCos();
+ aGeo.nShearWink = 0;
+ aGeo.RecalcTan();
+
+ // force metric to pool metric
+ SfxMapUnit eMapUnit = GetObjectItemSet().GetPool()->GetMetric(0);
+ if(eMapUnit != SFX_MAPUNIT_100TH_MM)
+ {
+ switch(eMapUnit)
+ {
+ case SFX_MAPUNIT_TWIP :
+ {
+ // position
+ aTranslate.setX(ImplMMToTwips(aTranslate.getX()));
+ aTranslate.setY(ImplMMToTwips(aTranslate.getY()));
+
+ // size
+ aScale.setX(ImplMMToTwips(aScale.getX()));
+ aScale.setY(ImplMMToTwips(aScale.getY()));
+
+ // polygon
+ basegfx::B2DHomMatrix aMMToTwips;
+ const double fFactorMMToTwips(72.0 / 127.0);
+ aMMToTwips.scale(fFactorMMToTwips, fFactorMMToTwips);
+ aNewPolyPolygon.transform(aMMToTwips);
+
+ break;
+ }
+ default:
+ {
+ DBG_ERROR("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
+ }
+ }
+ }
+
+ if( pModel && pModel->IsWriter() )
+ {
+ // if anchor is used, make position relative to it
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // create transformation for polygon, set values at aGeo direct
+ basegfx::B2DHomMatrix aTransform;
+
+ // #i75086#
+ // Given polygon is already scaled (for historical reasons), but not mirrored yet.
+ // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
+ if(basegfx::fTools::less(aScale.getX(), 0.0) || basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aTransform.scale(
+ basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0,
+ basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
+ }
+
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ aTransform.shearX(tan(-atan(fShearX)));
+ aGeo.nShearWink = FRound(atan(fShearX) / F_PI18000);
+ aGeo.RecalcTan();
+ }
+
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ // #i78696#
+ // fRotate is matematically correct for linear transformations, so it's
+ // the one to use for the geometry change
+ aTransform.rotate(fRotate);
+
+ // #i78696#
+ // fRotate is matematically correct, but aGeoStat.nDrehWink is
+ // mirrored -> mirror value here
+ aGeo.nDrehWink = NormAngle360(FRound(-fRotate / F_PI18000));
+ aGeo.RecalcSinCos();
+ }
+
+ if(!aTranslate.equalZero())
+ {
+ // #i39529# absolute positioning, so get current position (without control points (!))
+ const basegfx::B2DRange aCurrentRange(basegfx::tools::getRange(aNewPolyPolygon));
+ aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
+ }
+
+ // transform polygon and trigger change
+ aNewPolyPolygon.transform(aTransform);
+ SetPathPoly(aNewPolyPolygon);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// eof