Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / fly.cxx
blob7fc0a566b9d5605501bac82b27fb8c57ca7e5ad3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_wasm_strip.h>
22 #include <svl/itemiter.hxx>
23 #include <vcl/imap.hxx>
24 #include <tools/helpers.hxx>
25 #include <editeng/protitem.hxx>
26 #include <editeng/opaqitem.hxx>
27 #include <editeng/ulspitem.hxx>
28 #include <editeng/frmdiritem.hxx>
29 #include <fmtfsize.hxx>
30 #include <fmtclds.hxx>
31 #include <fmtcntnt.hxx>
32 #include <fmturl.hxx>
33 #include <fmtsrnd.hxx>
34 #include <fmtornt.hxx>
35 #include <fmtcnct.hxx>
36 #include <ndgrf.hxx>
37 #include <tolayoutanchoredobjectposition.hxx>
38 #include <fmtfollowtextflow.hxx>
39 #include <sortedobjs.hxx>
40 #include <objectformatter.hxx>
41 #include <ndole.hxx>
42 #include <swtable.hxx>
43 #include <svx/svdoashp.hxx>
44 #include <svx/svdpage.hxx>
45 #include <layouter.hxx>
46 #include <pagefrm.hxx>
47 #include <rootfrm.hxx>
48 #include <viewimp.hxx>
49 #include <viewopt.hxx>
50 #include <dcontact.hxx>
51 #include <dflyobj.hxx>
52 #include <dview.hxx>
53 #include <frmatr.hxx>
54 #include <frmtool.hxx>
55 #include <hints.hxx>
56 #include <tabfrm.hxx>
57 #include <txtfrm.hxx>
58 #include <notxtfrm.hxx>
59 #include <flyfrms.hxx>
60 #include <sectfrm.hxx>
61 #include <vcl/svapp.hxx>
62 #include <calbck.hxx>
63 #include <IDocumentDrawModelAccess.hxx>
64 #include <IDocumentSettingAccess.hxx>
65 #include <IDocumentLayoutAccess.hxx>
66 #include <textboxhelper.hxx>
67 #include <txtfly.hxx>
68 #include <ndindex.hxx>
69 #include <basegfx/matrix/b2dhommatrixtools.hxx>
70 #include <osl/diagnose.h>
71 #include <o3tl/string_view.hxx>
73 #include <wrtsh.hxx>
74 #include <view.hxx>
75 #include <edtwin.hxx>
76 #include <bodyfrm.hxx>
77 #include <FrameControlsManager.hxx>
78 #include <ndtxt.hxx>
79 #include <formatflysplit.hxx>
81 using namespace ::com::sun::star;
83 namespace
85 /// Gets the bottom position which is a deadline for a split fly.
86 SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& rAnchor)
88 SwRectFnSet aRectFnSet(pFly);
90 const SwPageFrame* pPage = rAnchor.FindPageFrame();
91 if (!pPage)
93 return 0;
96 const SwFrame* pBody = pPage->FindBodyCont();
97 if (!pBody)
99 return 0;
102 const auto& rFrameFormat = pFly->GetFrameFormat();
103 const IDocumentSettingAccess& rIDSA = rFrameFormat.getIDocumentSettingAccess();
104 // Allow overlap with bottom margin / footer only in case we're relative to the page frame.
105 bool bVertPageFrame = rFrameFormat.GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME;
106 bool bInBody = rAnchor.IsInDocBody();
107 bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN) && (bVertPageFrame || !bInBody);
108 if (bLegacy)
110 // Word <= 2010 style: the fly can overlap with the bottom margin / footer area in case the
111 // fly height fits the body height and the fly bottom fits the page.
112 // See if the fly height would fit at least the page height, ignoring the vertical offset.
113 SwTwips nFlyHeight = aRectFnSet.GetHeight(pFly->getFrameArea());
114 SwTwips nPageHeight = aRectFnSet.GetHeight(pPage->getFramePrintArea());
115 SwTwips nFlyTop = aRectFnSet.GetTop(pFly->getFrameArea());
116 SwTwips nBodyTop = aRectFnSet.GetTop(pBody->getFrameArea());
117 if (nFlyTop < nBodyTop)
119 // Fly frame overlaps with the top margin area, ignore that part of the fly frame for
120 // top/height purposes.
121 nFlyHeight -= nBodyTop - nFlyTop;
122 nFlyTop = nBodyTop;
124 if (nFlyHeight <= nPageHeight)
126 // Yes, it would fit: allow overlap if there is no problematic vertical offset.
127 SwTwips nDeadline = aRectFnSet.GetBottom(pPage->getFrameArea());
128 SwTwips nBodyHeight = aRectFnSet.GetHeight(pBody->getFramePrintArea());
129 if (nDeadline - nFlyTop > nBodyHeight)
131 // If the fly would now grow to nDeadline then it would not fit the body height, so
132 // limit the height.
133 nDeadline = nFlyTop + nBodyHeight;
135 return nDeadline;
139 // Word >= 2013 style: the fly has to stay inside the body frame.
140 return aRectFnSet.GetPrtBottom(*pBody);
144 static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame );
146 SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch, bool bFollow ) :
147 SwLayoutFrame( pFormat, pSib ),
148 // #i26791#
149 m_pPrevLink( nullptr ),
150 m_pNextLink( nullptr ),
151 m_bInCnt( false ),
152 m_bAtCnt( false ),
153 m_bLayout( false ),
154 m_bAutoPosition( false ),
155 m_bDeleted( false ),
156 m_nAuthor( std::string::npos ),
157 m_bValidContentPos( false )
159 mnFrameType = SwFrameType::Fly;
161 m_bInvalid = m_bNotifyBack = true;
162 m_bLocked = m_bMinHeight =
163 m_bHeightClipped = m_bWidthClipped = m_bFormatHeightOnly = false;
165 // Size setting: Fixed size is always the width
166 const SwFormatFrameSize &rFrameSize = pFormat->GetFrameSize();
167 const SvxFrameDirection nDir = pFormat->GetFormatAttr( RES_FRAMEDIR ).GetValue();
168 if( SvxFrameDirection::Environment == nDir )
170 mbDerivedVert = true;
171 mbDerivedR2L = true;
173 else
175 mbInvalidVert = false;
176 mbDerivedVert = false;
177 mbDerivedR2L = false;
178 if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
180 mbVertLR = false;
181 mbVertical = false;
183 else
185 const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
186 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
188 mbVertLR = false;
189 mbVertical = false;
191 else
193 mbVertical = true;
195 if ( SvxFrameDirection::Vertical_LR_TB == nDir )
196 mbVertLR = true;
197 else if (nDir == SvxFrameDirection::Vertical_LR_BT)
199 mbVertLR = true;
200 mbVertLRBT = true;
202 else
203 mbVertLR = false;
207 mbInvalidR2L = false;
208 if( SvxFrameDirection::Horizontal_RL_TB == nDir )
209 mbRightToLeft = true;
210 else
211 mbRightToLeft = false;
215 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
216 aFrm.Width( rFrameSize.GetWidth() );
217 aFrm.Height( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable ? MINFLY : rFrameSize.GetHeight() );
220 // Fixed or variable Height?
221 if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
222 m_bMinHeight = true;
223 else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
224 mbFixSize = true;
226 // insert columns, if necessary
227 InsertColumns();
229 // First the Init, then the Content:
230 // This is due to the fact that the Content may have Objects/Frames,
231 // which are then registered
232 InitDrawObj(*pAnch);
234 Chain( pAnch );
236 if (!bFollow)
238 InsertCnt();
241 // Put it somewhere outside so that out document is not formatted unnecessarily often
242 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
243 aFrm.Pos().setX(FAR_AWAY);
244 aFrm.Pos().setY(FAR_AWAY);
247 void SwFlyFrame::Chain( SwFrame* _pAnch )
249 // Connect to chain neighbours.
250 // No problem, if a neighbor doesn't exist - the construction of the
251 // neighbor will make the connection
252 const SwFormatChain& rChain = GetFormat()->GetChain();
253 if ( !(rChain.GetPrev() || rChain.GetNext()) )
254 return;
256 if ( rChain.GetNext() )
258 SwFlyFrame* pFollow = FindChainNeighbour( *rChain.GetNext(), _pAnch );
259 if ( pFollow )
261 OSL_ENSURE( !pFollow->GetPrevLink(), "wrong chain detected" );
262 if ( !pFollow->GetPrevLink() )
263 SwFlyFrame::ChainFrames( this, pFollow );
266 if ( rChain.GetPrev() )
268 SwFlyFrame *pMaster = FindChainNeighbour( *rChain.GetPrev(), _pAnch );
269 if ( pMaster )
271 OSL_ENSURE( !pMaster->GetNextLink(), "wrong chain detected" );
272 if ( !pMaster->GetNextLink() )
273 SwFlyFrame::ChainFrames( pMaster, this );
278 void SwFlyFrame::InsertCnt()
280 if ( GetPrevLink() )
281 return;
283 const SwFormatContent& rContent = GetFormat()->GetContent();
284 OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." );
285 SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
286 // Lower() means SwColumnFrame; the Content then needs to be inserted into the (Column)BodyFrame
287 ::InsertCnt_( Lower() ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()) : static_cast<SwLayoutFrame*>(this),
288 GetFormat()->GetDoc(), nIndex );
290 // NoText always have a fixed height.
291 if ( Lower() && Lower()->IsNoTextFrame() )
293 mbFixSize = true;
294 m_bMinHeight = false;
298 void SwFlyFrame::InsertColumns()
300 // #i97379#
301 // Check, if column are allowed.
302 // Columns are not allowed for fly frames, which represent graphics or embedded objects.
303 const SwFormatContent& rContent = GetFormat()->GetContent();
304 OSL_ENSURE( rContent.GetContentIdx(), "<SwFlyFrame::InsertColumns()> - no content prepared." );
305 SwNodeIndex aFirstContent( *(rContent.GetContentIdx()), 1 );
306 if ( aFirstContent.GetNode().IsNoTextNode() )
308 return;
311 const SwFormatCol &rCol = GetFormat()->GetCol();
312 if ( rCol.GetNumCols() <= 1 )
313 return;
315 // Start off PrtArea to be as large as Frame, so that we can put in the columns
316 // properly. It'll adjust later on.
318 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
319 aPrt.Width( getFrameArea().Width() );
320 aPrt.Height( getFrameArea().Height() );
323 const SwFormatCol aOld; // ChgColumns() also needs an old value passed
324 ChgColumns( aOld, rCol );
327 void SwFlyFrame::DestroyImpl()
329 // Accessible objects for fly frames will be destroyed in this destructor.
330 // For frames bound as char or frames that don't have an anchor we have
331 // to do that ourselves. For any other frame the call RemoveFly at the
332 // anchor will do that.
333 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
334 if( IsAccessibleFrame() && GetFormat() && (IsFlyInContentFrame() || !GetAnchorFrame()) )
336 SwRootFrame *pRootFrame = getRootFrame();
337 if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
339 SwViewShell *pVSh = pRootFrame->GetCurrShell();
340 if( pVSh && pVSh->Imp() )
342 // Lowers aren't disposed already, so we have to do a recursive
343 // dispose
344 pVSh->Imp()->DisposeAccessibleFrame( this, true );
348 #endif
350 if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
352 ClearTmpConsiderWrapInfluence(); // remove this from SwLayouter
354 Unchain();
356 DeleteCnt();
358 if ( GetAnchorFrame() )
359 AnchorFrame()->RemoveFly( this );
362 FinitDrawObj();
364 SwLayoutFrame::DestroyImpl();
366 SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(getRootFrame()->GetCurrShell());
367 UpdateUnfloatButton(pWrtSh, false);
370 SwFlyFrame::~SwFlyFrame()
374 const IDocumentDrawModelAccess& SwFlyFrame::getIDocumentDrawModelAccess()
376 return GetFormat()->getIDocumentDrawModelAccess();
379 void SwFlyFrame::Unchain()
381 if ( GetPrevLink() )
382 UnchainFrames( GetPrevLink(), this );
383 if ( GetNextLink() )
384 UnchainFrames( this, GetNextLink() );
387 void SwFlyFrame::DeleteCnt()
389 SwFrame* pFrame = m_pLower;
390 while ( pFrame )
392 while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
394 SwAnchoredObject *pAnchoredObj = (*pFrame->GetDrawObjs())[0];
395 if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
397 SwFrame::DestroyFrame(pFlyFrame);
399 else if ( dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) != nullptr )
401 // consider 'virtual' drawing objects
402 SdrObject* pObj = pAnchoredObj->DrawObj();
403 if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj) )
405 pDrawVirtObj->RemoveFromWriterLayout();
406 pDrawVirtObj->RemoveFromDrawingPage();
408 else
410 SwDrawContact* pContact =
411 static_cast<SwDrawContact*>(::GetUserCall( pObj ));
412 if ( pContact )
414 pContact->DisconnectFromLayout();
420 pFrame->RemoveFromLayout();
421 SwFrame::DestroyFrame(pFrame);
422 pFrame = m_pLower;
425 InvalidatePage();
428 void SwFlyFrame::InitDrawObj(SwFrame const& rAnchorFrame)
430 SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat(), rAnchorFrame));
432 // Set the right Layer
433 IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
434 SdrLayerID nHeavenId = rIDDMA.GetHeavenId();
435 SdrLayerID nHellId = rIDDMA.GetHellId();
436 GetVirtDrawObj()->SetLayer( GetFormat()->GetOpaque().GetValue()
437 ? nHeavenId
438 : nHellId );
441 static SwPosition ResolveFlyAnchor(SwFrameFormat const& rFlyFrame)
443 SwFormatAnchor const& rAnch(rFlyFrame.GetAnchor());
444 if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
445 { // arbitrarily pick last node
446 return SwPosition(rFlyFrame.GetDoc()->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
448 else
450 SwPosition const*const pPos(rAnch.GetContentAnchor());
451 assert(pPos);
452 if (SwFrameFormat const*const pParent = pPos->GetNode().GetFlyFormat())
454 return ResolveFlyAnchor(*pParent);
456 else if (pPos->GetContentNode())
458 return *pPos;
460 else
462 return SwPosition(*pPos->GetNode().GetContentNode(), 0);
467 void SwFlyFrame::FinitDrawObj()
469 if(!GetVirtDrawObj() )
470 return;
471 SwFormat* pFormat = GetFormat();
472 // Deregister from SdrPageViews if the Objects is still selected there.
473 if(!pFormat->GetDoc()->IsInDtor())
475 SwViewShell* p1St = getRootFrame()->GetCurrShell();
476 if(p1St)
478 for(SwViewShell& rCurrentShell : p1St->GetRingContainer())
479 { // At the moment the Drawing can do just do an Unmark on everything,
480 // as the Object was already removed
481 if (rCurrentShell.HasDrawView() &&
482 rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())
484 SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, &rCurrentShell);
485 if (pOldSelFly == this)
487 assert(rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1);
488 if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell))
489 { // tdf#131679 move any cursor out of fly
490 rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
491 if (pOldSelFly)
493 SwPaM const temp(ResolveFlyAnchor(*pOldSelFly->GetFormat()));
494 pFEShell->SetSelection(temp);
495 // could also call SetCursor() like SwFEShell::SelectObj()
496 // does, but that would access layout a bit much...
499 else
501 rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
509 SwVirtFlyDrawObj* pVirtDrawObj = GetVirtDrawObj();
510 // Else calls delete of the ContactObj
511 pVirtDrawObj->SetUserCall(nullptr);
513 if ( pVirtDrawObj->getSdrPageFromSdrObject() )
514 pVirtDrawObj->getSdrPageFromSdrObject()->RemoveObject( pVirtDrawObj->GetOrdNum() );
515 ClearDrawObj();
518 void SwFlyFrame::ChainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
520 OSL_ENSURE( pMaster && pFollow, "incomplete chain" );
521 OSL_ENSURE( !pMaster->GetNextLink(), "link can not be changed" );
522 OSL_ENSURE( !pFollow->GetPrevLink(), "link can not be changed" );
524 pMaster->m_pNextLink = pFollow;
525 pFollow->m_pPrevLink = pMaster;
527 if ( pMaster->ContainsContent() )
529 // To get a text flow we need to invalidate
530 SwFrame *pInva = pMaster->FindLastLower();
531 SwRectFnSet aRectFnSet(pMaster);
532 const tools::Long nBottom = aRectFnSet.GetPrtBottom(*pMaster);
533 while ( pInva )
535 if( aRectFnSet.BottomDist( pInva->getFrameArea(), nBottom ) <= 0 )
537 pInva->InvalidateSize();
538 pInva->Prepare();
539 pInva = pInva->FindPrev();
541 else
542 pInva = nullptr;
546 if ( pFollow->ContainsContent() )
548 // There's only the content from the Masters left; the content from the Follow
549 // does not have any Frames left (should always be exactly one empty TextNode).
550 SwFrame *pFrame = pFollow->ContainsContent();
551 OSL_ENSURE( !pFrame->IsTabFrame() && !pFrame->FindNext(), "follow in chain contains content" );
552 pFrame->Cut();
553 SwFrame::DestroyFrame(pFrame);
556 // invalidate accessible relation set (accessibility wrapper)
557 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
558 SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
559 if( pSh )
561 SwRootFrame* pLayout = pMaster->getRootFrame();
562 if( pLayout && pLayout->IsAnyShellAccessible() )
563 pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
565 #endif
568 void SwFlyFrame::UnchainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
570 pMaster->m_pNextLink = nullptr;
571 pFollow->m_pPrevLink = nullptr;
573 if ( pFollow->ContainsContent() )
575 // The Master sucks up the content of the Follow
576 SwLayoutFrame *pUpper = pMaster;
577 if ( pUpper->Lower()->IsColumnFrame() )
579 pUpper = static_cast<SwLayoutFrame*>(pUpper->GetLastLower());
580 pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); // The (Column)BodyFrame
581 OSL_ENSURE( pUpper && pUpper->IsColBodyFrame(), "Missing ColumnBody" );
583 SwFlyFrame *pFoll = pFollow;
584 while ( pFoll )
586 SwFrame *pTmp = ::SaveContent( pFoll );
587 if ( pTmp )
588 ::RestoreContent( pTmp, pUpper, pMaster->FindLastLower() );
589 pFoll->SetCompletePaint();
590 pFoll->InvalidateSize();
591 pFoll = pFoll->GetNextLink();
595 // The Follow needs his own content to be served
596 const SwFormatContent &rContent = pFollow->GetFormat()->GetContent();
597 OSL_ENSURE( rContent.GetContentIdx(), ":-( No content prepared." );
598 SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
599 // Lower() means SwColumnFrame: this one contains another SwBodyFrame
600 ::InsertCnt_( pFollow->Lower() ? const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFollow->Lower())->Lower()))
601 : static_cast<SwLayoutFrame*>(pFollow),
602 pFollow->GetFormat()->GetDoc(), ++nIndex );
604 // invalidate accessible relation set (accessibility wrapper)
605 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
606 SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
607 if( pSh )
609 SwRootFrame* pLayout = pMaster->getRootFrame();
610 if( pLayout && pLayout->IsAnyShellAccessible() )
611 pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
613 #endif
616 SwFlyFrame *SwFlyFrame::FindChainNeighbour( SwFrameFormat const &rChain, SwFrame *pAnch )
618 // We look for the Fly that's in the same Area.
619 // Areas can for now only be Head/Footer or Flys.
621 if ( !pAnch ) // If an Anchor was passed along, that one counts (ctor!)
622 pAnch = AnchorFrame();
624 SwLayoutFrame *pLay;
625 if ( pAnch->IsInFly() )
626 pLay = pAnch->FindFlyFrame();
627 else
629 // FindFooterOrHeader is not appropriate here, as we may not have a
630 // connection to the Anchor yet.
631 pLay = pAnch->GetUpper();
632 while ( pLay && !(pLay->GetType() & (SwFrameType::Header|SwFrameType::Footer)) )
633 pLay = pLay->GetUpper();
636 SwIterator<SwFlyFrame,SwFormat> aIter( rChain );
637 SwFlyFrame *pFly = aIter.First();
638 if ( pLay )
640 while ( pFly )
642 if ( pFly->GetAnchorFrame() )
644 if ( pFly->GetAnchorFrame()->IsInFly() )
646 if ( pFly->AnchorFrame()->FindFlyFrame() == pLay )
647 break;
649 else if ( pLay == pFly->FindFooterOrHeader() )
650 break;
652 pFly = aIter.Next();
655 else if ( pFly )
657 OSL_ENSURE( !aIter.Next(), "chain with more than one instance" );
659 return pFly;
662 bool SwFlyFrame::IsFlySplitAllowed() const
664 if (!IsFlyAtContentFrame())
666 return false;
669 const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
670 if (rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
672 return false;
675 if (FindFooterOrHeader())
677 // Adding a new page would not increase the header/footer area.
678 return false;
681 const SwFrame* pFlyAnchor = GetAnchorFrame();
682 if (pFlyAnchor && pFlyAnchor->FindColFrame())
684 // No split in multi-column sections, so GetFlyAnchorBottom() can assume that our innermost
685 // body frame and the page's body frame is the same.
686 // This is also consistent with the Word behavior.
687 return false;
690 if (pFlyAnchor && pFlyAnchor->IsInFootnote())
692 // No split in footnotes.
693 return false;
696 const SwFlyFrameFormat* pFormat = GetFormat();
697 const SwFormatVertOrient& rVertOrient = pFormat->GetVertOrient();
698 if (rVertOrient.GetVertOrient() == text::VertOrientation::BOTTOM)
700 // We have to grow from bottom to top, and the fly split code assumes that we grow from top
701 // to bottom, so don't split for now.
702 if (rVertOrient.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA)
704 // Growing from the bottom of the body frame.
705 return false;
709 return pFormat->GetFlySplit().GetValue();
712 SwFrame *SwFlyFrame::FindLastLower()
714 SwFrame *pRet = ContainsAny();
715 if ( pRet && pRet->IsInTab() )
716 pRet = pRet->FindTabFrame();
717 SwFrame *pNxt = pRet;
718 while ( pNxt && IsAnLower( pNxt ) )
719 { pRet = pNxt;
720 pNxt = pNxt->FindNext();
722 return pRet;
725 bool SwFlyFrame::FrameSizeChg( const SwFormatFrameSize &rFrameSize )
727 bool bRet = false;
728 SwTwips nDiffHeight = getFrameArea().Height();
729 if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable )
730 mbFixSize = m_bMinHeight = false;
731 else
733 if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
735 mbFixSize = true;
736 m_bMinHeight = false;
738 else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
740 mbFixSize = false;
741 m_bMinHeight = true;
743 nDiffHeight -= rFrameSize.GetHeight();
745 // If the Fly contains columns, we already need to set the Fly
746 // and the Columns to the required value or else we run into problems.
747 if ( Lower() )
749 if ( Lower()->IsColumnFrame() )
751 const SwRect aOld( GetObjRectWithSpaces() );
752 const Size aOldSz( getFramePrintArea().SSize() );
753 const SwTwips nDiffWidth = getFrameArea().Width() - rFrameSize.GetWidth();
756 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
757 aFrm.Height( aFrm.Height() - nDiffHeight );
758 aFrm.Width ( aFrm.Width() - nDiffWidth );
761 // #i68520#
762 InvalidateObjRectWithSpaces();
765 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
766 aPrt.Height( aPrt.Height() - nDiffHeight );
767 aPrt.Width ( aPrt.Width() - nDiffWidth );
770 ChgLowersProp( aOldSz );
771 ::Notify( this, FindPageFrame(), aOld );
772 setFrameAreaPositionValid(false);
773 bRet = true;
775 else if ( Lower()->IsNoTextFrame() )
777 mbFixSize = true;
778 m_bMinHeight = false;
781 return bRet;
784 void SwFlyFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
786 if (rHint.GetId() == SfxHintId::SwLegacyModify)
788 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
789 SwFlyFrameInvFlags eInvFlags = SwFlyFrameInvFlags::NONE;
790 if(pLegacy->m_pNew && pLegacy->m_pOld && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
792 SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
793 SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
794 const SfxPoolItem* pNItem = aNIter.GetCurItem();
795 const SfxPoolItem* pOItem = aOIter.GetCurItem();
796 SwAttrSetChg aOldSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld));
797 SwAttrSetChg aNewSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew));
800 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
801 pNItem = aNIter.NextItem();
802 pOItem = aOIter.NextItem();
803 } while(pNItem);
804 if(aOldSet.Count() || aNewSet.Count())
805 SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
807 else
808 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
810 if(eInvFlags == SwFlyFrameInvFlags::NONE)
811 return;
813 Invalidate_();
814 if(eInvFlags & SwFlyFrameInvFlags::InvalidatePos)
816 InvalidatePos_();
817 // #i68520#
818 InvalidateObjRectWithSpaces();
820 if(eInvFlags & SwFlyFrameInvFlags::InvalidateSize)
822 InvalidateSize_();
823 // #i68520#
824 InvalidateObjRectWithSpaces();
826 if(eInvFlags & SwFlyFrameInvFlags::InvalidatePrt)
827 InvalidatePrt_();
828 if(eInvFlags & SwFlyFrameInvFlags::SetNotifyBack)
829 SetNotifyBack();
830 if(eInvFlags & SwFlyFrameInvFlags::SetCompletePaint)
831 SetCompletePaint();
832 if((eInvFlags & SwFlyFrameInvFlags::ClearContourCache) && Lower() && Lower()->IsNoTextFrame())
833 ClrContourCache( GetVirtDrawObj() );
834 SwRootFrame *pRoot;
835 if(eInvFlags & SwFlyFrameInvFlags::InvalidateBrowseWidth && nullptr != (pRoot = getRootFrame()))
836 pRoot->InvalidateBrowseWidth();
837 // #i28701#
838 if(eInvFlags & SwFlyFrameInvFlags::UpdateObjInSortedList)
840 // update sorted object lists, the Writer fly frame is registered at.
841 UpdateObjInSortedList();
844 // #i87645# - reset flags for the layout process (only if something has been invalidated)
845 ResetLayoutProcessBools();
847 else if (rHint.GetId() == SfxHintId::SwGetZOrder)
849 auto pGetZOrdnerHint = static_cast<const sw::GetZOrderHint*>(&rHint);
850 const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
851 if (rFormat.Which() == RES_FLYFRMFMT && rFormat.getIDocumentLayoutAccess().GetCurrentViewShell()) // #i11176#
852 pGetZOrdnerHint->m_rnZOrder = GetVirtDrawObj()->GetOrdNum();
854 else if (rHint.GetId() == SfxHintId::SwGetObjectConnected)
856 auto pConnectedHint = static_cast<const sw::GetObjectConnectedHint*>(&rHint);
857 const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
858 if (!pConnectedHint->m_risConnected && rFormat.Which() == RES_FLYFRMFMT && (!pConnectedHint->m_pRoot || pConnectedHint->m_pRoot == getRootFrame()))
859 pConnectedHint->m_risConnected = true;
863 void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
864 SwFlyFrameInvFlags &rInvFlags,
865 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
867 bool bClear = true;
868 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
869 SwViewShell *pSh = getRootFrame()->GetCurrShell();
870 switch( nWhich )
872 case RES_VERT_ORIENT:
873 case RES_HORI_ORIENT:
874 // #i18732# - consider new option 'follow text flow'
875 case RES_FOLLOW_TEXT_FLOW:
877 // ATTENTION: Always also change Action in ChgRePos()!
878 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
880 break;
881 // #i28701# - consider new option 'wrap influence on position'
882 case RES_WRAP_INFLUENCE_ON_OBJPOS:
884 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack
885 | SwFlyFrameInvFlags::UpdateObjInSortedList;
887 break;
888 case RES_SURROUND:
890 //#i28701# - invalidate position on change of
891 // wrapping style.
892 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
893 // The background needs to be messaged and invalidated
894 const SwRect aTmp( GetObjRectWithSpaces() );
895 NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameAttributesChanged );
897 // By changing the flow of frame-bound Frames, a vertical alignment
898 // can be activated/deactivated => MakeFlyPos
899 if( RndStdIds::FLY_AT_FLY == GetFormat()->GetAnchor().GetAnchorId() )
900 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
902 // Delete contour in the Node if necessary
903 if ( Lower() && Lower()->IsNoTextFrame() &&
904 !GetFormat()->GetSurround().IsContour() )
906 SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
907 if ( pNd->HasContour() )
908 pNd->SetContour( nullptr );
910 // #i28701# - perform reorder of object lists
911 // at anchor frame and at page frame.
912 rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
914 break;
916 case RES_PROTECT:
917 if (pNew)
919 const SvxProtectItem *pP = static_cast<const SvxProtectItem*>(pNew);
920 GetVirtDrawObj()->SetMoveProtect( pP->IsPosProtected() );
921 GetVirtDrawObj()->SetResizeProtect( pP->IsSizeProtected() );
922 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
923 if( pSh )
925 SwRootFrame* pLayout = getRootFrame();
926 if( pLayout && pLayout->IsAnyShellAccessible() )
927 pSh->Imp()->InvalidateAccessibleEditableState( true, this );
929 #endif
931 break;
932 case RES_COL:
933 if (pOld && pNew)
935 ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
936 const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
937 if ( FrameSizeChg( rNew ) )
938 NotifyDrawObj();
939 rInvFlags |= SwFlyFrameInvFlags::InvalidateSize | SwFlyFrameInvFlags::SetNotifyBack
940 | SwFlyFrameInvFlags::SetCompletePaint;
942 break;
944 case RES_FRM_SIZE:
945 case RES_FMT_CHG:
946 case RES_FLY_SPLIT:
948 const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
949 if ( FrameSizeChg( rNew ) )
950 NotifyDrawObj();
951 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
952 | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetNotifyBack
953 | SwFlyFrameInvFlags::SetCompletePaint
954 | SwFlyFrameInvFlags::InvalidateBrowseWidth
955 | SwFlyFrameInvFlags::ClearContourCache;
956 if (pOld && RES_FMT_CHG == nWhich)
958 SwRect aNew( GetObjRectWithSpaces() );
959 SwRect aOld( getFrameArea() );
960 const SvxULSpaceItem &rUL = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetULSpace();
961 aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
962 aOld.AddHeight(rUL.GetLower() );
963 const SvxLRSpaceItem &rLR = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetLRSpace();
964 aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
965 aOld.AddWidth(rLR.GetRight() );
966 aNew.Union( aOld );
967 NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
969 // Special case:
970 // When assigning a template we cannot rely on the old column
971 // attribute. As there need to be at least enough for ChgColumns,
972 // we need to create a temporary attribute.
973 SwFormatCol aCol;
974 if ( Lower() && Lower()->IsColumnFrame() )
976 sal_uInt16 nCol = 0;
977 SwFrame *pTmp = Lower();
979 { ++nCol;
980 pTmp = pTmp->GetNext();
981 } while ( pTmp );
982 aCol.Init( nCol, 0, 1000 );
984 ChgColumns( aCol, GetFormat()->GetCol() );
987 SwFormatURL aURL( GetFormat()->GetURL() );
989 SwFormatFrameSize *pNewFormatFrameSize = nullptr;
990 SwFormatChg *pOldFormatChg = nullptr;
991 if (nWhich == RES_FRM_SIZE)
992 pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew));
993 else if (nWhich == RES_FMT_CHG)
994 pOldFormatChg = const_cast<SwFormatChg*>(static_cast<const SwFormatChg*>(pOld));
995 else if (nWhich == RES_FLY_SPLIT)
997 // If the fly frame has a table lower, invalidate that, so it joins its follow tab
998 // frames and re-splits according to the new fly split rule.
999 if (Lower() && Lower()->IsTabFrame())
1001 Lower()->InvalidateAll_();
1005 if (aURL.GetMap() && (pNewFormatFrameSize || pOldFormatChg))
1007 const SwFormatFrameSize &rOld = pNewFormatFrameSize ?
1008 *pNewFormatFrameSize :
1009 pOldFormatChg->pChangedFormat->GetFrameSize();
1010 //#35091# Can be "times zero", when loading the template
1011 if ( rOld.GetWidth() && rOld.GetHeight() )
1014 Fraction aScaleX( rOld.GetWidth(), rNew.GetWidth() );
1015 Fraction aScaleY( rOld.GetHeight(), rOld.GetHeight() );
1016 aURL.GetMap()->Scale( aScaleX, aScaleY );
1017 SwFrameFormat *pFormat = GetFormat();
1018 pFormat->LockModify();
1019 pFormat->SetFormatAttr( aURL );
1020 pFormat->UnlockModify();
1023 const SvxProtectItem &rP = GetFormat()->GetProtect();
1024 GetVirtDrawObj()->SetMoveProtect( rP.IsPosProtected() );
1025 GetVirtDrawObj()->SetResizeProtect( rP.IsSizeProtected() );
1027 if ( pSh )
1028 pSh->InvalidateWindows( getFrameArea() );
1029 const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
1030 const SdrLayerID nId = GetFormat()->GetOpaque().GetValue() ?
1031 rIDDMA.GetHeavenId() :
1032 rIDDMA.GetHellId();
1033 GetVirtDrawObj()->SetLayer( nId );
1035 if ( Lower() )
1037 // Delete contour in the Node if necessary
1038 if( Lower()->IsNoTextFrame() &&
1039 !GetFormat()->GetSurround().IsContour() )
1041 SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
1042 if ( pNd->HasContour() )
1043 pNd->SetContour( nullptr );
1045 else if( !Lower()->IsColumnFrame() )
1047 SwFrame* pFrame = GetLastLower();
1048 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
1049 pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
1053 // #i28701# - perform reorder of object lists
1054 // at anchor frame and at page frame.
1055 rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
1057 break;
1059 case RES_UL_SPACE:
1060 case RES_LR_SPACE:
1062 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
1063 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
1064 getRootFrame()->InvalidateBrowseWidth();
1065 SwRect aNew( GetObjRectWithSpaces() );
1066 SwRect aOld( getFrameArea() );
1067 if (pNew)
1069 if ( RES_UL_SPACE == nWhich )
1071 const SvxULSpaceItem &rUL = *static_cast<const SvxULSpaceItem*>(pNew);
1072 aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
1073 aOld.AddHeight(rUL.GetLower() );
1075 else
1077 const SvxLRSpaceItem &rLR = *static_cast<const SvxLRSpaceItem*>(pNew);
1078 aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
1079 aOld.AddWidth(rLR.GetRight() );
1082 aNew.Union( aOld );
1083 NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
1085 break;
1087 case RES_TEXT_VERT_ADJUST:
1089 InvalidateContentPos();
1090 rInvFlags |= SwFlyFrameInvFlags::SetCompletePaint;
1092 break;
1094 case RES_BOX:
1095 case RES_SHADOW:
1096 rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
1097 | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetCompletePaint;
1098 break;
1100 case RES_FRAMEDIR :
1101 SetDerivedVert( false );
1102 SetDerivedR2L( false );
1103 CheckDirChange();
1104 break;
1106 case RES_OPAQUE:
1107 if (pNew)
1109 if ( pSh )
1110 pSh->InvalidateWindows( getFrameArea() );
1112 const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
1113 const SdrLayerID nId = static_cast<const SvxOpaqueItem*>(pNew)->GetValue() ?
1114 rIDDMA.GetHeavenId() :
1115 rIDDMA.GetHellId();
1116 GetVirtDrawObj()->SetLayer( nId );
1117 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1118 if( pSh )
1120 SwRootFrame* pLayout = getRootFrame();
1121 if( pLayout && pLayout->IsAnyShellAccessible() )
1123 pSh->Imp()->DisposeAccessibleFrame( this );
1124 pSh->Imp()->AddAccessibleFrame( this );
1127 #endif
1128 // #i28701# - perform reorder of object lists
1129 // at anchor frame and at page frame.
1130 rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
1132 break;
1134 case RES_URL:
1135 // The interface changes the frame size when interacting with text frames,
1136 // the Map, however, needs to be relative to FrameSize().
1137 if ( (!Lower() || !Lower()->IsNoTextFrame()) && pNew && pOld &&
1138 static_cast<const SwFormatURL*>(pNew)->GetMap() && static_cast<const SwFormatURL*>(pOld)->GetMap() )
1140 const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
1141 if ( rSz.GetHeight() != getFrameArea().Height() ||
1142 rSz.GetWidth() != getFrameArea().Width() )
1144 SwFormatURL aURL( GetFormat()->GetURL() );
1145 Fraction aScaleX( getFrameArea().Width(), rSz.GetWidth() );
1146 Fraction aScaleY( getFrameArea().Height(), rSz.GetHeight() );
1147 aURL.GetMap()->Scale( aScaleX, aScaleY );
1148 SwFrameFormat *pFormat = GetFormat();
1149 pFormat->LockModify();
1150 pFormat->SetFormatAttr( aURL );
1151 pFormat->UnlockModify();
1154 // No invalidation necessary
1155 break;
1157 case RES_CHAIN:
1158 if (pNew)
1160 const SwFormatChain *pChain = static_cast<const SwFormatChain*>(pNew);
1161 if ( pChain->GetNext() )
1163 SwFlyFrame *pFollow = FindChainNeighbour( *pChain->GetNext() );
1164 if ( GetNextLink() && pFollow != GetNextLink() )
1165 SwFlyFrame::UnchainFrames( this, GetNextLink());
1166 if ( pFollow )
1168 if ( pFollow->GetPrevLink() &&
1169 pFollow->GetPrevLink() != this )
1170 SwFlyFrame::UnchainFrames( pFollow->GetPrevLink(),
1171 pFollow );
1172 if ( !GetNextLink() )
1173 SwFlyFrame::ChainFrames( this, pFollow );
1176 else if ( GetNextLink() )
1177 SwFlyFrame::UnchainFrames( this, GetNextLink() );
1178 if ( pChain->GetPrev() )
1180 SwFlyFrame *pMaster = FindChainNeighbour( *pChain->GetPrev() );
1181 if ( GetPrevLink() && pMaster != GetPrevLink() )
1182 SwFlyFrame::UnchainFrames( GetPrevLink(), this );
1183 if ( pMaster )
1185 if ( pMaster->GetNextLink() &&
1186 pMaster->GetNextLink() != this )
1187 SwFlyFrame::UnchainFrames( pMaster,
1188 pMaster->GetNextLink() );
1189 if ( !GetPrevLink() )
1190 SwFlyFrame::ChainFrames( pMaster, this );
1193 else if ( GetPrevLink() )
1194 SwFlyFrame::UnchainFrames( GetPrevLink(), this );
1196 [[fallthrough]];
1197 default:
1198 bClear = false;
1200 if ( !bClear )
1201 return;
1203 if ( pOldSet || pNewSet )
1205 if ( pOldSet )
1206 pOldSet->ClearItem( nWhich );
1207 if ( pNewSet )
1208 pNewSet->ClearItem( nWhich );
1210 else
1212 SwModify aMod;
1213 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
1217 /// Gets information from the Modify
1218 bool SwFlyFrame::GetInfo( SfxPoolItem & rInfo ) const
1220 if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
1221 return false; // There's a FlyFrame, so use it
1222 return true; // Continue searching
1225 void SwFlyFrame::Invalidate_( SwPageFrame const *pPage )
1227 InvalidatePage( pPage );
1228 m_bNotifyBack = m_bInvalid = true;
1230 SwFlyFrame *pFrame;
1231 if ( GetAnchorFrame() && nullptr != (pFrame = AnchorFrame()->FindFlyFrame()) )
1233 // Very bad case: If the Fly is bound within another Fly which
1234 // contains columns, the Format should be from that one.
1235 if ( !pFrame->IsLocked() && !pFrame->IsColLocked() &&
1236 pFrame->Lower() && pFrame->Lower()->IsColumnFrame() )
1237 pFrame->InvalidateSize();
1240 // #i85216#
1241 // if vertical position is oriented at a layout frame inside a ghost section,
1242 // assure that the position is invalidated and that the information about
1243 // the vertical position oriented frame is cleared
1244 if ( GetVertPosOrientFrame() && GetVertPosOrientFrame()->IsLayoutFrame() )
1246 const SwSectionFrame* pSectFrame( GetVertPosOrientFrame()->FindSctFrame() );
1247 if ( pSectFrame && pSectFrame->GetSection() == nullptr )
1249 InvalidatePos();
1250 ClearVertPosOrientFrame();
1255 /** Change the relative position
1257 * The position will be Fix automatically and the attribute is changed accordingly.
1259 void SwFlyFrame::ChgRelPos( const Point &rNewPos )
1261 if ( GetCurrRelPos() == rNewPos )
1262 return;
1264 SwFrameFormat *pFormat = GetFormat();
1265 const bool bVert = GetAnchorFrame()->IsVertical();
1266 const SwTwips nNewY = bVert ? rNewPos.X() : rNewPos.Y();
1267 SwTwips nTmpY = nNewY == LONG_MAX ? 0 : nNewY;
1268 if( bVert )
1269 nTmpY = -nTmpY;
1270 SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( pFormat->GetDoc()->GetAttrPool() );
1272 SwFormatVertOrient aVert( pFormat->GetVertOrient() );
1273 const SwTextFrame *pAutoFrame = nullptr;
1274 // #i34948# - handle also at-page and at-fly anchored
1275 // Writer fly frames
1276 const RndStdIds eAnchorType = GetFrameFormat().GetAnchor().GetAnchorId();
1277 if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
1279 aVert.SetVertOrient( text::VertOrientation::NONE );
1280 aVert.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
1282 else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
1284 aVert.SetVertOrient( text::VertOrientation::NONE );
1285 aVert.SetRelationOrient( text::RelOrientation::FRAME );
1287 else if ( IsFlyAtContentFrame() || text::VertOrientation::NONE != aVert.GetVertOrient() )
1289 if( text::RelOrientation::CHAR == aVert.GetRelationOrient() && IsAutoPos() )
1291 if( LONG_MAX != nNewY )
1293 aVert.SetVertOrient( text::VertOrientation::NONE );
1294 assert(GetAnchorFrame()->IsTextFrame());
1295 pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
1296 TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
1297 *pFormat->GetAnchor().GetContentAnchor()));
1298 while( pAutoFrame->GetFollow() &&
1299 pAutoFrame->GetFollow()->GetOffset() <= nOfs )
1301 if( pAutoFrame == GetAnchorFrame() )
1302 nTmpY += pAutoFrame->GetRelPos().Y();
1303 nTmpY -= pAutoFrame->GetUpper()->getFramePrintArea().Height();
1304 pAutoFrame = pAutoFrame->GetFollow();
1306 nTmpY = static_cast<SwFlyAtContentFrame*>(this)->GetRelCharY(pAutoFrame)-nTmpY;
1308 else
1309 aVert.SetVertOrient( text::VertOrientation::CHAR_BOTTOM );
1311 else
1313 aVert.SetVertOrient( text::VertOrientation::NONE );
1314 aVert.SetRelationOrient( text::RelOrientation::FRAME );
1317 aVert.SetPos( nTmpY );
1318 aSet.Put( aVert );
1320 // For Flys in the Cnt, the horizontal orientation is of no interest,
1321 // as it's always 0
1322 if ( !IsFlyInContentFrame() )
1324 const SwTwips nNewX = bVert ? rNewPos.Y() : rNewPos.X();
1325 SwTwips nTmpX = nNewX == LONG_MAX ? 0 : nNewX;
1326 SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
1327 // #i34948# - handle also at-page and at-fly anchored
1328 // Writer fly frames
1329 if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
1331 aHori.SetHoriOrient( text::HoriOrientation::NONE );
1332 aHori.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
1333 aHori.SetPosToggle( false );
1335 else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
1337 aHori.SetHoriOrient( text::HoriOrientation::NONE );
1338 aHori.SetRelationOrient( text::RelOrientation::FRAME );
1339 aHori.SetPosToggle( false );
1341 else if ( IsFlyAtContentFrame() || text::HoriOrientation::NONE != aHori.GetHoriOrient() )
1343 aHori.SetHoriOrient( text::HoriOrientation::NONE );
1344 if( text::RelOrientation::CHAR == aHori.GetRelationOrient() && IsAutoPos() )
1346 if( LONG_MAX != nNewX )
1348 if( !pAutoFrame )
1350 assert(GetAnchorFrame()->IsTextFrame());
1351 pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
1352 TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
1353 *pFormat->GetAnchor().GetContentAnchor()));
1354 while( pAutoFrame->GetFollow() &&
1355 pAutoFrame->GetFollow()->GetOffset() <= nOfs )
1356 pAutoFrame = pAutoFrame->GetFollow();
1358 nTmpX -= static_cast<SwFlyAtContentFrame*>(this)->GetRelCharX(pAutoFrame);
1361 else
1362 aHori.SetRelationOrient( text::RelOrientation::FRAME );
1363 aHori.SetPosToggle( false );
1365 aHori.SetPos( nTmpX );
1366 aSet.Put( aHori );
1368 SetCurrRelPos( rNewPos );
1369 pFormat->GetDoc()->SetAttr( aSet, *pFormat );
1373 /** "Formats" the Frame; Frame and PrtArea.
1375 * The FixSize is not inserted here.
1377 void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
1379 OSL_ENSURE( pAttrs, "FlyFrame::Format, pAttrs is 0." );
1381 ColLock();
1383 if ( !isFrameAreaSizeValid() )
1385 if ( getFrameArea().Top() == FAR_AWAY && getFrameArea().Left() == FAR_AWAY )
1387 // Remove safety switch (see SwFrame::CTor)
1389 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1390 aFrm.Pos().setX(0);
1391 aFrm.Pos().setY(0);
1394 // #i68520#
1395 InvalidateObjRectWithSpaces();
1398 // Check column width and set it if needed
1399 if ( Lower() && Lower()->IsColumnFrame() )
1400 AdjustColumns( nullptr, false );
1402 setFrameAreaSizeValid(true);
1404 const SwTwips nUL = pAttrs->CalcTopLine() + pAttrs->CalcBottomLine();
1405 const SwTwips nLR = pAttrs->CalcLeftLine() + pAttrs->CalcRightLine();
1406 const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
1407 Size aRelSize( CalcRel( rFrameSz ) );
1409 OSL_ENSURE( pAttrs->GetSize().Height() != 0 || rFrameSz.GetHeightPercent(), "FrameAttr height is 0." );
1410 OSL_ENSURE( pAttrs->GetSize().Width() != 0 || rFrameSz.GetWidthPercent(), "FrameAttr width is 0." );
1412 SwRectFnSet aRectFnSet(this);
1413 if( !HasFixSize() )
1415 tools::Long nMinHeight = 0;
1416 if( IsMinHeight() )
1417 nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
1419 SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL);
1420 if( IsMinHeight() && (nRemaining + nUL) < nMinHeight )
1421 nRemaining = nMinHeight - nUL;
1422 // Because the Grow/Shrink of the Flys does not directly
1423 // set the size - only indirectly by triggering a Format()
1424 // via Invalidate() - the sizes need to be set here.
1425 // Notification is running along already.
1426 // As we already got a lot of zeros per attribute, we block them
1427 // from now on.
1429 if ( nRemaining < MINFLY )
1430 nRemaining = MINFLY;
1432 const SwFrame* pAnchor = GetAnchorFrame();
1433 if (SwFrame* pAnchorChar = FindAnchorCharFrame())
1435 // If we find a follow of the anchor that is effectively the anchor of this fly,
1436 // then use that as the anchor for sizing purposes.
1437 pAnchor = pAnchorChar;
1439 if (pAnchor && IsFlySplitAllowed())
1441 // If the fly is allowed to be split, then limit its size to the upper of the
1442 // anchor.
1443 SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
1444 SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
1445 SwTwips nBottom = aRectFnSet.GetTop(getFrameArea()) + nRemaining;
1446 if (nBottom > nDeadline && nDeadline > nTop)
1448 nRemaining = nDeadline - nTop;
1453 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1454 aRectFnSet.SetHeight( aPrt, nRemaining );
1457 nRemaining -= aRectFnSet.GetHeight(getFrameArea());
1460 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1461 aRectFnSet.AddBottom( aFrm, nRemaining + nUL );
1464 // #i68520#
1465 if ( nRemaining + nUL != 0 )
1467 InvalidateObjRectWithSpaces();
1470 setFrameAreaSizeValid(true);
1472 if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
1474 // This fly is a textbox of a draw shape.
1475 SdrObject* pShape = pShapeFormat->FindSdrObject();
1476 if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pShape) )
1478 // The shape is a customshape: then inform it about the calculated fly size.
1479 Size aSize(getFrameArea().Width(), getFrameArea().Height());
1480 pCustomShape->SuggestTextFrameSize(aSize);
1481 // Do the calculations normally done after touching editeng text of the shape.
1482 pCustomShape->NbcSetOutlinerParaObjectForText(std::nullopt, nullptr);
1486 else
1488 // Fixed Frames do not Format itself
1489 setFrameAreaSizeValid(true);
1491 // Flys set their size using the attr
1492 SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
1493 nNewSize -= nUL;
1494 if( nNewSize < MINFLY )
1495 nNewSize = MINFLY;
1498 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1499 aRectFnSet.SetHeight( aPrt, nNewSize );
1502 nNewSize += nUL - aRectFnSet.GetHeight(getFrameArea());
1505 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1506 aRectFnSet.AddBottom( aFrm, nNewSize );
1509 // #i68520#
1510 if ( nNewSize != 0 )
1512 InvalidateObjRectWithSpaces();
1516 if ( !m_bFormatHeightOnly )
1518 OSL_ENSURE( aRelSize == CalcRel( rFrameSz ), "SwFlyFrame::Format CalcRel problem" );
1519 SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Height() : aRelSize.Width();
1521 if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
1523 // #i9046# Autowidth for fly frames
1524 const SwTwips nAutoWidth = lcl_CalcAutoWidth( *this );
1525 if ( nAutoWidth )
1527 if( SwFrameSize::Minimum == rFrameSz.GetWidthSizeType() )
1528 nNewSize = std::max( nNewSize - nLR, nAutoWidth );
1529 else
1530 nNewSize = nAutoWidth;
1533 else
1534 nNewSize -= nLR;
1536 if( nNewSize < MINFLY )
1537 nNewSize = MINFLY;
1540 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1541 aRectFnSet.SetWidth( aPrt, nNewSize );
1544 nNewSize += nLR - aRectFnSet.GetWidth(getFrameArea());
1547 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1548 aRectFnSet.AddRight( aFrm, nNewSize );
1551 // #i68520#
1552 if ( nNewSize != 0 )
1554 InvalidateObjRectWithSpaces();
1558 ColUnlock();
1561 // #i11760# - change parameter <bNoColl>: type <bool>;
1562 // add new parameter <bNoCalcFollow> with
1563 // new parameter <bNoCalcFollow> was used by method
1564 // <FormatWidthCols(..)> to avoid follow formatting
1565 // for text frames. But, unformatted follows causes
1566 // problems in method <SwContentFrame::WouldFit_(..)>,
1567 // which assumes that the follows are formatted.
1568 // Thus, <bNoCalcFollow> no longer used by <FormatWidthCols(..)>.
1569 void CalcContent( SwLayoutFrame *pLay, bool bNoColl )
1571 vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
1572 SwSectionFrame* pSect;
1573 bool bCollect = false;
1574 if( pLay->IsSctFrame() )
1576 pSect = static_cast<SwSectionFrame*>(pLay);
1577 if( pSect->IsEndnAtEnd() && !bNoColl )
1579 bCollect = true;
1580 SwLayouter::CollectEndnotes( pLay->GetFormat()->GetDoc(), pSect );
1582 pSect->CalcFootnoteContent();
1584 else
1585 pSect = nullptr;
1586 SwFrame *pFrame = pLay->ContainsAny();
1587 if ( !pFrame )
1589 if( pSect )
1591 if( pSect->HasFollow() )
1592 pFrame = pSect->GetFollow()->ContainsAny();
1593 if( !pFrame )
1595 if( pSect->IsEndnAtEnd() )
1597 if( bCollect )
1598 pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->
1599 InsertEndnotes( pSect );
1600 bool bLock = pSect->IsFootnoteLock();
1601 pSect->SetFootnoteLock( true );
1602 pSect->CalcFootnoteContent();
1603 pSect->CalcFootnoteContent();
1604 pSect->SetFootnoteLock( bLock );
1606 return;
1608 pFrame->InvalidatePos_();
1610 else
1611 return;
1613 pFrame->InvalidatePage();
1617 // local variables to avoid loops caused by anchored object positioning
1618 SwAnchoredObject* pAgainObj1 = nullptr;
1619 SwAnchoredObject* pAgainObj2 = nullptr;
1621 // FME 2007-08-30 #i81146# new loop control
1622 int nLoopControlRuns = 0;
1623 // tdf#152106 loop control for multi-column sections
1624 int nLoopControlRunsInMultiCol = 0;
1625 const int nLoopControlMax = 20;
1626 const SwFrame* pLoopControlCond = nullptr;
1628 SwFrame* pLast;
1631 pLast = pFrame;
1632 bool const wasFrameLowerOfLay(pLay->IsAnLower(pFrame));
1633 if( pFrame->IsVertical() ?
1634 ( pFrame->GetUpper()->getFramePrintArea().Height() != pFrame->getFrameArea().Height() )
1635 : ( pFrame->GetUpper()->getFramePrintArea().Width() != pFrame->getFrameArea().Width() ) )
1637 pFrame->Prepare( PrepareHint::FixSizeChanged );
1638 pFrame->InvalidateSize_();
1641 if ( pFrame->IsTabFrame() )
1643 static_cast<SwTabFrame*>(pFrame)->m_bCalcLowers = true;
1644 // #i18103# - lock move backward of follow table,
1645 // if no section content is formatted or follow table belongs
1646 // to the section, which content is formatted.
1647 if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() &&
1648 ( !pSect || pSect == pFrame->FindSctFrame() ) )
1650 static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = true;
1655 SwFrameDeleteGuard aDeletePageGuard(pSect ? pSect->FindPageFrame() : nullptr);
1656 SwFrameDeleteGuard aDeleteGuard(pSect);
1657 pFrame->Calc(pRenderContext);
1660 // #i11760# - reset control flag for follow format.
1661 if ( pFrame->IsTextFrame() )
1663 static_cast<SwTextFrame*>(pFrame)->AllowFollowFormat();
1666 // The keep-attribute can cause the position
1667 // of the prev to be invalid:
1668 // Do not consider invalid previous frame
1669 // due to its keep-attribute, if current frame is a follow or is locked.
1670 // #i44049# - do not consider invalid previous
1671 // frame due to its keep-attribute, if it can't move forward.
1672 // #i57765# - do not consider invalid previous
1673 // frame, if current frame has a column/page break before attribute.
1674 SwFrame* pTmpPrev = pFrame->FindPrev();
1675 SwFlowFrame* pTmpPrevFlowFrame = pTmpPrev && pTmpPrev->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pTmpPrev) : nullptr;
1676 SwFlowFrame* pTmpFlowFrame = pFrame->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pFrame) : nullptr;
1678 bool bPrevInvalid = pTmpPrevFlowFrame && pTmpFlowFrame &&
1679 !pTmpFlowFrame->IsFollow() &&
1680 !StackHack::IsLocked() && // #i76382#
1681 !pTmpFlowFrame->IsJoinLocked() &&
1682 !pTmpPrev->isFrameAreaPositionValid() &&
1683 pLay->IsAnLower( pTmpPrev ) &&
1684 pTmpPrevFlowFrame->IsKeep(pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()) &&
1685 pTmpPrevFlowFrame->IsKeepFwdMoveAllowed();
1687 // format floating screen objects anchored to the frame.
1688 if ( !bPrevInvalid && pFrame->GetDrawObjs() && pLay->IsAnLower( pFrame ) )
1690 bool bAgain = false;
1691 bool bRestartLayoutProcess = false;
1692 size_t nCnt = pFrame->GetDrawObjs()->size();
1693 size_t i = 0;
1694 while ( i < nCnt )
1696 // pFrame can move to a different page in FormatObj()
1697 SwPageFrame *const pPageFrame = pFrame->FindPageFrame();
1699 // #i28701#
1700 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
1701 assert(pAnchoredObj);
1703 // determine if anchored object has to be
1704 // formatted and, in case, format it
1705 if ( !pAnchoredObj->PositionLocked() && pAnchoredObj->IsFormatPossible() )
1707 // #i43737# - no invalidation of
1708 // anchored object needed - causes loops for as-character
1709 // anchored objects.
1710 //pAnchoredObj->InvalidateObjPos();
1711 SwRect aRect( pAnchoredObj->GetObjRect() );
1713 SwFrame* pAnchorFrame = pFrame;
1714 SwPageFrame* pAnchorPageFrame = pPageFrame;
1715 if (SwFlyFrame* pFlyFrame = pAnchoredObj->DynCastFlyFrame())
1717 if (pFlyFrame->IsFlySplitAllowed())
1719 // Split flys are at-para anchored, but the follow fly's anchor char
1720 // frame is not the master frame but can be also a follow of pFrame.
1721 SwTextFrame* pAnchorCharFrame = pFlyFrame->FindAnchorCharFrame();
1722 if (pAnchorCharFrame)
1724 // Found an anchor char frame, update the anchor frame and the
1725 // anchor page frame accordingly.
1726 pAnchorFrame = pAnchorCharFrame;
1727 pAnchorPageFrame = pAnchorCharFrame->FindPageFrame();
1732 if ( !SwObjectFormatter::FormatObj( *pAnchoredObj, pAnchorFrame, pAnchorPageFrame ) )
1734 bRestartLayoutProcess = true;
1735 break;
1737 // #i3317# - restart layout process,
1738 // if the position of the anchored object is locked now.
1739 if ( pAnchoredObj->PositionLocked() )
1741 bRestartLayoutProcess = true;
1742 break;
1745 if ( aRect != pAnchoredObj->GetObjRect() )
1747 bAgain = true;
1748 if ( pAgainObj2 == pAnchoredObj )
1750 OSL_FAIL( "::CalcContent(..) - loop detected, perform attribute changes to avoid the loop" );
1751 // Prevent oscillation
1752 SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
1753 SwFormatSurround aAttr( rFormat.GetSurround() );
1754 if( css::text::WrapTextMode_THROUGH != aAttr.GetSurround() )
1756 // When on auto position, we can only set it to
1757 // flow through
1758 if ((rFormat.GetAnchor().GetAnchorId() ==
1759 RndStdIds::FLY_AT_CHAR) &&
1760 (css::text::WrapTextMode_PARALLEL ==
1761 aAttr.GetSurround()))
1763 aAttr.SetSurround( css::text::WrapTextMode_THROUGH );
1765 else
1767 aAttr.SetSurround( css::text::WrapTextMode_PARALLEL );
1769 rFormat.LockModify();
1770 rFormat.SetFormatAttr( aAttr );
1771 rFormat.UnlockModify();
1774 else
1776 if ( pAgainObj1 == pAnchoredObj )
1777 pAgainObj2 = pAnchoredObj;
1778 pAgainObj1 = pAnchoredObj;
1782 if ( !pFrame->GetDrawObjs() )
1783 break;
1784 if ( pFrame->GetDrawObjs()->size() < nCnt )
1786 --nCnt;
1787 // Do not increment index, in this case
1788 continue;
1791 ++i;
1794 // #i28701# - restart layout process, if
1795 // requested by floating screen object formatting
1796 if (bRestartLayoutProcess
1797 // tdf#152106 loop control in multi-column sections to avoid of freezing
1798 && nLoopControlRunsInMultiCol < nLoopControlMax
1799 // tdf#142080 if it was already on next page, and still is,
1800 // ignore restart, as restart could cause infinite loop
1801 && (wasFrameLowerOfLay || pLay->IsAnLower(pFrame)))
1803 bool bIsMultiColumn = pSect && pSect->GetSection() && pSect->Lower() &&
1804 pSect->Lower()->IsColumnFrame() && pSect->Lower()->GetNext();
1805 if ( bIsMultiColumn )
1806 ++nLoopControlRunsInMultiCol;
1807 pFrame = pLay->ContainsAny();
1808 pAgainObj1 = nullptr;
1809 pAgainObj2 = nullptr;
1810 continue;
1813 // #i28701# - format anchor frame after its objects
1814 // are formatted, if the wrapping style influence has to be considered.
1815 if ( pLay->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
1817 pFrame->Calc(pRenderContext);
1820 if ( bAgain )
1822 pFrame = pLay->ContainsContent();
1823 if ( pFrame && pFrame->IsInTab() )
1824 pFrame = pFrame->FindTabFrame();
1825 if( pFrame && pFrame->IsInSct() )
1827 SwSectionFrame* pTmp = pFrame->FindSctFrame();
1828 if( pTmp != pLay && pLay->IsAnLower( pTmp ) )
1829 pFrame = pTmp;
1832 if ( pFrame == pLoopControlCond )
1833 ++nLoopControlRuns;
1834 else
1836 nLoopControlRuns = 0;
1837 pLoopControlCond = pFrame;
1840 if ( nLoopControlRuns < nLoopControlMax )
1841 continue;
1843 OSL_FAIL( "LoopControl in CalcContent" );
1846 if ( pFrame->IsTabFrame() )
1848 if (static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove)
1850 assert(static_cast<SwTabFrame*>(pFrame)->IsFollow());
1851 static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = false;
1852 // tdf#150606 encourage it to move back in FormatLayout()
1853 if (static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove)
1855 static_cast<SwTabFrame*>(pFrame)->m_bWantBackMove = false;
1856 pFrame->InvalidatePos();
1861 pFrame = bPrevInvalid ? pTmpPrev : pFrame->FindNext();
1862 if( !bPrevInvalid && pFrame && pFrame->IsSctFrame() && pSect )
1864 // Empty SectionFrames could be present here
1865 while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
1866 pFrame = pFrame->FindNext();
1868 // If FindNext returns the Follow of the original Area, we want to
1869 // continue with this content as long as it flows back.
1870 if( pFrame && pFrame->IsSctFrame() && ( pFrame == pSect->GetFollow() ||
1871 static_cast<SwSectionFrame*>(pFrame)->IsAnFollow( pSect ) ) )
1873 pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
1874 if( pFrame )
1875 pFrame->InvalidatePos_();
1878 // Stay in the pLay.
1879 // Except for SectionFrames with Follow: the first ContentFrame of the
1880 // Follow will be formatted, so that it gets a chance to move back
1881 // into the pLay. Continue as long as these Frames land in pLay.
1882 } while ( pFrame &&
1883 ( pLay->IsAnLower( pFrame ) ||
1884 ( pSect &&
1885 ( ( pSect->HasFollow() &&
1886 ( pLay->IsAnLower( pLast ) ||
1887 ( pLast->IsInSct() &&
1888 pLast->FindSctFrame()->IsAnFollow(pSect) ) ) &&
1889 pSect->GetFollow()->IsAnLower( pFrame ) ) ||
1890 ( pFrame->IsInSct() &&
1891 pFrame->FindSctFrame()->IsAnFollow( pSect ) ) ) ) ) );
1892 if( pSect )
1894 if( bCollect )
1896 pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->InsertEndnotes(pSect);
1897 pSect->CalcFootnoteContent();
1899 if( pSect->HasFollow() )
1901 SwSectionFrame* pNxt = pSect->GetFollow();
1902 while( pNxt && !pNxt->ContainsContent() )
1903 pNxt = pNxt->GetFollow();
1904 if( pNxt )
1905 pNxt->CalcFootnoteContent();
1907 if( bCollect )
1909 pFrame = pLay->ContainsAny();
1910 bCollect = false;
1911 if( pFrame )
1912 continue;
1915 break;
1917 while( true );
1920 void SwFlyFrame::MakeObjPos()
1922 if ( isFrameAreaPositionValid() )
1923 return;
1925 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
1926 setFrameAreaPositionValid(true);
1928 // use new class to position object
1929 GetAnchorFrame()->Calc(pRenderContext);
1930 objectpositioning::SwToLayoutAnchoredObjectPosition
1931 aObjPositioning( *GetVirtDrawObj() );
1932 aObjPositioning.CalcPosition();
1934 // #i58280#
1935 // update relative position
1936 SetCurrRelPos( aObjPositioning.GetRelPos() );
1939 SwRectFnSet aRectFnSet(GetAnchorFrame());
1940 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1941 aFrm.Pos( aObjPositioning.GetRelPos() );
1942 aFrm.Pos() += aRectFnSet.GetPos(GetAnchorFrame()->getFrameArea());
1945 // #i69335#
1946 InvalidateObjRectWithSpaces();
1949 void SwFlyFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
1951 if ( !isFramePrintAreaValid() )
1953 setFramePrintAreaValid(true);
1955 // consider vertical layout
1956 SwRectFnSet aRectFnSet(this);
1957 aRectFnSet.SetXMargins( *this, rAttrs.CalcLeftLine(),
1958 rAttrs.CalcRightLine() );
1959 aRectFnSet.SetYMargins( *this, rAttrs.CalcTopLine(),
1960 rAttrs.CalcBottomLine() );
1964 void SwFlyFrame::MakeContentPos( const SwBorderAttrs &rAttrs )
1966 if ( m_bValidContentPos )
1967 return;
1969 m_bValidContentPos = true;
1971 const SwTwips nUL = rAttrs.CalcTopLine() + rAttrs.CalcBottomLine();
1972 Size aRelSize( CalcRel( GetFormat()->GetFrameSize() ) );
1974 SwRectFnSet aRectFnSet(this);
1975 tools::Long nMinHeight = 0;
1976 if( IsMinHeight() )
1977 nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
1979 Point aNewContentPos = getFramePrintArea().Pos();
1980 const SdrTextVertAdjust nAdjust = GetFormat()->GetTextVertAdjust().GetValue();
1982 if( nAdjust != SDRTEXTVERTADJUST_TOP )
1984 const SwTwips nContentHeight = CalcContentHeight(&rAttrs, nMinHeight, nUL);
1985 SwTwips nDiff = 0;
1987 if( nContentHeight != 0)
1988 nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nContentHeight;
1990 if( nDiff > 0 )
1992 if( nAdjust == SDRTEXTVERTADJUST_CENTER )
1994 if( aRectFnSet.IsVertL2R() )
1995 aNewContentPos.setX(aNewContentPos.getX() + nDiff/2);
1996 else if( aRectFnSet.IsVert() )
1997 aNewContentPos.setX(aNewContentPos.getX() - nDiff/2);
1998 else
1999 aNewContentPos.setY(aNewContentPos.getY() + nDiff/2);
2001 else if( nAdjust == SDRTEXTVERTADJUST_BOTTOM )
2003 if( aRectFnSet.IsVertL2R() )
2004 aNewContentPos.setX(aNewContentPos.getX() + nDiff);
2005 else if( aRectFnSet.IsVert() )
2006 aNewContentPos.setX(aNewContentPos.getX() - nDiff);
2007 else
2008 aNewContentPos.setY(aNewContentPos.getY() + nDiff);
2012 if( aNewContentPos != ContentPos() )
2014 ContentPos() = aNewContentPos;
2015 for( SwFrame *pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
2017 pFrame->InvalidatePos();
2023 void SwFlyFrame::InvalidateContentPos()
2025 m_bValidContentPos = false;
2026 Invalidate_();
2029 void SwFlyFrame::SelectionHasChanged(SwFEShell* pShell)
2031 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >(pShell);
2032 if (pWrtSh == nullptr)
2033 return;
2035 UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
2038 bool SwFlyFrame::IsShowUnfloatButton(SwWrtShell* pWrtSh) const
2040 if (pWrtSh == nullptr)
2041 return false;
2043 // In read only mode we don't allow unfloat operation
2044 if (pWrtSh->GetViewOptions()->IsReadonly())
2045 return false;
2047 const SdrObject *pObj = GetFrameFormat().FindRealSdrObject();
2048 if (pObj == nullptr)
2049 return false;
2051 // SwFlyFrame itself can mean images, ole objects, etc, but we interested in actual text frames
2052 if (SwFEShell::GetObjCntType(*pObj) != OBJCNT_FLY)
2053 return false;
2055 // We show the button only for the selected text frame
2056 SwDrawView *pView = pWrtSh->Imp()->GetDrawView();
2057 if (pView == nullptr)
2058 return false;
2060 // Fly frame can be selected only alone
2061 if (pView->GetMarkedObjectList().GetMarkCount() != 1)
2062 return false;
2064 if(!pView->IsObjMarked(pObj))
2065 return false;
2067 // A frame is a floating table if there is only one table (and maybe some whitespaces) inside it
2068 int nTableCount = 0;
2069 const SwFrame* pLower = GetLower();
2070 const SwTabFrame* pTable = nullptr;
2071 while (pLower)
2073 if (pLower->IsTabFrame())
2075 pTable = static_cast<const SwTabFrame*>(pLower);
2076 ++nTableCount;
2077 if (nTableCount > 1 || pTable == nullptr)
2078 return false;
2081 if (pLower->IsTextFrame())
2083 const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pLower);
2084 if (!o3tl::trim(pTextFrame->GetText()).empty())
2085 return false;
2087 pLower = pLower->GetNext();
2090 if (nTableCount != 1 || pTable == nullptr)
2091 return false;
2093 // Show the unfold button only for multipage tables
2094 const SwBodyFrame *pBody = GetAnchorFrame()->FindBodyFrame();
2095 if (pBody == nullptr)
2096 return false;
2098 tools::Long nBodyHeight = pBody->getFrameArea().Height();
2099 tools::Long nTableHeight = pTable->getFrameArea().Height();
2100 tools::Long nFrameOffset = std::abs(GetAnchorFrame()->getFrameArea().Top() - pBody->getFrameArea().Top());
2102 return nBodyHeight < nTableHeight + nFrameOffset;
2105 void SwFlyFrame::ActiveUnfloatButton(SwWrtShell* pWrtSh)
2107 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
2108 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
2109 SwFrameControlPtr pControl = rMngr.GetControl(FrameControlType::FloatingTable, this);
2110 if (pControl && pControl->GetWindow())
2112 pControl->GetWindow()->MouseButtonDown(MouseEvent());
2116 void SwFlyFrame::UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const
2118 if (pWrtSh == nullptr)
2119 return;
2121 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
2122 SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
2123 Point aTopRightPixel = rEditWin.LogicToPixel( getFrameArea().TopRight() );
2124 rMngr.SetUnfloatTableButton(this, bShow, aTopRightPixel);
2127 SwFlyAtContentFrame* SwFlyFrame::DynCastFlyAtContentFrame()
2129 return IsFlyAtContentFrame() ? static_cast<SwFlyAtContentFrame*>(this) : nullptr;
2132 SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
2134 SwRectFnSet aRectFnSet(this);
2135 if ( Lower() && !IsColLocked() && !HasFixSize() )
2137 SwTwips nSize = aRectFnSet.GetHeight(getFrameArea());
2138 if( nSize > 0 && nDist > ( LONG_MAX - nSize ) )
2139 nDist = LONG_MAX - nSize;
2141 if ( nDist <= 0 )
2142 return 0;
2144 if ( Lower()->IsColumnFrame() )
2145 { // If it's a Column Frame, the Format takes control of the
2146 // resizing (due to the adjustment).
2147 if ( !bTst )
2149 // #i28701# - unlock position of Writer fly frame
2150 UnlockPosition();
2151 InvalidatePos_();
2152 InvalidateSize();
2154 return 0;
2157 if ( !bTst )
2159 const SwRect aOld( GetObjRectWithSpaces() );
2160 InvalidateSize_();
2161 const bool bOldLock = m_bLocked;
2162 Unlock();
2163 if ( IsFlyFreeFrame() )
2165 // #i37068# - no format of position here
2166 // and prevent move in method <CheckClip(..)>.
2167 // This is needed to prevent layout loop caused by nested
2168 // Writer fly frames - inner Writer fly frames format its
2169 // anchor, which grows/shrinks the outer Writer fly frame.
2170 // Note: position will be invalidated below.
2171 setFrameAreaPositionValid(true);
2173 // #i55416#
2174 // Suppress format of width for autowidth frame, because the
2175 // format of the width would call <SwTextFrame::CalcFitToContent()>
2176 // for the lower frame, which initiated this grow.
2177 const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
2178 const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
2179 if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
2181 m_bFormatHeightOnly = true;
2183 SwViewShell* pSh = getRootFrame()->GetCurrShell();
2184 if (pSh)
2186 static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
2187 static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(pSh->GetOut());
2188 static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
2190 // #i55416#
2191 if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
2193 m_bFormatHeightOnly = bOldFormatHeightOnly;
2196 else
2197 MakeAll(getRootFrame()->GetCurrShell()->GetOut());
2198 InvalidateSize_();
2199 InvalidatePos();
2200 if ( bOldLock )
2201 Lock();
2202 SwRect aNew(GetObjRectWithSpaces());
2203 if (IsFlySplitAllowed() && aNew.Height() - aOld.Height() < nDist)
2205 // We are allowed to split and the actual growth is less than the requested growth.
2206 const SwFrame* pAnchor = GetAnchorFrame();
2207 if (SwFrame* pAnchorChar = FindAnchorCharFrame())
2209 pAnchor = pAnchorChar;
2211 if (pAnchor)
2213 SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
2214 SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
2215 SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea());
2216 SwTwips nMaxGrow = nDeadline - nBottom;
2217 if (nDist > nMaxGrow)
2219 // The requested growth is more than what we can provide, limit it.
2220 nDist = nMaxGrow;
2222 // Grow & invalidate the size.
2223 SwTwips nRemaining = nDist - (aNew.Height() - aOld.Height());
2225 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2226 aRectFnSet.AddBottom(aFrm, nRemaining);
2228 InvalidateObjRectWithSpaces();
2230 // Margins are unchanged, so increase the print height similar to the frame
2231 // height.
2232 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2233 aRectFnSet.AddBottom(aPrt, nRemaining );
2235 aNew = GetObjRectWithSpaces();
2238 if ( aOld != aNew )
2239 ::Notify( this, FindPageFrame(), aOld );
2240 return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
2242 else
2244 // We're in test mode. Don't promise infinite growth for split flys, rather limit the
2245 // max size to the bottom of the upper.
2246 const SwFrame* pAnchor = GetAnchorFrame();
2247 if (SwFrame* pAnchorChar = FindAnchorCharFrame())
2249 pAnchor = pAnchorChar;
2251 if (pAnchor && IsFlySplitAllowed())
2253 SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
2254 SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
2255 SwTwips nBottom = nTop + aRectFnSet.GetHeight(getFrameArea());
2256 // Calculate max grow and compare to the requested growth, adding to nDist may
2257 // overflow when it's LONG_MAX.
2258 SwTwips nMaxGrow = nDeadline - nBottom;
2259 if (nDist > nMaxGrow)
2261 nDist = nMaxGrow;
2265 return nDist;
2267 return 0;
2270 SwTwips SwFlyFrame::Shrink_( SwTwips nDist, bool bTst )
2272 if( Lower() && !IsColLocked() && !HasFixSize() )
2274 SwRectFnSet aRectFnSet(this);
2275 SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
2276 if ( nDist > nHeight )
2277 nDist = nHeight;
2279 SwTwips nVal = nDist;
2280 if ( IsMinHeight() )
2282 const SwFormatFrameSize& rFormatSize = GetFormat()->GetFrameSize();
2283 SwTwips nFormatHeight = aRectFnSet.IsVert() ? rFormatSize.GetWidth() : rFormatSize.GetHeight();
2285 nVal = std::min( nDist, nHeight - nFormatHeight );
2288 if ( nVal <= 0 )
2289 return 0;
2291 if ( Lower()->IsColumnFrame() )
2292 { // If it's a Column Frame, the Format takes control of the
2293 // resizing (due to the adjustment).
2294 if ( !bTst )
2296 SwRect aOld( GetObjRectWithSpaces() );
2299 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
2300 aRectFnSet.SetHeight( aFrm, nHeight - nVal );
2303 // #i68520#
2304 if ( nHeight - nVal != 0 )
2306 InvalidateObjRectWithSpaces();
2309 nHeight = aRectFnSet.GetHeight(getFramePrintArea());
2312 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
2313 aRectFnSet.SetHeight( aPrt, nHeight - nVal );
2316 InvalidatePos_();
2317 InvalidateSize();
2318 ::Notify( this, FindPageFrame(), aOld );
2319 NotifyDrawObj();
2320 if ( GetAnchorFrame()->IsInFly() )
2321 AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
2323 return 0;
2326 if ( !bTst )
2328 const SwRect aOld( GetObjRectWithSpaces() );
2329 InvalidateSize_();
2330 const bool bOldLocked = m_bLocked;
2331 Unlock();
2332 if ( IsFlyFreeFrame() )
2334 // #i37068# - no format of position here
2335 // and prevent move in method <CheckClip(..)>.
2336 // This is needed to prevent layout loop caused by nested
2337 // Writer fly frames - inner Writer fly frames format its
2338 // anchor, which grows/shrinks the outer Writer fly frame.
2339 // Note: position will be invalidated below.
2340 setFrameAreaPositionValid(true);
2342 // #i55416#
2343 // Suppress format of width for autowidth frame, because the
2344 // format of the width would call <SwTextFrame::CalcFitToContent()>
2345 // for the lower frame, which initiated this shrink.
2346 const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
2347 const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
2348 if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
2350 m_bFormatHeightOnly = true;
2352 static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
2353 static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
2354 static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
2355 // #i55416#
2356 if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
2358 m_bFormatHeightOnly = bOldFormatHeightOnly;
2361 else
2362 MakeAll(getRootFrame()->GetCurrShell()->GetOut());
2363 InvalidateSize_();
2364 InvalidatePos();
2365 if ( bOldLocked )
2366 Lock();
2367 const SwRect aNew( GetObjRectWithSpaces() );
2368 if ( aOld != aNew )
2370 ::Notify( this, FindPageFrame(), aOld );
2371 if ( GetAnchorFrame()->IsInFly() )
2372 AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
2374 return aRectFnSet.GetHeight(aOld) -
2375 aRectFnSet.GetHeight(aNew);
2377 return nVal;
2379 return 0;
2382 Size SwFlyFrame::ChgSize( const Size& aNewSize )
2384 // #i53298#
2385 // If the fly frame anchored at-paragraph or at-character contains an OLE
2386 // object, assure that the new size fits into the current clipping area
2387 // of the fly frame
2388 Size aAdjustedNewSize( aNewSize );
2390 if ( dynamic_cast<SwFlyAtContentFrame*>(this) &&
2391 Lower() && dynamic_cast<SwNoTextFrame*>(Lower()) &&
2392 static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() )
2394 SwRect aClipRect;
2395 ::CalcClipRect( GetVirtDrawObj(), aClipRect, false );
2396 if ( aAdjustedNewSize.Width() > aClipRect.Width() )
2398 aAdjustedNewSize.setWidth( aClipRect.Width() );
2400 if ( aAdjustedNewSize.Height() > aClipRect.Height() )
2402 aAdjustedNewSize.setWidth( aClipRect.Height() );
2407 if ( aAdjustedNewSize != getFrameArea().SSize() )
2409 SwFrameFormat *pFormat = GetFormat();
2410 SwFormatFrameSize aSz( pFormat->GetFrameSize() );
2411 aSz.SetWidth( aAdjustedNewSize.Width() );
2412 aSz.SetHeight( aAdjustedNewSize.Height() );
2413 // go via the Doc for UNDO
2414 pFormat->GetDoc()->SetAttr( aSz, *pFormat );
2415 return aSz.GetSize();
2417 else
2418 return getFrameArea().SSize();
2421 bool SwFlyFrame::IsLowerOf( const SwLayoutFrame* pUpperFrame ) const
2423 OSL_ENSURE( GetAnchorFrame(), "8-( Fly is lost in Space." );
2424 const SwFrame* pFrame = GetAnchorFrame();
2427 if ( pFrame == pUpperFrame )
2428 return true;
2429 pFrame = pFrame->IsFlyFrame()
2430 ? static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame()
2431 : pFrame->GetUpper();
2432 } while ( pFrame );
2433 return false;
2436 void SwFlyFrame::Cut()
2440 void SwFrame::AppendFly( SwFlyFrame *pNew )
2442 if (!m_pDrawObjs)
2444 m_pDrawObjs.reset(new SwSortedObjs());
2446 m_pDrawObjs->Insert( *pNew );
2447 pNew->ChgAnchorFrame( this );
2449 // Register at the page
2450 // If there's none present, register via SwPageFrame::PreparePage
2451 SwPageFrame* pPage = FindPageFrame();
2452 if ( pPage != nullptr )
2454 pPage->AppendFlyToPage( pNew );
2458 void SwFrame::RemoveFly( SwFlyFrame *pToRemove )
2460 // Deregister from the page
2461 // Could already have happened, if the page was already destructed
2462 SwPageFrame *pPage = pToRemove->FindPageFrame();
2463 if ( pPage && pPage->GetSortedObjs() )
2465 pPage->RemoveFlyFromPage( pToRemove );
2467 // #i73201#
2468 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2469 else
2471 if ( pToRemove->IsAccessibleFrame() &&
2472 pToRemove->GetFormat() &&
2473 !pToRemove->IsFlyInContentFrame() )
2475 SwRootFrame *pRootFrame = getRootFrame();
2476 if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
2478 SwViewShell *pVSh = pRootFrame->GetCurrShell();
2479 if( pVSh && pVSh->Imp() )
2481 pVSh->Imp()->DisposeAccessibleFrame( pToRemove );
2486 #endif
2488 m_pDrawObjs->Remove(*pToRemove);
2489 if (!m_pDrawObjs->size())
2491 m_pDrawObjs.reset();
2494 pToRemove->ChgAnchorFrame( nullptr );
2496 if ( !pToRemove->IsFlyInContentFrame() && GetUpper() && IsInTab() )//MA_FLY_HEIGHT
2497 GetUpper()->InvalidateSize();
2500 void SwFrame::AppendDrawObj( SwAnchoredObject& _rNewObj )
2502 assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
2504 if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) == nullptr )
2506 OSL_FAIL( "SwFrame::AppendDrawObj(..) - anchored object of unexpected type -> object not appended" );
2507 return;
2510 if ( dynamic_cast<const SwDrawVirtObj*>(_rNewObj.GetDrawObj()) == nullptr &&
2511 _rNewObj.GetAnchorFrame() && _rNewObj.GetAnchorFrame() != this )
2513 assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
2514 // perform disconnect from layout, if 'master' drawing object is appended
2515 // to a new frame.
2516 static_cast<SwDrawContact*>(::GetUserCall( _rNewObj.GetDrawObj() ))->
2517 DisconnectFromLayout( false );
2518 assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
2521 if ( _rNewObj.GetAnchorFrame() != this )
2523 if (!m_pDrawObjs)
2525 m_pDrawObjs.reset(new SwSortedObjs());
2527 m_pDrawObjs->Insert(_rNewObj);
2528 _rNewObj.ChgAnchorFrame( this );
2531 // #i113730#
2532 // Assure the control objects and group objects containing controls are on the control layer
2533 if ( ::CheckControlLayer( _rNewObj.DrawObj() ) )
2535 const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
2536 const SdrLayerID aCurrentLayer(_rNewObj.DrawObj()->GetLayer());
2537 const SdrLayerID aControlLayerID(rIDDMA.GetControlsId());
2538 const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId());
2540 if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID)
2542 if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() ||
2543 aCurrentLayer == rIDDMA.GetInvisibleHeavenId() )
2545 _rNewObj.DrawObj()->SetLayer(aInvisibleControlLayerID);
2547 else
2549 _rNewObj.DrawObj()->SetLayer(aControlLayerID);
2551 //The layer is part of the key used to sort the obj, so update
2552 //its position if the layer changed.
2553 m_pDrawObjs->Update(_rNewObj);
2557 // no direct positioning needed, but invalidate the drawing object position
2558 _rNewObj.InvalidateObjPos();
2560 // register at page frame
2561 SwPageFrame* pPage = FindPageFrame();
2562 if ( pPage )
2564 pPage->AppendDrawObjToPage( _rNewObj );
2567 // Notify accessible layout.
2568 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2569 SwViewShell* pSh = getRootFrame()->GetCurrShell();
2570 if( pSh )
2572 SwRootFrame* pLayout = getRootFrame();
2573 if( pLayout && pLayout->IsAnyShellAccessible() )
2575 pSh->Imp()->AddAccessibleObj( _rNewObj.GetDrawObj() );
2578 #endif
2580 assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
2583 void SwFrame::RemoveDrawObj( SwAnchoredObject& _rToRemoveObj )
2585 // Notify accessible layout.
2586 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2587 SwViewShell* pSh = getRootFrame()->GetCurrShell();
2588 if( pSh )
2590 SwRootFrame* pLayout = getRootFrame();
2591 if (pLayout && pLayout->IsAnyShellAccessible())
2592 pSh->Imp()->DisposeAccessibleObj(_rToRemoveObj.GetDrawObj(), false);
2594 #endif
2596 // deregister from page frame
2597 SwPageFrame* pPage = _rToRemoveObj.GetPageFrame();
2598 if ( pPage && pPage->GetSortedObjs() )
2599 pPage->RemoveDrawObjFromPage( _rToRemoveObj );
2601 m_pDrawObjs->Remove(_rToRemoveObj);
2602 if (!m_pDrawObjs->size())
2604 m_pDrawObjs.reset();
2606 _rToRemoveObj.ChgAnchorFrame( nullptr );
2608 assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
2611 void SwFrame::InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs )
2613 if ( !GetDrawObjs() )
2614 return;
2616 // #i26945# - determine page the frame is on,
2617 // in order to check, if anchored object is registered at the same
2618 // page.
2619 const SwPageFrame* pPageFrame = FindPageFrame();
2620 // #i28701# - re-factoring
2621 for (SwAnchoredObject* pAnchoredObj : *GetDrawObjs())
2623 if ( _bNoInvaOfAsCharAnchoredObjs &&
2624 (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
2625 == RndStdIds::FLY_AS_CHAR) )
2627 continue;
2629 // #i26945# - no invalidation, if anchored object
2630 // isn't registered at the same page and instead is registered at
2631 // the page, where its anchor character text frame is on.
2632 if ( pAnchoredObj->GetPageFrame() &&
2633 pAnchoredObj->GetPageFrame() != pPageFrame )
2635 SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
2636 if ( pAnchorCharFrame &&
2637 pAnchoredObj->GetPageFrame() == pAnchorCharFrame->FindPageFrame() )
2639 continue;
2641 // #115759# - unlock its position, if anchored
2642 // object isn't registered at the page, where its anchor
2643 // character text frame is on, respectively if it has no
2644 // anchor character text frame.
2645 else
2647 pAnchoredObj->UnlockPosition();
2650 // #i51474# - reset flag, that anchored object
2651 // has cleared environment, and unlock its position, if the anchored
2652 // object is registered at the same page as the anchor frame is on.
2653 if ( pAnchoredObj->ClearedEnvironment() &&
2654 pAnchoredObj->GetPageFrame() &&
2655 pAnchoredObj->GetPageFrame() == pPageFrame )
2657 pAnchoredObj->UnlockPosition();
2658 pAnchoredObj->SetClearedEnvironment( false );
2660 // distinguish between writer fly frames and drawing objects
2661 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
2663 pFly->Invalidate_();
2664 pFly->InvalidatePos_();
2666 else
2668 pAnchoredObj->InvalidateObjPos();
2670 } // end of loop on objects, which are connected to the frame
2673 // #i26945# - correct check, if anchored object is a lower
2674 // of the layout frame. E.g., anchor character text frame can be a follow text
2675 // frame.
2676 // #i44016# - add parameter <_bUnlockPosOfObjs> to
2677 // force an unlockposition call for the lower objects.
2678 void SwLayoutFrame::NotifyLowerObjs( const bool _bUnlockPosOfObjs )
2680 // invalidate lower floating screen objects
2681 SwPageFrame* pPageFrame = FindPageFrame();
2682 if ( !(pPageFrame && pPageFrame->GetSortedObjs()) )
2683 return;
2685 SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
2686 for (SwAnchoredObject* pObj : rObjs)
2688 // #i26945# - check if anchored object is a lower
2689 // of the layout frame is changed to check, if its anchor frame
2690 // is a lower of the layout frame.
2691 // Determine the anchor frame - usually it's the anchor frame,
2692 // for at-character/as-character anchored objects the anchor character
2693 // text frame is taken.
2694 const SwFrame* pAnchorFrame = pObj->GetAnchorFrameContainingAnchPos();
2695 if ( auto pFly = pObj->DynCastFlyFrame() )
2697 if ( pFly->getFrameArea().Left() == FAR_AWAY )
2698 continue;
2700 if ( pFly->IsAnLower( this ) )
2701 continue;
2703 // #i26945# - use <pAnchorFrame> to check, if
2704 // fly frame is lower of layout frame resp. if fly frame is
2705 // at a different page registered as its anchor frame is on.
2706 const bool bLow = IsAnLower( pAnchorFrame );
2707 if ( bLow || pAnchorFrame->FindPageFrame() != pPageFrame )
2709 pFly->Invalidate_( pPageFrame );
2710 if ( !bLow || pFly->IsFlyAtContentFrame() )
2712 // #i44016#
2713 if ( _bUnlockPosOfObjs )
2715 pFly->UnlockPosition();
2717 pFly->InvalidatePos_();
2719 else
2720 pFly->InvalidatePrt_();
2723 else
2725 assert( dynamic_cast<const SwAnchoredDrawObject*>( pObj) &&
2726 "<SwLayoutFrame::NotifyFlys() - anchored object of unexpected type" );
2727 // tdf#156728 invalidate fly positioned dependent on header/footer size
2728 bool isPositionedByHF(false);
2729 if (IsHeaderFrame() || IsFooterFrame())
2731 auto const nO(pObj->GetFrameFormat().GetVertOrient().GetRelationOrient());
2732 if (nO == text::RelOrientation::PAGE_PRINT_AREA
2733 || nO == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM
2734 || nO == text::RelOrientation::PAGE_PRINT_AREA_TOP)
2736 isPositionedByHF = true;
2739 // #i26945# - use <pAnchorFrame> to check, if
2740 // fly frame is lower of layout frame resp. if fly frame is
2741 // at a different page registered as its anchor frame is on.
2742 if ( IsAnLower( pAnchorFrame ) ||
2743 isPositionedByHF ||
2744 pAnchorFrame->FindPageFrame() != pPageFrame )
2746 // #i44016#
2747 if ( _bUnlockPosOfObjs )
2749 pObj->UnlockPosition();
2751 pObj->InvalidateObjPos();
2757 void SwFlyFrame::NotifyDrawObj()
2759 SwVirtFlyDrawObj* pObj = GetVirtDrawObj();
2760 pObj->SetRect();
2761 pObj->SetBoundAndSnapRectsDirty();
2762 pObj->SetChanged();
2763 pObj->BroadcastObjectChange();
2765 if ( GetFormat()->GetSurround().IsContour() )
2767 ClrContourCache( pObj );
2769 else if(IsFlyFreeFrame() && static_cast< const SwFlyFreeFrame* >(this)->supportsAutoContour())
2771 // RotateFlyFrame3: Also need to clear when changes happen
2772 // Caution: isTransformableSwFrame is already reset when resetting rotation, so
2773 // *additionally* reset in SwFlyFreeFrame::MakeAll when no more rotation
2774 ClrContourCache( pObj );
2778 Size SwFlyFrame::CalcRel( const SwFormatFrameSize &rSz ) const
2780 Size aRet( rSz.GetSize() );
2782 const SwFrame *pRel = IsFlyLayFrame() ? GetAnchorFrame() : GetAnchorFrame()->GetUpper();
2783 if( pRel ) // LAYER_IMPL
2785 tools::Long nRelWidth = LONG_MAX, nRelHeight = LONG_MAX;
2786 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
2787 if ( ( pRel->IsBodyFrame() || pRel->IsPageFrame() ) &&
2788 pSh && pSh->GetViewOptions()->getBrowseMode() &&
2789 pSh->VisArea().HasArea() )
2791 nRelWidth = pSh->GetBrowseWidth();
2792 nRelHeight = pSh->VisArea().Height();
2793 Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
2794 nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
2795 nRelHeight -= 2*aBorder.Height();
2796 nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
2799 // At the moment only the "== PAGE_FRAME" and "!= PAGE_FRAME" cases are handled.
2800 // When size is a relative to page size, ignore size of SwBodyFrame.
2801 if (rSz.GetWidthPercentRelation() != text::RelOrientation::PAGE_FRAME)
2802 nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
2803 else if ( pRel->IsPageFrame() )
2804 nRelWidth = std::min( nRelWidth, pRel->getFrameArea().Width() );
2806 if (rSz.GetHeightPercentRelation() != text::RelOrientation::PAGE_FRAME)
2807 nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
2808 else if ( pRel->IsPageFrame() )
2809 nRelHeight = std::min( nRelHeight, pRel->getFrameArea().Height() );
2811 if( !pRel->IsPageFrame() )
2813 const SwPageFrame* pPage = FindPageFrame();
2814 if( pPage )
2816 if (rSz.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME)
2817 // Ignore margins of pPage.
2818 nRelWidth = std::min( nRelWidth, pPage->getFrameArea().Width() );
2819 else
2820 nRelWidth = std::min( nRelWidth, pPage->getFramePrintArea().Width() );
2821 if (rSz.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME)
2822 // Ignore margins of pPage.
2823 nRelHeight = std::min( nRelHeight, pPage->getFrameArea().Height() );
2824 else
2825 nRelHeight = std::min( nRelHeight, pPage->getFramePrintArea().Height() );
2829 if ( rSz.GetWidthPercent() && rSz.GetWidthPercent() != SwFormatFrameSize::SYNCED )
2830 aRet.setWidth( nRelWidth * rSz.GetWidthPercent() / 100 );
2831 if ( rSz.GetHeightPercent() && rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED )
2832 aRet.setHeight( nRelHeight * rSz.GetHeightPercent() / 100 );
2834 if ( rSz.GetHeight() && rSz.GetWidthPercent() == SwFormatFrameSize::SYNCED )
2836 aRet.setWidth( aRet.Width() * ( aRet.Height()) );
2837 aRet.setWidth( aRet.Width() / ( rSz.GetHeight()) );
2839 else if ( rSz.GetWidth() && rSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
2841 aRet.setHeight( aRet.Height() * ( aRet.Width()) );
2842 aRet.setHeight( aRet.Height() / ( rSz.GetWidth()) );
2845 return aRet;
2848 static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame )
2850 SwTwips nRet = 0;
2851 SwTwips nMin = 0;
2852 const SwFrame* pFrame = rFrame.Lower();
2854 // No autowidth defined for columned frames
2855 if ( !pFrame || pFrame->IsColumnFrame() )
2856 return nRet;
2858 int nParagraphCount = 0;
2859 while ( pFrame )
2861 nParagraphCount++;
2862 if ( pFrame->IsSctFrame() )
2864 nMin = lcl_CalcAutoWidth( *static_cast<const SwSectionFrame*>(pFrame) );
2866 if ( pFrame->IsTextFrame() )
2868 nMin = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent();
2869 auto const& rParaSet(static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet());
2870 SvxFirstLineIndentItem const& rFirstLine(rParaSet.GetFirstLineIndent());
2871 SvxTextLeftMarginItem const& rLeftMargin(rParaSet.GetTextLeftMargin());
2872 SvxRightMarginItem const& rRightMargin(rParaSet.GetRightMargin());
2873 if (!static_cast<const SwTextFrame*>(pFrame)->IsLocked())
2875 nMin += rRightMargin.GetRight() + rLeftMargin.GetTextLeft()
2876 + rFirstLine.GetTextFirstLineOffset();
2879 else if ( pFrame->IsTabFrame() )
2881 const SwFormatFrameSize& rTableFormatSz = static_cast<const SwTabFrame*>(pFrame)->GetTable()->GetFrameFormat()->GetFrameSize();
2882 if ( USHRT_MAX == rTableFormatSz.GetSize().Width() ||
2883 text::HoriOrientation::NONE == static_cast<const SwTabFrame*>(pFrame)->GetFormat()->GetHoriOrient().GetHoriOrient() )
2885 const SwPageFrame* pPage = rFrame.FindPageFrame();
2886 // auto width table
2887 nMin = pFrame->GetUpper()->IsVertical() ?
2888 pPage->getFramePrintArea().Height() :
2889 pPage->getFramePrintArea().Width();
2891 else
2893 nMin = rTableFormatSz.GetSize().Width();
2897 if ( nMin > nRet )
2898 nRet = nMin;
2900 pFrame = pFrame->GetNext();
2903 // tdf#124423 In Microsoft compatibility mode: widen the frame to max (PrintArea of the frame it anchored to) if it contains at least 2 paragraphs,
2904 // or 1 paragraph wider than its parent area.
2905 if (rFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA))
2907 const SwFrame* pFrameRect = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame*>(&rFrame)->GetAnchorFrame() : rFrame.Lower()->FindPageFrame();
2908 SwTwips nParentWidth = rFrame.IsVertical() ? pFrameRect->getFramePrintArea().Height() : pFrameRect->getFramePrintArea().Width();
2909 if (nParagraphCount > 1 || nRet > nParentWidth)
2911 return nParentWidth;
2915 return nRet;
2918 /// #i13147# - If called for paint and the <SwNoTextFrame> contains
2919 /// a graphic, load of intrinsic graphic has to be avoided.
2920 bool SwFlyFrame::GetContour( tools::PolyPolygon& rContour,
2921 const bool _bForPaint ) const
2923 vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
2924 bool bRet = false;
2925 const bool bIsCandidate(Lower() && Lower()->IsNoTextFrame());
2927 if(bIsCandidate)
2929 if(GetFormat()->GetSurround().IsContour())
2931 SwNoTextNode *pNd = const_cast<SwNoTextNode*>(static_cast<const SwNoTextNode*>(static_cast<const SwNoTextFrame*>(Lower())->GetNode()));
2932 // #i13147# - determine <GraphicObject> instead of <Graphic>
2933 // in order to avoid load of graphic, if <SwNoTextNode> contains a graphic
2934 // node and method is called for paint.
2935 std::unique_ptr<GraphicObject> xTmpGrfObj;
2936 const GraphicObject* pGrfObj = nullptr;
2937 const SwGrfNode* pGrfNd = pNd->GetGrfNode();
2938 if ( pGrfNd && _bForPaint )
2940 pGrfObj = &(pGrfNd->GetGrfObj());
2942 else
2944 xTmpGrfObj.reset(new GraphicObject(pNd->GetGraphic()));
2945 pGrfObj = xTmpGrfObj.get();
2947 assert(pGrfObj && "SwFlyFrame::GetContour() - No Graphic/GraphicObject found at <SwNoTextNode>.");
2948 if (pGrfObj->GetType() != GraphicType::NONE)
2950 if( !pNd->HasContour() )
2952 //#i13147# - no <CreateContour> for a graphic
2953 // during paint. Thus, return (value of <bRet> should be <false>).
2954 if ( pGrfNd && _bForPaint )
2956 OSL_FAIL( "SwFlyFrame::GetContour() - No Contour found at <SwNoTextNode> during paint." );
2957 return bRet;
2959 pNd->CreateContour();
2961 pNd->GetContour( rContour );
2962 // The Node holds the Polygon matching the original size of the graphic
2963 // We need to include the scaling here
2964 SwRect aClip;
2965 SwRect aOrig;
2966 Lower()->Calc(pRenderContext);
2967 static_cast<const SwNoTextFrame*>(Lower())->GetGrfArea( aClip, &aOrig );
2968 // #i13147# - copy method code <SvxContourDlg::ScaleContour(..)>
2969 // in order to avoid that graphic has to be loaded for contour scale.
2970 //SvxContourDlg::ScaleContour( rContour, aGrf, MapUnit::MapTwip, aOrig.SSize() );
2972 OutputDevice* pOutDev = Application::GetDefaultDevice();
2973 const MapMode aDispMap( MapUnit::MapTwip );
2974 const MapMode aGrfMap( pGrfObj->GetPrefMapMode() );
2975 const Size aGrfSize( pGrfObj->GetPrefSize() );
2976 Size aOrgSize;
2977 Point aNewPoint;
2978 bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
2980 if ( bPixelMap )
2981 aOrgSize = pOutDev->PixelToLogic( aGrfSize, aDispMap );
2982 else
2983 aOrgSize = OutputDevice::LogicToLogic( aGrfSize, aGrfMap, aDispMap );
2985 if ( aOrgSize.Width() && aOrgSize.Height() )
2987 double fScaleX = static_cast<double>(aOrig.Width()) / aOrgSize.Width();
2988 double fScaleY = static_cast<double>(aOrig.Height()) / aOrgSize.Height();
2990 for ( sal_uInt16 j = 0, nPolyCount = rContour.Count(); j < nPolyCount; j++ )
2992 tools::Polygon& rPoly = rContour[ j ];
2994 for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
2996 if ( bPixelMap )
2997 aNewPoint = pOutDev->PixelToLogic( rPoly[ i ], aDispMap );
2998 else
2999 aNewPoint = OutputDevice::LogicToLogic( rPoly[ i ], aGrfMap, aDispMap );
3001 rPoly[ i ] = Point( FRound( aNewPoint.getX() * fScaleX ), FRound( aNewPoint.getY() * fScaleY ) );
3006 // destroy created <GraphicObject>.
3007 xTmpGrfObj.reset();
3008 rContour.Move( aOrig.Left(), aOrig.Top() );
3009 if( !aClip.Width() )
3010 aClip.Width( 1 );
3011 if( !aClip.Height() )
3012 aClip.Height( 1 );
3013 rContour.Clip( aClip.SVRect() );
3014 rContour.Optimize(PolyOptimizeFlags::CLOSE);
3015 bRet = true;
3018 else if (IsFlyFreeFrame())
3020 const SwFlyFreeFrame* pSwFlyFreeFrame(static_cast< const SwFlyFreeFrame* >(this));
3022 if(nullptr != pSwFlyFreeFrame &&
3023 pSwFlyFreeFrame->supportsAutoContour() &&
3024 // isTransformableSwFrame already used in supportsAutoContour(), but
3025 // better check twice when it may get changed there...
3026 pSwFlyFreeFrame->isTransformableSwFrame())
3028 // RotateFlyFrame: use untransformed SwFrame to allow text floating around.
3029 // Will be transformed below
3030 const TransformableSwFrame* pTransformableSwFrame(pSwFlyFreeFrame->getTransformableSwFrame());
3031 const SwRect aFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
3032 rContour = tools::PolyPolygon(tools::Polygon(aFrameArea.SVRect()));
3033 bRet = (0 != rContour.Count());
3037 if(bRet && 0 != rContour.Count())
3039 if (IsFlyFreeFrame() &&
3040 static_cast< const SwFlyFreeFrame* >(this)->isTransformableSwFrame())
3042 // Need to adapt contour to transformation
3043 basegfx::B2DVector aScale, aTranslate;
3044 double fRotate, fShearX;
3045 getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
3047 if(!basegfx::fTools::equalZero(fRotate))
3049 basegfx::B2DPolyPolygon aSource(rContour.getB2DPolyPolygon());
3050 const basegfx::B2DPoint aCenter(getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
3051 const basegfx::B2DHomMatrix aRotateAroundCenter(
3052 basegfx::utils::createRotateAroundPoint(
3053 aCenter.getX(),
3054 aCenter.getY(),
3055 fRotate));
3056 aSource.transform(aRotateAroundCenter);
3057 rContour = tools::PolyPolygon(aSource);
3063 return bRet;
3067 const SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj() const
3069 return static_cast<const SwVirtFlyDrawObj*>(GetDrawObj());
3071 SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj()
3073 return static_cast<SwVirtFlyDrawObj*>(DrawObj());
3076 // implementation of pure virtual method declared in
3077 // base class <SwAnchoredObject>
3079 void SwFlyFrame::InvalidateObjPos()
3081 InvalidatePos();
3082 // #i68520#
3083 InvalidateObjRectWithSpaces();
3086 SwFrameFormat& SwFlyFrame::GetFrameFormat()
3088 OSL_ENSURE( GetFormat(),
3089 "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
3090 return *GetFormat();
3092 const SwFrameFormat& SwFlyFrame::GetFrameFormat() const
3094 OSL_ENSURE( GetFormat(),
3095 "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
3096 return *GetFormat();
3099 SwRect SwFlyFrame::GetObjRect() const
3101 return getFrameArea();
3104 // #i70122#
3105 // for Writer fly frames the bounding rectangle equals the object rectangles
3106 SwRect SwFlyFrame::GetObjBoundRect() const
3108 return GetObjRect();
3111 // #i68520#
3112 bool SwFlyFrame::SetObjTop_( const SwTwips _nTop )
3114 const bool bChanged( getFrameArea().Pos().getY() != _nTop );
3115 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3116 aFrm.Pos().setY(_nTop);
3118 return bChanged;
3120 bool SwFlyFrame::SetObjLeft_( const SwTwips _nLeft )
3122 const bool bChanged( getFrameArea().Pos().getX() != _nLeft );
3123 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
3124 aFrm.Pos().setX(_nLeft);
3126 return bChanged;
3129 /** method to assure that anchored object is registered at the correct
3130 page frame
3132 OD 2004-07-02 #i28701#
3134 void SwFlyFrame::RegisterAtCorrectPage()
3136 // default behaviour is to do nothing.
3139 void SwFlyFrame::RegisterAtPage(SwPageFrame &)
3141 // default behaviour is to do nothing.
3144 /** method to determine, if a <MakeAll()> on the Writer fly frame is possible
3146 OD 2004-05-11 #i28701#
3148 bool SwFlyFrame::IsFormatPossible() const
3150 return SwAnchoredObject::IsFormatPossible() &&
3151 !IsLocked() && !IsColLocked();
3154 void SwFlyFrame::GetAnchoredObjects( std::vector<SwAnchoredObject*>& aVector, const SwFormat& rFormat )
3156 SwIterator<SwFlyFrame,SwFormat> aIter( rFormat );
3157 for( SwFlyFrame* pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next() )
3158 aVector.push_back( pFlyFrame );
3161 const SwFlyFrameFormat * SwFlyFrame::GetFormat() const
3163 return static_cast< const SwFlyFrameFormat * >( GetDep() );
3166 SwFlyFrameFormat * SwFlyFrame::GetFormat()
3168 return static_cast< SwFlyFrameFormat * >( GetDep() );
3171 void SwFlyFrame::dumpAsXml(xmlTextWriterPtr writer) const
3173 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("fly"));
3174 dumpAsXmlAttributes(writer);
3176 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
3177 dumpInfosAsXml(writer);
3178 (void)xmlTextWriterEndElement(writer);
3179 const SwSortedObjs* pAnchored = GetDrawObjs();
3180 if (pAnchored && pAnchored->size() > 0)
3182 (void)xmlTextWriterStartElement(writer, BAD_CAST("anchored"));
3184 for (SwAnchoredObject* pObject : *pAnchored)
3186 pObject->dumpAsXml(writer);
3189 (void)xmlTextWriterEndElement(writer);
3191 dumpChildrenAsXml(writer);
3193 SwAnchoredObject::dumpAsXml(writer);
3195 (void)xmlTextWriterEndElement(writer);
3198 void SwFlyFrame::Calc(vcl::RenderContext* pRenderContext) const
3200 if ( !m_bValidContentPos )
3201 const_cast<SwFlyFrame*>(this)->PrepareMake(pRenderContext);
3202 else
3203 SwLayoutFrame::Calc(pRenderContext);
3206 SwTwips SwFlyFrame::CalcContentHeight(const SwBorderAttrs *pAttrs, const SwTwips nMinHeight, const SwTwips nUL)
3208 SwRectFnSet aRectFnSet(this);
3209 SwTwips nHeight = 0;
3210 if ( Lower() )
3212 if ( Lower()->IsColumnFrame() )
3214 FormatWidthCols( *pAttrs, nUL, nMinHeight );
3215 nHeight = aRectFnSet.GetHeight(Lower()->getFrameArea());
3217 else
3219 SwFrame *pFrame = Lower();
3220 while ( pFrame )
3222 nHeight += aRectFnSet.GetHeight(pFrame->getFrameArea());
3223 if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
3224 // This TextFrame would like to be a bit larger
3225 nHeight += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
3226 - aRectFnSet.GetHeight(pFrame->getFramePrintArea());
3227 else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
3228 nHeight += static_cast<SwSectionFrame*>(pFrame)->Undersize();
3229 pFrame = pFrame->GetNext();
3232 if ( GetDrawObjs() )
3234 const size_t nCnt = GetDrawObjs()->size();
3235 SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
3236 SwTwips nBorder = aRectFnSet.GetHeight(getFrameArea()) -
3237 aRectFnSet.GetHeight(getFramePrintArea());
3238 for ( size_t i = 0; i < nCnt; ++i )
3240 SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
3241 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
3243 // consider only Writer fly frames, which follow the text flow.
3244 if ( pFly->IsFlyLayFrame() &&
3245 pFly->getFrameArea().Top() != FAR_AWAY &&
3246 pFly->GetFormat()->GetFollowTextFlow().GetValue() )
3248 SwTwips nDist = -aRectFnSet.BottomDist( pFly->getFrameArea(), nTop );
3249 if( nDist > nBorder + nHeight )
3250 nHeight = nDist - nBorder;
3256 return nHeight;
3259 const SwFormatAnchor* SwFlyFrame::GetAnchorFromPoolItem(const SfxPoolItem& rItem)
3261 switch(rItem.Which())
3263 case RES_ATTRSET_CHG:
3264 return rItem.StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItem(RES_ANCHOR, false);
3265 case RES_ANCHOR:
3266 return static_cast<const SwFormatAnchor*>(&rItem);
3267 default:
3268 return nullptr;
3272 const SwFlyFrame* SwFlyFrame::DynCastFlyFrame() const
3274 return this;
3277 SwFlyFrame* SwFlyFrame::DynCastFlyFrame()
3279 return this;
3282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */