/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace sdr; void SdrPolyEditView::ImpResetPolyPossibilityFlags() { m_eMarkedPointsSmooth=SdrPathSmoothKind::DontCare; m_eMarkedSegmentsKind=SdrPathSegmentKind::DontCare; m_bSetMarkedPointsSmoothPossible=false; m_bSetMarkedSegmentsKindPossible=false; } SdrPolyEditView::SdrPolyEditView( SdrModel& rSdrModel, OutputDevice* pOut) : SdrEditView(rSdrModel, pOut) { ImpResetPolyPossibilityFlags(); } SdrPolyEditView::~SdrPolyEditView() { } void SdrPolyEditView::ImpCheckPolyPossibilities() { const SdrMarkList& rMarkList = GetMarkedObjectList(); ImpResetPolyPossibilityFlags(); const size_t nMarkCount(rMarkList.GetMarkCount()); if(!nMarkCount || ImpIsFrameHandles()) return; bool b1stSmooth(true); bool b1stSegm(true); bool bCurve(false); bool bSmoothFuz(false); bool bSegmFuz(false); basegfx::B2VectorContinuity eSmooth = basegfx::B2VectorContinuity::NONE; for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum) { SdrMark* pM = rMarkList.GetMark(nMarkNum); CheckPolyPossibilitiesHelper( pM, b1stSmooth, b1stSegm, bCurve, bSmoothFuz, bSegmFuz, eSmooth ); } } void SdrPolyEditView::CheckPolyPossibilitiesHelper( SdrMark* pM, bool& b1stSmooth, bool& b1stSegm, bool& bCurve, bool& bSmoothFuz, bool& bSegmFuz, basegfx::B2VectorContinuity& eSmooth ) { SdrObject* pObj = pM->GetMarkedSdrObj(); SdrPathObj* pPath = dynamic_cast( pObj ); if (!pPath) return; SdrUShortCont& rPts = pM->GetMarkedPoints(); if (rPts.empty()) return; const bool bClosed(pPath->IsClosed()); m_bSetMarkedPointsSmoothPossible = true; if (bClosed) { m_bSetMarkedSegmentsKindPossible = true; } for (const auto& rPt : rPts) { sal_uInt32 nNum(rPt); sal_uInt32 nPolyNum, nPntNum; if(PolyPolygonEditor::GetRelativePolyPoint(pPath->GetPathPoly(), nNum, nPolyNum, nPntNum)) { const basegfx::B2DPolygon aLocalPolygon(pPath->GetPathPoly().getB2DPolygon(nPolyNum)); bool bCanSegment(bClosed || nPntNum < aLocalPolygon.count() - 1); if(!m_bSetMarkedSegmentsKindPossible && bCanSegment) { m_bSetMarkedSegmentsKindPossible = true; } if(!bSmoothFuz) { if (b1stSmooth) { b1stSmooth = false; eSmooth = basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum); } else { bSmoothFuz = (eSmooth != basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum)); } } if(!bSegmFuz && bCanSegment) { bool bCrv(aLocalPolygon.isNextControlPointUsed(nPntNum)); if(b1stSegm) { b1stSegm = false; bCurve = bCrv; } else { bSegmFuz = (bCrv != bCurve); } } } } if(!b1stSmooth && !bSmoothFuz) { if(basegfx::B2VectorContinuity::NONE == eSmooth) { m_eMarkedPointsSmooth = SdrPathSmoothKind::Angular; } if(basegfx::B2VectorContinuity::C1 == eSmooth) { m_eMarkedPointsSmooth = SdrPathSmoothKind::Asymmetric; } if(basegfx::B2VectorContinuity::C2 == eSmooth) { m_eMarkedPointsSmooth = SdrPathSmoothKind::Symmetric; } } if(!b1stSegm && !bSegmFuz) { m_eMarkedSegmentsKind = bCurve ? SdrPathSegmentKind::Curve : SdrPathSegmentKind::Line; } } void SdrPolyEditView::SetMarkedPointsSmooth(SdrPathSmoothKind eKind) { basegfx::B2VectorContinuity eFlags; if(SdrPathSmoothKind::Angular == eKind) { eFlags = basegfx::B2VectorContinuity::NONE; } else if(SdrPathSmoothKind::Asymmetric == eKind) { eFlags = basegfx::B2VectorContinuity::C1; } else if(SdrPathSmoothKind::Symmetric == eKind) { eFlags = basegfx::B2VectorContinuity::C2; } else { return; } if(!HasMarkedPoints()) return; const SdrMarkList& rMarkList = GetMarkedObjectList(); rMarkList.ForceSort(); const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(SvxResId(STR_EditSetPointsSmooth), rMarkList.GetPointMarkDescription()); const size_t nMarkCount(rMarkList.GetMarkCount()); for(size_t nMarkNum(nMarkCount); nMarkNum > 0;) { --nMarkNum; SdrMark* pM = rMarkList.GetMark(nMarkNum); SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() ); if (!pPath) continue; SdrUShortCont& rPts = pM->GetMarkedPoints(); PolyPolygonEditor aEditor(pPath->GetPathPoly()); if (aEditor.SetPointsSmooth(eFlags, rPts)) { if( bUndo ) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath)); pPath->SetPathPoly(aEditor.GetPolyPolygon()); } } if( bUndo ) EndUndo(); } void SdrPolyEditView::SetMarkedSegmentsKind(SdrPathSegmentKind eKind) { if(!HasMarkedPoints()) return; const SdrMarkList& rMarkList = GetMarkedObjectList(); rMarkList.ForceSort(); const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(SvxResId(STR_EditSetSegmentsKind), rMarkList.GetPointMarkDescription()); const size_t nMarkCount(rMarkList.GetMarkCount()); for(size_t nMarkNum=nMarkCount; nMarkNum > 0;) { --nMarkNum; SdrMark* pM = rMarkList.GetMark(nMarkNum); SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() ); if (!pPath) continue; SdrUShortCont& rPts = pM->GetMarkedPoints(); PolyPolygonEditor aEditor( pPath->GetPathPoly()); if (aEditor.SetSegmentsKind(eKind, rPts)) { if( bUndo ) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath)); pPath->SetPathPoly(aEditor.GetPolyPolygon()); } } if( bUndo ) EndUndo(); } bool SdrPolyEditView::IsSetMarkedPointsSmoothPossible() const { ForcePossibilities(); return m_bSetMarkedPointsSmoothPossible; } SdrPathSmoothKind SdrPolyEditView::GetMarkedPointsSmooth() const { ForcePossibilities(); return m_eMarkedPointsSmooth; } bool SdrPolyEditView::IsSetMarkedSegmentsKindPossible() const { ForcePossibilities(); return m_bSetMarkedSegmentsKindPossible; } SdrPathSegmentKind SdrPolyEditView::GetMarkedSegmentsKind() const { ForcePossibilities(); return m_eMarkedSegmentsKind; } bool SdrPolyEditView::IsDeleteMarkedPointsPossible() const { return HasMarkedPoints(); } void SdrPolyEditView::DeleteMarkedPoints() { if (!HasMarkedPoints()) return; BrkAction(); const SdrMarkList& rMarkList = GetMarkedObjectList(); rMarkList.ForceSort(); const size_t nMarkCount=rMarkList.GetMarkCount(); const bool bUndo = IsUndoEnabled(); if( bUndo ) { // Description BegUndo(SvxResId(STR_EditDelete),rMarkList.GetPointMarkDescription(),SdrRepeatFunc::Delete); } for (size_t nMarkNum=nMarkCount; nMarkNum>0;) { --nMarkNum; SdrMark* pM=rMarkList.GetMark(nMarkNum); SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() ); if (!pPath) continue; SdrUShortCont& rPts = pM->GetMarkedPoints(); PolyPolygonEditor aEditor( pPath->GetPathPoly()); if (aEditor.DeletePoints(rPts)) { if( aEditor.GetPolyPolygon().count() ) { if (bUndo) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pPath)); pPath->SetPathPoly( aEditor.GetPolyPolygon() ); } else { if (bUndo) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pPath)); pM->GetPageView()->GetObjList()->RemoveObject(pPath->GetOrdNum()); } } } if( bUndo ) EndUndo(); UnmarkAllPoints(); MarkListHasChanged(); } void SdrPolyEditView::RipUpAtMarkedPoints() { if(!HasMarkedPoints()) return; const SdrMarkList& rMarkList = GetMarkedObjectList(); rMarkList.ForceSort(); const size_t nMarkCount(rMarkList.GetMarkCount()); const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(SvxResId(STR_EditRipUp), rMarkList.GetPointMarkDescription()); for(size_t nMarkNum = nMarkCount; nMarkNum > 0;) { --nMarkNum; SdrMark* pM = rMarkList.GetMark(nMarkNum); SdrPathObj* pObj = dynamic_cast( pM->GetMarkedSdrObj() ); if (!pObj) continue; SdrUShortCont& rPts = pM->GetMarkedPoints(); if (bUndo) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj)); bool bCorrectionFlag(false); sal_uInt32 nMax(pObj->GetHdlCount()); for(SdrUShortCont::const_reverse_iterator it = rPts.rbegin(); it != rPts.rend(); ++it) { sal_uInt32 nNewPt0Idx(0); rtl::Reference pNewObj = pObj->RipPoint(*it, nNewPt0Idx); if(pNewObj) { pM->GetPageView()->GetObjList()->InsertObject(pNewObj.get(), pObj->GetOrdNum() + 1); if (bUndo) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewObj)); MarkObj(pNewObj.get(), pM->GetPageView(), false, true); } if(nNewPt0Idx) { // correction necessary? DBG_ASSERT(!bCorrectionFlag,"Multiple index corrections at SdrPolyEditView::RipUp()."); if(!bCorrectionFlag) { bCorrectionFlag = true; SdrUShortCont aReplaceSet; for(const auto& rPt : rPts) { sal_uInt32 nPntNum(rPt); nPntNum += nNewPt0Idx; if(nPntNum >= nMax) { nPntNum -= nMax; } aReplaceSet.insert( static_cast(nPntNum) ); } rPts.swap(aReplaceSet); it = rPts.rbegin(); } } } } UnmarkAllPoints(); if( bUndo ) EndUndo(); MarkListHasChanged(); } bool SdrPolyEditView::IsRipUpAtMarkedPointsPossible() const { bool bRetval(false); const SdrMarkList& rMarkList = GetMarkedObjectList(); const size_t nMarkCount(rMarkList.GetMarkCount()); for(size_t a = 0; a < nMarkCount; ++a) { const SdrMark* pMark = rMarkList.GetMark(a); const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj()); if (!pMarkedPathObject) continue; const SdrUShortCont& rSelectedPoints = pMark->GetMarkedPoints(); if (rSelectedPoints.empty()) continue; const basegfx::B2DPolyPolygon& rPathPolyPolygon = pMarkedPathObject->GetPathPoly(); if(1 == rPathPolyPolygon.count()) { // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions // are different and methods need to be changed thoroughly with interaction rework const tools::Polygon aPathPolygon(rPathPolyPolygon.getB2DPolygon(0)); const sal_uInt16 nPointCount(aPathPolygon.GetSize()); if(nPointCount >= 3) { bRetval = pMarkedPathObject->IsClosedObj() // #i76617# || std::any_of(rSelectedPoints.begin(), rSelectedPoints.end(), [nPointCount](const sal_uInt16 nMarkedPointNum) { return nMarkedPointNum > 0 && nMarkedPointNum < nPointCount - 1; }); } } } return bRetval; } bool SdrPolyEditView::IsOpenCloseMarkedObjectsPossible() const { bool bRetval(false); const SdrMarkList& rMarkList = GetMarkedObjectList(); const size_t nMarkCount(rMarkList.GetMarkCount()); for(size_t a = 0; a < nMarkCount; ++a) { const SdrMark* pMark = rMarkList.GetMark(a); const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj()); if(pMarkedPathObject) { // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions // are different and methods need to be changed thoroughly with interaction rework const tools::PolyPolygon aPathPolyPolygon(pMarkedPathObject->GetPathPoly()); const sal_uInt16 nPolygonCount(aPathPolyPolygon.Count()); for(sal_uInt16 b(0); !bRetval && b < nPolygonCount; b++) { const tools::Polygon& rPathPolygon = aPathPolyPolygon[b]; const sal_uInt16 nPointCount(rPathPolygon.GetSize()); bRetval = (nPointCount >= 3); } } } return bRetval; } SdrObjClosedKind SdrPolyEditView::GetMarkedObjectsClosedState() const { bool bOpen(false); bool bClosed(false); const SdrMarkList& rMarkList = GetMarkedObjectList(); const size_t nMarkCount(rMarkList.GetMarkCount()); for(size_t a = 0; !(bOpen && bClosed) && a < nMarkCount; ++a) { const SdrMark* pMark = rMarkList.GetMark(a); const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj()); if(pMarkedPathObject) { if(pMarkedPathObject->IsClosedObj()) { bClosed = true; } else { bOpen = true; } } } if(bOpen && bClosed) { return SdrObjClosedKind::DontCare; } else if(bOpen) { return SdrObjClosedKind::Open; } else { return SdrObjClosedKind::Closed; } } void SdrPolyEditView::ImpTransformMarkedPoints(PPolyTrFunc pTrFunc, const void* p1, const void* p2, const void* p3, const void* p4) { const bool bUndo = IsUndoEnabled(); const SdrMarkList& rMarkList = GetMarkedObjectList(); const size_t nMarkCount=rMarkList.GetMarkCount(); for (size_t nm=0; nmGetMarkedSdrObj(); SdrPathObj* pPath=dynamic_cast( pObj ); if (!pPath) continue; const SdrUShortCont& rPts = pM->GetMarkedPoints(); if (rPts.empty()) continue; if( bUndo ) AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj)); basegfx::B2DPolyPolygon aXPP(pPath->GetPathPoly()); for (const auto& rPt : rPts) { sal_uInt32 nPt = rPt; sal_uInt32 nPolyNum, nPointNum; if(PolyPolygonEditor::GetRelativePolyPoint(aXPP, nPt, nPolyNum, nPointNum)) { //#i83671# used nLocalPointNum (which was the polygon point count) // instead of the point index (nPointNum). This of course led // to a wrong point access to the B2DPolygon. basegfx::B2DPolygon aNewXP(aXPP.getB2DPolygon(nPolyNum)); Point aPos, aC1, aC2; bool bC1(false); bool bC2(false); const basegfx::B2DPoint aB2DPos(aNewXP.getB2DPoint(nPointNum)); aPos = Point(basegfx::fround(aB2DPos.getX()), basegfx::fround(aB2DPos.getY())); if(aNewXP.isPrevControlPointUsed(nPointNum)) { const basegfx::B2DPoint aB2DC1(aNewXP.getPrevControlPoint(nPointNum)); aC1 = Point(basegfx::fround(aB2DC1.getX()), basegfx::fround(aB2DC1.getY())); bC1 = true; } if(aNewXP.isNextControlPointUsed(nPointNum)) { const basegfx::B2DPoint aB2DC2(aNewXP.getNextControlPoint(nPointNum)); aC2 = Point(basegfx::fround(aB2DC2.getX()), basegfx::fround(aB2DC2.getY())); bC2 = true; } (*pTrFunc)(aPos,&aC1,&aC2,p1,p2,p3,p4); aNewXP.setB2DPoint(nPointNum, basegfx::B2DPoint(aPos.X(), aPos.Y())); if (bC1) { aNewXP.setPrevControlPoint(nPointNum, basegfx::B2DPoint(aC1.X(), aC1.Y())); } if (bC2) { aNewXP.setNextControlPoint(nPointNum, basegfx::B2DPoint(aC2.X(), aC2.Y())); } aXPP.setB2DPolygon(nPolyNum, aNewXP); } } pPath->SetPathPoly(aXPP); } } static void ImpMove(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/) { rPt.Move(*static_cast(p1)); if (pC1!=nullptr) pC1->Move(*static_cast(p1)); if (pC2!=nullptr) pC2->Move(*static_cast(p1)); } void SdrPolyEditView::MoveMarkedPoints(const Size& rSiz) { ForceUndirtyMrkPnt(); OUString aStr(SvxResId(STR_EditMove)); const SdrMarkList& rMarkList = GetMarkedObjectList(); BegUndo(aStr,rMarkList.GetPointMarkDescription(),SdrRepeatFunc::Move); ImpTransformMarkedPoints(ImpMove,&rSiz); EndUndo(); AdjustMarkHdl(); } static void ImpResize(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* p2, const void* p3, const void* /*p4*/) { ResizePoint(rPt,*static_cast(p1),*static_cast(p2),*static_cast(p3)); if (pC1!=nullptr) ResizePoint(*pC1,*static_cast(p1),*static_cast(p2),*static_cast(p3)); if (pC2!=nullptr) ResizePoint(*pC2,*static_cast(p1),*static_cast(p2),*static_cast(p3)); } void SdrPolyEditView::ResizeMarkedPoints(const Point& rRef, const Fraction& xFact, const Fraction& yFact) { ForceUndirtyMrkPnt(); OUString aStr(SvxResId(STR_EditResize)); const SdrMarkList& rMarkList = GetMarkedObjectList(); BegUndo(aStr,rMarkList.GetPointMarkDescription(),SdrRepeatFunc::Resize); ImpTransformMarkedPoints(ImpResize,&rRef,&xFact,&yFact); EndUndo(); AdjustMarkHdl(); } static void ImpRotate(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* p3, const void* p4) { RotatePoint(rPt,*static_cast(p1),*static_cast(p3),*static_cast(p4)); if (pC1!=nullptr) RotatePoint(*pC1,*static_cast(p1),*static_cast(p3),*static_cast(p4)); if (pC2!=nullptr) RotatePoint(*pC2,*static_cast(p1),*static_cast(p3),*static_cast(p4)); } void SdrPolyEditView::RotateMarkedPoints(const Point& rRef, Degree100 nAngle) { ForceUndirtyMrkPnt(); OUString aStr(SvxResId(STR_EditResize)); const SdrMarkList& rMarkList = GetMarkedObjectList(); BegUndo(aStr,rMarkList.GetPointMarkDescription(),SdrRepeatFunc::Rotate); double nSin = sin(toRadians(nAngle)); double nCos = cos(toRadians(nAngle)); ImpTransformMarkedPoints(ImpRotate,&rRef,&nAngle,&nSin,&nCos); EndUndo(); AdjustMarkHdl(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */