1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <svx/svdpoev.hxx>
23 #include <svx/svdpagv.hxx>
24 #include <svx/svdpage.hxx>
25 #include <svx/svdopath.hxx>
26 #include <svx/svdundo.hxx>
27 #include <svx/strings.hrc>
28 #include <svx/dialmgr.hxx>
29 #include <svx/svdtrans.hxx>
30 #include <basegfx/polygon/b2dpolygon.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <tools/debug.hxx>
33 #include <tools/helpers.hxx>
35 #include <svx/polypolygoneditor.hxx>
40 void SdrPolyEditView::ImpResetPolyPossibilityFlags()
42 eMarkedPointsSmooth
=SdrPathSmoothKind::DontCare
;
43 eMarkedSegmentsKind
=SdrPathSegmentKind::DontCare
;
44 bSetMarkedPointsSmoothPossible
=false;
45 bSetMarkedSegmentsKindPossible
=false;
48 SdrPolyEditView::SdrPolyEditView(
51 : SdrEditView(rSdrModel
, pOut
)
53 ImpResetPolyPossibilityFlags();
56 SdrPolyEditView::~SdrPolyEditView()
60 void SdrPolyEditView::ImpCheckPolyPossibilities()
62 ImpResetPolyPossibilityFlags();
63 const size_t nMarkCount(GetMarkedObjectCount());
65 if(!nMarkCount
|| ImpIsFrameHandles())
68 bool b1stSmooth(true);
71 bool bSmoothFuz(false);
73 basegfx::B2VectorContinuity eSmooth
= basegfx::B2VectorContinuity::NONE
;
75 for(size_t nMarkNum
= 0; nMarkNum
< nMarkCount
; ++nMarkNum
)
77 SdrMark
* pM
= GetSdrMarkByIndex(nMarkNum
);
78 CheckPolyPossibilitiesHelper( pM
, b1stSmooth
, b1stSegm
, bCurve
, bSmoothFuz
, bSegmFuz
, eSmooth
);
82 void SdrPolyEditView::CheckPolyPossibilitiesHelper( SdrMark
* pM
, bool& b1stSmooth
, bool& b1stSegm
, bool& bCurve
, bool& bSmoothFuz
, bool& bSegmFuz
, basegfx::B2VectorContinuity
& eSmooth
)
84 SdrObject
* pObj
= pM
->GetMarkedSdrObj();
85 SdrPathObj
* pPath
= dynamic_cast<SdrPathObj
*>( pObj
);
90 SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
94 const bool bClosed(pPath
->IsClosed());
95 bSetMarkedPointsSmoothPossible
= true;
99 bSetMarkedSegmentsKindPossible
= true;
102 for (const auto& rPt
: rPts
)
104 sal_uInt32
nNum(rPt
);
105 sal_uInt32 nPolyNum
, nPntNum
;
107 if(PolyPolygonEditor::GetRelativePolyPoint(pPath
->GetPathPoly(), nNum
, nPolyNum
, nPntNum
))
109 const basegfx::B2DPolygon
aLocalPolygon(pPath
->GetPathPoly().getB2DPolygon(nPolyNum
));
110 bool bCanSegment(bClosed
|| nPntNum
< aLocalPolygon
.count() - 1);
112 if(!bSetMarkedSegmentsKindPossible
&& bCanSegment
)
114 bSetMarkedSegmentsKindPossible
= true;
122 eSmooth
= basegfx::utils::getContinuityInPoint(aLocalPolygon
, nPntNum
);
126 bSmoothFuz
= (eSmooth
!= basegfx::utils::getContinuityInPoint(aLocalPolygon
, nPntNum
));
130 if(!bSegmFuz
&& bCanSegment
)
132 bool bCrv(aLocalPolygon
.isNextControlPointUsed(nPntNum
));
141 bSegmFuz
= (bCrv
!= bCurve
);
147 if(!b1stSmooth
&& !bSmoothFuz
)
149 if(basegfx::B2VectorContinuity::NONE
== eSmooth
)
151 eMarkedPointsSmooth
= SdrPathSmoothKind::Angular
;
154 if(basegfx::B2VectorContinuity::C1
== eSmooth
)
156 eMarkedPointsSmooth
= SdrPathSmoothKind::Asymmetric
;
159 if(basegfx::B2VectorContinuity::C2
== eSmooth
)
161 eMarkedPointsSmooth
= SdrPathSmoothKind::Symmetric
;
165 if(!b1stSegm
&& !bSegmFuz
)
167 eMarkedSegmentsKind
= bCurve
? SdrPathSegmentKind::Curve
: SdrPathSegmentKind::Line
;
171 void SdrPolyEditView::SetMarkedPointsSmooth(SdrPathSmoothKind eKind
)
173 basegfx::B2VectorContinuity eFlags
;
175 if(SdrPathSmoothKind::Angular
== eKind
)
177 eFlags
= basegfx::B2VectorContinuity::NONE
;
179 else if(SdrPathSmoothKind::Asymmetric
== eKind
)
181 eFlags
= basegfx::B2VectorContinuity::C1
;
183 else if(SdrPathSmoothKind::Symmetric
== eKind
)
185 eFlags
= basegfx::B2VectorContinuity::C2
;
192 if(!HasMarkedPoints())
197 const bool bUndo
= IsUndoEnabled();
199 BegUndo(SvxResId(STR_EditSetPointsSmooth
), GetDescriptionOfMarkedPoints());
200 const size_t nMarkCount(GetMarkedObjectCount());
202 for(size_t nMarkNum(nMarkCount
); nMarkNum
> 0;)
205 SdrMark
* pM
= GetSdrMarkByIndex(nMarkNum
);
206 SdrPathObj
* pPath
= dynamic_cast< SdrPathObj
* >( pM
->GetMarkedSdrObj() );
210 SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
211 PolyPolygonEditor
aEditor(pPath
->GetPathPoly());
212 if (aEditor
.SetPointsSmooth(eFlags
, rPts
))
215 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath
));
216 pPath
->SetPathPoly(aEditor
.GetPolyPolygon());
224 void SdrPolyEditView::SetMarkedSegmentsKind(SdrPathSegmentKind eKind
)
226 if(!HasMarkedPoints())
231 const bool bUndo
= IsUndoEnabled();
233 BegUndo(SvxResId(STR_EditSetSegmentsKind
), GetDescriptionOfMarkedPoints());
234 const size_t nMarkCount(GetMarkedObjectCount());
236 for(size_t nMarkNum
=nMarkCount
; nMarkNum
> 0;)
239 SdrMark
* pM
= GetSdrMarkByIndex(nMarkNum
);
240 SdrPathObj
* pPath
= dynamic_cast< SdrPathObj
* >( pM
->GetMarkedSdrObj() );
243 SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
244 PolyPolygonEditor
aEditor( pPath
->GetPathPoly());
245 if (aEditor
.SetSegmentsKind(eKind
, rPts
))
248 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath
));
249 pPath
->SetPathPoly(aEditor
.GetPolyPolygon());
257 bool SdrPolyEditView::IsSetMarkedPointsSmoothPossible() const
259 ForcePossibilities();
260 return bSetMarkedPointsSmoothPossible
;
263 SdrPathSmoothKind
SdrPolyEditView::GetMarkedPointsSmooth() const
265 ForcePossibilities();
266 return eMarkedPointsSmooth
;
269 bool SdrPolyEditView::IsSetMarkedSegmentsKindPossible() const
271 ForcePossibilities();
272 return bSetMarkedSegmentsKindPossible
;
275 SdrPathSegmentKind
SdrPolyEditView::GetMarkedSegmentsKind() const
277 ForcePossibilities();
278 return eMarkedSegmentsKind
;
281 bool SdrPolyEditView::IsDeleteMarkedPointsPossible() const
283 return HasMarkedPoints();
286 void SdrPolyEditView::DeleteMarkedPoints()
288 if (!HasMarkedPoints())
293 const size_t nMarkCount
=GetMarkedObjectCount();
295 const bool bUndo
= IsUndoEnabled();
299 BegUndo(SvxResId(STR_EditDelete
),GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Delete
);
302 for (size_t nMarkNum
=nMarkCount
; nMarkNum
>0;)
305 SdrMark
* pM
=GetSdrMarkByIndex(nMarkNum
);
306 SdrPathObj
* pPath
= dynamic_cast< SdrPathObj
* >( pM
->GetMarkedSdrObj() );
310 SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
311 PolyPolygonEditor
aEditor( pPath
->GetPathPoly());
312 if (aEditor
.DeletePoints(rPts
))
314 if( aEditor
.GetPolyPolygon().count() )
317 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath
));
318 pPath
->SetPathPoly( aEditor
.GetPolyPolygon() );
323 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pPath
));
324 pM
->GetPageView()->GetObjList()->RemoveObject(pPath
->GetOrdNum());
332 MarkListHasChanged();
335 void SdrPolyEditView::RipUpAtMarkedPoints()
337 if(!HasMarkedPoints())
341 const size_t nMarkCount(GetMarkedObjectCount());
343 const bool bUndo
= IsUndoEnabled();
345 BegUndo(SvxResId(STR_EditRipUp
), GetDescriptionOfMarkedPoints());
347 for(size_t nMarkNum
= nMarkCount
; nMarkNum
> 0;)
350 SdrMark
* pM
= GetSdrMarkByIndex(nMarkNum
);
351 SdrPathObj
* pObj
= dynamic_cast<SdrPathObj
*>( pM
->GetMarkedSdrObj() );
355 SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
358 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj
));
359 bool bCorrectionFlag(false);
360 sal_uInt32
nMax(pObj
->GetHdlCount());
362 for(SdrUShortCont::const_reverse_iterator it
= rPts
.rbegin(); it
!= rPts
.rend(); ++it
)
364 sal_uInt32
nNewPt0Idx(0);
365 rtl::Reference
<SdrPathObj
> pNewObj
= pObj
->RipPoint(*it
, nNewPt0Idx
);
369 pM
->GetPageView()->GetObjList()->InsertObject(pNewObj
.get(), pObj
->GetOrdNum() + 1);
371 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewObj
));
372 MarkObj(pNewObj
.get(), pM
->GetPageView(), false, true);
377 // correction necessary?
378 DBG_ASSERT(!bCorrectionFlag
,"Multiple index corrections at SdrPolyEditView::RipUp().");
381 bCorrectionFlag
= true;
383 SdrUShortCont aReplaceSet
;
384 for(const auto& rPt
: rPts
)
386 sal_uInt32
nPntNum(rPt
);
387 nPntNum
+= nNewPt0Idx
;
394 aReplaceSet
.insert( static_cast<sal_uInt16
>(nPntNum
) );
396 rPts
.swap(aReplaceSet
);
407 MarkListHasChanged();
410 bool SdrPolyEditView::IsRipUpAtMarkedPointsPossible() const
413 const size_t nMarkCount(GetMarkedObjectCount());
415 for(size_t a
= 0; a
< nMarkCount
; ++a
)
417 const SdrMark
* pMark
= GetSdrMarkByIndex(a
);
418 const SdrPathObj
* pMarkedPathObject
= dynamic_cast< const SdrPathObj
* >(pMark
->GetMarkedSdrObj());
420 if (!pMarkedPathObject
)
423 const SdrUShortCont
& rSelectedPoints
= pMark
->GetMarkedPoints();
424 if (rSelectedPoints
.empty())
427 const basegfx::B2DPolyPolygon
& rPathPolyPolygon
= pMarkedPathObject
->GetPathPoly();
429 if(1 == rPathPolyPolygon
.count())
431 // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
432 // are different and methods need to be changed thoroughly with interaction rework
433 const tools::Polygon
aPathPolygon(rPathPolyPolygon
.getB2DPolygon(0));
434 const sal_uInt16
nPointCount(aPathPolygon
.GetSize());
438 bRetval
= pMarkedPathObject
->IsClosedObj() // #i76617#
439 || std::any_of(rSelectedPoints
.begin(), rSelectedPoints
.end(),
440 [nPointCount
](const sal_uInt16 nMarkedPointNum
) {
441 return nMarkedPointNum
> 0 && nMarkedPointNum
< nPointCount
- 1;
450 bool SdrPolyEditView::IsOpenCloseMarkedObjectsPossible() const
453 const size_t nMarkCount(GetMarkedObjectCount());
455 for(size_t a
= 0; a
< nMarkCount
; ++a
)
457 const SdrMark
* pMark
= GetSdrMarkByIndex(a
);
458 const SdrPathObj
* pMarkedPathObject
= dynamic_cast< const SdrPathObj
* >(pMark
->GetMarkedSdrObj());
460 if(pMarkedPathObject
)
462 // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
463 // are different and methods need to be changed thoroughly with interaction rework
464 const tools::PolyPolygon
aPathPolyPolygon(pMarkedPathObject
->GetPathPoly());
465 const sal_uInt16
nPolygonCount(aPathPolyPolygon
.Count());
467 for(sal_uInt16
b(0); !bRetval
&& b
< nPolygonCount
; b
++)
469 const tools::Polygon
& rPathPolygon
= aPathPolyPolygon
[b
];
470 const sal_uInt16
nPointCount(rPathPolygon
.GetSize());
472 bRetval
= (nPointCount
>= 3);
480 SdrObjClosedKind
SdrPolyEditView::GetMarkedObjectsClosedState() const
484 const size_t nMarkCount(GetMarkedObjectCount());
486 for(size_t a
= 0; !(bOpen
&& bClosed
) && a
< nMarkCount
; ++a
)
488 const SdrMark
* pMark
= GetSdrMarkByIndex(a
);
489 const SdrPathObj
* pMarkedPathObject
= dynamic_cast< const SdrPathObj
* >(pMark
->GetMarkedSdrObj());
491 if(pMarkedPathObject
)
493 if(pMarkedPathObject
->IsClosedObj())
506 return SdrObjClosedKind::DontCare
;
510 return SdrObjClosedKind::Open
;
514 return SdrObjClosedKind::Closed
;
518 void SdrPolyEditView::ImpTransformMarkedPoints(PPolyTrFunc pTrFunc
, const void* p1
, const void* p2
, const void* p3
, const void* p4
)
520 const bool bUndo
= IsUndoEnabled();
522 const size_t nMarkCount
=GetMarkedObjectCount();
523 for (size_t nm
=0; nm
<nMarkCount
; ++nm
)
525 SdrMark
* pM
=GetSdrMarkByIndex(nm
);
526 SdrObject
* pObj
=pM
->GetMarkedSdrObj();
527 SdrPathObj
* pPath
=dynamic_cast<SdrPathObj
*>( pObj
);
531 const SdrUShortCont
& rPts
= pM
->GetMarkedPoints();
536 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj
));
538 basegfx::B2DPolyPolygon
aXPP(pPath
->GetPathPoly());
540 for (const auto& rPt
: rPts
)
542 sal_uInt32 nPt
= rPt
;
543 sal_uInt32 nPolyNum
, nPointNum
;
545 if(PolyPolygonEditor::GetRelativePolyPoint(aXPP
, nPt
, nPolyNum
, nPointNum
))
547 //#i83671# used nLocalPointNum (which was the polygon point count)
548 // instead of the point index (nPointNum). This of course led
549 // to a wrong point access to the B2DPolygon.
550 basegfx::B2DPolygon
aNewXP(aXPP
.getB2DPolygon(nPolyNum
));
551 Point aPos
, aC1
, aC2
;
555 const basegfx::B2DPoint
aB2DPos(aNewXP
.getB2DPoint(nPointNum
));
556 aPos
= Point(FRound(aB2DPos
.getX()), FRound(aB2DPos
.getY()));
558 if(aNewXP
.isPrevControlPointUsed(nPointNum
))
560 const basegfx::B2DPoint
aB2DC1(aNewXP
.getPrevControlPoint(nPointNum
));
561 aC1
= Point(FRound(aB2DC1
.getX()), FRound(aB2DC1
.getY()));
565 if(aNewXP
.isNextControlPointUsed(nPointNum
))
567 const basegfx::B2DPoint
aB2DC2(aNewXP
.getNextControlPoint(nPointNum
));
568 aC2
= Point(FRound(aB2DC2
.getX()), FRound(aB2DC2
.getY()));
572 (*pTrFunc
)(aPos
,&aC1
,&aC2
,p1
,p2
,p3
,p4
);
573 aNewXP
.setB2DPoint(nPointNum
, basegfx::B2DPoint(aPos
.X(), aPos
.Y()));
577 aNewXP
.setPrevControlPoint(nPointNum
, basegfx::B2DPoint(aC1
.X(), aC1
.Y()));
582 aNewXP
.setNextControlPoint(nPointNum
, basegfx::B2DPoint(aC2
.X(), aC2
.Y()));
585 aXPP
.setB2DPolygon(nPolyNum
, aNewXP
);
589 pPath
->SetPathPoly(aXPP
);
594 static void ImpMove(Point
& rPt
, Point
* pC1
, Point
* pC2
, const void* p1
, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/)
596 rPt
.Move(*static_cast<const Size
*>(p1
));
597 if (pC1
!=nullptr) pC1
->Move(*static_cast<const Size
*>(p1
));
598 if (pC2
!=nullptr) pC2
->Move(*static_cast<const Size
*>(p1
));
601 void SdrPolyEditView::MoveMarkedPoints(const Size
& rSiz
)
603 ForceUndirtyMrkPnt();
604 OUString
aStr(SvxResId(STR_EditMove
));
605 BegUndo(aStr
,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Move
);
606 ImpTransformMarkedPoints(ImpMove
,&rSiz
);
611 static void ImpResize(Point
& rPt
, Point
* pC1
, Point
* pC2
, const void* p1
, const void* p2
, const void* p3
, const void* /*p4*/)
613 ResizePoint(rPt
,*static_cast<const Point
*>(p1
),*static_cast<const Fraction
*>(p2
),*static_cast<const Fraction
*>(p3
));
614 if (pC1
!=nullptr) ResizePoint(*pC1
,*static_cast<const Point
*>(p1
),*static_cast<const Fraction
*>(p2
),*static_cast<const Fraction
*>(p3
));
615 if (pC2
!=nullptr) ResizePoint(*pC2
,*static_cast<const Point
*>(p1
),*static_cast<const Fraction
*>(p2
),*static_cast<const Fraction
*>(p3
));
618 void SdrPolyEditView::ResizeMarkedPoints(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
620 ForceUndirtyMrkPnt();
621 OUString
aStr(SvxResId(STR_EditResize
));
622 BegUndo(aStr
,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Resize
);
623 ImpTransformMarkedPoints(ImpResize
,&rRef
,&xFact
,&yFact
);
628 static void ImpRotate(Point
& rPt
, Point
* pC1
, Point
* pC2
, const void* p1
, const void* /*p2*/, const void* p3
, const void* p4
)
630 RotatePoint(rPt
,*static_cast<const Point
*>(p1
),*static_cast<const double*>(p3
),*static_cast<const double*>(p4
));
631 if (pC1
!=nullptr) RotatePoint(*pC1
,*static_cast<const Point
*>(p1
),*static_cast<const double*>(p3
),*static_cast<const double*>(p4
));
632 if (pC2
!=nullptr) RotatePoint(*pC2
,*static_cast<const Point
*>(p1
),*static_cast<const double*>(p3
),*static_cast<const double*>(p4
));
635 void SdrPolyEditView::RotateMarkedPoints(const Point
& rRef
, Degree100 nAngle
)
637 ForceUndirtyMrkPnt();
638 OUString
aStr(SvxResId(STR_EditResize
));
639 BegUndo(aStr
,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Rotate
);
640 double nSin
= sin(toRadians(nAngle
));
641 double nCos
= cos(toRadians(nAngle
));
642 ImpTransformMarkedPoints(ImpRotate
,&rRef
,&nAngle
,&nSin
,&nCos
);
647 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */