Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / svdraw / svdpoev.cxx
blob1cfca1ab94b8c39fd960a86aac3877c3fb78e204
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <math.h>
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>
37 using namespace sdr;
40 void SdrPolyEditView::ImpResetPolyPossibilityFlags()
42 eMarkedPointsSmooth=SdrPathSmoothKind::DontCare;
43 eMarkedSegmentsKind=SdrPathSegmentKind::DontCare;
44 bSetMarkedPointsSmoothPossible=false;
45 bSetMarkedSegmentsKindPossible=false;
48 SdrPolyEditView::SdrPolyEditView(
49 SdrModel& rSdrModel,
50 OutputDevice* pOut)
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())
66 return;
68 bool b1stSmooth(true);
69 bool b1stSegm(true);
70 bool bCurve(false);
71 bool bSmoothFuz(false);
72 bool bSegmFuz(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 );
87 if (!pPath)
88 return;
90 SdrUShortCont& rPts = pM->GetMarkedPoints();
91 if (rPts.empty())
92 return;
94 const bool bClosed(pPath->IsClosed());
95 bSetMarkedPointsSmoothPossible = true;
97 if (bClosed)
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;
117 if(!bSmoothFuz)
119 if (b1stSmooth)
121 b1stSmooth = false;
122 eSmooth = basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum);
124 else
126 bSmoothFuz = (eSmooth != basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum));
130 if(!bSegmFuz && bCanSegment)
132 bool bCrv(aLocalPolygon.isNextControlPointUsed(nPntNum));
134 if(b1stSegm)
136 b1stSegm = false;
137 bCurve = bCrv;
139 else
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;
187 else
189 return;
192 if(!HasMarkedPoints())
193 return;
195 SortMarkedObjects();
197 const bool bUndo = IsUndoEnabled();
198 if( bUndo )
199 BegUndo(SvxResId(STR_EditSetPointsSmooth), GetDescriptionOfMarkedPoints());
200 const size_t nMarkCount(GetMarkedObjectCount());
202 for(size_t nMarkNum(nMarkCount); nMarkNum > 0;)
204 --nMarkNum;
205 SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
206 SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
207 if (!pPath)
208 continue;
210 SdrUShortCont& rPts = pM->GetMarkedPoints();
211 PolyPolygonEditor aEditor(pPath->GetPathPoly());
212 if (aEditor.SetPointsSmooth(eFlags, rPts))
214 if( bUndo )
215 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
216 pPath->SetPathPoly(aEditor.GetPolyPolygon());
220 if( bUndo )
221 EndUndo();
224 void SdrPolyEditView::SetMarkedSegmentsKind(SdrPathSegmentKind eKind)
226 if(!HasMarkedPoints())
227 return;
229 SortMarkedObjects();
231 const bool bUndo = IsUndoEnabled();
232 if( bUndo )
233 BegUndo(SvxResId(STR_EditSetSegmentsKind), GetDescriptionOfMarkedPoints());
234 const size_t nMarkCount(GetMarkedObjectCount());
236 for(size_t nMarkNum=nMarkCount; nMarkNum > 0;)
238 --nMarkNum;
239 SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
240 SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
241 if (!pPath)
242 continue;
243 SdrUShortCont& rPts = pM->GetMarkedPoints();
244 PolyPolygonEditor aEditor( pPath->GetPathPoly());
245 if (aEditor.SetSegmentsKind(eKind, rPts))
247 if( bUndo )
248 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
249 pPath->SetPathPoly(aEditor.GetPolyPolygon());
253 if( bUndo )
254 EndUndo();
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())
289 return;
291 BrkAction();
292 SortMarkedObjects();
293 const size_t nMarkCount=GetMarkedObjectCount();
295 const bool bUndo = IsUndoEnabled();
296 if( bUndo )
298 // Description
299 BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Delete);
302 for (size_t nMarkNum=nMarkCount; nMarkNum>0;)
304 --nMarkNum;
305 SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
306 SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
307 if (!pPath)
308 continue;
310 SdrUShortCont& rPts = pM->GetMarkedPoints();
311 PolyPolygonEditor aEditor( pPath->GetPathPoly());
312 if (aEditor.DeletePoints(rPts))
314 if( aEditor.GetPolyPolygon().count() )
316 if (bUndo)
317 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
318 pPath->SetPathPoly( aEditor.GetPolyPolygon() );
320 else
322 if (bUndo)
323 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pPath));
324 pM->GetPageView()->GetObjList()->RemoveObject(pPath->GetOrdNum());
329 if( bUndo )
330 EndUndo();
331 UnmarkAllPoints();
332 MarkListHasChanged();
335 void SdrPolyEditView::RipUpAtMarkedPoints()
337 if(!HasMarkedPoints())
338 return;
340 SortMarkedObjects();
341 const size_t nMarkCount(GetMarkedObjectCount());
343 const bool bUndo = IsUndoEnabled();
344 if( bUndo )
345 BegUndo(SvxResId(STR_EditRipUp), GetDescriptionOfMarkedPoints());
347 for(size_t nMarkNum = nMarkCount; nMarkNum > 0;)
349 --nMarkNum;
350 SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
351 SdrPathObj* pObj = dynamic_cast<SdrPathObj*>( pM->GetMarkedSdrObj() );
352 if (!pObj)
353 continue;
355 SdrUShortCont& rPts = pM->GetMarkedPoints();
357 if (bUndo)
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);
367 if(pNewObj)
369 pM->GetPageView()->GetObjList()->InsertObject(pNewObj.get(), pObj->GetOrdNum() + 1);
370 if (bUndo)
371 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewObj));
372 MarkObj(pNewObj.get(), pM->GetPageView(), false, true);
375 if(nNewPt0Idx)
377 // correction necessary?
378 DBG_ASSERT(!bCorrectionFlag,"Multiple index corrections at SdrPolyEditView::RipUp().");
379 if(!bCorrectionFlag)
381 bCorrectionFlag = true;
383 SdrUShortCont aReplaceSet;
384 for(const auto& rPt : rPts)
386 sal_uInt32 nPntNum(rPt);
387 nPntNum += nNewPt0Idx;
389 if(nPntNum >= nMax)
391 nPntNum -= nMax;
394 aReplaceSet.insert( static_cast<sal_uInt16>(nPntNum) );
396 rPts.swap(aReplaceSet);
398 it = rPts.rbegin();
404 UnmarkAllPoints();
405 if( bUndo )
406 EndUndo();
407 MarkListHasChanged();
410 bool SdrPolyEditView::IsRipUpAtMarkedPointsPossible() const
412 bool bRetval(false);
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)
421 continue;
423 const SdrUShortCont& rSelectedPoints = pMark->GetMarkedPoints();
424 if (rSelectedPoints.empty())
425 continue;
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());
436 if(nPointCount >= 3)
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;
447 return bRetval;
450 bool SdrPolyEditView::IsOpenCloseMarkedObjectsPossible() const
452 bool bRetval(false);
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);
477 return bRetval;
480 SdrObjClosedKind SdrPolyEditView::GetMarkedObjectsClosedState() const
482 bool bOpen(false);
483 bool bClosed(false);
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())
495 bClosed = true;
497 else
499 bOpen = true;
504 if(bOpen && bClosed)
506 return SdrObjClosedKind::DontCare;
508 else if(bOpen)
510 return SdrObjClosedKind::Open;
512 else
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 );
528 if (!pPath)
529 continue;
531 const SdrUShortCont& rPts = pM->GetMarkedPoints();
532 if (rPts.empty())
533 continue;
535 if( bUndo )
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;
552 bool bC1(false);
553 bool bC2(false);
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()));
562 bC1 = true;
565 if(aNewXP.isNextControlPointUsed(nPointNum))
567 const basegfx::B2DPoint aB2DC2(aNewXP.getNextControlPoint(nPointNum));
568 aC2 = Point(FRound(aB2DC2.getX()), FRound(aB2DC2.getY()));
569 bC2 = true;
572 (*pTrFunc)(aPos,&aC1,&aC2,p1,p2,p3,p4);
573 aNewXP.setB2DPoint(nPointNum, basegfx::B2DPoint(aPos.X(), aPos.Y()));
575 if (bC1)
577 aNewXP.setPrevControlPoint(nPointNum, basegfx::B2DPoint(aC1.X(), aC1.Y()));
580 if (bC2)
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);
607 EndUndo();
608 AdjustMarkHdl();
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);
624 EndUndo();
625 AdjustMarkHdl();
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);
643 EndUndo();
644 AdjustMarkHdl();
647 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */