1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 #include <comphelper/lok.hxx>
22 #include <tools/mapunit.hxx>
23 #include <svx/svdhdl.hxx>
24 #include <svx/svdtrans.hxx>
25 #include <editeng/protitem.hxx>
26 #include <svx/svdpage.hxx>
27 #include <vcl/canvastools.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/ptrstyle.hxx>
31 
32 #include <fmtclds.hxx>
33 #include <fmtornt.hxx>
34 #include <fmtfsize.hxx>
35 #include <fmturl.hxx>
36 #include <viewsh.hxx>
37 #include <frmatr.hxx>
38 #include <doc.hxx>
39 #include <IDocumentUndoRedo.hxx>
40 #include <dflyobj.hxx>
41 #include <flyfrm.hxx>
42 #include <frmfmt.hxx>
43 #include <viewopt.hxx>
44 #include <frmtool.hxx>
45 #include <flyfrms.hxx>
46 #include <ndnotxt.hxx>
47 #include <grfatr.hxx>
48 #include <pagefrm.hxx>
49 #include <rootfrm.hxx>
50 #include <textboxhelper.hxx>
51 #include <wrtsh.hxx>
52 #include <ndgrf.hxx>
53 #include <frmmgr.hxx>
54 
55 #include <svx/sdr/properties/defaultproperties.hxx>
56 #include <basegfx/range/b2drange.hxx>
57 #include <basegfx/polygon/b2dpolygontools.hxx>
58 #include <basegfx/polygon/b2dpolygon.hxx>
59 
60 // AW: For VCOfDrawVirtObj and stuff
61 #include <svx/sdr/contact/viewcontactofvirtobj.hxx>
62 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
63 #include <drawinglayer/geometry/viewinformation2d.hxx>
64 #include <sw_primitivetypes2d.hxx>
65 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
66 #include <basegfx/matrix/b2dhommatrixtools.hxx>
67 #include <notxtfrm.hxx>
68 
69 using namespace ::com::sun::star;
70 
71 static bool bInResize = false;
72 
73 
74 namespace sdr
75 {
76     namespace contact
77     {
78         /**
79          * @see #i95264#
80          *
81          * currently needed since createViewIndependentPrimitive2DSequence() is called when
82          * RecalcBoundRect() is used. There should currently no VOCs being constructed since it
83          * gets not visualized (instead the corresponding SwVirtFlyDrawObj's referencing this one
84          * are visualized).
85          */
86         class VCOfSwFlyDrawObj : public ViewContactOfSdrObj
87         {
88         protected:
89             /** This method is responsible for creating the graphical visualisation data
90              *
91              * @note ONLY based on model data
92              */
93             virtual drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override;
94 
95         public:
96             /// basic constructor, used from SdrObject.
VCOfSwFlyDrawObj(SwFlyDrawObj & rObj)97             explicit VCOfSwFlyDrawObj(SwFlyDrawObj& rObj)
98             :   ViewContactOfSdrObj(rObj)
99             {
100             }
101         };
102 
createViewIndependentPrimitive2DSequence() const103         drawinglayer::primitive2d::Primitive2DContainer VCOfSwFlyDrawObj::createViewIndependentPrimitive2DSequence() const
104         {
105             // currently gets not visualized, return empty sequence
106             return drawinglayer::primitive2d::Primitive2DContainer();
107         }
108 
109     } // end of namespace contact
110 } // end of namespace sdr
111 
CreateObjectSpecificProperties()112 std::unique_ptr<sdr::properties::BaseProperties> SwFlyDrawObj::CreateObjectSpecificProperties()
113 {
114     // create default properties
115     return std::make_unique<sdr::properties::DefaultProperties>(*this);
116 }
117 
CreateObjectSpecificViewContact()118 std::unique_ptr<sdr::contact::ViewContact> SwFlyDrawObj::CreateObjectSpecificViewContact()
119 {
120     // needs an own VC since createViewIndependentPrimitive2DSequence()
121     // is called when RecalcBoundRect() is used
122     return std::make_unique<sdr::contact::VCOfSwFlyDrawObj>(*this);
123 }
124 
SwFlyDrawObj(SdrModel & rSdrModel)125 SwFlyDrawObj::SwFlyDrawObj(SdrModel& rSdrModel)
126 :   SdrObject(rSdrModel),
127     mbIsTextBox(false)
128 {
129 }
130 
~SwFlyDrawObj()131 SwFlyDrawObj::~SwFlyDrawObj()
132 {
133 }
134 
135 // SwFlyDrawObj - Factory-Methods
GetObjInventor() const136 SdrInventor SwFlyDrawObj::GetObjInventor() const
137 {
138     return SdrInventor::Swg;
139 }
140 
GetObjIdentifier() const141 sal_uInt16 SwFlyDrawObj::GetObjIdentifier() const
142 {
143     return SwFlyDrawObjIdentifier;
144 }
145 
146 // TODO: Need own primitive to get the FlyFrame paint working
147 namespace drawinglayer
148 {
149     namespace primitive2d
150     {
151         class SwVirtFlyDrawObjPrimitive : public BufferedDecompositionPrimitive2D
152         {
153         private:
154             const SwVirtFlyDrawObj&                 mrSwVirtFlyDrawObj;
155             const basegfx::B2DRange                 maOuterRange;
156 
157         protected:
158             /// method which is to be used to implement the local decomposition of a 2D primitive
159             virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override;
160 
161         public:
SwVirtFlyDrawObjPrimitive(const SwVirtFlyDrawObj & rSwVirtFlyDrawObj,const basegfx::B2DRange & rOuterRange)162             SwVirtFlyDrawObjPrimitive(
163                 const SwVirtFlyDrawObj& rSwVirtFlyDrawObj,
164                 const basegfx::B2DRange &rOuterRange)
165             :   BufferedDecompositionPrimitive2D(),
166                 mrSwVirtFlyDrawObj(rSwVirtFlyDrawObj),
167                 maOuterRange(rOuterRange)
168             {
169             }
170 
171             virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
172 
173             virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
174 
175             // override to allow callbacks to wrap_DoPaintObject
176             virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
177 
178             // data read access
getSwVirtFlyDrawObj() const179             const SwVirtFlyDrawObj& getSwVirtFlyDrawObj() const { return mrSwVirtFlyDrawObj; }
getOuterRange() const180             const basegfx::B2DRange& getOuterRange() const { return maOuterRange; }
181 
182             /// provide unique ID
183             DeclPrimitive2DIDBlock()
184         };
185     } // end of namespace primitive2d
186 } // end of namespace drawinglayer
187 
188 namespace drawinglayer
189 {
190     namespace primitive2d
191     {
create2DDecomposition(Primitive2DContainer & rContainer,const geometry::ViewInformation2D &) const192         void SwVirtFlyDrawObjPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
193         {
194             if(!getOuterRange().isEmpty())
195             {
196                 // currently this SW object has no primitive representation. As long as this is the case,
197                 // create invisible geometry to allow correct HitTest and BoundRect calculations for the
198                 // object. Use a filled primitive to get 'inside' as default object hit. The special cases from
199                 // the old SwVirtFlyDrawObj::CheckHit implementation are handled now in SwDrawView::PickObj;
200                 // this removed the 'hack' to get a view from inside model data or to react on null-tolerance
201                 // as it was done in the old implementation
202                 rContainer.push_back(
203                     createHiddenGeometryPrimitives2D(
204                         true,
205                         getOuterRange()));
206             }
207         }
208 
operator ==(const BasePrimitive2D & rPrimitive) const209         bool SwVirtFlyDrawObjPrimitive::operator==(const BasePrimitive2D& rPrimitive) const
210         {
211             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
212             {
213                 const SwVirtFlyDrawObjPrimitive& rCompare = static_cast<const SwVirtFlyDrawObjPrimitive&>(rPrimitive);
214 
215                 return (&getSwVirtFlyDrawObj() == &rCompare.getSwVirtFlyDrawObj()
216                     && getOuterRange() == rCompare.getOuterRange());
217             }
218 
219             return false;
220         }
221 
getB2DRange(const geometry::ViewInformation2D &) const222         basegfx::B2DRange SwVirtFlyDrawObjPrimitive::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
223         {
224             return getOuterRange();
225         }
226 
get2DDecomposition(Primitive2DDecompositionVisitor & rVisitor,const geometry::ViewInformation2D & rViewInformation) const227         void SwVirtFlyDrawObjPrimitive::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
228         {
229             // This is the callback to keep the FlyFrame painting in SW alive as long as it
230             // is not changed to primitives. This is the method which will be called by the processors
231             // when they do not know this primitive (and they do not). Inside wrap_DoPaintObject
232             // there needs to be a test that paint is only done during SW repaints (see there).
233             // Using this mechanism guarantees the correct Z-Order of the VirtualObject-based FlyFrames.
234             getSwVirtFlyDrawObj().wrap_DoPaintObject(rViewInformation);
235 
236             // call parent
237             BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
238         }
239 
240         // provide unique ID
241         ImplPrimitive2DIDBlock(SwVirtFlyDrawObjPrimitive, PRIMITIVE2D_ID_SWVIRTFLYDRAWOBJPRIMITIVE2D)
242 
243     } // end of namespace primitive2d
244 } // end of namespace drawinglayer
245 
246 // AW: own sdr::contact::ViewContact (VC) sdr::contact::ViewObjectContact (VOC) needed
247 // since offset is defined different from SdrVirtObj's sdr::contact::ViewContactOfVirtObj.
248 // For paint, that offset is used by setting at the OutputDevice; for primitives this is
249 // not possible since we have no OutputDevice, but define the geometry itself.
250 
251 namespace sdr
252 {
253     namespace contact
254     {
255         class VCOfSwVirtFlyDrawObj : public ViewContactOfVirtObj
256         {
257         protected:
258             /** This method is responsible for creating the graphical visualisation data
259              *
260              * @note ONLY based on model data
261              */
262             virtual drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override;
263 
264         public:
265             /// basic constructor, used from SdrObject.
VCOfSwVirtFlyDrawObj(SwVirtFlyDrawObj & rObj)266             explicit VCOfSwVirtFlyDrawObj(SwVirtFlyDrawObj& rObj)
267             :   ViewContactOfVirtObj(rObj)
268             {
269             }
270 
271             /// access to SwVirtFlyDrawObj
GetSwVirtFlyDrawObj() const272             SwVirtFlyDrawObj& GetSwVirtFlyDrawObj() const
273             {
274                 return static_cast<SwVirtFlyDrawObj&>(mrObject);
275             }
276         };
277     } // end of namespace contact
278 } // end of namespace sdr
279 
280 namespace sdr
281 {
282     namespace contact
283     {
createViewIndependentPrimitive2DSequence() const284         drawinglayer::primitive2d::Primitive2DContainer VCOfSwVirtFlyDrawObj::createViewIndependentPrimitive2DSequence() const
285         {
286             drawinglayer::primitive2d::Primitive2DContainer xRetval;
287             const SdrObject& rReferencedObject = GetSwVirtFlyDrawObj().GetReferencedObj();
288 
289             if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) !=  nullptr)
290             {
291                 // create an own specialized primitive which is used as repaint callpoint and HitTest
292                 // for HitTest processor (see primitive implementation above)
293                 const basegfx::B2DRange aOuterRange(GetSwVirtFlyDrawObj().getOuterBound());
294 
295                 if(!aOuterRange.isEmpty())
296                 {
297                     const drawinglayer::primitive2d::Primitive2DReference xPrimitive(
298                         new drawinglayer::primitive2d::SwVirtFlyDrawObjPrimitive(
299                             GetSwVirtFlyDrawObj(),
300                             aOuterRange));
301 
302                     xRetval = drawinglayer::primitive2d::Primitive2DContainer { xPrimitive };
303                 }
304             }
305 
306             return xRetval;
307         }
308 
309     } // end of namespace contact
310 } // end of namespace sdr
311 
getOuterBound() const312 basegfx::B2DRange SwVirtFlyDrawObj::getOuterBound() const
313 {
314     basegfx::B2DRange aOuterRange;
315     const SdrObject& rReferencedObject = GetReferencedObj();
316 
317     if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) !=  nullptr)
318     {
319         const SwFlyFrame* pFlyFrame = GetFlyFrame();
320 
321         if(pFlyFrame)
322         {
323             const tools::Rectangle aOuterRectangle(pFlyFrame->getFrameArea().Pos(), pFlyFrame->getFrameArea().SSize());
324 
325             if(!aOuterRectangle.IsEmpty())
326             {
327                 aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Left(), aOuterRectangle.Top()));
328                 aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Right(), aOuterRectangle.Bottom()));
329             }
330         }
331     }
332 
333     return aOuterRange;
334 }
335 
getInnerBound() const336 basegfx::B2DRange SwVirtFlyDrawObj::getInnerBound() const
337 {
338     basegfx::B2DRange aInnerRange;
339     const SdrObject& rReferencedObject = GetReferencedObj();
340 
341     if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) !=  nullptr)
342     {
343         const SwFlyFrame* pFlyFrame = GetFlyFrame();
344 
345         if(pFlyFrame)
346         {
347             const tools::Rectangle aInnerRectangle(pFlyFrame->getFrameArea().Pos() + pFlyFrame->getFramePrintArea().Pos(), pFlyFrame->getFramePrintArea().SSize());
348 
349             if(!aInnerRectangle.IsEmpty())
350             {
351                 aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Left(), aInnerRectangle.Top()));
352                 aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Right(), aInnerRectangle.Bottom()));
353             }
354         }
355     }
356 
357     return aInnerRange;
358 }
359 
ContainsSwGrfNode() const360 bool SwVirtFlyDrawObj::ContainsSwGrfNode() const
361 {
362     // RotGrfFlyFrame: Check if this is a SwGrfNode
363     const SwFlyFrame* pFlyFrame(GetFlyFrame());
364 
365     if(nullptr != pFlyFrame && pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame())
366     {
367         const SwNoTextFrame *const pNTF(static_cast<const SwNoTextFrame*>(pFlyFrame->Lower()));
368 
369         const SwGrfNode *const pGrfNd(pNTF->GetNode()->GetGrfNode());
370 
371         return nullptr != pGrfNd;
372     }
373 
374     return false;
375 }
376 
HasLimitedRotation() const377 bool SwVirtFlyDrawObj::HasLimitedRotation() const
378 {
379     // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation.
380     // This is the case for SwGrfNode instances
381     return ContainsSwGrfNode();
382 }
383 
Rotate(const Point & rRef,long nAngle,double sn,double cs)384 void SwVirtFlyDrawObj::Rotate(const Point& rRef, long nAngle, double sn, double cs)
385 {
386     if(ContainsSwGrfNode())
387     {
388         // RotGrfFlyFrame: Here is where the positively completed rotate interaction is executed.
389         // Rotation is in 1/100th degree and may be signed (!)
390         nAngle /= 10;
391 
392         while(nAngle < 0)
393         {
394             nAngle += 3600;
395         }
396 
397         SwWrtShell *pShForAngle = nAngle ? dynamic_cast<SwWrtShell*>(GetFlyFrame()->getRootFrame()->GetCurrShell()) : nullptr;
398         if (pShForAngle)
399         {
400             // RotGrfFlyFrame: Add transformation to placeholder object
401             Size aSize;
402             const sal_uInt16 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize));
403             SwFlyFrameAttrMgr aMgr(false, pShForAngle, Frmmgr_Type::NONE);
404 
405             aMgr.SetRotation(nOldRot, (nOldRot + static_cast<sal_uInt16>(nAngle)) % 3600, aSize);
406         }
407     }
408     else
409     {
410         // call parent
411         SdrVirtObj::Rotate(rRef, nAngle, sn, cs);
412     }
413 }
414 
CreateObjectSpecificViewContact()415 std::unique_ptr<sdr::contact::ViewContact> SwVirtFlyDrawObj::CreateObjectSpecificViewContact()
416 {
417     // need an own ViewContact (VC) to allow creation of a specialized primitive
418     // for being able to visualize the FlyFrames in primitive renderers
419     return std::make_unique<sdr::contact::VCOfSwVirtFlyDrawObj>(*this);
420 }
421 
SwVirtFlyDrawObj(SdrModel & rSdrModel,SdrObject & rNew,SwFlyFrame * pFly)422 SwVirtFlyDrawObj::SwVirtFlyDrawObj(
423     SdrModel& rSdrModel,
424     SdrObject& rNew,
425     SwFlyFrame* pFly)
426 :   SdrVirtObj(rSdrModel, rNew),
427     m_pFlyFrame(pFly)
428 {
429     const SvxProtectItem &rP = m_pFlyFrame->GetFormat()->GetProtect();
430     bMovProt = rP.IsPosProtected();
431     bSizProt = rP.IsSizeProtected();
432 }
433 
~SwVirtFlyDrawObj()434 SwVirtFlyDrawObj::~SwVirtFlyDrawObj()
435 {
436     if ( getSdrPageFromSdrObject() )    //Withdraw SdrPage the responsibility.
437         getSdrPageFromSdrObject()->RemoveObject( GetOrdNum() );
438 }
439 
GetFormat() const440 const SwFrameFormat *SwVirtFlyDrawObj::GetFormat() const
441 {
442     return GetFlyFrame()->GetFormat();
443 }
GetFormat()444 SwFrameFormat *SwVirtFlyDrawObj::GetFormat()
445 {
446     return GetFlyFrame()->GetFormat();
447 }
448 
449 // --> OD #i102707#
450 namespace
451 {
452     class RestoreMapMode
453     {
454         public:
RestoreMapMode(SwViewShell const * pViewShell)455             explicit RestoreMapMode( SwViewShell const * pViewShell )
456                 : mbMapModeRestored( false )
457                 , mpOutDev( pViewShell->GetOut() )
458             {
459                 if ( pViewShell->getPrePostMapMode() != mpOutDev->GetMapMode() )
460                 {
461                     mpOutDev->Push(PushFlags::MAPMODE);
462 
463                     GDIMetaFile* pMetaFile = mpOutDev->GetConnectMetaFile();
464                     if ( pMetaFile &&
465                          pMetaFile->IsRecord() && !pMetaFile->IsPause() )
466                     {
467                         OSL_FAIL( "MapMode restoration during meta file creation is somehow suspect - using <SetRelativeMapMode(..)>, but not sure, if correct." );
468                         mpOutDev->SetRelativeMapMode( pViewShell->getPrePostMapMode() );
469                     }
470                     else
471                     {
472                         mpOutDev->SetMapMode( pViewShell->getPrePostMapMode() );
473                     }
474 
475                     mbMapModeRestored = true;
476                 }
477             };
478 
~RestoreMapMode()479             ~RestoreMapMode()
480             {
481                 if ( mbMapModeRestored )
482                 {
483                     mpOutDev->Pop();
484                 }
485             };
486 
487         private:
488             bool mbMapModeRestored;
489             VclPtr<OutputDevice> mpOutDev;
490     };
491 }
492 // <--
493 
wrap_DoPaintObject(drawinglayer::geometry::ViewInformation2D const & rViewInformation) const494 void SwVirtFlyDrawObj::wrap_DoPaintObject(
495     drawinglayer::geometry::ViewInformation2D const& rViewInformation) const
496 {
497     SwViewShell* pShell = m_pFlyFrame->getRootFrame()->GetCurrShell();
498 
499     // Only paint when we have a current shell and a DrawingLayer paint is in progress.
500     // This avoids evtl. problems with renderers which do processing stuff,
501     // but no paints. IsPaintInProgress() depends on SW repaint, so, as long
502     // as SW paints self and calls DrawLayer() for Heaven and Hell, this will
503     // be correct
504     if ( pShell && pShell->IsDrawingLayerPaintInProgress() )
505     {
506         bool bDrawObject(true);
507 
508         if ( !SwFlyFrame::IsPaint( const_cast<SwVirtFlyDrawObj*>(this), pShell ) )
509         {
510             bDrawObject = false;
511         }
512 
513         if ( bDrawObject )
514         {
515             // if there's no viewport set, all fly-frames will be painted,
516             // which is slow, wastes memory, and can cause other trouble.
517             (void) rViewInformation; // suppress "unused parameter" warning
518             assert(comphelper::LibreOfficeKit::isActive() || !rViewInformation.getViewport().isEmpty());
519             if ( !m_pFlyFrame->IsFlyInContentFrame() )
520             {
521                 // it is also necessary to restore the VCL MapMode from ViewInformation since e.g.
522                 // the VCL PixelRenderer resets it at the used OutputDevice. Unfortunately, this
523                 // excludes shears and rotates which are not expressible in MapMode.
524                 // OD #i102707#
525                 // new helper class to restore MapMode - restoration, only if
526                 // needed and consideration of paint for meta file creation .
527                 RestoreMapMode aRestoreMapModeIfNeeded( pShell );
528 
529                 // paint the FlyFrame (use standard VCL-Paint)
530                 m_pFlyFrame->PaintSwFrame( *pShell->GetOut(), GetFlyFrame()->getFrameArea() );
531             }
532         }
533     }
534 }
535 
TakeObjInfo(SdrObjTransformInfoRec & rInfo) const536 void SwVirtFlyDrawObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const
537 {
538     rInfo.bMoveAllowed =
539     rInfo.bResizeFreeAllowed = rInfo.bResizePropAllowed = true;
540 
541     // RotGrfFlyFrame: Some rotation may be allowed
542     rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = HasLimitedRotation();
543 
544     rInfo.bMirrorFreeAllowed = rInfo.bMirror45Allowed =
545     rInfo.bMirror90Allowed   = rInfo.bShearAllowed    =
546     rInfo.bCanConvToPath     = rInfo.bCanConvToPoly   =
547     rInfo.bCanConvToPathLineToArea = rInfo.bCanConvToPolyLineToArea = false;
548 }
549 
550 // SwVirtFlyDrawObj - Size Determination
551 
SetRect() const552 void SwVirtFlyDrawObj::SetRect() const
553 {
554     if ( GetFlyFrame()->getFrameArea().HasArea() )
555         const_cast<SwVirtFlyDrawObj*>(this)->aOutRect = GetFlyFrame()->getFrameArea().SVRect();
556     else
557         const_cast<SwVirtFlyDrawObj*>(this)->aOutRect = tools::Rectangle();
558 }
559 
GetCurrentBoundRect() const560 const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const
561 {
562     SetRect();
563     return aOutRect;
564 }
565 
GetLastBoundRect() const566 const tools::Rectangle& SwVirtFlyDrawObj::GetLastBoundRect() const
567 {
568     return GetCurrentBoundRect();
569 }
570 
RecalcBoundRect()571 void SwVirtFlyDrawObj::RecalcBoundRect()
572 {
573     SetRect();
574 }
575 
RecalcSnapRect()576 void SwVirtFlyDrawObj::RecalcSnapRect()
577 {
578     SetRect();
579 }
580 
GetSnapRect() const581 const tools::Rectangle& SwVirtFlyDrawObj::GetSnapRect()  const
582 {
583     SetRect();
584     return aOutRect;
585 }
586 
SetSnapRect(const tools::Rectangle &)587 void SwVirtFlyDrawObj::SetSnapRect(const tools::Rectangle& )
588 {
589     tools::Rectangle aTmp( GetLastBoundRect() );
590     SetRect();
591     SetChanged();
592     BroadcastObjectChange();
593     if (pUserCall!=nullptr)
594         pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp);
595 }
596 
NbcSetSnapRect(const tools::Rectangle &)597 void SwVirtFlyDrawObj::NbcSetSnapRect(const tools::Rectangle& )
598 {
599     SetRect();
600 }
601 
GetLogicRect() const602 const tools::Rectangle& SwVirtFlyDrawObj::GetLogicRect() const
603 {
604     SetRect();
605     return aOutRect;
606 }
607 
SetLogicRect(const tools::Rectangle &)608 void SwVirtFlyDrawObj::SetLogicRect(const tools::Rectangle& )
609 {
610     tools::Rectangle aTmp( GetLastBoundRect() );
611     SetRect();
612     SetChanged();
613     BroadcastObjectChange();
614     if (pUserCall!=nullptr)
615         pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp);
616 }
617 
NbcSetLogicRect(const tools::Rectangle &)618 void SwVirtFlyDrawObj::NbcSetLogicRect(const tools::Rectangle& )
619 {
620     SetRect();
621 }
622 
TakeXorPoly() const623 ::basegfx::B2DPolyPolygon SwVirtFlyDrawObj::TakeXorPoly() const
624 {
625     const tools::Rectangle aSourceRectangle(GetFlyFrame()->getFrameArea().SVRect());
626     const ::basegfx::B2DRange aSourceRange = vcl::unotools::b2DRectangleFromRectangle(aSourceRectangle);
627     ::basegfx::B2DPolyPolygon aRetval;
628 
629     aRetval.append(::basegfx::utils::createPolygonFromRect(aSourceRange));
630 
631     return aRetval;
632 }
633 
634 //  SwVirtFlyDrawObj::Move() and Resize()
NbcMove(const Size & rSiz)635 void SwVirtFlyDrawObj::NbcMove(const Size& rSiz)
636 {
637     if(GetFlyFrame()->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame())
638     {
639         // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used),
640         // we need to fall back to the un-transformed state to keep the old code below
641         // working properly. Restore FrameArea and use aOutRect from old FrameArea.
642         TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
643         pTransformableSwFrame->restoreFrameAreas();
644         aOutRect = GetFlyFrame()->getFrameArea().SVRect();
645     }
646 
647     aOutRect.Move( rSiz );
648     const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() );
649     const Point aNewPos( aOutRect.TopLeft() );
650     const SwRect aFlyRect( aOutRect );
651 
652     //If the Fly has an automatic align (right or top),
653     //so preserve the automatic.
654     SwFrameFormat *pFormat = GetFlyFrame()->GetFormat();
655     const sal_Int16 eHori = pFormat->GetHoriOrient().GetHoriOrient();
656     const sal_Int16 eVert = pFormat->GetVertOrient().GetVertOrient();
657     const sal_Int16 eRelHori = pFormat->GetHoriOrient().GetRelationOrient();
658     const sal_Int16 eRelVert = pFormat->GetVertOrient().GetRelationOrient();
659     //On paragraph bound Flys starting from the new position a new
660     //anchor must be set. Anchor and the new RelPos is calculated and
661     //placed by the Fly itself.
662     if( GetFlyFrame()->IsFlyAtContentFrame() )
663     {
664         static_cast<SwFlyAtContentFrame*>(GetFlyFrame())->SetAbsPos( aNewPos );
665     }
666     else
667     {
668         const SwFrameFormat *pTmpFormat = GetFormat();
669         const SwFormatVertOrient &rVert = pTmpFormat->GetVertOrient();
670         const SwFormatHoriOrient &rHori = pTmpFormat->GetHoriOrient();
671         long lXDiff = aNewPos.X() - aOldPos.X();
672         if( rHori.IsPosToggle() && text::HoriOrientation::NONE == eHori &&
673             !GetFlyFrame()->FindPageFrame()->OnRightPage() )
674             lXDiff = -lXDiff;
675 
676         if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() &&
677             text::HoriOrientation::NONE == eHori )
678             lXDiff = -lXDiff;
679 
680         long lYDiff = aNewPos.Y() - aOldPos.Y();
681         if( GetFlyFrame()->GetAnchorFrame()->IsVertical() )
682         {
683             //lXDiff -= rVert.GetPos();
684             //lYDiff += rHori.GetPos();
685 
686             if ( GetFlyFrame()->GetAnchorFrame()->IsVertLR() )
687             {
688                 lXDiff += rVert.GetPos();
689                 lXDiff = -lXDiff;
690             }
691             else
692             {
693                 lXDiff -= rVert.GetPos();
694                 lYDiff += rHori.GetPos();
695             }
696         }
697         else
698         {
699             lXDiff += rHori.GetPos();
700             lYDiff += rVert.GetPos();
701         }
702 
703         if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() &&
704             text::HoriOrientation::NONE != eHori )
705             lXDiff = GetFlyFrame()->GetAnchorFrame()->getFrameArea().Width() -
706                      aFlyRect.Width() - lXDiff;
707 
708         const Point aTmp( lXDiff, lYDiff );
709         GetFlyFrame()->ChgRelPos( aTmp );
710     }
711 
712     SwAttrSet aSet( pFormat->GetDoc()->GetAttrPool(),
713                                             RES_VERT_ORIENT, RES_HORI_ORIENT );
714     SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
715     SwFormatVertOrient aVert( pFormat->GetVertOrient() );
716     bool bPut = false;
717 
718     if( !GetFlyFrame()->IsFlyLayFrame() &&
719         ::GetHtmlMode(pFormat->GetDoc()->GetDocShell()) )
720     {
721         //In HTML-Mode only automatic aligns are allowed.
722         //Only we can try a snap to left/right respectively left-/right border
723         const SwFrame* pAnch = GetFlyFrame()->GetAnchorFrame();
724         bool bNextLine = false;
725 
726         if( !GetFlyFrame()->IsAutoPos() || text::RelOrientation::PAGE_FRAME != aHori.GetRelationOrient() )
727         {
728             if( text::RelOrientation::CHAR == eRelHori )
729             {
730                 aHori.SetHoriOrient( text::HoriOrientation::LEFT );
731                 aHori.SetRelationOrient( text::RelOrientation::CHAR );
732             }
733             else
734             {
735                 bNextLine = true;
736                 //Horizontal Align:
737                 const bool bLeftFrame =
738                     aFlyRect.Left() < pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Left(),
739                     bLeftPrt = aFlyRect.Left() + aFlyRect.Width() <
740                                pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width()/2;
741                 if ( bLeftFrame || bLeftPrt )
742                 {
743                     aHori.SetHoriOrient( text::HoriOrientation::LEFT );
744                     aHori.SetRelationOrient( bLeftFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
745                 }
746                 else
747                 {
748                     const bool bRightFrame = aFlyRect.Left() >
749                                        pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width();
750                     aHori.SetHoriOrient( text::HoriOrientation::RIGHT );
751                     aHori.SetRelationOrient( bRightFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
752                 }
753             }
754             aSet.Put( aHori );
755         }
756         //Vertical alignment simply is retained principally,
757         //only on manual align will be switched over.
758         bool bRelChar = text::RelOrientation::CHAR == eRelVert;
759         aVert.SetVertOrient( eVert != text::VertOrientation::NONE ? eVert :
760                 GetFlyFrame()->IsFlyInContentFrame() ? text::VertOrientation::CHAR_CENTER :
761                 bRelChar && bNextLine ? text::VertOrientation::CHAR_TOP : text::VertOrientation::TOP );
762         if( bRelChar )
763             aVert.SetRelationOrient( text::RelOrientation::CHAR );
764         else
765             aVert.SetRelationOrient( text::RelOrientation::PRINT_AREA );
766         aSet.Put( aVert );
767         bPut = true;
768     }
769 
770     //We want preferably not to lose the automatic alignments.
771     if ( !bPut && bInResize )
772     {
773         if ( text::HoriOrientation::NONE != eHori )
774         {
775             aHori.SetHoriOrient( eHori );
776             aHori.SetRelationOrient( eRelHori );
777             aSet.Put( aHori );
778             bPut = true;
779         }
780         if ( text::VertOrientation::NONE != eVert )
781         {
782             aVert.SetVertOrient( eVert );
783             aVert.SetRelationOrient( eRelVert );
784             aSet.Put( aVert );
785             bPut = true;
786         }
787     }
788     if ( bPut )
789         pFormat->SetFormatAttr( aSet );
790 }
791 
792 
NbcCrop(const basegfx::B2DPoint & rRef,double fxFact,double fyFact)793 void SwVirtFlyDrawObj::NbcCrop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
794 {
795     // Get Wrt Shell
796     SwWrtShell *pSh = dynamic_cast<SwWrtShell*>( GetFlyFrame()->getRootFrame()->GetCurrShell() );
797 
798     if (!pSh)
799     {
800         return;
801     }
802 
803     GraphicObject const *pGraphicObject = pSh->GetGraphicObj();
804 
805     if (!pGraphicObject)
806     {
807         return;
808     }
809 
810     // Get graphic object size in 100th of mm
811     const MapMode aMapMode100thmm(MapUnit::Map100thMM);
812     Size aGraphicSize(pGraphicObject->GetPrefSize());
813 
814     if( MapUnit::MapPixel == pGraphicObject->GetPrefMapMode().GetMapUnit() )
815     {
816         aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphicSize, aMapMode100thmm );
817     }
818     else
819     {
820         aGraphicSize = OutputDevice::LogicToLogic( aGraphicSize, pGraphicObject->GetPrefMapMode(), aMapMode100thmm);
821     }
822 
823     if( aGraphicSize.Width() == 0 || aGraphicSize.Height() == 0 )
824     {
825         return ;
826     }
827 
828     const bool bIsTransformableSwFrame(
829         GetFlyFrame()->IsFlyFreeFrame() &&
830         static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame());
831 
832     if(bIsTransformableSwFrame)
833     {
834         // When we have a change and are in transformed state (e.g. rotation used),
835         // we need to fall back to the un-transformed state to keep the old code below
836         // working properly. Restore FrameArea and use aOutRect from old FrameArea.
837         TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
838         pTransformableSwFrame->restoreFrameAreas();
839         aOutRect = GetFlyFrame()->getFrameArea().SVRect();
840     }
841 
842     // Compute old and new rect. This will give us the deformation to apply to
843     // the object to crop. OldRect is the inner frame, see getFullDragClone()
844     // below where getFramePrintAreaTransformation is used as object geometry for Crop
845     const tools::Rectangle aOldRect(
846         GetFlyFrame()->getFrameArea().TopLeft() + GetFlyFrame()->getFramePrintArea().TopLeft(),
847         GetFlyFrame()->getFramePrintArea().SSize());
848     const long nOldWidth(aOldRect.GetWidth());
849     const long nOldHeight(aOldRect.GetHeight());
850 
851     if (!nOldWidth || !nOldHeight)
852     {
853         return;
854     }
855 
856     // rRef is relative to the Crop-Action, si in X/Y-Ranges of [0.0 .. 1.0],
857     // to get the correct absolute position, transform using the old Rect
858     const Point aRef(
859         aOldRect.Left() + basegfx::fround(aOldRect.GetWidth() * rRef.getX()),
860         aOldRect.Top() + basegfx::fround(aOldRect.GetHeight() * rRef.getY()));
861 
862     // apply transformation, use old ResizeRect for now
863     tools::Rectangle aNewRect( aOldRect );
864     ResizeRect(
865         aNewRect,
866         aRef,
867         Fraction(fxFact),
868         Fraction(fyFact));
869 
870     // Get old values for crop in 10th of mm
871     SfxItemSet aSet( pSh->GetAttrPool(), svl::Items<RES_GRFATR_CROPGRF, RES_GRFATR_CROPGRF>{} );
872     pSh->GetCurAttr( aSet );
873     SwCropGrf aCrop( aSet.Get(RES_GRFATR_CROPGRF) );
874 
875     tools::Rectangle aCropRectangle(
876         convertTwipToMm100(aCrop.GetLeft()),
877         convertTwipToMm100(aCrop.GetTop()),
878         convertTwipToMm100(aCrop.GetRight()),
879         convertTwipToMm100(aCrop.GetBottom()) );
880 
881     // Compute delta to apply
882     double fScaleX = ( aGraphicSize.Width() - aCropRectangle.Left() - aCropRectangle.Right() ) / static_cast<double>(nOldWidth);
883     double fScaleY = ( aGraphicSize.Height() - aCropRectangle.Top() - aCropRectangle.Bottom() ) / static_cast<double>(nOldHeight);
884 
885     sal_Int32 nDiffLeft = aNewRect.Left() - aOldRect.Left();
886     sal_Int32 nDiffTop = aNewRect.Top() - aOldRect.Top();
887     sal_Int32 nDiffRight = aNewRect.Right() - aOldRect.Right();
888     sal_Int32 nDiffBottom = aNewRect.Bottom() - aOldRect.Bottom();
889 
890     // Compute new values in 10th of mm
891     sal_Int32 nLeftCrop = static_cast<sal_Int32>( aCropRectangle.Left() + nDiffLeft * fScaleX );
892     sal_Int32 nTopCrop = static_cast<sal_Int32>( aCropRectangle.Top() + nDiffTop * fScaleY );
893     sal_Int32 nRightCrop = static_cast<sal_Int32>( aCropRectangle.Right() - nDiffRight * fScaleX );
894     sal_Int32 nBottomCrop = static_cast<sal_Int32>( aCropRectangle.Bottom() - nDiffBottom * fScaleY );
895 
896     // Apply values
897     pSh->StartAllAction();
898     // pSh->StartUndo(SwUndoId::START);
899 
900     // Set new crop values in twips
901     aCrop.SetLeft  (convertMm100ToTwip(nLeftCrop));
902     aCrop.SetTop   (convertMm100ToTwip(nTopCrop));
903     aCrop.SetRight (convertMm100ToTwip(nRightCrop));
904     aCrop.SetBottom(convertMm100ToTwip(nBottomCrop));
905     pSh->SetAttrItem(aCrop);
906 
907     // Set new frame size
908     SwFrameFormat *pFormat = GetFormat();
909     SwFormatFrameSize aSz( pFormat->GetFrameSize() );
910     const long aNewWidth(aNewRect.GetWidth() + (aOutRect.GetWidth() - aOldRect.GetWidth()));
911     const long aNewHeight(aNewRect.GetHeight() + (aOutRect.GetHeight() - aOldRect.GetHeight()));
912     aSz.SetWidth(aNewWidth);
913     aSz.SetHeight(aNewHeight);
914     pFormat->GetDoc()->SetAttr( aSz, *pFormat );
915 
916     // add move - to make result look better. Fill with defaults
917     // for the untransformed case
918     Point aNewTopLeft(aNewRect.TopLeft());
919     const Point aOldTopLeft(aOldRect.TopLeft());
920 
921     if(bIsTransformableSwFrame)
922     {
923         // Need to correct the NewTopLeft position in transformed state to make
924         // the interaction look correct. First, extract rotation
925         basegfx::B2DVector aScale, aTranslate;
926         double fRotate, fShearX;
927         GetFlyFrame()->getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
928 
929         // calc the center of the unchanged object
930         const basegfx::B2DPoint aFormerCenter(
931             GetFlyFrame()->getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
932 
933         // define the existing rotation around that former center
934         const basegfx::B2DHomMatrix aRotFormerCenter(
935             basegfx::utils::createRotateAroundPoint(
936                 aFormerCenter.getX(),
937                 aFormerCenter.getY(),
938                 fRotate));
939 
940         // use the new center of the unrotated object, rotate it around the
941         // former center
942         const Point aNewCenter(aNewRect.Center());
943         const basegfx::B2DPoint aRotNewCenter(
944             aRotFormerCenter * basegfx::B2DPoint(aNewCenter.X(), aNewCenter.Y()));
945 
946         // Create the new TopLeft of the unrotated, cropped object by creating
947         // as if re-creating the unrotated geometry
948         aNewTopLeft = Point(
949             basegfx::fround(aRotNewCenter.getX() - (0.5 * aNewRect.getWidth())),
950             basegfx::fround(aRotNewCenter.getY() - (0.5 * aNewRect.getHeight())));
951     }
952 
953     // check if we have movement and execute if yes
954     const Size aDeltaMove(
955         aNewTopLeft.X() - aOldTopLeft.X(),
956         aNewTopLeft.Y() - aOldTopLeft.Y());
957 
958     if(0 != aDeltaMove.Width() || 0 != aDeltaMove.Height())
959     {
960         NbcMove(aDeltaMove);
961     }
962 
963     // pSh->EndUndo(SwUndoId::END);
964     pSh->EndAllAction();
965 }
966 
NbcResize(const Point & rRef,const Fraction & xFact,const Fraction & yFact)967 void SwVirtFlyDrawObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
968 {
969     const SwFrame* pTmpFrame = GetFlyFrame()->GetAnchorFrame();
970 
971     if( !pTmpFrame )
972     {
973         pTmpFrame = GetFlyFrame();
974     }
975 
976     const bool bVertX(pTmpFrame->IsVertical());
977     const bool bRTL(pTmpFrame->IsRightToLeft());
978     const bool bVertL2RX(pTmpFrame->IsVertLR());
979     const bool bUseRightEdge((bVertX && !bVertL2RX ) || bRTL);
980     const bool bIsTransformableSwFrame(
981         GetFlyFrame()->IsFlyFreeFrame() &&
982         static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame());
983 
984     if(bIsTransformableSwFrame)
985     {
986         // When we have a change in transformed state, we need to fall back to the
987         // state without possible transformations.
988         // In the Resize case to correctly handle the changes, apply to the transformation
989         // and extract the new, untransformed state from that modified transformation
990         basegfx::B2DHomMatrix aNewMat(GetFlyFrame()->getFrameAreaTransformation());
991         const basegfx::B2DPoint aRef(rRef.X(), rRef.Y());
992 
993         // apply state to already valid transformation
994         aNewMat.translate(-aRef.getX(), -aRef.getY());
995         aNewMat.scale(double(xFact), double(yFact));
996         aNewMat.translate(aRef.getX(), aRef.getY());
997 
998         // get center of transformed state
999         const basegfx::B2DPoint aCenter(aNewMat * basegfx::B2DPoint(0.5, 0.5));
1000 
1001         // decompose to extract scale
1002         basegfx::B2DVector aScale, aTranslate;
1003         double fRotate, fShearX;
1004         aNewMat.decompose(aScale, aTranslate, fRotate, fShearX);
1005         const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
1006 
1007         // create new modified, but untransformed OutRect
1008         aOutRect = tools::Rectangle(
1009             basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())),
1010             basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())),
1011             basegfx::fround(aCenter.getX() + (0.5 * aAbsScale.getX())),
1012             basegfx::fround(aCenter.getY() + (0.5 * aAbsScale.getY())));
1013 
1014         // restore FrameAreas so that actions below not adapted to new
1015         // full transformations take the correct actions
1016         TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
1017         pTransformableSwFrame->restoreFrameAreas();
1018     }
1019     else
1020     {
1021         ResizeRect( aOutRect, rRef, xFact, yFact );
1022     }
1023 
1024     // Position may also change, remember old one. This is now already
1025     // the one in the unrotated, old coordinate system
1026     Point aOldPos(bUseRightEdge ? GetFlyFrame()->getFrameArea().TopRight() : GetFlyFrame()->getFrameArea().Pos());
1027 
1028     // get target size in old coordinate system
1029     Size aSz( aOutRect.Right() - aOutRect.Left() + 1, aOutRect.Bottom()- aOutRect.Top()  + 1 );
1030 
1031     // compare with restored FrameArea
1032     if( aSz != GetFlyFrame()->getFrameArea().SSize() )
1033     {
1034         //The width of the columns should not be too narrow
1035         if ( GetFlyFrame()->Lower() && GetFlyFrame()->Lower()->IsColumnFrame() )
1036         {
1037             SwBorderAttrAccess aAccess( SwFrame::GetCache(), GetFlyFrame() );
1038             const SwBorderAttrs &rAttrs = *aAccess.Get();
1039             long nMin = rAttrs.CalcLeftLine()+rAttrs.CalcRightLine();
1040             const SwFormatCol& rCol = rAttrs.GetAttrSet().GetCol();
1041             if ( rCol.GetColumns().size() > 1 )
1042             {
1043                 for ( const auto &rC : rCol.GetColumns() )
1044                 {
1045                     nMin += rC.GetLeft() + rC.GetRight() + MINFLY;
1046                 }
1047                 nMin -= MINFLY;
1048             }
1049             aSz.setWidth( std::max( aSz.Width(), nMin ) );
1050         }
1051 
1052         SwFrameFormat *pFormat = GetFormat();
1053         const SwFormatFrameSize aOldFrameSz( pFormat->GetFrameSize() );
1054         GetFlyFrame()->ChgSize( aSz );
1055         SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() );
1056 
1057         if ( aFrameSz.GetWidthPercent() || aFrameSz.GetHeightPercent() )
1058         {
1059             long nRelWidth, nRelHeight;
1060             const SwFrame *pRel = GetFlyFrame()->IsFlyLayFrame() ?
1061                                 GetFlyFrame()->GetAnchorFrame() :
1062                                 GetFlyFrame()->GetAnchorFrame()->GetUpper();
1063             const SwViewShell *pSh = GetFlyFrame()->getRootFrame()->GetCurrShell();
1064 
1065             if ( pSh && pRel->IsBodyFrame() &&
1066                  pSh->GetViewOptions()->getBrowseMode() &&
1067                  pSh->VisArea().HasArea() )
1068             {
1069                 nRelWidth  = pSh->GetBrowseWidth();
1070                 nRelHeight = pSh->VisArea().Height();
1071                 const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
1072                 nRelHeight -= 2*aBorder.Height();
1073             }
1074             else
1075             {
1076                 nRelWidth  = pRel->getFramePrintArea().Width();
1077                 nRelHeight = pRel->getFramePrintArea().Height();
1078             }
1079 
1080             if ( aFrameSz.GetWidthPercent() && aFrameSz.GetWidthPercent() != SwFormatFrameSize::SYNCED &&
1081                  aOldFrameSz.GetWidth() != aFrameSz.GetWidth() )
1082             {
1083                 aFrameSz.SetWidthPercent( sal_uInt8(aSz.Width() * 100.0 / nRelWidth + 0.5) );
1084             }
1085 
1086             if ( aFrameSz.GetHeightPercent() && aFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED &&
1087                  aOldFrameSz.GetHeight() != aFrameSz.GetHeight() )
1088             {
1089                 aFrameSz.SetHeightPercent( sal_uInt8(aSz.Height() * 100.0 / nRelHeight + 0.5) );
1090             }
1091 
1092             pFormat->GetDoc()->SetAttr( aFrameSz, *pFormat );
1093         }
1094     }
1095 
1096     //Position can also be changed, get new one
1097     const Point aNewPos(bUseRightEdge ? aOutRect.Right() + 1 : aOutRect.Left(), aOutRect.Top());
1098 
1099     if ( aNewPos != aOldPos )
1100     {
1101         // Former late change in aOutRect by ChgSize
1102         // is now taken into account directly by calculating
1103         // aNewPos *after* calling ChgSize (see old code).
1104         // Still need to adapt aOutRect since the 'Move' is already applied
1105         // here (see ResizeRect) and it's the same SdrObject
1106         const Size aDeltaMove(
1107                 aNewPos.X() - aOldPos.X(),
1108                 aNewPos.Y() - aOldPos.Y());
1109         aOutRect.Move(-aDeltaMove.Width(), -aDeltaMove.Height());
1110 
1111         // Now, move as needed (no empty delta which was a hack anyways)
1112         if(bIsTransformableSwFrame)
1113         {
1114             // need to save aOutRect to FrameArea, will be restored to aOutRect in
1115             // SwVirtFlyDrawObj::NbcMove currently for TransformableSwFrames
1116             SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*GetFlyFrame());
1117             aFrm.setSwRect(aOutRect);
1118         }
1119 
1120         // keep old hack - not clear what happens here
1121         bInResize = true;
1122         NbcMove(aDeltaMove);
1123         bInResize = false;
1124     }
1125 }
1126 
Move(const Size & rSiz)1127 void SwVirtFlyDrawObj::Move(const Size& rSiz)
1128 {
1129     NbcMove( rSiz );
1130     SetChanged();
1131     GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
1132 }
1133 
Resize(const Point & rRef,const Fraction & xFact,const Fraction & yFact,bool)1134 void SwVirtFlyDrawObj::Resize(const Point& rRef,
1135                     const Fraction& xFact, const Fraction& yFact, bool /*bUnsetRelative*/)
1136 {
1137     NbcResize( rRef, xFact, yFact );
1138     SetChanged();
1139     GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
1140 }
1141 
Crop(const basegfx::B2DPoint & rRef,double fxFact,double fyFact)1142 void SwVirtFlyDrawObj::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
1143 {
1144     NbcCrop( rRef, fxFact, fyFact );
1145     SetChanged();
1146     GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
1147 }
1148 
1149 // RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame
getPossibleRotationFromFraphicFrame(Size & rSize) const1150 sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const
1151 {
1152     sal_uInt16 nRetval(0);
1153     const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower());
1154 
1155     if(pNoTx)
1156     {
1157         SwNoTextNode& rNoTNd = const_cast< SwNoTextNode& >(*static_cast<const SwNoTextNode*>(pNoTx->GetNode()));
1158         SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
1159 
1160         if(nullptr != pGrfNd)
1161         {
1162             const SwAttrSet& rSet = pGrfNd->GetSwAttrSet();
1163             const SwRotationGrf& rRotation = rSet.GetRotationGrf();
1164 
1165             rSize = rRotation.GetUnrotatedSize();
1166             nRetval = rRotation.GetValue();
1167         }
1168     }
1169 
1170     return nRetval;
1171 }
1172 
GetRotateAngle() const1173 long SwVirtFlyDrawObj::GetRotateAngle() const
1174 {
1175     if(ContainsSwGrfNode())
1176     {
1177         Size aSize;
1178         return getPossibleRotationFromFraphicFrame(aSize);
1179     }
1180     else
1181     {
1182         return SdrVirtObj::GetRotateAngle();
1183     }
1184 }
1185 
getFullDragClone() const1186 SdrObjectUniquePtr SwVirtFlyDrawObj::getFullDragClone() const
1187 {
1188     // call parent
1189     SdrObjectUniquePtr pRetval = SdrVirtObj::getFullDragClone();
1190 
1191     if(pRetval && GetFlyFrame() && ContainsSwGrfNode())
1192     {
1193         // RotGrfFlyFrame3: get inner bounds/transformation
1194         const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation());
1195 
1196         pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon());
1197     }
1198 
1199     return pRetval;
1200 }
1201 
addCropHandles(SdrHdlList & rTarget) const1202 void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const
1203 {
1204     // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame
1205     if(GetFlyFrame()->getFrameArea().HasArea())
1206     {
1207         // Use InnerBound, OuterBound (same as GetFlyFrame()->getFrameArea().SVRect())
1208         // may have a distance to InnerBound which needs to be taken into account.
1209         // The Graphic is mapped to InnerBound, as is the rotated Graphic.
1210         const basegfx::B2DRange aTargetRange(getInnerBound());
1211 
1212         if(!aTargetRange.isEmpty())
1213         {
1214             // RotGrfFlyFrame3: get inner bounds/transformation
1215             const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation());
1216 
1217             // break up matrix
1218             basegfx::B2DTuple aScale;
1219             basegfx::B2DTuple aTranslate;
1220             double fRotate(0.0);
1221             double fShearX(0.0);
1222             aTargetTransform.decompose(aScale, aTranslate, fRotate, fShearX);
1223             basegfx::B2DPoint aPos;
1224 
1225             aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.0);
1226             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate));
1227             aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0);
1228             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate));
1229             aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0);
1230             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate));
1231             aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5);
1232             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate));
1233             aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5);
1234             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate));
1235             aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0);
1236             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate));
1237             aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0);
1238             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate));
1239             aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0);
1240             rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate));
1241         }
1242     }
1243 }
1244 
1245 // Macro
1246 
GetMacroPointer(const SdrObjMacroHitRec &) const1247 PointerStyle  SwVirtFlyDrawObj::GetMacroPointer(
1248     const SdrObjMacroHitRec& ) const
1249 {
1250     return PointerStyle::RefHand;
1251 }
1252 
HasMacro() const1253 bool SwVirtFlyDrawObj::HasMacro() const
1254 {
1255     const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL();
1256     return rURL.GetMap() || !rURL.GetURL().isEmpty();
1257 }
1258 
CheckMacroHit(const SdrObjMacroHitRec & rRec) const1259 SdrObject* SwVirtFlyDrawObj::CheckMacroHit( const SdrObjMacroHitRec& rRec ) const
1260 {
1261     const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL();
1262     if( rURL.GetMap() || !rURL.GetURL().isEmpty() )
1263     {
1264         SwRect aRect;
1265         if ( m_pFlyFrame->Lower() && m_pFlyFrame->Lower()->IsNoTextFrame() )
1266         {
1267             aRect = m_pFlyFrame->getFramePrintArea();
1268             aRect += m_pFlyFrame->getFrameArea().Pos();
1269         }
1270         else
1271             aRect = m_pFlyFrame->getFrameArea();
1272 
1273         if( aRect.IsInside( rRec.aPos ) )
1274         {
1275             aRect.Pos().setX(aRect.Pos().getX() + rRec.nTol);
1276             aRect.Pos().setY(aRect.Pos().getY() + rRec.nTol);
1277             aRect.SSize().AdjustHeight( -(2 * rRec.nTol) );
1278             aRect.SSize().AdjustWidth( -(2 * rRec.nTol) );
1279 
1280             if( aRect.IsInside( rRec.aPos ) )
1281             {
1282                 if( !rURL.GetMap() ||
1283                     m_pFlyFrame->GetFormat()->GetIMapObject( rRec.aPos, m_pFlyFrame ))
1284                     return const_cast<SwVirtFlyDrawObj*>(this);
1285 
1286                 return nullptr;
1287             }
1288         }
1289     }
1290     return SdrObject::CheckMacroHit( rRec );
1291 }
1292 
IsTextBox() const1293 bool SwVirtFlyDrawObj::IsTextBox() const
1294 {
1295     return SwTextBoxHelper::isTextBox(GetFormat(), RES_FLYFRMFMT);
1296 }
1297 
1298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1299