basegfx/source/matrix/b2dhommatrixtools.cxx | 42 ++ include/basegfx/matrix/b2dhommatrixtools.hxx | 7 svx/source/svdraw/svddrgmt.cxx | 443 ++++++++++----------------- sw/source/core/doc/notxtfrm.cxx | 51 --- sw/source/core/draw/dflyobj.cxx | 100 +++++- sw/source/core/inc/dflyobj.hxx | 7 sw/source/core/inc/frmtool.hxx | 7 7 files changed, 331 insertions(+), 326 deletions(-)
New commits: commit 0de6fda489c7d07036edba92491d5798892b845b Author: Armin Le Grand <[email protected]> Date: Thu Sep 28 10:20:15 2017 +0200 RotGrfFlyFrame: Adapt Crop functionality to rotated Graphic The FlyFrame which may contain a Graphic needs working Crop, interactive and in core. Adapted this to work with now possible rotation, changed common code in svx which has to handle cases for Draw/Impress/Calc and Writer differently. Tried to use as much in common as possible. Additionally furter adaptions to rotation itself. Change-Id: Ia961e9490e2627c74220b186116f5aa4fcabca78 diff --git a/basegfx/source/matrix/b2dhommatrixtools.cxx b/basegfx/source/matrix/b2dhommatrixtools.cxx index 89ab91424706..013bb7d23703 100644 --- a/basegfx/source/matrix/b2dhommatrixtools.cxx +++ b/basegfx/source/matrix/b2dhommatrixtools.cxx @@ -358,6 +358,48 @@ namespace basegfx return aRetval; } + BASEGFX_DLLPUBLIC B2DHomMatrix createRotateAroundCenterKeepAspectRatioStayInsideRange( + const basegfx::B2DRange& rTargetRange, + double fRotate) + { + basegfx::B2DHomMatrix aRetval; + + // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees + if(0.0 != fRotate) + { + // Fit rotated graphic to center of available space, keeping page ratio: + // Adapt scaling ratio of unit object and rotate it + aRetval.scale(1.0, rTargetRange.getHeight() / rTargetRange.getWidth()); + aRetval.rotate(fRotate); + + // get the range to see where we are in unit coordinates + basegfx::B2DRange aFullRange(0.0, 0.0, 1.0, 1.0); + aFullRange.transform(aRetval); + + // detect needed scales in X/Y and choose the smallest for staying inside the + // available space while keeping aspect ratio of the source + const double fScaleX(rTargetRange.getWidth() / aFullRange.getWidth()); + const double fScaleY(rTargetRange.getHeight() / aFullRange.getHeight()); + const double fScaleMin(std::min(fScaleX, fScaleY)); + + // TopLeft to zero, then scale, then move to center of available space + aRetval.translate(-aFullRange.getMinX(), -aFullRange.getMinY()); + aRetval.scale(fScaleMin, fScaleMin); + aRetval.translate( + rTargetRange.getCenterX() - (0.5 * fScaleMin * aFullRange.getWidth()), + rTargetRange.getCenterY() - (0.5 * fScaleMin * aFullRange.getHeight())); + } + else + { + // just scale/translate needed + aRetval *= createScaleTranslateB2DHomMatrix( + rTargetRange.getRange(), + rTargetRange.getMinimum()); + } + + return aRetval; + } + /// special for the case to map from source range to target range B2DHomMatrix createSourceRangeTargetRangeTransform( const B2DRange& rSourceRange, diff --git a/include/basegfx/matrix/b2dhommatrixtools.hxx b/include/basegfx/matrix/b2dhommatrixtools.hxx index aa3c047a20c5..1a2706cd1378 100644 --- a/include/basegfx/matrix/b2dhommatrixtools.hxx +++ b/include/basegfx/matrix/b2dhommatrixtools.hxx @@ -126,6 +126,13 @@ namespace basegfx fRadiant); } + /// special for creating a mapping for a Range rotated around it's center + /// while keeping AspectRatio unchanged and staying inside the given Range + /// by optimally using the available space (no overlap or outside allowed) + BASEGFX_DLLPUBLIC B2DHomMatrix createRotateAroundCenterKeepAspectRatioStayInsideRange( + const basegfx::B2DRange& rTargetRange, + double fRotate); + /// special for the case to map from source range to target range BASEGFX_DLLPUBLIC B2DHomMatrix createSourceRangeTargetRangeTransform( const B2DRange& rSourceRange, diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx index ecb7800273f3..67efd4b9da5a 100644 --- a/svx/source/svdraw/svddrgmt.cxx +++ b/svx/source/svdraw/svddrgmt.cxx @@ -3585,200 +3585,62 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) { Hide(); - if( DragStat().GetDX()==0 && DragStat().GetDY()==0 ) + if(0 == DragStat().GetDX() && 0 == DragStat().GetDY()) + { + // no change, done return false; + } const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList(); - if( rMarkList.GetMarkCount() != 1 ) + if(1 != rMarkList.GetMarkCount()) + { + // Crop only with single Object selected return false; + } - SdrObject* pSdrObject = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); - - // tdf#34555: in order to implement visual crop in Writer, we need to handle two - // cases: - // EndSdrDrag when called in Impress/Draw/...: pSdrObject is a SdrGrafObj - // EndSdrDrag when called in Writer: pSdrObject is a SwVirtFlyDrawObj - // Main principle: if marked object is not SdrGrafObj, we start a generic handling - // based on virtual methods added to SdrObject, on MM100/Twip coordinates and so on. - // If marked object is SdrGrafObj, we do all the work here with matrix based - // coordinates. - if (dynamic_cast<const SdrGrafObj*>( pSdrObject) == nullptr) { - const bool bUndo = getSdrDragView().IsUndoEnabled(); - if( bUndo ) - { - OUString aUndoStr; - ImpTakeDescriptionStr(STR_DragMethCrop, aUndoStr); - getSdrDragView().BegUndo( aUndoStr ); - getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pSdrObject)); - // also need attr undo, the SdrGrafCropItem will be changed - getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pSdrObject)); - } - - // We need to produce a reference point and two (X & Y) scales - SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft); - SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight); - - if (pRef1==nullptr || pRef2==nullptr) - return false; - - tools::Rectangle rect(pRef1->GetPos(),pRef2->GetPos()); - - Point aEnd(DragStat().GetNow()); - Point aStart(DragStat().GetStart()); - Point aRef(rect.Center()); - - // Reference point is the point opposed to the dragged handle - switch(GetDragHdlKind()) - { - case SdrHdlKind::UpperLeft: aRef = rect.BottomRight(); break; - case SdrHdlKind::Upper: aRef = rect.BottomCenter(); DragStat().SetHorFixed(true); break; - case SdrHdlKind::UpperRight: aRef = rect.BottomLeft(); break; - case SdrHdlKind::Left : aRef = rect.RightCenter(); DragStat().SetVerFixed(true); break; - case SdrHdlKind::Right: aRef = rect.LeftCenter(); DragStat().SetVerFixed(true); break; - case SdrHdlKind::LowerLeft: aRef = rect.TopRight(); break; - case SdrHdlKind::Lower: aRef = rect.TopCenter(); DragStat().SetHorFixed(true); break; - case SdrHdlKind::LowerRight: aRef = rect.TopLeft(); break; - default: break; - } - - // By default, scale is new size / old size - long nXDiv = aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1; - long nYDiv = aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1; - long nXMul = aEnd.X()-aRef.X(); - long nYMul = aEnd.Y()-aRef.Y(); - - if (nXDiv<0) - { - nXDiv=-nXDiv; - nXMul=-nXMul; - } - - if (nYDiv<0) - { - nYDiv=-nYDiv; - nYMul=-nYMul; - } - - // Take ortho into account. - 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; - } + // 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(); + struct SdrObjDeleter { void operator()(SdrObject* b) { SdrObject::Free(b); }}; + std::unique_ptr< SdrObject, SdrObjDeleter > pFullDragClone; + bool bExternal(false); + SdrObject* pExternalSdrObject(nullptr); - if (DragStat().IsVerFixed()) - { - bYNeg=false; - nYMul=1; - nYDiv=1; - } - } - } - Fraction aXFact(nXMul,nXDiv); - Fraction aYFact(nYMul,nYDiv); - Fraction aMaxFact(0x7FFFFFFF,1); + // 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.reset(pSdrObject->getFullDragClone()); - if (bOrtho) + if(pFullDragClone && dynamic_cast< SdrGrafObj* >(pFullDragClone.get())) { - if (aXFact>aMaxFact) - { - aXFact=aMaxFact; - aYFact=aMaxFact; - } - - if (aYFact>aMaxFact) - { - aXFact=aMaxFact; - aYFact=aMaxFact; - } + bExternal = true; + pExternalSdrObject = pSdrObject; + pSdrObject = pFullDragClone.get(); } - - if (bXNeg) - aXFact=Fraction(-aXFact.GetNumerator(),aXFact.GetDenominator()); - - if (bYNeg) - aYFact=Fraction(-aYFact.GetNumerator(),aYFact.GetDenominator()); - - // With Ref point (opposed to dragged point), X scale and Y scale, - // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj - // crop - pSdrObject->Crop(aRef, aXFact, aYFact); - - if( bUndo ) - getSdrDragView().EndUndo(); - - // Job's done - return true; } - // This part of code handles the case where pSdrObject is SdrGrafObj - + // get and check for SdrGrafObj now SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject ); - if( !pObj || (pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default) ) - return false; - const GraphicObject& rGraphicObject = pObj->GetGraphicObject(); - const MapMode aMapMode100thmm(MapUnit::Map100thMM); - Size aGraphicSize(rGraphicObject.GetPrefSize()); - - if( MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit() ) - aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphicSize, aMapMode100thmm ); - else - aGraphicSize = OutputDevice::LogicToLogic( aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm); - - if( aGraphicSize.Width() == 0 || aGraphicSize.Height() == 0 ) + if(!pObj) + { return false; + } - const SdrGrafCropItem& rOldCrop = static_cast<const SdrGrafCropItem&>(pObj->GetMergedItem(SDRATTR_GRAFCROP)); - - const bool bUndo = getSdrDragView().IsUndoEnabled(); + // no undo for external needed, done there + const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled()); - if( bUndo ) + if(bUndo) { OUString aUndoStr; ImpTakeDescriptionStr(STR_DragMethCrop, aUndoStr); @@ -3789,20 +3651,15 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); } - // new part to commute the user's drag activities // get the original objects transformation basegfx::B2DHomMatrix aOriginalMatrix; basegfx::B2DPolyPolygon aPolyPolygon; bool bShearCorrected(false); - - // get transformation from object pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon); { // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080 - basegfx::B2DTuple aScale; - basegfx::B2DTuple aTranslate; + basegfx::B2DTuple aScale, aTranslate; double fRotate(0.0), fShearX(0.0); - aOriginalMatrix.decompose(aScale, aTranslate, fRotate, fShearX); if(!basegfx::fTools::equalZero(fShearX)) @@ -3816,11 +3673,6 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) } } - // invert it to be able to work on unit coordinates - basegfx::B2DHomMatrix aInverse(aOriginalMatrix); - - aInverse.invert(); - // 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); @@ -3839,7 +3691,10 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) default: break; } - // create the current drag position in unit coordinates + // 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 @@ -3884,48 +3739,18 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) } } - // preparematrix to apply to object; evtl. back-correct shear - basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix); - - if(bShearCorrected) - { - // back-correct shear - basegfx::B2DTuple aScale; - basegfx::B2DTuple aTranslate; - double fRotate(0.0), fShearX(0.0); - - aNewObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); - aNewObjectMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( - aScale, - -fShearX, - fRotate, - aTranslate); - } - - // apply change to object by applying the unit coordinate change followed - // by the original change - pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon); - - // the following old code uses aOldRect/aNewRect to calculate the crop change for - // the crop item. It implies unrotated objects, so create the unrotated original - // rectangle and the unrotated modified rectangle. Latter can in case of shear and/or - // rotation not be fetched by using - - //Rectangle aNewRect( pObj->GetLogicRect() ); - - // as it was done before because the top-left of that new rect *will* have an offset - // caused by the evtl. existing shear and/or rotation, so calculate a unrotated - // rectangle how it would be as a result when applying the unit coordinate change - // to the unrotated original transformation. - basegfx::B2DTuple aScale; - basegfx::B2DTuple aTranslate; + // 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 + basegfx::B2DTuple aScale, aTranslate; double fRotate, fShearX; // get access to scale and translate aOriginalMatrix.decompose(aScale, aTranslate, fRotate, fShearX); // prepare unsheared/unrotated versions of the old and new transformation - const basegfx::B2DHomMatrix aMatrixOriginalNoShearNoRotate( + const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate( basegfx::tools::createScaleTranslateB2DHomMatrix( basegfx::absolute(aScale), aTranslate)); @@ -3933,55 +3758,129 @@ bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/) // 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 Ref point (opposed to dragged point), X scale and Y scale, + // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj + // crop. aRef needs to be adapted to concrete Object's boundaries which + // is different from Crop-Ranges. This is because the Graphic and it's + // SdrObject representaion is inside the FlyFrame, but not identical + // with it. + const tools::Rectangle& rOutRect(pExternalSdrObject->GetCurrentBoundRect()); + const basegfx::B2DHomMatrix aExternalTransform( + basegfx::tools::createScaleTranslateB2DHomMatrix( + rOutRect.getWidth(), rOutRect.getHeight(), + rOutRect.Left(), rOutRect.Top())); + const basegfx::B2DPoint aRef(aExternalTransform * aLocalStart); + const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth()); + const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight()); + + pExternalSdrObject->Crop( + Point(basegfx::fround(aRef.getX()), basegfx::fround(aRef.getY())), + Fraction(fScaleX), + Fraction(fScaleY)); + } + else + { + // prepare matrix to apply to object; evtl. back-correct shear + basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix); + + if(bShearCorrected) + { + // back-correct shear + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate(0.0), fShearX(0.0); - aRangeOriginalNoShearNoRotate.transform(aMatrixOriginalNoShearNoRotate); - aRangeNewNoShearNoRotate.transform(aMatrixOriginalNoShearNoRotate * aDiscreteChangeMatrix); - - // 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(); - - double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / (double)aOldRect.GetWidth(); - double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / (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(); - SfxItemSet aSet( rPool, svl::Items<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP>{} ); - aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) ); - getSdrDragView().SetAttributes( aSet, false ); - - if( bUndo ) + aNewObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + aNewObjectMatrix = basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, + -fShearX, + fRotate, + aTranslate); + } + + // 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(); + const MapMode aMapMode100thmm(MapUnit::Map100thMM); + Size aGraphicSize(rGraphicObject.GetPrefSize()); + + if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit()) + { + aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapMode100thmm); + } + else + { + aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm); + } + + if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height()) + { + return false; + } + + const SdrGrafCropItem& rOldCrop = static_cast<const SdrGrafCropItem&>(pObj->GetMergedItem(SDRATTR_GRAFCROP)); + double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / (double)aOldRect.GetWidth(); + double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / (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(); + SfxItemSet aSet( rPool, svl::Items<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP>{} ); + aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) ); + getSdrDragView().SetAttributes( aSet, false ); + } + + if(bUndo) + { getSdrDragView().EndUndo(); + } return true; } diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx index 69a359539456..550f862b5754 100644 --- a/sw/source/core/doc/notxtfrm.cxx +++ b/sw/source/core/doc/notxtfrm.cxx @@ -757,53 +757,24 @@ bool paintUsingPrimitivesHelper( return false; } -void paintGraphicUsingPrimitivesHelper(vcl::RenderContext & rOutputDevice, - GraphicObject const& rGrfObj, GraphicAttr const& rGraphicAttr, - SwRect const& rAlignedGrfArea) +void paintGraphicUsingPrimitivesHelper( + vcl::RenderContext & rOutputDevice, + GraphicObject const& rGrfObj, + GraphicAttr const& rGraphicAttr, + SwRect const& rAlignedGrfArea) { - // unify using GraphicPrimitive2D + // RotGrfFlyFrame: unify using GraphicPrimitive2D // -> the primitive handles all crop and mirror stuff // -> the primitive renderer will create the needed pdf export data // -> if bitmap content, it will be cached system-dependent const basegfx::B2DRange aTargetRange( rAlignedGrfArea.Left(), rAlignedGrfArea.Top(), rAlignedGrfArea.Right(), rAlignedGrfArea.Bottom()); - basegfx::B2DHomMatrix aTargetTransform; - - // RotGrfFlyFrame: Take rotation into account. Rotation is in 10th degrees - if(0 != rGraphicAttr.GetRotation()) - { - // Fit rotated graphic to center of available space, keeping page ratio: - // Adapt scaling ratio of unit object and rotate it - const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0)); - aTargetTransform.scale(1.0, aTargetRange.getHeight() / aTargetRange.getWidth()); - aTargetTransform.rotate(fRotate); - - // get the range to see where we are in unit coordinates - basegfx::B2DRange aFullRange(0.0, 0.0, 1.0, 1.0); - aFullRange.transform(aTargetTransform); - - // detect needed scales in X/Y and choose the smallest for staying inside the - // available space while keeping aspect ratio of the source - const double fScaleX(aTargetRange.getWidth() / aFullRange.getWidth()); - const double fScaleY(aTargetRange.getHeight() / aFullRange.getHeight()); - const double fScaleMin(std::min(fScaleX, fScaleY)); - - // TopLeft to zero, then scale, then move to center of available space - aTargetTransform.translate(-aFullRange.getMinX(), -aFullRange.getMinY()); - aTargetTransform.scale(fScaleMin, fScaleMin); - aTargetTransform.translate( - aTargetRange.getCenterX() - (0.5 * fScaleMin * aFullRange.getWidth()), - aTargetRange.getCenterY() - (0.5 * fScaleMin * aFullRange.getHeight())); - } - else - { - // just scale/translate needed - aTargetTransform *= basegfx::tools::createScaleTranslateB2DHomMatrix( - aTargetRange.getRange(), - aTargetRange.getMinimum()); - } - + const double fRotate(static_cast< double >(-rGraphicAttr.GetRotation()) * (M_PI/1800.0)); + const basegfx::B2DHomMatrix aTargetTransform( + basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( + aTargetRange, + fRotate)); drawinglayer::primitive2d::Primitive2DContainer aContent(1); bool bDone(false); diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx index c94f293ed876..770788d5d4dc 100644 --- a/sw/source/core/draw/dflyobj.cxx +++ b/sw/source/core/draw/dflyobj.cxx @@ -59,6 +59,8 @@ #include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <sw_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <notxtfrm.hxx> using namespace ::com::sun::star; @@ -920,20 +922,96 @@ void SwVirtFlyDrawObj::Crop(const Point& rRef, const Fraction& xFact, const Frac GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); } -void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const +// RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame +sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame() const { - tools::Rectangle aRect(GetSnapRect()); + sal_uInt16 nRetval(0); + const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower()); - if(!aRect.IsEmpty()) + if(pNoTx) { - rTarget.AddHdl(new SdrCropHdl(aRect.TopLeft() , SdrHdlKind::UpperLeft, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.TopCenter() , SdrHdlKind::Upper, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.TopRight() , SdrHdlKind::UpperRight, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.LeftCenter() , SdrHdlKind::Left , 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.RightCenter() , SdrHdlKind::Right, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.BottomLeft() , SdrHdlKind::LowerLeft, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.BottomCenter(), SdrHdlKind::Lower, 0, 0)); - rTarget.AddHdl(new SdrCropHdl(aRect.BottomRight() , SdrHdlKind::LowerRight, 0, 0)); + SwNoTextNode& rNoTNd = const_cast< SwNoTextNode& >(*static_cast<const SwNoTextNode*>(pNoTx->GetNode())); + SwGrfNode* pGrfNd = rNoTNd.GetGrfNode(); + + if(nullptr != pGrfNd) + { + const SwAttrSet& rSet = pGrfNd->GetSwAttrSet(); + const SwRotationGrf& rRotation = rSet.GetRotationGrf(); + + nRetval = rRotation.GetValue(); + } + } + + return nRetval; +} + +SdrObject* SwVirtFlyDrawObj::getFullDragClone() const +{ + // call parent + SdrObject* pRetval = SdrVirtObj::getFullDragClone(); + + if(pRetval) + { + // RotGrfFlyFrame: Add transformation to placeholder object + const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame()); + + if(0 != nRotation) + { + const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); + const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect()); + const basegfx::B2DRange aTargetRange( + aOutRect.Left(), aOutRect.Top(), + aOutRect.Right(), aOutRect.Bottom()); + const basegfx::B2DHomMatrix aTargetTransform( + basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( + aTargetRange, + fRotate)); + + pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon()); + } + } + + return pRetval; +} + +void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const +{ + // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame + if(GetFlyFrame()->Frame().HasArea()) + { + const tools::Rectangle aOutRect(GetFlyFrame()->Frame().SVRect()); + const basegfx::B2DRange aTargetRange( + aOutRect.Left(), aOutRect.Top(), + aOutRect.Right(), aOutRect.Bottom()); + + if(!aTargetRange.isEmpty()) + { + const sal_uInt16 nRotation(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame()); + const double fRotate(static_cast< double >(-nRotation) * (M_PI/1800.0)); + const basegfx::B2DHomMatrix aTargetTransform( + basegfx::tools::createRotateAroundCenterKeepAspectRatioStayInsideRange( + aTargetRange, + fRotate)); + basegfx::B2DPoint aPos; + const double fShearX(0.0); + + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate)); + aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0); + rTarget.AddHdl(new SdrCropHdl(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate)); + } } } diff --git a/sw/source/core/inc/dflyobj.hxx b/sw/source/core/inc/dflyobj.hxx index 9368eb893b07..528edc8aff39 100644 --- a/sw/source/core/inc/dflyobj.hxx +++ b/sw/source/core/inc/dflyobj.hxx @@ -58,6 +58,10 @@ class SwVirtFlyDrawObj : public SdrVirtObj private: SwFlyFrame *m_pFlyFrame; + // RotGrfFlyFrame: Helper to acces sthe rotation angle (in 10th degrees, left-handed) + // of a GraphicFrame + sal_uInt16 getPossibleRotationFromFraphicFrame() const; + protected: // AW: Need own sdr::contact::ViewContact since AnchorPos from parent is // not used but something own (top left of new SnapRect minus top left @@ -102,6 +106,9 @@ public: virtual void Crop(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override; virtual void addCropHandles(SdrHdlList& rTarget) const override; + // FullDrag support + virtual SdrObject* getFullDragClone() const override; + const SwFrameFormat *GetFormat() const; SwFrameFormat *GetFormat(); diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx index 31f96590cd8b..e1a894385f37 100644 --- a/sw/source/core/inc/frmtool.hxx +++ b/sw/source/core/inc/frmtool.hxx @@ -72,10 +72,11 @@ bool DrawFillAttributes( const basegfx::tools::B2DClipState& rClipState, OutputDevice& rOut); +// RotGrfFlyFrame: Adapted to rotation void paintGraphicUsingPrimitivesHelper( - OutputDevice & rOutputDevice, - GraphicObject const& rGraphicObj, GraphicAttr const& rGraphicAttr, - SwRect const& rAlignedGrfArea); + OutputDevice & rOutputDevice, + GraphicObject const& rGraphicObj, GraphicAttr const& rGraphicAttr, + SwRect const& rAlignedGrfArea); // method to align rectangle. // Created declaration here to avoid <extern> declarations _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
