1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <comphelper/lok.hxx>
21 #include <ndole.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <svl/itemiter.hxx>
25 #include <fmtfsize.hxx>
26 #include <fmthdft.hxx>
27 #include <fmtclds.hxx>
28 #include <fmtpdsc.hxx>
29 #include <fmtornt.hxx>
30 #include <fmtsrnd.hxx>
31 #include <ftninfo.hxx>
32 #include <frmtool.hxx>
33 #include <tgrditem.hxx>
34 #include <viewopt.hxx>
35 #include <docsh.hxx>
36 #include <wrtsh.hxx>
37 #include <view.hxx>
38 #include <edtwin.hxx>
39 #include <frameformats.hxx>
40
41 #include <viewimp.hxx>
42 #include <pagefrm.hxx>
43 #include <rootfrm.hxx>
44 #include <IDocumentDrawModelAccess.hxx>
45 #include <IDocumentSettingAccess.hxx>
46 #include <IDocumentFieldsAccess.hxx>
47 #include <dcontact.hxx>
48 #include <hints.hxx>
49 #include <FrameControlsManager.hxx>
50
51 #include <ftnidx.hxx>
52 #include <bodyfrm.hxx>
53 #include <ftnfrm.hxx>
54 #include <tabfrm.hxx>
55 #include <txtfrm.hxx>
56 #include <notxtfrm.hxx>
57 #include <layact.hxx>
58 #include <flyfrms.hxx>
59 #include <htmltbl.hxx>
60 #include <pagedesc.hxx>
61 #include <editeng/frmdiritem.hxx>
62 #include <sortedobjs.hxx>
63 #include <calbck.hxx>
64 #include <txtfly.hxx>
65
66 using namespace ::com::sun::star;
67
SwBodyFrame(SwFrameFormat * pFormat,SwFrame * pSib)68 SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
69 SwLayoutFrame( pFormat, pSib )
70 {
71 mnFrameType = SwFrameType::Body;
72 }
73
Format(vcl::RenderContext *,const SwBorderAttrs *)74 void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
75 {
76 // Formatting of the body is too simple, thus, it gets its own format method.
77 // Borders etc. are not taken into account here.
78 // Width is taken from the PrtArea of the Upper. Height is the height of the
79 // PrtArea of the Upper minus any neighbors (for robustness).
80 // The PrtArea has always the size of the frame.
81
82 if ( !isFrameAreaSizeValid() )
83 {
84 SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
85 SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
86 const SwFrame *pFrame = GetUpper()->Lower();
87 do
88 {
89 if ( pFrame != this )
90 {
91 if( pFrame->IsVertical() )
92 nWidth -= pFrame->getFrameArea().Width();
93 else
94 nHeight -= pFrame->getFrameArea().Height();
95 }
96 pFrame = pFrame->GetNext();
97 } while ( pFrame );
98
99 if ( nHeight < 0 )
100 {
101 nHeight = 0;
102 }
103
104 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
105 aFrm.Height( nHeight );
106
107 if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
108 {
109 aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
110 }
111
112 aFrm.Width( nWidth );
113 }
114
115 bool bNoGrid = true;
116 if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
117 {
118 SwTextGridItem const*const pGrid(
119 GetGridItem(static_cast<SwPageFrame*>(GetUpper())));
120 if( pGrid )
121 {
122 bNoGrid = false;
123 tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
124 SwRectFnSet aRectFnSet(this);
125 tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
126 tools::Long nBorder = 0;
127 if( GRID_LINES_CHARS == pGrid->GetGridType() )
128 {
129 //for textgrid refactor
130 SwDoc *pDoc = GetFormat()->GetDoc();
131 nBorder = nSize % (GetGridWidth(*pGrid, *pDoc));
132 nSize -= nBorder;
133 nBorder /= 2;
134 }
135
136 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
137 aRectFnSet.SetPosX( aPrt, nBorder );
138 aRectFnSet.SetWidth( aPrt, nSize );
139
140 // Height of body frame:
141 nBorder = aRectFnSet.GetHeight(getFrameArea());
142
143 // Number of possible lines in area of body frame:
144 tools::Long nNumberOfLines = nBorder / nSum;
145 if( nNumberOfLines > pGrid->GetLines() )
146 nNumberOfLines = pGrid->GetLines();
147
148 // Space required for nNumberOfLines lines:
149 nSize = nNumberOfLines * nSum;
150 nBorder -= nSize;
151 nBorder /= 2;
152
153 // #i21774# Footnotes and centering the grid does not work together:
154 const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc()->
155 GetFootnoteIdxs().empty();
156
157 aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
158 aRectFnSet.SetHeight( aPrt, nSize );
159 }
160 }
161
162 if( bNoGrid )
163 {
164 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
165 aPrt.Pos().setX(0);
166 aPrt.Pos().setY(0);
167 aPrt.Height( getFrameArea().Height() );
168 aPrt.Width( getFrameArea().Width() );
169 }
170
171 setFrameAreaSizeValid(true);
172 setFramePrintAreaValid(true);
173 }
174
SwPageFrame(SwFrameFormat * pFormat,SwFrame * pSib,SwPageDesc * pPgDsc)175 SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
176 SwFootnoteBossFrame( pFormat, pSib ),
177 m_pDesc( pPgDsc ),
178 m_nPhyPageNum( 0 )
179 {
180 SetDerivedVert( false );
181 SetDerivedR2L( false );
182 if( m_pDesc )
183 {
184 m_bHasGrid = true;
185 SwTextGridItem const*const pGrid(GetGridItem(this));
186 if( !pGrid )
187 m_bHasGrid = false;
188 }
189 else
190 m_bHasGrid = false;
191 SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
192 pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
193 mnFrameType = SwFrameType::Page;
194 m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
195 m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;
196
197 SwViewShell *pSh = getRootFrame()->GetCurrShell();
198 const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
199 vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
200
201 {
202 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
203
204 if ( bBrowseMode )
205 {
206 aFrm.Height( 0 );
207 tools::Long nWidth = pSh->VisArea().Width();
208
209 if ( !nWidth )
210 {
211 nWidth = 5000; // changes anyway
212 }
213
214 aFrm.Width ( nWidth );
215 }
216 else
217 {
218 aFrm.SSize( pFormat->GetFrameSize().GetSize() );
219 }
220 }
221
222 // create and insert body area if it is not a blank page
223 SwDoc* pDoc(pFormat->GetDoc());
224 m_bEmptyPage = (pFormat == pDoc->GetEmptyPageFormat());
225
226 if(m_bEmptyPage)
227 {
228 return;
229 }
230
231 Calc(pRenderContext); // so that the PrtArea is correct
232 SwBodyFrame *pBodyFrame = new SwBodyFrame( pDoc->GetDfltFrameFormat(), this );
233 pBodyFrame->ChgSize( getFramePrintArea().SSize() );
234 pBodyFrame->Paste( this );
235 pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
236 pBodyFrame->InvalidatePos();
237
238 if ( bBrowseMode )
239 InvalidateSize_();
240
241 // insert header/footer,, but only if active.
242 if ( pFormat->GetHeader().IsActive() )
243 PrepareHeader();
244 if ( pFormat->GetFooter().IsActive() )
245 PrepareFooter();
246
247 const SwFormatCol &rCol = pFormat->GetCol();
248 if ( rCol.GetNumCols() > 1 )
249 {
250 const SwFormatCol aOld; //ChgColumns() needs an old value
251 pBodyFrame->ChgColumns( aOld, rCol );
252 }
253
254 }
255
DestroyImpl()256 void SwPageFrame::DestroyImpl()
257 {
258 // Cleanup the header-footer controls in the SwEditWin
259 SwViewShell* pSh = getRootFrame()->GetCurrShell();
260 SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( pSh );
261 if ( pWrtSh )
262 {
263 SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
264 rEditWin.GetFrameControlsManager( ).RemoveControls( this );
265 }
266
267 // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
268 if (m_pSortedObjs)
269 {
270 // Objects can be anchored at pages that are before their anchors (why ever...).
271 // In such cases, we would access already freed memory.
272 for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
273 {
274 pAnchoredObj->SetPageFrame( nullptr );
275 }
276 m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
277 }
278
279 // prevent access to destroyed pages
280 SwDoc *pDoc = GetFormat() ? GetFormat()->GetDoc() : nullptr;
281 if( pDoc && !pDoc->IsInDtor() )
282 {
283 if ( pSh )
284 {
285 SwViewShellImp *pImp = pSh->Imp();
286 pImp->SetFirstVisPageInvalid();
287 if ( pImp->IsAction() )
288 pImp->GetLayAction().SetAgain();
289 // #i9719# - retouche area of page
290 // including border and shadow area.
291 const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
292 SwRect aRetoucheRect;
293 SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
294 pSh->AddPaintRect( aRetoucheRect );
295 }
296 }
297
298 SwFootnoteBossFrame::DestroyImpl();
299 }
300
~SwPageFrame()301 SwPageFrame::~SwPageFrame()
302 {
303 }
304
CheckGrid(bool bInvalidate)305 void SwPageFrame::CheckGrid( bool bInvalidate )
306 {
307 bool bOld = m_bHasGrid;
308 m_bHasGrid = true;
309 SwTextGridItem const*const pGrid(GetGridItem(this));
310 m_bHasGrid = nullptr != pGrid;
311 if( !(bInvalidate || bOld != m_bHasGrid) )
312 return;
313
314 SwLayoutFrame* pBody = FindBodyCont();
315 if( pBody )
316 {
317 pBody->InvalidatePrt();
318 SwContentFrame* pFrame = pBody->ContainsContent();
319 while( pBody->IsAnLower( pFrame ) )
320 {
321 static_cast<SwTextFrame*>(pFrame)->Prepare();
322 pFrame = pFrame->GetNextContentFrame();
323 }
324 }
325 SetCompletePaint();
326 }
327
CheckDirection(bool bVert)328 void SwPageFrame::CheckDirection( bool bVert )
329 {
330 SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
331 if( bVert )
332 {
333 if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
334 {
335 mbVertLR = false;
336 mbVertical = false;
337 }
338 else
339 {
340 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
341 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
342 {
343 mbVertLR = false;
344 mbVertical = false;
345 }
346 else
347 {
348 mbVertical = true;
349
350 if(SvxFrameDirection::Vertical_RL_TB == nDir)
351 mbVertLR = false;
352 else if(SvxFrameDirection::Vertical_LR_TB==nDir)
353 mbVertLR = true;
354 }
355 }
356
357 mbInvalidVert = false;
358 }
359 else
360 {
361 if( SvxFrameDirection::Horizontal_RL_TB == nDir )
362 mbRightToLeft = true;
363 else
364 mbRightToLeft = false;
365 mbInvalidR2L = false;
366 }
367 }
368
369 /// create specific Flys for this page and format generic content
lcl_FormatLay(SwLayoutFrame * pLay)370 static void lcl_FormatLay( SwLayoutFrame *pLay )
371 {
372 vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
373 // format all LayoutFrames - no tables, Flys etc.
374
375 SwFrame *pTmp = pLay->Lower();
376 // first the low-level ones
377 while ( pTmp )
378 {
379 const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
380 | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont
381 | SwFrameType::Ftn | SwFrameType::Body;
382 if ( pTmp->GetType() & nTypes )
383 ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
384 pTmp = pTmp->GetNext();
385 }
386 pLay->Calc(pRenderContext);
387 }
388
389 /// Create Flys or register draw objects
lcl_MakeObjs(const SwFrameFormats & rTable,SwPageFrame * pPage)390 static void lcl_MakeObjs( const SwFrameFormats &rTable, SwPageFrame *pPage )
391 {
392 // formats are in the special table of the document
393
394 for ( size_t i = 0; i < rTable.size(); ++i )
395 {
396 SwFrameFormat *pFormat = rTable[i];
397 const SwFormatAnchor &rAnch = pFormat->GetAnchor();
398 if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() )
399 {
400 if( rAnch.GetContentAnchor() )
401 {
402 if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
403 {
404 SwFormatAnchor aAnch( rAnch );
405 aAnch.SetAnchor( nullptr );
406 pFormat->SetFormatAttr( aAnch );
407 }
408 else
409 continue;
410 }
411
412 // is it a border or a SdrObject?
413 bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
414 SdrObject *pSdrObj = nullptr;
415 if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
416 {
417 OSL_FAIL( "DrawObject not found." );
418 pFormat->GetDoc()->DelFrameFormat( pFormat );
419 --i;
420 continue;
421 }
422 // The object might be anchored to another page, e.g. when inserting
423 // a new page due to a page descriptor change. In such cases, the
424 // object needs to be moved.
425 // In some cases the object is already anchored to the correct page.
426 // This will be handled here and does not need to be coded extra.
427 SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
428 if ( bSdrObj )
429 {
430 // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
431 SwDrawContact *pContact =
432 static_cast<SwDrawContact*>(::GetUserCall(pSdrObj));
433 if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
434 {
435 if ( pContact )
436 {
437 pDrawVirtObj->RemoveFromWriterLayout();
438 pDrawVirtObj->RemoveFromDrawingPage();
439 pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
440 }
441 }
442 else
443 {
444 if ( pContact->GetAnchorFrame() )
445 pContact->DisconnectFromLayout( false );
446 pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
447 }
448 }
449 else
450 {
451 SwIterator<SwFlyFrame,SwFormat> aIter( *pFormat );
452 SwFlyFrame *pFly = aIter.First();
453 if ( pFly)
454 {
455 if( pFly->GetAnchorFrame() )
456 pFly->AnchorFrame()->RemoveFly( pFly );
457 }
458 else
459 pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pPg, pPg );
460 pPg->AppendFly( pFly );
461 ::RegistFlys( pPg, pFly );
462 }
463 }
464 }
465 }
466
PreparePage(bool bFootnote)467 void SwPageFrame::PreparePage( bool bFootnote )
468 {
469 SetFootnotePage( bFootnote );
470
471 // #i82258#
472 // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
473 // the side effect, that the content of page header and footer are formatted.
474 // For this formatting it is needed that the anchored objects are registered
475 // at the <SwPageFrame> instance.
476 // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
477 ::RegistFlys( this, this );
478
479 if ( Lower() )
480 {
481 ::lcl_FormatLay( this );
482 }
483
484 // Flys and draw objects that are still attached to the document.
485 // Footnote pages do not have page-bound Flys!
486 // There might be Flys or draw objects that want to be placed on
487 // empty pages, however, the empty pages ignore that and the following
488 // pages take care of them.
489 if ( !bFootnote && !IsEmptyPage() )
490 {
491 SwDoc *pDoc = GetFormat()->GetDoc();
492
493 if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
494 lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
495 lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), this );
496 }
497 }
498
SwClientNotify(const SwModify & rModify,const SfxHint & rHint)499 void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
500 {
501 if(typeid(sw::PageFootnoteHint) == typeid(rHint))
502 {
503 // currently the savest way:
504 static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
505 SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
506 if(!GetMaxFootnoteHeight())
507 SetMaxFootnoteHeight(LONG_MAX);
508 SetColMaxFootnoteHeight();
509 // here, the page might be destroyed:
510 static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
511 }
512 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
513 {
514 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
515 if(auto pSh = getRootFrame()->GetCurrShell())
516 pSh->SetFirstVisPageInvalid();
517
518 SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
519 if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
520 {
521 auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
522 auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
523 SfxItemIter aOIter(*rOldSetChg.GetChgSet());
524 SfxItemIter aNIter(*rNewSetChg.GetChgSet());
525 const SfxPoolItem* pOItem = aOIter.GetCurItem();
526 const SfxPoolItem* pNItem = aNIter.GetCurItem();
527 SwAttrSetChg aOldSet(rOldSetChg);
528 SwAttrSetChg aNewSet(rNewSetChg);
529 do
530 {
531 UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
532 pOItem = aOIter.NextItem();
533 pNItem = aNIter.NextItem();
534 } while(pNItem);
535 if(aOldSet.Count() || aNewSet.Count())
536 SwLayoutFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
537 }
538 else
539 UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
540
541 if (eInvFlags == SwPageFrameInvFlags::NONE)
542 return;
543
544 InvalidatePage( this );
545 if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
546 InvalidatePrt_();
547 if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
548 SetCompletePaint();
549 if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
550 GetNext()->InvalidatePos();
551 if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
552 PrepareHeader();
553 if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
554 PrepareFooter();
555 if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
556 CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
557 } else
558 SwFrame::SwClientNotify(rModify, rHint);
559 }
560
UpdateAttr_(const SfxPoolItem * pOld,const SfxPoolItem * pNew,SwPageFrameInvFlags & rInvFlags,SwAttrSetChg * pOldSet,SwAttrSetChg * pNewSet)561 void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
562 SwPageFrameInvFlags &rInvFlags,
563 SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
564 {
565 bool bClear = true;
566 const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
567 switch( nWhich )
568 {
569 case RES_FMT_CHG:
570 {
571 // state of m_bEmptyPage needs to be determined newly
572 const bool bNewState(GetFormat() == GetFormat()->GetDoc()->GetEmptyPageFormat());
573
574 if(m_bEmptyPage != bNewState)
575 {
576 // copy new state
577 m_bEmptyPage = bNewState;
578
579 if(nullptr == GetLower())
580 {
581 // if we were an empty page before there is not yet a BodyArea in the
582 // form of a SwBodyFrame, see constructor
583 SwViewShell* pSh(getRootFrame()->GetCurrShell());
584 vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
585 Calc(pRenderContext); // so that the PrtArea is correct
586 SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
587 pBodyFrame->ChgSize(getFramePrintArea().SSize());
588 pBodyFrame->Paste(this);
589 pBodyFrame->InvalidatePos();
590 }
591 }
592
593 // If the frame format is changed, several things might also change:
594 // 1. columns:
595 assert(pOld && pNew); //FMT_CHG Missing Format
596 const SwFormat *const pOldFormat = static_cast<const SwFormatChg*>(pOld)->pChangedFormat;
597 const SwFormat *const pNewFormat = static_cast<const SwFormatChg*>(pNew)->pChangedFormat;
598 assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
599 const SwFormatCol &rOldCol = pOldFormat->GetCol();
600 const SwFormatCol &rNewCol = pNewFormat->GetCol();
601 if( rOldCol != rNewCol )
602 {
603 SwLayoutFrame *pB = FindBodyCont();
604 assert(pB && "Page without Body.");
605 pB->ChgColumns( rOldCol, rNewCol );
606 rInvFlags |= SwPageFrameInvFlags::CheckGrid;
607 }
608
609 // 2. header and footer:
610 const SwFormatHeader &rOldH = pOldFormat->GetHeader();
611 const SwFormatHeader &rNewH = pNewFormat->GetHeader();
612 if( rOldH != rNewH )
613 rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
614
615 const SwFormatFooter &rOldF = pOldFormat->GetFooter();
616 const SwFormatFooter &rNewF = pNewFormat->GetFooter();
617 if( rOldF != rNewF )
618 rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
619 CheckDirChange();
620
621 [[fallthrough]];
622 }
623 case RES_FRM_SIZE:
624 {
625 const SwRect aOldPageFrameRect( getFrameArea() );
626 SwViewShell *pSh = getRootFrame()->GetCurrShell();
627 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
628 {
629 setFrameAreaSizeValid(false);
630 // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
631 // Calculation of the page is not necessary, because its size is
632 // invalidated here and further invalidation is done in the
633 // calling method <SwPageFrame::Modify(..)> and probably by calling
634 // <SwLayoutFrame::SwClientNotify(..)> at the end.
635 // It can also causes inconsistences, because the lowers are
636 // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
637 // a next page is called. This is performed on the switch to the
638 // online layout.
639 //MakeAll();
640 }
641 else if (pNew)
642 {
643 const SwFormatFrameSize &rSz = nWhich == RES_FMT_CHG ?
644 static_cast<const SwFormatChg*>(pNew)->pChangedFormat->GetFrameSize() :
645 static_cast<const SwFormatFrameSize&>(*pNew);
646
647 {
648 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
649 aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
650 aFrm.Width ( std::max( rSz.GetWidth(), tools::Long(MINLAY) ) );
651 }
652
653 if ( GetUpper() )
654 {
655 static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
656 }
657 }
658 // cleanup Window
659 if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
660 {
661 // #i9719# - consider border and shadow of
662 // page frame for determine 'old' rectangle - it's used for invalidating.
663 const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
664 SwRect aOldRectWithBorderAndShadow;
665 SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
666 IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
667 pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
668 }
669 rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
670 if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
671 rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
672 }
673 break;
674
675 case RES_COL:
676 assert(pOld && pNew); //COL Missing Format
677 if (pOld && pNew)
678 {
679 SwLayoutFrame *pB = FindBodyCont();
680 assert(pB); //page without body
681 pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
682 rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
683 }
684 break;
685
686 case RES_HEADER:
687 rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
688 break;
689
690 case RES_FOOTER:
691 rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
692 break;
693 case RES_TEXTGRID:
694 rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
695 break;
696 case RES_FRAMEDIR :
697 CheckDirChange();
698 break;
699
700 default:
701 bClear = false;
702 }
703 if ( !bClear )
704 return;
705
706 if ( pOldSet || pNewSet )
707 {
708 if ( pOldSet )
709 pOldSet->ClearItem( nWhich );
710 if ( pNewSet )
711 pNewSet->ClearItem( nWhich );
712 }
713 else
714 {
715 SwModify aMod;
716 SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
717 }
718 }
719
720 /// get information from Modify
GetInfo(SfxPoolItem & rInfo) const721 bool SwPageFrame::GetInfo( SfxPoolItem & rInfo ) const
722 {
723 if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
724 {
725 // a page frame exists, so use this one
726 return false;
727 }
728 return true; // continue searching
729 }
730
SetPageDesc(SwPageDesc * pNew,SwFrameFormat * pFormat)731 void SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
732 {
733 m_pDesc = pNew;
734 if ( pFormat )
735 SetFrameFormat( pFormat );
736 }
737
738 /* determine the right PageDesc:
739 * 0. from the document for footnote and endnote pages
740 * 1. from the first BodyContent below a page
741 * 2. from PageDesc of the predecessor page
742 * 3. from PageDesc of the previous page if blank page
743 * 3.1 from PageDesc of the next page if no predecessor exists
744 * 4. default PageDesc
745 * 5. In BrowseMode use the first paragraph or default PageDesc.
746 */
FindPageDesc()747 SwPageDesc *SwPageFrame::FindPageDesc()
748 {
749 // 0.
750 if ( IsFootnotePage() )
751 {
752 SwDoc *pDoc = GetFormat()->GetDoc();
753 if ( IsEndNotePage() )
754 return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
755 else
756 return pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
757 }
758
759 SwPageDesc *pRet = nullptr;
760
761 //5.
762 const SwViewShell *pSh = getRootFrame()->GetCurrShell();
763 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
764 {
765 SwContentFrame *pFrame = GetUpper()->ContainsContent();
766 while (pFrame && !pFrame->IsInDocBody())
767 pFrame = pFrame->GetNextContentFrame();
768 if (pFrame)
769 {
770 SwFrame *pFlow = pFrame;
771 if ( pFlow->IsInTab() )
772 pFlow = pFlow->FindTabFrame();
773 pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
774 }
775 if ( !pRet )
776 pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
777 return pRet;
778 }
779
780 SwFrame *pFlow = FindFirstBodyContent();
781 if ( pFlow && pFlow->IsInTab() )
782 pFlow = pFlow->FindTabFrame();
783
784 //1.
785 if ( pFlow )
786 {
787 SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
788 if ( !pTmp->IsFollow() )
789 pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
790 }
791
792 //3. and 3.1
793 if ( !pRet && IsEmptyPage() )
794 // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
795 // the same page description as its prev, just like after construction
796 // of the empty page.
797 pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
798 GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;
799
800 //2.
801 if ( !pRet )
802 pRet = GetPrev() ?
803 static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;
804
805 //4.
806 if ( !pRet )
807 pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
808
809 OSL_ENSURE( pRet, "could not find page descriptor." );
810 return pRet;
811 }
812
813 // Notify if the RootFrame changes its size
AdjustSizeChgNotify(SwRootFrame * pRoot)814 void AdjustSizeChgNotify( SwRootFrame *pRoot )
815 {
816 const bool bOld = pRoot->IsSuperfluous();
817 pRoot->mbCheckSuperfluous = false;
818 if ( pRoot->GetCurrShell() )
819 {
820 for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
821 {
822 if( pRoot == rSh.GetLayout() )
823 {
824 rSh.SizeChgNotify();
825 if ( rSh.Imp() )
826 rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
827 }
828 }
829 }
830 pRoot->mbCheckSuperfluous = bOld;
831 }
832
SetLastPage(SwPageFrame * pPage)833 inline void SetLastPage( SwPageFrame *pPage )
834 {
835 static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
836 }
837
Cut()838 void SwPageFrame::Cut()
839 {
840 SwViewShell *pSh = getRootFrame()->GetCurrShell();
841 if ( !IsEmptyPage() )
842 {
843 if ( GetNext() )
844 GetNext()->InvalidatePos();
845
846 // move Flys whose anchor is on a different page (draw objects are not relevant here)
847 if ( GetSortedObjs() )
848 {
849 size_t i = 0;
850 while ( GetSortedObjs() && i < GetSortedObjs()->size() )
851 {
852 // #i28701#
853 SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];
854
855 if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
856 {
857 SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
858 pFly->AnchorFrame()->FindPageFrame() : nullptr;
859 if ( pAnchPage && (pAnchPage != this) )
860 {
861 MoveFly( pFly, pAnchPage );
862 pFly->InvalidateSize();
863 pFly->InvalidatePos_();
864 // Do not increment index, in this case
865 continue;
866 }
867 }
868 ++i;
869 }
870 }
871 // cleanup Window
872 if ( pSh && pSh->GetWin() )
873 pSh->InvalidateWindows( getFrameArea() );
874 }
875
876 // decrease the root's page number
877 static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
878 SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
879 if ( pPg )
880 {
881 while ( pPg )
882 {
883 --pPg->m_nPhyPageNum;
884 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
885 }
886 }
887 else
888 ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );
889
890 SwFrame* pRootFrame = GetUpper();
891
892 // cut all connections
893 RemoveFromLayout();
894
895 if ( pRootFrame )
896 static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
897 }
898
Paste(SwFrame * pParent,SwFrame * pSibling)899 void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
900 {
901 OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
902 OSL_ENSURE( pParent, "No parent for Paste()." );
903 OSL_ENSURE( pParent != this, "I'm my own parent." );
904 OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
905 OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
906 "I am still registered somewhere." );
907
908 // insert into tree structure
909 InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
910
911 // increase the root's page number
912 static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
913 if( GetPrev() )
914 SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
915 else
916 SetPhyPageNum( 1 );
917 SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
918 if ( pPg )
919 {
920 while ( pPg )
921 {
922 ++pPg->m_nPhyPageNum;
923 pPg->InvalidatePos_();
924 pPg->InvalidateLayout();
925 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
926 }
927 }
928 else
929 ::SetLastPage( this );
930
931 if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
932 InvalidateSize_();
933
934 InvalidatePos();
935
936 SwViewShell *pSh = getRootFrame()->GetCurrShell();
937 if ( pSh )
938 pSh->SetFirstVisPageInvalid();
939
940 getRootFrame()->CheckViewLayout( nullptr, nullptr );
941 }
942
lcl_PrepFlyInCntRegister(SwContentFrame * pFrame)943 static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
944 {
945 pFrame->Prepare( PrepareHint::Register );
946 if( !pFrame->GetDrawObjs() )
947 return;
948
949 for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
950 {
951 // #i28701#
952 if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
953 {
954 SwContentFrame *pCnt = pFly->ContainsContent();
955 while ( pCnt )
956 {
957 lcl_PrepFlyInCntRegister( pCnt );
958 pCnt = pCnt->GetNextContentFrame();
959 }
960 }
961 }
962 }
963
PrepareRegisterChg()964 void SwPageFrame::PrepareRegisterChg()
965 {
966 SwContentFrame *pFrame = FindFirstBodyContent();
967 while( pFrame )
968 {
969 lcl_PrepFlyInCntRegister( pFrame );
970 pFrame = pFrame->GetNextContentFrame();
971 if( !IsAnLower( pFrame ) )
972 break;
973 }
974 if( !GetSortedObjs() )
975 return;
976
977 for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
978 {
979 // #i28701#
980 if ( auto pFly = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
981 {
982 pFrame = pFly->ContainsContent();
983 while ( pFrame )
984 {
985 ::lcl_PrepFlyInCntRegister( pFrame );
986 pFrame = pFrame->GetNextContentFrame();
987 }
988 }
989 }
990 }
991
992 namespace sw {
993
994 /// check if there's content on the page that requires it to exist
IsPageFrameEmpty(SwPageFrame const & rPage)995 bool IsPageFrameEmpty(SwPageFrame const& rPage)
996 {
997 bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
998 if (bExistEssentialObjs)
999 {
1000 // Only because the page has Flys does not mean that it is needed. If all Flys are
1001 // attached to generic content it is also superfluous (checking DocBody should be enough)
1002 // OD 19.06.2003 - consider that drawing objects in
1003 // header/footer are supported now.
1004 bool bOnlySuperfluousObjs = true;
1005 SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
1006 for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
1007 {
1008 // #i28701#
1009 SwAnchoredObject* pAnchoredObj = rObjs[i];
1010 // do not consider hidden objects
1011 if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
1012 pAnchoredObj->GetDrawObj()->GetLayer() ) &&
1013 !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
1014 {
1015 bOnlySuperfluousObjs = false;
1016 }
1017 }
1018 bExistEssentialObjs = !bOnlySuperfluousObjs;
1019 }
1020
1021 // optimization: check first if essential objects exist.
1022 const SwLayoutFrame* pBody = nullptr;
1023 if ( bExistEssentialObjs ||
1024 rPage.FindFootnoteCont() ||
1025 (nullptr != (pBody = rPage.FindBodyCont()) &&
1026 ( pBody->ContainsContent() ||
1027 // #i47580#
1028 // Do not delete page if there's an empty tabframe
1029 // left. I think it might be correct to use ContainsAny()
1030 // instead of ContainsContent() to cover the empty-table-case,
1031 // but I'm not fully sure, since ContainsAny() also returns
1032 // SectionFrames. Therefore I prefer to do it the safe way:
1033 ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
1034 {
1035 return false;
1036 }
1037 else
1038 {
1039 return true;
1040 }
1041 }
1042
1043 } // namespace sw
1044
1045 //FIXME: provide missing documentation
1046 /** Check all pages (starting from the given one) if they use the appropriate frame format.
1047 *
1048 * If "wrong" pages are found, try to fix this as simple as possible.
1049 *
1050 * Also delete pages that don't have content on them.
1051 *
1052 * @param pStart the page from where to start searching
1053 * @param bNotifyFields
1054 * @param ppPrev
1055 */
CheckPageDescs(SwPageFrame * pStart,bool bNotifyFields,SwPageFrame ** ppPrev)1056 void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
1057 {
1058 SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
1059 assert(pStart && "no starting page.");
1060
1061 SwViewShell *pSh = pStart->getRootFrame()->GetCurrShell();
1062 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
1063
1064 if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1065 {
1066 pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
1067 SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
1068 << pStart->GetPhyPageNum() << ")" );
1069 return;
1070 }
1071
1072 // For the update of page numbering fields, nDocPos provides
1073 // the page position from where invalidation should start.
1074 SwTwips nDocPos = LONG_MAX;
1075
1076 SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
1077 SwDoc* pDoc = pStart->GetFormat()->GetDoc();
1078 const bool bFootnotes = !pDoc->GetFootnoteIdxs().empty();
1079
1080 SwPageFrame *pPage = pStart;
1081 if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
1082 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1083 while ( pPage )
1084 {
1085 SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1086 SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
1087
1088 SwPageDesc *pDesc = pPage->FindPageDesc();
1089 /// page is intentionally empty page
1090 bool bIsEmpty = pPage->IsEmptyPage();
1091 // false for intentionally empty pages, they need additional check
1092 bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
1093 bool bIsOdd = pPage->OnRightPage();
1094 bool bWantOdd = pPage->WannaRightPage();
1095 bool bFirst = pPage->OnFirstPage();
1096 SwFrameFormat *pFormatWish = bWantOdd
1097 ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);
1098
1099 if ( bIsOdd != bWantOdd ||
1100 pDesc != pPage->GetPageDesc() || // wrong Desc
1101 ( pFormatWish != pPage->GetFormat() && // wrong format and
1102 ( !bIsEmpty || pFormatWish ) // not blank /empty
1103 )
1104 )
1105 {
1106 // Updating a page might take a while, so check the WaitCursor
1107 if( pImp )
1108 pImp->CheckWaitCursor();
1109
1110 // invalidate the field, starting from here
1111 if ( nDocPos == LONG_MAX )
1112 nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();
1113
1114 // Cases:
1115 // 1. Empty page should be "normal" page -> remove empty page and take next one
1116 // 2. Empty page should have different descriptor -> change
1117 // 3. Normal page should be empty -> insert empty page if previous page
1118 // is not empty, otherwise see (6).
1119 // 4. Normal page should have different descriptor -> change
1120 // 5. Normal page should have different format -> change
1121 // 6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc
1122
1123 if ( bIsEmpty && ( pFormatWish || //1.
1124 ( !bWantOdd && !pPrevPage ) ) )
1125 {
1126 // Check all cases for the next page, so we don't oscillate empty pages
1127 // Skip case 1 and 2, as we require a non-empty next page to save the empty page
1128 // Case 3 is the one we actually want to predict and skip
1129 // We can skip the empty check of case 3, as we just work on an existing next page
1130 bool bNextWantOdd;
1131 SwPageDesc *pNextDesc;
1132 if ( pNextPage && !pNextPage->IsEmptyPage() && //3.
1133 pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
1134 pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) ) //4.
1135 {
1136 bool bNextFirst = pNextPage->OnFirstPage();
1137 SwFrameFormat *pNextFormatWish = bNextWantOdd ? //5.
1138 pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
1139 if ( !pNextFormatWish ) // 6.
1140 pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
1141 if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
1142 {
1143 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1144 << " c: 1+3 - skip next page of p: " << pPage );
1145 if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
1146 pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
1147 // We can skip the next page, as all checks were already done!
1148 pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
1149 continue;
1150 }
1151 }
1152
1153 pPage->Cut();
1154 bool bUpdatePrev = false;
1155 if (ppPrev && *ppPrev == pPage)
1156 bUpdatePrev = true;
1157 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1158 << " c: 1 - destroy p: " << pPage );
1159 SwFrame::DestroyFrame(pPage);
1160 if ( pStart == pPage )
1161 pStart = pNextPage;
1162 pPage = pNextPage;
1163 if (bUpdatePrev)
1164 *ppPrev = pNextPage;
1165 continue;
1166 }
1167 else if ( bIsEmpty && !pFormatWish && //2.
1168 pDesc != pPage->GetPageDesc() )
1169 {
1170 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1171 << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
1172 pPage->SetPageDesc( pDesc, nullptr );
1173 }
1174 else if ( !bIsEmpty && //3.
1175 bIsOdd != bWantOdd &&
1176 ( ( !pPrevPage && !bWantOdd ) ||
1177 ( pPrevPage && !pPrevPage->IsEmptyPage() )
1178 )
1179 )
1180 {
1181 if ( pPrevPage )
1182 pDesc = pPrevPage->GetPageDesc();
1183 SwPageFrame *pTmp = new SwPageFrame( pDoc->GetEmptyPageFormat(), pRoot, pDesc );
1184 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1185 << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
1186 pTmp->Paste( pRoot, pPage );
1187 pTmp->PreparePage( false );
1188 pPage = pTmp;
1189 isPageFrameEmpty = false; // don't delete it right away!
1190 }
1191 else if ( pPage->GetPageDesc() != pDesc ) //4.
1192 {
1193 SwPageDesc *pOld = pPage->GetPageDesc();
1194 pPage->SetPageDesc( pDesc, pFormatWish );
1195 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1196 << " c: 4 - set desc + format p: " << pPage
1197 << " d: " << pDesc << " f: " << pFormatWish );
1198 if ( bFootnotes )
1199 {
1200 // If specific values of the FootnoteInfo are changed, something has to happen.
1201 // We try to limit the damage...
1202 // If the page has no FootnoteCont it might be problematic.
1203 // Let's hope that invalidation is enough.
1204 SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
1205 if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
1206 pCont->InvalidateAll_();
1207 }
1208 }
1209 else if ( pFormatWish && pPage->GetFormat() != pFormatWish ) //5.
1210 {
1211 pPage->SetFrameFormat( pFormatWish );
1212 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1213 << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
1214 }
1215 else if ( !pFormatWish ) //6.
1216 {
1217 // get format with inverted logic
1218 pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
1219 if ( pFormatWish && pPage->GetFormat() != pFormatWish )
1220 {
1221 pPage->SetFrameFormat( pFormatWish );
1222 SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
1223 << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
1224 }
1225 }
1226 #if OSL_DEBUG_LEVEL > 0
1227 else
1228 {
1229 OSL_FAIL( "CheckPageDescs, missing solution" );
1230 }
1231 #endif
1232 }
1233 assert(!bIsEmpty || !isPageFrameEmpty);
1234 const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
1235 if (bWantRemovePage && !pPage->IsDeleteForbidden())
1236 {
1237 // It also might be that an empty page is not needed at all.
1238 // However, the algorithm above cannot determine that. It is not needed if the following
1239 // page can live without it. Do obtain that information, we need to dig deeper...
1240 SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
1241 if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
1242 {
1243 // The following page can find a FrameFormat or has no successor -> empty page not needed
1244 SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
1245 if (isPageFrameEmpty && pPage->GetPrev())
1246 { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
1247 pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
1248 }
1249 pPage->Cut();
1250 bool bUpdatePrev = false;
1251 if (ppPrev && *ppPrev == pPage)
1252 bUpdatePrev = true;
1253 SwFrame::DestroyFrame(pPage);
1254 SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
1255 if ( pStart == pPage )
1256 pStart = pTmp;
1257 pPage = pTmp;
1258 if (bUpdatePrev)
1259 *ppPrev = pTmp;
1260 continue;
1261 }
1262 }
1263 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1264 }
1265
1266 pRoot->SetAssertFlyPages();
1267 SwRootFrame::AssertPageFlys( pStart );
1268
1269 if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
1270 {
1271 SwDocPosUpdate aMsgHint( nDocPos );
1272 pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1273 }
1274
1275 #if OSL_DEBUG_LEVEL > 0
1276 //1. check if two empty pages are behind one another
1277 bool bEmpty = false;
1278 SwPageFrame *pPg = pStart;
1279 while ( pPg )
1280 {
1281 if ( pPg->IsEmptyPage() )
1282 {
1283 if ( bEmpty )
1284 {
1285 OSL_FAIL( "double empty pages." );
1286 break; // once is enough
1287 }
1288 bEmpty = true;
1289 }
1290 else
1291 bEmpty = false;
1292
1293 pPg = static_cast<SwPageFrame*>(pPg->GetNext());
1294 }
1295 #endif
1296 SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
1297 }
1298
1299 namespace
1300 {
isDeleteForbidden(const SwPageFrame * pDel)1301 bool isDeleteForbidden(const SwPageFrame *pDel)
1302 {
1303 if (pDel->IsDeleteForbidden())
1304 return true;
1305 const SwLayoutFrame* pBody = pDel->FindBodyCont();
1306 const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
1307 return pBodyContent && pBodyContent->IsDeleteForbidden();
1308 }
1309
doInsertPage(SwRootFrame * pRoot,SwPageFrame ** pRefSibling,SwFrameFormat * pFormat,SwPageDesc * pDesc,bool bFootnote,SwPageFrame ** pRefPage)1310 bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
1311 SwFrameFormat *pFormat, SwPageDesc *pDesc,
1312 bool bFootnote, SwPageFrame **pRefPage )
1313 {
1314 SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
1315 SwPageFrame *pSibling = *pRefSibling;
1316 if ( pRefPage )
1317 {
1318 *pRefPage = pPage;
1319 SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
1320 << " d: " << pDesc << " f: " << pFormat );
1321 }
1322 else
1323 SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
1324 << pPage << " d: " << pDesc );
1325 pPage->Paste( pRoot, pSibling );
1326
1327 SwViewShell* pViewShell = pRoot->GetCurrShell();
1328 if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
1329 {
1330 // Hide-whitespace mode does not shrink the last page, so resize the page that used to
1331 // be the last one.
1332 if (SwFrame* pPrevPage = pPage->GetPrev())
1333 {
1334 pPrevPage->InvalidateSize();
1335 }
1336 }
1337
1338 pPage->PreparePage( bFootnote );
1339 // If the sibling has no body text, destroy it as long as it is no footnote page.
1340 if ( pSibling && !pSibling->IsFootnotePage() &&
1341 !pSibling->FindFirstBodyContent() &&
1342 (!pRefPage || !isDeleteForbidden(pSibling)) )
1343 {
1344 pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
1345 return false;
1346 }
1347 return true;
1348 }
1349 }
1350
InsertPage(SwPageFrame * pPrevPage,bool bFootnote)1351 SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
1352 {
1353 SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
1354 SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
1355 SwPageDesc *pDesc = nullptr;
1356
1357 // insert right (odd) or left (even) page?
1358 bool bNextRightPage = !pPrevPage->OnRightPage();
1359 bool bWishedRightPage = bNextRightPage;
1360
1361 // Which PageDesc is relevant?
1362 // For ContentFrame take the one from format if provided,
1363 // otherwise from the Follow of the PrevPage
1364 if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
1365 {
1366 SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
1367 pDesc = rDesc.GetPageDesc();
1368 if ( rDesc.GetNumOffset() )
1369 {
1370 ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
1371 bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
1372 // use the opportunity to set the flag at root
1373 pRoot->SetVirtPageNum( true );
1374 }
1375 }
1376 if ( !pDesc )
1377 pDesc = pPrevPage->GetPageDesc()->GetFollow();
1378
1379 assert(pDesc && "Missing PageDesc");
1380 if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
1381 bWishedRightPage = !bWishedRightPage;
1382 bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();
1383
1384 SwDoc *pDoc = pPrevPage->GetFormat()->GetDoc();
1385 bool bCheckPages = false;
1386 // If there is no FrameFormat for this page, create an empty page.
1387 if (bWishedRightPage != bNextRightPage)
1388 {
1389 if( doInsertPage( pRoot, &pSibling, pDoc->GetEmptyPageFormat(),
1390 pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
1391 bCheckPages = true;
1392 }
1393 SwFrameFormat *const pFormat( bWishedRightPage
1394 ? pDesc->GetRightFormat(bWishedFirst)
1395 : pDesc->GetLeftFormat(bWishedFirst) );
1396 assert(pFormat);
1397 SwPageFrame *pPage = nullptr;
1398 if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
1399 bCheckPages = true;
1400
1401 if ( pSibling )
1402 {
1403 if ( bCheckPages )
1404 {
1405 CheckPageDescs( pSibling, false );
1406 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1407 SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
1408 if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
1409 {
1410 const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
1411 if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
1412 {
1413 pImp->GetLayAction().SetCheckPageNumDirect(
1414 pSibling->GetPhyPageNum() );
1415 SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
1416 << pSibling->GetPhyPageNum() );
1417 }
1418 return pPage;
1419 }
1420 }
1421 else
1422 SwRootFrame::AssertPageFlys( pSibling );
1423 }
1424
1425 // For the update of page numbering fields, nDocPos provides
1426 // the page position from where invalidation should start.
1427 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1428 if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
1429 {
1430 SwDocPosUpdate aMsgHint( pPrevPage->getFrameArea().Top() );
1431 pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1432 }
1433 return pPage;
1434 }
1435
SidebarPosition() const1436 sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
1437 {
1438 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1439 if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
1440 {
1441 return sw::sidebarwindows::SidebarPosition::RIGHT;
1442 }
1443 else
1444 {
1445 const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
1446 const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
1447 const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());
1448
1449 return bRightSidebar
1450 ? sw::sidebarwindows::SidebarPosition::RIGHT
1451 : sw::sidebarwindows::SidebarPosition::LEFT;
1452 }
1453 }
1454
GrowFrame(SwTwips nDist,bool bTst,bool)1455 SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
1456 {
1457 if ( !bTst )
1458 {
1459 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1460 aFrm.AddHeight(nDist );
1461 }
1462
1463 return nDist;
1464 }
1465
ShrinkFrame(SwTwips nDist,bool bTst,bool)1466 SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
1467 {
1468 OSL_ENSURE( nDist >= 0, "nDist < 0." );
1469 OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );
1470
1471 if ( !bTst )
1472 {
1473 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1474 aFrm.AddHeight( -nDist );
1475 }
1476
1477 return nDist;
1478 }
1479
RemovePage(SwPageFrame ** pDelRef,SwRemoveResult eResult)1480 void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
1481 {
1482 SwPageFrame *pDel = *pDelRef;
1483 (*pDelRef) = static_cast<SwPageFrame*>(
1484 eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
1485 if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
1486 RemoveFootnotes( pDel, true );
1487 pDel->Cut();
1488 SwFrame::DestroyFrame( pDel );
1489 }
1490
1491 /// remove pages that are not needed at all
RemoveSuperfluous()1492 void SwRootFrame::RemoveSuperfluous()
1493 {
1494 // A page is empty if the body text area has no ContentFrame, but not if there
1495 // is at least one Fly or one footnote attached to the page. Two runs are
1496 // needed: one for endnote pages and one for the pages of the body text.
1497
1498 if ( !IsSuperfluous() )
1499 return;
1500 mbCheckSuperfluous = false;
1501
1502 SwPageFrame *pPage = GetLastPage();
1503 tools::Long nDocPos = LONG_MAX;
1504
1505 // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
1506 do
1507 {
1508 if (!sw::IsPageFrameEmpty(*pPage))
1509 {
1510 if ( pPage->IsFootnotePage() )
1511 {
1512 while ( pPage->IsFootnotePage() )
1513 {
1514 pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
1515 OSL_ENSURE( pPage, "only endnote pages remain." );
1516 }
1517 continue;
1518 }
1519 else
1520 pPage = nullptr;
1521 }
1522
1523 if ( pPage )
1524 {
1525 SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
1526 RemovePage( &pPage, SwRemoveResult::Prev );
1527 nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
1528 }
1529 } while ( pPage );
1530
1531 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1532 if ( nDocPos != LONG_MAX &&
1533 (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
1534 {
1535 SwDocPosUpdate aMsgHint( nDocPos );
1536 GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
1537 }
1538 }
1539
1540 /// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
AssertFlyPages()1541 void SwRootFrame::AssertFlyPages()
1542 {
1543 if ( !IsAssertFlyPages() )
1544 return;
1545 mbAssertFlyPages = false;
1546
1547 SwDoc *pDoc = GetFormat()->GetDoc();
1548 const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
1549
1550 // what page targets the "last" Fly?
1551 // note the needed pages in a set
1552 sal_uInt16 nMaxPg(0);
1553 o3tl::sorted_vector< sal_uInt16 > neededPages;
1554 neededPages.reserve(pTable->size());
1555
1556 for ( size_t i = 0; i < pTable->size(); ++i )
1557 {
1558 const SwFormatAnchor &rAnch = (*pTable)[i]->GetAnchor();
1559 if(!rAnch.GetContentAnchor())
1560 {
1561 const sal_uInt16 nPageNum(rAnch.GetPageNum());
1562
1563 // calc MaxPage (as before)
1564 nMaxPg = std::max(nMaxPg, nPageNum);
1565
1566 // note as needed page
1567 neededPages.insert(nPageNum);
1568 }
1569 }
1570
1571 // How many pages exist at the moment?
1572 // And are there EmptyPages that are needed?
1573 SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
1574 SwPageFrame* pPrevPage(nullptr);
1575 SwPageFrame* pFirstRevivedEmptyPage(nullptr);
1576
1577 while(pPage) // moved two while-conditions to break-statements (see below)
1578 {
1579 const sal_uInt16 nPageNum(pPage->GetPhyPageNum());
1580
1581 if(pPage->IsEmptyPage() &&
1582 nullptr != pPrevPage &&
1583 neededPages.find(nPageNum) != neededPages.end())
1584 {
1585 // This is an empty page, but it *is* needed since a SwFrame
1586 // is anchored at it directly. Initially these SwFrames are
1587 // not fully initialized. Need to change the format of this SwFrame
1588 // and let the ::Notify mechanism newly evaluate
1589 // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
1590 // adapted from ::InsertPage (used below), this needs previous page
1591 bool bWishedRightPage(!pPrevPage->OnRightPage());
1592 SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
1593 assert(pDesc && "Missing PageDesc");
1594
1595 if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
1596 {
1597 bWishedRightPage = !bWishedRightPage;
1598 }
1599
1600 bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
1601 SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));
1602
1603 // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
1604 // m_bEmptyPage, too
1605 pPage->SetFrameFormat(pFormat);
1606
1607 if(nullptr == pFirstRevivedEmptyPage)
1608 {
1609 // remember first (lowest) SwPageFrame which needed correction
1610 pFirstRevivedEmptyPage = pPage;
1611 }
1612 }
1613
1614 // original while-condition II
1615 if(nullptr == pPage->GetNext())
1616 {
1617 break;
1618 }
1619
1620 // original while-condition III
1621 if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
1622 {
1623 break;
1624 }
1625
1626 pPrevPage = pPage;
1627 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1628 }
1629
1630 if ( nMaxPg > pPage->GetPhyPageNum() )
1631 {
1632 for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
1633 pPage = InsertPage( pPage, false );
1634
1635 // If the endnote pages are now corrupt, destroy them.
1636 if ( !pDoc->GetFootnoteIdxs().empty() )
1637 {
1638 pPage = static_cast<SwPageFrame*>(Lower());
1639 while ( pPage && !pPage->IsFootnotePage() )
1640 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1641
1642 if ( pPage )
1643 {
1644 SwPageDesc *pTmpDesc = pPage->FindPageDesc();
1645 bool isRightPage = pPage->OnRightPage();
1646 if ( pPage->GetFormat() !=
1647 (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
1648 RemoveFootnotes( pPage, false, true );
1649 }
1650 }
1651 }
1652
1653 // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
1654 // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
1655 // which did think until now that these Page(s) are empty.
1656 // After trying to correct myself I found SwRootFrame::AssertPageFlys
1657 // directly below that already does that, so use it.
1658 if(nullptr != pFirstRevivedEmptyPage)
1659 {
1660 AssertPageFlys(pFirstRevivedEmptyPage);
1661 }
1662
1663 //Remove masters that haven't been replaced yet from the list.
1664 RemoveMasterObjs( mpDrawPage );
1665
1666 #if OSL_DEBUG_LEVEL > 0
1667 pPage = static_cast<SwPageFrame*>(Lower());
1668 while ( pPage && pPage->GetNext() &&
1669 !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
1670 {
1671 SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1672 << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1673 << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1674 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1675 }
1676 SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
1677 << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
1678 << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
1679 #endif
1680 }
1681
1682 /// Ensure that after the given page all page-bound objects are located on the correct page
AssertPageFlys(SwPageFrame * pPage)1683 void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
1684 {
1685 SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
1686 while ( pPage )
1687 {
1688 if (pPage->GetSortedObjs())
1689 {
1690 size_t i = 0;
1691 while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
1692 {
1693 // #i28701#
1694 SwFrameFormat& rFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
1695 const SwFormatAnchor &rAnch = rFormat.GetAnchor();
1696 const sal_uInt16 nPg = rAnch.GetPageNum();
1697 if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
1698 nPg != pPage->GetPhyPageNum() )
1699 {
1700 SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
1701 // If on the wrong page, check if previous page is empty
1702 if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
1703 static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
1704 {
1705 // It can move by itself. Just send a modify to its anchor attribute.
1706 #if OSL_DEBUG_LEVEL > 1
1707 const size_t nCnt = pPage->GetSortedObjs()->size();
1708 rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1709 OSL_ENSURE( !pPage->GetSortedObjs() ||
1710 nCnt != pPage->GetSortedObjs()->size(),
1711 "Object couldn't be reattached!" );
1712 #else
1713 rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
1714 #endif
1715 // Do not increment index, in this case
1716 continue;
1717 }
1718 }
1719 ++i;
1720 }
1721 }
1722 pPage = static_cast<SwPageFrame*>(pPage->GetNext());
1723 }
1724 SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
1725 }
1726
ChgSize(const Size & aNewSize)1727 Size SwRootFrame::ChgSize( const Size& aNewSize )
1728 {
1729 {
1730 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1731 aFrm.SSize(aNewSize);
1732 }
1733
1734 InvalidatePrt_();
1735 mbFixSize = false;
1736 return getFrameArea().SSize();
1737 }
1738
MakeAll(vcl::RenderContext *)1739 void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
1740 {
1741 if ( !isFrameAreaPositionValid() )
1742 {
1743 setFrameAreaPositionValid(true);
1744 SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
1745 aFrm.Pos().setX(DOCUMENTBORDER);
1746 aFrm.Pos().setY(DOCUMENTBORDER);
1747 }
1748
1749 if ( !isFramePrintAreaValid() )
1750 {
1751 setFramePrintAreaValid(true);
1752 SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
1753 aPrt.Pos().setX(0);
1754 aPrt.Pos().setY(0);
1755 aPrt.SSize( getFrameArea().SSize() );
1756 }
1757
1758 if ( !isFrameAreaSizeValid() )
1759 {
1760 // SSize is set by the pages (Cut/Paste).
1761 setFrameAreaSizeValid(true);
1762 }
1763 }
1764
ImplInvalidateBrowseWidth()1765 void SwRootFrame::ImplInvalidateBrowseWidth()
1766 {
1767 mbBrowseWidthValid = false;
1768 SwFrame *pPg = Lower();
1769 while ( pPg )
1770 {
1771 pPg->InvalidateSize();
1772 pPg = pPg->GetNext();
1773 }
1774 }
1775
ImplCalcBrowseWidth()1776 void SwRootFrame::ImplCalcBrowseWidth()
1777 {
1778 OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
1779 "CalcBrowseWidth and not in BrowseView" );
1780
1781 // The (minimal) with is determined from borders, tables and paint objects.
1782 // It is calculated based on the attributes. Thus, it is not relevant how wide they are
1783 // currently but only how wide they want to be.
1784 // Frames and paint objects inside other objects (frames, tables) do not count.
1785 // Borders and columns are not taken into account.
1786
1787 SwFrame *pFrame = ContainsContent();
1788 while ( pFrame && !pFrame->IsInDocBody() )
1789 pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
1790 if ( !pFrame )
1791 return;
1792
1793 mbBrowseWidthValid = true;
1794 SwViewShell *pSh = getRootFrame()->GetCurrShell();
1795 mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;
1796
1797 do
1798 {
1799 if ( pFrame->IsInTab() )
1800 pFrame = pFrame->FindTabFrame();
1801
1802 if ( pFrame->IsTabFrame() &&
1803 !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
1804 {
1805 SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
1806 const SwBorderAttrs &rAttrs = *aAccess.Get();
1807 const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
1808 tools::Long nWidth = rAttrs.GetSize().Width();
1809 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)
1810 text::HoriOrientation::FULL != rHori.GetHoriOrient() )
1811 {
1812 const SwHTMLTableLayout *pLayoutInfo =
1813 static_cast<const SwTabFrame *>(pFrame)->GetTable()
1814 ->GetHTMLTableLayout();
1815 if ( pLayoutInfo )
1816 nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );
1817
1818 switch ( rHori.GetHoriOrient() )
1819 {
1820 case text::HoriOrientation::NONE:
1821 // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
1822 nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
1823 break;
1824 case text::HoriOrientation::LEFT_AND_WIDTH:
1825 nWidth += rAttrs.CalcLeft( pFrame );
1826 break;
1827 default:
1828 break;
1829 }
1830 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1831 }
1832 }
1833 else if ( pFrame->GetDrawObjs() )
1834 {
1835 for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
1836 {
1837 // #i28701#
1838 SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
1839 const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
1840 const bool bFly = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr;
1841 if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
1842 || rFormat.GetFrameSize().GetWidthPercent())
1843 {
1844 continue;
1845 }
1846
1847 tools::Long nWidth = 0;
1848 switch ( rFormat.GetAnchor().GetAnchorId() )
1849 {
1850 case RndStdIds::FLY_AS_CHAR:
1851 nWidth = bFly ? rFormat.GetFrameSize().GetWidth() :
1852 pAnchoredObj->GetObjRect().Width();
1853 break;
1854 case RndStdIds::FLY_AT_PARA:
1855 {
1856 // #i33170#
1857 // Reactivated old code because
1858 // nWidth = pAnchoredObj->GetObjRect().Right()
1859 // gives wrong results for objects that are still
1860 // at position FAR_AWAY.
1861 if ( bFly )
1862 {
1863 nWidth = rFormat.GetFrameSize().GetWidth();
1864 const SwFormatHoriOrient &rHori = rFormat.GetHoriOrient();
1865 switch ( rHori.GetHoriOrient() )
1866 {
1867 case text::HoriOrientation::NONE:
1868 nWidth += rHori.GetPos();
1869 break;
1870 case text::HoriOrientation::INSIDE:
1871 case text::HoriOrientation::LEFT:
1872 if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
1873 nWidth += pFrame->getFramePrintArea().Left();
1874 break;
1875 default:
1876 break;
1877 }
1878 }
1879 else
1880 // Paint objects to not have attributes and
1881 // are defined by their current size
1882 nWidth = pAnchoredObj->GetObjRect().Right() -
1883 pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
1884 }
1885 break;
1886 default: /* do nothing */;
1887 }
1888 mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
1889 }
1890 }
1891 pFrame = pFrame->FindNextCnt();
1892 } while ( pFrame );
1893 }
1894
StartAllAction()1895 void SwRootFrame::StartAllAction()
1896 {
1897 if ( GetCurrShell() )
1898 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1899 {
1900 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1901 pCursorShell->StartAction();
1902 else
1903 rSh.StartAction();
1904 }
1905 }
1906
EndAllAction(bool bVirDev)1907 void SwRootFrame::EndAllAction( bool bVirDev )
1908 {
1909 if ( !GetCurrShell() )
1910 return;
1911
1912 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1913 {
1914 const bool bOldEndActionByVirDev = rSh.IsEndActionByVirDev();
1915 rSh.SetEndActionByVirDev( bVirDev );
1916 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1917 {
1918 pCursorShell->EndAction();
1919 pCursorShell->CallChgLnk();
1920 if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
1921 pFEShell->SetChainMarker();
1922 }
1923 else
1924 rSh.EndAction();
1925 rSh.SetEndActionByVirDev( bOldEndActionByVirDev );
1926 }
1927 }
1928
UnoRemoveAllActions()1929 void SwRootFrame::UnoRemoveAllActions()
1930 {
1931 if ( !GetCurrShell() )
1932 return;
1933
1934 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1935 {
1936 // #i84729#
1937 // No end action, if <SwViewShell> instance is currently in its end action.
1938 // Recursives calls to <::EndAction()> are not allowed.
1939 if ( !rSh.IsInEndAction() )
1940 {
1941 OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
1942 bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr;
1943 bool bFE = dynamic_cast<const SwFEShell*>( &rSh) != nullptr;
1944 sal_uInt16 nRestore = 0;
1945 while( rSh.ActionCount() )
1946 {
1947 if( bCursor )
1948 {
1949 static_cast<SwCursorShell*>(&rSh)->EndAction();
1950 static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
1951 if ( bFE )
1952 static_cast<SwFEShell*>(&rSh)->SetChainMarker();
1953 }
1954 else
1955 rSh.EndAction();
1956 nRestore++;
1957 }
1958 rSh.SetRestoreActions(nRestore);
1959 }
1960 rSh.LockView(true);
1961 }
1962 }
1963
UnoRestoreAllActions()1964 void SwRootFrame::UnoRestoreAllActions()
1965 {
1966 if ( !GetCurrShell() )
1967 return;
1968
1969 for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
1970 {
1971 sal_uInt16 nActions = rSh.GetRestoreActions();
1972 while( nActions-- )
1973 {
1974 if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
1975 pCursorShell->StartAction();
1976 else
1977 rSh.StartAction();
1978 }
1979 rSh.SetRestoreActions(0);
1980 rSh.LockView(false);
1981 }
1982 }
1983
1984 // Helper functions for SwRootFrame::CheckViewLayout
1985 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );
1986
lcl_MoveAllLowerObjs(SwFrame * pFrame,const Point & rOffset)1987 static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
1988 {
1989 const bool bPage = pFrame->IsPageFrame();
1990 const SwSortedObjs* pSortedObj = bPage
1991 ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
1992 : pFrame->GetDrawObjs();
1993 if (pSortedObj == nullptr)
1994 return;
1995
1996 // note: pSortedObj elements may be removed and inserted from
1997 // MoveObjectIfActive(), invalidating iterators
1998 // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
1999 for (size_t i = 0; i < pSortedObj->size(); ++i)
2000 {
2001 SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
2002 const SwFrameFormat& rObjFormat = pAnchoredObj->GetFrameFormat();
2003 const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
2004
2005 // all except from the as character anchored objects are moved
2006 // when processing the page frame:
2007 if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
2008 continue;
2009
2010 SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );
2011
2012 if ( auto pFlyFrame = dynamic_cast<SwFlyFrame *>( pAnchoredObj ) )
2013 {
2014 lcl_MoveAllLowers( pFlyFrame, rOffset );
2015 // tdf#138785 update position specific to as-char flys
2016 if (pFlyFrame->IsFlyInContentFrame())
2017 {
2018 static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
2019 }
2020 pFlyFrame->NotifyDrawObj();
2021 // --> let the active embedded object be moved
2022 SwFrame* pLower = pFlyFrame->Lower();
2023 if ( pLower && pLower->IsNoTextFrame() )
2024 {
2025 SwRootFrame* pRoot = pLower->getRootFrame();
2026 SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
2027 if ( pSh )
2028 {
2029 SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
2030 SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
2031 if ( pNode )
2032 {
2033 svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
2034 if ( xObj.is() )
2035 {
2036 for(SwViewShell& rSh : pSh->GetRingContainer())
2037 {
2038 SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
2039 if ( pFEShell )
2040 pFEShell->MoveObjectIfActive( xObj, rOffset );
2041 }
2042 }
2043 }
2044 }
2045 }
2046 }
2047 else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
2048 {
2049 // don't touch objects that are not yet positioned:
2050 if ( pAnchoredDrawObj->NotYetPositioned() )
2051 continue;
2052
2053 const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
2054 const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
2055 pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
2056 pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );
2057
2058 // clear contour cache
2059 if ( pAnchoredDrawObj->GetFrameFormat().GetSurround().IsContour() )
2060 ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
2061 }
2062 // #i92511#
2063 // cache for object rectangle inclusive spaces has to be invalidated.
2064 pAnchoredObj->InvalidateObjRectWithSpaces();
2065 }
2066 }
2067
lcl_MoveAllLowers(SwFrame * pFrame,const Point & rOffset)2068 static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
2069 {
2070 const SwRect aFrame( pFrame->getFrameArea() );
2071
2072 // first move the current frame
2073 // RotateFlyFrame3: moved to transform_translate instead of
2074 // direct modification to allow the SwFrame evtl. needed own reactions
2075 pFrame->transform_translate(rOffset);
2076
2077 // Don't forget accessibility:
2078 if( pFrame->IsAccessibleFrame() )
2079 {
2080 SwRootFrame *pRootFrame = pFrame->getRootFrame();
2081 if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
2082 pRootFrame->GetCurrShell() )
2083 {
2084 pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
2085 }
2086 }
2087
2088 // the move any objects
2089 lcl_MoveAllLowerObjs( pFrame, rOffset );
2090
2091 // finally, for layout frames we have to call this function recursively:
2092 if ( dynamic_cast< const SwLayoutFrame *>( pFrame ) != nullptr )
2093 {
2094 SwFrame* pLowerFrame = pFrame->GetLower();
2095 while ( pLowerFrame )
2096 {
2097 lcl_MoveAllLowers( pLowerFrame, rOffset );
2098 pLowerFrame = pLowerFrame->GetNext();
2099 }
2100 }
2101 }
2102
2103 // Calculate how the pages have to be positioned
CheckViewLayout(const SwViewOption * pViewOpt,const SwRect * pVisArea)2104 void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
2105 {
2106 SwViewShell* pSh = GetCurrShell();
2107 vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
2108 // #i91432#
2109 // No calculation of page positions, if only an empty page is present.
2110 // This situation occurs when <SwRootFrame> instance is in construction
2111 // and the document contains only left pages.
2112 if ( Lower()->GetNext() == nullptr &&
2113 static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
2114 {
2115 return;
2116 }
2117
2118 if ( !pVisArea )
2119 {
2120 // no early return for bNewPage
2121 if ( mnViewWidth < 0 )
2122 mnViewWidth = 0;
2123 }
2124 else
2125 {
2126 assert(pViewOpt && "CheckViewLayout required ViewOptions");
2127
2128 const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
2129 const bool bBookMode = pViewOpt->IsViewLayoutBookMode();
2130
2131 if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
2132 return;
2133
2134 mnColumns = nColumns;
2135 mbBookMode = bBookMode;
2136 mnViewWidth = pVisArea->Width();
2137 mbSidebarChanged = false;
2138 }
2139
2140 if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
2141 {
2142 mnColumns = 1;
2143 mbBookMode = false;
2144 }
2145
2146 Calc(pRenderContext);
2147
2148 const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
2149 SetCallbackActionEnabled( false );
2150
2151 maPageRects.clear();
2152
2153 const tools::Long nBorder = getFrameArea().Pos().getX();
2154 const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
2155 const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
2156 : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
2157 : SwViewOption::defGapBetweenPages);
2158
2159 // check how many pages fit into the first page layout row:
2160 SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());
2161
2162 // will contain the number of pages per row. 0 means that
2163 // the page does not fit.
2164 tools::Long nWidthRemain = nVisWidth;
2165
2166 // after one row has been processed, these variables contain
2167 // the width of the row and the maximum of the page heights
2168 tools::Long nCurrentRowHeight = 0;
2169 tools::Long nCurrentRowWidth = 0;
2170
2171 // these variables are used to finally set the size of the
2172 // root frame
2173 tools::Long nSumRowHeight = 0;
2174 SwTwips nMinPageLeft = TWIPS_MAX;
2175 SwTwips nMaxPageRight = 0;
2176 SwPageFrame* pStartOfRow = pPageFrame;
2177 sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
2178 bool bFirstRow = true;
2179
2180 bool bPageChanged = false;
2181 const bool bRTL = !IsLeftToRightViewLayout();
2182 const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );
2183
2184 while ( pPageFrame )
2185 {
2186 // we consider the current page to be "start of row" if
2187 // 1. it is the first page in the current row or
2188 // 2. it is the second page in the row and the first page is an empty page in non-book view:
2189 const bool bStartOfRow = pPageFrame == pStartOfRow ||
2190 ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );
2191
2192 const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;
2193
2194 // no half doc border space for first page in each row and
2195 tools::Long nPageWidth = 0;
2196 tools::Long nPageHeight = 0;
2197
2198 if ( mbBookMode )
2199 {
2200 const SwFrame& rFormatPage = pPageFrame->GetFormatPage();
2201
2202 nPageWidth = rFormatPage.getFrameArea().Width() + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
2203 nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
2204 }
2205 else
2206 {
2207 if ( !pPageFrame->IsEmptyPage() )
2208 {
2209 nPageWidth = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
2210 nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
2211 }
2212 }
2213
2214 if ( !bEmptyPage )
2215 ++nNumberOfPagesInRow;
2216
2217 // finish current row if
2218 // 1. in dynamic mode the current page does not fit anymore or
2219 // 2. the current page exceeds the maximum number of columns
2220 bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
2221 (0 != mnColumns && mnColumns < nNumberOfPagesInRow);
2222
2223 // make sure that at least one page goes to the current row:
2224 if ( !bRowFinished || bStartOfRow )
2225 {
2226 // current page is allowed to be in current row
2227 nWidthRemain = nWidthRemain - nPageWidth;
2228
2229 nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
2230 nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );
2231
2232 pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());
2233
2234 if ( !pPageFrame )
2235 bRowFinished = true;
2236 }
2237
2238 if ( bRowFinished )
2239 {
2240 // pPageFrame now points to the first page in the new row or null
2241 // pStartOfRow points to the first page in the current row
2242
2243 // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
2244 if ( !pPageFrame && nWidthRemain > 0 )
2245 {
2246 // find last page in current row:
2247 const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
2248 while( pLastPageInCurrentRow->GetNext() )
2249 pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());
2250
2251 if ( pLastPageInCurrentRow->IsEmptyPage() )
2252 pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());
2253
2254 // check how many times the last page would still fit into the remaining space:
2255 sal_uInt16 nNumberOfVirtualPages = 0;
2256 const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
2257 SwTwips nRemain = nWidthRemain;
2258 SwTwips nVirtualPagesWidth = 0;
2259 SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;
2260
2261 while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
2262 {
2263 SwTwips nLastPageWidthWithGap = nLastPageWidth;
2264 if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
2265 nLastPageWidthWithGap += nGapBetweenPages;
2266
2267 if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
2268 {
2269 ++nNumberOfVirtualPages;
2270 nVirtualPagesWidth += nLastPageWidthWithGap;
2271 }
2272 nRemain = nRemain - nLastPageWidthWithGap;
2273 }
2274
2275 nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
2276 }
2277
2278 // first page in book mode is always special:
2279 if ( bFirstRow && mbBookMode )
2280 {
2281 // #i88036#
2282 nCurrentRowWidth +=
2283 pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2284 }
2285
2286 // center page if possible
2287 tools::Long nSizeDiff = 0;
2288 if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
2289 nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;
2290
2291 // adjust positions of pages in current row
2292 tools::Long nX = nSizeDiff;
2293
2294 const tools::Long nRowStart = nBorder + nSizeDiff;
2295 const tools::Long nRowEnd = nRowStart + nCurrentRowWidth;
2296
2297 if ( bFirstRow && mbBookMode )
2298 {
2299 // #i88036#
2300 nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
2301 }
2302
2303 SwPageFrame* pEndOfRow = pPageFrame;
2304 SwPageFrame* pPageToAdjust = pStartOfRow;
2305
2306 do
2307 {
2308 const SwPageFrame* pFormatPage = pPageToAdjust;
2309 if ( mbBookMode )
2310 pFormatPage = &pPageToAdjust->GetFormatPage();
2311
2312 const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
2313 const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
2314 const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
2315 const SwTwips nLeftPageAddOffset = bLeftSidebar ?
2316 nSidebarWidth :
2317 0;
2318
2319 Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
2320 Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );
2321
2322 // RTL view layout: Calculate mirrored page position
2323 if ( bRTL )
2324 {
2325 const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
2326 aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
2327 aNewPagePosWithLeftOffset = aNewPagePos;
2328 aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
2329 }
2330
2331 if ( aNewPagePosWithLeftOffset != aOldPagePos )
2332 {
2333 lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
2334 pPageToAdjust->SetCompletePaint();
2335 bPageChanged = true;
2336 }
2337
2338 // calculate area covered by the current page and store to
2339 // maPageRects. This is used e.g., for cursor setting
2340 const bool bFirstColumn = pPageToAdjust == pStartOfRow;
2341 const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
2342 const bool bLastRow = !pEndOfRow;
2343
2344 nMinPageLeft = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
2345 nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));
2346
2347 // border of nGapBetweenPages around the current page:
2348 SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
2349 aNewPagePos.getY(),
2350 pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
2351 nCurrentRowHeight );
2352
2353 static const tools::Long nOuterClickDiff = 1000000;
2354
2355 // adjust borders for these special cases:
2356 if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
2357 aPageRectWithBorders.SubLeft( nOuterClickDiff );
2358 if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
2359 aPageRectWithBorders.AddRight( nOuterClickDiff );
2360 if ( bFirstRow )
2361 aPageRectWithBorders.SubTop( nOuterClickDiff );
2362 if ( bLastRow )
2363 aPageRectWithBorders.AddBottom( nOuterClickDiff );
2364
2365 maPageRects.push_back( aPageRectWithBorders );
2366
2367 nX = nX + nCurrentPageWidth;
2368 pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());
2369
2370 // distance to next page
2371 if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
2372 {
2373 // in book view, we add the x gap before left (even) pages:
2374 if ( mbBookMode )
2375 {
2376 if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
2377 nX = nX + nGapBetweenPages;
2378 }
2379 else
2380 {
2381 // in non-book view, don't add x gap before
2382 // 1. the last empty page in a row
2383 // 2. after an empty page
2384 const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
2385 ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );
2386
2387 if ( !bDontAddGap )
2388 nX = nX + nGapBetweenPages;
2389 }
2390 }
2391 }
2392 while (pPageToAdjust && pPageToAdjust != pEndOfRow);
2393
2394 // adjust values for root frame size
2395 nSumRowHeight = nSumRowHeight + nCurrentRowHeight;
2396
2397 // start new row:
2398 nCurrentRowHeight = 0;
2399 nCurrentRowWidth = 0;
2400 pStartOfRow = pEndOfRow;
2401 nWidthRemain = nVisWidth;
2402 nNumberOfPagesInRow = 0;
2403 bFirstRow = false;
2404 } // end row finished
2405 } // end while
2406
2407 // set size of root frame:
2408 const Size aOldSize( getFrameArea().SSize() );
2409 const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );
2410
2411 if ( bPageChanged || aNewSize != aOldSize )
2412 {
2413 ChgSize( aNewSize );
2414 ::AdjustSizeChgNotify( this );
2415 Calc(pRenderContext);
2416
2417 if ( pSh && pSh->GetDoc()->GetDocShell() )
2418 {
2419 pSh->SetFirstVisPageInvalid();
2420 if (bOldCallbackActionEnabled)
2421 {
2422 pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
2423 pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
2424 }
2425 }
2426 }
2427
2428 maPagesArea.Pos( getFrameArea().Pos() );
2429 maPagesArea.SSize( aNewSize );
2430 if ( TWIPS_MAX != nMinPageLeft )
2431 maPagesArea.Left_( nMinPageLeft );
2432
2433 SetCallbackActionEnabled( bOldCallbackActionEnabled );
2434 }
2435
IsLeftToRightViewLayout() const2436 bool SwRootFrame::IsLeftToRightViewLayout() const
2437 {
2438 // Layout direction determined by layout direction of the first page.
2439 // #i88036#
2440 // Only ask a non-empty page frame for its layout direction
2441 assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
2442 const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
2443 return !rPage.IsRightToLeft() && !rPage.IsVertical();
2444 }
2445
GetFormatPage() const2446 const SwPageFrame& SwPageFrame::GetFormatPage() const
2447 {
2448 const SwPageFrame* pRet = this;
2449 if ( IsEmptyPage() )
2450 {
2451 pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
2452 // #i88035#
2453 // Typically a right empty page frame has a next non-empty page frame and
2454 // a left empty page frame has a previous non-empty page frame.
2455 // But under certain circumstances this assumption is not true -
2456 // e.g. during insertion of a left page at the end of the document right
2457 // after a left page in an intermediate state a right empty page does not
2458 // have a next page frame.
2459 if ( pRet == nullptr )
2460 {
2461 if ( OnRightPage() )
2462 {
2463 pRet = static_cast<const SwPageFrame*>( GetPrev() );
2464 }
2465 else
2466 {
2467 pRet = static_cast<const SwPageFrame*>( GetNext() );
2468 }
2469 }
2470 assert(pRet &&
2471 "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
2472 }
2473 return *pRet;
2474 }
2475
IsOverHeaderFooterArea(const Point & rPt,FrameControlType & rControl) const2476 bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
2477 {
2478 tools::Long nUpperLimit = 0;
2479 tools::Long nLowerLimit = 0;
2480 const SwFrame* pFrame = Lower();
2481 while ( pFrame )
2482 {
2483 if ( pFrame->IsBodyFrame() )
2484 {
2485 nUpperLimit = pFrame->getFrameArea().Top();
2486 nLowerLimit = pFrame->getFrameArea().Bottom();
2487 }
2488 else if ( pFrame->IsFootnoteContFrame() )
2489 nLowerLimit = pFrame->getFrameArea().Bottom();
2490
2491 pFrame = pFrame->GetNext();
2492 }
2493
2494 SwRect aHeaderArea( getFrameArea().TopLeft(),
2495 Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );
2496
2497 SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
2498 const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
2499 if ( aHeaderArea.IsInside( rPt ) )
2500 {
2501 if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
2502 {
2503 rControl = FrameControlType::Header;
2504 return true;
2505 }
2506 }
2507 else
2508 {
2509 SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
2510 Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );
2511
2512 if ( aFooterArea.IsInside( rPt ) &&
2513 (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
2514 {
2515 rControl = FrameControlType::Footer;
2516 return true;
2517 }
2518 }
2519
2520 return false;
2521 }
2522
CheckPageHeightValidForHideWhitespace(SwTwips nDiff)2523 bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
2524 {
2525 SwViewShell* pShell = getRootFrame()->GetCurrShell();
2526 if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
2527 {
2528 // When whitespace is hidden, the page frame has two heights: the
2529 // nominal (defined by the frame format), and the actual (which is
2530 // at most the nominal height, but can be smaller in case there is
2531 // no content for the whole page).
2532 // The layout size is the actual one, but we want to move the
2533 // content frame to a new page only in case it doesn't fit the
2534 // nominal size.
2535 if (nDiff < 0)
2536 {
2537 // Content frame doesn't fit the actual size, check if it fits the nominal one.
2538 const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
2539 const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
2540 tools::Long nWhitespace = rPageSize.getHeight() - getFrameArea().Height();
2541 if (nWhitespace > -nDiff)
2542 {
2543 // It does: don't move it and invalidate our page frame so
2544 // that it gets a larger height.
2545 return false;
2546 }
2547 }
2548 }
2549
2550 return true;
2551 }
2552
GetHeaderFrame() const2553 const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
2554 {
2555 const SwFrame* pLowerFrame = Lower();
2556 while (pLowerFrame)
2557 {
2558 if (pLowerFrame->IsHeaderFrame())
2559 return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
2560 pLowerFrame = pLowerFrame->GetNext();
2561 }
2562 return nullptr;
2563 }
2564
GetFooterFrame() const2565 const SwFooterFrame* SwPageFrame::GetFooterFrame() const
2566 {
2567 const SwFrame* pLowerFrame = Lower();
2568 while (pLowerFrame)
2569 {
2570 if (pLowerFrame->IsFooterFrame())
2571 return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
2572 pLowerFrame = pLowerFrame->GetNext();
2573 }
2574 return nullptr;
2575 }
2576
GetGridItem(SwPageFrame const * const pPage)2577 SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
2578 {
2579 if (pPage && pPage->HasGrid())
2580 {
2581 SwTextGridItem const& rGridItem(
2582 pPage->GetPageDesc()->GetMaster().GetTextGrid());
2583 if (GRID_NONE != rGridItem.GetGridType())
2584 {
2585 return &rGridItem;
2586 }
2587 }
2588 return nullptr;
2589 }
2590
GetGridWidth(SwTextGridItem const & rG,SwDoc const & rDoc)2591 sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
2592 {
2593 return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
2594 }
2595
2596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2597