diff options
author | Armin Le Grand <Armin.Le.Grand@cib.de> | 2017-09-28 10:20:15 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@cib.de> | 2017-10-20 14:13:22 +0200 |
commit | d9b16effeda21488911d1b5035f9d3be05731ca2 (patch) | |
tree | 3c16a374a8bb42c7b0207e0e34bcacd66efb150f /svx | |
parent | 4ddd74e2d119eb7b25df75a65fcb214ce08ec672 (diff) |
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
Diffstat (limited to 'svx')
-rw-r--r-- | svx/source/svdraw/svddrgmt.cxx | 443 |
1 files changed, 171 insertions, 272 deletions
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx index aed40a9f13d1..90a6ad41f452 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::utils::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::utils::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::utils::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::utils::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; } |