tdf#149907: sc: Add UItest
[LibreOffice.git] / sw / source / core / layout / frmtool.cxx
blob8205ee2680a9cc550135fc73c36178bc592f8242
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 <svx/svdpage.hxx>
23 #include <editeng/brushitem.hxx>
24 #include <editeng/shaditem.hxx>
25 #include <editeng/ulspitem.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/lspcitem.hxx>
28 #include <editeng/fhgtitem.hxx>
29 #include <sal/log.hxx>
30 #include <o3tl/deleter.hxx>
31 #include <osl/diagnose.h>
33 #include <drawdoc.hxx>
34 #include <fmtornt.hxx>
35 #include <fmthdft.hxx>
36 #include <fmtfsize.hxx>
37 #include <fmtsrnd.hxx>
38 #include <docary.hxx>
39 #include <lineinfo.hxx>
40 #include <swmodule.hxx>
41 #include <pagefrm.hxx>
42 #include <colfrm.hxx>
43 #include <fesh.hxx>
44 #include <viewimp.hxx>
45 #include <viewopt.hxx>
46 #include <dflyobj.hxx>
47 #include <dcontact.hxx>
48 #include <frmatr.hxx>
49 #include <frmtool.hxx>
50 #include <tabfrm.hxx>
51 #include <rowfrm.hxx>
52 #include <ftnfrm.hxx>
53 #include <txtfrm.hxx>
54 #include <notxtfrm.hxx>
55 #include <flyfrms.hxx>
56 #include <layact.hxx>
57 #include <pagedesc.hxx>
58 #include <section.hxx>
59 #include <sectfrm.hxx>
60 #include <node2lay.hxx>
61 #include <ndole.hxx>
62 #include <hints.hxx>
63 #include "layhelp.hxx"
64 #include <laycache.hxx>
65 #include <rootfrm.hxx>
66 #include <paratr.hxx>
67 #include <redline.hxx>
68 #include <sortedobjs.hxx>
69 #include <objectformatter.hxx>
70 #include <calbck.hxx>
71 #include <ndtxt.hxx>
72 #include <undobj.hxx>
73 #include <DocumentSettingManager.hxx>
74 #include <IDocumentDrawModelAccess.hxx>
75 #include <IDocumentLayoutAccess.hxx>
76 #include <IDocumentTimerAccess.hxx>
77 #include <IDocumentRedlineAccess.hxx>
78 #include <IDocumentFieldsAccess.hxx>
79 #include <IDocumentState.hxx>
80 #include <frameformats.hxx>
81 #include <boost/circular_buffer.hpp>
82 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
84 using namespace ::com::sun::star;
86 namespace {
87 // FIXME: would likely better be a member of SwRootFrame instead of a global flag
88 bool isFlyCreationSuppressed = false;
90 namespace sw {
91 FlyCreationSuppressor::FlyCreationSuppressor(bool wasAlreadySuppressedAllowed)
92 : m_wasAlreadySuppressed(isFlyCreationSuppressed)
94 (void)wasAlreadySuppressedAllowed;
95 assert(wasAlreadySuppressedAllowed || !isFlyCreationSuppressed);
96 isFlyCreationSuppressed = true;
98 FlyCreationSuppressor::~FlyCreationSuppressor()
100 isFlyCreationSuppressed = m_wasAlreadySuppressed;
104 bool bObjsDirect = true;
105 bool bSetCompletePaintOnInvalidate = false;
107 sal_uInt8 StackHack::s_nCnt = 0;
108 bool StackHack::s_bLocked = false;
110 SwFrameNotify::SwFrameNotify( SwFrame *pF ) :
111 mpFrame( pF ),
112 maFrame( pF->getFrameArea() ),
113 maPrt( pF->getFramePrintArea() ),
114 mbInvaKeep( false ),
115 mbValidSize( pF->isFrameAreaSizeValid() )
117 if ( pF->IsTextFrame() )
119 mnFlyAnchorOfst = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( true );
120 mnFlyAnchorOfstNoWrap = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( false );
122 else
124 mnFlyAnchorOfst = 0;
125 mnFlyAnchorOfstNoWrap = 0;
128 mbHadFollow = pF->IsContentFrame() && static_cast<SwContentFrame*>(pF)->GetFollow();
131 SwFrameNotify::~SwFrameNotify()
133 suppress_fun_call_w_exception(ImplDestroy());
136 void SwFrameNotify::ImplDestroy()
138 SwRectFnSet aRectFnSet(mpFrame);
139 const bool bAbsP = aRectFnSet.PosDiff(maFrame, mpFrame->getFrameArea());
140 const bool bChgWidth =
141 aRectFnSet.GetWidth(maFrame) != aRectFnSet.GetWidth(mpFrame->getFrameArea());
142 const bool bChgHeight =
143 aRectFnSet.GetHeight(maFrame)!=aRectFnSet.GetHeight(mpFrame->getFrameArea());
144 const bool bChgFlyBasePos = mpFrame->IsTextFrame() &&
145 ( ( mnFlyAnchorOfst != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( true ) ) ||
146 ( mnFlyAnchorOfstNoWrap != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( false ) ) );
148 if ( mpFrame->IsFlowFrame() && !mpFrame->IsInFootnote() )
150 SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( mpFrame );
152 if ( !pFlow->IsFollow() )
154 if ( !mpFrame->GetIndPrev() )
156 if ( mbInvaKeep )
158 SwFrame *pPre = pFlow->FindPrevIgnoreHidden();
159 if ( pPre && pPre->IsFlowFrame() )
161 // 1. pPre wants to keep with me:
162 bool bInvalidPrePos = SwFlowFrame::CastFlowFrame(pPre)->IsKeep(pPre->GetAttrSet()->GetKeep(), pPre->GetBreakItem())
163 && pPre->GetIndPrev();
165 // 2. pPre is a table and the last row wants to keep with me:
166 if ( !bInvalidPrePos && pPre->IsTabFrame() )
168 SwTabFrame* pPreTab = static_cast<SwTabFrame*>(pPre);
169 if ( pPreTab->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
171 SwRowFrame* pLastRow = static_cast<SwRowFrame*>(pPreTab->GetLastLower());
172 if ( pLastRow && pLastRow->ShouldRowKeepWithNext() )
173 bInvalidPrePos = true;
177 if ( bInvalidPrePos )
178 pPre->InvalidatePos();
182 else if ( !pFlow->HasFollow() )
184 tools::Long nOldHeight = aRectFnSet.GetHeight(maFrame);
185 tools::Long nNewHeight = aRectFnSet.GetHeight(mpFrame->getFrameArea());
186 if( (nOldHeight > nNewHeight) || (!nOldHeight && nNewHeight) )
187 pFlow->CheckKeep();
192 if ( bAbsP )
194 mpFrame->SetCompletePaint();
196 SwFrame* pNxt = mpFrame->GetIndNext();
197 // #121888# - skip empty section frames
198 while ( pNxt &&
199 pNxt->IsSctFrame() && !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
201 pNxt = pNxt->GetIndNext();
204 if ( pNxt )
206 pNxt->InvalidatePos();
207 if (pNxt->IsTextFrame() && static_cast<SwTextFrame*>(pNxt)->IsUndersized())
208 { // tdf#137523 it could have more space at new pos
209 pNxt->InvalidateSize();
210 pNxt->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
213 else
215 // #104100# - correct condition for setting retouche
216 // flag for vertical layout.
217 if( mpFrame->IsRetoucheFrame() &&
218 aRectFnSet.TopDist( maFrame, aRectFnSet.GetTop(mpFrame->getFrameArea()) ) > 0 )
220 mpFrame->SetRetouche();
223 // A fresh follow frame does not have to be invalidated, because
224 // it is already formatted:
225 if ( mbHadFollow || !mpFrame->IsContentFrame() || !static_cast<SwContentFrame*>(mpFrame)->GetFollow() )
227 if ( !mpFrame->IsTabFrame() || !static_cast<SwTabFrame*>(mpFrame)->GetFollow() )
228 mpFrame->InvalidateNextPos();
233 //For each resize of the background graphics is a repaint necessary.
234 const bool bPrtWidth =
235 aRectFnSet.GetWidth(maPrt) != aRectFnSet.GetWidth(mpFrame->getFramePrintArea());
236 const bool bPrtHeight =
237 aRectFnSet.GetHeight(maPrt)!=aRectFnSet.GetHeight(mpFrame->getFramePrintArea());
238 if ( bPrtWidth || bPrtHeight )
240 bool bUseNewFillProperties(false);
241 if (mpFrame->supportsFullDrawingLayerFillAttributeSet())
243 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(mpFrame->getSdrAllFillAttributesHelper());
244 if(aFillAttributes && aFillAttributes->isUsed())
246 bUseNewFillProperties = true;
247 // use SetCompletePaint if needed
248 if(aFillAttributes->needCompleteRepaint())
250 mpFrame->SetCompletePaint();
254 if (!bUseNewFillProperties)
256 const SvxGraphicPosition ePos = mpFrame->GetAttrSet()->GetBackground().GetGraphicPos();
257 if(GPOS_NONE != ePos && GPOS_TILED != ePos)
258 mpFrame->SetCompletePaint();
261 else
263 // #97597# - consider case that *only* margins between
264 // frame and printing area has changed. Then, frame has to be repainted,
265 // in order to force paint of the margin areas.
266 if ( !bAbsP && (bChgWidth || bChgHeight) )
268 mpFrame->SetCompletePaint();
272 const bool bPrtP = aRectFnSet.PosDiff( maPrt, mpFrame->getFramePrintArea() );
273 if ( bAbsP || bPrtP || bChgWidth || bChgHeight ||
274 bPrtWidth || bPrtHeight || bChgFlyBasePos )
276 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
277 if( mpFrame->IsAccessibleFrame() )
279 SwRootFrame *pRootFrame = mpFrame->getRootFrame();
280 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
281 pRootFrame->GetCurrShell() )
283 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame );
286 #endif
288 // Notification of anchored objects
289 if ( mpFrame->GetDrawObjs() )
291 const SwSortedObjs &rObjs = *mpFrame->GetDrawObjs();
292 SwPageFrame* pPageFrame = nullptr;
293 for (SwAnchoredObject* pObj : rObjs)
295 // OD 2004-03-31 #i26791# - no general distinction between
296 // Writer fly frames and drawing objects
297 bool bNotify = false;
298 bool bNotifySize = false;
299 SwContact* pContact = ::GetUserCall( pObj->GetDrawObj() );
300 if (!pContact)
301 continue;
302 const bool bAnchoredAsChar = pContact->ObjAnchoredAsChar();
303 if ( !bAnchoredAsChar )
305 // Notify object, which aren't anchored as-character:
307 // always notify objects, if frame position has changed
308 // or if the object is to-page|to-fly anchored.
309 if ( bAbsP ||
310 pContact->ObjAnchoredAtPage() ||
311 pContact->ObjAnchoredAtFly() )
313 bNotify = true;
315 // assure that to-fly anchored Writer fly frames are
316 // registered at the correct page frame, if frame
317 // position has changed.
318 if ( bAbsP && pContact->ObjAnchoredAtFly() &&
319 pObj->DynCastFlyFrame() != nullptr )
321 // determine to-fly anchored Writer fly frame
322 SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj);
323 // determine page frame of to-fly anchored
324 // Writer fly frame
325 SwPageFrame* pFlyPageFrame = pFlyFrame->FindPageFrame();
326 // determine page frame, if needed.
327 if ( !pPageFrame )
329 pPageFrame = mpFrame->FindPageFrame();
331 if ( pPageFrame != pFlyPageFrame )
333 OSL_ENSURE( pFlyPageFrame, "~SwFrameNotify: Fly from Nowhere" );
334 if( pFlyPageFrame )
335 pFlyPageFrame->MoveFly( pFlyFrame, pPageFrame );
336 else
337 pPageFrame->AppendFlyToPage( pFlyFrame );
341 // otherwise the objects are notified in dependence to
342 // its positioning and alignment
343 else
345 const SwFormatVertOrient& rVert =
346 pContact->GetFormat()->GetVertOrient();
347 if ( ( rVert.GetVertOrient() == text::VertOrientation::CENTER ||
348 rVert.GetVertOrient() == text::VertOrientation::BOTTOM ||
349 rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) &&
350 ( bChgHeight || bPrtHeight ) )
352 bNotify = true;
354 if ( !bNotify )
356 const SwFormatHoriOrient& rHori =
357 pContact->GetFormat()->GetHoriOrient();
358 if ( ( rHori.GetHoriOrient() != text::HoriOrientation::NONE ||
359 rHori.GetRelationOrient()== text::RelOrientation::PRINT_AREA ||
360 rHori.GetRelationOrient()== text::RelOrientation::FRAME ) &&
361 ( bChgWidth || bPrtWidth || bChgFlyBasePos ) )
363 bNotify = true;
368 else if ( bPrtWidth )
370 // Notify as-character anchored objects, if printing area
371 // width has changed.
372 bNotify = true;
373 bNotifySize = true;
376 // perform notification via the corresponding invalidations
377 if ( bNotify )
379 if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
381 if ( bNotifySize )
382 pFlyFrame->InvalidateSize_();
383 // #115759# - no invalidation of
384 // position for as-character anchored objects.
385 if ( !bAnchoredAsChar )
387 pFlyFrame->InvalidatePos_();
389 pFlyFrame->Invalidate_();
391 else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
393 // #115759# - no invalidation of
394 // position for as-character anchored objects.
395 if ( !bAnchoredAsChar )
397 pObj->InvalidateObjPos();
400 else
402 OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - unknown anchored object type." );
408 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
409 else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() )
411 SwRootFrame *pRootFrame = mpFrame->getRootFrame();
412 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
413 pRootFrame->GetCurrShell() )
415 pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame );
418 #endif
420 // #i9046# Automatic frame width
421 SwFlyFrame* pFly = nullptr;
422 // #i35879# Do not trust the inf flags. pFrame does not
423 // necessarily have to have an upper!
424 if ( mpFrame->IsFlyFrame() || nullptr == ( pFly = mpFrame->ImplFindFlyFrame() ))
425 return;
427 // #i61999#
428 // no invalidation of columned Writer fly frames, because automatic
429 // width doesn't make sense for such Writer fly frames.
430 SwFrame* pLower = pFly->Lower();
431 if ( !pLower || pLower->IsColumnFrame() )
432 return;
434 const SwFormatFrameSize &rFrameSz = pFly->GetFormat()->GetFrameSize();
436 // This could be optimized. Basically the fly frame only has to
437 // be invalidated, if the first line of pFrame (if pFrame is a content
438 // frame, for other frame types it's the print area) has changed its
439 // size and pFrame was responsible for the current width of pFly. On
440 // the other hand, this is only rarely used and re-calculation of
441 // the fly frame does not cause too much trouble. So we keep it this
442 // way:
443 if ( SwFrameSize::Fixed != rFrameSz.GetWidthSizeType() )
445 // #i50668#, #i50998# - invalidation of position
446 // of as-character anchored fly frames not needed and can cause
447 // layout loops
448 if ( dynamic_cast<const SwFlyInContentFrame*>( pFly) == nullptr )
450 pFly->InvalidatePos();
452 pFly->InvalidateSize();
456 SwLayNotify::SwLayNotify( SwLayoutFrame *pLayFrame ) :
457 SwFrameNotify( pLayFrame ),
458 m_bLowersComplete( false )
462 // OD 2004-05-11 #i28701# - local method to invalidate the position of all
463 // frames inclusive its floating screen objects, which are lowers of the given
464 // layout frame
465 static void lcl_InvalidatePosOfLowers( SwLayoutFrame& _rLayoutFrame )
467 if( _rLayoutFrame.IsFlyFrame() && _rLayoutFrame.GetDrawObjs() )
469 _rLayoutFrame.InvalidateObjs( false );
472 SwFrame* pLowerFrame = _rLayoutFrame.Lower();
473 while ( pLowerFrame )
475 pLowerFrame->InvalidatePos();
476 if ( pLowerFrame->IsTextFrame() )
478 static_cast<SwTextFrame*>(pLowerFrame)->Prepare( PrepareHint::FramePositionChanged );
480 else if ( pLowerFrame->IsTabFrame() )
482 pLowerFrame->InvalidatePrt();
485 pLowerFrame->InvalidateObjs( false );
487 pLowerFrame = pLowerFrame->GetNext();
491 void SwLayNotify::ImplDestroy()
493 SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(mpFrame);
494 SwRectFnSet aRectFnSet(pLay);
495 bool bNotify = false;
496 if ( pLay->getFramePrintArea().SSize() != maPrt.SSize() )
498 if ( !IsLowersComplete() )
500 bool bInvaPercent;
502 if ( pLay->IsRowFrame() )
504 bInvaPercent = true;
505 tools::Long nNew = aRectFnSet.GetHeight(pLay->getFramePrintArea());
506 if( nNew != aRectFnSet.GetHeight(maPrt) )
507 static_cast<SwRowFrame*>(pLay)->AdjustCells( nNew, true);
508 if( aRectFnSet.GetWidth(pLay->getFramePrintArea())
509 != aRectFnSet.GetWidth(maPrt) )
510 static_cast<SwRowFrame*>(pLay)->AdjustCells( 0, false );
512 else
514 //Proportional adoption of the internal.
515 //1. If the formatted is no Fly
516 //2. If he contains no columns
517 //3. If the Fly has a fixed height and the columns
518 // are next to be.
519 // Hoehe danebenliegen.
520 //4. Never at SectionFrames.
521 bool bLow;
522 if( pLay->IsFlyFrame() )
524 if ( pLay->Lower() )
526 bLow = !pLay->Lower()->IsColumnFrame() ||
527 aRectFnSet.GetHeight(pLay->Lower()->getFrameArea())
528 != aRectFnSet.GetHeight(pLay->getFramePrintArea());
530 else
531 bLow = false;
533 else if( pLay->IsSctFrame() )
535 if ( pLay->Lower() )
537 if( pLay->Lower()->IsColumnFrame() && pLay->Lower()->GetNext() )
538 bLow = pLay->Lower()->getFrameArea().Height() != pLay->getFramePrintArea().Height();
539 else
540 bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
542 else
543 bLow = false;
545 else if( pLay->IsFooterFrame() && !pLay->HasFixSize() )
546 bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
547 else
548 bLow = true;
549 bInvaPercent = bLow;
550 if ( bLow )
552 pLay->ChgLowersProp( maPrt.SSize() );
554 // If the PrtArea has been extended, it might be possible that the chain of parts
555 // can take another frame. As a result, the "possible right one" needs to be
556 // invalidated. This only pays off if this or its Uppers are moveable sections.
557 // A PrtArea has been extended if width or height are larger than before.
558 if ( (pLay->getFramePrintArea().Height() > maPrt.Height() ||
559 pLay->getFramePrintArea().Width() > maPrt.Width()) &&
560 (pLay->IsMoveable() || pLay->IsFlyFrame()) )
562 SwFrame *pTmpFrame = pLay->Lower();
563 if ( pTmpFrame && pTmpFrame->IsFlowFrame() )
565 while ( pTmpFrame->GetNext() )
566 pTmpFrame = pTmpFrame->GetNext();
567 pTmpFrame->InvalidateNextPos();
571 bNotify = true;
572 //EXPENSIVE!! But how we do it more elegant?
573 if( bInvaPercent )
574 pLay->InvaPercentLowers( pLay->getFramePrintArea().Height() - maPrt.Height() );
576 if ( pLay->IsTabFrame() )
577 //So that _only_ the shadow is drawn while resizing.
578 static_cast<SwTabFrame*>(pLay)->SetComplete();
579 else
581 const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
582 if( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) ||
583 !(pLay->GetType() & (SwFrameType::Body | SwFrameType::Page)) )
584 //Thereby the subordinates are retouched clean.
585 //Example problem: Take the Flys with the handles and downsize.
586 //Not for body and page, otherwise it flickers when loading HTML.
587 pLay->SetCompletePaint();
590 //Notify Lower if the position has changed.
591 const bool bPrtPos = aRectFnSet.PosDiff( maPrt, pLay->getFramePrintArea() );
592 const bool bPos = bPrtPos || aRectFnSet.PosDiff( maFrame, pLay->getFrameArea() );
593 const bool bSize = pLay->getFrameArea().SSize() != maFrame.SSize();
595 if ( bPos && pLay->Lower() && !IsLowersComplete() )
597 pLay->Lower()->InvalidatePos();
598 SwFootnoteFrame* pFtnFrame = pLay->Lower()->IsFootnoteFrame() ?
599 static_cast<SwFootnoteFrame*>(pLay->Lower()) : nullptr;
600 SwFrame* pFtnLower = pFtnFrame ? pFtnFrame->Lower() : nullptr;
601 if (pFtnLower)
602 pFtnLower->InvalidatePos();
605 if ( bPrtPos )
606 pLay->SetCompletePaint();
608 //Inform the Follower if the SSize has changed.
609 if ( bSize )
611 if( pLay->GetNext() )
613 if ( pLay->GetNext()->IsLayoutFrame() )
614 pLay->GetNext()->InvalidatePos_();
615 else
616 pLay->GetNext()->InvalidatePos();
618 else if( pLay->IsSctFrame() )
619 pLay->InvalidateNextPos();
622 SwFrame* pLower = pLay->Lower();
623 if ( !IsLowersComplete() &&
624 !(pLay->GetType()&(SwFrameType::Fly|SwFrameType::Section) &&
625 pLower && pLower->IsColumnFrame()) &&
626 (bPos || bNotify) &&
627 !(pLay->GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
629 // #i44016# - force unlock of position of lower objects.
630 // #i43913# - no unlock of position of objects,
631 // if <pLay> is a cell frame, and its table frame resp. its parent table
632 // frame is locked.
633 // #i47458# - force unlock of position of lower objects,
634 // only if position of layout frame has changed.
635 bool bUnlockPosOfObjs( bPos );
636 if ( bUnlockPosOfObjs && pLay->IsCellFrame() )
638 SwTabFrame* pTabFrame( pLay->FindTabFrame() );
639 if ( pTabFrame &&
640 ( pTabFrame->IsJoinLocked() ||
641 ( pTabFrame->IsFollow() &&
642 pTabFrame->FindMaster()->IsJoinLocked() ) ) )
644 bUnlockPosOfObjs = false;
647 // #i49383# - check for footnote frame, if unlock
648 // of position of lower objects is allowed.
649 else if ( bUnlockPosOfObjs && pLay->IsFootnoteFrame() )
651 bUnlockPosOfObjs = static_cast<SwFootnoteFrame*>(pLay)->IsUnlockPosOfLowerObjs();
653 // #i51303# - no unlock of object positions for sections
654 else if ( bUnlockPosOfObjs && pLay->IsSctFrame() )
656 bUnlockPosOfObjs = false;
658 pLay->NotifyLowerObjs( bUnlockPosOfObjs );
660 if ( bPos && pLay->IsFootnoteFrame() && pLay->Lower() )
662 // OD 2004-05-11 #i28701#
663 ::lcl_InvalidatePosOfLowers( *pLay );
665 if( ( bPos || bSize ) && pLay->IsFlyFrame() && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()
666 && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()->IsFlyFrame() )
667 static_cast<SwFlyFrame*>(pLay)->AnchorFrame()->InvalidateSize();
670 SwLayNotify::~SwLayNotify()
672 suppress_fun_call_w_exception(ImplDestroy());
675 SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) :
676 SwLayNotify( pFlyFrame ),
677 // #115759# - keep correct page frame - the page frame
678 // the Writer fly frame is currently registered at.
679 m_pOldPage( pFlyFrame->GetPageFrame() ),
680 m_aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() )
684 void SwFlyNotify::ImplDestroy()
686 SwFlyFrame *pFly = static_cast<SwFlyFrame*>(mpFrame);
687 if ( pFly->IsNotifyBack() )
689 SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
690 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
691 if ( !pImp || !pImp->IsAction() || !pImp->GetLayAction().IsAgain() )
693 //If in the LayAction the IsAgain is set it can be
694 //that the old page is destroyed in the meantime!
695 ::Notify( pFly, m_pOldPage, m_aFrameAndSpace, &maPrt );
696 // #i35640# - additional notify anchor text frame,
697 // if Writer fly frame has changed its page
698 if ( pFly->GetAnchorFrame()->IsTextFrame() &&
699 pFly->GetPageFrame() != m_pOldPage )
701 pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
704 pFly->ResetNotifyBack();
706 if (pFly->IsForceNotifyNewBackground())
708 pFly->NotifyBackground(pFly->FindPageFrame(), pFly->GetObjRectWithSpaces(), PrepareHint::FlyFrameArrive);
709 pFly->SetForceNotifyNewBackground(false);
712 //Have the size or the position changed,
713 //so should the view know this.
714 SwRectFnSet aRectFnSet(pFly);
715 const bool bPosChgd = aRectFnSet.PosDiff( maFrame, pFly->getFrameArea() );
716 const bool bFrameChgd = pFly->getFrameArea().SSize() != maFrame.SSize();
717 const bool bPrtChgd = maPrt != pFly->getFramePrintArea();
718 if ( bPosChgd || bFrameChgd || bPrtChgd )
720 pFly->NotifyDrawObj();
722 if ( bPosChgd && maFrame.Pos().X() != FAR_AWAY )
724 // OD 2004-05-10 #i28701# - no direct move of lower Writer fly frames.
725 // reason: New positioning and alignment (e.g. to-paragraph anchored,
726 // but aligned at page) are introduced.
727 // <SwLayNotify::~SwLayNotify()> takes care of invalidation of lower
728 // floating screen objects by calling method <SwLayoutFrame::NotifyLowerObjs()>.
730 if ( pFly->IsFlyAtContentFrame() )
732 SwFrame *pNxt = pFly->AnchorFrame()->FindNext();
733 while (pNxt)
735 pNxt->InvalidatePos();
736 if (!pNxt->IsSctFrame())
738 break;
740 // invalidating pos of a section frame doesn't have much
741 // effect, so try again with its lower
742 pNxt = static_cast<SwSectionFrame*>(pNxt)->Lower();
746 // #i26945# - notify anchor.
747 // Needed for negative positioned Writer fly frames
748 if ( pFly->GetAnchorFrame()->IsTextFrame() )
750 pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
754 // OD 2004-05-13 #i28701#
755 // #i45180# - no adjustment of layout process flags and
756 // further notifications/invalidations, if format is called by grow/shrink
757 if ( !pFly->ConsiderObjWrapInfluenceOnObjPos() )
758 return;
759 if (pFly->IsFlyFreeFrame())
761 if (static_cast<SwFlyFreeFrame*>(pFly)->IsNoMoveOnCheckClip())
762 return;
765 // #i54138# - suppress restart of the layout process
766 // on changed frame height.
767 // Note: It doesn't seem to be necessary and can cause layout loops.
768 if ( bPosChgd )
770 // indicate a restart of the layout process
771 pFly->SetRestartLayoutProcess( true );
773 else
775 // lock position
776 pFly->LockPosition();
779 if ( pFly->ConsiderForTextWrap() )
780 return;
782 // indicate that object has to be considered for text wrap
783 pFly->SetConsiderForTextWrap( true );
784 // invalidate 'background' in order to allow its 'background'
785 // to wrap around it.
786 pFly->NotifyBackground( pFly->GetPageFrame(),
787 pFly->GetObjRectWithSpaces(),
788 PrepareHint::FlyFrameArrive );
789 // invalidate position of anchor frame in order to force
790 // a re-format of the anchor frame, which also causes a
791 // re-format of the invalid previous frames of the anchor frame.
792 pFly->AnchorFrame()->InvalidatePos();
795 SwFlyNotify::~SwFlyNotify()
797 suppress_fun_call_w_exception(ImplDestroy());
800 SwContentNotify::SwContentNotify( SwContentFrame *pContentFrame ) :
801 SwFrameNotify( pContentFrame ),
802 // OD 08.01.2004 #i11859#
803 mbChkHeightOfLastLine( false ),
804 mnHeightOfLastLine( 0 ),
805 // OD 2004-02-26 #i25029#
806 mbInvalidatePrevPrtArea( false ),
807 mbBordersJoinedWithPrev( false )
809 // OD 08.01.2004 #i11859#
810 if ( !pContentFrame->IsTextFrame() )
811 return;
813 SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pContentFrame);
814 if (!pTextFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING))
816 const SvxLineSpacingItem &rSpace = pTextFrame->GetAttrSet()->GetLineSpacing();
817 if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
819 mbChkHeightOfLastLine = true;
820 mnHeightOfLastLine = pTextFrame->GetHeightOfLastLine();
825 void SwContentNotify::ImplDestroy()
827 SwContentFrame *pCnt = static_cast<SwContentFrame*>(mpFrame);
828 if ( bSetCompletePaintOnInvalidate )
829 pCnt->SetCompletePaint();
831 SwRectFnSet aRectFnSet(pCnt);
832 if ( pCnt->IsInTab() && ( aRectFnSet.PosDiff( pCnt->getFrameArea(), maFrame ) ||
833 pCnt->getFrameArea().SSize() != maFrame.SSize()))
835 SwLayoutFrame* pCell = pCnt->GetUpper();
836 while( !pCell->IsCellFrame() && pCell->GetUpper() )
837 pCell = pCell->GetUpper();
838 OSL_ENSURE( pCell->IsCellFrame(), "Where's my cell?" );
839 if ( text::VertOrientation::NONE != pCell->GetFormat()->GetVertOrient().GetVertOrient() )
840 pCell->InvalidatePrt(); //for the vertical align.
843 // OD 2004-02-26 #i25029#
844 if ( mbInvalidatePrevPrtArea && mbBordersJoinedWithPrev &&
845 pCnt->IsTextFrame() &&
846 !pCnt->IsFollow() && !pCnt->GetIndPrev() )
848 // determine previous frame
849 SwFrame* pPrevFrame = pCnt->FindPrev();
850 // skip empty section frames and hidden text frames
852 while (pPrevFrame && pPrevFrame->IsHiddenNow())
854 pPrevFrame = pPrevFrame->FindPrev();
858 // Invalidate printing area of found previous frame
859 if ( pPrevFrame )
861 if ( pPrevFrame->IsSctFrame() )
863 if ( pCnt->IsInSct() )
865 // Note: found previous frame is a section frame and
866 // <pCnt> is also inside a section.
867 // Thus due to <mbBordersJoinedWithPrev>,
868 // <pCnt> had joined its borders/shadow with the
869 // last content of the found section.
870 // Invalidate printing area of last content in found section.
871 SwFrame* pLstContentOfSctFrame =
872 static_cast<SwSectionFrame*>(pPrevFrame)->FindLastContent();
873 if ( pLstContentOfSctFrame )
875 pLstContentOfSctFrame->InvalidatePrt();
879 else
881 pPrevFrame->InvalidatePrt();
886 const bool bFirst = aRectFnSet.GetWidth(maFrame) == 0;
888 if ( pCnt->IsNoTextFrame() )
890 //Active PlugIn's or OLE-Objects should know something of the change
891 //thereby they move their window appropriate.
892 SwViewShell *pSh = pCnt->getRootFrame()->GetCurrShell();
893 if ( pSh )
895 SwOLENode *const pNd(static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetOLENode());
896 if (nullptr != pNd &&
897 (pNd->GetOLEObj().IsOleRef() ||
898 pNd->IsOLESizeInvalid()) )
900 const bool bNoTextFramePrtAreaChanged =
901 ( maPrt.SSize().Width() != 0 &&
902 maPrt.SSize().Height() != 0 ) &&
903 maPrt.SSize() != pCnt->getFramePrintArea().SSize();
904 OSL_ENSURE( pCnt->IsInFly(), "OLE not in FlyFrame" );
905 SwFlyFrame *pFly = pCnt->FindFlyFrame();
906 svt::EmbeddedObjectRef& xObj = pNd->GetOLEObj().GetObject();
907 SwFEShell *pFESh = nullptr;
908 for(SwViewShell& rCurrentShell : pSh->GetRingContainer())
909 { if ( dynamic_cast<const SwCursorShell*>( &rCurrentShell) != nullptr )
911 pFESh = static_cast<SwFEShell*>(&rCurrentShell);
912 // #108369#: Here used to be the condition if (!bFirst).
913 // I think this should mean "do not call CalcAndSetScale"
914 // if the frame is formatted for the first time.
915 // Unfortunately this is not valid anymore since the
916 // SwNoTextFrame already gets a width during CalcLowerPreps.
917 // Nevertheless, the indention of !bFirst seemed to be
918 // to assure that the OLE objects have already been notified
919 // if necessary before calling CalcAndSetScale.
920 // So I replaced !bFirst by !IsOLESizeInvalid. There is
921 // one additional problem specific to the word import:
922 // The layout is calculated _before_ calling PrtOLENotify,
923 // and the OLE objects are not invalidated during import.
924 // Therefore I added the condition !IsUpdateExpField,
925 // have a look at the occurrence of CalcLayout in
926 // uiview/view.cxx.
927 if ( !pNd->IsOLESizeInvalid() &&
928 !pSh->GetDoc()->getIDocumentState().IsUpdateExpField() )
929 pFESh->CalcAndSetScale( xObj,
930 &pFly->getFramePrintArea(), &pFly->getFrameArea(),
931 bNoTextFramePrtAreaChanged );
935 if ( pFESh && pNd->IsOLESizeInvalid() )
937 pNd->SetOLESizeInvalid( false );
938 pFESh->CalcAndSetScale( xObj ); // create client
941 // ditto animated graphics
942 if ( getFrameArea().HasArea() && static_cast<SwNoTextFrame*>(pCnt)->HasAnimation() )
944 static_cast<SwNoTextFrame*>(pCnt)->StopAnimation();
945 pSh->InvalidateWindows( getFrameArea() );
950 if ( bFirst )
952 pCnt->SetRetouche(); //fix(13870)
954 SwDoc& rDoc = pCnt->IsTextFrame()
955 ? static_cast<SwTextFrame*>(pCnt)->GetDoc()
956 : static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetDoc();
957 if ( !rDoc.GetSpzFrameFormats()->empty() &&
958 rDoc.DoesContainAtPageObjWithContentAnchor() && !rDoc.getIDocumentState().IsNewDoc() )
960 // If certain import filters for foreign file format import
961 // AT_PAGE anchored objects, the corresponding page number is
962 // typically not known. In this case the content position is
963 // stored at which the anchored object is found in the
964 // imported document.
965 // When this content is formatted it is the time at which
966 // the page is known. Thus, this data can be corrected now.
968 const SwPageFrame *pPage = nullptr;
969 for(sw::SpzFrameFormat* pFormat: *rDoc.GetSpzFrameFormats())
971 const SwFormatAnchor &rAnch = pFormat->GetAnchor();
972 if ( RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId() ||
973 rAnch.GetAnchorNode() == nullptr )
975 continue;
978 if (FrameContainsNode(*pCnt, rAnch.GetAnchorNode()->GetIndex()))
980 OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - to page anchored object with content position." );
981 if ( !pPage )
983 pPage = pCnt->FindPageFrame();
985 SwFormatAnchor aAnch( rAnch );
986 aAnch.SetAnchor( nullptr );
987 aAnch.SetPageNum( pPage->GetPhyPageNum() );
988 pFormat->SetFormatAttr( aAnch );
989 if ( RES_DRAWFRMFMT != pFormat->Which() )
991 pFormat->MakeFrames();
998 // OD 12.01.2004 #i11859# - invalidate printing area of following frame,
999 // if height of last line has changed.
1000 if ( pCnt->IsTextFrame() && mbChkHeightOfLastLine )
1002 if ( mnHeightOfLastLine != static_cast<SwTextFrame*>(pCnt)->GetHeightOfLastLine() )
1004 pCnt->InvalidateNextPrtArea();
1008 // #i44049#
1009 if ( pCnt->IsTextFrame() && aRectFnSet.PosDiff( maFrame, pCnt->getFrameArea() ) )
1011 pCnt->InvalidateObjs();
1014 // #i43255# - move code to invalidate at-character
1015 // anchored objects due to a change of its anchor character from
1016 // method <SwTextFrame::Format(..)>.
1017 if ( !pCnt->IsTextFrame() )
1018 return;
1020 SwTextFrame* pMasterFrame = pCnt->IsFollow()
1021 ? static_cast<SwTextFrame*>(pCnt)->FindMaster()
1022 : static_cast<SwTextFrame*>(pCnt);
1023 if ( pMasterFrame && !pMasterFrame->IsFlyLock() &&
1024 pMasterFrame->GetDrawObjs() )
1026 SwSortedObjs* pObjs = pMasterFrame->GetDrawObjs();
1027 for (SwAnchoredObject* pAnchoredObj : *pObjs)
1029 if ( pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
1030 == RndStdIds::FLY_AT_CHAR )
1032 pAnchoredObj->CheckCharRectAndTopOfLine( !pMasterFrame->IsEmpty() );
1038 SwContentNotify::~SwContentNotify()
1040 suppress_fun_call_w_exception(ImplDestroy());
1043 // note this *cannot* be static because it's a friend
1044 void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *const pFormat, const SwFormatAnchor & rAnch)
1046 const bool bFlyAtFly = rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY; // LAYER_IMPL
1047 //Is a frame or a SdrObject described?
1048 const bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
1049 // OD 23.06.2003 #108784# - append also drawing objects anchored
1050 // as character.
1051 const bool bDrawObjInContent = bSdrObj &&
1052 (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
1054 if( !(bFlyAtFly ||
1055 (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
1056 (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
1057 bDrawObjInContent) )
1058 return;
1060 SdrObject* pSdrObj = nullptr;
1061 if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
1063 OSL_ENSURE( !bSdrObj, "DrawObject not found." );
1064 pFormat->GetDoc()->DelFrameFormat( pFormat );
1065 return;
1067 if ( pSdrObj )
1069 if ( !pSdrObj->getSdrPageFromSdrObject() )
1071 pFormat->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)->
1072 InsertObject(pSdrObj, pSdrObj->GetOrdNumDirect());
1075 if (SwDrawContact* pNew = static_cast<SwDrawContact*>(GetUserCall( pSdrObj )))
1077 if ( !pNew->GetAnchorFrame() )
1079 pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( nullptr )) );
1081 // OD 19.06.2003 #108784# - add 'virtual' drawing object,
1082 // if necessary. But control objects have to be excluded.
1083 else if ( !::CheckControlLayer( pSdrObj ) &&
1084 pNew->GetAnchorFrame() != pFrame &&
1085 !pNew->GetDrawObjectByAnchorFrame( *pFrame ) )
1087 SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame);
1088 pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) );
1090 pDrawVirtObj->ActionChanged();
1094 else
1096 SwFlyFrame *pFly;
1097 if( bFlyAtFly )
1098 pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
1099 else
1100 pFly = new SwFlyAtContentFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
1101 pFly->Lock();
1102 pFrame->AppendFly( pFly );
1103 pFly->Unlock();
1104 if ( pPage )
1105 ::RegistFlys( pPage, pFly );
1109 static bool IsShown(SwNodeOffset const nIndex,
1110 const SwFormatAnchor & rAnch,
1111 std::vector<sw::Extent>::const_iterator const*const pIter,
1112 std::vector<sw::Extent>::const_iterator const*const pEnd,
1113 SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
1115 assert(!pIter || *pIter == *pEnd || (*pIter)->pNode->GetIndex() == nIndex);
1116 SwNode* pAnchorNode = rAnch.GetAnchorNode();
1117 if (pAnchorNode->GetIndex() != nIndex)
1119 return false;
1121 if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
1123 return pIter == nullptr // not merged
1124 || pIter != pEnd // at least one char visible in node
1125 || !IsSelectFrameAnchoredAtPara(*rAnch.GetContentAnchor(),
1126 SwPosition(*pFirstNode, 0),
1127 SwPosition(*pLastNode, pLastNode->Len()));
1129 if (pIter)
1131 // note: frames are not sorted by anchor position.
1132 assert(pEnd);
1133 assert(pFirstNode);
1134 assert(pLastNode);
1135 assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY);
1136 if (*pIter == *pEnd && rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
1137 { // tdf#149595 special case - it *could* be shown if first == last
1138 return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(),
1139 SwPosition(*pFirstNode, 0),
1140 SwPosition(*pLastNode, pLastNode->Len()));
1142 for (auto iter = *pIter; iter != *pEnd; ++iter)
1144 assert(iter->nStart != iter->nEnd); // TODO possible?
1145 assert(iter->pNode->GetIndex() == nIndex);
1146 if (rAnch.GetAnchorContentOffset() < iter->nStart)
1148 return false;
1150 if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
1152 // if there is an extent then obviously the node was not
1153 // deleted fully...
1154 // show if start <= pos <= end
1155 // *or* if first-node/0 *and* not StartOfSection
1156 // *or* if last-node/Len *and* not EndOfSection
1158 // first determine the extent to compare to, then
1159 // construct start/end positions for the deletion *before* the
1160 // extent and compare once.
1161 // the interesting corner cases are on the edge of the extent!
1162 // no need to check for > the last extent because those
1163 // are never visible.
1164 if (rAnch.GetAnchorContentOffset() <= iter->nEnd)
1166 if (iter->nStart == 0)
1168 return true;
1170 else
1172 SwPosition const start(
1173 iter == *pIter
1174 ? *pFirstNode // simplification
1175 : *iter->pNode,
1176 iter == *pIter // first extent?
1177 ? iter->pNode == pFirstNode
1178 ? 0 // at start of 1st node
1179 : pFirstNode->Len() // previous node; simplification but should get right result
1180 : (iter-1)->nEnd); // previous extent
1181 SwPosition const end(*iter->pNode, iter->nStart);
1182 return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(), start, end);
1185 else if (iter == *pEnd - 1) // special case: after last extent
1187 if (iter->nEnd == iter->pNode->Len())
1189 return true; // special case: end of node
1191 else
1193 SwPosition const start(*iter->pNode, iter->nEnd);
1194 SwPosition const end(
1195 *pLastNode, // simplification
1196 iter->pNode == pLastNode
1197 ? iter->pNode->Len()
1198 : 0);
1199 return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(), start, end);
1203 else
1205 assert(rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
1206 // for AS_CHAR obviously must be <
1207 if (rAnch.GetAnchorContentOffset() < iter->nEnd)
1209 return true;
1213 return false;
1215 else
1217 return true;
1221 void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
1222 std::vector<sw::Extent>::const_iterator const*const pIter,
1223 std::vector<sw::Extent>::const_iterator const*const pEnd,
1224 SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
1226 std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
1227 for (SwFrameFormat * pFrameFormat : rFlys)
1229 SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor();
1230 if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
1231 || rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
1233 assert(rAnchor.GetAnchorNode()->GetIndex() == rNode.GetIndex());
1234 if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode))
1236 pFrameFormat->DelFrames();
1242 void AppendObjsOfNode(sw::FrameFormats<sw::SpzFrameFormat*> const*const pTable, SwNodeOffset const nIndex,
1243 SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc,
1244 std::vector<sw::Extent>::const_iterator const*const pIter,
1245 std::vector<sw::Extent>::const_iterator const*const pEnd,
1246 SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
1248 #if OSL_DEBUG_LEVEL > 0
1249 std::vector<SwFrameFormat*> checkFormats;
1250 for(auto pFormat: *pTable)
1252 const SwFormatAnchor &rAnch = pFormat->GetAnchor();
1253 if ( rAnch.GetAnchorNode() &&
1254 IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
1256 checkFormats.push_back( pFormat );
1259 #else
1260 (void)pTable;
1261 #endif
1263 SwNode const& rNode(*pDoc->GetNodes()[nIndex]);
1264 std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
1265 for (size_t it = 0; it != rFlys.size(); )
1267 SwFrameFormat *const pFormat = rFlys[it];
1268 const SwFormatAnchor &rAnch = pFormat->GetAnchor();
1269 if ( rAnch.GetAnchorNode() &&
1270 IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
1272 #if OSL_DEBUG_LEVEL > 0
1273 std::vector<SwFrameFormat*>::iterator checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat );
1274 assert( checkPos != checkFormats.end());
1275 checkFormats.erase( checkPos );
1276 #endif
1277 AppendObj(pFrame, pPage, pFormat, rAnch);
1279 ++it;
1282 #if OSL_DEBUG_LEVEL > 0
1283 assert( checkFormats.empty());
1284 #endif
1288 void AppendObjs(const sw::FrameFormats<sw::SpzFrameFormat*> *const pTable, SwNodeOffset const nIndex,
1289 SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc)
1291 if (pFrame->IsTextFrame())
1293 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
1294 if (sw::MergedPara const*const pMerged = pTextFrame->GetMergedPara())
1296 std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
1297 std::vector<sw::Extent>::const_iterator iter(iterFirst);
1298 SwTextNode const* pNode(pMerged->pFirstNode);
1299 for ( ; ; ++iter)
1301 if (iter == pMerged->extents.end()
1302 || iter->pNode != pNode)
1304 AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, pDoc,
1305 &iterFirst, &iter, pMerged->pFirstNode, pMerged->pLastNode);
1306 SwNodeOffset const until = iter == pMerged->extents.end()
1307 ? pMerged->pLastNode->GetIndex() + 1
1308 : iter->pNode->GetIndex();
1309 for (SwNodeOffset i = pNode->GetIndex() + 1; i < until; ++i)
1311 // let's show at-para flys on nodes that contain start/end of
1312 // redline too, even if there's no text there
1313 SwNode const*const pTmp(pNode->GetNodes()[i]);
1314 if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
1316 AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, pDoc, &iter, &iter, pMerged->pFirstNode, pMerged->pLastNode);
1319 if (iter == pMerged->extents.end())
1321 break;
1323 pNode = iter->pNode;
1324 iterFirst = iter;
1328 else
1330 return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
1333 else
1335 return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
1339 bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor)
1341 assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA ||
1342 rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR ||
1343 rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
1344 bool ret(true);
1345 if (auto const pMergedPara = rFrame.GetMergedPara())
1347 ret = false;
1348 SwNode* pAnchorNode(rAnchor.GetAnchorNode());
1349 auto iterFirst(pMergedPara->extents.cbegin());
1350 if (iterFirst == pMergedPara->extents.end()
1351 && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
1352 || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR))
1354 ret = (pAnchorNode == pMergedPara->pFirstNode
1355 && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
1356 || rAnchor.GetAnchorContentOffset() == 0))
1357 || (pAnchorNode == pMergedPara->pLastNode
1358 && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
1359 || rAnchor.GetAnchorContentOffset() == pMergedPara->pLastNode->Len()));
1361 auto iter(iterFirst);
1362 SwTextNode const* pNode(pMergedPara->pFirstNode);
1363 for ( ; ; ++iter)
1365 if (iter == pMergedPara->extents.end()
1366 || iter->pNode != pNode)
1368 assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
1369 if (pNode == pAnchorNode)
1371 ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter,
1372 pMergedPara->pFirstNode, pMergedPara->pLastNode);
1373 break;
1375 if (iter == pMergedPara->extents.end())
1377 break;
1379 pNode = iter->pNode;
1380 if (pAnchorNode->GetIndex() < pNode->GetIndex())
1382 break;
1384 iterFirst = iter;
1388 return ret;
1391 void AppendAllObjs(const sw::FrameFormats<sw::SpzFrameFormat*>* pTable, const SwFrame* pSib)
1393 //Connecting of all Objects, which are described in the SpzTable with the
1394 //layout.
1396 boost::circular_buffer<SwFrameFormat*> vFormatsToConnect(pTable->size());
1397 for(const auto& pFormat : *pTable)
1399 const auto& rAnch = pFormat->GetAnchor();
1400 // Formats can still remain, because we neither use character bound
1401 // frames nor objects which are anchored to character bounds.
1402 if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && (rAnch.GetAnchorId() != RndStdIds::FLY_AS_CHAR))
1404 const SwNode* pAnchorNode = rAnch.GetAnchorNode();
1405 // formats in header/footer have no dependencies
1406 if(pAnchorNode && pFormat->GetDoc()->IsInHeaderFooter(*pAnchorNode))
1407 pFormat->MakeFrames();
1408 else
1409 vFormatsToConnect.push_back(pFormat);
1412 const SwRootFrame* pRoot = pSib ? pSib->getRootFrame() : nullptr;
1413 const SwFrameFormat* pFirstRequeued(nullptr);
1414 while(!vFormatsToConnect.empty())
1416 SwFrameFormat* pFormat = vFormatsToConnect.front();
1417 bool isConnected(false);
1418 pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
1419 if(!isConnected)
1421 pFormat->MakeFrames();
1422 pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
1424 // do this *before* push_back! the circular_buffer can be "full"!
1425 vFormatsToConnect.pop_front();
1426 if (!isConnected)
1428 if(pFirstRequeued == pFormat)
1429 // If nothing happens anymore we can stop.
1430 break;
1431 if(!pFirstRequeued)
1432 pFirstRequeued = pFormat;
1433 assert(!vFormatsToConnect.full());
1434 vFormatsToConnect.push_back(pFormat);
1436 else
1438 pFirstRequeued = nullptr;
1443 namespace sw {
1445 void RecreateStartTextFrames(SwTextNode & rNode)
1447 std::vector<SwTextFrame*> frames;
1448 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
1449 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
1451 if (pFrame->getRootFrame()->HasMergedParas())
1453 frames.push_back(pFrame);
1456 auto eMode(sw::FrameMode::Existing);
1457 for (SwTextFrame * pFrame : frames)
1459 // SplitNode could have moved the original frame to the start node
1460 // & created a new one on end, or could have created new frame on
1461 // start node... grab start node's frame and recreate MergedPara.
1462 SwTextNode & rFirstNode(pFrame->GetMergedPara()
1463 ? *pFrame->GetMergedPara()->pFirstNode
1464 : rNode);
1465 assert(rFirstNode.GetIndex() <= rNode.GetIndex());
1466 // clear old one first to avoid DelFrames confusing updates & asserts...
1467 pFrame->SetMergedPara(nullptr);
1468 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
1469 *pFrame, rFirstNode, eMode));
1470 eMode = sw::FrameMode::New; // Existing is not idempotent!
1471 // note: this may or may not delete frames on the end node
1475 } // namespace sw
1477 /** local method to set 'working' position for newly inserted frames
1479 OD 12.08.2003 #i17969#
1481 static void lcl_SetPos( SwFrame& _rNewFrame,
1482 const SwLayoutFrame& _rLayFrame )
1484 SwRectFnSet aRectFnSet(&_rLayFrame);
1485 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(_rNewFrame);
1486 aRectFnSet.SetPos( aFrm, aRectFnSet.GetPos(_rLayFrame.getFrameArea()) );
1488 // move position by one SwTwip in text flow direction in order to get
1489 // notifications for a new calculated position after its formatting.
1490 if ( aRectFnSet.IsVert() )
1492 aFrm.Pos().AdjustX( -1 );
1494 else
1496 aFrm.Pos().AdjustY(1 );
1500 void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc,
1501 SwNodeOffset nIndex, bool bPages, SwNodeOffset nEndIndex,
1502 SwFrame *pPrv, sw::FrameMode const eMode )
1504 pDoc->getIDocumentTimerAccess().BlockIdling();
1505 SwRootFrame* pLayout = pLay->getRootFrame();
1506 const bool bOldCallbackActionEnabled = pLayout && pLayout->IsCallbackActionEnabled();
1507 if( bOldCallbackActionEnabled )
1508 pLayout->SetCallbackActionEnabled( false );
1510 //In the generation of the Layout bPages=true will be handed over.
1511 //Then will be new pages generated all x paragraphs already times in advance.
1512 //On breaks and/or pagedescriptorchanges the corresponding will be generated
1513 //immediately.
1514 //The advantage is, that on one hand already a nearly realistic number of
1515 //pages are created, but above all there are no almost endless long chain
1516 //of paragraphs, which must be moved expensively until it reaches a tolerable
1517 //reduced level.
1518 //We'd like to think that 20 Paragraphs fit on one page.
1519 //So that it does not become in extreme situations so violent we calculate depending
1520 //on the node something to it.
1521 //If in the DocStatistic a usable given pagenumber
1522 //(Will be cared for while writing), so it will be presumed that this will be
1523 //number of pages.
1524 const bool bStartPercent = bPages && !nEndIndex;
1526 SwPageFrame *pPage = pLay->FindPageFrame();
1527 sw::SpzFrameFormats* pTable = pDoc->GetSpzFrameFormats();
1528 SwFrame *pFrame = nullptr;
1529 std::unique_ptr<SwActualSection> pActualSection;
1530 std::unique_ptr<SwLayHelper> pPageMaker;
1532 //If the layout will be created (bPages == true) we do head on the progress
1533 //Flys and DrawObjects are not connected immediately, this
1534 //happens only at the end of the function.
1535 if ( bPages )
1537 // Attention: the SwLayHelper class uses references to the content-,
1538 // page-, layout-frame etc. and may change them!
1539 pPageMaker.reset(new SwLayHelper( pDoc, pFrame, pPrv, pPage, pLay,
1540 pActualSection, nIndex, SwNodeOffset(0) == nEndIndex ));
1541 if( bStartPercent )
1543 const sal_uLong nPageCount = pPageMaker->CalcPageCount();
1544 if( nPageCount )
1545 bObjsDirect = false;
1549 if( pLay->IsInSct() &&
1550 ( pLay->IsSctFrame() || pLay->GetUpper() ) ) // Hereby will newbies
1551 // be intercepted, of which flags could not determined yet,
1552 // for e.g. while inserting a table
1554 SwSectionFrame* pSct = pLay->FindSctFrame();
1555 // If content will be inserted in a footnote, which in a column area,
1556 // the column area it is not allowed to be broken up.
1557 // Only if in the inner of the footnote lies an area, is this a candidate
1558 // for pActualSection.
1559 // The same applies for areas in tables, if inside the table will be
1560 // something inserted, it's only allowed to break up areas, which
1561 // lies in the inside also.
1562 if( ( !pLay->IsInFootnote() || pSct->IsInFootnote() ) &&
1563 ( !pLay->IsInTab() || pSct->IsInTab() ) )
1565 pActualSection.reset(new SwActualSection(nullptr, pSct, pSct->GetSection()->GetFormat()->GetSectionNode()));
1566 // tdf#132236 for SwUndoDelete: find outer sections whose start
1567 // nodes aren't contained in the range but whose end nodes are,
1568 // because section frames may need to be created for them
1569 SwActualSection * pUpperSection(pActualSection.get());
1570 while (pUpperSection->GetSectionNode()->EndOfSectionIndex() < nEndIndex)
1572 SwStartNode *const pStart(pUpperSection->GetSectionNode()->StartOfSectionNode());
1573 if (!pStart->IsSectionNode())
1575 break;
1577 // note: these don't have a section frame, check it in EndNode case!
1578 auto const pTmp(new SwActualSection(nullptr, nullptr, static_cast<SwSectionNode*>(pStart)));
1579 pUpperSection->SetUpper(pTmp);
1580 pUpperSection = pTmp;
1582 OSL_ENSURE( !pLay->Lower() || !pLay->Lower()->IsColumnFrame(),
1583 "InsertCnt_: Wrong Call" );
1587 //If a section is "open", the pActualSection points to an SwActualSection.
1588 //If the page breaks, for "open" sections a follow will created.
1589 //For nested sections (which have, however, not a nested layout),
1590 //the SwActualSection class has a member, which points to an upper(section).
1591 //When the "inner" section finishes, the upper will used instead.
1593 std::vector<SwSectionFrame *> newHiddenSections;
1595 // Do not consider the end node. The caller (Section/MakeFrames()) has to
1596 // ensure that the end of this range is positioned before EndIndex!
1597 for ( ; nEndIndex == SwNodeOffset(0) || nIndex < nEndIndex; ++nIndex)
1599 assert(pLayout);
1600 SwNode *pNd = pDoc->GetNodes()[nIndex];
1601 if ( pNd->IsContentNode() )
1603 SwContentNode* pNode = static_cast<SwContentNode*>(pNd);
1604 if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
1606 if (pNd->IsTextNode()
1607 && pNd->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
1608 { // must have a frame already
1609 assert(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pLayout))->GetMergedPara());
1611 continue; // skip it
1613 pFrame = pNode->IsTextNode()
1614 ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode)
1615 : pNode->MakeFrame(pLay);
1616 if (pPageMaker && !pLay->IsHiddenNow())
1617 pPageMaker->CheckInsert( nIndex );
1619 pFrame->InsertBehind( pLay, pPrv );
1620 if (!pPrv)
1622 if (SwSectionFrame *const pSection = pLay->FindSctFrame())
1624 if (pSection->ContainsAny() == pFrame)
1625 { // tdf#146258 section PrtArea depends on paragraph upper margin
1626 pSection->InvalidatePrt();
1630 // #i27138#
1631 // notify accessibility paragraphs objects about changed
1632 // CONTENT_FLOWS_FROM/_TO relation.
1633 // Relation CONTENT_FLOWS_FROM for next paragraph will change
1634 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
1635 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1636 if ( pFrame->IsTextFrame() )
1638 SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
1639 // no notification, if <SwViewShell> is in construction
1640 if ( pViewShell && !pViewShell->IsInConstructor() &&
1641 pViewShell->GetLayout() &&
1642 pViewShell->GetLayout()->IsAnyShellAccessible() &&
1643 pFrame->FindPageFrame() != nullptr)
1645 auto pNext = pFrame->FindNextCnt( true );
1646 auto pPrev = pFrame->FindPrevCnt();
1647 pViewShell->InvalidateAccessibleParaFlowRelation(
1648 pNext ? pNext->DynCastTextFrame() : nullptr,
1649 pPrev ? pPrev->DynCastTextFrame() : nullptr );
1650 // #i68958#
1651 // The information flags of the text frame are validated
1652 // in methods <FindNextCnt(..)> and <FindPrevCnt(..)>.
1653 // The information flags have to be invalidated, because
1654 // it is possible, that the one of its upper frames
1655 // isn't inserted into the layout.
1656 pFrame->InvalidateInfFlags();
1659 #endif
1660 // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
1661 // for setting position at newly inserted frame
1662 lcl_SetPos( *pFrame, *pLay );
1663 pPrv = pFrame;
1665 if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
1666 AppendObjs( pTable, nIndex, pFrame, pPage, pDoc );
1668 else if ( pNd->IsTableNode() )
1669 { //Should we have encountered a table?
1670 SwTableNode *pTableNode = static_cast<SwTableNode*>(pNd);
1671 if (pLayout->IsHideRedlines())
1673 // in the problematic case, there can be only 1 redline...
1674 SwPosition const tmp(*pNd);
1675 SwRangeRedline const*const pRedline(
1676 pDoc->getIDocumentRedlineAccess().GetRedline(tmp, nullptr));
1677 // pathology: redline that starts on a TableNode; cannot
1678 // be created in UI but by import filters...
1679 if (pRedline
1680 && pRedline->GetType() == RedlineType::Delete
1681 && &pRedline->Start()->GetNode() == pNd)
1683 SAL_WARN("sw.pageframe", "skipping table frame creation on bizarre redline");
1684 while (true)
1686 pTableNode->GetNodes()[nIndex]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
1687 if (nIndex == pTableNode->EndOfSectionIndex())
1689 break;
1691 ++nIndex;
1693 continue;
1696 if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
1698 assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
1699 nIndex = pTableNode->EndOfSectionIndex();
1700 continue; // skip it
1703 pFrame = pTableNode->MakeFrame( pLay );
1705 // skip tables deleted with track changes
1706 if ( !static_cast<SwTabFrame*>(pFrame)->Lower() )
1708 nIndex = pTableNode->EndOfSectionIndex();
1709 continue; // skip it
1712 // #108116# loading may produce table structures that GCLines
1713 // needs to clean up. To keep table formulas correct, change
1714 // all table formulas to internal (BOXPTR) representation.
1715 pTableNode->GetTable().SwitchFormulasToInternalRepresentation();
1716 pTableNode->GetTable().GCLines();
1718 if( pPageMaker )
1719 pPageMaker->CheckInsert( nIndex );
1721 pFrame->InsertBehind( pLay, pPrv );
1722 if (pPage) // would null in SwCellFrame ctor
1723 { // tdf#134931 call ResetTurbo(); not sure if Paste() would be
1724 pFrame->InvalidatePage(pPage); // better than InsertBehind()?
1726 // #i27138#
1727 // notify accessibility paragraphs objects about changed
1728 // CONTENT_FLOWS_FROM/_TO relation.
1729 // Relation CONTENT_FLOWS_FROM for next paragraph will change
1730 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
1731 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1733 SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
1734 // no notification, if <SwViewShell> is in construction
1735 if ( pViewShell && !pViewShell->IsInConstructor() &&
1736 pViewShell->GetLayout() &&
1737 pViewShell->GetLayout()->IsAnyShellAccessible() &&
1738 pFrame->FindPageFrame() != nullptr)
1740 auto pNext = pFrame->FindNextCnt( true );
1741 auto pPrev = pFrame->FindPrevCnt();
1742 pViewShell->InvalidateAccessibleParaFlowRelation(
1743 pNext ? pNext->DynCastTextFrame() : nullptr,
1744 pPrev ? pPrev->DynCastTextFrame() : nullptr );
1747 #endif
1748 if ( bObjsDirect && !pTable->empty() )
1749 static_cast<SwTabFrame*>(pFrame)->RegistFlys();
1750 // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
1751 // for setting position at newly inserted frame
1752 lcl_SetPos( *pFrame, *pLay );
1754 pPrv = pFrame;
1755 //Set the index to the endnode of the table section.
1756 nIndex = pTableNode->EndOfSectionIndex();
1758 SwTabFrame* pTmpFrame = static_cast<SwTabFrame*>(pFrame);
1759 while ( pTmpFrame )
1761 pTmpFrame->CheckDirChange();
1762 pTmpFrame = pTmpFrame->IsFollow() ? pTmpFrame->FindMaster() : nullptr;
1766 else if ( pNd->IsSectionNode() )
1768 if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
1770 assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
1771 continue; // skip it
1773 SwSectionNode *pNode = static_cast<SwSectionNode*>(pNd);
1775 if (pActualSection)
1776 pActualSection->SetLastPos(pPrv);
1778 pFrame = pNode->MakeFrame(pLay, pNode->GetSection().CalcHiddenFlag());
1779 pActualSection.reset( new SwActualSection( pActualSection.release(),
1780 static_cast<SwSectionFrame*>(pFrame), pNode ) );
1781 if ( pActualSection->GetUpper() )
1783 //Insert behind the Upper, the "Follow" of the Upper will be
1784 //generated at the EndNode.
1785 SwSectionFrame *pTmp = pActualSection->GetUpper()->GetSectionFrame();
1786 pFrame->InsertBehind( pTmp->GetUpper(), pTmp );
1787 // OD 25.03.2003 #108339# - direct initialization of section
1788 // after insertion in the layout
1789 static_cast<SwSectionFrame*>(pFrame)->Init();
1791 else
1793 pFrame->InsertBehind( pLay, pPrv );
1794 // OD 25.03.2003 #108339# - direct initialization of section
1795 // after insertion in the layout
1796 static_cast<SwSectionFrame*>(pFrame)->Init();
1798 // #i33963#
1799 // Do not trust the IsInFootnote flag. If we are currently
1800 // building up a table, the upper of pPrv may be a cell
1801 // frame, but the cell frame does not have an upper yet.
1802 if( pPrv && nullptr != pPrv->ImplFindFootnoteFrame() )
1804 if( pPrv->IsSctFrame() )
1805 pPrv = static_cast<SwSectionFrame*>(pPrv)->ContainsContent();
1806 if( pPrv && pPrv->IsTextFrame() )
1807 static_cast<SwTextFrame*>(pPrv)->Prepare( PrepareHint::QuoVadis, nullptr, false );
1811 if (nIndex + 1 == nEndIndex
1812 // tdf#136452 may also be needed at end of section
1813 || pNode->EndOfSectionIndex() - 1 == nEndIndex)
1814 { // tdf#131684 tdf#132236 fix upper of frame moved in
1815 // SwUndoDelete; can't be done there unfortunately
1816 // because empty section frames are deleted here
1817 SwFrame *const pNext(
1818 // if there's a parent section, it has been split
1819 // into 2 SwSectionFrame already :(
1820 ( pFrame->GetNext()
1821 && pFrame->GetNext()->IsSctFrame()
1822 && pActualSection->GetUpper()
1823 && pActualSection->GetUpper()->GetSectionNode() ==
1824 static_cast<SwSectionFrame const*>(pFrame->GetNext())->GetSection()->GetFormat()->GetSectionNode())
1825 ? static_cast<SwSectionFrame *>(pFrame->GetNext())->ContainsContent()
1826 : pFrame->GetNext());
1827 if (pNext
1828 && pNext->IsTextFrame()
1829 && static_cast<SwTextFrame*>(pNext)->GetTextNodeFirst() == pDoc->GetNodes()[nEndIndex]
1830 && (pNext->GetUpper() == pFrame->GetUpper()
1831 || pFrame->GetNext()->IsSctFrame())) // checked above
1833 pNext->Cut();
1834 pNext->InvalidateInfFlags(); // mbInfSct changed
1835 // could have columns
1836 SwSectionFrame *const pSection(static_cast<SwSectionFrame*>(pFrame));
1837 assert(!pSection->Lower() || pSection->Lower()->IsLayoutFrame());
1838 SwLayoutFrame *const pParent(pSection->Lower() ? pSection->GetNextLayoutLeaf() : pSection);
1839 assert(!pParent->Lower());
1840 // paste invalidates, section could have indent...
1841 pNext->Paste(pParent, nullptr);
1844 // #i27138#
1845 // notify accessibility paragraphs objects about changed
1846 // CONTENT_FLOWS_FROM/_TO relation.
1847 // Relation CONTENT_FLOWS_FROM for next paragraph will change
1848 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
1849 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1851 SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
1852 // no notification, if <SwViewShell> is in construction
1853 if ( pViewShell && !pViewShell->IsInConstructor() &&
1854 pViewShell->GetLayout() &&
1855 pViewShell->GetLayout()->IsAnyShellAccessible() &&
1856 pFrame->FindPageFrame() != nullptr)
1858 auto pNext = pFrame->FindNextCnt( true );
1859 auto pPrev = pFrame->FindPrevCnt();
1860 pViewShell->InvalidateAccessibleParaFlowRelation(
1861 pNext ? pNext->DynCastTextFrame() : nullptr,
1862 pPrev ? pPrev->DynCastTextFrame() : nullptr );
1865 #endif
1866 pFrame->CheckDirChange();
1868 // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
1869 // for setting position at newly inserted frame
1870 lcl_SetPos( *pFrame, *pLay );
1872 // OD 20.11.2002 #105405# - no page, no invalidate.
1873 if ( pPage )
1875 // OD 18.09.2002 #100522#
1876 // invalidate page in order to force format and paint of
1877 // inserted section frame
1878 pFrame->InvalidatePage( pPage );
1880 // FME 10.11.2003 #112243#
1881 // Invalidate fly content flag:
1882 if ( pFrame->IsInFly() )
1883 pPage->InvalidateFlyContent();
1885 // OD 14.11.2002 #104684# - invalidate page content in order to
1886 // force format and paint of section content.
1887 pPage->InvalidateContent();
1890 pLay = static_cast<SwLayoutFrame*>(pFrame);
1891 if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
1892 pLay = pLay->GetNextLayoutLeaf();
1893 pPrv = nullptr;
1896 else if ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )
1898 if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
1900 assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
1901 continue; // skip it
1903 if (pLayout->HasMergedParas() && !pNd->StartOfSectionNode()->IsCreateFrameWhenHidingRedlines())
1904 { // tdf#135014 section break in fieldmark (start inside, end outside)
1905 assert(pNd->StartOfSectionNode()->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
1906 continue; // skip it
1908 assert(pActualSection && "Section end without section start?");
1909 assert(pActualSection->GetSectionNode() == pNd->StartOfSectionNode());
1911 //Close the section, where appropriate activate the surrounding
1912 //section again.
1913 pActualSection.reset(pActualSection->GetUpper());
1914 pLay = pLay->FindSctFrame();
1915 if ( pActualSection )
1917 //Could be, that the last SectionFrame remains empty.
1918 //Then now is the time to remove them.
1919 if ( !pLay->ContainsContent() )
1921 SwFrame *pTmpFrame = pLay;
1922 pLay = pTmpFrame->GetUpper();
1923 pPrv = pTmpFrame->GetPrev();
1924 pTmpFrame->RemoveFromLayout();
1925 SwFrame::DestroyFrame(pTmpFrame);
1927 else
1929 if (pLay->IsHiddenNow())
1931 newHiddenSections.push_back(static_cast<SwSectionFrame*>(pLay));
1933 pPrv = pLay;
1934 pLay = pLay->GetUpper();
1937 // new section frame
1938 if (SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame())
1940 // Splitting moves the trailing content to the next frame
1941 pFrame = pOuterSectionFrame->SplitSect(pActualSection->GetLastPos(), pPrv);
1943 // We don't want to leave empty parts back.
1944 if (! pOuterSectionFrame->IsColLocked() &&
1945 ! pOuterSectionFrame->ContainsContent() )
1947 pOuterSectionFrame->DelEmpty( true );
1948 SwFrame::DestroyFrame(pOuterSectionFrame);
1950 else if (pOuterSectionFrame->IsHiddenNow())
1952 newHiddenSections.push_back(pOuterSectionFrame);
1955 else
1957 pFrame = pActualSection->GetSectionNode()->MakeFrame(
1958 pLay, pActualSection->GetSectionNode()->GetSection().IsHiddenFlag());
1959 pFrame->InsertBehind( pLay, pPrv );
1960 static_cast<SwSectionFrame*>(pFrame)->Init();
1962 // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
1963 // for setting position at newly inserted frame
1964 lcl_SetPos( *pFrame, *pLay );
1967 pActualSection->SetSectionFrame( static_cast<SwSectionFrame*>(pFrame) );
1969 pLay = static_cast<SwLayoutFrame*>(pFrame);
1970 if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
1971 pLay = pLay->GetNextLayoutLeaf();
1972 pPrv = nullptr;
1974 else
1976 if (pLay->IsHiddenNow())
1978 newHiddenSections.push_back(static_cast<SwSectionFrame*>(pLay));
1980 //Nothing more with sections, it goes on right behind
1981 //the SectionFrame.
1982 pPrv = pLay;
1983 pLay = pLay->GetUpper();
1986 else if( pNd->IsStartNode() &&
1987 SwFlyStartNode == static_cast<SwStartNode*>(pNd)->GetStartNodeType() )
1989 if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
1991 assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
1992 assert(false); // actually a fly-section can't be deleted?
1993 continue; // skip it
1995 if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
1997 SwFlyFrame* pFly = pLay->FindFlyFrame();
1998 if( pFly )
1999 AppendObjs( pTable, nIndex, pFly, pPage, pDoc );
2002 else
2004 assert(!pLayout->HasMergedParas()
2005 || pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
2006 // Neither Content nor table nor section, so we are done.
2007 break;
2011 if ( pActualSection )
2013 // Might happen that an empty (Follow-)Section is left over.
2014 if ( !(pLay = pActualSection->GetSectionFrame())->ContainsContent() )
2016 pLay->RemoveFromLayout();
2017 SwFrame::DestroyFrame(pLay);
2019 pActualSection.reset();
2022 if ( bPages ) // let the Flys connect to each other
2024 if ( !isFlyCreationSuppressed )
2025 AppendAllObjs( pTable, pLayout );
2026 bObjsDirect = true;
2029 // do it after AppendAllObjs()
2030 for (SwSectionFrame * pNew : newHiddenSections)
2032 for (SwFlowFrame * pSect = pNew; pSect; pSect = pSect->GetPrecede())
2033 { // flys were created visible; section may be paginated so iterate
2034 pSect->GetFrame().HideAndShowObjects();
2038 if( pPageMaker )
2040 pPageMaker->CheckFlyCache( pPage );
2041 pPageMaker.reset();
2042 if( pDoc->GetLayoutCache() )
2044 #ifdef DBG_UTIL
2045 pDoc->GetLayoutCache()->CompareLayout( *pDoc );
2046 #endif
2047 pDoc->GetLayoutCache()->ClearImpl();
2051 pDoc->getIDocumentTimerAccess().UnblockIdling();
2052 if( bOldCallbackActionEnabled )
2053 pLayout->SetCallbackActionEnabled( bOldCallbackActionEnabled );
2056 void MakeFrames( SwDoc *pDoc, SwNode &rSttIdx, SwNode &rEndIdx )
2058 bObjsDirect = false;
2060 SwNodeOffset nEndIdx = rEndIdx.GetIndex();
2061 // TODO for multiple layouts there should be a loop here
2062 SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( rSttIdx,
2063 pDoc->GetNodes()[ nEndIdx-1 ],
2064 pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
2065 if ( pNd )
2067 bool bAfter = *pNd < rSttIdx;
2068 SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() );
2069 sw::FrameMode eMode = sw::FrameMode::Existing;
2070 ::std::vector<SwFrame*> frames;
2071 while (SwFrame* pFrame = aNode2Layout.NextFrame())
2072 { // tdf#150500 new frames may be created that end up merged on pNd
2073 // so copy the currently existing ones; they shouldn't get deleted
2074 frames.push_back(pFrame);
2076 for (SwFrame *const pFrame : frames)
2078 SwLayoutFrame *pUpper = pFrame->GetUpper();
2079 SwFootnoteFrame* pFootnoteFrame = pUpper->FindFootnoteFrame();
2080 bool bOldLock, bOldFootnote;
2081 if( pFootnoteFrame )
2083 bOldFootnote = pFootnoteFrame->IsColLocked();
2084 pFootnoteFrame->ColLock();
2086 else
2087 bOldFootnote = true;
2088 SwSectionFrame* pSct = pUpper->FindSctFrame();
2089 // Inside of footnotes only those areas are interesting that are inside of them. But
2090 // not the ones (e.g. column areas) in which are the footnote containers positioned.
2091 // #109767# Table frame is in section, insert section in cell frame.
2092 if( pSct && ((pFootnoteFrame && !pSct->IsInFootnote()) || pUpper->IsCellFrame()) )
2093 pSct = nullptr;
2094 if( pSct )
2095 { // to prevent pTmp->MoveFwd from destroying the SectionFrame
2096 bOldLock = pSct->IsColLocked();
2097 pSct->ColLock();
2099 else
2100 bOldLock = true;
2102 // If pFrame cannot be moved, it is not possible to move it to the next page. This applies
2103 // also for frames (in the first column of a frame pFrame is moveable) and column
2104 // sections of tables (also here pFrame is moveable).
2105 bool bMoveNext = nEndIdx - rSttIdx.GetIndex() > SwNodeOffset(120);
2106 bool bAllowMove = !pFrame->IsInFly() && pFrame->IsMoveable() &&
2107 (!pFrame->IsInTab() || pFrame->IsTabFrame() );
2108 if ( bMoveNext && bAllowMove )
2110 SwFrame *pMove = pFrame;
2111 SwFrame *pPrev = pFrame->GetPrev();
2112 SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pMove );
2113 assert(pTmp);
2115 if ( bAfter )
2117 // The rest of this page should be empty. Thus, the following one has to move to
2118 // the next page (it might also be located in the following column).
2119 assert(!pTmp->HasFollow() && "prev. node's frame is not last");
2120 pPrev = pFrame;
2121 // If the surrounding SectionFrame has a "next" one,
2122 // so this one needs to be moved as well.
2123 pMove = pFrame->GetIndNext();
2124 SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pFrame->FindColFrame());
2125 if( pCol )
2126 pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
2129 if( pCol && !pMove )
2130 { // No successor so far, look into the next column
2131 pMove = pCol->ContainsAny();
2132 if( pCol->GetNext() )
2133 pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
2134 else if( pCol->IsInSct() )
2135 { // If there is no following column but we are in a column frame,
2136 // there might be (page) columns outside of it.
2137 pCol = static_cast<SwColumnFrame*>(pCol->FindSctFrame()->FindColFrame());
2138 if( pCol )
2139 pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
2141 else
2142 pCol = nullptr;
2144 // skip invalid SectionFrames
2145 while( pMove && pMove->IsSctFrame() &&
2146 !static_cast<SwSectionFrame*>(pMove)->GetSection() )
2147 pMove = pMove->GetNext();
2148 } while( !pMove && pCol );
2150 if( pMove )
2152 if ( pMove->IsContentFrame() )
2153 pTmp = static_cast<SwContentFrame*>(pMove);
2154 else if ( pMove->IsTabFrame() )
2155 pTmp = static_cast<SwTabFrame*>(pMove);
2156 else if ( pMove->IsSctFrame() )
2158 pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
2159 if( pMove )
2160 pTmp = SwFlowFrame::CastFlowFrame( pMove );
2161 else
2162 pTmp = nullptr;
2165 else
2166 pTmp = nullptr;
2168 else
2170 assert(!pTmp->IsFollow() && "next node's frame is not master");
2171 // move the _content_ of a section frame
2172 if( pMove->IsSctFrame() )
2174 while( pMove && pMove->IsSctFrame() &&
2175 !static_cast<SwSectionFrame*>(pMove)->GetSection() )
2176 pMove = pMove->GetNext();
2177 if( pMove && pMove->IsSctFrame() )
2178 pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
2179 if( pMove )
2180 pTmp = SwFlowFrame::CastFlowFrame( pMove );
2181 else
2182 pTmp = nullptr;
2186 if( pTmp )
2188 SwFrame* pOldUp = pTmp->GetFrame().GetUpper();
2189 // MoveFwd==true means that we are still on the same page.
2190 // But since we want to move if possible!
2191 bool bTmpOldLock = pTmp->IsJoinLocked();
2192 pTmp->LockJoin();
2193 while( pTmp->MoveFwd( true, false, true ) )
2195 if( pOldUp == pTmp->GetFrame().GetUpper() )
2196 break;
2197 pOldUp = pTmp->GetFrame().GetUpper();
2199 if( !bTmpOldLock )
2200 pTmp->UnlockJoin();
2202 ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(),
2203 pFrame->IsInDocBody(), nEndIdx, pPrev, eMode );
2205 else
2207 SwFrame* pPrv = bAfter ? pFrame : pFrame->GetPrev();
2209 ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), false,
2210 nEndIdx, pPrv, eMode );
2211 // OD 23.06.2003 #108784# - correction: append objects doesn't
2212 // depend on value of <bAllowMove>
2213 if( !isFlyCreationSuppressed )
2215 const sw::SpzFrameFormats* pTable = pDoc->GetSpzFrameFormats();
2216 if( !pTable->empty() )
2217 AppendAllObjs( pTable, pUpper );
2220 if( pFrame->IsInFly() )
2221 pFrame->FindFlyFrame()->Invalidate_();
2222 if( pFrame->IsInTab() )
2223 pFrame->InvalidateSize();
2226 SwPageFrame *pPage = pUpper->FindPageFrame();
2227 SwFrame::CheckPageDescs( pPage, false );
2228 if( !bOldFootnote )
2229 pFootnoteFrame->ColUnlock();
2230 if( !bOldLock )
2232 pSct->ColUnlock();
2233 // pSct might be empty (e.g. when inserting linked section containing further
2234 // sections) and can be destroyed in such cases.
2235 if( !pSct->ContainsContent() )
2237 pSct->DelEmpty( true );
2238 pUpper->getRootFrame()->RemoveFromList( pSct );
2239 SwFrame::DestroyFrame(pSct);
2242 eMode = sw::FrameMode::New; // use Existing only once!
2246 bObjsDirect = true;
2249 SwBorderAttrs::SwBorderAttrs(const sw::BorderCacheOwner* pOwner, const SwFrame* pConstructor)
2250 : SwCacheObj(pOwner)
2251 , m_rAttrSet(pConstructor->IsContentFrame()
2252 ? pConstructor->IsTextFrame()
2253 ? static_cast<const SwTextFrame*>(pConstructor)->GetTextNodeForParaProps()->GetSwAttrSet()
2254 : static_cast<const SwNoTextFrame*>(pConstructor)->GetNode()->GetSwAttrSet()
2255 : static_cast<const SwLayoutFrame*>(pConstructor)->GetFormat()->GetAttrSet())
2256 , m_rUL(m_rAttrSet.GetULSpace())
2257 , m_rBox(m_rAttrSet.GetBox())
2258 , m_rShadow(m_rAttrSet.GetShadow())
2259 , m_aFrameSize(m_rAttrSet.GetFrameSize().GetSize())
2260 , m_bIsLine(false)
2261 , m_bJoinedWithPrev(false)
2262 , m_bJoinedWithNext(false)
2263 , m_nTopLine(0)
2264 , m_nBottomLine(0)
2265 , m_nLeftLine(0)
2266 , m_nRightLine(0)
2267 , m_nTop(0)
2268 , m_nBottom(0)
2269 , m_nGetTopLine(0)
2270 , m_nGetBottomLine(0)
2271 , m_nLineSpacing(0)
2273 // #i96772#
2274 const SwTextFrame* pTextFrame = pConstructor->DynCastTextFrame();
2275 if ( pTextFrame )
2277 m_pFirstLineIndent.reset(m_rAttrSet.GetFirstLineIndent().Clone());
2278 m_pTextLeftMargin.reset(m_rAttrSet.GetTextLeftMargin().Clone());
2279 m_pRightMargin.reset(m_rAttrSet.GetRightMargin().Clone());
2280 assert(m_pFirstLineIndent);
2281 assert(m_pTextLeftMargin);
2283 else
2285 // LRSpaceItem is copied due to the possibility that it is adjusted
2286 m_xLR.reset(m_rAttrSet.GetLRSpace().Clone());
2287 if (pConstructor->IsNoTextFrame())
2289 m_xLR = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
2291 assert(m_xLR);
2294 // Caution: The USHORTs for the cached values are not initialized by intention!
2296 // everything needs to be calculated at least once:
2297 m_bTopLine = m_bBottomLine = m_bLeftLine = m_bRightLine =
2298 m_bTop = m_bBottom = m_bLine = true;
2300 // except this one: calculate line spacing before cell border only for text frames
2301 m_bLineSpacing = bool(pTextFrame);
2303 m_bCacheGetLine = m_bCachedGetTopLine = m_bCachedGetBottomLine = false;
2304 // OD 21.05.2003 #108789# - init cache status for values <m_bJoinedWithPrev>
2305 // and <m_bJoinedWithNext>, which aren't initialized by default.
2306 m_bCachedJoinedWithPrev = false;
2307 m_bCachedJoinedWithNext = false;
2310 SwBorderAttrs::~SwBorderAttrs()
2312 const_cast<sw::BorderCacheOwner*>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = false;
2315 /* All calc methods calculate a safety distance in addition to the values given by the attributes.
2316 * This safety distance is only added when working with borders and/or shadows to prevent that
2317 * e.g. borders are painted over.
2320 void SwBorderAttrs::CalcTop_()
2322 m_nTop = CalcTopLine() + m_rUL.GetUpper();
2324 bool bGutterAtTop = m_rAttrSet.GetDoc()->getIDocumentSettingAccess().get(
2325 DocumentSettingId::GUTTER_AT_TOP);
2326 if (bGutterAtTop && m_xLR)
2328 // Decrease the print area: the top space is the sum of top and gutter margins.
2329 m_nTop += m_xLR->GetGutterMargin();
2332 m_bTop = false;
2335 void SwBorderAttrs::CalcBottom_()
2337 m_nBottom = CalcBottomLine() + m_rUL.GetLower();
2338 m_bBottom = false;
2341 tools::Long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const
2343 tools::Long nRight=0;
2345 if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING)) {
2346 // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
2347 // and right border are painted on the right respectively left.
2348 if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
2349 nRight = CalcLeftLine();
2350 else
2351 nRight = CalcRightLine();
2354 // for paragraphs, "left" is "before text" and "right" is "after text"
2355 if (pCaller->IsTextFrame())
2357 // tdf#163913: Only apply the fixed-width part of the margin here.
2358 // Font-relative margins will be applied as an adjustment later on.
2359 if (pCaller->IsRightToLeft())
2361 nRight += m_pTextLeftMargin->ResolveLeftFixedPart(*m_pFirstLineIndent);
2363 else
2365 nRight += m_pRightMargin->ResolveRightFixedPart();
2368 else
2369 nRight += m_xLR->ResolveRight({});
2371 // correction: retrieve left margin for numbering in R2L-layout
2372 if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() )
2374 nRight += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
2377 if (pCaller->IsPageFrame())
2379 const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
2380 bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
2381 DocumentSettingId::GUTTER_AT_TOP);
2382 if (!bGutterAtTop)
2384 bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
2385 tools::Long nGutterMargin = bRtlGutter ? m_xLR->GetGutterMargin() : m_xLR->GetRightGutterMargin();
2386 // Decrease the print area: the right space is the sum of right and right gutter
2387 // margins.
2388 nRight += nGutterMargin;
2392 return nRight;
2395 tools::Long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const
2397 tools::Long nLeft=0;
2399 if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING))
2401 // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
2402 // and right border are painted on the right respectively left.
2403 if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
2404 nLeft = CalcRightLine();
2405 else
2406 nLeft = CalcLeftLine();
2409 // for paragraphs, "left" is "before text" and "right" is "after text"
2411 // tdf#163913: Only apply the fixed-width part of the margin here.
2412 // Font-relative margins will be applied as an adjustment later on.
2413 if (pCaller->IsTextFrame() && pCaller->IsRightToLeft())
2415 nLeft += m_pRightMargin->ResolveRightFixedPart();
2417 else
2419 if (pCaller->IsTextFrame())
2421 nLeft += m_pTextLeftMargin->ResolveLeftFixedPart(*m_pFirstLineIndent);
2423 else
2425 nLeft += m_xLR->ResolveLeft({});
2429 // correction: do not retrieve left margin for numbering in R2L-layout
2430 if ( pCaller->IsTextFrame() && !pCaller->IsRightToLeft() )
2432 nLeft += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
2435 if (pCaller->IsPageFrame())
2437 const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
2438 bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
2439 DocumentSettingId::GUTTER_AT_TOP);
2440 if (!bGutterAtTop)
2442 bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
2443 tools::Long nGutterMargin = bRtlGutter ? m_xLR->GetRightGutterMargin() : m_xLR->GetGutterMargin();
2444 // Decrease the print area: the left space is the sum of left and gutter margins.
2445 nLeft += nGutterMargin;
2449 return nLeft;
2452 /* Calculated values for borders and shadows.
2453 * It might be that a distance is wanted even without lines. This will be
2454 * considered here and not by the attribute (e.g. bBorderDist for cells).
2457 void SwBorderAttrs::CalcTopLine_()
2459 m_nTopLine = m_rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true );
2460 m_nTopLine = m_nTopLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::TOP);
2461 m_bTopLine = false;
2464 void SwBorderAttrs::CalcBottomLine_()
2466 m_nBottomLine = m_rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM, true );
2467 m_nBottomLine = m_nBottomLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM);
2468 m_bBottomLine = false;
2471 void SwBorderAttrs::CalcLeftLine_()
2473 m_nLeftLine = m_rBox.CalcLineSpace( SvxBoxItemLine::LEFT, true);
2474 m_nLeftLine = m_nLeftLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT);
2475 m_bLeftLine = false;
2478 void SwBorderAttrs::CalcRightLine_()
2480 m_nRightLine = m_rBox.CalcLineSpace( SvxBoxItemLine::RIGHT, true );
2481 m_nRightLine = m_nRightLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT);
2482 m_bRightLine = false;
2485 void SwBorderAttrs::IsLine_()
2487 m_bIsLine = m_rBox.GetTop() || m_rBox.GetBottom() ||
2488 m_rBox.GetLeft()|| m_rBox.GetRight();
2489 m_bLine = false;
2492 /* The borders of neighboring paragraphs are condensed by following algorithm:
2494 * 1. No top border if the predecessor has the same top border and (3) applies.
2495 * In addition, the paragraph needs to have a border at least one side (left/right/bottom).
2496 * 2. No bottom border if the successor has the same bottom border and (3) applies.
2497 * In addition, the paragraph needs to have a border at least one side (left/right/top).
2498 * 3. The borders on the left and right side are identical between the current and the
2499 * pre-/succeeding paragraph.
2502 static bool CmpLines( const editeng::SvxBorderLine *pL1, const editeng::SvxBorderLine *pL2 )
2504 return ( ((pL1 && pL2) && (*pL1 == *pL2)) || (!pL1 && !pL2) );
2507 // OD 21.05.2003 #108789# - change name of 1st parameter - "rAttrs" -> "rCmpAttrs"
2508 // OD 21.05.2003 #108789# - compare <CalcRight()> and <rCmpAttrs.CalcRight()>
2509 // instead of only the right LR-spacing, because R2L-layout has to be
2510 // considered.
2511 bool SwBorderAttrs::CmpLeftRight( const SwBorderAttrs &rCmpAttrs,
2512 const SwFrame *pCaller,
2513 const SwFrame *pCmp ) const
2515 return ( CmpLines( rCmpAttrs.GetBox().GetLeft(), GetBox().GetLeft() ) &&
2516 CmpLines( rCmpAttrs.GetBox().GetRight(),GetBox().GetRight() ) &&
2517 CalcLeft( pCaller ) == rCmpAttrs.CalcLeft( pCmp ) &&
2518 // OD 21.05.2003 #108789# - compare <CalcRight> with <rCmpAttrs.CalcRight>.
2519 CalcRight( pCaller ) == rCmpAttrs.CalcRight( pCmp ) );
2522 bool SwBorderAttrs::JoinWithCmp( const SwFrame& _rCallerFrame,
2523 const SwFrame& _rCmpFrame ) const
2525 bool bReturnVal = false;
2527 SwBorderAttrAccess aCmpAccess( SwFrame::GetCache(), &_rCmpFrame );
2528 const SwBorderAttrs &rCmpAttrs = *aCmpAccess.Get();
2529 if ( m_rShadow == rCmpAttrs.GetShadow() &&
2530 CmpLines( m_rBox.GetTop(), rCmpAttrs.GetBox().GetTop() ) &&
2531 CmpLines( m_rBox.GetBottom(), rCmpAttrs.GetBox().GetBottom() ) &&
2532 CmpLeftRight( rCmpAttrs, &_rCallerFrame, &_rCmpFrame )
2535 bReturnVal = true;
2538 return bReturnVal;
2541 // OD 21.05.2003 #108789# - method to determine, if borders are joined with
2542 // previous frame. Calculated value saved in cached value <m_bJoinedWithPrev>
2543 // OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>
2544 void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame,
2545 const SwFrame* _pPrevFrame ) const
2547 // set default
2548 m_bJoinedWithPrev = false;
2550 if ( _rFrame.IsTextFrame() )
2552 // text frame can potentially join with previous text frame, if
2553 // corresponding attribute set is set at previous text frame.
2554 // OD 2004-02-26 #i25029# - If parameter <_pPrevFrame> is set, take this
2555 // one as previous frame.
2556 const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev();
2557 // OD 2004-02-13 #i25029# - skip hidden text frames.
2558 while (pPrevFrame && pPrevFrame->IsHiddenNow())
2560 pPrevFrame = pPrevFrame->GetPrev();
2562 if ( pPrevFrame && pPrevFrame->IsTextFrame() &&
2563 pPrevFrame->GetAttrSet()->GetParaConnectBorder().GetValue()
2566 m_bJoinedWithPrev = JoinWithCmp( _rFrame, *pPrevFrame );
2570 // valid cache status, if demanded
2571 // OD 2004-02-26 #i25029# - Do not validate cache, if parameter <_pPrevFrame>
2572 // is set.
2573 m_bCachedJoinedWithPrev = m_bCacheGetLine && !_pPrevFrame;
2576 // OD 21.05.2003 #108789# - method to determine, if borders are joined with
2577 // next frame. Calculated value saved in cached value <m_bJoinedWithNext>
2578 void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame ) const
2580 // set default
2581 m_bJoinedWithNext = false;
2583 if ( _rFrame.IsTextFrame() )
2585 // text frame can potentially join with next text frame, if
2586 // corresponding attribute set is set at current text frame.
2587 // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames.
2588 const SwFrame* pNextFrame = _rFrame.GetNext();
2589 while (pNextFrame && pNextFrame->IsHiddenNow())
2591 pNextFrame = pNextFrame->GetNext();
2593 if ( pNextFrame && pNextFrame->IsTextFrame() &&
2594 _rFrame.GetAttrSet()->GetParaConnectBorder().GetValue()
2597 m_bJoinedWithNext = JoinWithCmp( _rFrame, *pNextFrame );
2601 // valid cache status, if demanded
2602 m_bCachedJoinedWithNext = m_bCacheGetLine;
2605 // OD 21.05.2003 #108789# - accessor for cached values <m_bJoinedWithPrev>
2606 // OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>, which is passed to
2607 // method <_CalcJoindWithPrev(..)>.
2608 bool SwBorderAttrs::JoinedWithPrev( const SwFrame& _rFrame,
2609 const SwFrame* _pPrevFrame ) const
2611 if ( !m_bCachedJoinedWithPrev || _pPrevFrame )
2613 // OD 2004-02-26 #i25029# - pass <_pPrevFrame> as 2nd parameter
2614 CalcJoinedWithPrev( _rFrame, _pPrevFrame );
2617 return m_bJoinedWithPrev;
2620 bool SwBorderAttrs::JoinedWithNext( const SwFrame& _rFrame ) const
2622 if ( !m_bCachedJoinedWithNext )
2624 CalcJoinedWithNext( _rFrame );
2627 return m_bJoinedWithNext;
2630 // OD 2004-02-26 #i25029# - added 2nd parameter <_pPrevFrame>, which is passed to
2631 // method <JoinedWithPrev>
2632 void SwBorderAttrs::GetTopLine_( const SwFrame& _rFrame,
2633 const SwFrame* _pPrevFrame )
2635 sal_uInt16 nRet = CalcTopLine();
2637 // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
2638 // OD 2004-02-26 #i25029# - add 2nd parameter
2639 if ( JoinedWithPrev( _rFrame, _pPrevFrame ) )
2641 nRet = 0;
2644 m_bCachedGetTopLine = m_bCacheGetLine;
2646 m_nGetTopLine = nRet;
2649 void SwBorderAttrs::GetBottomLine_( const SwFrame& _rFrame )
2651 sal_uInt16 nRet = CalcBottomLine();
2653 // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
2654 if ( JoinedWithNext( _rFrame ) )
2656 nRet = 0;
2659 m_bCachedGetBottomLine = m_bCacheGetLine;
2661 m_nGetBottomLine = nRet;
2664 void SwBorderAttrs::CalcLineSpacing_()
2666 // tdf#125300 compatibility option AddParaLineSpacingToTableCells needs also line spacing
2667 const SvxLineSpacingItem &rSpace = m_rAttrSet.GetLineSpacing();
2668 if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop && rSpace.GetPropLineSpace() > 100 )
2670 sal_Int32 nFontSize = m_rAttrSet.Get(RES_CHRATR_FONTSIZE).GetHeight();
2671 m_nLineSpacing = nFontSize * (rSpace.GetPropLineSpace() - 100) * 1.15 / 100;
2673 m_bLineSpacing = false;
2676 static sw::BorderCacheOwner const* GetBorderCacheOwner(SwFrame const& rFrame)
2678 return rFrame.IsContentFrame()
2679 ? static_cast<sw::BorderCacheOwner const*>(rFrame.IsTextFrame()
2680 // sw_redlinehide: presumably this caches the border attrs at the model level and can be shared across different layouts so we want the ParaProps node here
2681 ? static_cast<const SwTextFrame&>(rFrame).GetTextNodeForParaProps()
2682 : static_cast<const SwNoTextFrame&>(rFrame).GetNode())
2683 : static_cast<sw::BorderCacheOwner const*>(static_cast<const SwLayoutFrame&>(rFrame).GetFormat());
2686 SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) :
2687 SwCacheAccess( rCach,
2688 static_cast<void const *>(GetBorderCacheOwner(*pFrame)),
2689 GetBorderCacheOwner(*pFrame)->IsInCache()),
2690 m_pConstructor( pFrame )
2694 SwCacheObj *SwBorderAttrAccess::NewObj()
2696 const_cast<sw::BorderCacheOwner *>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = true;
2697 return new SwBorderAttrs( static_cast<sw::BorderCacheOwner const *>(m_pOwner), m_pConstructor );
2700 SwBorderAttrs *SwBorderAttrAccess::Get()
2702 return static_cast<SwBorderAttrs*>(SwCacheAccess::Get());
2705 SwOrderIter::SwOrderIter( const SwPageFrame *pPg ) :
2706 m_pPage( pPg ),
2707 m_pCurrent( nullptr )
2711 void SwOrderIter::Top()
2713 m_pCurrent = nullptr;
2714 if ( !m_pPage->GetSortedObjs() )
2715 return;
2717 const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
2718 if ( !pObjs->size() )
2719 return;
2721 sal_uInt32 nTopOrd = 0;
2722 (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
2723 for (SwAnchoredObject* i : *pObjs)
2725 const SdrObject* pObj = i->GetDrawObj();
2726 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
2727 continue;
2728 sal_uInt32 nTmp = pObj->GetOrdNumDirect();
2729 if ( nTmp >= nTopOrd )
2731 nTopOrd = nTmp;
2732 m_pCurrent = pObj;
2737 const SdrObject *SwOrderIter::Bottom()
2739 m_pCurrent = nullptr;
2740 if ( m_pPage->GetSortedObjs() )
2742 sal_uInt32 nBotOrd = USHRT_MAX;
2743 const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
2744 if ( pObjs->size() )
2746 (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
2747 for (SwAnchoredObject* i : *pObjs)
2749 const SdrObject* pObj = i->GetDrawObj();
2750 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
2751 continue;
2752 sal_uInt32 nTmp = pObj->GetOrdNumDirect();
2753 if ( nTmp < nBotOrd )
2755 nBotOrd = nTmp;
2756 m_pCurrent = pObj;
2761 return m_pCurrent;
2764 const SdrObject *SwOrderIter::Next()
2766 const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
2767 m_pCurrent = nullptr;
2768 if ( m_pPage->GetSortedObjs() )
2770 sal_uInt32 nOrd = USHRT_MAX;
2771 const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
2772 if ( pObjs->size() )
2774 (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
2775 for (SwAnchoredObject* i : *pObjs)
2777 const SdrObject* pObj = i->GetDrawObj();
2778 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
2779 continue;
2780 sal_uInt32 nTmp = pObj->GetOrdNumDirect();
2781 if ( nTmp > nCurOrd && nTmp < nOrd )
2783 nOrd = nTmp;
2784 m_pCurrent = pObj;
2789 return m_pCurrent;
2792 void SwOrderIter::Prev()
2794 const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
2795 m_pCurrent = nullptr;
2796 if ( !m_pPage->GetSortedObjs() )
2797 return;
2799 const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
2800 if ( !pObjs->size() )
2801 return;
2803 sal_uInt32 nOrd = 0;
2804 (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
2805 for (SwAnchoredObject* i : *pObjs)
2807 const SdrObject* pObj = i->GetDrawObj();
2808 if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
2809 continue;
2810 sal_uInt32 nTmp = pObj->GetOrdNumDirect();
2811 if ( nTmp < nCurOrd && nTmp >= nOrd )
2813 nOrd = nTmp;
2814 m_pCurrent = pObj;
2819 /// Keep and restore the substructure of a layout frame for an action.
2820 // New algorithm:
2821 // Do not look at each neighbor one by one to set all pointers correctly.
2822 // It is sufficient to detach a part of a chain and check if another chain needs to be added
2823 // when attaching it again. Only the pointers necessary for the chain connection need to be
2824 // adjusted. The correction happens in RestoreContent(). In between all access is restricted.
2825 // During this action, the Flys are detached from the page.
2827 // #115759# - 'remove' also drawing object from page and
2828 // at-fly anchored objects from page
2829 static void lcl_RemoveObjsFromPage( SwFrame* _pFrame )
2831 OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_RemoveObjsFromPage." );
2832 SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
2833 for (SwAnchoredObject* pObj : rObjs)
2835 // #115759# - reset member, at which the anchored
2836 // object orients its vertical position
2837 pObj->ClearVertPosOrientFrame();
2838 // #i43913#
2839 pObj->ResetLayoutProcessBools();
2840 // #115759# - remove also lower objects of as-character
2841 // anchored Writer fly frames from page
2842 if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
2844 // #115759# - remove also direct lowers of Writer
2845 // fly frame from page
2846 if ( pFlyFrame->GetDrawObjs() )
2848 ::lcl_RemoveObjsFromPage( pFlyFrame );
2851 SwContentFrame* pCnt = pFlyFrame->ContainsContent();
2852 while ( pCnt )
2854 if ( pCnt->GetDrawObjs() )
2855 ::lcl_RemoveObjsFromPage( pCnt );
2856 pCnt = pCnt->GetNextContentFrame();
2858 if ( pFlyFrame->IsFlyFreeFrame() )
2860 // #i28701# - use new method <GetPageFrame()>
2861 if (SwPageFrame *pPg = pFlyFrame->GetPageFrame())
2862 pPg->RemoveFlyFromPage(pFlyFrame);
2865 // #115759# - remove also drawing objects from page
2866 else if ( auto pDrawObj = dynamic_cast<SwAnchoredDrawObject*>( pObj) )
2868 if (pObj->GetFrameFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
2870 if (SwPageFrame *pPg = pObj->GetPageFrame())
2871 pPg->RemoveDrawObjFromPage( *pDrawObj );
2877 SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart )
2879 if( pLay->IsSctFrame() && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
2880 sw_RemoveFootnotes( static_cast<SwColumnFrame*>(pLay->Lower()), true, true );
2882 SwFrame *pSav = pLay->ContainsAny();
2883 if ( nullptr == pSav )
2884 return nullptr;
2886 if( pSav->IsInFootnote() && !pLay->IsInFootnote() )
2889 pSav = pSav->FindNext();
2890 while( pSav && pSav->IsInFootnote() );
2891 if( !pSav || !pLay->IsAnLower( pSav ) )
2892 return nullptr;
2895 // Tables should be saved as a whole, exception:
2896 // The contents of a section or a cell inside a table should be saved
2897 if ( pSav->IsInTab() && !( ( pLay->IsSctFrame() || pLay->IsCellFrame() ) && pLay->IsInTab() ) )
2898 while ( !pSav->IsTabFrame() )
2899 pSav = pSav->GetUpper();
2901 if( pSav->IsInSct() )
2902 { // search the upmost section inside of pLay
2903 SwFrame* pSect = pLay->FindSctFrame();
2904 SwFrame *pTmp = pSav;
2907 pSav = pTmp;
2908 pTmp = (pSav && pSav->GetUpper()) ? pSav->GetUpper()->FindSctFrame() : nullptr;
2909 } while ( pTmp != pSect );
2912 SwFrame *pFloat = pSav;
2913 if( !pStart )
2914 pStart = pSav;
2915 bool bGo = pStart == pSav;
2918 if( bGo )
2919 pFloat->GetUpper()->m_pLower = nullptr; // detach the chain part
2921 // search the end of the chain part, remove Flys on the way
2924 if( bGo )
2926 if ( pFloat->IsContentFrame() )
2928 if ( pFloat->GetDrawObjs() )
2929 ::lcl_RemoveObjsFromPage( static_cast<SwContentFrame*>(pFloat) );
2931 else if ( pFloat->IsTabFrame() || pFloat->IsSctFrame() )
2933 SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(pFloat)->ContainsContent();
2934 if( pCnt )
2937 { if ( pCnt->GetDrawObjs() )
2938 ::lcl_RemoveObjsFromPage( pCnt );
2939 pCnt = pCnt->GetNextContentFrame();
2940 } while ( pCnt && static_cast<SwLayoutFrame*>(pFloat)->IsAnLower( pCnt ) );
2943 else {
2944 OSL_ENSURE( !pFloat, "new FloatFrame?" );
2947 if ( pFloat->GetNext() )
2949 if( bGo )
2950 pFloat->mpUpper = nullptr;
2951 pFloat = pFloat->GetNext();
2952 if( !bGo && pFloat == pStart )
2954 bGo = true;
2955 pFloat->mpPrev->mpNext = nullptr;
2956 pFloat->mpPrev = nullptr;
2959 else
2960 break;
2962 } while ( pFloat );
2964 // search next chain part and connect both chains
2965 SwFrame *pTmp = pFloat ? pFloat->FindNext() : nullptr;
2966 if (bGo && pFloat)
2967 pFloat->mpUpper = nullptr;
2969 if( !pLay->IsInFootnote() )
2970 while( pTmp && pTmp->IsInFootnote() )
2971 pTmp = pTmp->FindNext();
2973 if ( !pLay->IsAnLower( pTmp ) )
2974 pTmp = nullptr;
2976 if ( pTmp && bGo )
2978 pFloat->mpNext = pTmp; // connect both chains
2979 pFloat->mpNext->mpPrev = pFloat;
2981 pFloat = pTmp;
2982 bGo = bGo || ( pStart == pFloat );
2983 } while ( pFloat );
2985 return bGo ? pStart : nullptr;
2988 // #115759# - add also drawing objects to page and at-fly
2989 // anchored objects to page
2990 static void lcl_AddObjsToPage( SwFrame* _pFrame, SwPageFrame* _pPage )
2992 OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_AddObjsToPage." );
2993 SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
2994 for (SwAnchoredObject* pObj : rObjs)
2996 // #115759# - unlock position of anchored object
2997 // in order to get the object's position calculated.
2998 pObj->UnlockPosition();
2999 // #115759# - add also lower objects of as-character
3000 // anchored Writer fly frames from page
3001 if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
3003 if (pFlyFrame->IsFlyFreeFrame())
3005 _pPage->AppendFlyToPage( pFlyFrame );
3007 pFlyFrame->InvalidatePos_();
3008 pFlyFrame->InvalidateSize_();
3009 pFlyFrame->InvalidatePage( _pPage );
3011 // #115759# - add also at-fly anchored objects
3012 // to page
3013 if ( pFlyFrame->GetDrawObjs() )
3015 ::lcl_AddObjsToPage( pFlyFrame, _pPage );
3018 SwContentFrame *pCnt = pFlyFrame->ContainsContent();
3019 while ( pCnt )
3021 if ( pCnt->GetDrawObjs() )
3022 ::lcl_AddObjsToPage( pCnt, _pPage );
3023 pCnt = pCnt->GetNextContentFrame();
3026 // #115759# - remove also drawing objects from page
3027 else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
3029 if (pObj->GetFrameFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
3031 pObj->InvalidateObjPos();
3032 _pPage->AppendDrawObjToPage(
3033 *static_cast<SwAnchoredDrawObject*>(pObj) );
3039 void RestoreContent( SwFrame *pSav, SwLayoutFrame *pParent, SwFrame *pSibling )
3041 assert(pSav && pParent && "no Save or Parent provided for RestoreContent.");
3042 SwRectFnSet aRectFnSet(pParent);
3044 // If there are already FlowFrames below the new parent, so add the chain (starting with pSav)
3045 // after the last one. The parts are inserted and invalidated if needed.
3046 // On the way, the Flys of the ContentFrames are registered at the page.
3048 SwPageFrame *pPage = pParent->FindPageFrame();
3050 if ( pPage )
3051 pPage->InvalidatePage( pPage );
3053 // determine predecessor and establish connection or initialize
3054 pSav->mpPrev = pSibling;
3055 SwFrame* pNxt;
3056 if ( pSibling )
3058 pNxt = pSibling->mpNext;
3059 pSibling->mpNext = pSav;
3060 pSibling->InvalidatePrt_();
3061 pSibling->InvalidatePage( pPage );
3062 SwFlowFrame *pFlowFrame = dynamic_cast<SwFlowFrame*>(pSibling);
3063 if (pFlowFrame && pFlowFrame->GetFollow())
3064 pSibling->Prepare( PrepareHint::Clear, nullptr, false );
3066 else
3067 { pNxt = pParent->m_pLower;
3068 pParent->m_pLower = pSav;
3069 pSav->mpUpper = pParent; // set here already, so that it is explicit when invalidating
3071 if ( pSav->IsContentFrame() )
3072 static_cast<SwContentFrame*>(pSav)->InvalidatePage( pPage );
3073 else
3074 { // pSav might be an empty SectFrame
3075 SwContentFrame* pCnt = pParent->ContainsContent();
3076 if( pCnt )
3077 pCnt->InvalidatePage( pPage );
3081 // the parent needs to grow appropriately
3082 SwTwips nGrowVal = 0;
3083 SwFrame* pLast;
3085 { pSav->mpUpper = pParent;
3086 nGrowVal += aRectFnSet.GetHeight(pSav->getFrameArea());
3087 pSav->InvalidateAll_();
3089 // register Flys, if TextFrames than also invalidate appropriately
3090 if ( pSav->IsContentFrame() )
3092 if ( pSav->IsTextFrame() &&
3093 static_cast<SwTextFrame*>(pSav)->GetCacheIdx() != USHRT_MAX )
3094 static_cast<SwTextFrame*>(pSav)->Init(); // I am its friend
3096 if ( pPage && pSav->GetDrawObjs() )
3097 ::lcl_AddObjsToPage( static_cast<SwContentFrame*>(pSav), pPage );
3099 else
3100 { SwContentFrame *pBlub = static_cast<SwLayoutFrame*>(pSav)->ContainsContent();
3101 if( pBlub )
3104 { if ( pPage && pBlub->GetDrawObjs() )
3105 ::lcl_AddObjsToPage( pBlub, pPage );
3106 if( pBlub->IsTextFrame() && static_cast<SwTextFrame*>(pBlub)->HasFootnote() &&
3107 static_cast<SwTextFrame*>(pBlub)->GetCacheIdx() != USHRT_MAX )
3108 static_cast<SwTextFrame*>(pBlub)->Init(); // I am its friend
3109 pBlub = pBlub->GetNextContentFrame();
3110 } while ( pBlub && static_cast<SwLayoutFrame*>(pSav)->IsAnLower( pBlub ));
3113 pLast = pSav;
3114 pSav = pSav->GetNext();
3116 } while ( pSav );
3118 if( pNxt )
3120 pLast->mpNext = pNxt;
3121 pNxt->mpPrev = pLast;
3124 pParent->Grow( nGrowVal );
3127 namespace sw {
3129 bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 const nPageNum)
3131 assert(rLayout.GetLower());
3132 // unfortunately can only get SwPageDesc, not SwFormatPageDesc here...
3133 auto const nFirstVirtPageNum(rLayout.GetLower()->GetVirtPageNum());
3134 bool const isFirstPageOfLayoutOdd(nFirstVirtPageNum % 2 == 1);
3135 return ((nPageNum % 2) == 1) == isFirstPageOfLayoutOdd;
3138 } // namespace sw
3140 SwPageFrame * InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper,
3141 bool const isRightPage, bool const bFirst, bool bInsertEmpty,
3142 bool const bFootnote,
3143 SwFrame *pSibling,
3144 bool const bVeryFirstPage )
3146 assert(pUpper);
3147 assert(pUpper->IsRootFrame());
3148 assert(!pSibling || static_cast<SwLayoutFrame const*>(pUpper)->Lower() != pSibling); // currently no insert before 1st page
3149 SwPageFrame *pRet;
3150 SwDoc *pDoc = static_cast<SwLayoutFrame*>(pUpper)->GetFormat()->GetDoc();
3151 if (bFirst)
3153 if (rDesc.IsFirstShared())
3155 // We need to fallback to left or right page format, decide it now.
3156 // FIXME: is this still needed?
3157 if (isRightPage)
3159 rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetHeader() );
3160 rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetFooter() );
3161 // fdo#60250 copy margins for mirrored pages
3162 rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetLRSpace() );
3164 else
3166 rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetHeader() );
3167 rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetFooter() );
3168 rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetLRSpace() );
3172 SwFrameFormat *pFormat(isRightPage ? rDesc.GetRightFormat(bFirst) : rDesc.GetLeftFormat(bFirst));
3173 // If there is no FrameFormat for this page, add an empty page
3174 if ( !pFormat )
3176 pFormat = isRightPage ? rDesc.GetLeftFormat(bVeryFirstPage) : rDesc.GetRightFormat(bVeryFirstPage);
3177 OSL_ENSURE( pFormat, "Descriptor without any format?!" );
3178 bInsertEmpty = !bInsertEmpty;
3180 if( bInsertEmpty )
3182 SwPageDesc *pTmpDesc = pSibling && pSibling->GetPrev() ?
3183 static_cast<SwPageFrame*>(pSibling->GetPrev())->GetPageDesc() : &rDesc;
3184 pRet = new SwPageFrame( pDoc->GetEmptyPageFormat(), pUpper, pTmpDesc );
3185 SAL_INFO( "sw.pageframe", "InsertNewPage - insert empty p: " << pRet << " d: " << pTmpDesc );
3186 pRet->Paste( pUpper, pSibling );
3187 pRet->PreparePage( bFootnote );
3189 pRet = new SwPageFrame( pFormat, pUpper, &rDesc );
3190 SAL_INFO( "sw.pageframe", "InsertNewPage p: " << pRet << " d: " << &rDesc << " f: " << pFormat );
3191 pRet->Paste( pUpper, pSibling );
3192 pRet->PreparePage( bFootnote );
3193 if ( pRet->GetNext() )
3194 SwRootFrame::AssertPageFlys( pRet );
3195 return pRet;
3198 /* The following two methods search the layout structure recursively and
3199 * register all Flys at the page that have a Frame in this structure as an anchor.
3202 static void lcl_Regist( SwPageFrame *pPage, const SwFrame *pAnch )
3204 SwSortedObjs *pObjs = const_cast<SwSortedObjs*>(pAnch->GetDrawObjs());
3205 for (SwAnchoredObject* pObj : *pObjs)
3207 if (SwFlyFrame* pFly = pObj->DynCastFlyFrame())
3209 // register (not if already known)
3210 // #i28701# - use new method <GetPageFrame()>
3211 SwPageFrame *pPg = pFly->IsFlyFreeFrame()
3212 ? pFly->GetPageFrame() : pFly->FindPageFrame();
3213 if ( pPg != pPage )
3215 if ( pPg )
3216 pPg->RemoveFlyFromPage( pFly );
3217 pPage->AppendFlyToPage( pFly );
3219 ::RegistFlys( pPage, pFly );
3221 else
3223 // #i87493#
3224 if ( pPage != pObj->GetPageFrame() )
3226 pObj->RegisterAtPage(*pPage);
3230 const SwFlyFrame* pFly = pAnch->FindFlyFrame();
3231 if ( pFly &&
3232 pObj->GetDrawObj()->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() &&
3233 pObj->GetDrawObj()->getSdrPageFromSdrObject() )
3235 //#i119945# set pFly's OrdNum to pObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
3236 pObj->DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(),
3237 pObj->GetDrawObj()->GetOrdNumDirect() );
3242 void RegistFlys( SwPageFrame *pPage, const SwLayoutFrame *pLay )
3244 if ( pLay->GetDrawObjs() )
3245 ::lcl_Regist( pPage, pLay );
3246 const SwFrame *pFrame = pLay->Lower();
3247 while ( pFrame )
3249 if ( pFrame->IsLayoutFrame() )
3250 ::RegistFlys( pPage, static_cast<const SwLayoutFrame*>(pFrame) );
3251 else if ( pFrame->GetDrawObjs() )
3252 ::lcl_Regist( pPage, pFrame );
3253 pFrame = pFrame->GetNext();
3257 /// Notify the background based on the difference between old and new rectangle
3258 void Notify( SwFlyFrame *pFly, SwPageFrame *pOld, const SwRect &rOld,
3259 const SwRect* pOldPrt )
3261 const SwRect aFrame( pFly->GetObjRectWithSpaces() );
3262 if ( rOld.Pos() != aFrame.Pos() )
3263 { // changed position, invalidate old and new area
3264 if (rOld.HasArea()
3265 && rOld.Left() + pFly->GetFormat()->GetLRSpace().ResolveLeft({}) < FAR_AWAY)
3267 pFly->NotifyBackground( pOld, rOld, PrepareHint::FlyFrameLeave );
3269 pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
3271 else if ( rOld.SSize() != aFrame.SSize() )
3272 { // changed size, invalidate the area that was left or is now overlapped
3273 // For simplicity, we purposely invalidate a Twip even if not needed.
3275 SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
3276 if( pSh && rOld.HasArea() )
3277 pSh->InvalidateWindows( rOld );
3279 // #i51941# - consider case that fly frame isn't
3280 // registered at the old page <pOld>
3281 SwPageFrame* pPageFrame = pFly->FindPageFrame();
3282 if ( pOld != pPageFrame )
3284 pFly->NotifyBackground( pPageFrame, aFrame, PrepareHint::FlyFrameArrive );
3287 if ( rOld.Left() != aFrame.Left() )
3289 SwRect aTmp( rOld );
3290 aTmp.Union( aFrame );
3291 aTmp.Left( std::min(aFrame.Left(), rOld.Left()) );
3292 aTmp.Right( std::max(aFrame.Left(), rOld.Left()) );
3293 pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
3295 SwTwips nOld = rOld.Right();
3296 SwTwips nNew = aFrame.Right();
3297 if ( nOld != nNew )
3299 SwRect aTmp( rOld );
3300 aTmp.Union( aFrame );
3301 aTmp.Left( std::min(nNew, nOld) );
3302 aTmp.Right( std::max(nNew, nOld) );
3303 pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
3305 if ( rOld.Top() != aFrame.Top() )
3307 SwRect aTmp( rOld );
3308 aTmp.Union( aFrame );
3309 aTmp.Top( std::min(aFrame.Top(), rOld.Top()) );
3310 aTmp.Bottom( std::max(aFrame.Top(), rOld.Top()) );
3311 pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
3313 nOld = rOld.Bottom();
3314 nNew = aFrame.Bottom();
3315 if ( nOld != nNew )
3317 SwRect aTmp( rOld );
3318 aTmp.Union( aFrame );
3319 aTmp.Top( std::min(nNew, nOld) );
3320 aTmp.Bottom( std::max(nNew, nOld) );
3321 pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
3324 else if(pOldPrt && *pOldPrt != pFly->getFramePrintArea())
3326 bool bNotifyBackground(pFly->GetFormat()->GetSurround().IsContour());
3328 if(!bNotifyBackground &&
3329 pFly->IsFlyFreeFrame() &&
3330 static_cast< const SwFlyFreeFrame* >(pFly)->supportsAutoContour())
3332 // RotateFlyFrame3: Also notify for FlyFrames which allow AutoContour
3333 bNotifyBackground = true;
3336 if(bNotifyBackground)
3338 // #i24097#
3339 pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
3344 static void lcl_CheckFlowBack( SwFrame* pFrame, const SwRect &rRect )
3346 SwTwips nBottom = rRect.Bottom();
3347 while( pFrame )
3349 if( pFrame->IsLayoutFrame() )
3351 if( rRect.Overlaps( pFrame->getFrameArea() ) )
3352 lcl_CheckFlowBack( static_cast<SwLayoutFrame*>(pFrame)->Lower(), rRect );
3354 else if( !pFrame->GetNext() && nBottom > pFrame->getFrameArea().Bottom() )
3356 if( pFrame->IsContentFrame() && static_cast<SwContentFrame*>(pFrame)->HasFollow() )
3357 pFrame->InvalidateSize();
3358 else
3359 pFrame->InvalidateNextPos();
3361 pFrame = pFrame->GetNext();
3365 static void lcl_NotifyContent( const SdrObject *pThis, SwContentFrame *pCnt,
3366 const SwRect &rRect, const PrepareHint eHint )
3368 if ( !pCnt->IsTextFrame() )
3369 return;
3371 auto pTextFrame = static_cast<SwTextFrame*>(pCnt);
3372 SwRect aCntPrt( pCnt->getFramePrintArea() );
3373 aCntPrt.Pos() += pCnt->getFrameArea().Pos();
3375 if (eHint == PrepareHint::FlyFrameArrive)
3377 SwTwips nLower = pTextFrame->GetLowerMarginForFlyIntersect();
3378 if (nLower > 0)
3380 aCntPrt.AddBottom(nLower);
3384 if ( eHint == PrepareHint::FlyFrameAttributesChanged )
3386 // #i35640# - use given rectangle <rRect> instead
3387 // of current bound rectangle
3388 if ( aCntPrt.Overlaps( rRect ) )
3389 pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged );
3391 // #i23129# - only invalidate, if the text frame
3392 // printing area overlaps with the given rectangle.
3393 else if ( aCntPrt.Overlaps( rRect ) )
3394 pCnt->Prepare( eHint, static_cast<void*>(&aCntPrt.Intersection_( rRect )) );
3395 if ( !pCnt->GetDrawObjs() )
3396 return;
3398 const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
3399 for (SwAnchoredObject* pObj : rObjs)
3401 if ( auto pFly = pObj->DynCastFlyFrame() )
3403 if ( pFly->IsFlyInContentFrame() )
3405 SwContentFrame *pContent = pFly->ContainsContent();
3406 while ( pContent )
3408 ::lcl_NotifyContent( pThis, pContent, rRect, eHint );
3409 pContent = pContent->GetNextContentFrame();
3416 void Notify_Background( const SdrObject* pObj,
3417 SwPageFrame* pPage,
3418 const SwRect& rRect,
3419 const PrepareHint eHint,
3420 const bool bInva )
3422 // If the frame was positioned correctly for the first time, do not inform the old area
3423 if ( eHint == PrepareHint::FlyFrameLeave && rRect.Top() == FAR_AWAY )
3424 return;
3426 SwLayoutFrame* pArea;
3427 SwFlyFrame *pFlyFrame = nullptr;
3428 SwFrame* pAnchor;
3429 if( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) )
3431 pFlyFrame = const_cast<SwVirtFlyDrawObj*>(pVirtFlyDrawObj)->GetFlyFrame();
3432 pAnchor = pFlyFrame->AnchorFrame();
3434 else
3436 pFlyFrame = nullptr;
3437 if (SwDrawContact* pC = static_cast<SwDrawContact*>(GetUserCall(pObj)))
3438 pAnchor = const_cast<SwFrame*>(pC->GetAnchoredObj(pObj)->GetAnchorFrame());
3439 else
3440 return;
3442 if( PrepareHint::FlyFrameLeave != eHint && pAnchor->IsInFly() )
3443 pArea = pAnchor->FindFlyFrame();
3444 else
3445 pArea = pPage;
3446 SwContentFrame *pCnt = nullptr;
3447 if ( pArea )
3449 if( PrepareHint::FlyFrameArrive != eHint )
3450 lcl_CheckFlowBack( pArea, rRect );
3452 // Only the Flys following this anchor are reacting. Thus, those do not
3453 // need to be processed.
3454 // An exception is LEAVE, since the Fly might come "from above".
3455 // If the anchor is positioned on the previous page, the whole page
3456 // needs to be processed (47722).
3457 // OD 2004-05-13 #i28701# - If the wrapping style has to be considered
3458 // on the object positioning, the complete area has to be processed,
3459 // because content frames before the anchor frame also have to consider
3460 // the object for the text wrapping.
3461 // #i3317# - The complete area has always been
3462 // processed.
3464 pCnt = pArea->ContainsContent();
3467 SwFrame *pLastTab = nullptr;
3469 bool isValidTableBeforeAnchor(false);
3470 while ( pCnt && pArea && pArea->IsAnLower( pCnt ) )
3472 ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
3473 if ( pCnt->IsInTab() )
3475 SwTabFrame *pTab = pCnt->FindTabFrame();
3476 if ( pTab != pLastTab )
3478 pLastTab = pTab;
3479 isValidTableBeforeAnchor = false;
3480 if (PrepareHint::FlyFrameArrive == eHint
3481 && pFlyFrame // TODO: do it for draw objects too?
3482 && pTab->IsFollow() // table starts on previous page?
3483 // "through" means they will actually overlap anyway
3484 && css::text::WrapTextMode_THROUGH != pFlyFrame->GetFormat()->GetSurround().GetSurround()
3485 // if it's anchored in footer it can't move to other page
3486 && !pAnchor->FindFooterOrHeader())
3488 SwFrame * pTmp(pAnchor->GetPrev());
3489 while (pTmp)
3491 if (pTmp == pTab)
3493 // tdf#99460 the table shouldn't be moved by the fly
3494 isValidTableBeforeAnchor = true;
3495 break;
3497 pTmp = pTmp->GetPrev();
3500 // #i40606# - use <GetLastBoundRect()>
3501 // instead of <GetCurrentBoundRect()>, because a recalculation
3502 // of the bounding rectangle isn't intended here.
3503 if (!isValidTableBeforeAnchor
3504 && (pTab->getFrameArea().Overlaps(SwRect(pObj->GetLastBoundRect())) ||
3505 pTab->getFrameArea().Overlaps(rRect)))
3507 if ( !pFlyFrame || !pFlyFrame->IsLowerOf( pTab ) )
3508 pTab->InvalidatePrt();
3511 SwLayoutFrame* pCell = pCnt->GetUpper();
3512 // #i40606# - use <GetLastBoundRect()>
3513 // instead of <GetCurrentBoundRect()>, because a recalculation
3514 // of the bounding rectangle isn't intended here.
3515 if (!isValidTableBeforeAnchor && pCell->IsCellFrame() &&
3516 ( pCell->getFrameArea().Overlaps( SwRect(pObj->GetLastBoundRect()) ) ||
3517 pCell->getFrameArea().Overlaps( rRect ) ) )
3519 const SwFormatVertOrient &rOri = pCell->GetFormat()->GetVertOrient();
3520 if ( text::VertOrientation::NONE != rOri.GetVertOrient() )
3521 pCell->InvalidatePrt();
3524 pCnt = pCnt->GetNextContentFrame();
3526 // #128702# - make code robust
3527 if ( pPage && pPage->GetSortedObjs() )
3529 pObj->GetOrdNum();
3530 const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
3531 for (SwAnchoredObject* pAnchoredObj : rObjs)
3533 if ( pAnchoredObj->DynCastFlyFrame() != nullptr )
3535 if( pAnchoredObj->GetDrawObj() == pObj )
3536 continue;
3537 SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj);
3538 if ( pFly->getFrameArea().Top() == FAR_AWAY )
3539 continue;
3541 if ( !pFlyFrame ||
3542 (!pFly->IsLowerOf( pFlyFrame ) &&
3543 pFly->GetVirtDrawObj()->GetOrdNumDirect() < pObj->GetOrdNumDirect()))
3545 pCnt = pFly->ContainsContent();
3546 while ( pCnt )
3548 ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
3549 pCnt = pCnt->GetNextContentFrame();
3552 if( pFly->IsFlyLayFrame() )
3554 SwFrame* pLower = pFly->Lower();
3555 if( pLower && pLower->IsColumnFrame() &&
3556 pFly->getFrameArea().Bottom() >= rRect.Top() &&
3557 pFly->getFrameArea().Top() <= rRect.Bottom() &&
3558 pFly->getFrameArea().Right() >= rRect.Left() &&
3559 pFly->getFrameArea().Left() <= rRect.Right() )
3561 pFly->InvalidateSize();
3564 // Flys above myself might sidestep if they have an automatic
3565 // alignment. This happens independently of my attributes since
3566 // this might have been changed as well.
3567 else if ( pFly->IsFlyAtContentFrame() &&
3568 pObj->GetOrdNumDirect() <
3569 pFly->GetVirtDrawObj()->GetOrdNumDirect() &&
3570 pFlyFrame && !pFly->IsLowerOf( pFlyFrame ) )
3572 const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient();
3573 if ( text::HoriOrientation::NONE != rH.GetHoriOrient() &&
3574 text::HoriOrientation::CENTER != rH.GetHoriOrient() &&
3575 ( !pFly->IsAutoPos() || text::RelOrientation::CHAR != rH.GetRelationOrient() ) &&
3576 (pFly->getFrameArea().Bottom() >= rRect.Top() &&
3577 pFly->getFrameArea().Top() <= rRect.Bottom()) )
3578 pFly->InvalidatePos();
3583 if ( pFlyFrame && pAnchor->GetUpper() && pAnchor->IsInTab() )//MA_FLY_HEIGHT
3584 pAnchor->GetUpper()->InvalidateSize();
3586 // #i82258# - make code robust
3587 SwViewShell* pSh = nullptr;
3588 if ( bInva && pPage &&
3589 nullptr != (pSh = pPage->getRootFrame()->GetCurrShell()) )
3591 pSh->InvalidateWindows( rRect );
3595 /// Provides the Upper of an anchor in paragraph-bound objects. If the latter
3596 /// is a chained border or a footnote, the "virtual" Upper might be returned.
3597 const SwFrame* GetVirtualUpper( const SwFrame* pFrame, const Point& rPos )
3599 if( pFrame->IsTextFrame() )
3601 pFrame = pFrame->GetUpper();
3602 if( !pFrame->getFrameArea().Contains( rPos ) )
3604 if( pFrame->IsFootnoteFrame() )
3606 const SwFootnoteFrame* pTmp = static_cast<const SwFootnoteFrame*>(pFrame)->GetFollow();
3607 while( pTmp )
3609 if( pTmp->getFrameArea().Contains( rPos ) )
3610 return pTmp;
3611 pTmp = pTmp->GetFollow();
3614 else
3616 SwFlyFrame* pTmp = const_cast<SwFlyFrame*>(pFrame->FindFlyFrame());
3617 while( pTmp )
3619 if( pTmp->getFrameArea().Contains( rPos ) )
3620 return pTmp;
3621 pTmp = pTmp->GetNextLink();
3626 return pFrame;
3629 bool Is_Lower_Of(const SwFrame *pCurrFrame, const SdrObject* pObj)
3631 Point aPos;
3632 const SwFrame* pFrame;
3633 if (const SwVirtFlyDrawObj *pFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(pObj))
3635 const SwFlyFrame* pFly = pFlyDrawObj->GetFlyFrame();
3636 pFrame = pFly->GetAnchorFrame();
3637 aPos = pFly->getFrameArea().Pos();
3639 else
3641 if (SwDrawContact* pC = static_cast<SwDrawContact*>(GetUserCall(pObj)))
3643 pFrame = pC->GetAnchorFrame(pObj);
3644 aPos = pObj->GetCurrentBoundRect().TopLeft();
3646 else
3647 return false;
3649 OSL_ENSURE( pFrame, "8-( Fly is lost in Space." );
3650 pFrame = GetVirtualUpper( pFrame, aPos );
3652 { if ( pFrame == pCurrFrame )
3653 return true;
3654 if( pFrame->IsFlyFrame() )
3656 aPos = pFrame->getFrameArea().Pos();
3657 pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
3659 else
3660 pFrame = pFrame->GetUpper();
3661 } while ( pFrame );
3662 return false;
3665 /// provides the area of a frame in that no Fly from another area can overlap
3666 const SwFrame *FindContext( const SwFrame *pFrame, SwFrameType nAdditionalContextType )
3668 const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont |
3669 SwFrameType::Ftn | SwFrameType::Fly |
3670 SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell |
3671 nAdditionalContextType;
3673 { if ( pFrame->GetType() & nTyp )
3674 break;
3675 pFrame = pFrame->GetUpper();
3676 } while( pFrame );
3677 return pFrame;
3680 bool IsFrameInSameContext( const SwFrame *pInnerFrame, const SwFrame *pFrame )
3682 const SwFrame *pContext = FindContext( pInnerFrame, SwFrameType::None );
3684 const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont |
3685 SwFrameType::Ftn | SwFrameType::Fly |
3686 SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell;
3688 { if ( pFrame->GetType() & nTyp )
3690 if( pFrame == pContext )
3691 return true;
3692 if( pFrame->IsCellFrame() )
3693 return false;
3695 if( pFrame->IsFlyFrame() )
3697 Point aPos( pFrame->getFrameArea().Pos() );
3698 pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
3700 else
3701 pFrame = pFrame->GetUpper();
3702 } while( pFrame );
3704 return false;
3707 static SwTwips lcl_CalcCellRstHeight( SwLayoutFrame *pCell )
3709 SwFrame *pLow = pCell->Lower();
3710 if ( pLow && (pLow->IsContentFrame() || pLow->IsSctFrame()) )
3712 tools::Long nHeight = 0, nFlyAdd = 0;
3715 tools::Long nLow = pLow->getFrameArea().Height();
3716 if( pLow->IsTextFrame() && static_cast<SwTextFrame*>(pLow)->IsUndersized() )
3717 nLow += static_cast<SwTextFrame*>(pLow)->GetParHeight()-pLow->getFramePrintArea().Height();
3718 else if( pLow->IsSctFrame() && static_cast<SwSectionFrame*>(pLow)->IsUndersized() )
3719 nLow += static_cast<SwSectionFrame*>(pLow)->Undersize();
3720 nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLow );
3721 nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
3722 nHeight += nLow;
3723 pLow = pLow->GetNext();
3724 } while ( pLow );
3725 if ( nFlyAdd )
3726 nHeight += nFlyAdd;
3728 // The border cannot be calculated based on PrtArea and Frame, since both can be invalid.
3729 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
3730 const SwBorderAttrs &rAttrs = *aAccess.Get();
3731 nHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
3733 return pCell->getFrameArea().Height() - nHeight;
3735 else
3737 tools::Long nRstHeight = 0;
3738 while (pLow && pLow->IsLayoutFrame())
3740 nRstHeight += ::CalcRowRstHeight(static_cast<SwLayoutFrame*>(pLow));
3741 pLow = pLow->GetNext();
3743 return nRstHeight;
3747 SwTwips CalcRowRstHeight( SwLayoutFrame *pRow )
3749 SwFrame *pLow = pRow->Lower();
3750 if (!(pLow && pLow->IsLayoutFrame()))
3752 return 0;
3754 SwTwips nRstHeight = LONG_MAX;
3755 while (pLow && pLow->IsLayoutFrame())
3757 nRstHeight = std::min(nRstHeight, ::lcl_CalcCellRstHeight(static_cast<SwLayoutFrame*>(pLow)));
3758 pLow = pLow->GetNext();
3760 return nRstHeight;
3763 const SwFrame* FindPage( const SwRect &rRect, const SwFrame *pPage )
3765 if ( !rRect.Overlaps( pPage->getFrameArea() ) )
3767 const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(pPage->GetUpper());
3768 const SwFrame* pTmpPage = pRootFrame ? pRootFrame->GetPageAtPos( rRect.TopLeft(), &rRect.SSize(), true ) : nullptr;
3769 if ( pTmpPage )
3770 pPage = pTmpPage;
3773 return pPage;
3776 namespace {
3778 class SwFrameHolder : private SfxListener
3780 SwFrame* m_pFrame;
3781 bool m_bSet;
3782 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
3783 public:
3784 SwFrameHolder()
3785 : m_pFrame(nullptr)
3786 , m_bSet(false)
3789 void SetFrame( SwFrame* pHold );
3790 SwFrame* GetFrame() { return m_pFrame; }
3791 void Reset();
3792 bool IsSet() const { return m_bSet; }
3797 void SwFrameHolder::SetFrame( SwFrame* pHold )
3799 m_bSet = true;
3800 if (m_pFrame != pHold)
3802 if (m_pFrame)
3803 EndListening(*m_pFrame);
3804 StartListening(*pHold);
3805 m_pFrame = pHold;
3809 void SwFrameHolder::Reset()
3811 if (m_pFrame)
3812 EndListening(*m_pFrame);
3813 m_bSet = false;
3814 m_pFrame = nullptr;
3817 void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
3819 if (rHint.GetId() == SfxHintId::Dying && &rBC == m_pFrame)
3821 m_pFrame = nullptr;
3825 static sal_uInt64 CalcCurrentDist(sal_Int64 nDiffX, sal_Int64 nDiffY)
3827 sal_Int64 nDiffX2, nDiffY2;
3828 if (o3tl::checked_multiply(nDiffX, nDiffX, nDiffX2))
3830 SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: X " << nDiffX);
3831 return std::numeric_limits<sal_uInt64>::max();
3833 if (o3tl::checked_multiply(nDiffY, nDiffY, nDiffY2))
3835 SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: Y " << nDiffY);
3836 return std::numeric_limits<sal_uInt64>::max();
3838 sal_uInt64 ret;
3839 if (o3tl::checked_add<sal_uInt64>(nDiffX2, nDiffY2, ret))
3841 SAL_WARN("sw.pageframe", "CalcCurrentDist overflow: " << nDiffX2 << " + " << nDiffY2);
3842 return std::numeric_limits<sal_uInt64>::max();
3844 return ret;
3847 SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, sw::BroadcastingModify const& rMod,
3848 SwFrameType const nFrameType, SwPosition const*const pPos,
3849 std::pair<Point, bool> const*const pViewPosAndCalcFrame)
3851 SwFrame *pMinFrame = nullptr, *pTmpFrame;
3852 SwFrameHolder aHolder;
3853 SwRect aCalcRect;
3854 bool bClientIterChanged = false;
3856 SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
3857 do {
3858 pMinFrame = nullptr;
3859 aHolder.Reset();
3860 sal_uInt64 nMinDist = 0;
3861 bClientIterChanged = false;
3863 for( pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
3865 if( pTmpFrame->GetType() & nFrameType &&
3866 ( !pLayout || pLayout == pTmpFrame->getRootFrame() ) &&
3867 (!pTmpFrame->IsFlowFrame() ||
3868 !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow() ))
3870 if (pViewPosAndCalcFrame)
3872 // watch for Frame being deleted
3873 if ( pMinFrame )
3874 aHolder.SetFrame( pMinFrame );
3875 else
3876 aHolder.Reset();
3878 if (pViewPosAndCalcFrame->second)
3880 // tdf#108118 prevent recursion
3881 DisableCallbackAction a(*pTmpFrame->getRootFrame());
3882 // - format parent Writer
3883 // fly frame, if it isn't been formatted yet.
3884 // Note: The Writer fly frame could be the frame itself.
3885 SwFlyFrame* pFlyFrame( pTmpFrame->FindFlyFrame() );
3886 if ( pFlyFrame &&
3887 pFlyFrame->getFrameArea().Pos().X() == FAR_AWAY &&
3888 pFlyFrame->getFrameArea().Pos().Y() == FAR_AWAY )
3890 SwObjectFormatter::FormatObj( *pFlyFrame );
3892 pTmpFrame->Calc(pLayout ? pLayout->GetCurrShell()->GetOut() : nullptr);
3895 // aIter.IsChanged checks if the current pTmpFrame has been deleted while
3896 // it is the current iterator
3897 // FrameHolder watches for deletion of the current pMinFrame
3898 if( aIter.IsChanged() || ( aHolder.IsSet() && !aHolder.GetFrame() ) )
3900 // restart iteration
3901 bClientIterChanged = true;
3902 break;
3905 // for Flys go via the parent if the Fly is not yet "formatted"
3906 if (!pViewPosAndCalcFrame->second &&
3907 pTmpFrame->GetType() & SwFrameType::Fly &&
3908 static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame() &&
3909 FAR_AWAY == pTmpFrame->getFrameArea().Pos().getX() &&
3910 FAR_AWAY == pTmpFrame->getFrameArea().Pos().getY() )
3911 aCalcRect = static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame()->getFrameArea();
3912 else
3913 aCalcRect = pTmpFrame->getFrameArea();
3915 if (aCalcRect.Contains(pViewPosAndCalcFrame->first))
3917 pMinFrame = pTmpFrame;
3918 break;
3921 // Point not in rectangle. Compare distances:
3922 const Point aCalcRectCenter = aCalcRect.Center();
3923 const Point aDiff = aCalcRectCenter - pViewPosAndCalcFrame->first;
3924 const sal_uInt64 nCurrentDist = CalcCurrentDist(aDiff.getX(), aDiff.getY());
3925 if ( !pMinFrame || nCurrentDist < nMinDist )
3927 pMinFrame = pTmpFrame;
3928 nMinDist = nCurrentDist;
3931 else
3933 // if no pViewPosAndCalcFrame is provided, take the first one
3934 pMinFrame = pTmpFrame;
3935 break;
3939 } while( bClientIterChanged );
3941 if( pPos && pMinFrame && pMinFrame->IsTextFrame() )
3943 SwTextFrame * pAtPos(static_cast<SwTextFrame*>(pMinFrame)->GetFrameAtPos(*pPos));
3944 if (!pViewPosAndCalcFrame)
3946 return pAtPos;
3948 TextFrameIndex nPos(pAtPos->MapModelToViewPos(*pPos));
3949 SwPageFrame const*const pPage(pAtPos->getRootFrame()->GetPageAtPos(
3950 pViewPosAndCalcFrame->first, nullptr, true));
3951 SwFrame * pOnPage(pAtPos); // if all else fails return first one
3952 ++nPos; // follow field portions are on follow frames that have mnOffset
3953 // already incremented past the field, need to check that index too
3954 while (pAtPos && pAtPos->GetOffset() <= nPos)
3956 if (pAtPos->getFrameArea().Contains(pViewPosAndCalcFrame->first))
3958 return pAtPos;
3960 if (pAtPos->FindPageFrame() == pPage)
3962 pOnPage = pAtPos;
3964 pAtPos = pAtPos->GetFollow();
3966 return pOnPage;
3969 return pMinFrame;
3972 bool IsExtraData( const SwDoc *pDoc )
3974 const SwLineNumberInfo &rInf = pDoc->GetLineNumberInfo();
3975 if (rInf.IsPaintLineNumbers() ||
3976 rInf.IsCountInFlys() ||
3977 (static_cast<sal_Int16>(SwModule::get()->GetRedlineMarkPos()) != text::HoriOrientation::NONE &&
3978 !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()))
3980 return true;
3983 const SwEditShell* pSh = pDoc->GetEditShell();
3984 const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr;
3985 return pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton();
3988 // OD 22.09.2003 #110978#
3989 SwRect SwPageFrame::PrtWithoutHeaderAndFooter() const
3991 SwRect aPrtWithoutHeaderFooter( getFramePrintArea() );
3992 aPrtWithoutHeaderFooter.Pos() += getFrameArea().Pos();
3994 const SwFrame* pLowerFrame = Lower();
3995 while ( pLowerFrame )
3997 // Note: independent on text direction page header and page footer are
3998 // always at top respectively at bottom of the page frame.
3999 if ( pLowerFrame->IsHeaderFrame() )
4001 aPrtWithoutHeaderFooter.AddTop( pLowerFrame->getFrameArea().Height() );
4003 if ( pLowerFrame->IsFooterFrame() )
4005 aPrtWithoutHeaderFooter.AddBottom( - pLowerFrame->getFrameArea().Height() );
4008 pLowerFrame = pLowerFrame->GetNext();
4011 return aPrtWithoutHeaderFooter;
4014 /** method to determine the spacing values of a frame
4016 OD 2004-03-10 #i28701#
4017 OD 2009-08-28 #i102458#
4018 Add output parameter <obIsLineSpacingProportional>
4020 void GetSpacingValuesOfFrame( const SwFrame& rFrame,
4021 SwTwips& onLowerSpacing,
4022 SwTwips& onLineSpacing,
4023 bool& obIsLineSpacingProportional,
4024 bool bIdenticalStyles )
4026 if ( !rFrame.IsFlowFrame() )
4028 onLowerSpacing = 0;
4029 onLineSpacing = 0;
4031 else
4033 const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace();
4034 // check contextual spacing if the style of actual and next paragraphs are identical
4035 if (bIdenticalStyles)
4036 onLowerSpacing = (rULSpace.GetContext() ? 0 : rULSpace.GetLower());
4037 else
4038 onLowerSpacing = rULSpace.GetLower();
4040 onLineSpacing = 0;
4041 obIsLineSpacingProportional = false;
4042 if ( rFrame.IsTextFrame() )
4044 onLineSpacing = static_cast<const SwTextFrame&>(rFrame).GetLineSpace();
4045 obIsLineSpacingProportional =
4046 onLineSpacing != 0 &&
4047 static_cast<const SwTextFrame&>(rFrame).GetLineSpace( true ) == 0;
4050 OSL_ENSURE( onLowerSpacing >= 0 && onLineSpacing >= 0,
4051 "<GetSpacingValuesOfFrame(..)> - spacing values aren't positive!" );
4055 /// get the content of the table cell, skipping content from nested tables
4056 const SwContentFrame* GetCellContent( const SwLayoutFrame& rCell )
4058 const SwContentFrame* pContent = rCell.ContainsContent();
4059 const SwTabFrame* pTab = rCell.FindTabFrame();
4061 while ( pContent && rCell.IsAnLower( pContent ) )
4063 const SwTabFrame* pTmpTab = pContent->FindTabFrame();
4064 if ( pTmpTab != pTab )
4066 SwFrame const*const pTmp = pTmpTab->FindLastContentOrTable();
4067 if (pTmp)
4069 pContent = pTmp->FindNextCnt();
4071 else
4073 pContent = nullptr;
4076 else
4077 break;
4079 return pContent;
4082 SwDeletionChecker::SwDeletionChecker(const SwFrame* pFrame)
4083 : mpFrame( pFrame )
4084 , mpRegIn( pFrame
4085 ? pFrame->IsTextFrame()
4086 // sw_redlinehide: GetDep() may be a member of SwTextFrame!
4087 ? static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()
4088 : const_cast<SwFrame*>(pFrame)->GetDep()
4089 : nullptr )
4093 /// Can be used to check if a frame has been deleted
4094 bool SwDeletionChecker::HasBeenDeleted() const
4096 if ( !mpFrame || !mpRegIn )
4097 return false;
4099 SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*mpRegIn);
4100 SwFrame* pLast = aIter.First();
4101 while ( pLast )
4103 if ( pLast == mpFrame )
4104 return false;
4105 pLast = aIter.Next();
4108 return true;
4111 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */