Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / layout / pagechg.cxx
blob6f11380fc0308eccb39a940c36326959157ac043
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 <comphelper/lok.hxx>
23 #include <ndole.hxx>
24 #include <sal/log.hxx>
25 #include <osl/diagnose.h>
26 #include <svl/itemiter.hxx>
27 #include <fmtfsize.hxx>
28 #include <fmthdft.hxx>
29 #include <fmtclds.hxx>
30 #include <fmtpdsc.hxx>
31 #include <fmtornt.hxx>
32 #include <fmtsrnd.hxx>
33 #include <ftninfo.hxx>
34 #include <frmtool.hxx>
35 #include <tgrditem.hxx>
36 #include <viewopt.hxx>
37 #include <docsh.hxx>
38 #include <wrtsh.hxx>
39 #include <view.hxx>
40 #include <edtwin.hxx>
41 #include <frameformats.hxx>
43 #include <viewimp.hxx>
44 #include <pagefrm.hxx>
45 #include <rootfrm.hxx>
46 #include <IDocumentDrawModelAccess.hxx>
47 #include <IDocumentSettingAccess.hxx>
48 #include <IDocumentFieldsAccess.hxx>
49 #include <dcontact.hxx>
50 #include <hints.hxx>
51 #include <FrameControlsManager.hxx>
53 #include <ftnidx.hxx>
54 #include <bodyfrm.hxx>
55 #include <ftnfrm.hxx>
56 #include <tabfrm.hxx>
57 #include <txtfrm.hxx>
58 #include <notxtfrm.hxx>
59 #include <layact.hxx>
60 #include <flyfrms.hxx>
61 #include <htmltbl.hxx>
62 #include <pagedesc.hxx>
63 #include <editeng/frmdiritem.hxx>
64 #include <sortedobjs.hxx>
65 #include <calbck.hxx>
66 #include <txtfly.hxx>
68 using namespace ::com::sun::star;
70 SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
71 SwLayoutFrame( pFormat, pSib )
73 mnFrameType = SwFrameType::Body;
76 void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
78 // Formatting of the body is too simple, thus, it gets its own format method.
79 // Borders etc. are not taken into account here.
80 // Width is taken from the PrtArea of the Upper. Height is the height of the
81 // PrtArea of the Upper minus any neighbors (for robustness).
82 // The PrtArea has always the size of the frame.
84 if ( !isFrameAreaSizeValid() )
86 SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
87 SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
88 const SwFrame *pFrame = GetUpper()->Lower();
91 if ( pFrame != this )
93 if( pFrame->IsVertical() )
94 nWidth -= pFrame->getFrameArea().Width();
95 else
96 nHeight -= pFrame->getFrameArea().Height();
98 pFrame = pFrame->GetNext();
99 } while ( pFrame );
101 if ( nHeight < 0 )
103 nHeight = 0;
106 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
107 aFrm.Height( nHeight );
109 if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
111 aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
114 aFrm.Width( nWidth );
117 bool bNoGrid = true;
118 if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
120 SwTextGridItem const*const pGrid(
121 GetGridItem(static_cast<SwPageFrame*>(GetUpper())));
122 if( pGrid )
124 bNoGrid = false;
125 tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
126 SwRectFnSet aRectFnSet(this);
127 tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
128 tools::Long nBorder = 0;
129 if( GRID_LINES_CHARS == pGrid->GetGridType() )
131 //for textgrid refactor
132 SwDoc *pDoc = GetFormat()->GetDoc();
133 nBorder = nSize % (GetGridWidth(*pGrid, *pDoc));
134 nSize -= nBorder;
135 nBorder /= 2;
138 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
139 aRectFnSet.SetPosX( aPrt, nBorder );
140 aRectFnSet.SetWidth( aPrt, nSize );
142 // Height of body frame:
143 nBorder = aRectFnSet.GetHeight(getFrameArea());
145 // Number of possible lines in area of body frame:
146 tools::Long nNumberOfLines = nBorder / nSum;
147 if( nNumberOfLines > pGrid->GetLines() )
148 nNumberOfLines = pGrid->GetLines();
150 // Space required for nNumberOfLines lines:
151 nSize = nNumberOfLines * nSum;
152 nBorder -= nSize;
153 nBorder /= 2;
155 // #i21774# Footnotes and centering the grid does not work together:
156 const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc()->
157 GetFootnoteIdxs().empty();
159 aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
160 aRectFnSet.SetHeight( aPrt, nSize );
164 if( bNoGrid )
166 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
167 aPrt.Pos().setX(0);
168 aPrt.Pos().setY(0);
169 aPrt.Height( getFrameArea().Height() );
170 aPrt.Width( getFrameArea().Width() );
173 setFrameAreaSizeValid(true);
174 setFramePrintAreaValid(true);
177 void SwBodyFrame::dumpAsXml(xmlTextWriterPtr writer) const
179 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("body"));
180 dumpAsXmlAttributes(writer);
182 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
183 dumpInfosAsXml(writer);
184 (void)xmlTextWriterEndElement(writer);
185 dumpChildrenAsXml(writer);
187 (void)xmlTextWriterEndElement(writer);
190 SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
191 SwFootnoteBossFrame( pFormat, pSib ),
192 m_pDesc( pPgDsc ),
193 m_nPhyPageNum( 0 )
195 SetDerivedVert( false );
196 SetDerivedR2L( false );
197 if( m_pDesc )
199 m_bHasGrid = true;
200 SwTextGridItem const*const pGrid(GetGridItem(this));
201 if( !pGrid )
202 m_bHasGrid = false;
204 else
205 m_bHasGrid = false;
206 SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
207 pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
208 mnFrameType = SwFrameType::Page;
209 m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
210 m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;
212 SwViewShell *pSh = getRootFrame()->GetCurrShell();
213 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
214 vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
217 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
219 if ( bBrowseMode )
221 aFrm.Height( 0 );
222 tools::Long nWidth = pSh->VisArea().Width();
224 if ( !nWidth )
226 nWidth = 5000; // changes anyway
229 aFrm.Width ( nWidth );
231 else
233 aFrm.SSize( pFormat->GetFrameSize().GetSize() );
237 // create and insert body area if it is not a blank page
238 SwDoc* pDoc(pFormat->GetDoc());
239 m_bEmptyPage = (pFormat == pDoc->GetEmptyPageFormat());
241 if(m_bEmptyPage)
243 return;
246 Calc(pRenderContext); // so that the PrtArea is correct
247 SwBodyFrame *pBodyFrame = new SwBodyFrame( pDoc->GetDfltFrameFormat(), this );
248 pBodyFrame->ChgSize( getFramePrintArea().SSize() );
249 pBodyFrame->Paste( this );
250 pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
251 pBodyFrame->InvalidatePos();
253 if ( bBrowseMode )
254 InvalidateSize_();
256 // insert header/footer,, but only if active.
257 if ( pFormat->GetHeader().IsActive() )
258 PrepareHeader();
259 if ( pFormat->GetFooter().IsActive() )
260 PrepareFooter();
262 const SwFormatCol &rCol = pFormat->GetCol();
263 if ( rCol.GetNumCols() > 1 )
265 const SwFormatCol aOld; //ChgColumns() needs an old value
266 pBodyFrame->ChgColumns( aOld, rCol );
271 void SwPageFrame::DestroyImpl()
273 // Cleanup the header-footer controls in all SwEditWins
274 SwViewShell* pSh = getRootFrame()->GetCurrShell();
275 if (pSh)
277 for (SwViewShell& rSh : pSh->GetRingContainer())
279 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( &rSh );
280 if ( pWrtSh )
282 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
283 rEditWin.GetFrameControlsManager( ).RemoveControls( this );
288 // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
289 if (m_pSortedObjs)
291 // Objects can be anchored at pages that are before their anchors (why ever...).
292 // In such cases, we would access already freed memory.
293 for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
295 pAnchoredObj->SetPageFrame( nullptr );
297 m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
300 // prevent access to destroyed pages
301 SwDoc *pDoc = GetFormat() ? GetFormat()->GetDoc() : nullptr;
302 if( pDoc && !pDoc->IsInDtor() )
304 if ( pSh )
306 SwViewShellImp *pImp = pSh->Imp();
307 pImp->SetFirstVisPageInvalid();
308 if ( pImp->IsAction() )
309 pImp->GetLayAction().SetAgain(true);
310 // #i9719# - retouche area of page
311 // including border and shadow area.
312 const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
313 SwRect aRetoucheRect;
314 SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
315 pSh->AddPaintRect( aRetoucheRect );
319 SwFootnoteBossFrame::DestroyImpl();
322 SwPageFrame::~SwPageFrame()
326 void SwPageFrame::CheckGrid( bool bInvalidate )
328 bool bOld = m_bHasGrid;
329 m_bHasGrid = true;
330 SwTextGridItem const*const pGrid(GetGridItem(this));
331 m_bHasGrid = nullptr != pGrid;
332 if( !(bInvalidate || bOld != m_bHasGrid) )
333 return;
335 SwLayoutFrame* pBody = FindBodyCont();
336 if( pBody )
338 pBody->InvalidatePrt();
339 SwContentFrame* pFrame = pBody->ContainsContent();
340 while( pBody->IsAnLower( pFrame ) )
342 static_cast<SwTextFrame*>(pFrame)->Prepare();
343 pFrame = pFrame->GetNextContentFrame();
346 SetCompletePaint();
349 void SwPageFrame::CheckDirection( bool bVert )
351 SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
352 if( bVert )
354 if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
356 mbVertLR = false;
357 mbVertical = false;
359 else
361 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
362 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
364 mbVertLR = false;
365 mbVertical = false;
367 else
369 mbVertical = true;
371 if(SvxFrameDirection::Vertical_RL_TB == nDir)
372 mbVertLR = false;
373 else if(SvxFrameDirection::Vertical_LR_TB==nDir)
374 mbVertLR = true;
378 mbInvalidVert = false;
380 else
382 if( SvxFrameDirection::Horizontal_RL_TB == nDir )
383 mbRightToLeft = true;
384 else
385 mbRightToLeft = false;
386 mbInvalidR2L = false;
390 /// create specific Flys for this page and format generic content
391 static void lcl_FormatLay( SwLayoutFrame *pLay )
393 vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
394 // format all LayoutFrames - no tables, Flys etc.
396 SwFrame *pTmp = pLay->Lower();
397 // first the low-level ones
398 while ( pTmp )
400 const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
401 | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont
402 | SwFrameType::Ftn | SwFrameType::Body;
403 if ( pTmp->GetType() & nTypes )
404 ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
405 pTmp = pTmp->GetNext();
407 pLay->Calc(pRenderContext);
410 /// Create Flys or register draw objects
411 static void lcl_MakeObjs(const sw::FrameFormats<sw::SpzFrameFormat*>& rSpzs, SwPageFrame* pPage)
413 // formats are in the special table of the document
414 for(size_t i = 0; i < rSpzs.size(); ++i )
416 auto pSpz = rSpzs[i];
417 const SwFormatAnchor &rAnch = pSpz->GetAnchor();
418 if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() )
420 if( rAnch.GetAnchorNode() )
422 if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
424 SwFormatAnchor aAnch( rAnch );
425 aAnch.SetAnchor( nullptr );
426 pSpz->SetFormatAttr( aAnch );
428 else
429 continue;
432 // is it a border or a SdrObject?
433 bool bSdrObj = RES_DRAWFRMFMT == pSpz->Which();
434 SdrObject *pSdrObj = nullptr;
435 if ( bSdrObj && nullptr == (pSdrObj = pSpz->FindSdrObject()) )
437 OSL_FAIL( "DrawObject not found." );
438 pSpz->GetDoc()->DelFrameFormat( pSpz );
439 --i;
440 continue;
442 // The object might be anchored to another page, e.g. when inserting
443 // a new page due to a page descriptor change. In such cases, the
444 // object needs to be moved.
445 // In some cases the object is already anchored to the correct page.
446 // This will be handled here and does not need to be coded extra.
447 SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
448 if ( bSdrObj )
450 // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
451 SwDrawContact *pContact =
452 static_cast<SwDrawContact*>(::GetUserCall(pSdrObj));
453 if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
455 if ( pContact )
457 pDrawVirtObj->RemoveFromWriterLayout();
458 pDrawVirtObj->RemoveFromDrawingPage();
459 pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
462 else
464 if ( pContact->GetAnchorFrame() )
465 pContact->DisconnectFromLayout( false );
466 pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
469 else
471 SwIterator<SwFlyFrame,SwFormat> aIter( *pSpz );
472 SwFlyFrame *pFly = aIter.First();
473 if ( pFly)
475 if( pFly->GetAnchorFrame() )
476 pFly->AnchorFrame()->RemoveFly( pFly );
478 else
479 pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pSpz), pPg, pPg );
480 pPg->AppendFly( pFly );
481 ::RegistFlys( pPg, pFly );
487 void SwPageFrame::PreparePage( bool bFootnote )
489 SetFootnotePage( bFootnote );
491 // #i82258#
492 // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
493 // the side effect, that the content of page header and footer are formatted.
494 // For this formatting it is needed that the anchored objects are registered
495 // at the <SwPageFrame> instance.
496 // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
497 ::RegistFlys( this, this );
499 if ( Lower() )
501 ::lcl_FormatLay( this );
504 // Flys and draw objects that are still attached to the document.
505 // Footnote pages do not have page-bound Flys!
506 // There might be Flys or draw objects that want to be placed on
507 // empty pages, however, the empty pages ignore that and the following
508 // pages take care of them.
509 if ( !bFootnote && !IsEmptyPage() )
511 SwDoc *pDoc = GetFormat()->GetDoc();
513 if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
514 lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
515 lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), this );
519 void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
521 if(typeid(sw::PageFootnoteHint) == typeid(rHint))
523 // currently the savest way:
524 static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
525 SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
526 if(!GetMaxFootnoteHeight())
527 SetMaxFootnoteHeight(LONG_MAX);
528 SetColMaxFootnoteHeight();
529 // here, the page might be destroyed:
530 static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
532 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
534 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
535 if(auto pSh = getRootFrame()->GetCurrShell())
536 pSh->SetFirstVisPageInvalid();
538 SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
539 if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
541 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
542 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
543 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
544 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
545 const SfxPoolItem* pOItem = aOIter.GetCurItem();
546 const SfxPoolItem* pNItem = aNIter.GetCurItem();
547 SwAttrSetChg aOldSet(rOldSetChg);
548 SwAttrSetChg aNewSet(rNewSetChg);
551 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
552 pOItem = aOIter.NextItem();
553 pNItem = aNIter.NextItem();
554 } while(pNItem);
555 if(aOldSet.Count() || aNewSet.Count())
556 SwLayoutFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
558 else
559 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
561 if (eInvFlags == SwPageFrameInvFlags::NONE)
562 return;
564 InvalidatePage( this );
565 if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
566 InvalidatePrt_();
567 if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
568 SetCompletePaint();
569 if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
570 GetNext()->InvalidatePos();
571 if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
572 PrepareHeader();
573 if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
574 PrepareFooter();
575 if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
576 CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
577 } else
578 SwFrame::SwClientNotify(rModify, rHint);
581 void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
582 SwPageFrameInvFlags &rInvFlags,
583 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
585 bool bClear = true;
586 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
587 switch( nWhich )
589 case RES_FMT_CHG:
591 // state of m_bEmptyPage needs to be determined newly
592 const bool bNewState(GetFormat() == GetFormat()->GetDoc()->GetEmptyPageFormat());
594 if(m_bEmptyPage != bNewState)
596 // copy new state
597 m_bEmptyPage = bNewState;
599 if(nullptr == GetLower())
601 // if we were an empty page before there is not yet a BodyArea in the
602 // form of a SwBodyFrame, see constructor
603 SwViewShell* pSh(getRootFrame()->GetCurrShell());
604 vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
605 Calc(pRenderContext); // so that the PrtArea is correct
606 SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
607 pBodyFrame->ChgSize(getFramePrintArea().SSize());
608 pBodyFrame->Paste(this);
609 pBodyFrame->InvalidatePos();
613 // If the frame format is changed, several things might also change:
614 // 1. columns:
615 assert(pOld && pNew); //FMT_CHG Missing Format
616 const SwFormat *const pOldFormat = static_cast<const SwFormatChg*>(pOld)->pChangedFormat;
617 const SwFormat *const pNewFormat = static_cast<const SwFormatChg*>(pNew)->pChangedFormat;
618 assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
619 const SwFormatCol &rOldCol = pOldFormat->GetCol();
620 const SwFormatCol &rNewCol = pNewFormat->GetCol();
621 if( rOldCol != rNewCol )
623 SwLayoutFrame *pB = FindBodyCont();
624 assert(pB && "Page without Body.");
625 pB->ChgColumns( rOldCol, rNewCol );
626 rInvFlags |= SwPageFrameInvFlags::CheckGrid;
629 // 2. header and footer:
630 const SwFormatHeader &rOldH = pOldFormat->GetHeader();
631 const SwFormatHeader &rNewH = pNewFormat->GetHeader();
632 if( rOldH != rNewH )
633 rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
635 const SwFormatFooter &rOldF = pOldFormat->GetFooter();
636 const SwFormatFooter &rNewF = pNewFormat->GetFooter();
637 if( rOldF != rNewF )
638 rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
639 CheckDirChange();
641 [[fallthrough]];
643 case RES_FRM_SIZE:
645 const SwRect aOldPageFrameRect( getFrameArea() );
646 SwViewShell *pSh = getRootFrame()->GetCurrShell();
647 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
649 setFrameAreaSizeValid(false);
650 // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
651 // Calculation of the page is not necessary, because its size is
652 // invalidated here and further invalidation is done in the
653 // calling method <SwPageFrame::Modify(..)> and probably by calling
654 // <SwLayoutFrame::SwClientNotify(..)> at the end.
655 // It can also causes inconsistences, because the lowers are
656 // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
657 // a next page is called. This is performed on the switch to the
658 // online layout.
659 //MakeAll();
661 else if (pNew)
663 const SwFormatFrameSize &rSz = nWhich == RES_FMT_CHG ?
664 static_cast<const SwFormatChg*>(pNew)->pChangedFormat->GetFrameSize() :
665 static_cast<const SwFormatFrameSize&>(*pNew);
668 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
669 aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
670 aFrm.Width ( std::max( rSz.GetWidth(), tools::Long(MINLAY) ) );
673 if ( GetUpper() )
675 static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
678 // cleanup Window
679 if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
681 // #i9719# - consider border and shadow of
682 // page frame for determine 'old' rectangle - it's used for invalidating.
683 const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
684 SwRect aOldRectWithBorderAndShadow;
685 SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
686 IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
687 pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
689 rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
690 if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
691 rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
693 break;
695 case RES_COL:
696 assert(pOld && pNew); //COL Missing Format
697 if (pOld && pNew)
699 SwLayoutFrame *pB = FindBodyCont();
700 assert(pB); //page without body
701 pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
702 rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
704 break;
706 case RES_HEADER:
707 rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
708 break;
710 case RES_FOOTER:
711 rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
712 break;
713 case RES_TEXTGRID:
714 rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
715 break;
716 case RES_FRAMEDIR :
717 CheckDirChange();
718 break;
720 default:
721 bClear = false;
723 if ( !bClear )
724 return;
726 if ( pOldSet || pNewSet )
728 if ( pOldSet )
729 pOldSet->ClearItem( nWhich );
730 if ( pNewSet )
731 pNewSet->ClearItem( nWhich );
733 else
735 SwModify aMod;
736 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
740 /// get information from Modify
741 bool SwPageFrame::GetInfo( SfxPoolItem & rInfo ) const
743 if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
745 // a page frame exists, so use this one
746 return false;
748 return true; // continue searching
751 void SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
753 m_pDesc = pNew;
754 if ( pFormat )
755 SetFrameFormat( pFormat );
758 /* determine the right PageDesc:
759 * 0. from the document for footnote and endnote pages
760 * 1. from the first BodyContent below a page
761 * 2. from PageDesc of the predecessor page
762 * 3. from PageDesc of the previous page if blank page
763 * 3.1 from PageDesc of the next page if no predecessor exists
764 * 4. default PageDesc
765 * 5. In BrowseMode use the first paragraph or default PageDesc.
767 SwPageDesc *SwPageFrame::FindPageDesc()
769 // 0.
770 if ( IsFootnotePage() )
772 SwDoc *pDoc = GetFormat()->GetDoc();
773 if ( IsEndNotePage() )
774 return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
775 else
776 return pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
779 SwPageDesc *pRet = nullptr;
781 //5.
782 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
783 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
785 SwContentFrame *pFrame = GetUpper()->ContainsContent();
786 while (pFrame && !pFrame->IsInDocBody())
787 pFrame = pFrame->GetNextContentFrame();
788 if (pFrame)
790 SwFrame *pFlow = pFrame;
791 if ( pFlow->IsInTab() )
792 pFlow = pFlow->FindTabFrame();
793 pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
795 if ( !pRet )
796 pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
797 return pRet;
800 SwFrame *pFlow = FindFirstBodyContent();
801 if ( pFlow && pFlow->IsInTab() )
802 pFlow = pFlow->FindTabFrame();
804 //1.
805 if ( pFlow )
807 SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
808 if ( !pTmp->IsFollow() )
809 pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
812 //3. and 3.1
813 if ( !pRet && IsEmptyPage() )
814 // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
815 // the same page description as its prev, just like after construction
816 // of the empty page.
817 pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
818 GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;
820 //2.
821 if ( !pRet )
822 pRet = GetPrev() ?
823 static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;
825 //4.
826 if ( !pRet )
827 pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
829 OSL_ENSURE( pRet, "could not find page descriptor." );
830 return pRet;
833 // Notify if the RootFrame changes its size
834 void AdjustSizeChgNotify( SwRootFrame *pRoot )
836 const bool bOld = pRoot->IsSuperfluous();
837 pRoot->mbCheckSuperfluous = false;
838 if ( pRoot->GetCurrShell() )
840 for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
842 if( pRoot == rSh.GetLayout() )
844 rSh.SizeChgNotify();
845 if ( rSh.Imp() )
846 rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
850 pRoot->mbCheckSuperfluous = bOld;
853 inline void SetLastPage( SwPageFrame *pPage )
855 static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
858 void SwPageFrame::Cut()
860 SwViewShell *pSh = getRootFrame()->GetCurrShell();
861 if ( !IsEmptyPage() )
863 if ( GetNext() )
864 GetNext()->InvalidatePos();
866 // move Flys whose anchor is on a different page (draw objects are not relevant here)
867 if ( GetSortedObjs() )
869 size_t i = 0;
870 while ( GetSortedObjs() && i < GetSortedObjs()->size() )
872 // #i28701#
873 SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];
875 if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
877 SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
878 pFly->AnchorFrame()->FindPageFrame() : nullptr;
879 if ( pAnchPage && (pAnchPage != this) )
881 MoveFly( pFly, pAnchPage );
882 pFly->InvalidateSize();
883 pFly->InvalidatePos_();
884 // Do not increment index, in this case
885 continue;
888 ++i;
891 // cleanup Window
892 if ( pSh && pSh->GetWin() )
893 pSh->InvalidateWindows( getFrameArea() );
896 // decrease the root's page number
897 static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
898 SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
899 if ( pPg )
901 while ( pPg )
903 --pPg->m_nPhyPageNum;
904 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
907 else
908 ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );
910 SwFrame* pRootFrame = GetUpper();
912 // cut all connections
913 RemoveFromLayout();
915 if ( pRootFrame )
916 static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
919 void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
921 OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
922 OSL_ENSURE( pParent, "No parent for Paste()." );
923 OSL_ENSURE( pParent != this, "I'm my own parent." );
924 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
925 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
926 "I am still registered somewhere." );
928 // insert into tree structure
929 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
931 // increase the root's page number
932 static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
933 if( GetPrev() )
934 SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
935 else
936 SetPhyPageNum( 1 );
937 SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
938 if ( pPg )
940 while ( pPg )
942 ++pPg->m_nPhyPageNum;
943 pPg->InvalidatePos_();
944 pPg->InvalidateLayout();
945 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
948 else
949 ::SetLastPage( this );
951 if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
952 InvalidateSize_();
954 InvalidatePos();
956 SwViewShell *pSh = getRootFrame()->GetCurrShell();
957 if ( pSh )
958 pSh->SetFirstVisPageInvalid();
960 getRootFrame()->CheckViewLayout( nullptr, nullptr );
963 static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
965 pFrame->Prepare( PrepareHint::Register );
966 if( !pFrame->GetDrawObjs() )
967 return;
969 for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
971 // #i28701#
972 if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
974 SwContentFrame *pCnt = pFly->ContainsContent();
975 while ( pCnt )
977 lcl_PrepFlyInCntRegister( pCnt );
978 pCnt = pCnt->GetNextContentFrame();
984 void SwPageFrame::PrepareRegisterChg()
986 SwContentFrame *pFrame = FindFirstBodyContent();
987 while( pFrame )
989 lcl_PrepFlyInCntRegister( pFrame );
990 pFrame = pFrame->GetNextContentFrame();
991 if( !IsAnLower( pFrame ) )
992 break;
994 if( !GetSortedObjs() )
995 return;
997 for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
999 // #i28701#
1000 if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
1002 pFrame = pFly->ContainsContent();
1003 while ( pFrame )
1005 ::lcl_PrepFlyInCntRegister( pFrame );
1006 pFrame = pFrame->GetNextContentFrame();
1012 namespace sw {
1014 /// check if there's content on the page that requires it to exist
1015 bool IsPageFrameEmpty(SwPageFrame const& rPage)
1017 bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
1018 if (bExistEssentialObjs)
1020 // Only because the page has Flys does not mean that it is needed. If all Flys are
1021 // attached to generic content it is also superfluous (checking DocBody should be enough)
1022 // OD 19.06.2003 - consider that drawing objects in
1023 // header/footer are supported now.
1024 bool bOnlySuperfluousObjs = true;
1025 SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
1026 for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
1028 // #i28701#
1029 SwAnchoredObject* pAnchoredObj = rObjs[i];
1030 // do not consider hidden objects
1031 if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
1032 pAnchoredObj->GetDrawObj()->GetLayer() ) &&
1033 !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
1035 bOnlySuperfluousObjs = false;
1038 bExistEssentialObjs = !bOnlySuperfluousObjs;
1041 // optimization: check first if essential objects exist.
1042 const SwLayoutFrame* pBody = nullptr;
1043 if ( bExistEssentialObjs ||
1044 rPage.FindFootnoteCont() ||
1045 (nullptr != (pBody = rPage.FindBodyCont()) &&
1046 ( pBody->ContainsContent() ||
1047 // check for section frames that are being formatted on the stack
1048 rPage.ContainsDeleteForbiddenLayFrame() ||
1049 // #i47580#
1050 // Do not delete page if there's an empty tabframe
1051 // left. I think it might be correct to use ContainsAny()
1052 // instead of ContainsContent() to cover the empty-table-case,
1053 // but I'm not fully sure, since ContainsAny() also returns
1054 // SectionFrames. Therefore I prefer to do it the safe way:
1055 ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
1057 return false;
1059 else
1061 return true;
1065 } // namespace sw
1067 //FIXME: provide missing documentation
1068 /** Check all pages (starting from the given one) if they use the appropriate frame format.
1070 * If "wrong" pages are found, try to fix this as simple as possible.
1072 * Also delete pages that don't have content on them.
1074 * @param pStart the page from where to start searching
1075 * @param bNotifyFields
1076 * @param ppPrev
1078 void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
1080 SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
1081 assert(pStart && "no starting page.");
1083 SwViewShell *pSh = pStart->getRootFrame()->GetCurrShell();
1084 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
1086 if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1088 pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
1089 SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
1090 << pStart->GetPhyPageNum() << ")" );
1091 return;
1094 // For the update of page numbering fields, nDocPos provides
1095 // the page position from where invalidation should start.
1096 SwTwips nDocPos = LONG_MAX;
1098 SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
1099 SwDoc* pDoc = pStart->GetFormat()->GetDoc();
1100 const bool bFootnotes = !pDoc->GetFootnoteIdxs().empty();
1102 SwPageFrame *pPage = pStart;
1103 if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
1104 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1105 while ( pPage )
1107 SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1108 SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
1110 SwPageDesc *pDesc = pPage->FindPageDesc();
1111 /// page is intentionally empty page
1112 bool bIsEmpty = pPage->IsEmptyPage();
1113 // false for intentionally empty pages, they need additional check
1114 bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
1115 bool bIsOdd = pPage->OnRightPage();
1116 bool bWantOdd = pPage->WannaRightPage();
1117 bool bFirst = pPage->OnFirstPage();
1118 SwFrameFormat *pFormatWish = bWantOdd
1119 ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);
1121 if ( bIsOdd != bWantOdd ||
1122 pDesc != pPage->GetPageDesc() || // wrong Desc
1123 ( pFormatWish != pPage->GetFormat() && // wrong format and
1124 ( !bIsEmpty || pFormatWish ) // not blank /empty
1128 // Updating a page might take a while, so check the WaitCursor
1129 if( pImp )
1130 pImp->CheckWaitCursor();
1132 // invalidate the field, starting from here
1133 if ( nDocPos == LONG_MAX )
1134 nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();
1136 // Cases:
1137 // 1. Empty page should be "normal" page -> remove empty page and take next one
1138 // 2. Empty page should have different descriptor -> change
1139 // 3. Normal page should be empty -> insert empty page if previous page
1140 // is not empty, otherwise see (6).
1141 // 4. Normal page should have different descriptor -> change
1142 // 5. Normal page should have different format -> change
1143 // 6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc
1145 if ( bIsEmpty && ( pFormatWish || //1.
1146 ( !bWantOdd && !pPrevPage ) ) )
1148 // Check all cases for the next page, so we don't oscillate empty pages
1149 // Skip case 1 and 2, as we require a non-empty next page to save the empty page
1150 // Case 3 is the one we actually want to predict and skip
1151 // We can skip the empty check of case 3, as we just work on an existing next page
1152 bool bNextWantOdd;
1153 SwPageDesc *pNextDesc;
1154 if ( pNextPage && !pNextPage->IsEmptyPage() && //3.
1155 pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
1156 pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) ) //4.
1158 bool bNextFirst = pNextPage->OnFirstPage();
1159 SwFrameFormat *pNextFormatWish = bNextWantOdd ? //5.
1160 pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
1161 if ( !pNextFormatWish ) // 6.
1162 pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
1163 if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
1165 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1166 << " c: 1+3 - skip next page of p: " << pPage );
1167 if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
1168 pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
1169 // We can skip the next page, as all checks were already done!
1170 pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
1171 continue;
1175 pPage->Cut();
1176 bool bUpdatePrev = false;
1177 if (ppPrev && *ppPrev == pPage)
1178 bUpdatePrev = true;
1179 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1180 << " c: 1 - destroy p: " << pPage );
1181 SwFrame::DestroyFrame(pPage);
1182 if ( pStart == pPage )
1183 pStart = pNextPage;
1184 pPage = pNextPage;
1185 if (bUpdatePrev)
1186 *ppPrev = pNextPage;
1187 continue;
1189 else if ( bIsEmpty && !pFormatWish && //2.
1190 pDesc != pPage->GetPageDesc() )
1192 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1193 << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
1194 pPage->SetPageDesc( pDesc, nullptr );
1196 else if ( !bIsEmpty && //3.
1197 bIsOdd != bWantOdd &&
1198 ( ( !pPrevPage && !bWantOdd ) ||
1199 ( pPrevPage && !pPrevPage->IsEmptyPage() )
1203 if ( pPrevPage )
1204 pDesc = pPrevPage->GetPageDesc();
1205 SwPageFrame *pTmp = new SwPageFrame( pDoc->GetEmptyPageFormat(), pRoot, pDesc );
1206 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1207 << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
1208 pTmp->Paste( pRoot, pPage );
1209 pTmp->PreparePage( false );
1210 pPage = pTmp;
1211 isPageFrameEmpty = false; // don't delete it right away!
1213 else if ( pPage->GetPageDesc() != pDesc ) //4.
1215 SwPageDesc *pOld = pPage->GetPageDesc();
1216 pPage->SetPageDesc( pDesc, pFormatWish );
1217 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1218 << " c: 4 - set desc + format p: " << pPage
1219 << " d: " << pDesc << " f: " << pFormatWish );
1220 if ( bFootnotes )
1222 // If specific values of the FootnoteInfo are changed, something has to happen.
1223 // We try to limit the damage...
1224 // If the page has no FootnoteCont it might be problematic.
1225 // Let's hope that invalidation is enough.
1226 SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
1227 if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
1228 pCont->InvalidateAll_();
1231 else if ( pFormatWish && pPage->GetFormat() != pFormatWish ) //5.
1233 pPage->SetFrameFormat( pFormatWish );
1234 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1235 << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
1237 else if ( !pFormatWish ) //6.
1239 // get format with inverted logic
1240 pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
1241 if ( pFormatWish && pPage->GetFormat() != pFormatWish )
1243 pPage->SetFrameFormat( pFormatWish );
1244 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1245 << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
1248 #if OSL_DEBUG_LEVEL > 0
1249 else
1251 OSL_FAIL( "CheckPageDescs, missing solution" );
1253 #endif
1255 assert(!bIsEmpty || !isPageFrameEmpty);
1256 const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
1257 if (bWantRemovePage && !pPage->IsDeleteForbidden())
1259 // It also might be that an empty page is not needed at all.
1260 // However, the algorithm above cannot determine that. It is not needed if the following
1261 // page can live without it. Do obtain that information, we need to dig deeper...
1262 SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
1263 if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
1265 // The following page can find a FrameFormat or has no successor -> empty page not needed
1266 SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
1267 if (isPageFrameEmpty && pPage->GetPrev())
1268 { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
1269 pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
1271 pPage->Cut();
1272 bool bUpdatePrev = false;
1273 if (ppPrev && *ppPrev == pPage)
1274 bUpdatePrev = true;
1275 SwFrame::DestroyFrame(pPage);
1276 SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
1277 if ( pStart == pPage )
1278 pStart = pTmp;
1279 pPage = pTmp;
1280 if (bUpdatePrev)
1281 *ppPrev = pTmp;
1282 continue;
1285 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1288 pRoot->SetAssertFlyPages();
1289 SwRootFrame::AssertPageFlys( pStart );
1291 if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
1293 pDoc->getIDocumentFieldsAccess().UpdatePageFields(nDocPos);
1296 #if OSL_DEBUG_LEVEL > 0
1297 //1. check if two empty pages are behind one another
1298 bool bEmpty = false;
1299 SwPageFrame *pPg = pStart;
1300 while ( pPg )
1302 if ( pPg->IsEmptyPage() )
1304 if ( bEmpty )
1306 OSL_FAIL( "double empty pages." );
1307 break; // once is enough
1309 bEmpty = true;
1311 else
1312 bEmpty = false;
1314 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
1316 #endif
1317 SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
1320 namespace
1322 bool isDeleteForbidden(const SwPageFrame *pDel)
1324 if (pDel->IsDeleteForbidden())
1325 return true;
1326 const SwLayoutFrame* pBody = pDel->FindBodyCont();
1327 const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
1328 return pBodyContent && pBodyContent->IsDeleteForbidden();
1331 bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
1332 SwFrameFormat *pFormat, SwPageDesc *pDesc,
1333 bool bFootnote, SwPageFrame **pRefPage )
1335 SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
1336 SwPageFrame *pSibling = *pRefSibling;
1337 if ( pRefPage )
1339 *pRefPage = pPage;
1340 SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
1341 << " d: " << pDesc << " f: " << pFormat );
1343 else
1344 SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
1345 << pPage << " d: " << pDesc );
1346 pPage->Paste( pRoot, pSibling );
1348 SwViewShell* pViewShell = pRoot->GetCurrShell();
1349 if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
1351 // Hide-whitespace mode does not shrink the last page, so resize the page that used to
1352 // be the last one.
1353 if (SwFrame* pPrevPage = pPage->GetPrev())
1355 pPrevPage->InvalidateSize();
1359 pPage->PreparePage( bFootnote );
1360 // If the sibling has no body text, destroy it as long as it is no footnote page.
1361 if (!pSibling)
1362 return true;
1363 if (pSibling->IsFootnotePage())
1364 return true;
1365 if (pSibling->FindFirstBodyContent())
1366 return true;
1368 if (!pRefPage || !isDeleteForbidden(pSibling))
1370 pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
1371 return false;
1374 return true;
1378 SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
1380 SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
1381 SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
1382 SwPageDesc *pDesc = nullptr;
1384 // insert right (odd) or left (even) page?
1385 bool bNextRightPage = !pPrevPage->OnRightPage();
1386 bool bWishedRightPage = bNextRightPage;
1388 // Which PageDesc is relevant?
1389 // For ContentFrame take the one from format if provided,
1390 // otherwise from the Follow of the PrevPage
1391 if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
1393 SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
1394 pDesc = rDesc.GetPageDesc();
1395 if ( rDesc.GetNumOffset() )
1397 ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
1398 bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
1399 // use the opportunity to set the flag at root
1400 pRoot->SetVirtPageNum( true );
1403 if ( !pDesc )
1404 pDesc = pPrevPage->GetPageDesc()->GetFollow();
1406 assert(pDesc && "Missing PageDesc");
1407 if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
1408 bWishedRightPage = !bWishedRightPage;
1409 bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();
1411 SwDoc *pDoc = pPrevPage->GetFormat()->GetDoc();
1412 bool bCheckPages = false;
1413 // If there is no FrameFormat for this page, create an empty page.
1414 if (bWishedRightPage != bNextRightPage)
1416 if( doInsertPage( pRoot, &pSibling, pDoc->GetEmptyPageFormat(),
1417 pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
1418 bCheckPages = true;
1420 SwFrameFormat *const pFormat( bWishedRightPage
1421 ? pDesc->GetRightFormat(bWishedFirst)
1422 : pDesc->GetLeftFormat(bWishedFirst) );
1423 assert(pFormat);
1424 SwPageFrame *pPage = nullptr;
1425 if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
1426 bCheckPages = true;
1428 if ( pSibling )
1430 if ( bCheckPages )
1432 CheckPageDescs( pSibling, false );
1433 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1434 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
1435 if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1437 const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
1438 if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
1440 pImp->GetLayAction().SetCheckPageNumDirect(
1441 pSibling->GetPhyPageNum() );
1442 SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
1443 << pSibling->GetPhyPageNum() );
1445 return pPage;
1448 else
1449 SwRootFrame::AssertPageFlys( pSibling );
1452 // For the update of page numbering fields, nDocPos provides
1453 // the page position from where invalidation should start.
1454 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1455 if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
1457 pDoc->getIDocumentFieldsAccess().UpdatePageFields(pPrevPage->getFrameArea().Top());
1459 return pPage;
1462 sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
1464 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1465 if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
1467 return sw::sidebarwindows::SidebarPosition::RIGHT;
1469 else
1471 const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
1472 const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
1473 const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());
1475 return bRightSidebar
1476 ? sw::sidebarwindows::SidebarPosition::RIGHT
1477 : sw::sidebarwindows::SidebarPosition::LEFT;
1481 SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
1483 if ( !bTst )
1485 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1486 aFrm.AddHeight(nDist );
1489 return nDist;
1492 SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
1494 OSL_ENSURE( nDist >= 0, "nDist < 0." );
1495 OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );
1497 if ( !bTst )
1499 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1500 aFrm.AddHeight( -nDist );
1503 return nDist;
1506 void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
1508 SwPageFrame *pDel = *pDelRef;
1509 (*pDelRef) = static_cast<SwPageFrame*>(
1510 eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
1511 if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
1512 RemoveFootnotes( pDel, true );
1513 pDel->Cut();
1514 SwFrame::DestroyFrame( pDel );
1517 /// remove pages that are not needed at all
1518 void SwRootFrame::RemoveSuperfluous()
1520 // A page is empty if the body text area has no ContentFrame, but not if there
1521 // is at least one Fly or one footnote attached to the page. Two runs are
1522 // needed: one for endnote pages and one for the pages of the body text.
1524 if ( !IsSuperfluous() )
1525 return;
1526 mbCheckSuperfluous = false;
1528 SwPageFrame *pPage = GetLastPage();
1529 tools::Long nDocPos = LONG_MAX;
1531 // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
1534 if (!sw::IsPageFrameEmpty(*pPage))
1536 if ( pPage->IsFootnotePage() )
1538 while ( pPage->IsFootnotePage() )
1540 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1541 OSL_ENSURE( pPage, "only endnote pages remain." );
1543 continue;
1545 else
1546 pPage = nullptr;
1549 if ( pPage )
1551 SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
1552 RemovePage( &pPage, SwRemoveResult::Prev );
1553 nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
1555 } while ( pPage );
1557 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1558 if ( nDocPos != LONG_MAX &&
1559 (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
1561 GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(nDocPos);
1565 /// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
1566 void SwRootFrame::AssertFlyPages()
1568 if ( !IsAssertFlyPages() )
1569 return;
1570 mbAssertFlyPages = false;
1572 SwDoc *pDoc = GetFormat()->GetDoc();
1573 const sw::SpzFrameFormats* pSpzs = pDoc->GetSpzFrameFormats();
1575 // what page targets the "last" Fly?
1576 // note the needed pages in a set
1577 sal_uInt16 nMaxPg(0);
1578 o3tl::sorted_vector< sal_uInt16 > neededPages;
1579 neededPages.reserve(pSpzs->size());
1581 for(auto pSpz: *pSpzs )
1583 const SwFormatAnchor &rAnch = pSpz->GetAnchor();
1584 if(!rAnch.GetAnchorNode())
1586 const sal_uInt16 nPageNum(rAnch.GetPageNum());
1588 // calc MaxPage (as before)
1589 nMaxPg = std::max(nMaxPg, nPageNum);
1591 // note as needed page
1592 neededPages.insert(nPageNum);
1596 // How many pages exist at the moment?
1597 // And are there EmptyPages that are needed?
1598 SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
1599 SwPageFrame* pPrevPage(nullptr);
1600 SwPageFrame* pFirstRevivedEmptyPage(nullptr);
1602 while(pPage) // moved two while-conditions to break-statements (see below)
1604 const sal_uInt16 nPageNum(pPage->GetPhyPageNum());
1606 if(pPage->IsEmptyPage() &&
1607 nullptr != pPrevPage &&
1608 neededPages.find(nPageNum) != neededPages.end())
1610 // This is an empty page, but it *is* needed since a SwFrame
1611 // is anchored at it directly. Initially these SwFrames are
1612 // not fully initialized. Need to change the format of this SwFrame
1613 // and let the ::Notify mechanism newly evaluate
1614 // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
1615 // adapted from ::InsertPage (used below), this needs previous page
1616 bool bWishedRightPage(!pPrevPage->OnRightPage());
1617 SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
1618 assert(pDesc && "Missing PageDesc");
1620 if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
1622 bWishedRightPage = !bWishedRightPage;
1625 bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
1626 SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));
1628 // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
1629 // m_bEmptyPage, too
1630 pPage->SetFrameFormat(pFormat);
1632 if(nullptr == pFirstRevivedEmptyPage)
1634 // remember first (lowest) SwPageFrame which needed correction
1635 pFirstRevivedEmptyPage = pPage;
1639 // original while-condition II
1640 if(nullptr == pPage->GetNext())
1642 break;
1645 // original while-condition III
1646 if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
1648 break;
1651 pPrevPage = pPage;
1652 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1655 if ( nMaxPg > pPage->GetPhyPageNum() )
1657 for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
1658 pPage = InsertPage( pPage, false );
1660 // If the endnote pages are now corrupt, destroy them.
1661 if ( !pDoc->GetFootnoteIdxs().empty() )
1663 pPage = static_cast<SwPageFrame*>(Lower());
1664 while ( pPage && !pPage->IsFootnotePage() )
1665 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1667 if ( pPage )
1669 SwPageDesc *pTmpDesc = pPage->FindPageDesc();
1670 bool isRightPage = pPage->OnRightPage();
1671 if ( pPage->GetFormat() !=
1672 (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
1673 RemoveFootnotes( pPage, false, true );
1678 // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
1679 // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
1680 // which did think until now that these Page(s) are empty.
1681 // After trying to correct myself I found SwRootFrame::AssertPageFlys
1682 // directly below that already does that, so use it.
1683 if(nullptr != pFirstRevivedEmptyPage)
1685 AssertPageFlys(pFirstRevivedEmptyPage);
1688 //Remove masters that haven't been replaced yet from the list.
1689 RemoveMasterObjs( mpDrawPage );
1691 #if OSL_DEBUG_LEVEL > 0
1692 pPage = static_cast<SwPageFrame*>(Lower());
1693 while ( pPage && pPage->GetNext() &&
1694 !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
1696 SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1697 << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1698 << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1699 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1701 SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1702 << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1703 << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1704 #endif
1707 /// Ensure that after the given page all page-bound objects are located on the correct page
1708 void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
1710 SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
1711 while ( pPage )
1713 if (pPage->GetSortedObjs())
1715 size_t i = 0;
1716 while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
1718 // #i28701#
1719 SwFrameFormat& rFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
1720 const SwFormatAnchor &rAnch = rFormat.GetAnchor();
1721 const sal_uInt16 nPg = rAnch.GetPageNum();
1722 if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
1723 nPg != pPage->GetPhyPageNum() )
1725 SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
1726 // If on the wrong page, check if previous page is empty
1727 if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
1728 static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
1730 // It can move by itself. Just send a modify to its anchor attribute.
1731 #if OSL_DEBUG_LEVEL > 1
1732 const size_t nCnt = pPage->GetSortedObjs()->size();
1733 rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1734 OSL_ENSURE( !pPage->GetSortedObjs() ||
1735 nCnt != pPage->GetSortedObjs()->size(),
1736 "Object couldn't be reattached!" );
1737 #else
1738 rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1739 #endif
1740 // Do not increment index, in this case
1741 continue;
1744 ++i;
1747 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1749 SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
1752 Size SwRootFrame::ChgSize( const Size& aNewSize )
1755 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1756 aFrm.SSize(aNewSize);
1759 InvalidatePrt_();
1760 mbFixSize = false;
1761 return getFrameArea().SSize();
1764 void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
1766 if ( !isFrameAreaPositionValid() )
1768 setFrameAreaPositionValid(true);
1769 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1770 aFrm.Pos().setX(DOCUMENTBORDER);
1771 aFrm.Pos().setY(DOCUMENTBORDER);
1774 if ( !isFramePrintAreaValid() )
1776 setFramePrintAreaValid(true);
1777 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1778 aPrt.Pos().setX(0);
1779 aPrt.Pos().setY(0);
1780 aPrt.SSize( getFrameArea().SSize() );
1783 if ( !isFrameAreaSizeValid() )
1785 // SSize is set by the pages (Cut/Paste).
1786 setFrameAreaSizeValid(true);
1790 void SwRootFrame::ImplInvalidateBrowseWidth()
1792 mbBrowseWidthValid = false;
1793 SwFrame *pPg = Lower();
1794 while ( pPg )
1796 pPg->InvalidateSize();
1797 pPg = pPg->GetNext();
1801 void SwRootFrame::ImplCalcBrowseWidth()
1803 OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
1804 "CalcBrowseWidth and not in BrowseView" );
1806 // The (minimal) with is determined from borders, tables and paint objects.
1807 // It is calculated based on the attributes. Thus, it is not relevant how wide they are
1808 // currently but only how wide they want to be.
1809 // Frames and paint objects inside other objects (frames, tables) do not count.
1810 // Borders and columns are not taken into account.
1812 SwFrame *pFrame = ContainsContent();
1813 while ( pFrame && !pFrame->IsInDocBody() )
1814 pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
1815 if ( !pFrame )
1816 return;
1818 mbBrowseWidthValid = true;
1819 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1820 mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;
1824 if ( pFrame->IsInTab() )
1825 pFrame = pFrame->FindTabFrame();
1827 if ( pFrame->IsTabFrame() &&
1828 !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
1830 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
1831 const SwBorderAttrs &rAttrs = *aAccess.Get();
1832 const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
1833 tools::Long nWidth = rAttrs.GetSize().Width();
1834 if ( nWidth < int(USHRT_MAX)-2000 && //-2k, because USHRT_MAX gets missing while trying to resize! (and cast to int to avoid -Wsign-compare due to broken USHRT_MAX on Android)
1835 text::HoriOrientation::FULL != rHori.GetHoriOrient() )
1837 const SwHTMLTableLayout *pLayoutInfo =
1838 static_cast<const SwTabFrame *>(pFrame)->GetTable()
1839 ->GetHTMLTableLayout();
1840 if ( pLayoutInfo )
1841 nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );
1843 switch ( rHori.GetHoriOrient() )
1845 case text::HoriOrientation::NONE:
1846 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
1847 nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
1848 break;
1849 case text::HoriOrientation::LEFT_AND_WIDTH:
1850 nWidth += rAttrs.CalcLeft( pFrame );
1851 break;
1852 default:
1853 break;
1855 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1858 else if ( pFrame->GetDrawObjs() )
1860 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
1862 // #i28701#
1863 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
1864 const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
1865 const bool bFly = pAnchoredObj->DynCastFlyFrame() != nullptr;
1866 if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
1867 || rFormat.GetFrameSize().GetWidthPercent())
1869 continue;
1872 tools::Long nWidth = 0;
1873 switch ( rFormat.GetAnchor().GetAnchorId() )
1875 case RndStdIds::FLY_AS_CHAR:
1876 nWidth = bFly ? rFormat.GetFrameSize().GetWidth() :
1877 pAnchoredObj->GetObjRect().Width();
1878 break;
1879 case RndStdIds::FLY_AT_PARA:
1881 // #i33170#
1882 // Reactivated old code because
1883 // nWidth = pAnchoredObj->GetObjRect().Right()
1884 // gives wrong results for objects that are still
1885 // at position FAR_AWAY.
1886 if ( bFly )
1888 nWidth = rFormat.GetFrameSize().GetWidth();
1889 const SwFormatHoriOrient &rHori = rFormat.GetHoriOrient();
1890 switch ( rHori.GetHoriOrient() )
1892 case text::HoriOrientation::NONE:
1893 nWidth += rHori.GetPos();
1894 break;
1895 case text::HoriOrientation::INSIDE:
1896 case text::HoriOrientation::LEFT:
1897 if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
1898 nWidth += pFrame->getFramePrintArea().Left();
1899 break;
1900 default:
1901 break;
1904 else
1905 // Paint objects to not have attributes and
1906 // are defined by their current size
1907 nWidth = pAnchoredObj->GetObjRect().Right() -
1908 pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
1910 break;
1911 default: /* do nothing */;
1913 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1916 pFrame = pFrame->FindNextCnt();
1917 } while ( pFrame );
1920 void SwRootFrame::StartAllAction()
1922 if ( GetCurrShell() )
1923 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1925 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1926 pCursorShell->StartAction();
1927 else
1928 rSh.StartAction();
1932 void SwRootFrame::EndAllAction()
1934 if ( !GetCurrShell() )
1935 return;
1937 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1939 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1941 pCursorShell->EndAction();
1942 pCursorShell->CallChgLnk();
1943 if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
1944 pFEShell->SetChainMarker();
1946 else
1947 rSh.EndAction();
1951 void SwRootFrame::UnoRemoveAllActions()
1953 if ( !GetCurrShell() )
1954 return;
1956 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1958 // #i84729#
1959 // No end action, if <SwViewShell> instance is currently in its end action.
1960 // Recursive calls to <::EndAction()> are not allowed.
1961 if ( !rSh.IsInEndAction() )
1963 OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
1964 bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr;
1965 bool bFE = dynamic_cast<const SwFEShell*>( &rSh) != nullptr;
1966 sal_uInt16 nRestore = 0;
1967 while( rSh.ActionCount() )
1969 if( bCursor )
1971 static_cast<SwCursorShell*>(&rSh)->EndAction();
1972 static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
1973 if ( bFE )
1974 static_cast<SwFEShell*>(&rSh)->SetChainMarker();
1976 else
1977 rSh.EndAction();
1978 nRestore++;
1980 rSh.SetRestoreActions(nRestore);
1982 rSh.LockView(true);
1986 void SwRootFrame::UnoRestoreAllActions()
1988 if ( !GetCurrShell() )
1989 return;
1991 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1993 sal_uInt16 nActions = rSh.GetRestoreActions();
1994 while( nActions-- )
1996 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1997 pCursorShell->StartAction();
1998 else
1999 rSh.StartAction();
2001 rSh.SetRestoreActions(0);
2002 rSh.LockView(false);
2006 // Helper functions for SwRootFrame::CheckViewLayout
2007 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );
2009 static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
2011 const bool bPage = pFrame->IsPageFrame();
2012 const SwSortedObjs* pSortedObj = bPage
2013 ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
2014 : pFrame->GetDrawObjs();
2015 if (pSortedObj == nullptr)
2016 return;
2018 // note: pSortedObj elements may be removed and inserted from
2019 // MoveObjectIfActive(), invalidating iterators
2020 // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
2021 for (size_t i = 0; i < pSortedObj->size(); ++i)
2023 SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
2024 const SwFrameFormat& rObjFormat = pAnchoredObj->GetFrameFormat();
2025 const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
2027 // all except from the as character anchored objects are moved
2028 // when processing the page frame:
2029 if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
2030 continue;
2032 SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );
2034 if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
2036 lcl_MoveAllLowers( pFlyFrame, rOffset );
2037 // tdf#138785 update position specific to as-char flys
2038 if (pFlyFrame->IsFlyInContentFrame())
2040 static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
2042 pFlyFrame->NotifyDrawObj();
2043 // --> let the active embedded object be moved
2044 SwFrame* pLower = pFlyFrame->Lower();
2045 if ( pLower && pLower->IsNoTextFrame() )
2047 SwRootFrame* pRoot = pLower->getRootFrame();
2048 SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
2049 if ( pSh )
2051 SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
2052 SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
2053 if ( pNode )
2055 svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
2056 if ( xObj.is() )
2058 for(SwViewShell& rSh : pSh->GetRingContainer())
2060 SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
2061 if ( pFEShell )
2062 pFEShell->MoveObjectIfActive( xObj, rOffset );
2069 else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
2071 // don't touch objects that are not yet positioned:
2072 if ( pAnchoredDrawObj->NotYetPositioned() )
2073 continue;
2075 const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
2076 const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
2077 pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
2078 pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );
2080 // clear contour cache
2081 if ( pAnchoredDrawObj->GetFrameFormat().GetSurround().IsContour() )
2082 ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
2084 // #i92511#
2085 // cache for object rectangle inclusive spaces has to be invalidated.
2086 pAnchoredObj->InvalidateObjRectWithSpaces();
2090 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
2092 // first move the current frame
2093 // RotateFlyFrame3: moved to transform_translate instead of
2094 // direct modification to allow the SwFrame evtl. needed own reactions
2095 pFrame->transform_translate(rOffset);
2097 // Don't forget accessibility:
2098 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2099 if( pFrame->IsAccessibleFrame() )
2101 SwRootFrame *pRootFrame = pFrame->getRootFrame();
2102 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
2103 pRootFrame->GetCurrShell() )
2105 const SwRect aFrame( pFrame->getFrameArea() );
2107 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
2110 #endif
2112 // the move any objects
2113 lcl_MoveAllLowerObjs( pFrame, rOffset );
2115 // finally, for layout frames we have to call this function recursively:
2116 if (pFrame->IsLayoutFrame())
2118 SwFrame* pLowerFrame = pFrame->GetLower();
2119 while ( pLowerFrame )
2121 lcl_MoveAllLowers( pLowerFrame, rOffset );
2122 pLowerFrame = pLowerFrame->GetNext();
2127 // Calculate how the pages have to be positioned
2128 void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
2130 SwViewShell* pSh = GetCurrShell();
2131 vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
2132 // #i91432#
2133 // No calculation of page positions, if only an empty page is present.
2134 // This situation occurs when <SwRootFrame> instance is in construction
2135 // and the document contains only left pages.
2136 if ( Lower()->GetNext() == nullptr &&
2137 static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
2139 return;
2142 if ( !pVisArea )
2144 // no early return for bNewPage
2145 if ( mnViewWidth < 0 )
2146 mnViewWidth = 0;
2148 else
2150 assert(pViewOpt && "CheckViewLayout required ViewOptions");
2152 const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
2153 const bool bBookMode = pViewOpt->IsViewLayoutBookMode();
2155 if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
2156 return;
2158 mnColumns = nColumns;
2159 mbBookMode = bBookMode;
2160 mnViewWidth = pVisArea->Width();
2161 mbSidebarChanged = false;
2164 if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
2166 mnColumns = 1;
2167 mbBookMode = false;
2170 Calc(pRenderContext);
2172 const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
2173 SetCallbackActionEnabled( false );
2175 maPageRects.clear();
2177 const tools::Long nBorder = getFrameArea().Pos().getX();
2178 const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
2179 const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
2180 : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
2181 : SwViewOption::defGapBetweenPages);
2183 // check how many pages fit into the first page layout row:
2184 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());
2186 // will contain the number of pages per row. 0 means that
2187 // the page does not fit.
2188 tools::Long nWidthRemain = nVisWidth;
2190 // after one row has been processed, these variables contain
2191 // the width of the row and the maximum of the page heights
2192 tools::Long nCurrentRowHeight = 0;
2193 tools::Long nCurrentRowWidth = 0;
2195 // these variables are used to finally set the size of the
2196 // root frame
2197 tools::Long nSumRowHeight = 0;
2198 SwTwips nMinPageLeft = TWIPS_MAX;
2199 SwTwips nMaxPageRight = 0;
2200 SwPageFrame* pStartOfRow = pPageFrame;
2201 sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
2202 bool bFirstRow = true;
2204 bool bPageChanged = false;
2205 const bool bRTL = !IsLeftToRightViewLayout();
2206 const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );
2208 while ( pPageFrame )
2210 // we consider the current page to be "start of row" if
2211 // 1. it is the first page in the current row or
2212 // 2. it is the second page in the row and the first page is an empty page in non-book view:
2213 const bool bStartOfRow = pPageFrame == pStartOfRow ||
2214 ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );
2216 const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;
2218 // no half doc border space for first page in each row and
2219 tools::Long nPageWidth = 0;
2220 tools::Long nPageHeight = 0;
2222 if ( mbBookMode )
2224 const SwFrame& rFormatPage = pPageFrame->GetFormatPage();
2226 nPageWidth = rFormatPage.getFrameArea().Width() + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
2227 nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
2229 else
2231 if ( !pPageFrame->IsEmptyPage() )
2233 nPageWidth = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
2234 nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
2238 if ( !bEmptyPage )
2239 ++nNumberOfPagesInRow;
2241 // finish current row if
2242 // 1. in dynamic mode the current page does not fit anymore or
2243 // 2. the current page exceeds the maximum number of columns
2244 bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
2245 (0 != mnColumns && mnColumns < nNumberOfPagesInRow);
2247 // make sure that at least one page goes to the current row:
2248 if ( !bRowFinished || bStartOfRow )
2250 // current page is allowed to be in current row
2251 nWidthRemain = nWidthRemain - nPageWidth;
2253 nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
2254 nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );
2256 pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());
2258 if ( !pPageFrame )
2259 bRowFinished = true;
2262 if ( bRowFinished )
2264 // pPageFrame now points to the first page in the new row or null
2265 // pStartOfRow points to the first page in the current row
2267 // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
2268 if ( !pPageFrame && nWidthRemain > 0 )
2270 // find last page in current row:
2271 const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
2272 while( pLastPageInCurrentRow->GetNext() )
2273 pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());
2275 if ( pLastPageInCurrentRow->IsEmptyPage() )
2276 pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());
2278 // check how many times the last page would still fit into the remaining space:
2279 sal_uInt16 nNumberOfVirtualPages = 0;
2280 const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
2281 SwTwips nRemain = nWidthRemain;
2282 SwTwips nVirtualPagesWidth = 0;
2283 SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;
2285 while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
2287 SwTwips nLastPageWidthWithGap = nLastPageWidth;
2288 if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
2289 nLastPageWidthWithGap += nGapBetweenPages;
2291 if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
2293 ++nNumberOfVirtualPages;
2294 nVirtualPagesWidth += nLastPageWidthWithGap;
2296 nRemain = nRemain - nLastPageWidthWithGap;
2299 nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
2302 // first page in book mode is always special:
2303 if ( bFirstRow && mbBookMode )
2305 // #i88036#
2306 nCurrentRowWidth +=
2307 pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2310 // center page if possible
2311 tools::Long nSizeDiff = 0;
2312 if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
2313 nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;
2315 // adjust positions of pages in current row
2316 tools::Long nX = nSizeDiff;
2318 const tools::Long nRowStart = nBorder + nSizeDiff;
2319 const tools::Long nRowEnd = nRowStart + nCurrentRowWidth;
2321 if ( bFirstRow && mbBookMode )
2323 // #i88036#
2324 nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2327 SwPageFrame* pEndOfRow = pPageFrame;
2328 SwPageFrame* pPageToAdjust = pStartOfRow;
2332 const SwPageFrame* pFormatPage = pPageToAdjust;
2333 if ( mbBookMode )
2334 pFormatPage = &pPageToAdjust->GetFormatPage();
2336 const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
2337 const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
2338 const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
2339 const SwTwips nLeftPageAddOffset = bLeftSidebar ?
2340 nSidebarWidth :
2343 Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
2344 Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );
2346 // RTL view layout: Calculate mirrored page position
2347 if ( bRTL )
2349 const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
2350 aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
2351 aNewPagePosWithLeftOffset = aNewPagePos;
2352 aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
2355 if ( aNewPagePosWithLeftOffset != aOldPagePos )
2357 lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
2358 pPageToAdjust->SetCompletePaint();
2359 bPageChanged = true;
2362 // calculate area covered by the current page and store to
2363 // maPageRects. This is used e.g., for cursor setting
2364 const bool bFirstColumn = pPageToAdjust == pStartOfRow;
2365 const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
2366 const bool bLastRow = !pEndOfRow;
2368 nMinPageLeft = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
2369 nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));
2371 // border of nGapBetweenPages around the current page:
2372 SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
2373 aNewPagePos.getY(),
2374 pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
2375 nCurrentRowHeight );
2377 static const tools::Long nOuterClickDiff = 1000000;
2379 // adjust borders for these special cases:
2380 if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
2381 aPageRectWithBorders.SubLeft( nOuterClickDiff );
2382 if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
2383 aPageRectWithBorders.AddRight( nOuterClickDiff );
2384 if ( bFirstRow )
2385 aPageRectWithBorders.SubTop( nOuterClickDiff );
2386 if ( bLastRow )
2387 aPageRectWithBorders.AddBottom( nOuterClickDiff );
2389 maPageRects.push_back( aPageRectWithBorders );
2391 nX = nX + nCurrentPageWidth;
2392 pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());
2394 // distance to next page
2395 if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
2397 // in book view, we add the x gap before left (even) pages:
2398 if ( mbBookMode )
2400 if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
2401 nX = nX + nGapBetweenPages;
2403 else
2405 // in non-book view, don't add x gap before
2406 // 1. the last empty page in a row
2407 // 2. after an empty page
2408 const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
2409 ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );
2411 if ( !bDontAddGap )
2412 nX = nX + nGapBetweenPages;
2416 while (pPageToAdjust && pPageToAdjust != pEndOfRow);
2418 // adjust values for root frame size
2419 nSumRowHeight = nSumRowHeight + nCurrentRowHeight;
2421 // start new row:
2422 nCurrentRowHeight = 0;
2423 nCurrentRowWidth = 0;
2424 pStartOfRow = pEndOfRow;
2425 nWidthRemain = nVisWidth;
2426 nNumberOfPagesInRow = 0;
2427 bFirstRow = false;
2428 } // end row finished
2429 } // end while
2431 // set size of root frame:
2432 const Size aOldSize( getFrameArea().SSize() );
2433 const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );
2435 if ( bPageChanged || aNewSize != aOldSize )
2437 ChgSize( aNewSize );
2438 ::AdjustSizeChgNotify( this );
2439 Calc(pRenderContext);
2441 if ( pSh && pSh->GetDoc()->GetDocShell() )
2443 pSh->SetFirstVisPageInvalid();
2444 if (bOldCallbackActionEnabled)
2446 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
2447 pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
2452 maPagesArea.Pos( getFrameArea().Pos() );
2453 maPagesArea.SSize( aNewSize );
2454 if ( TWIPS_MAX != nMinPageLeft )
2455 maPagesArea.Left_( nMinPageLeft );
2457 SetCallbackActionEnabled( bOldCallbackActionEnabled );
2460 bool SwRootFrame::IsLeftToRightViewLayout() const
2462 // Layout direction determined by layout direction of the first page.
2463 // #i88036#
2464 // Only ask a non-empty page frame for its layout direction
2465 assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
2466 const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
2467 return !rPage.IsRightToLeft() && !rPage.IsVertical();
2470 const SwPageFrame& SwPageFrame::GetFormatPage() const
2472 const SwPageFrame* pRet = this;
2473 if ( IsEmptyPage() )
2475 pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
2476 // #i88035#
2477 // Typically a right empty page frame has a next non-empty page frame and
2478 // a left empty page frame has a previous non-empty page frame.
2479 // But under certain circumstances this assumption is not true -
2480 // e.g. during insertion of a left page at the end of the document right
2481 // after a left page in an intermediate state a right empty page does not
2482 // have a next page frame.
2483 if ( pRet == nullptr )
2485 if ( OnRightPage() )
2487 pRet = static_cast<const SwPageFrame*>( GetPrev() );
2489 else
2491 pRet = static_cast<const SwPageFrame*>( GetNext() );
2494 assert(pRet &&
2495 "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
2497 return *pRet;
2500 bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
2502 tools::Long nUpperLimit = 0;
2503 tools::Long nLowerLimit = 0;
2504 const SwFrame* pFrame = Lower();
2505 while ( pFrame )
2507 if ( pFrame->IsBodyFrame() )
2509 nUpperLimit = pFrame->getFrameArea().Top();
2510 nLowerLimit = pFrame->getFrameArea().Bottom();
2512 else if ( pFrame->IsFootnoteContFrame() )
2513 nLowerLimit = pFrame->getFrameArea().Bottom();
2515 pFrame = pFrame->GetNext();
2518 SwRect aHeaderArea( getFrameArea().TopLeft(),
2519 Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );
2521 SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
2522 const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
2523 if ( aHeaderArea.Contains( rPt ) )
2525 if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
2527 rControl = FrameControlType::Header;
2528 return true;
2531 else
2533 SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
2534 Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );
2536 if ( aFooterArea.Contains( rPt ) &&
2537 (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
2539 rControl = FrameControlType::Footer;
2540 return true;
2544 return false;
2547 bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
2549 SwViewShell* pShell = getRootFrame()->GetCurrShell();
2550 if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
2552 // When whitespace is hidden, the page frame has two heights: the
2553 // nominal (defined by the frame format), and the actual (which is
2554 // at most the nominal height, but can be smaller in case there is
2555 // no content for the whole page).
2556 // The layout size is the actual one, but we want to move the
2557 // content frame to a new page only in case it doesn't fit the
2558 // nominal size.
2559 if (nDiff < 0)
2561 // Content frame doesn't fit the actual size, check if it fits the nominal one.
2562 const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
2563 const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
2564 tools::Long nWhitespace = rPageSize.getHeight() - getFrameArea().Height();
2565 if (nWhitespace > -nDiff)
2567 // It does: don't move it and invalidate our page frame so
2568 // that it gets a larger height.
2569 return false;
2574 return true;
2577 const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
2579 const SwFrame* pLowerFrame = Lower();
2580 while (pLowerFrame)
2582 if (pLowerFrame->IsHeaderFrame())
2583 return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
2584 pLowerFrame = pLowerFrame->GetNext();
2586 return nullptr;
2589 const SwFooterFrame* SwPageFrame::GetFooterFrame() const
2591 const SwFrame* pLowerFrame = Lower();
2592 while (pLowerFrame)
2594 if (pLowerFrame->IsFooterFrame())
2595 return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
2596 pLowerFrame = pLowerFrame->GetNext();
2598 return nullptr;
2601 void SwPageFrame::dumpAsXml(xmlTextWriterPtr writer) const
2603 (void)xmlTextWriterStartElement(writer, reinterpret_cast<const xmlChar*>("page"));
2604 dumpAsXmlAttributes(writer);
2606 (void)xmlTextWriterStartElement(writer, BAD_CAST("page_status"));
2607 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyLayout"), BAD_CAST(OString::boolean(!IsInvalidFlyLayout()).getStr()));
2608 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyContent"), BAD_CAST(OString::boolean(!IsInvalidFlyContent()).getStr()));
2609 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyInCnt"), BAD_CAST(OString::boolean(!IsInvalidFlyInCnt()).getStr()));
2610 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!IsInvalidLayout()).getStr()));
2611 (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!IsInvalidContent()).getStr()));
2612 (void)xmlTextWriterEndElement(writer);
2613 (void)xmlTextWriterStartElement(writer, BAD_CAST("page_info"));
2614 (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", GetPhyPageNum());
2615 (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", GetVirtPageNum());
2616 OUString aFormatName = GetPageDesc()->GetName();
2617 (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr()));
2618 (void)xmlTextWriterEndElement(writer);
2619 if (auto const* pObjs = GetSortedObjs())
2621 (void)xmlTextWriterStartElement(writer, BAD_CAST("sorted_objs"));
2622 for (SwAnchoredObject const*const pObj : *pObjs)
2623 { // just print pointer, full details will be printed on its anchor frame
2624 // this nonsense is needed because of multiple inheritance
2625 if (SwFlyFrame const* pFly = pObj->DynCastFlyFrame())
2627 (void)xmlTextWriterStartElement(writer, BAD_CAST("fly"));
2628 (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pFly);
2630 else
2632 (void)xmlTextWriterStartElement(writer, BAD_CAST(pObj->getElementName()));
2633 (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pObj);
2635 (void)xmlTextWriterEndElement(writer);
2637 (void)xmlTextWriterEndElement(writer);
2640 (void)xmlTextWriterStartElement(writer, BAD_CAST("infos"));
2641 dumpInfosAsXml(writer);
2642 (void)xmlTextWriterEndElement(writer);
2643 const SwSortedObjs* pAnchored = GetDrawObjs();
2644 if ( pAnchored && pAnchored->size() > 0 )
2646 (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );
2648 for (SwAnchoredObject* pObject : *pAnchored)
2650 pObject->dumpAsXml( writer );
2653 (void)xmlTextWriterEndElement( writer );
2655 dumpChildrenAsXml(writer);
2656 (void)xmlTextWriterEndElement(writer);
2659 SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
2661 if (pPage && pPage->HasGrid())
2663 SwTextGridItem const& rGridItem(
2664 pPage->GetPageDesc()->GetMaster().GetTextGrid());
2665 if (GRID_NONE != rGridItem.GetGridType())
2667 return &rGridItem;
2670 return nullptr;
2673 sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
2675 return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
2678 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */