diff options
Diffstat (limited to '')
-rw-r--r-- | svx/source/svdraw/svddrgmt.cxx | 3860 |
1 files changed, 3860 insertions, 0 deletions
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx new file mode 100644 index 000000000..40fd8df27 --- /dev/null +++ b/svx/source/svdraw/svddrgmt.cxx @@ -0,0 +1,3860 @@ +/* -*- 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 "svddrgm1.hxx" +#include <math.h> + +#include <o3tl/numeric.hxx> +#include <osl/diagnose.h> +#include <vcl/canvastools.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> +#include <svx/xpoly.hxx> +#include <svx/svdtrans.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svddrgv.hxx> +#include <svx/svdograf.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/sdgcpitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdtagitm.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <sdr/overlay/overlayrollingrectangle.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/svditer.hxx> +#include <svx/svdopath.hxx> +#include <svx/polypolygoneditor.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <sdr/primitive2d/sdrprimitivetools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/attribute/sdrlineattribute.hxx> +#include <drawinglayer/attribute/sdrlinestartendattribute.hxx> +#include <svl/itempool.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <comphelper/lok.hxx> +#include <map> +#include <vector> + + +SdrDragEntry::SdrDragEntry() +: mbAddToTransparent(false) +{ +} + +SdrDragEntry::~SdrDragEntry() +{ +} + + +SdrDragEntryPolyPolygon::SdrDragEntryPolyPolygon(const basegfx::B2DPolyPolygon& rOriginalPolyPolygon) +: maOriginalPolyPolygon(rOriginalPolyPolygon) +{ +} + +SdrDragEntryPolyPolygon::~SdrDragEntryPolyPolygon() +{ +} + +drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPolyPolygon::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod) +{ + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(maOriginalPolyPolygon.count()) + { + basegfx::B2DPolyPolygon aCopy(maOriginalPolyPolygon); + + rDragMethod.applyCurrentTransformationToPolyPolygon(aCopy); + basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor()); + basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor()); + const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength()); + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + aColB.invert(); + } + + aRetval.resize(2); + aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D( + aCopy, + aColA, + aColB, + fStripeLength); + + const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor()); + const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); + + aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D( + aCopy, + aHilightColor, + fTransparence, + 3.0, + false); + } + + return aRetval; +} + + +SdrDragEntrySdrObject::SdrDragEntrySdrObject( + const SdrObject& rOriginal, + bool bModify) +: maOriginal(rOriginal), + mbModify(bModify) +{ + // add SdrObject parts to transparent overlay stuff + setAddToTransparent(true); +} + +SdrDragEntrySdrObject::~SdrDragEntrySdrObject() +{ +} + +void SdrDragEntrySdrObject::prepareCurrentState(SdrDragMethod& rDragMethod) +{ + // for the moment, i need to re-create the clone in all cases. I need to figure + // out when clone and original have the same class, so that i can use operator= + // in those cases + + mxClone.reset(); + + if(mbModify) + { + mxClone = maOriginal.getFullDragClone(); + + // apply original transformation, implemented at the DragMethods + rDragMethod.applyCurrentTransformationToSdrObject(*mxClone); + } +} + +drawinglayer::primitive2d::Primitive2DContainer SdrDragEntrySdrObject::createPrimitive2DSequenceInCurrentState(SdrDragMethod&) +{ + const SdrObject* pSource = &maOriginal; + + if(mbModify && mxClone) + { + // choose source for geometry data + pSource = mxClone.get(); + } + + // use the view-independent primitive representation (without + // evtl. GridOffset, that may be applied to the DragEntry individually) + drawinglayer::primitive2d::Primitive2DContainer xRetval; + pSource->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval); + return xRetval; +} + + +SdrDragEntryPrimitive2DSequence::SdrDragEntryPrimitive2DSequence( + drawinglayer::primitive2d::Primitive2DContainer&& rSequence) +: maPrimitive2DSequence(std::move(rSequence)) +{ + // add parts to transparent overlay stuff if necessary + setAddToTransparent(true); +} + +SdrDragEntryPrimitive2DSequence::~SdrDragEntryPrimitive2DSequence() +{ +} + +drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPrimitive2DSequence::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod) +{ + drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D( + new drawinglayer::primitive2d::TransformPrimitive2D( + rDragMethod.getCurrentTransformation(), + drawinglayer::primitive2d::Primitive2DContainer(maPrimitive2DSequence))); + + return drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D }; +} + + +SdrDragEntryPointGlueDrag::SdrDragEntryPointGlueDrag(std::vector< basegfx::B2DPoint >&& rPositions, bool bIsPointDrag) +: maPositions(std::move(rPositions)), + mbIsPointDrag(bIsPointDrag) +{ + // add SdrObject parts to transparent overlay stuff + setAddToTransparent(true); +} + +SdrDragEntryPointGlueDrag::~SdrDragEntryPointGlueDrag() +{ +} + +drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPointGlueDrag::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod) +{ + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(!maPositions.empty()) + { + basegfx::B2DPolygon aPolygon; + + for(auto const & a: maPositions) + { + aPolygon.append(a); + } + + basegfx::B2DPolyPolygon aPolyPolygon(aPolygon); + + rDragMethod.applyCurrentTransformationToPolyPolygon(aPolyPolygon); + + const basegfx::B2DPolygon aTransformed(aPolyPolygon.getB2DPolygon(0)); + std::vector< basegfx::B2DPoint > aTransformedPositions; + + aTransformedPositions.reserve(aTransformed.count()); + + for(sal_uInt32 a = 0; a < aTransformed.count(); a++) + { + aTransformedPositions.push_back(aTransformed.getB2DPoint(a)); + } + + if(mbIsPointDrag) + { + basegfx::BColor aColor(SvtOptionsDrawinglayer::GetStripeColorA().getBColor()); + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + aColor = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + } + + drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D( + new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions), + drawinglayer::primitive2d::createDefaultCross_3x3(aColor))); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D }; + } + else + { + drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D( + new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions), + SdrHdl::createGluePointBitmap())); + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D }; + } + } + + return aRetval; +} + + +void SdrDragMethod::resetSdrDragEntries() +{ + // clear entries; creation is on demand + clearSdrDragEntries(); +} + +basegfx::B2DRange SdrDragMethod::getCurrentRange() const +{ + return maOverlayObjectList.getBaseRange(); +} + +void SdrDragMethod::clearSdrDragEntries() +{ + maSdrDragEntries.clear(); +} + +void SdrDragMethod::addSdrDragEntry(std::unique_ptr<SdrDragEntry> pNew) +{ + assert(pNew); + maSdrDragEntries.push_back(std::move(pNew)); +} + +void SdrDragMethod::createSdrDragEntries() +{ + if(!(getSdrDragView().GetSdrPageView() && getSdrDragView().GetSdrPageView()->HasMarkedObjPageView())) + return; + + if(getSdrDragView().IsDraggingPoints()) + { + createSdrDragEntries_PointDrag(); + } + else if(getSdrDragView().IsDraggingGluePoints()) + { + createSdrDragEntries_GlueDrag(); + } + else + { + if(getSolidDraggingActive()) + { + createSdrDragEntries_SolidDrag(); + } + else + { + createSdrDragEntries_PolygonDrag(); + } + } +} + +void SdrDragMethod::createSdrDragEntryForSdrObject(const SdrObject& rOriginal) +{ + // add full object drag; Clone() at the object has to work + // for this + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(rOriginal, true/*bModify*/))); +} + +void SdrDragMethod::insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject, + const sdr::contact::ObjectContact& rObjectContact, + sdr::overlay::OverlayManager& rOverlayManager) +{ + // check if we have an OverlayObject + if(!pOverlayObject) + { + return; + } + + // add to OverlayManager + rOverlayManager.add(*pOverlayObject); + + // Add GridOffset for non-linear ViewToDevice transformation (calc) + if(rObjectContact.supportsGridOffsets()) + { + const basegfx::B2DRange& rNewRange(pOverlayObject->getBaseRange()); + + if(!rNewRange.isEmpty()) + { + basegfx::B2DVector aOffset(0.0, 0.0); + rObjectContact.calculateGridOffsetForB2DRange(aOffset, rNewRange); + + if(!aOffset.equalZero()) + { + pOverlayObject->setOffset(aOffset); + } + } + } + + // add to local OverlayObjectList - ownership change (!) + maOverlayObjectList.append(std::move(pOverlayObject)); +} + +void SdrDragMethod::createSdrDragEntries_SolidDrag() +{ + const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount()); + SdrPageView* pPV = getSdrDragView().GetSdrPageView(); + + if(!pPV) + return; + + for(size_t a = 0; a < nMarkCount; ++a) + { + SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a); + + if(pM->GetPageView() == pPV) + { + const SdrObject* pObject = pM->GetMarkedSdrObj(); + + if(pObject) + { + if(pPV->PageWindowCount()) + { + SdrObjListIter aIter(*pObject); + + while(aIter.IsMore()) + { + SdrObject* pCandidate = aIter.Next(); + + if(pCandidate) + { + const bool bSuppressFullDrag(!pCandidate->supportsFullDrag()); + bool bAddWireframe(bSuppressFullDrag); + + if(!bAddWireframe && !pCandidate->HasLineStyle()) + { + // add wireframe for objects without outline + bAddWireframe = true; + } + + if(!bSuppressFullDrag) + { + // add full object drag; Clone() at the object has to work + // for this + createSdrDragEntryForSdrObject(*pCandidate); + } + + if(bAddWireframe) + { + // when dragging a 50% transparent copy of a filled or not filled object without + // outline, this is normally hard to see. Add extra wireframe in that case. This + // works nice e.g. with text frames etc. + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(pCandidate->TakeXorPoly()))); + } + } + } + } + } + } + } +} + +void SdrDragMethod::createSdrDragEntries_PolygonDrag() +{ + const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount()); + bool bNoPolygons(getSdrDragView().IsNoDragXorPolys() || nMarkCount > SdrDragView::GetDragXorPolyLimit()); + basegfx::B2DPolyPolygon aResult; + sal_uInt32 nPointCount(0); + + for(size_t a = 0; !bNoPolygons && a < nMarkCount; ++a) + { + SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a); + + if(pM->GetPageView() == getSdrDragView().GetSdrPageView()) + { + const basegfx::B2DPolyPolygon aNewPolyPolygon(pM->GetMarkedSdrObj()->TakeXorPoly()); + + for(auto const& rPolygon : aNewPolyPolygon) + { + nPointCount += rPolygon.count(); + } + + if(nPointCount > SdrDragView::GetDragXorPointLimit()) + { + bNoPolygons = true; + } + + if(!bNoPolygons) + { + aResult.append(aNewPolyPolygon); + } + } + } + + if(bNoPolygons) + { + const tools::Rectangle aR(getSdrDragView().GetSdrPageView()->MarkSnap()); + const basegfx::B2DRange aNewRectangle = vcl::unotools::b2DRectangleFromRectangle(aR); + basegfx::B2DPolygon aNewPolygon(basegfx::utils::createPolygonFromRect(aNewRectangle)); + + aResult = basegfx::B2DPolyPolygon(basegfx::utils::expandToCurve(aNewPolygon)); + } + + if(aResult.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aResult))); + } +} + +void SdrDragMethod::createSdrDragEntries_PointDrag() +{ + const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount()); + std::vector< basegfx::B2DPoint > aPositions; + + for(size_t nm = 0; nm < nMarkCount; ++nm) + { + SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm); + + if(pM->GetPageView() == getSdrDragView().GetSdrPageView()) + { + const SdrUShortCont& rPts = pM->GetMarkedPoints(); + + if (!rPts.empty()) + { + const SdrObject* pObj = pM->GetMarkedSdrObj(); + const SdrPathObj* pPath = dynamic_cast< const SdrPathObj* >(pObj); + + if(pPath) + { + const basegfx::B2DPolyPolygon& aPathXPP = pPath->GetPathPoly(); + + if(aPathXPP.count()) + { + for(const sal_uInt16 nObjPt : rPts) + { + sal_uInt32 nPolyNum, nPointNum; + + if(sdr::PolyPolygonEditor::GetRelativePolyPoint(aPathXPP, nObjPt, nPolyNum, nPointNum)) + { + aPositions.push_back(aPathXPP.getB2DPolygon(nPolyNum).getB2DPoint(nPointNum)); + } + } + } + } + } + } + } + + if(!aPositions.empty()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), true))); + } +} + +void SdrDragMethod::createSdrDragEntries_GlueDrag() +{ + const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount()); + std::vector< basegfx::B2DPoint > aPositions; + + for(size_t nm = 0; nm < nMarkCount; ++nm) + { + SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm); + + if(pM->GetPageView() == getSdrDragView().GetSdrPageView()) + { + const SdrUShortCont& rPts = pM->GetMarkedGluePoints(); + + if (!rPts.empty()) + { + const SdrObject* pObj = pM->GetMarkedSdrObj(); + const SdrGluePointList* pGPL = pObj->GetGluePointList(); + + if (pGPL) + { + for(const sal_uInt16 nObjPt : rPts) + { + const sal_uInt16 nGlueNum(pGPL->FindGluePoint(nObjPt)); + + if(SDRGLUEPOINT_NOTFOUND != nGlueNum) + { + const Point aPoint((*pGPL)[nGlueNum].GetAbsolutePos(*pObj)); + aPositions.emplace_back(aPoint.X(), aPoint.Y()); + } + } + } + } + } + } + + if(!aPositions.empty()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), false))); + } +} + +OUString SdrDragMethod::ImpGetDescriptionStr(TranslateId pStrCacheID) const +{ + ImpGetDescriptionOptions nOpt=ImpGetDescriptionOptions::NONE; + if (IsDraggingPoints()) { + nOpt=ImpGetDescriptionOptions::POINTS; + } else if (IsDraggingGluePoints()) { + nOpt=ImpGetDescriptionOptions::GLUEPOINTS; + } + return getSdrDragView().ImpGetDescriptionString(pStrCacheID, nOpt); +} + +SdrObject* SdrDragMethod::GetDragObj() const +{ + SdrObject* pObj=nullptr; + if (getSdrDragView().mpDragHdl!=nullptr) pObj=getSdrDragView().mpDragHdl->GetObj(); + if (pObj==nullptr) pObj=getSdrDragView().mpMarkedObj; + return pObj; +} + +SdrPageView* SdrDragMethod::GetDragPV() const +{ + SdrPageView* pPV=nullptr; + if (getSdrDragView().mpDragHdl!=nullptr) pPV=getSdrDragView().mpDragHdl->GetPageView(); + if (pPV==nullptr) pPV=getSdrDragView().mpMarkedPV; + return pPV; +} + +void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + // the original applies the transformation using TRGetBaseGeometry/TRSetBaseGeometry. + // Later this should be the only needed one for linear transforms (not for SdrDragCrook and + // SdrDragDistort, those are NOT linear). Currently, this can not yet be used since the + // special handling of rotate/mirror due to the not-being-able to handle it in the old + // drawinglayer stuff. Text would currently not correctly be mirrored in the preview. + basegfx::B2DHomMatrix aObjectTransform; + basegfx::B2DPolyPolygon aObjectPolyPolygon; + bool bPolyUsed(rTarget.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon)); + + // apply transform to object transform + aObjectTransform *= getCurrentTransformation(); + + if(bPolyUsed) + { + // do something special since the object size is in the polygon + // break up matrix to get the scale + const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aObjectTransform); + + // get polygon's position and size + const basegfx::B2DRange aPolyRange(aObjectPolyPolygon.getB2DRange()); + + // get the scaling factors (do not mirror, this is in the object transformation) + const double fScaleX(fabs(aTmpDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth())); + const double fScaleY(fabs(aTmpDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight())); + + // prepare transform matrix for polygon + basegfx::B2DHomMatrix aPolyTransform( + basegfx::utils::createTranslateB2DHomMatrix( + -aPolyRange.getMinX(), + -aPolyRange.getMinY())); + aPolyTransform.scale(fScaleX, fScaleY); + + // transform the polygon + aObjectPolyPolygon.transform(aPolyTransform); + } + + rTarget.TRSetBaseGeometry(getCurrentTransformation() * aObjectTransform, aObjectPolyPolygon); +} + +void SdrDragMethod::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) +{ + // original uses CurrentTransformation + rTarget.transform(getCurrentTransformation()); +} + +SdrDragMethod::SdrDragMethod(SdrDragView& rNewView) +: mrSdrDragView(rNewView), + mbMoveOnly(false), + mbSolidDraggingActive(getSdrDragView().IsSolidDragging()), + mbShiftPressed(false) +{ + if(mbSolidDraggingActive && Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + // fallback to wireframe when high contrast is used + mbSolidDraggingActive = false; + } +} + +SdrDragMethod::~SdrDragMethod() +{ + clearSdrDragEntries(); +} + +void SdrDragMethod::Show() +{ + getSdrDragView().ShowDragObj(); +} + +void SdrDragMethod::Hide() +{ + getSdrDragView().HideDragObj(); +} + +basegfx::B2DHomMatrix SdrDragMethod::getCurrentTransformation() const +{ + return basegfx::B2DHomMatrix(); +} + +void SdrDragMethod::CancelSdrDrag() +{ + Hide(); +} + +typedef std::map< const SdrObject*, SdrObject* > SdrObjectAndCloneMap; + +void SdrDragMethod::CreateOverlayGeometry( + sdr::overlay::OverlayManager& rOverlayManager, + const sdr::contact::ObjectContact& rObjectContact) +{ + // We do client-side object manipulation with the Kit API + if (comphelper::LibreOfficeKit::isActive()) + return; + + // create SdrDragEntries on demand + if(maSdrDragEntries.empty()) + { + createSdrDragEntries(); + } + + // if there are entries, derive OverlayObjects from the entries, including + // modification from current interactive state + if(!maSdrDragEntries.empty()) + { + // #i54102# SdrDragEntrySdrObject creates clones of SdrObjects as base for creating the needed + // primitives, holding the original and the clone. If connectors (Edges) are involved, + // the cloned connectors need to be connected to the cloned SdrObjects (after cloning + // they are connected to the original SdrObjects). To do so, trigger the preparation + // steps for SdrDragEntrySdrObject, save an association of (orig, clone) in a helper + // and evtl. remember if it was an edge + SdrObjectAndCloneMap aOriginalAndClones; + std::vector< SdrEdgeObj* > aEdges; + + // #i54102# execute prepareCurrentState for all SdrDragEntrySdrObject, register pair of original and + // clone, remember edges + for(auto const & a: maSdrDragEntries) + { + SdrDragEntrySdrObject* pSdrDragEntrySdrObject = dynamic_cast< SdrDragEntrySdrObject*>(a.get()); + + if(pSdrDragEntrySdrObject) + { + pSdrDragEntrySdrObject->prepareCurrentState(*this); + + SdrEdgeObj* pSdrEdgeObj = dynamic_cast< SdrEdgeObj* >(pSdrDragEntrySdrObject->getClone()); + + if(pSdrEdgeObj) + { + aEdges.push_back(pSdrEdgeObj); + } + + if(pSdrDragEntrySdrObject->getClone()) + { + aOriginalAndClones[&pSdrDragEntrySdrObject->getOriginal()] = pSdrDragEntrySdrObject->getClone(); + } + } + } + + // #i54102# if there are edges, reconnect their ends to the corresponding clones (if found) + for(SdrEdgeObj* pSdrEdgeObj: aEdges) + { + SdrObject* pConnectedTo = pSdrEdgeObj->GetConnectedNode(true); + + if(pConnectedTo) + { + SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo); + + if(aEntry != aOriginalAndClones.end()) + { + pSdrEdgeObj->ConnectToNode(true, aEntry->second); + } + } + + pConnectedTo = pSdrEdgeObj->GetConnectedNode(false); + + if(pConnectedTo) + { + SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo); + + if(aEntry != aOriginalAndClones.end()) + { + pSdrEdgeObj->ConnectToNode(false, aEntry->second); + } + } + } + + // collect primitives for visualisation + drawinglayer::primitive2d::Primitive2DContainer aResult; + drawinglayer::primitive2d::Primitive2DContainer aResultTransparent; + + for(auto & pCandidate: maSdrDragEntries) + { + const drawinglayer::primitive2d::Primitive2DContainer aCandidateResult(pCandidate->createPrimitive2DSequenceInCurrentState(*this)); + + if(!aCandidateResult.empty()) + { + if(pCandidate->getAddToTransparent()) + { + aResultTransparent.append(aCandidateResult); + } + else + { + aResult.append(aCandidateResult); + } + } + } + + if(DoAddConnectorOverlays()) + { + const drawinglayer::primitive2d::Primitive2DContainer aConnectorOverlays(AddConnectorOverlays()); + + if(!aConnectorOverlays.empty()) + { + // add connector overlays to transparent part + aResultTransparent.append(aConnectorOverlays); + } + } + + if(!aResult.empty()) + { + std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject( + new sdr::overlay::OverlayPrimitive2DSequenceObject( + std::move(aResult))); + + insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::move(pNewOverlayObject), + rObjectContact, + rOverlayManager); + } + + if(!aResultTransparent.empty()) + { + drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aResultTransparent), 0.5)); + aResultTransparent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D }; + + std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject( + new sdr::overlay::OverlayPrimitive2DSequenceObject( + std::move(aResultTransparent))); + + insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::move(pNewOverlayObject), + rObjectContact, + rOverlayManager); + } + } + + // add DragStripes if necessary (help lines cross the page when dragging) + if(!getSdrDragView().IsDragStripes()) + return; + + tools::Rectangle aActionRectangle; + getSdrDragView().TakeActionRect(aActionRectangle); + + const basegfx::B2DPoint aTopLeft(aActionRectangle.Left(), aActionRectangle.Top()); + const basegfx::B2DPoint aBottomRight(aActionRectangle.Right(), aActionRectangle.Bottom()); + std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew( + new sdr::overlay::OverlayRollingRectangleStriped( + aTopLeft, + aBottomRight, + true, + false)); + + insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::move(pNew), + rObjectContact, + rOverlayManager); +} + +void SdrDragMethod::destroyOverlayGeometry() +{ + maOverlayObjectList.clear(); +} + +bool SdrDragMethod::DoAddConnectorOverlays() +{ + // these conditions are translated from SdrDragView::ImpDrawEdgeXor + const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes(); + + if(!rMarkedNodes.GetMarkCount()) + { + return false; + } + + if(getSdrDragView().IsDraggingPoints() || getSdrDragView().IsDraggingGluePoints()) + { + return false; + } + + if(!getMoveOnly() && !( + dynamic_cast<const SdrDragMove*>(this) != nullptr || dynamic_cast<const SdrDragResize*>(this) != nullptr || + dynamic_cast<const SdrDragRotate*>(this) != nullptr || dynamic_cast<const SdrDragMirror*>(this) != nullptr )) + { + return false; + } + + // one more migrated from SdrEdgeObj::NspToggleEdgeXor + if( dynamic_cast< const SdrDragObjOwn* >(this) != nullptr || dynamic_cast< const SdrDragMovHdl* >(this) != nullptr ) + { + return false; + } + + return true; +} + +drawinglayer::primitive2d::Primitive2DContainer SdrDragMethod::AddConnectorOverlays() +{ + drawinglayer::primitive2d::Primitive2DContainer aRetval; + const bool bDetail(getMoveOnly()); + const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes(); + + for(size_t a = 0; a < rMarkedNodes.GetMarkCount(); ++a) + { + SdrMark* pEM = rMarkedNodes.GetMark(a); + + if(pEM && pEM->GetMarkedSdrObj()) + { + SdrEdgeObj* pEdge = dynamic_cast< SdrEdgeObj* >(pEM->GetMarkedSdrObj()); + + if(pEdge) + { + const basegfx::B2DPolygon aEdgePolygon(pEdge->ImplAddConnectorOverlay(*this, pEM->IsCon1(), pEM->IsCon2(), bDetail)); + + if(aEdgePolygon.count()) + { + // this polygon is a temporary calculated connector path, so it is not possible to fetch + // the needed primitives directly from the pEdge object which does not get changed. If full + // drag is on, use the SdrObjects ItemSet to create an adequate representation + bool bUseSolidDragging(getSolidDraggingActive()); + + if(bUseSolidDragging) + { + // switch off solid dragging if connector is not visible + if(!pEdge->HasLineStyle()) + { + bUseSolidDragging = false; + } + } + + if(bUseSolidDragging) + { + const SfxItemSet& rItemSet = pEdge->GetMergedItemSet(); + const drawinglayer::attribute::SdrLineAttribute aLine( + drawinglayer::primitive2d::createNewSdrLineAttribute(rItemSet)); + + if(!aLine.isDefault()) + { + const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd( + drawinglayer::primitive2d::createNewSdrLineStartEndAttribute( + rItemSet, + aLine.getWidth())); + + aRetval.push_back(drawinglayer::primitive2d::createPolygonLinePrimitive( + aEdgePolygon, + aLine, + aLineStartEnd)); + } + } + else + { + basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor()); + basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor()); + const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength()); + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + aColB.invert(); + } + + drawinglayer::primitive2d::Primitive2DReference aPolyPolygonMarkerPrimitive2D( + new drawinglayer::primitive2d::PolygonMarkerPrimitive2D( + aEdgePolygon, aColA, aColB, fStripeLength)); + aRetval.push_back(aPolyPolygonMarkerPrimitive2D); + } + } + } + } + } + + return aRetval; +} + + +SdrDragMovHdl::SdrDragMovHdl(SdrDragView& rNewView) +: SdrDragMethod(rNewView) +{ +} + +void SdrDragMovHdl::createSdrDragEntries() +{ + // SdrDragMovHdl does not use the default drags, + // but creates nothing +} + +OUString SdrDragMovHdl::GetSdrDragComment() const +{ + OUString aStr=SvxResId(STR_DragMethMovHdl); + if (getSdrDragView().IsDragWithCopy()) aStr+=SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragMovHdl::BeginSdrDrag() +{ + if( !GetDragHdl() ) + return false; + + DragStat().SetRef1(GetDragHdl()->GetPos()); + DragStat().SetShown(!DragStat().IsShown()); + SdrHdlKind eKind=GetDragHdl()->GetKind(); + SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1); + SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2); + + if (eKind==SdrHdlKind::MirrorAxis) + { + if (pH1==nullptr || pH2==nullptr) + { + OSL_FAIL("SdrDragMovHdl::BeginSdrDrag(): Moving the axis of reflection: reference handles not found."); + return false; + } + + DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos())); + } + else + { + Point aPt(GetDragHdl()->GetPos()); + DragStat().SetActionRect(tools::Rectangle(aPt,aPt)); + } + + return true; +} + +void SdrDragMovHdl::MoveSdrDrag(const Point& rNoSnapPnt) +{ + Point aPnt(rNoSnapPnt); + + if ( !(GetDragHdl() && DragStat().CheckMinMoved(rNoSnapPnt))) + return; + + if (GetDragHdl()->GetKind()==SdrHdlKind::MirrorAxis) + { + SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1); + SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2); + + if (pH1==nullptr || pH2==nullptr) + return; + + if (!DragStat().IsNoSnap()) + { + tools::Long nBestXSnap=0; + tools::Long nBestYSnap=0; + bool bXSnapped=false; + bool bYSnapped=false; + Point aDif(aPnt-DragStat().GetStart()); + getSdrDragView().CheckSnap(Ref1()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped); + getSdrDragView().CheckSnap(Ref2()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped); + aPnt.AdjustX(nBestXSnap ); + aPnt.AdjustY(nBestYSnap ); + } + + if (aPnt!=DragStat().GetNow()) + { + Hide(); + DragStat().NextMove(aPnt); + Point aDif(DragStat().GetNow()-DragStat().GetStart()); + pH1->SetPos(Ref1()+aDif); + pH2->SetPos(Ref2()+aDif); + + SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis); + + if(pHM) + pHM->Touch(); + + Show(); + DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos())); + } + } + else + { + if (!DragStat().IsNoSnap()) SnapPos(aPnt); + Degree100 nSA(0); + + if (getSdrDragView().IsAngleSnapEnabled()) + nSA=getSdrDragView().GetSnapAngle(); + + if (getSdrDragView().IsMirrorAllowed(true,true)) + { // limited + if (!getSdrDragView().IsMirrorAllowed()) nSA=4500_deg100; + if (!getSdrDragView().IsMirrorAllowed(true)) nSA=9000_deg100; + } + + if (getSdrDragView().IsOrtho() && nSA!=9000_deg100) + nSA=4500_deg100; + + if (nSA) + { // angle snapping + SdrHdlKind eRef=SdrHdlKind::Ref1; + + if (GetDragHdl()->GetKind()==SdrHdlKind::Ref1) + eRef=SdrHdlKind::Ref2; + + SdrHdl* pH=GetHdlList().GetHdl(eRef); + + if (pH!=nullptr) + { + Point aRef(pH->GetPos()); + Degree100 nAngle=NormAngle36000(GetAngle(aPnt-aRef)); + Degree100 nNewAngle=nAngle; + nNewAngle+=nSA/2_deg100; + nNewAngle/=nSA; + nNewAngle*=nSA; + nNewAngle=NormAngle36000(nNewAngle); + double a=toRadians(nNewAngle-nAngle); + double nSin=sin(a); + double nCos=cos(a); + RotatePoint(aPnt,aRef,nSin,nCos); + + // eliminate rounding errors for certain values + if (nSA==9000_deg100) + { + if (nNewAngle==0_deg100 || nNewAngle==18000_deg100) aPnt.setY(aRef.Y() ); + if (nNewAngle==9000_deg100 || nNewAngle==27000_deg100) aPnt.setX(aRef.X() ); + } + + if (nSA==4500_deg100) + OrthoDistance8(aRef,aPnt,true); + } + } + + if (aPnt!=DragStat().GetNow()) + { + Hide(); + DragStat().NextMove(aPnt); + GetDragHdl()->SetPos(DragStat().GetNow()); + SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis); + + if(pHM) + pHM->Touch(); + + Show(); + DragStat().SetActionRect(tools::Rectangle(aPnt,aPnt)); + } + } +} + +bool SdrDragMovHdl::EndSdrDrag(bool /*bCopy*/) +{ + if( GetDragHdl() ) + { + switch (GetDragHdl()->GetKind()) + { + case SdrHdlKind::Ref1: + Ref1()=DragStat().GetNow(); + break; + + case SdrHdlKind::Ref2: + Ref2()=DragStat().GetNow(); + break; + + case SdrHdlKind::MirrorAxis: + Ref1()+=DragStat().GetNow()-DragStat().GetStart(); + Ref2()+=DragStat().GetNow()-DragStat().GetStart(); + break; + + default: break; + } + } + + return true; +} + +void SdrDragMovHdl::CancelSdrDrag() +{ + Hide(); + + SdrHdl* pHdl = GetDragHdl(); + if( pHdl ) + pHdl->SetPos(DragStat().GetRef1()); + + SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis); + + if(pHM) + pHM->Touch(); +} + +PointerStyle SdrDragMovHdl::GetSdrDragPointer() const +{ + const SdrHdl* pHdl = GetDragHdl(); + + if (pHdl!=nullptr) + { + return pHdl->GetPointer(); + } + + return PointerStyle::RefHand; +} + + +SdrDragObjOwn::SdrDragObjOwn(SdrDragView& rNewView) +: SdrDragMethod(rNewView) +{ + const SdrObject* pObj = GetDragObj(); + + if(pObj) + { + // suppress full drag for some object types + setSolidDraggingActive(pObj->supportsFullDrag()); + } +} + +SdrDragObjOwn::~SdrDragObjOwn() +{ +} + +void SdrDragObjOwn::createSdrDragEntries() +{ + if(!mxClone) + return; + + basegfx::B2DPolyPolygon aDragPolyPolygon; + bool bAddWireframe(true); + + if(getSolidDraggingActive()) + { + SdrPageView* pPV = getSdrDragView().GetSdrPageView(); + + if(pPV && pPV->PageWindowCount()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(*mxClone, false))); + + // potentially no wireframe needed, full drag works + bAddWireframe = false; + } + } + + if(!bAddWireframe) + { + // check for extra conditions for wireframe, e.g. no border at + // objects + if(!mxClone->HasLineStyle()) + { + bAddWireframe = true; + } + } + + if(bAddWireframe) + { + // use wireframe poly when full drag is off or did not work + aDragPolyPolygon = mxClone->TakeXorPoly(); + } + + // add evtl. extra DragPolyPolygon + const basegfx::B2DPolyPolygon aSpecialDragPolyPolygon(mxClone->getSpecialDragPoly(DragStat())); + + if(aSpecialDragPolyPolygon.count()) + { + aDragPolyPolygon.append(aSpecialDragPolyPolygon); + } + + if(aDragPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragPolyPolygon))); + } +} + +OUString SdrDragObjOwn::GetSdrDragComment() const +{ + OUString aStr; + // #i103058# get info string from the clone preferred, the original will + // not be changed. For security, use original as fallback + if(mxClone) + { + aStr = mxClone->getSpecialDragComment(DragStat()); + } + else + { + const SdrObject* pObj = GetDragObj(); + + if(pObj) + { + aStr = pObj->getSpecialDragComment(DragStat()); + } + } + return aStr; +} + +bool SdrDragObjOwn::BeginSdrDrag() +{ + if(!mxClone) + { + const SdrObject* pObj = GetDragObj(); + + if(pObj && !pObj->IsResizeProtect()) + { + if(pObj->beginSpecialDrag(DragStat())) + { + // create initial clone to have a start visualization + mxClone = pObj->getFullDragClone(); + mxClone->applySpecialDrag(DragStat()); + + return true; + } + } + } + + return false; +} + +void SdrDragObjOwn::MoveSdrDrag(const Point& rNoSnapPnt) +{ + const SdrObject* pObj = GetDragObj(); + + if (!pObj) + // No object to drag. Bail out. + return; + + Point aPnt(rNoSnapPnt); + SdrPageView* pPV = GetDragPV(); + + if (!pPV) + // No page view available. Bail out. + return; + + if(!DragStat().IsNoSnap()) + { + SnapPos(aPnt); + } + if(getSdrDragView().IsOrtho()) + { + if (DragStat().IsOrtho8Possible()) + { + OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho()); + } + else if (DragStat().IsOrtho4Possible()) + { + OrthoDistance4(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho()); + } + } + + if (!DragStat().CheckMinMoved(rNoSnapPnt)) + // Not moved by the minimum threshold. Nothing to do. + return; + + Hide(); + DragStat().NextMove(aPnt); + + // since SdrDragObjOwn currently supports no transformation of + // existing SdrDragEntries but only their recreation, a recreation + // after every move is needed in this mode. Delete existing + // SdrDragEntries here to force their recreation in the following Show(). + clearSdrDragEntries(); + + // delete current clone (after the last reference to it is deleted above) + mxClone.reset(); + + // create a new clone and modify to current drag state + mxClone = pObj->getFullDragClone(); + mxClone->applySpecialDrag(DragStat()); + + // AutoGrowWidth may change for SdrTextObj due to the automatism used + // with bDisableAutoWidthOnDragging, so not only geometry changes but + // also this (pretty indirect) property change is possible. If it gets + // changed, it needs to be copied to the original since nothing will + // happen when it only changes in the drag clone + const bool bOldAutoGrowWidth(pObj->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue()); + const bool bNewAutoGrowWidth(mxClone->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue()); + + if (bOldAutoGrowWidth != bNewAutoGrowWidth) + { + GetDragObj()->SetMergedItem(makeSdrTextAutoGrowWidthItem(bNewAutoGrowWidth)); + } + + Show(); +} + +bool SdrDragObjOwn::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + std::vector< std::unique_ptr<SdrUndoAction> > vConnectorUndoActions; + bool bRet = false; + SdrObject* pObj = GetDragObj(); + + if(pObj) + { + std::unique_ptr<SdrUndoAction> pUndo; + std::unique_ptr<SdrUndoAction> pUndo2; + const bool bUndo = getSdrDragView().IsUndoEnabled(); + + if( bUndo ) + { + getSdrDragView().EndTextEditCurrentView(); + if(!getSdrDragView().IsInsObjPoint() && pObj->IsInserted() ) + { + if (DragStat().IsEndDragChangesAttributes()) + { + pUndo=getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj); + + if (DragStat().IsEndDragChangesGeoAndAttributes()) + { + vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj ); + pUndo2 = getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj); + } + } + else + { + vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj ); + pUndo= getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj); + } + } + + if( pUndo ) + { + getSdrDragView().BegUndo( pUndo->GetComment() ); + } + else + { + getSdrDragView().BegUndo(); + } + } + + // Maybe use operator = for setting changed object data (do not change selection in + // view, this will destroy the interactor). This is possible since a clone is now + // directly modified by the modifiers. Only SdrTableObj is adding own UNDOs + // in its SdrTableObj::endSpecialDrag, so currently not possible. OTOH it uses + // a CreateUndoGeoObject(), so maybe setting SetEndDragChangesAttributes is okay. I + // will test this now + tools::Rectangle aBoundRect0; + + if(pObj->GetUserCall()) + { + aBoundRect0 = pObj->GetLastBoundRect(); + } + + bRet = pObj->applySpecialDrag(DragStat()); + if (DragStat().IsEndDragChangesLayout()) + { + auto pGeoUndo = dynamic_cast<SdrUndoGeoObj*>(pUndo.get()); + if (pGeoUndo) + pGeoUndo->SetSkipChangeLayout(true); + } + + if(bRet) + { + pObj->SetChanged(); + pObj->BroadcastObjectChange(); + pObj->SendUserCall( SdrUserCallType::Resize, aBoundRect0 ); + } + + if(bRet && bUndo ) + { + getSdrDragView().AddUndoActions( std::move(vConnectorUndoActions) ); + + if ( pUndo ) + { + getSdrDragView().AddUndo(std::move(pUndo)); + } + + if ( pUndo2 ) + { + getSdrDragView().AddUndo(std::move(pUndo2)); + } + } + + if( bUndo ) + getSdrDragView().EndUndo(); + } + + return bRet; +} + +PointerStyle SdrDragObjOwn::GetSdrDragPointer() const +{ + const SdrHdl* pHdl=GetDragHdl(); + + if (pHdl) + { + return pHdl->GetPointer(); + } + + return PointerStyle::Move; +} + + +void SdrDragMove::createSdrDragEntryForSdrObject(const SdrObject& rOriginal) +{ + // use the view-independent primitive representation (without + // evtl. GridOffset, that may be applied to the DragEntry individually) + drawinglayer::primitive2d::Primitive2DContainer xRetval; + rOriginal.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval); + addSdrDragEntry( + std::unique_ptr<SdrDragEntry>( + new SdrDragEntryPrimitive2DSequence( + std::move(xRetval)))); + +} + +void SdrDragMove::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + rTarget.Move(Size(DragStat().GetDX(), DragStat().GetDY())); +} + +SdrDragMove::SdrDragMove(SdrDragView& rNewView) + : SdrDragMethod(rNewView) + , nBestXSnap(0) + , nBestYSnap(0) + , bXSnapped(false) + , bYSnapped(false) +{ + setMoveOnly(true); +} + +OUString SdrDragMove::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethMove) + + " (x=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX()) + + " y=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY()) + + ")"; + + if(getSdrDragView().IsDragWithCopy()) + { + if(!getSdrDragView().IsInsObjPoint() && !getSdrDragView().IsInsGluePoint()) + { + aStr += SvxResId(STR_EditWithCopy); + } + } + return aStr; +} + +bool SdrDragMove::BeginSdrDrag() +{ + DragStat().SetActionRect(GetMarkedRect()); + Show(); + + return true; +} + +basegfx::B2DHomMatrix SdrDragMove::getCurrentTransformation() const +{ + return basegfx::utils::createTranslateB2DHomMatrix(DragStat().GetDX(), DragStat().GetDY()); +} + +void SdrDragMove::ImpCheckSnap(const Point& rPt) +{ + Point aPt(rPt); + SdrSnap nRet=SnapPos(aPt); + aPt-=rPt; + + if (nRet & SdrSnap::XSNAPPED) + { + if (bXSnapped) + { + if (std::abs(aPt.X())<std::abs(nBestXSnap)) + { + nBestXSnap=aPt.X(); + } + } + else + { + nBestXSnap=aPt.X(); + bXSnapped=true; + } + } + + if (!(nRet & SdrSnap::YSNAPPED)) + return; + + if (bYSnapped) + { + if (std::abs(aPt.Y())<std::abs(nBestYSnap)) + { + nBestYSnap=aPt.Y(); + } + } + else + { + nBestYSnap=aPt.Y(); + bYSnapped=true; + } +} + +void SdrDragMove::MoveSdrDrag(const Point& rNoSnapPnt_) +{ + nBestXSnap=0; + nBestYSnap=0; + bXSnapped=false; + bYSnapped=false; + Point aNoSnapPnt(rNoSnapPnt_); + const tools::Rectangle& aSR=GetMarkedRect(); + tools::Long nMovedx=aNoSnapPnt.X()-DragStat().GetStart().X(); + tools::Long nMovedy=aNoSnapPnt.Y()-DragStat().GetStart().Y(); + Point aLO(aSR.TopLeft()); aLO.AdjustX(nMovedx ); aLO.AdjustY(nMovedy ); + Point aRU(aSR.BottomRight()); aRU.AdjustX(nMovedx ); aRU.AdjustY(nMovedy ); + Point aLU(aLO.X(),aRU.Y()); + Point aRO(aRU.X(),aLO.Y()); + ImpCheckSnap(aLO); + + if (!getSdrDragView().IsMoveSnapOnlyTopLeft()) + { + ImpCheckSnap(aRO); + ImpCheckSnap(aLU); + ImpCheckSnap(aRU); + } + + Point aPnt(aNoSnapPnt.X()+nBestXSnap,aNoSnapPnt.Y()+nBestYSnap); + bool bOrtho=getSdrDragView().IsOrtho(); + + if (bOrtho) + OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho()); + + if (!DragStat().CheckMinMoved(aNoSnapPnt)) + return; + + Point aPt1(aPnt); + tools::Rectangle aLR(getSdrDragView().GetWorkArea()); + bool bWorkArea=!aLR.IsEmpty(); + bool bDragLimit=IsDragLimit(); + + if (bDragLimit || bWorkArea) + { + tools::Rectangle aSR2(GetMarkedRect()); + Point aD(aPt1-DragStat().GetStart()); + + if (bDragLimit) + { + tools::Rectangle aR2(GetDragLimitRect()); + + if (bWorkArea) + aLR.Intersection(aR2); + else + aLR=aR2; + } + + if (aSR2.Left()>aLR.Left() || aSR2.Right()<aLR.Right()) + { // any space to move to? + aSR2.Move(aD.X(),0); + + if (aSR2.Left()<aLR.Left()) + { + aPt1.AdjustX( -(aSR2.Left()-aLR.Left()) ); + } + else if (aSR2.Right()>aLR.Right()) + { + aPt1.AdjustX( -(aSR2.Right()-aLR.Right()) ); + } + } + else + aPt1.setX(DragStat().GetStart().X() ); // no space to move to + + if (aSR2.Top()>aLR.Top() || aSR2.Bottom()<aLR.Bottom()) + { // any space to move to? + aSR2.Move(0,aD.Y()); + + if (aSR2.Top()<aLR.Top()) + { + aPt1.AdjustY( -(aSR2.Top()-aLR.Top()) ); + } + else if (aSR2.Bottom()>aLR.Bottom()) + { + aPt1.AdjustY( -(aSR2.Bottom()-aLR.Bottom()) ); + } + } + else + aPt1.setY(DragStat().GetStart().Y() ); // no space to move to + } + + if (getSdrDragView().IsDraggingGluePoints()) + { // restrict gluepoints to the BoundRect of the Obj + aPt1-=DragStat().GetStart(); + const SdrMarkList& rML=GetMarkedObjectList(); + const size_t nMarkCount=rML.GetMarkCount(); + + for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) + { + const SdrMark* pM=rML.GetMark(nMarkNum); + const SdrUShortCont& rPts = pM->GetMarkedGluePoints(); + + if (!rPts.empty()) + { + const SdrObject* pObj=pM->GetMarkedSdrObj(); + const SdrGluePointList* pGPL=pObj->GetGluePointList(); + tools::Rectangle aBound(pObj->GetCurrentBoundRect()); + + for (sal_uInt16 nId : rPts) + { + sal_uInt16 nGlueNum=pGPL->FindGluePoint(nId); + + if (nGlueNum!=SDRGLUEPOINT_NOTFOUND) + { + Point aPt((*pGPL)[nGlueNum].GetAbsolutePos(*pObj)); + aPt+=aPt1; // move by this much + if (aPt.X()<aBound.Left() ) aPt1.AdjustX( -(aPt.X()-aBound.Left()) ) ; + if (aPt.X()>aBound.Right() ) aPt1.AdjustX( -(aPt.X()-aBound.Right()) ) ; + if (aPt.Y()<aBound.Top() ) aPt1.AdjustY( -(aPt.Y()-aBound.Top()) ) ; + if (aPt.Y()>aBound.Bottom()) aPt1.AdjustY( -(aPt.Y()-aBound.Bottom()) ); + } + } + } + } + + aPt1+=DragStat().GetStart(); + } + + if (bOrtho) + OrthoDistance8(DragStat().GetStart(),aPt1,false); + + if (aPt1!=DragStat().GetNow()) + { + Hide(); + DragStat().NextMove(aPt1); + tools::Rectangle aAction(GetMarkedRect()); + aAction.Move(DragStat().GetDX(),DragStat().GetDY()); + DragStat().SetActionRect(aAction); + Show(); + } +} + +bool SdrDragMove::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (getSdrDragView().IsInsObjPoint() || getSdrDragView().IsInsGluePoint()) + bCopy=false; + + if (IsDraggingPoints()) + { + getSdrDragView().MoveMarkedPoints(Size(DragStat().GetDX(),DragStat().GetDY())); + } + else if (IsDraggingGluePoints()) + { + getSdrDragView().MoveMarkedGluePoints(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy); + } + else + { + getSdrDragView().MoveMarkedObj(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy); + } + + return true; +} + +PointerStyle SdrDragMove::GetSdrDragPointer() const +{ + if (IsDraggingPoints() || IsDraggingGluePoints()) + { + return PointerStyle::MovePoint; + } + else + { + return PointerStyle::Move; + } +} + + +SdrDragResize::SdrDragResize(SdrDragView& rNewView) +: SdrDragMethod(rNewView), + aXFact(1,1), + aYFact(1,1) +{ +} + +OUString SdrDragResize::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethResize); + Fraction aFact1(1,1); + Point aStart(DragStat().GetStart()); + Point aRef(DragStat().GetRef1()); + sal_Int32 nXDiv(aStart.X() - aRef.X()); + + if(!nXDiv) + nXDiv = 1; + + sal_Int32 nYDiv(aStart.Y() - aRef.Y()); + + if(!nYDiv) + nYDiv = 1; + + bool bX(aXFact != aFact1 && std::abs(nXDiv) > 1); + bool bY(aYFact != aFact1 && std::abs(nYDiv) > 1); + + if(bX || bY) + { + aStr += " ("; + + bool bEqual(aXFact == aYFact); + if(bX) + { + if(!bEqual) + aStr += "x="; + + aStr += SdrModel::GetPercentString(aXFact); + } + + if(bY && !bEqual) + { + if(bX) + aStr += " "; + + aStr += "y=" + SdrModel::GetPercentString(aYFact); + } + + aStr += ")"; + } + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragResize::BeginSdrDrag() +{ + SdrHdlKind eRefHdl=SdrHdlKind::Move; + SdrHdl* pRefHdl=nullptr; + + switch (GetDragHdlKind()) + { + case SdrHdlKind::UpperLeft: eRefHdl=SdrHdlKind::LowerRight; break; + case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; DragStat().SetHorFixed(true); break; + case SdrHdlKind::UpperRight: eRefHdl=SdrHdlKind::LowerLeft; break; + case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; DragStat().SetVerFixed(true); break; + case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; DragStat().SetVerFixed(true); break; + case SdrHdlKind::LowerLeft: eRefHdl=SdrHdlKind::UpperRight; break; + case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; DragStat().SetHorFixed(true); break; + case SdrHdlKind::LowerRight: eRefHdl=SdrHdlKind::UpperLeft; break; + default: break; + } + + if (eRefHdl!=SdrHdlKind::Move) + pRefHdl=GetHdlList().GetHdl(eRefHdl); + + if (pRefHdl!=nullptr && !getSdrDragView().IsResizeAtCenter()) + { + DragStat().SetRef1(pRefHdl->GetPos()); + } + else + { + SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft); + SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight); + + if (pRef1!=nullptr && pRef2!=nullptr) + { + DragStat().SetRef1(tools::Rectangle(pRef1->GetPos(),pRef2->GetPos()).Center()); + } + else + { + DragStat().SetRef1(GetMarkedRect().Center()); + } + } + + Show(); + + return true; +} + +basegfx::B2DHomMatrix SdrDragResize::getCurrentTransformation() const +{ + basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix( + -DragStat().GetRef1().X(), -DragStat().GetRef1().Y())); + aRetval.scale(double(aXFact), double(aYFact)); + aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y()); + + return aRetval; +} + +void SdrDragResize::MoveSdrDrag(const Point& rNoSnapPnt) +{ + Point aPnt(GetSnapPos(rNoSnapPnt)); + Point aStart(DragStat().GetStart()); + Point aRef(DragStat().GetRef1()); + Fraction aMaxFact(0x7FFFFFFF,1); + tools::Rectangle aLR(getSdrDragView().GetWorkArea()); + bool bWorkArea=!aLR.IsEmpty(); + bool bDragLimit=IsDragLimit(); + + if (bDragLimit || bWorkArea) + { + tools::Rectangle aSR(GetMarkedRect()); + + if (bDragLimit) + { + tools::Rectangle aR2(GetDragLimitRect()); + + if (bWorkArea) + aLR.Intersection(aR2); + else + aLR=aR2; + } + + if (aPnt.X()<aLR.Left()) + aPnt.setX(aLR.Left() ); + else if (aPnt.X()>aLR.Right()) + aPnt.setX(aLR.Right() ); + + if (aPnt.Y()<aLR.Top()) + aPnt.setY(aLR.Top() ); + else if (aPnt.Y()>aLR.Bottom()) + aPnt.setY(aLR.Bottom() ); + + if (aRef.X()>aSR.Left()) + { + Fraction aMax(aRef.X()-aLR.Left(),aRef.X()-aSR.Left()); + + if (aMax<aMaxFact) + aMaxFact=aMax; + } + + if (aRef.X()<aSR.Right()) + { + Fraction aMax(aLR.Right()-aRef.X(),aSR.Right()-aRef.X()); + + if (aMax<aMaxFact) + aMaxFact=aMax; + } + + if (aRef.Y()>aSR.Top()) + { + Fraction aMax(aRef.Y()-aLR.Top(),aRef.Y()-aSR.Top()); + + if (aMax<aMaxFact) + aMaxFact=aMax; + } + + if (aRef.Y()<aSR.Bottom()) + { + Fraction aMax(aLR.Bottom()-aRef.Y(),aSR.Bottom()-aRef.Y()); + + if (aMax<aMaxFact) + aMaxFact=aMax; + } + } + + tools::Long nXDiv=aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1; + tools::Long nYDiv=aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1; + tools::Long nXMul=aPnt.X()-aRef.X(); + tools::Long nYMul=aPnt.Y()-aRef.Y(); + + if (nXDiv<0) + { + nXDiv=-nXDiv; + nXMul=-nXMul; + } + + if (nYDiv<0) + { + nYDiv=-nYDiv; + nYMul=-nYMul; + } + + bool bXNeg=nXMul<0; if (bXNeg) nXMul=-nXMul; + bool bYNeg=nYMul<0; if (bYNeg) nYMul=-nYMul; + bool bOrtho=getSdrDragView().IsOrtho() || !getSdrDragView().IsResizeAllowed(); + + if (!DragStat().IsHorFixed() && !DragStat().IsVerFixed()) + { + if (std::abs(nXDiv)<=1 || std::abs(nYDiv)<=1) + bOrtho=false; + + if (bOrtho) + { + if ((Fraction(nXMul,nXDiv)>Fraction(nYMul,nYDiv)) !=getSdrDragView().IsBigOrtho()) + { + nXMul=nYMul; + nXDiv=nYDiv; + } + else + { + nYMul=nXMul; + nYDiv=nXDiv; + } + } + } + else + { + if (bOrtho) + { + if (DragStat().IsHorFixed()) + { + bXNeg=false; + nXMul=nYMul; + nXDiv=nYDiv; + } + + if (DragStat().IsVerFixed()) + { + bYNeg=false; + nYMul=nXMul; + nYDiv=nXDiv; + } + } + else + { + if (DragStat().IsHorFixed()) + { + bXNeg=false; + nXMul=1; + nXDiv=1; + } + + if (DragStat().IsVerFixed()) + { + bYNeg=false; + nYMul=1; + nYDiv=1; + } + } + } + + Fraction aNewXFact(nXMul,nXDiv); + Fraction aNewYFact(nYMul,nYDiv); + + if (bOrtho) + { + if (aNewXFact>aMaxFact) + { + aNewXFact=aMaxFact; + aNewYFact=aMaxFact; + } + + if (aNewYFact>aMaxFact) + { + aNewXFact=aMaxFact; + aNewYFact=aMaxFact; + } + } + + if (bXNeg) + aNewXFact=Fraction(-aNewXFact.GetNumerator(),aNewXFact.GetDenominator()); + + if (bYNeg) + aNewYFact=Fraction(-aNewYFact.GetNumerator(),aNewYFact.GetDenominator()); + + if (DragStat().CheckMinMoved(aPnt)) + { + if ((!DragStat().IsHorFixed() && aPnt.X()!=DragStat().GetNow().X()) || + (!DragStat().IsVerFixed() && aPnt.Y()!=DragStat().GetNow().Y())) + { + Hide(); + DragStat().NextMove(aPnt); + aXFact=aNewXFact; + aYFact=aNewYFact; + Show(); + } + } +} + +void SdrDragResize::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + rTarget.Resize(DragStat().GetRef1(),aXFact,aYFact); +} + +bool SdrDragResize::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (IsDraggingPoints()) + { + getSdrDragView().ResizeMarkedPoints(DragStat().GetRef1(),aXFact,aYFact); + } + else if (IsDraggingGluePoints()) + { + getSdrDragView().ResizeMarkedGluePoints(DragStat().GetRef1(),aXFact,aYFact,bCopy); + } + else + { + getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aXFact,aYFact,bCopy); + } + + return true; +} + +PointerStyle SdrDragResize::GetSdrDragPointer() const +{ + const SdrHdl* pHdl=GetDragHdl(); + + if (pHdl!=nullptr) + { + return pHdl->GetPointer(); + } + + return PointerStyle::Move; +} + + +void SdrDragRotate::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + rTarget.Rotate(DragStat().GetRef1(), nAngle, nSin, nCos); +} + +SdrDragRotate::SdrDragRotate(SdrDragView& rNewView) +: SdrDragMethod(rNewView), + nSin(0.0), + nCos(1.0), + nAngle0(0), + nAngle(0), + bRight(false) +{ +} + +OUString SdrDragRotate::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethRotate) + + " ("; + Degree100 nTmpAngle(NormAngle36000(nAngle)); + + if(bRight && nAngle) + { + nTmpAngle -= 36000_deg100; + } + + aStr += SdrModel::GetAngleString(nTmpAngle) + ")"; + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragRotate::BeginSdrDrag() +{ + SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1); + + if (nullptr != pH) + { + Show(); + DragStat().SetRef1(pH->GetPos()); + nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1()); + return true; + } + + // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally + // the rotation point) + const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect()); + + if(!aLocalMarkRect.IsEmpty()) + { + Show(); + DragStat().SetRef1(aLocalMarkRect.Center()); + nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1()); + return true; + } + + OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found."); + return false; +} + +basegfx::B2DHomMatrix SdrDragRotate::getCurrentTransformation() const +{ + return basegfx::utils::createRotateAroundPoint( + DragStat().GetRef1().X(), DragStat().GetRef1().Y(), + -atan2(nSin, nCos)); +} + +void SdrDragRotate::MoveSdrDrag(const Point& rPnt_) +{ + Point aPnt(rPnt_); + if (!DragStat().CheckMinMoved(aPnt)) + return; + + Degree100 nNewAngle=NormAngle36000(GetAngle(aPnt-DragStat().GetRef1())-nAngle0); + Degree100 nSA(0); + + if (getSdrDragView().IsAngleSnapEnabled()) + nSA=getSdrDragView().GetSnapAngle(); + + if (!getSdrDragView().IsRotateAllowed()) + nSA=9000_deg100; + + if (nSA) + { // angle snapping + nNewAngle += nSA / 2_deg100; + nNewAngle /= nSA; + nNewAngle *= nSA; + } + + nNewAngle=NormAngle18000(nNewAngle); + + if (nAngle==nNewAngle) + return; + + sal_uInt16 nSekt0=GetAngleSector(nAngle); + sal_uInt16 nSekt1=GetAngleSector(nNewAngle); + + if (nSekt0==0 && nSekt1==3) + bRight=true; + + if (nSekt0==3 && nSekt1==0) + bRight=false; + + nAngle=nNewAngle; + double a = toRadians(nAngle); + double nSin1=sin(a); // calculate now, so as little time as possible + double nCos1=cos(a); // passes between Hide() and Show() + Hide(); + nSin=nSin1; + nCos=nCos1; + DragStat().NextMove(aPnt); + Show(); +} + +bool SdrDragRotate::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (nAngle!=0_deg100) + { + if (IsDraggingPoints()) + { + getSdrDragView().RotateMarkedPoints(DragStat().GetRef1(),nAngle); + } + else if (IsDraggingGluePoints()) + { + getSdrDragView().RotateMarkedGluePoints(DragStat().GetRef1(),nAngle,bCopy); + } + else + { + getSdrDragView().RotateMarkedObj(DragStat().GetRef1(),nAngle,bCopy); + } + } + return true; +} + +PointerStyle SdrDragRotate::GetSdrDragPointer() const +{ + return PointerStyle::Rotate; +} + + +SdrDragShear::SdrDragShear(SdrDragView& rNewView, bool bSlant1) +: SdrDragMethod(rNewView), + aFact(1,1), + nAngle0(0), + nAngle(0), + nTan(0.0), + bVertical(false), + bResize(false), + bUpSideDown(false), + bSlant(bSlant1) +{ +} + +OUString SdrDragShear::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethShear) + + " ("; + + Degree100 nTmpAngle(nAngle); + + if(bUpSideDown) + nTmpAngle += 18000_deg100; + + nTmpAngle = NormAngle18000(nTmpAngle); + + aStr += SdrModel::GetAngleString(nTmpAngle) + ")"; + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragShear::BeginSdrDrag() +{ + SdrHdlKind eRefHdl=SdrHdlKind::Move; + SdrHdl* pRefHdl=nullptr; + + switch (GetDragHdlKind()) + { + case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; break; + case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; break; + case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; bVertical=true; break; + case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; bVertical=true; break; + default: break; + } + + if (eRefHdl!=SdrHdlKind::Move) + pRefHdl=GetHdlList().GetHdl(eRefHdl); + + if (pRefHdl!=nullptr) + { + DragStat().SetRef1(pRefHdl->GetPos()); + nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1()); + } + else + { + OSL_FAIL("SdrDragShear::BeginSdrDrag(): No reference point handle for shearing found."); + return false; + } + + Show(); + return true; +} + +basegfx::B2DHomMatrix SdrDragShear::getCurrentTransformation() const +{ + basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix( + -DragStat().GetRef1().X(), -DragStat().GetRef1().Y())); + + if (bResize) + { + if (bVertical) + { + aRetval.scale(double(aFact), 1.0); + aRetval.shearY(-nTan); + } + else + { + aRetval.scale(1.0, double(aFact)); + aRetval.shearX(-nTan); + } + } + + aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y()); + + return aRetval; +} + +void SdrDragShear::MoveSdrDrag(const Point& rPnt) +{ + if (!DragStat().CheckMinMoved(rPnt)) + return; + + bResize=!getSdrDragView().IsOrtho(); + Degree100 nSA(0); + + if (getSdrDragView().IsAngleSnapEnabled()) + nSA=getSdrDragView().GetSnapAngle(); + + Point aP0(DragStat().GetStart()); + Point aPnt(rPnt); + Fraction aNewFract(1,1); + + // if angle snapping not activated, snap to raster (except when using slant) + if (nSA==0_deg100 && !bSlant) + aPnt=GetSnapPos(aPnt); + + if (!bSlant && !bResize) + { // shear, but no resize + if (bVertical) + aPnt.setX(aP0.X() ); + else + aPnt.setY(aP0.Y() ); + } + + Point aRef(DragStat().GetRef1()); + Point aDif(aPnt-aRef); + + Degree100 nNewAngle(0); + + if (bSlant) + { + nNewAngle=NormAngle18000(-(GetAngle(aDif)-nAngle0)); + + if (bVertical) + nNewAngle=NormAngle18000(-nNewAngle); + } + else + { + if (bVertical) + nNewAngle=NormAngle18000(GetAngle(aDif)); + else + nNewAngle=NormAngle18000(-(GetAngle(aDif)-9000_deg100)); + + if (nNewAngle<Degree100(-9000) || nNewAngle>9000_deg100) + nNewAngle=NormAngle18000(nNewAngle+18000_deg100); + + if (bResize) + { + Point aPt2(aPnt); + + if (nSA!=0_deg100) + aPt2=GetSnapPos(aPnt); // snap this one in any case + + if (bVertical) + { + aNewFract=Fraction(aPt2.X()-aRef.X(),aP0.X()-aRef.X()); + } + else + { + aNewFract=Fraction(aPt2.Y()-aRef.Y(),aP0.Y()-aRef.Y()); + } + } + } + + bool bNeg=nNewAngle<0_deg100; + + if (bNeg) + nNewAngle=-nNewAngle; + + if (nSA) + { // angle snapping + nNewAngle += nSA / 2_deg100; + nNewAngle /= nSA; + nNewAngle *= nSA; + } + + nNewAngle=NormAngle36000(nNewAngle); + bUpSideDown=nNewAngle>9000_deg100 && nNewAngle<27000_deg100; + + if (bSlant) + { // calculate resize for slant + // when angle snapping is activated, disable 89 degree limit + Degree100 nTmpAngle=nNewAngle; + if (bUpSideDown) nNewAngle -= 18000_deg100; + if (bNeg) nTmpAngle=-nTmpAngle; + bResize=true; + aNewFract = cos(toRadians(nTmpAngle)); + aFact.ReduceInaccurate(10); // three decimals should be enough + } + + if (nNewAngle > 8900_deg100) + nNewAngle = 8900_deg100; + + if (bNeg) + nNewAngle=-nNewAngle; + + if (nAngle!=nNewAngle || aFact!=aNewFract) + { + nAngle=nNewAngle; + aFact=aNewFract; + double a = toRadians(nAngle); + double nTan1=tan(a); // calculate now, so as little time as possible passes between Hide() and Show() + Hide(); + nTan=nTan1; + DragStat().NextMove(rPnt); + Show(); + } +} + +void SdrDragShear::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + if (bResize) + { + if (bVertical) + { + rTarget.Resize(DragStat().GetRef1(),aFact,Fraction(1,1)); + } + else + { + rTarget.Resize(DragStat().GetRef1(),Fraction(1,1),aFact); + } + } + + if (nAngle) + { + rTarget.Shear(DragStat().GetRef1(), nAngle, nTan, bVertical); + } +} + +bool SdrDragShear::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (bResize && aFact==Fraction(1,1)) + bResize=false; + + if (nAngle || bResize) + { + if (nAngle && bResize) + { + OUString aStr = ImpGetDescriptionStr(STR_EditShear); + + if (bCopy) + aStr += SvxResId(STR_EditWithCopy); + + getSdrDragView().BegUndo(aStr); + } + + if (bResize) + { + if (bVertical) + { + getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aFact,Fraction(1,1),bCopy); + } + else + { + getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),Fraction(1,1),aFact,bCopy); + } + + bCopy=false; + } + + if (nAngle) + { + getSdrDragView().ShearMarkedObj(DragStat().GetRef1(),nAngle,bVertical,bCopy); + } + + if (nAngle && bResize) + getSdrDragView().EndUndo(); + + return true; + } + + return false; +} + +PointerStyle SdrDragShear::GetSdrDragPointer() const +{ + if (bVertical) + return PointerStyle::VShear; + else + return PointerStyle::HShear; +} + + +void SdrDragMirror::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + if(bMirrored) + { + rTarget.Mirror(DragStat().GetRef1(), DragStat().GetRef2()); + } +} + +SdrDragMirror::SdrDragMirror(SdrDragView& rNewView) +: SdrDragMethod(rNewView), + nAngle(0), + bMirrored(false), + bSide0(false) +{ +} + +bool SdrDragMirror::ImpCheckSide(const Point& rPnt) const +{ + Degree100 nAngle1=GetAngle(rPnt-DragStat().GetRef1()); + nAngle1-=nAngle; + nAngle1=NormAngle36000(nAngle1); + + return nAngle1<18000_deg100; +} + +OUString SdrDragMirror::GetSdrDragComment() const +{ + OUString aStr; + if (aDif.X()==0) + aStr = ImpGetDescriptionStr(STR_DragMethMirrorHori); + else if (aDif.Y()==0) + aStr = ImpGetDescriptionStr(STR_DragMethMirrorVert); + else if (std::abs(aDif.X()) == std::abs(aDif.Y())) + aStr = ImpGetDescriptionStr(STR_DragMethMirrorDiag); + else + aStr = ImpGetDescriptionStr(STR_DragMethMirrorFree); + + if (getSdrDragView().IsDragWithCopy()) + aStr+=SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragMirror::BeginSdrDrag() +{ + SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1); + SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2); + + if (pH1!=nullptr && pH2!=nullptr) + { + DragStat().SetRef1(pH1->GetPos()); + DragStat().SetRef2(pH2->GetPos()); + Ref1()=pH1->GetPos(); + Ref2()=pH2->GetPos(); + aDif=pH2->GetPos()-pH1->GetPos(); + bool b90=(aDif.X()==0) || aDif.Y()==0; + bool b45=b90 || (std::abs(aDif.X()) == std::abs(aDif.Y())); + nAngle=NormAngle36000(GetAngle(aDif)); + + if (!getSdrDragView().IsMirrorAllowed() && !b45) + return false; // free choice of axis angle not allowed + + if (!getSdrDragView().IsMirrorAllowed() && !b90) + return false; // 45 degrees not allowed either + + bSide0=ImpCheckSide(DragStat().GetStart()); + Show(); + return true; + } + else + { + OSL_FAIL("SdrDragMirror::BeginSdrDrag(): Axis of reflection not found."); + return false; + } +} + +basegfx::B2DHomMatrix SdrDragMirror::getCurrentTransformation() const +{ + basegfx::B2DHomMatrix aRetval; + + if (bMirrored) + { + const double fDeltaX(DragStat().GetRef2().X() - DragStat().GetRef1().X()); + const double fDeltaY(DragStat().GetRef2().Y() - DragStat().GetRef1().Y()); + const double fRotation(atan2(fDeltaY, fDeltaX)); + + aRetval = basegfx::utils::createTranslateB2DHomMatrix(-DragStat().GetRef1().X(), -DragStat().GetRef1().Y()); + aRetval.rotate(-fRotation); + aRetval.scale(1.0, -1.0); + aRetval.rotate(fRotation); + aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y()); + } + + return aRetval; +} + +void SdrDragMirror::MoveSdrDrag(const Point& rPnt) +{ + if (!DragStat().CheckMinMoved(rPnt)) + return; + + bool bNewSide=ImpCheckSide(rPnt); + bool bNewMirrored=bSide0!=bNewSide; + + if (bMirrored!=bNewMirrored) + { + Hide(); + bMirrored=bNewMirrored; + DragStat().NextMove(rPnt); + Show(); + } +} + +bool SdrDragMirror::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (bMirrored) + { + getSdrDragView().MirrorMarkedObj(DragStat().GetRef1(),DragStat().GetRef2(),bCopy); + } + + return true; +} + +PointerStyle SdrDragMirror::GetSdrDragPointer() const +{ + return PointerStyle::Mirror; +} + + +SdrDragGradient::SdrDragGradient(SdrDragView& rNewView, bool bGrad) +: SdrDragMethod(rNewView), + pIAOHandle(nullptr), + bIsGradient(bGrad) +{ +} + +OUString SdrDragGradient::GetSdrDragComment() const +{ + if(IsGradient()) + return ImpGetDescriptionStr(STR_DragMethGradient); + else + return ImpGetDescriptionStr(STR_DragMethTransparence); +} + +bool SdrDragGradient::BeginSdrDrag() +{ + bool bRetval(false); + + pIAOHandle = static_cast<SdrHdlGradient*>(GetHdlList().GetHdl(IsGradient() ? SdrHdlKind::Gradient : SdrHdlKind::Transparence)); + + if(pIAOHandle) + { + // save old values + DragStat().SetRef1( pIAOHandle->GetPos() ); + DragStat().SetRef2( pIAOHandle->Get2ndPos() ); + + // what was hit? + bool bHit(false); + SdrHdlColor* pColHdl = pIAOHandle->GetColorHdl1(); + + // init handling flags + pIAOHandle->SetMoveSingleHandle(false); + pIAOHandle->SetMoveFirstHandle(false); + + // test first color handle + if(pColHdl) + { + basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y()); + + if(pColHdl->getOverlayObjectList().isHitLogic(aPosition)) + { + bHit = true; + pIAOHandle->SetMoveSingleHandle(true); + pIAOHandle->SetMoveFirstHandle(true); + } + } + + // test second color handle + pColHdl = pIAOHandle->GetColorHdl2(); + + if(!bHit && pColHdl) + { + basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y()); + + if(pColHdl->getOverlayObjectList().isHitLogic(aPosition)) + { + bHit = true; + pIAOHandle->SetMoveSingleHandle(true); + } + } + + // test gradient handle itself + if(!bHit) + { + basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y()); + + if(pIAOHandle->getOverlayObjectList().isHitLogic(aPosition)) + { + bHit = true; + } + } + + // everything up and running :o} + bRetval = bHit; + } + else + { + OSL_FAIL("SdrDragGradient::BeginSdrDrag(): IAOGradient not found."); + } + + return bRetval; +} + +void SdrDragGradient::MoveSdrDrag(const Point& rPnt) +{ + if(!(pIAOHandle && DragStat().CheckMinMoved(rPnt))) + return; + + DragStat().NextMove(rPnt); + + // Do the Move here!!! DragStat().GetStart() + Point aMoveDiff = rPnt - DragStat().GetStart(); + + if(pIAOHandle->IsMoveSingleHandle()) + { + if(pIAOHandle->IsMoveFirstHandle()) + { + pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff); + if(pIAOHandle->GetColorHdl1()) + pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff); + } + else + { + pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff); + if(pIAOHandle->GetColorHdl2()) + pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff); + } + } + else + { + pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff); + pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff); + + if(pIAOHandle->GetColorHdl1()) + pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff); + + if(pIAOHandle->GetColorHdl2()) + pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff); + } + + // new state + pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), false, false); +} + +bool SdrDragGradient::EndSdrDrag(bool /*bCopy*/) +{ + Ref1() = pIAOHandle->GetPos(); + Ref2() = pIAOHandle->Get2ndPos(); + + // new state + pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, true); + + return true; +} + +void SdrDragGradient::CancelSdrDrag() +{ + // restore old values + pIAOHandle->SetPos(DragStat().GetRef1()); + pIAOHandle->Set2ndPos(DragStat().GetRef2()); + + if(pIAOHandle->GetColorHdl1()) + pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1()); + + if(pIAOHandle->GetColorHdl2()) + pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2()); + + // new state + pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, false); +} + +PointerStyle SdrDragGradient::GetSdrDragPointer() const +{ + return PointerStyle::RefHand; +} + + +SdrDragCrook::SdrDragCrook(SdrDragView& rNewView) +: SdrDragMethod(rNewView), + aFact(1,1), + bContortionAllowed(false), + bNoContortionAllowed(false), + bContortion(false), + bResizeAllowed(false), + bResize(false), + bRotateAllowed(false), + bRotate(false), + bVertical(false), + bValid(false), + bLft(false), + bRgt(false), + bUpr(false), + bLwr(false), + bAtCenter(false), + nAngle(0), + nMarkSize(0), + eMode(SdrCrookMode::Rotate) +{ +} + +OUString SdrDragCrook::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(!bContortion ? STR_DragMethCrook : STR_DragMethCrookContortion); + + if(bValid) + { + aStr += " ("; + + sal_Int32 nVal(nAngle); + + if(bAtCenter) + nVal *= 2; + + nVal = std::abs(nVal); + aStr += SdrModel::GetAngleString(Degree100(nVal)) + ")"; + } + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +// These defines parametrize the created raster +// for interactions +#define DRAG_CROOK_RASTER_MINIMUM (4) +#define DRAG_CROOK_RASTER_MAXIMUM (15) +#define DRAG_CROOK_RASTER_DISTANCE (30) + +static basegfx::B2DPolyPolygon impCreateDragRaster(SdrPageView const & rPageView, const tools::Rectangle& rMarkRect) +{ + basegfx::B2DPolyPolygon aRetval; + + if(rPageView.PageWindowCount()) + { + OutputDevice& rOut = rPageView.GetPageWindow(0)->GetPaintWindow().GetOutputDevice(); + tools::Rectangle aPixelSize = rOut.LogicToPixel(rMarkRect); + sal_uInt32 nHorDiv(aPixelSize.GetWidth() / DRAG_CROOK_RASTER_DISTANCE); + sal_uInt32 nVerDiv(aPixelSize.GetHeight() / DRAG_CROOK_RASTER_DISTANCE); + + if(nHorDiv > DRAG_CROOK_RASTER_MAXIMUM) + nHorDiv = DRAG_CROOK_RASTER_MAXIMUM; + if(nHorDiv < DRAG_CROOK_RASTER_MINIMUM) + nHorDiv = DRAG_CROOK_RASTER_MINIMUM; + + if(nVerDiv > DRAG_CROOK_RASTER_MAXIMUM) + nVerDiv = DRAG_CROOK_RASTER_MAXIMUM; + if(nVerDiv < DRAG_CROOK_RASTER_MINIMUM) + nVerDiv = DRAG_CROOK_RASTER_MINIMUM; + + const double fXLen(rMarkRect.GetWidth() / static_cast<double>(nHorDiv)); + const double fYLen(rMarkRect.GetHeight() / static_cast<double>(nVerDiv)); + double fYPos(rMarkRect.Top()); + sal_uInt32 a, b; + + for(a = 0; a <= nVerDiv; a++) + { + // horizontal lines + for(b = 0; b < nHorDiv; b++) + { + basegfx::B2DPolygon aHorLineSegment; + + const double fNewX(rMarkRect.Left() + (b * fXLen)); + aHorLineSegment.append(basegfx::B2DPoint(fNewX, fYPos)); + aHorLineSegment.appendBezierSegment( + basegfx::B2DPoint(fNewX + (fXLen * (1.0 / 3.0)), fYPos), + basegfx::B2DPoint(fNewX + (fXLen * (2.0 / 3.0)), fYPos), + basegfx::B2DPoint(fNewX + fXLen, fYPos)); + aRetval.append(aHorLineSegment); + } + + // increments + fYPos += fYLen; + } + + double fXPos(rMarkRect.Left()); + + for(a = 0; a <= nHorDiv; a++) + { + // vertical lines + for(b = 0; b < nVerDiv; b++) + { + basegfx::B2DPolygon aVerLineSegment; + + const double fNewY(rMarkRect.Top() + (b * fYLen)); + aVerLineSegment.append(basegfx::B2DPoint(fXPos, fNewY)); + aVerLineSegment.appendBezierSegment( + basegfx::B2DPoint(fXPos, fNewY + (fYLen * (1.0 / 3.0))), + basegfx::B2DPoint(fXPos, fNewY + (fYLen * (2.0 / 3.0))), + basegfx::B2DPoint(fXPos, fNewY + fYLen)); + aRetval.append(aVerLineSegment); + } + + // increments + fXPos += fXLen; + } + } + + return aRetval; +} + +void SdrDragCrook::createSdrDragEntries() +{ + // Add extended frame raster first, so it will be behind objects + if(getSdrDragView().GetSdrPageView()) + { + const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect())); + + if(aDragRaster.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster))); + } + } + + // call parent + SdrDragMethod::createSdrDragEntries(); +} + +bool SdrDragCrook::BeginSdrDrag() +{ + bContortionAllowed=getSdrDragView().IsCrookAllowed(); + bNoContortionAllowed=getSdrDragView().IsCrookAllowed(true); + bResizeAllowed=getSdrDragView().IsResizeAllowed(); + bRotateAllowed=getSdrDragView().IsRotateAllowed(); + + if (bContortionAllowed || bNoContortionAllowed) + { + bVertical=(GetDragHdlKind()==SdrHdlKind::Lower || GetDragHdlKind()==SdrHdlKind::Upper); + aMarkRect=GetMarkedRect(); + aMarkCenter=aMarkRect.Center(); + nMarkSize=bVertical ? (aMarkRect.GetHeight()-1) : (aMarkRect.GetWidth()-1); + aCenter=aMarkCenter; + aStart=DragStat().GetStart(); + Show(); + return true; + } + else + { + return false; + } +} + +void SdrDragCrook::MovAllPoints(basegfx::B2DPolyPolygon& rTarget) +{ + SdrPageView* pPV = getSdrDragView().GetSdrPageView(); + + if(!pPV) + return; + + XPolyPolygon aTempPolyPoly(rTarget); + + if (pPV->HasMarkedObjPageView()) + { + sal_uInt16 nPolyCount=aTempPolyPoly.Count(); + + if (!bContortion && !getSdrDragView().IsNoDragXorPolys()) + { + sal_uInt16 n1st=0,nLast=0; + Point aC(aCenter); + + while (n1st<nPolyCount) + { + nLast=n1st; + while (nLast<nPolyCount && aTempPolyPoly[nLast].GetPointCount()!=0) nLast++; + tools::Rectangle aBound(aTempPolyPoly[n1st].GetBoundRect()); + sal_uInt16 i; + + for (i=n1st+1; i<nLast; i++) + { + aBound.Union(aTempPolyPoly[n1st].GetBoundRect()); + } + + Point aCtr0(aBound.Center()); + Point aCtr1(aCtr0); + + if (bResize) + { + Fraction aFact1(1,1); + + if (bVertical) + { + ResizePoint(aCtr1,aC,aFact1,aFact); + } + else + { + ResizePoint(aCtr1,aC,aFact,aFact1); + } + } + + bool bRotOk=false; + double nSin=0,nCos=0; + + if (aRad.X()!=0 && aRad.Y()!=0) + { + bRotOk=bRotate; + + switch (eMode) + { + case SdrCrookMode::Rotate : CrookRotateXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break; + case SdrCrookMode::Slant : CrookSlantXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break; + case SdrCrookMode::Stretch: CrookStretchXPoint(aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical,aMarkRect); break; + } // switch + } + + aCtr1-=aCtr0; + + for (i=n1st; i<nLast; i++) + { + if (bRotOk) + { + RotateXPoly(aTempPolyPoly[i],aCtr0,nSin,nCos); + } + + aTempPolyPoly[i].Move(aCtr1.X(),aCtr1.Y()); + } + + n1st=nLast+1; + } + } + else + { + sal_uInt16 i,j; + + for (j=0; j<nPolyCount; j++) + { + XPolygon& aPol=aTempPolyPoly[j]; + sal_uInt16 nPointCount=aPol.GetPointCount(); + i=0; + + while (i<nPointCount) + { + Point* pPnt=&aPol[i]; + Point* pC1=nullptr; + Point* pC2=nullptr; + + if (i+1<nPointCount && aPol.IsControl(i)) + { // control point on the left + pC1=pPnt; + i++; + pPnt=&aPol[i]; + } + + i++; + + if (i<nPointCount && aPol.IsControl(i)) + { // control point on the right + pC2=&aPol[i]; + i++; + } + + MovCrookPoint(*pPnt,pC1,pC2); + } + } + } + } + + rTarget = aTempPolyPoly.getB2DPolyPolygon(); +} + +void SdrDragCrook::MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2) +{ + bool bVert=bVertical; + bool bC1=pC1!=nullptr; + bool bC2=pC2!=nullptr; + Point aC(aCenter); + + if (bResize) + { + Fraction aFact1(1,1); + + if (bVert) + { + ResizePoint(rPnt,aC,aFact1,aFact); + + if (bC1) + ResizePoint(*pC1,aC,aFact1,aFact); + + if (bC2) + ResizePoint(*pC2,aC,aFact1,aFact); + } + else + { + ResizePoint(rPnt,aC,aFact,aFact1); + + if (bC1) + ResizePoint(*pC1,aC,aFact,aFact1); + + if (bC2) + ResizePoint(*pC2,aC,aFact,aFact1); + } + } + + if (aRad.X()!=0 && aRad.Y()!=0) + { + double nSin,nCos; + + switch (eMode) + { + case SdrCrookMode::Rotate : CrookRotateXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break; + case SdrCrookMode::Slant : CrookSlantXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break; + case SdrCrookMode::Stretch: CrookStretchXPoint(rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert,aMarkRect); break; + } // switch + } +} + +void SdrDragCrook::MoveSdrDrag(const Point& rPnt) +{ + if (!DragStat().CheckMinMoved(rPnt)) + return; + + bool bNewMoveOnly=getSdrDragView().IsMoveOnlyDragging(); + bAtCenter=false; + SdrCrookMode eNewMode=getSdrDragView().GetCrookMode(); + bool bNewContortion=!bNewMoveOnly && ((bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed); + bResize=!getSdrDragView().IsOrtho() && bResizeAllowed && !bNewMoveOnly; + bool bNewRotate=bRotateAllowed && !bNewContortion && !bNewMoveOnly && eNewMode==SdrCrookMode::Rotate; + + Point aPnt(GetSnapPos(rPnt)); + + Point aNewCenter(aMarkCenter.X(),aStart.Y()); + + if (bVertical) + { + aNewCenter.setX(aStart.X() ); + aNewCenter.setY(aMarkCenter.Y() ); + } + + if (!getSdrDragView().IsCrookAtCenter()) + { + switch (GetDragHdlKind()) + { + case SdrHdlKind::UpperLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break; + case SdrHdlKind::Upper: aNewCenter.setY(aMarkRect.Bottom() ); bUpr=true; break; + case SdrHdlKind::UpperRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break; + case SdrHdlKind::Left : aNewCenter.setX(aMarkRect.Right() ); bLft=true; break; + case SdrHdlKind::Right: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break; + case SdrHdlKind::LowerLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break; + case SdrHdlKind::Lower: aNewCenter.setY(aMarkRect.Top() ); bLwr=true; break; + case SdrHdlKind::LowerRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break; + default: bAtCenter=true; + } + } + else + bAtCenter=true; + + Fraction aNewFract(1,1); + tools::Long dx1=aPnt.X()-aNewCenter.X(); + tools::Long dy1=aPnt.Y()-aNewCenter.Y(); + bValid=bVertical ? dx1!=0 : dy1!=0; + + if (bValid) + { + if (bVertical) + bValid = std::abs(dx1)*100>std::abs(dy1); + else + bValid = std::abs(dy1)*100>std::abs(dx1); + } + + tools::Long nNewRad=0; + nAngle=0_deg100; + + if (bValid) + { + double a=0; // slope of the radius + Degree100 nPntAngle(0); + + if (bVertical) + { + a=static_cast<double>(dy1)/static_cast<double>(dx1); // slope of the radius + nNewRad=(static_cast<tools::Long>(dy1*a)+dx1) /2; + aNewCenter.AdjustX(nNewRad ); + nPntAngle=GetAngle(aPnt-aNewCenter); + } + else + { + a=static_cast<double>(dx1)/static_cast<double>(dy1); // slope of the radius + nNewRad=(static_cast<tools::Long>(dx1*a)+dy1) /2; + aNewCenter.AdjustY(nNewRad ); + nPntAngle=GetAngle(aPnt-aNewCenter)-9000_deg100; + } + + if (!bAtCenter) + { + if (nNewRad<0) + { + if (bRgt) nPntAngle += 18000_deg100; + if (bLft) nPntAngle = 18000_deg100 - nPntAngle; + if (bLwr) nPntAngle =- nPntAngle; + } + else + { + if (bRgt) nPntAngle = -nPntAngle; + if (bUpr) nPntAngle = 18000_deg100 - nPntAngle; + if (bLwr) nPntAngle += 18000_deg100; + } + + nPntAngle=NormAngle36000(nPntAngle); + } + else + { + if (nNewRad<0) nPntAngle += 18000_deg100; + if (bVertical) nPntAngle = 18000_deg100 - nPntAngle; + nPntAngle = NormAngle18000(nPntAngle); + nPntAngle = abs(nPntAngle); + } + + double nCircumference = 2 * std::abs(nNewRad) * M_PI; + + if (bResize) + { + tools::Long nMul=static_cast<tools::Long>(nCircumference * NormAngle36000(nPntAngle).get() / 36000.0); + + if (bAtCenter) + nMul*=2; + + aNewFract=Fraction(nMul,nMarkSize); + nAngle=nPntAngle; + } + else + { + nAngle = Degree100(static_cast<tools::Long>((nMarkSize*360/nCircumference)*100)/2); + + if (nAngle==0_deg100) + bValid=false; + } + } + + if (nAngle==0_deg100 || nNewRad==0) + bValid=false; + + if (!bValid) + nNewRad=0; + + if (!bValid && bResize) + { + tools::Long nMul=bVertical ? dy1 : dx1; + + if (bLft || bUpr) + nMul=-nMul; + + tools::Long nDiv=nMarkSize; + + if (bAtCenter) + { + nMul*=2; + nMul = std::abs(nMul); + } + + aNewFract=Fraction(nMul,nDiv); + } + + if (aNewCenter==aCenter && bNewContortion==bContortion && aNewFract==aFact && + bNewMoveOnly == getMoveOnly() && bNewRotate==bRotate && eNewMode==eMode) + return; + + Hide(); + setMoveOnly(bNewMoveOnly); + bRotate=bNewRotate; + eMode=eNewMode; + bContortion=bNewContortion; + aCenter=aNewCenter; + aFact=aNewFract; + aRad=Point(nNewRad,nNewRad); + bResize=aFact!=Fraction(1,1) && aFact.GetDenominator()!=0 && aFact.IsValid(); + DragStat().NextMove(aPnt); + Show(); +} + +void SdrDragCrook::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + const bool bDoResize(aFact!=Fraction(1,1)); + const bool bDoCrook(aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0); + + if (!(bDoCrook || bDoResize)) + return; + + if (bDoResize) + { + Fraction aFact1(1,1); + + if (bContortion) + { + if (bVertical) + { + rTarget.Resize(aCenter,aFact1,aFact); + } + else + { + rTarget.Resize(aCenter,aFact,aFact1); + } + } + else + { + Point aCtr0(rTarget.GetSnapRect().Center()); + Point aCtr1(aCtr0); + + if (bVertical) + { + ResizePoint(aCtr1,aCenter,aFact1,aFact); + } + else + { + ResizePoint(aCtr1,aCenter,aFact,aFact1); + } + + Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y()); + + rTarget.Move(aSiz); + } + } + + if (bDoCrook) + { + const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect()); + const bool bLocalRotate(!bContortion && eMode == SdrCrookMode::Rotate && getSdrDragView().IsRotateAllowed()); + + SdrEditView::ImpCrookObj(&rTarget,aCenter,aRad,eMode,bVertical,!bContortion,bLocalRotate,aLocalMarkRect); + } +} + +void SdrDragCrook::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) +{ + // use helper derived from old stuff + MovAllPoints(rTarget); +} + +bool SdrDragCrook::EndSdrDrag(bool bCopy) +{ + Hide(); + + if (bResize && aFact==Fraction(1,1)) + bResize=false; + + const bool bUndo = getSdrDragView().IsUndoEnabled(); + + bool bDoCrook=aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0; + + if (bDoCrook || bResize) + { + if (bResize && bUndo) + { + OUString aStr = ImpGetDescriptionStr(!bContortion?STR_EditCrook:STR_EditCrookContortion); + + if (bCopy) + aStr += SvxResId(STR_EditWithCopy); + + getSdrDragView().BegUndo(aStr); + } + + if (bResize) + { + Fraction aFact1(1,1); + + if (bContortion) + { + if (bVertical) + getSdrDragView().ResizeMarkedObj(aCenter,aFact1,aFact,bCopy); + else + getSdrDragView().ResizeMarkedObj(aCenter,aFact,aFact1,bCopy); + } + else + { + if (bCopy) + getSdrDragView().CopyMarkedObj(); + + const size_t nMarkCount=getSdrDragView().GetMarkedObjectList().GetMarkCount(); + + for (size_t nm=0; nm<nMarkCount; ++nm) + { + SdrMark* pM=getSdrDragView().GetMarkedObjectList().GetMark(nm); + SdrObject* pO=pM->GetMarkedSdrObj(); + Point aCtr0(pO->GetSnapRect().Center()); + Point aCtr1(aCtr0); + + if (bVertical) + ResizePoint(aCtr1,aCenter,aFact1,aFact); + else + ResizePoint(aCtr1,aCenter,aFact,aFact1); + + Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y()); + if( bUndo ) + AddUndo(getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pO,aSiz)); + pO->Move(aSiz); + } + } + + bCopy=false; + } + + if (bDoCrook) + { + getSdrDragView().CrookMarkedObj(aCenter,aRad,eMode,bVertical,!bContortion,bCopy); + } + + if (bResize && bUndo) + getSdrDragView().EndUndo(); + + return true; + } + + return false; +} + +PointerStyle SdrDragCrook::GetSdrDragPointer() const +{ + return PointerStyle::Crook; +} + + +SdrDragDistort::SdrDragDistort(SdrDragView& rNewView) +: SdrDragMethod(rNewView), + nPolyPt(0), + bContortionAllowed(false), + bNoContortionAllowed(false), + bContortion(false) +{ +} + +OUString SdrDragDistort::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethDistort) + + " (x=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX()) + + " y=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY()) + + ")"; + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +void SdrDragDistort::createSdrDragEntries() +{ + // Add extended frame raster first, so it will be behind objects + if(getSdrDragView().GetSdrPageView()) + { + const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect())); + + if(aDragRaster.count()) + { + addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster))); + } + } + + // call parent + SdrDragMethod::createSdrDragEntries(); +} + +bool SdrDragDistort::BeginSdrDrag() +{ + bContortionAllowed=getSdrDragView().IsDistortAllowed(); + bNoContortionAllowed=getSdrDragView().IsDistortAllowed(true); + + if (bContortionAllowed || bNoContortionAllowed) + { + SdrHdlKind eKind=GetDragHdlKind(); + nPolyPt=0xFFFF; + + if (eKind==SdrHdlKind::UpperLeft) nPolyPt=0; + if (eKind==SdrHdlKind::UpperRight) nPolyPt=1; + if (eKind==SdrHdlKind::LowerRight) nPolyPt=2; + if (eKind==SdrHdlKind::LowerLeft) nPolyPt=3; + if (nPolyPt>3) return false; + + aMarkRect=GetMarkedRect(); + aDistortedRect=XPolygon(aMarkRect); + Show(); + return true; + } + else + { + return false; + } +} + +void SdrDragDistort::MovAllPoints(basegfx::B2DPolyPolygon& rTarget) +{ + if (!bContortion) + return; + + SdrPageView* pPV = getSdrDragView().GetSdrPageView(); + + if(pPV && pPV->HasMarkedObjPageView()) + { + basegfx::B2DPolyPolygon aDragPolygon(rTarget); + const basegfx::B2DRange aOriginalRange = vcl::unotools::b2DRectangleFromRectangle(aMarkRect); + const basegfx::B2DPoint aTopLeft(aDistortedRect[0].X(), aDistortedRect[0].Y()); + const basegfx::B2DPoint aTopRight(aDistortedRect[1].X(), aDistortedRect[1].Y()); + const basegfx::B2DPoint aBottomLeft(aDistortedRect[3].X(), aDistortedRect[3].Y()); + const basegfx::B2DPoint aBottomRight(aDistortedRect[2].X(), aDistortedRect[2].Y()); + + aDragPolygon = basegfx::utils::distort(aDragPolygon, aOriginalRange, aTopLeft, aTopRight, aBottomLeft, aBottomRight); + rTarget = aDragPolygon; + } +} + +void SdrDragDistort::MoveSdrDrag(const Point& rPnt) +{ + if (!DragStat().CheckMinMoved(rPnt)) + return; + + Point aPnt(GetSnapPos(rPnt)); + + if (getSdrDragView().IsOrtho()) + OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho()); + + bool bNewContortion=(bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed; + + if (bNewContortion!=bContortion || aDistortedRect[nPolyPt]!=aPnt) + { + Hide(); + aDistortedRect[nPolyPt]=aPnt; + bContortion=bNewContortion; + DragStat().NextMove(aPnt); + Show(); + } +} + +bool SdrDragDistort::EndSdrDrag(bool bCopy) +{ + Hide(); + bool bDoDistort=DragStat().GetDX()!=0 || DragStat().GetDY()!=0; + + if (bDoDistort) + { + getSdrDragView().DistortMarkedObj(aMarkRect,aDistortedRect,!bContortion,bCopy); + return true; + } + + return false; +} + +PointerStyle SdrDragDistort::GetSdrDragPointer() const +{ + return PointerStyle::RefHand; +} + +void SdrDragDistort::applyCurrentTransformationToSdrObject(SdrObject& rTarget) +{ + const bool bDoDistort(DragStat().GetDX()!=0 || DragStat().GetDY()!=0); + + if (bDoDistort) + { + SdrEditView::ImpDistortObj(&rTarget, aMarkRect, aDistortedRect, !bContortion); + } +} + +void SdrDragDistort::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) +{ + // use helper derived from old stuff + MovAllPoints(rTarget); +} + + +SdrDragCrop::SdrDragCrop(SdrDragView& rNewView) +: SdrDragObjOwn(rNewView) +{ + // switch off solid dragging for crop; it just makes no sense since showing + // a 50% transparent object above the original will not be visible + setSolidDraggingActive(false); +} + +OUString SdrDragCrop::GetSdrDragComment() const +{ + OUString aStr = ImpGetDescriptionStr(STR_DragMethCrop) + + " (x=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX()) + + " y=" + + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY()) + + ")"; + + if(getSdrDragView().IsDragWithCopy()) + aStr += SvxResId(STR_EditWithCopy); + return aStr; +} + +bool SdrDragCrop::BeginSdrDrag() +{ + // call parent + bool bRetval(SdrDragObjOwn::BeginSdrDrag()); + + if(!GetDragHdl()) + { + // we need the DragHdl, break if not there + bRetval = false; + } + + return bRetval; +} + +bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + + if(0 == DragStat().GetDX() && 0 == DragStat().GetDY()) + { + // no change, done + return false; + } + + const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList(); + + if(1 != rMarkList.GetMarkCount()) + { + // Crop only with single Object selected + return false; + } + + // prepare for SdrGrafObj or others. This code has to work with usual + // SdrGrafObj's from Draw/Impress/Calc, but also with SdrObjects from + // Writer. It would be better to handle this in Writer directly, but + // there are currently no easy mechanisms to plug an alternative interaction + // from there + SdrObject* pSdrObject = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObjectUniquePtr pFullDragClone; + bool bExternal(false); + SdrObject* pExternalSdrObject(nullptr); + + // RotGrfFlyFrame: Crop decision for DrawingLayer/Writer now + // locally, no two-in-one methods any more + if (nullptr != pSdrObject && dynamic_cast< const SdrGrafObj* >(pSdrObject) == nullptr) + { + // If Writer, get the already offered for interaction SdrGrafObj + // and set up for using that replacement object that contains the + // real transformation. That SdrObject is owned and has to be deleted, + // so use a std::unique_ptr with special handling for the protected + // SDrObject destructor + pFullDragClone = pSdrObject->getFullDragClone(); + + if(dynamic_cast< SdrGrafObj* >(pFullDragClone.get())) + { + bExternal = true; + pExternalSdrObject = pSdrObject; + pSdrObject = pFullDragClone.get(); + } + } + + // get and check for SdrGrafObj now + SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject ); + + if(!pObj) + { + return false; + } + + // no undo for external needed, done there + const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled()); + + if(bUndo) + { + OUString aUndoStr = ImpGetDescriptionStr(STR_DragMethCrop); + + getSdrDragView().BegUndo( aUndoStr ); + getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj)); + // also need attr undo, the SdrGrafCropItem will be changed + getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); + } + + // get the original objects transformation + basegfx::B2DHomMatrix aOriginalMatrix; + basegfx::B2DPolyPolygon aPolyPolygon; + bool bShearCorrected(false); + pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon); + + { // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080 + const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aOriginalMatrix); + + if(!basegfx::fTools::equalZero(aTmpDecomp.getShearX())) + { + bShearCorrected = true; + aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aTmpDecomp.getScale(), + -aTmpDecomp.getShearX(), + aTmpDecomp.getRotate(), + aTmpDecomp.getTranslate()); + } + } + + // generate start point of original drag vector in unit coordinates (the + // vis-a-vis of the drag point) + basegfx::B2DPoint aLocalStart(0.0, 0.0); + bool bOnAxis(false); + + switch(GetDragHdlKind()) + { + case SdrHdlKind::UpperLeft: aLocalStart.setX(1.0); aLocalStart.setY(1.0); break; + case SdrHdlKind::Upper: aLocalStart.setX(0.5); aLocalStart.setY(1.0); bOnAxis = true; break; + case SdrHdlKind::UpperRight: aLocalStart.setX(0.0); aLocalStart.setY(1.0); break; + case SdrHdlKind::Left : aLocalStart.setX(1.0); aLocalStart.setY(0.5); bOnAxis = true; break; + case SdrHdlKind::Right: aLocalStart.setX(0.0); aLocalStart.setY(0.5); bOnAxis = true; break; + case SdrHdlKind::LowerLeft: aLocalStart.setX(1.0); aLocalStart.setY(0.0); break; + case SdrHdlKind::Lower: aLocalStart.setX(0.5); aLocalStart.setY(0.0); bOnAxis = true; break; + case SdrHdlKind::LowerRight: aLocalStart.setX(0.0); aLocalStart.setY(0.0); break; + default: break; + } + + // create the current drag position in unit coordinates. To get there, + // transform back the DragPoint to UnitCoordinates + basegfx::B2DHomMatrix aInverse(aOriginalMatrix); + aInverse.invert(); + basegfx::B2DPoint aLocalCurrent(aInverse * basegfx::B2DPoint(DragStat().GetNow().X(), DragStat().GetNow().Y())); + + // if one of the edge handles is used, limit to X or Y drag only + if(bOnAxis) + { + if(basegfx::fTools::equal(aLocalStart.getX(), 0.5)) + { + aLocalCurrent.setX(aLocalStart.getX()); + } + else + { + aLocalCurrent.setY(aLocalStart.getY()); + } + } + + // create internal change in unit coordinates + basegfx::B2DHomMatrix aDiscreteChangeMatrix; + + if(!basegfx::fTools::equal(aLocalCurrent.getX(), aLocalStart.getX())) + { + if(aLocalStart.getX() < 0.5) + { + aDiscreteChangeMatrix.scale(aLocalCurrent.getX(), 1.0); + } + else + { + aDiscreteChangeMatrix.scale(1.0 - aLocalCurrent.getX(), 1.0); + aDiscreteChangeMatrix.translate(aLocalCurrent.getX(), 0.0); + } + } + + if(!basegfx::fTools::equal(aLocalCurrent.getY(), aLocalStart.getY())) + { + if(aLocalStart.getY() < 0.5) + { + aDiscreteChangeMatrix.scale(1.0, aLocalCurrent.getY()); + } + else + { + aDiscreteChangeMatrix.scale(1.0, 1.0 - aLocalCurrent.getY()); + aDiscreteChangeMatrix.translate(0.0, aLocalCurrent.getY()); + } + } + + // We now have the whole executed Crop in UnitCoordinates in + // aDiscreteChangeMatrix, go to concrete sizes now. + // Create the unrotated original rectangle and the unrotated modified + // rectangle as Ranges + const basegfx::utils::B2DHomMatrixBufferedDecompose aOriginalMatrixDecomp(aOriginalMatrix); + + // prepare unsheared/unrotated versions of the old and new transformation + const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate( + basegfx::utils::createScaleTranslateB2DHomMatrix( + basegfx::absolute(aOriginalMatrixDecomp.getScale()), + aOriginalMatrixDecomp.getTranslate())); + + // create the ranges for these + basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0); + basegfx::B2DRange aRangeNewNoShearNoRotate(0.0, 0.0, 1.0, 1.0); + aRangeOriginalNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate); + aRangeNewNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate * aDiscreteChangeMatrix); + + if(bExternal) + { + // With aLocalStart point (opposed to dragged point), X scale and Y scale, + // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj + // crop. Use aLocalStart unchanged, so being relative to the Crop-Action, + // the called instance knows best how to use it + const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth()); + const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight()); + + pExternalSdrObject->Crop( + aLocalStart, + fScaleX, + fScaleY); + } + else + { + // prepare matrix to apply to object; evtl. back-correct shear + basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix); + + if(bShearCorrected) + { + // back-correct shear + const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aNewObjectMatrix); + + aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aTmpDecomp.getScale(), + -aTmpDecomp.getShearX(), + aTmpDecomp.getRotate(), + aTmpDecomp.getTranslate()); + } + + // apply change to object by applying the unit coordinate change followed + // by the original change + pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon); + + // extract the old Rectangle structures + tools::Rectangle aOldRect( + basegfx::fround(aRangeOriginalNoShearNoRotate.getMinX()), + basegfx::fround(aRangeOriginalNoShearNoRotate.getMinY()), + basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxX()), + basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxY())); + tools::Rectangle aNewRect( + basegfx::fround(aRangeNewNoShearNoRotate.getMinX()), + basegfx::fround(aRangeNewNoShearNoRotate.getMinY()), + basegfx::fround(aRangeNewNoShearNoRotate.getMaxX()), + basegfx::fround(aRangeNewNoShearNoRotate.getMaxY())); + + // continue with the old original stuff + if (!aOldRect.GetWidth() || !aOldRect.GetHeight()) + { + throw o3tl::divide_by_zero(); + } + + if((pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default)) + { + return false; + } + + const GraphicObject& rGraphicObject(pObj->GetGraphicObject()); + // tdf#117145 Usually Writer will go the bExternal path (see above), but more correct for + // the future is to use the MapMode from the SdrModel/SfxItemPool if the Writer's current + // special handling should be unified to this path in the future. Usually it *should* be + // MapUnit::Map100thMM, but better do not mix up Units. + // Checked now what SwVirtFlyDrawObj::NbcCrop is doing - it calculates everything forced + // to MapUnit::Map100thMM, but extracts/packs Twips to the used SdrGrafCropItem in Writer. + const MapMode aMapModePool(pObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0)); + Size aGraphicSize(rGraphicObject.GetPrefSize()); + + if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit()) + { + aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapModePool); + } + else + { + aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapModePool); + } + + if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height()) + { + return false; + } + + const SdrGrafCropItem& rOldCrop = pObj->GetMergedItem(SDRATTR_GRAFCROP); + double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / static_cast<double>(aOldRect.GetWidth()); + double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / static_cast<double>(aOldRect.GetHeight()); + + sal_Int32 nDiffLeft = aNewRect.Left() - aOldRect.Left(); + sal_Int32 nDiffTop = aNewRect.Top() - aOldRect.Top(); + sal_Int32 nDiffRight = aNewRect.Right() - aOldRect.Right(); + sal_Int32 nDiffBottom = aNewRect.Bottom() - aOldRect.Bottom(); + + if(pObj->IsMirrored()) + { + // mirrored X or Y, for old stuff, exchange X + // check for aw080 + sal_Int32 nTmp(nDiffLeft); + nDiffLeft = -nDiffRight; + nDiffRight = -nTmp; + } + + sal_Int32 nLeftCrop = static_cast<sal_Int32>( rOldCrop.GetLeft() + nDiffLeft * fScaleX ); + sal_Int32 nTopCrop = static_cast<sal_Int32>( rOldCrop.GetTop() + nDiffTop * fScaleY ); + sal_Int32 nRightCrop = static_cast<sal_Int32>( rOldCrop.GetRight() - nDiffRight * fScaleX ); + sal_Int32 nBottomCrop = static_cast<sal_Int32>( rOldCrop.GetBottom() - nDiffBottom * fScaleY ); + + SfxItemPool& rPool = getSdrDragView().GetModel()->GetItemPool(); + SfxItemSetFixed<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP> aSet( rPool ); + aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) ); + getSdrDragView().SetAttributes( aSet, false ); + } + + if(bUndo) + { + getSdrDragView().EndUndo(); + } + + return true; +} + +PointerStyle SdrDragCrop::GetSdrDragPointer() const +{ + return PointerStyle::Crop; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |