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