2353 lines
94 KiB
C++
2353 lines
94 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <rootfrm.hxx>
|
|
#include <pagefrm.hxx>
|
|
#include <viewopt.hxx>
|
|
#include <frmatr.hxx>
|
|
#include <frmtool.hxx>
|
|
#include <txtftn.hxx>
|
|
#include <fmtftn.hxx>
|
|
#include <ndtxt.hxx>
|
|
#include <editeng/ulspitem.hxx>
|
|
#include <editeng/keepitem.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <svx/sdtaitm.hxx>
|
|
|
|
#include <fmtfsize.hxx>
|
|
#include <fmtanchr.hxx>
|
|
#include <fmtclbl.hxx>
|
|
|
|
#include <tabfrm.hxx>
|
|
#include <ftnfrm.hxx>
|
|
#include <txtfrm.hxx>
|
|
#include <sectfrm.hxx>
|
|
#include <dbg_lay.hxx>
|
|
|
|
#include <sortedobjs.hxx>
|
|
#include <layouter.hxx>
|
|
#include <flyfrms.hxx>
|
|
|
|
#include <DocumentSettingManager.hxx>
|
|
#include <IDocumentLayoutAccess.hxx>
|
|
|
|
// Move methods
|
|
|
|
/// Return value tells whether the Frame should be moved.
|
|
bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
|
|
{
|
|
if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
|
|
{
|
|
// Floating back a frm uses a bit of time unfortunately.
|
|
// The most common case is the following: The Frame wants to float to
|
|
// somewhere where the FixSize is the same that the Frame itself has already.
|
|
// In that case it's pretty easy to check if the Frame has enough space
|
|
// for its VarSize. If this is NOT the case, we already know that
|
|
// we don't need to move.
|
|
// The Frame checks itself whether it has enough space - respecting the fact
|
|
// that it could possibly split itself if needed.
|
|
// If, however, the FixSize differs from the Frame or Flys are involved
|
|
// (either in the old or the new position), checking is pointless,
|
|
// and we have to move the Frame just to see what happens - if there's
|
|
// some space available to do it, that is.
|
|
|
|
// The FixSize of the containers of Contents is always the width.
|
|
|
|
// If we moved more than one sheet back (for example jumping over empty
|
|
// pages), we have to move either way. Otherwise, if the Frame doesn't fit
|
|
// into the page, empty pages wouldn't be respected anymore.
|
|
sal_uInt8 nMoveAnyway = 0;
|
|
SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
|
|
SwPageFrame *pOldPage = FindPageFrame();
|
|
|
|
if ( SwFlowFrame::IsMoveBwdJump() )
|
|
return true;
|
|
|
|
if( IsInFootnote() && IsInSct() )
|
|
{
|
|
SwFootnoteFrame* pFootnote = FindFootnoteFrame();
|
|
SwSectionFrame* pMySect = pFootnote->FindSctFrame();
|
|
if( pMySect && pMySect->IsFootnoteLock() )
|
|
{
|
|
SwSectionFrame *pSect = pNewUpper->FindSctFrame();
|
|
while( pSect && pSect->IsInFootnote() )
|
|
pSect = pSect->GetUpper()->FindSctFrame();
|
|
OSL_ENSURE( pSect, "Escaping footnote" );
|
|
if( pSect != pMySect )
|
|
return false;
|
|
}
|
|
}
|
|
SwRectFnSet aRectFnSet(this);
|
|
SwRectFnSet fnRectX(pNewUpper);
|
|
if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
|
|
aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) {
|
|
// In this case, only a WouldFit_ with test move is possible
|
|
nMoveAnyway = 2;
|
|
}
|
|
|
|
// Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
|
|
nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
|
|
{
|
|
const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
|
|
SwTwips nSpace = 0;
|
|
SwRect aRect( pNewUpper->getFramePrintArea() );
|
|
aRect.Pos() += pNewUpper->getFrameArea().Pos();
|
|
const SwFrame *pPrevFrame = pNewUpper->Lower();
|
|
while ( pPrevFrame )
|
|
{
|
|
SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea());
|
|
// Consider lower spacing of last frame in a table cell
|
|
{
|
|
// Check if last frame is inside table and if it includes its lower spacing.
|
|
if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
|
|
rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
|
|
{
|
|
const SwFrame* pLastFrame = pPrevFrame;
|
|
// if last frame is a section, take its last content
|
|
if ( pPrevFrame->IsSctFrame() )
|
|
{
|
|
pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent();
|
|
if ( pLastFrame &&
|
|
pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
|
|
{
|
|
pLastFrame = pLastFrame->FindTabFrame();
|
|
}
|
|
}
|
|
|
|
if ( pLastFrame )
|
|
{
|
|
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
|
|
const SwBorderAttrs& rAttrs = *aAccess.Get();
|
|
nNewTop -= rAttrs.GetULSpace().GetLower();
|
|
if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
|
|
{
|
|
nNewTop -= rAttrs.CalcLineSpacing();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fnRectX.SetTop( aRect, nNewTop );
|
|
|
|
pPrevFrame = pPrevFrame->GetNext();
|
|
}
|
|
|
|
nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);
|
|
|
|
//determine space left in new upper frame
|
|
nSpace = fnRectX.GetHeight(aRect);
|
|
const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell();
|
|
if ( IsInFootnote() ||
|
|
(pSh && pSh->GetViewOptions()->getBrowseMode()) ||
|
|
pNewUpper->IsCellFrame() ||
|
|
( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
|
|
( pNewUpper->IsColBodyFrame() &&
|
|
!pNewUpper->GetUpper()->GetPrev() &&
|
|
!pNewUpper->GetUpper()->GetNext() ) ) ) )
|
|
nSpace += pNewUpper->Grow( LONG_MAX, true );
|
|
|
|
auto pTextFrame = DynCastTextFrame();
|
|
if (pTextFrame)
|
|
{
|
|
// This is a text frame. Check if it's an anchor for a non-last element in a split
|
|
// fly chain. If so, we can only move back in case not only the text frame itself,
|
|
// but also its fly fits nSpace.
|
|
SwFlyAtContentFrame* pFly = pTextFrame->HasNonLastSplitFlyDrawObj();
|
|
if (pFly && pFly->getFrameArea().Height() > nSpace)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( nMoveAnyway < 3 )
|
|
{
|
|
if (nSpace || IsHiddenNow())
|
|
{
|
|
// Do not notify footnotes which are stuck to the paragraph:
|
|
// This would require extremely confusing code, taking into
|
|
// account the widths
|
|
// and Flys, that in turn influence the footnotes, ...
|
|
|
|
// WouldFit_ can only be used if the width is the same and
|
|
// ONLY self-anchored Flys are present.
|
|
|
|
// WouldFit_ can also be used if ONLY Flys anchored
|
|
// somewhere else are present.
|
|
// In this case, the width doesn't even matter,
|
|
// because we're running a TestFormat in the new upper.
|
|
const sal_uInt8 nBwdMoveNecessaryResult =
|
|
BwdMoveNecessary( pNewPage, aRect);
|
|
const bool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
|
|
nBwdMoveNecessaryResult == 3 );
|
|
|
|
return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
|
|
bObjsInNewUpper );
|
|
}
|
|
// It's impossible for WouldFit_ to return a usable result if
|
|
// we have a fresh multi-column section - so we really have to
|
|
// float back unless there is no space.
|
|
return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
|
|
!fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
|
|
( pNewUpper->GetUpper()->GetPrev() ||
|
|
pNewUpper->GetUpper()->GetNext() );
|
|
}
|
|
|
|
// Check for space left in new upper
|
|
return nSpace != 0 || IsHiddenNow();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Calc methods
|
|
|
|
// Two little friendships form a secret society
|
|
inline void PrepareLock( SwFlowFrame *pTab )
|
|
{
|
|
pTab->LockJoin();
|
|
}
|
|
inline void PrepareUnlock( SwFlowFrame *pTab )
|
|
{
|
|
pTab->UnlockJoin();
|
|
|
|
}
|
|
|
|
// hopefully, one day this function simply will return 'false'
|
|
static bool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
|
|
{
|
|
return !rFrame.GetUpper()->IsSctFrame() &&
|
|
!rFrame.GetUpper()->IsFooterFrame() &&
|
|
// No format of upper Writer fly frame
|
|
!rFrame.GetUpper()->IsFlyFrame() &&
|
|
!( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
|
|
!( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
|
|
}
|
|
|
|
/** Prepares the Frame for "formatting" (MakeAll()).
|
|
*
|
|
* This method serves to save stack space: To calculate the position of the Frame
|
|
* we have to make sure that the positions of Upper and Prev respectively are
|
|
* valid. This may require a recursive call (a loop would be quite expensive,
|
|
* as it's not required very often).
|
|
*
|
|
* Every call of MakeAll requires around 500 bytes on the stack - you easily
|
|
* see where this leads to. This method requires only a little bit of stack
|
|
* space, so the recursive call should not be a problem here.
|
|
*
|
|
* Another advantage is that one nice day, this method and with it the
|
|
* formatting of predecessors could be avoided. Then it could probably be
|
|
* possible to jump "quickly" to the document's end.
|
|
*
|
|
* @see MakeAll()
|
|
*/
|
|
void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
|
|
{
|
|
StackHack aHack;
|
|
if ( GetUpper() )
|
|
{
|
|
SwFrameDeleteGuard aDeleteGuard(this);
|
|
if ( lcl_IsCalcUpperAllowed( *this ) )
|
|
GetUpper()->Calc(pRenderContext);
|
|
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
|
|
if ( !GetUpper() )
|
|
return;
|
|
|
|
const bool bCnt = IsContentFrame();
|
|
const bool bTab = IsTabFrame();
|
|
bool bNoSect = IsInSct();
|
|
bool bOldTabLock = false, bFoll = false;
|
|
SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
|
|
|
|
if ( bTab )
|
|
{
|
|
pThis = static_cast<SwTabFrame*>(this);
|
|
bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
|
|
::PrepareLock( static_cast<SwTabFrame*>(this) );
|
|
bFoll = pThis->IsFollow();
|
|
}
|
|
else if( IsSctFrame() )
|
|
{
|
|
pThis = static_cast<SwSectionFrame*>(this);
|
|
bFoll = pThis->IsFollow();
|
|
bNoSect = false;
|
|
}
|
|
else if ( bCnt )
|
|
{
|
|
bFoll = pThis->IsFollow();
|
|
if ( bFoll && GetPrev() )
|
|
{
|
|
// Do not follow the chain when we need only one instance
|
|
const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster();
|
|
if ( pMaster && pMaster->IsLocked() )
|
|
{
|
|
MakeAll(pRenderContext);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// There is no format of previous frame, if current frame is a table
|
|
// frame and its previous frame wants to keep with it.
|
|
bool bFormatPrev{!bTab};
|
|
if (!bFormatPrev)
|
|
{
|
|
SwFrame const* pPrev{this};
|
|
do
|
|
{
|
|
pPrev = pPrev->GetPrev();
|
|
}
|
|
while (pPrev && pPrev->IsHiddenNow());
|
|
bFormatPrev = pPrev && !pPrev->GetAttrSet()->GetKeep().GetValue();
|
|
}
|
|
if ( bFormatPrev )
|
|
{
|
|
SwFrame *pFrame = GetUpper()->Lower();
|
|
while ( pFrame != this )
|
|
{
|
|
OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
|
|
if ( !pFrame )
|
|
return; //Oioioioi ...
|
|
|
|
if ( !pFrame->isFrameAreaDefinitionValid() )
|
|
{
|
|
// A small interference that hopefully improves on the stability:
|
|
// If I'm Follow AND neighbor of a Frame before me, it would delete
|
|
// me when formatting. This as you can see could easily become a
|
|
// confusing situation that we want to avoid.
|
|
if ( bFoll && pFrame->IsFlowFrame() &&
|
|
SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
|
|
break;
|
|
|
|
bool const isLast(pFrame->GetNext() == this);
|
|
// note: this seems obvious but does *not* hold, a MakeAll()
|
|
// could move more than 1 frame backwards!
|
|
// that's why FindNext() is used below
|
|
// assert(pFrame->GetUpper() == GetUpper());
|
|
pFrame->MakeAll(pRenderContext);
|
|
if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
|
|
break;
|
|
if (isLast && pFrame->GetUpper() != GetUpper())
|
|
{
|
|
assert(GetUpper()->Lower() == this
|
|
// empty section frames are created all the time...
|
|
|| GetUpper()->Lower()->IsSctFrame()
|
|
// tab frame/section frame may split multiple times
|
|
|| ( SwFlowFrame::CastFlowFrame(pFrame)
|
|
&& SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
|
|
&& SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
|
|
SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
|
|
&& (GetUpper()->Lower()->GetNext() == this
|
|
// if it's more than 10 pages long...
|
|
|| (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
|
|
== SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
|
|
&& GetUpper()->Lower()->GetNext()->GetNext() == this)
|
|
// pre-existing empty section frames may end up between them...
|
|
|| GetUpper()->Lower()->GetNext()->IsSctFrame())));
|
|
break; // tdf#119109 frame was moved backward, prevent
|
|
// FindNext() returning a frame inside this if
|
|
} // this is a table!
|
|
}
|
|
// With ContentFrames, the chain may be broken while walking through
|
|
// it. Therefore we have to figure out the next frame in a bit more
|
|
// complicated way. However, I'll HAVE to get back to myself
|
|
// sometime again.
|
|
pFrame = pFrame->FindNext();
|
|
|
|
// If we started out in a SectionFrame, it might have happened that
|
|
// we landed in a Section Follow via the MakeAll calls.
|
|
// FindNext only gives us the SectionFrame, not it's content - we
|
|
// won't find ourselves anymore!
|
|
if( bNoSect && pFrame && pFrame->IsSctFrame() )
|
|
{
|
|
SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
|
|
if( pCnt )
|
|
pFrame = pCnt;
|
|
}
|
|
}
|
|
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
|
|
if ( !GetUpper() )
|
|
return;
|
|
|
|
if ( lcl_IsCalcUpperAllowed( *this ) )
|
|
GetUpper()->Calc(pRenderContext);
|
|
|
|
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
|
|
}
|
|
|
|
if ( bTab && !bOldTabLock )
|
|
::PrepareUnlock( static_cast<SwTabFrame*>(this) );
|
|
}
|
|
MakeAll(pRenderContext);
|
|
}
|
|
|
|
void SwFrame::OptPrepareMake()
|
|
{
|
|
// #i23129#, #i36347# - no format of upper Writer fly frame
|
|
if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
|
|
!GetUpper()->IsFlyFrame() )
|
|
{
|
|
{
|
|
SwFrameDeleteGuard aDeleteGuard(this);
|
|
GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
|
|
}
|
|
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
|
|
if ( !GetUpper() )
|
|
return;
|
|
}
|
|
if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
|
|
{
|
|
PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
|
|
}
|
|
else
|
|
{
|
|
StackHack aHack;
|
|
MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
|
|
}
|
|
}
|
|
|
|
void SwFrame::PrepareCursor()
|
|
{
|
|
StackHack aHack;
|
|
if( GetUpper() && !GetUpper()->IsSctFrame() )
|
|
{
|
|
const bool bCnt = IsContentFrame();
|
|
const bool bTab = IsTabFrame();
|
|
bool bNoSect = IsInSct();
|
|
|
|
std::optional<FlowFrameJoinLockGuard> tabGuard;
|
|
std::optional<SwFrameDeleteGuard> rowGuard;
|
|
SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
|
|
|
|
if ( bTab )
|
|
{
|
|
tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741
|
|
pThis = static_cast<SwTabFrame*>(this);
|
|
}
|
|
else if (IsRowFrame())
|
|
{
|
|
rowGuard.emplace(this); // tdf#125741 keep this alive
|
|
}
|
|
else if( IsSctFrame() )
|
|
{
|
|
pThis = static_cast<SwSectionFrame*>(this);
|
|
bNoSect = false;
|
|
}
|
|
|
|
GetUpper()->PrepareCursor();
|
|
GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
|
|
|
|
OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
|
|
if ( !GetUpper() )
|
|
return;
|
|
|
|
bool const bFoll = pThis && pThis->IsFollow();
|
|
|
|
SwFrame *pFrame = GetUpper()->Lower();
|
|
while ( pFrame != this )
|
|
{
|
|
OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
|
|
if ( !pFrame )
|
|
return; //Oioioioi ...
|
|
|
|
if ( !pFrame->isFrameAreaDefinitionValid() )
|
|
{
|
|
// A small interference that hopefully improves on the stability:
|
|
// If I'm Follow AND neighbor of a Frame before me, it would delete
|
|
// me when formatting. This as you can see could easily become a
|
|
// confusing situation that we want to avoid.
|
|
if ( bFoll && pFrame->IsFlowFrame() &&
|
|
SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
|
|
break;
|
|
|
|
bool const isLast(pFrame->GetNext() == this);
|
|
pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
|
|
if (isLast && pFrame->GetUpper() != GetUpper())
|
|
{
|
|
assert(GetUpper()->Lower() == this
|
|
// empty section frames are created all the time...
|
|
|| GetUpper()->Lower()->IsSctFrame()
|
|
// tab frame/section frame may split multiple times
|
|
|| ( SwFlowFrame::CastFlowFrame(pFrame)
|
|
&& SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
|
|
&& SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
|
|
SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
|
|
&& (GetUpper()->Lower()->GetNext() == this
|
|
// if it's more than 10 pages long...
|
|
|| (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
|
|
== SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
|
|
&& GetUpper()->Lower()->GetNext()->GetNext() == this)
|
|
// pre-existing empty section frames may end up between them...
|
|
|| GetUpper()->Lower()->GetNext()->IsSctFrame())));
|
|
break; // tdf#119109 frame was moved backward, prevent
|
|
// FindNext() returning a frame inside this if
|
|
} // this is a table!
|
|
}
|
|
// With ContentFrames, the chain may be broken while walking through
|
|
// it. Therefore we have to figure out the next frame in a bit more
|
|
// complicated way. However, I'll HAVE to get back to myself
|
|
// sometime again.
|
|
pFrame = pFrame->FindNext();
|
|
if( bNoSect && pFrame && pFrame->IsSctFrame() )
|
|
{
|
|
SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
|
|
if( pCnt )
|
|
pFrame = pCnt;
|
|
}
|
|
}
|
|
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
|
|
if ( !GetUpper() )
|
|
return;
|
|
|
|
GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
|
|
|
|
OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
|
|
}
|
|
Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
|
|
}
|
|
|
|
// Here we return GetPrev(); however we will ignore empty SectionFrames
|
|
static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
|
|
{
|
|
SwFrame* pRet = pFrame->GetPrev();
|
|
if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
|
|
bSectPrv && !pFrame->IsColumnFrame() )
|
|
pRet = pFrame->GetUpper()->GetPrev();
|
|
while( pRet && pRet->IsSctFrame() &&
|
|
!static_cast<SwSectionFrame*>(pRet)->GetSection() )
|
|
pRet = pRet->GetPrev();
|
|
return pRet;
|
|
}
|
|
|
|
static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
|
|
{
|
|
SwFrame *pRet = pFrame;
|
|
do
|
|
{
|
|
pRet = lcl_Prev( pRet );
|
|
} while ( pRet && pRet->IsHiddenNow() );
|
|
return pRet;
|
|
}
|
|
|
|
void SwFrame::MakePos()
|
|
{
|
|
if ( isFrameAreaPositionValid() )
|
|
return;
|
|
|
|
setFrameAreaPositionValid(true);
|
|
bool bUseUpper = false;
|
|
SwFrame* pPrv = lcl_Prev( this );
|
|
if ( pPrv &&
|
|
( !pPrv->IsContentFrame() ||
|
|
( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
|
|
)
|
|
{
|
|
if ( !StackHack::IsLocked() &&
|
|
( !IsInSct() || IsSctFrame() ) &&
|
|
!pPrv->IsSctFrame() &&
|
|
!pPrv->GetAttrSet()->GetKeep().GetValue()
|
|
)
|
|
{
|
|
// tdf#151866 pPrv may MoveBwd and if this is a newly created
|
|
// section frame then CheckPageDescs() may delete the whole page!
|
|
SwFrameDeleteGuard g(this); // Prevent it.
|
|
pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
|
|
}
|
|
else if ( pPrv->getFrameArea().Top() == 0 )
|
|
{
|
|
bUseUpper = true;
|
|
}
|
|
}
|
|
|
|
pPrv = lcl_Prev( this, false );
|
|
const SwFrameType nMyType = GetType();
|
|
SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? GetUpper() : this));
|
|
if ( !bUseUpper && pPrv )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Pos( pPrv->getFrameArea().Pos() );
|
|
|
|
if( FRM_NEIGHBOUR & nMyType )
|
|
{
|
|
const bool bR2L = IsRightToLeft();
|
|
|
|
if( bR2L )
|
|
{
|
|
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
|
|
}
|
|
else
|
|
{
|
|
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
|
|
}
|
|
|
|
// cells may now leave their uppers
|
|
if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
|
|
}
|
|
}
|
|
else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
|
|
{
|
|
if ( aRectFnSet.IsVertL2R() )
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
|
|
}
|
|
else
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
|
|
}
|
|
}
|
|
else if ( GetUpper() )
|
|
{
|
|
// If parent frame is a footer frame and its <ColLocked()>, then
|
|
// do *not* calculate it.
|
|
// NOTE: Footer frame is <ColLocked()> during its
|
|
// <FormatSize(..)>, which is called from <Format(..)>, which
|
|
// is called from <MakeAll()>, which is called from <Calc()>.
|
|
// #i56850#
|
|
// - no format of upper Writer fly frame, which is anchored
|
|
// at-paragraph or at-character.
|
|
if ( !GetUpper()->IsTabFrame() &&
|
|
!( IsTabFrame() && GetUpper()->IsInTab() ) &&
|
|
!GetUpper()->IsSctFrame() &&
|
|
!dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
|
|
!( GetUpper()->IsFooterFrame() &&
|
|
GetUpper()->IsColLocked() )
|
|
)
|
|
{
|
|
GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
|
|
}
|
|
pPrv = lcl_Prev( this, false );
|
|
if ( !bUseUpper && pPrv )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Pos( pPrv->getFrameArea().Pos() );
|
|
|
|
if( FRM_NEIGHBOUR & nMyType )
|
|
{
|
|
const bool bR2L = IsRightToLeft();
|
|
|
|
if( bR2L )
|
|
{
|
|
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
|
|
}
|
|
else
|
|
{
|
|
aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
|
|
}
|
|
|
|
// cells may now leave their uppers
|
|
if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
|
|
}
|
|
}
|
|
else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
|
|
}
|
|
else
|
|
{
|
|
aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Pos( GetUpper()->getFrameArea().Pos() );
|
|
|
|
if( GetUpper()->IsFlyFrame() )
|
|
{
|
|
aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
|
|
}
|
|
else
|
|
{
|
|
aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
|
|
}
|
|
|
|
if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
|
|
{
|
|
if( aRectFnSet.IsVert() )
|
|
{
|
|
aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
|
|
}
|
|
else
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
|
|
}
|
|
}
|
|
else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
|
|
{
|
|
aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Pos().setX(0);
|
|
aFrm.Pos().setY(0);
|
|
}
|
|
|
|
if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
|
|
}
|
|
|
|
setFrameAreaPositionValid(true);
|
|
}
|
|
|
|
// #i28701# - new type <SwSortedObjs>
|
|
static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, tools::Long& rBot)
|
|
{
|
|
// And then there can be paragraph anchored frames that sit below their paragraph.
|
|
tools::Long nMax = 0;
|
|
for (SwAnchoredObject* pObj : rSortedObjs)
|
|
{
|
|
// #i28701# - consider changed type of <SwSortedObjs>
|
|
// entries.
|
|
tools::Long nTmp = 0;
|
|
if ( auto pFly = pObj->DynCastFlyFrame() )
|
|
{
|
|
if( pFly->getFrameArea().Top() != FAR_AWAY &&
|
|
( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
|
|
( pFly->IsFlyAtContentFrame() &&
|
|
( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
|
|
pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
|
|
{
|
|
nTmp = pFly->getFrameArea().Bottom();
|
|
}
|
|
}
|
|
else
|
|
nTmp = pObj->GetObjRect().Bottom();
|
|
nMax = std::max( nTmp, nMax );
|
|
}
|
|
++nMax; // Lower edge vs. height!
|
|
rBot = std::max( rBot, nMax );
|
|
}
|
|
|
|
size_t SwPageFrame::GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const
|
|
{
|
|
OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
|
|
"SwPageFrame::GetContentHeight(): No support for columns.");
|
|
|
|
// In pages without columns, the content defines the size.
|
|
tools::Long nBot = getFrameArea().Top() + nTop;
|
|
const SwFrame *pFrame = Lower();
|
|
while (pFrame)
|
|
{
|
|
tools::Long nTmp = 0;
|
|
const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
|
|
while (pCnt && (pCnt->GetUpper() == pFrame ||
|
|
static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
|
|
{
|
|
nTmp += pCnt->getFrameArea().Height();
|
|
if (pCnt->IsTextFrame() &&
|
|
static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
|
|
{
|
|
// This TextFrame would like to be a bit bigger.
|
|
nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
|
|
- pCnt->getFramePrintArea().Height();
|
|
}
|
|
else if (pCnt->IsSctFrame())
|
|
{
|
|
// Grow if undersized, but don't shrink if oversized.
|
|
const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
|
|
if (delta > 0)
|
|
nTmp += delta;
|
|
}
|
|
|
|
pCnt = pCnt->FindNext();
|
|
}
|
|
// Consider invalid body frame properties
|
|
if (pFrame->IsBodyFrame() &&
|
|
(!pFrame->isFrameAreaSizeValid() ||
|
|
!pFrame->isFramePrintAreaValid()) &&
|
|
(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
|
|
)
|
|
{
|
|
nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
|
|
}
|
|
else
|
|
{
|
|
// Assert invalid lower property
|
|
OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
|
|
"SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
|
|
nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
|
|
}
|
|
if (!pFrame->IsBodyFrame())
|
|
nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
|
|
nBot += nTmp;
|
|
// Here we check whether paragraph anchored objects
|
|
// protrude outside the Body/FootnoteCont.
|
|
if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
|
|
!pFrame->IsFooterFrame())
|
|
lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
|
|
pFrame = pFrame->GetNext();
|
|
}
|
|
nBot += nBottom;
|
|
// And the page anchored ones
|
|
if (m_pSortedObjs)
|
|
lcl_CheckObjects(*m_pSortedObjs, this, nBot);
|
|
nBot -= getFrameArea().Top();
|
|
|
|
return nBot;
|
|
}
|
|
|
|
void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
|
|
{
|
|
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
|
|
|
|
const SwRect aOldRect( getFrameArea() ); // Adjust root size
|
|
const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
|
|
std::optional<SwBorderAttrAccess> oAccess;
|
|
const SwBorderAttrs*pAttrs = nullptr;
|
|
|
|
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
{
|
|
if ( !isFrameAreaPositionValid() )
|
|
{
|
|
setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
|
|
}
|
|
|
|
if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
{
|
|
if ( IsEmptyPage() )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Width( 0 );
|
|
aFrm.Height( 0 );
|
|
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aPrt.Width( 0 );
|
|
aPrt.Height( 0 );
|
|
aPrt.Left( 0 );
|
|
aPrt.Top( 0 );
|
|
|
|
setFrameAreaSizeValid(true);
|
|
setFramePrintAreaValid(true);
|
|
}
|
|
else
|
|
{
|
|
if (!oAccess)
|
|
{
|
|
oAccess.emplace(SwFrame::GetCache(), this);
|
|
pAttrs = oAccess->Get();
|
|
}
|
|
assert(pAttrs);
|
|
|
|
SwRootFrame* pRootFrame = getRootFrame();
|
|
SwViewShell* pSh = pRootFrame->GetCurrShell();
|
|
if (pSh && pSh->GetViewOptions()->getBrowseMode())
|
|
{
|
|
// In BrowseView, we use fixed settings
|
|
const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
|
|
const tools::Long nTop = pAttrs->CalcTopLine() + aBorder.Height();
|
|
const tools::Long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();
|
|
|
|
tools::Long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
|
|
const auto nDefWidth = pSh->GetBrowseWidth();
|
|
if (nWidth < nDefWidth)
|
|
nWidth = nDefWidth;
|
|
nWidth += + 2 * aBorder.Width();
|
|
|
|
constexpr tools::Long constTwips_2cm = o3tl::toTwips(2, o3tl::Length::cm);
|
|
nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);
|
|
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.Width( nWidth );
|
|
|
|
SwLayoutFrame *pBody = FindBodyCont();
|
|
if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
|
|
{
|
|
// Columns have a fixed height
|
|
aFrm.Height( pAttrs->GetSize().Height() );
|
|
}
|
|
else
|
|
{
|
|
// In pages without columns, the content defines the size.
|
|
tools::Long nBot = GetContentHeight(nTop, nBottom);
|
|
|
|
// #i35143# - If second page frame
|
|
// exists, the first page doesn't have to fulfill the
|
|
// visible area.
|
|
if ( !GetPrev() && !GetNext() )
|
|
{
|
|
nBot = std::max( nBot, pSh->VisArea().Height() );
|
|
}
|
|
// #i35143# - Assure, that the page
|
|
// doesn't exceed the defined browse height.
|
|
aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
|
|
}
|
|
}
|
|
|
|
{
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
|
|
aPrt.Top ( nTop );
|
|
aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
|
|
aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
|
|
}
|
|
|
|
setFrameAreaSizeValid(true);
|
|
setFramePrintAreaValid(true);
|
|
continue;
|
|
}
|
|
else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
|
|
{
|
|
tools::Long height = 0;
|
|
SwLayoutFrame *pBody = FindBodyCont();
|
|
SwTwips nFullBodyHeight = pAttrs->GetSize().Height() - pAttrs->CalcTop() - pAttrs->CalcBottom();
|
|
if (pRootFrame->GetLastPage() == this)
|
|
{
|
|
// Last page is only reduced by the top/bottom margin, the body frame height
|
|
// is not reduced.
|
|
height = nFullBodyHeight;
|
|
}
|
|
else if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
|
|
{
|
|
// Columns have a fixed height
|
|
height = pAttrs->GetSize().Height();
|
|
}
|
|
else
|
|
{
|
|
// No need for borders.
|
|
height = GetContentHeight(0, 0);
|
|
if (height > nFullBodyHeight)
|
|
{
|
|
// Content height would be larger than the show-whitespace body height,
|
|
// limit it.
|
|
height = nFullBodyHeight;
|
|
}
|
|
}
|
|
|
|
if (height > 0)
|
|
{
|
|
ChgSize(Size(getFrameArea().Width(), height));
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aPrt.Top(0);
|
|
aPrt.Height(height);
|
|
setFrameAreaSizeValid(true);
|
|
setFramePrintAreaValid(true);
|
|
continue;
|
|
}
|
|
|
|
// Fallback to default formatting. Especially relevant
|
|
// when loading a doc when Hide Whitespace is enabled.
|
|
// Heights are zero initially.
|
|
}
|
|
|
|
// Set FixSize. For pages, this is not done from Upper, but from
|
|
// the attribute.
|
|
//FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aFrm.SSize( pAttrs->GetSize() );
|
|
}
|
|
Format( pRenderContext, pAttrs );
|
|
}
|
|
}
|
|
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
|
|
if ( getFrameArea() != aOldRect && GetUpper() )
|
|
static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
|
|
|
|
OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
|
|
"Upper (Root) must be wide enough to contain the widest page");
|
|
}
|
|
|
|
void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
|
|
{
|
|
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
|
|
|
|
// takes care of the notification in the dtor
|
|
const SwLayNotify aNotify( this );
|
|
bool bVert = IsVertical();
|
|
|
|
SwRectFnSet fnRect(IsNeighbourFrame() != bVert, IsVertLR(), IsVertLRBT());
|
|
|
|
std::optional<SwBorderAttrAccess> oAccess;
|
|
const SwBorderAttrs*pAttrs = nullptr;
|
|
|
|
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
{
|
|
if ( !isFrameAreaPositionValid() )
|
|
{
|
|
MakePos();
|
|
}
|
|
|
|
if (IsHiddenNow())
|
|
{
|
|
MakeValidZeroHeight();
|
|
}
|
|
|
|
if ( GetUpper() )
|
|
{
|
|
// NEW TABLES
|
|
if ( IsLeaveUpperAllowed() )
|
|
{
|
|
if ( !isFrameAreaSizeValid() )
|
|
{
|
|
setFramePrintAreaValid(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !isFrameAreaSizeValid() )
|
|
{
|
|
// Set FixSize; VarSize is set by Format() after calculating the PrtArea
|
|
setFramePrintAreaValid(false);
|
|
|
|
SwTwips nPrtWidth = fnRect.GetWidth(GetUpper()->getFramePrintArea());
|
|
if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
|
|
{
|
|
SwFrame* pNxt = GetPrev();
|
|
while( pNxt && !pNxt->IsHeaderFrame() )
|
|
pNxt = pNxt->GetPrev();
|
|
if( pNxt )
|
|
nPrtWidth -= pNxt->getFrameArea().Height();
|
|
pNxt = GetNext();
|
|
while( pNxt && !pNxt->IsFooterFrame() )
|
|
pNxt = pNxt->GetNext();
|
|
if( pNxt )
|
|
nPrtWidth -= pNxt->getFrameArea().Height();
|
|
}
|
|
|
|
const tools::Long nDiff = nPrtWidth - fnRect.GetWidth(getFrameArea());
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
// SwRectFn switched between horizontal and vertical when bVert == IsNeighbourFrame().
|
|
// We pick fnSubLeft or fnAddRight that is correspondent to SwRectFn->fnAddBottom
|
|
if( ( IsCellFrame() && IsRightToLeft() ) || ( IsColumnFrame() && bVert && !IsVertLR() ) )
|
|
{
|
|
fnRect.SubLeft(aFrm, nDiff);
|
|
}
|
|
else
|
|
{
|
|
fnRect.AddRight(aFrm, nDiff);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't leave your upper
|
|
const SwTwips nDeadLine = fnRect.GetPrtBottom(*GetUpper());
|
|
if (fnRect.OverStep(getFrameArea(), nDeadLine))
|
|
{
|
|
setFrameAreaSizeValid(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
{
|
|
if ( !oAccess )
|
|
{
|
|
oAccess.emplace(SwFrame::GetCache(), this);
|
|
pAttrs = oAccess->Get();
|
|
}
|
|
Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
|
|
}
|
|
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
}
|
|
|
|
bool SwTextNode::IsCollapse() const
|
|
{
|
|
if (GetDoc().GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
|
|
&& GetText().isEmpty())
|
|
{
|
|
SwNodeOffset nIdx=GetIndex();
|
|
const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
|
|
const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();
|
|
|
|
// The paragraph is collapsed only if the NdAfter is the end of a cell
|
|
bool bInTable = FindTableNode( ) != nullptr;
|
|
|
|
SwSortedObjs* pObjs = getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
|
|
const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;
|
|
|
|
return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SwFrame::IsCollapse() const
|
|
{
|
|
if (!IsTextFrame())
|
|
return false;
|
|
|
|
const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this);
|
|
const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps();
|
|
// TODO this SwTextNode function is pointless and should be merged in here
|
|
return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
|
|
}
|
|
|
|
bool SwFrame::IsCollapseUpper() const
|
|
{
|
|
const SwTextFrame* pTextFrame = DynCastTextFrame();
|
|
if (!pTextFrame)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SwDoc& rDoc = pTextFrame->GetDoc();
|
|
const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
|
|
if (!rIDSA.get(DocumentSettingId::TAB_OVER_SPACING) || rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN))
|
|
{
|
|
// Writer or Word Word <= 2010 style: upper margin is never ignored.
|
|
return false;
|
|
}
|
|
|
|
if (IsInFly())
|
|
{
|
|
// Not in a page's body.
|
|
return false;
|
|
}
|
|
|
|
// Word >= 2013 style: when we're at the top of the page's body, but not on the first page, then
|
|
// ignore the upper margin for paragraphs.
|
|
if (GetPrev() || !GetUpper() || !GetUpper()->IsBodyFrame())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const SwPageFrame* pPageFrame = FindPageFrame();
|
|
if (!pPageFrame || !pPageFrame->GetPrev())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Avoid the ignore after applying a new page style (but do it after page breaks).
|
|
const SwTextNode* pTextNode = pTextFrame->GetTextNodeForParaProps();
|
|
if (pTextNode)
|
|
{
|
|
if (pTextNode->HasSwAttrSet() && pTextNode->GetSwAttrSet().HasItem(RES_PAGEDESC))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
|
|
{
|
|
if ( isFramePrintAreaValid() )
|
|
return;
|
|
|
|
setFramePrintAreaValid(true);
|
|
SwRectFnSet aRectFnSet(this);
|
|
SwTwips nUpper = 0;
|
|
if (IsTextFrame() && IsHiddenNow())
|
|
{
|
|
if ( static_cast<SwTextFrame*>(this)->HasFollow() )
|
|
static_cast<SwTextFrame*>(this)->JoinFrame();
|
|
|
|
if( aRectFnSet.GetHeight(getFramePrintArea()) )
|
|
{
|
|
static_cast<SwTextFrame*>(this)->HideHidden();
|
|
}
|
|
|
|
{
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aPrt.Pos().setX(0);
|
|
aPrt.Pos().setY(0);
|
|
aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
|
|
aRectFnSet.SetHeight( aPrt, 0 );
|
|
}
|
|
|
|
nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
|
|
}
|
|
else
|
|
{
|
|
// Simplification: ContentFrames are always variable in height!
|
|
|
|
// At the FixSize, the surrounding Frame enforces the size;
|
|
// the borders are simply subtracted.
|
|
const tools::Long nLeft = rAttrs.CalcLeft( this );
|
|
const tools::Long nRight = rAttrs.CalcRight( this );
|
|
aRectFnSet.SetXMargins( *this, nLeft, nRight );
|
|
|
|
SwViewShell *pSh = getRootFrame()->GetCurrShell();
|
|
SwTwips nWidthArea;
|
|
if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
|
|
GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
|
|
pSh->GetViewOptions()->getBrowseMode() )
|
|
{
|
|
// Do not protrude the edge of the visible area. The page may be
|
|
// wider, because there may be objects with excess width
|
|
// (RootFrame::ImplCalcBrowseWidth())
|
|
tools::Long nMinWidth = 0;
|
|
|
|
for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
|
|
{
|
|
// #i28701# - consider changed type of
|
|
// <SwSortedObjs> entries
|
|
SwAnchoredObject* pObj = (*GetDrawObjs())[i];
|
|
const SwFrameFormat* pFormat = pObj->GetFrameFormat();
|
|
const bool bFly = pObj->DynCastFlyFrame() != nullptr;
|
|
if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
|
|
|| pFormat->GetFrameSize().GetWidthPercent())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( RndStdIds::FLY_AS_CHAR == pFormat->GetAnchor().GetAnchorId() )
|
|
{
|
|
nMinWidth = std::max( nMinWidth,
|
|
bFly ? pFormat->GetFrameSize().GetWidth()
|
|
: pObj->GetObjRect().Width() );
|
|
}
|
|
}
|
|
|
|
const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
|
|
tools::Long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
|
|
nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
|
|
nWidth -= rAttrs.CalcRightLine();
|
|
nWidth = std::max( nMinWidth, nWidth );
|
|
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
|
|
}
|
|
|
|
if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
|
|
{
|
|
// The PrtArea should already be at least MINLAY wide, matching the
|
|
// minimal values of the UI
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
|
|
SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
|
|
|
|
if( aRectFnSet.GetLeft(aPrt) > nTmp )
|
|
{
|
|
aRectFnSet.SetLeft( aPrt, nTmp );
|
|
}
|
|
}
|
|
|
|
// The following rules apply for VarSize:
|
|
// 1. The first entry of a chain has no top border
|
|
// 2. There is never a bottom border
|
|
// 3. The top border is the maximum of the distance
|
|
// of Prev downwards and our own distance upwards
|
|
// Those three rules apply when calculating spacings
|
|
// that are given by UL- and LRSpace. There might be a spacing
|
|
// in all directions however; this may be caused by borders
|
|
// and / or shadows.
|
|
// 4. The spacing for TextFrames corresponds to the interline lead,
|
|
// at a minimum.
|
|
|
|
nUpper = CalcUpperSpace( &rAttrs );
|
|
|
|
SwTwips nLower = CalcLowerSpace( &rAttrs );
|
|
if (IsCollapse()) {
|
|
nUpper=0;
|
|
nLower=0;
|
|
}
|
|
|
|
{
|
|
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
|
|
aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
|
|
}
|
|
|
|
nUpper += nLower;
|
|
nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
|
|
}
|
|
// If there's a difference between old and new size, call Grow() or
|
|
// Shrink() respectively.
|
|
if ( nUpper )
|
|
{
|
|
if ( nUpper > 0 )
|
|
GrowFrame( nUpper );
|
|
else
|
|
ShrinkFrame( -nUpper );
|
|
}
|
|
}
|
|
|
|
#define STOP_FLY_FORMAT 10
|
|
// - loop prevention
|
|
const int cnStopFormat = 15;
|
|
|
|
inline void ValidateSz( SwFrame *pFrame )
|
|
{
|
|
if ( pFrame )
|
|
{
|
|
pFrame->setFrameAreaSizeValid(true);
|
|
pFrame->setFramePrintAreaValid(true);
|
|
}
|
|
}
|
|
|
|
void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
|
|
{
|
|
OSL_ENSURE( GetUpper(), "no Upper?" );
|
|
OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );
|
|
|
|
if ( !IsFollow() && StackHack::IsLocked() )
|
|
return;
|
|
|
|
if ( IsJoinLocked() )
|
|
return;
|
|
|
|
OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );
|
|
|
|
StackHack aHack;
|
|
|
|
if ( static_cast<SwTextFrame*>(this)->IsLocked() )
|
|
{
|
|
OSL_FAIL( "Format for locked TextFrame." );
|
|
return;
|
|
}
|
|
|
|
bool const isHiddenNow(static_cast<SwTextFrame*>(this)->IsHiddenNowImpl());
|
|
if (isHiddenNow)
|
|
{
|
|
while (HasFollow())
|
|
{
|
|
static_cast<SwTextFrame&>(*this).JoinFrame();
|
|
}
|
|
HideAndShowObjects();
|
|
}
|
|
|
|
std::optional<SwFrameDeleteGuard> oDeleteGuard(std::in_place, this);
|
|
LockJoin();
|
|
tools::Long nFormatCount = 0;
|
|
// - loop prevention
|
|
int nConsecutiveFormatsWithoutChange = 0;
|
|
PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
|
|
|
|
// takes care of the notification in the dtor
|
|
std::optional<SwContentNotify> oNotify( std::in_place, this );
|
|
|
|
// as long as bMakePage is true, a new page can be created (exactly once)
|
|
bool bMakePage = true;
|
|
// bMovedBwd gets set to true when the frame flows backwards
|
|
bool bMovedBwd = false;
|
|
// as long as bMovedFwd is false, the Frame may flow backwards (until
|
|
// it has been moved forward once)
|
|
bool bMovedFwd = false;
|
|
sal_Bool bFormatted = false; // For the widow/orphan rules, we encourage the
|
|
// last ContentFrame of a chain to format. This only
|
|
// needs to happen once. Every time the Frame is
|
|
// moved, the flag will have to be reset.
|
|
bool bMustFit = false; // Once the emergency brake is pulled,
|
|
// no other prepares will be triggered
|
|
bool bFitPromise = false; // If a paragraph didn't fit, but promises
|
|
// with WouldFit that it would adjust accordingly,
|
|
// this flag is set. If it turns out that it
|
|
// didn't keep it's promise, we can act in a
|
|
// controlled fashion.
|
|
const bool bFly = IsInFly();
|
|
const bool bTab = IsInTab();
|
|
const bool bFootnote = IsInFootnote();
|
|
const bool bSct = IsInSct();
|
|
Point aOldFramePos; // This is so we can compare with the last pos
|
|
Point aOldPrtPos; // and determine whether it makes sense to Prepare
|
|
|
|
SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
|
|
const SwBorderAttrs &rAttrs = *aAccess.Get();
|
|
|
|
if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
|
|
{
|
|
oNotify->SetBordersJoinedWithPrev();
|
|
}
|
|
|
|
const bool bKeep{!isHiddenNow && IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem())};
|
|
|
|
std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
|
|
if ( bFootnote )
|
|
{
|
|
SwFootnoteFrame *pFootnote = FindFootnoteFrame();
|
|
SwSectionFrame* pSct = pFootnote->FindSctFrame();
|
|
if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
|
|
{
|
|
SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
|
|
pFootnote->GetAttr()->GetFootnote().IsEndNote() );
|
|
if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
|
|
pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
|
|
static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
|
|
}
|
|
}
|
|
|
|
if ( GetUpper()->IsSctFrame() &&
|
|
HasFollow() && !GetFollow()->IsDeleteForbidden() &&
|
|
&GetFollow()->GetFrame() == GetNext() )
|
|
{
|
|
static_cast<SwTextFrame&>(*this).JoinFrame();
|
|
}
|
|
|
|
// #i28701# - move master forward, if it has to move,
|
|
// because of its object positioning.
|
|
if (!IsFollow())
|
|
{
|
|
sal_uInt32 nToPageNum = 0;
|
|
const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
|
|
*(GetAttrSet()->GetDoc()),
|
|
*static_cast<SwTextFrame*>(this),
|
|
nToPageNum );
|
|
// #i58182#
|
|
// Also move a paragraph forward, which is the first one inside a table cell.
|
|
if ( bMoveFwdByObjPos &&
|
|
FindPageFrame()->GetPhyPageNum() < nToPageNum &&
|
|
( lcl_Prev( this ) ||
|
|
GetUpper()->IsCellFrame() ||
|
|
( GetUpper()->IsSctFrame() &&
|
|
GetUpper()->GetUpper()->IsCellFrame() ) ) &&
|
|
IsMoveable() )
|
|
{
|
|
bMovedFwd = true;
|
|
MoveFwd( bMakePage, false );
|
|
}
|
|
}
|
|
else if (auto* prev = lcl_Prev(this); prev && IsMoveable())
|
|
{
|
|
// If a Follow sits next to its Master and doesn't fit, we know it can be moved right now.
|
|
bMovedFwd = true;
|
|
// If follow frame is in table, its master will be the last in the
|
|
// current table cell. Thus, invalidate the printing area of the master.
|
|
if ( IsInTab() )
|
|
{
|
|
prev->InvalidatePrt();
|
|
}
|
|
MoveFwd( bMakePage, false );
|
|
}
|
|
|
|
// Check footnote content for forward move.
|
|
// If a content of a footnote is on a prior page/column as its invalid
|
|
// reference, it can be moved forward.
|
|
if ( bFootnote && !isFrameAreaPositionValid() )
|
|
{
|
|
SwFootnoteFrame* pFootnote = FindFootnoteFrame();
|
|
SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;
|
|
|
|
if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
|
|
{
|
|
SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
|
|
SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame();
|
|
//<loop of movefwd until condition held or no move>
|
|
if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
|
|
pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
|
|
pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
|
|
{
|
|
bMovedFwd = true;
|
|
MoveFwd( bMakePage, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
SwRectFnSet aRectFnSet(this);
|
|
|
|
SwFrame const* pMoveBwdPre(nullptr);
|
|
bool isMoveBwdPreValid(false);
|
|
|
|
SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
|
|
SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;
|
|
|
|
while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
{
|
|
// - loop prevention
|
|
aOldFrame_StopFormat2 = aOldFrame_StopFormat;
|
|
aOldPrt_StopFormat2 = aOldPrt_StopFormat;
|
|
aOldFrame_StopFormat = getFrameArea();
|
|
aOldPrt_StopFormat = getFramePrintArea();
|
|
|
|
bool bMoveable = IsMoveable();
|
|
if (bMoveable)
|
|
{
|
|
SwFrame *pPre = GetIndPrev();
|
|
if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
|
|
{
|
|
aRectFnSet.Refresh(this);
|
|
bMovedFwd = true;
|
|
if ( bMovedBwd )
|
|
{
|
|
// While flowing back, the Upper was encouraged to
|
|
// completely re-paint itself. We can skip this now after
|
|
// flowing back and forth.
|
|
GetUpper()->ResetCompletePaint();
|
|
// The predecessor was invalidated, so this is obsolete as well now.
|
|
assert(pPre);
|
|
if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
|
|
::ValidateSz( pPre );
|
|
}
|
|
bMoveable = IsMoveable();
|
|
}
|
|
}
|
|
|
|
aOldFramePos = aRectFnSet.GetPos(getFrameArea());
|
|
aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
|
|
|
|
if ( !isFrameAreaPositionValid() )
|
|
{
|
|
MakePos();
|
|
}
|
|
|
|
if (isHiddenNow)
|
|
{ // call this after MakePos() otherwise Shrink may not work
|
|
MakeValidZeroHeight();
|
|
}
|
|
|
|
//Set FixSize. VarSize is being adjusted by Format().
|
|
if ( !isFrameAreaSizeValid() )
|
|
{
|
|
assert(!isHiddenNow); // hidden frame must not be formatted
|
|
// invalidate printing area flag, if the following conditions are hold:
|
|
// - current frame width is 0.
|
|
// - current printing area width is 0.
|
|
// - frame width is adjusted to a value greater than 0.
|
|
// - printing area flag is true.
|
|
// Thus, it's assured that the printing area is adjusted, if the
|
|
// frame area width changes its width from 0 to something greater
|
|
// than 0.
|
|
// Note: A text frame can be in such a situation, if the format is
|
|
// triggered by method call <SwCursorShell::SetCursor()> after
|
|
// loading the document.
|
|
const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
|
|
|
|
if ( isFramePrintAreaValid() &&
|
|
nNewFrameWidth > 0 &&
|
|
aRectFnSet.GetWidth(getFrameArea()) == 0 &&
|
|
aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
|
|
{
|
|
setFramePrintAreaValid(false);
|
|
}
|
|
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
|
|
}
|
|
|
|
// When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos.
|
|
if( GetUpper() && GetUpper()->IsFlyFrame() &&
|
|
GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
|
|
{
|
|
static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
|
|
GetUpper()->SetCompletePaint();
|
|
}
|
|
}
|
|
if ( !isFramePrintAreaValid() )
|
|
{
|
|
assert(!isHiddenNow); // hidden frame must not be formatted
|
|
const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
|
|
// #i34730# - keep current frame height
|
|
const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
|
|
MakePrtArea( rAttrs );
|
|
if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
|
|
Prepare( PrepareHint::FixSizeChanged );
|
|
// #i34730# - check, if frame height has changed.
|
|
// If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to
|
|
// force a format. The format will check in its method
|
|
// <SwTextFrame::CalcPreps()>, if the already formatted lines still
|
|
// fit and if not, performs necessary actions.
|
|
// #i40150# - no check, if frame is undersized.
|
|
if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
|
|
{
|
|
// #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size
|
|
// invalidation, if height decreases only by the additional
|
|
// lower space as last content of a table cell and an existing
|
|
// follow containing one line exists.
|
|
const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea());
|
|
const bool bNoPrepAdjustFrame =
|
|
nHDiff > 0 && IsInTab() && GetFollow() &&
|
|
(1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
|
|
|| aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
|
|
GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff;
|
|
if ( !bNoPrepAdjustFrame )
|
|
{
|
|
Prepare( PrepareHint::AdjustSizeWithoutFormatting );
|
|
setFrameAreaSizeValid(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// To make the widow and orphan rules work, we need to notify the ContentFrame.
|
|
// Criteria:
|
|
// - It needs to be movable (otherwise, splitting doesn't make sense)
|
|
// - It needs to overlap with the lower edge of the PrtArea of the Upper
|
|
if (!bMustFit && !isHiddenNow)
|
|
{
|
|
bool bWidow = true;
|
|
const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
|
|
if( bMoveable && !bFormatted &&
|
|
( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
|
|
{
|
|
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
|
|
setFrameAreaSizeValid(false);
|
|
bWidow = false;
|
|
}
|
|
if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
|
|
aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
|
|
{
|
|
// In this Prepare, an InvalidateSize_() might happen.
|
|
// isFrameAreaSizeValid() becomes false and Format() gets called.
|
|
Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
|
|
if ( bWidow && GetFollow() )
|
|
{
|
|
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
|
|
setFrameAreaSizeValid(false);
|
|
}
|
|
}
|
|
}
|
|
if ( !isFrameAreaSizeValid() )
|
|
{
|
|
assert(!isHiddenNow); // hidden frame must not be formatted
|
|
setFrameAreaSizeValid(true);
|
|
bFormatted = true;
|
|
++nFormatCount;
|
|
if( nFormatCount > STOP_FLY_FORMAT )
|
|
SetFlyLock( true );
|
|
// - loop prevention
|
|
// No format any longer, if <cnStopFormat> consecutive formats
|
|
// without change occur.
|
|
if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
|
|
{
|
|
Format(getRootFrame()->GetCurrShell()->GetOut());
|
|
}
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
else
|
|
{
|
|
OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// If this is the first one in a chain, check if this can flow
|
|
// backwards (if this is movable at all).
|
|
// To prevent oscillations/loops, check that this has not just
|
|
// flowed forwards.
|
|
bool bDummy;
|
|
auto const pTemp(GetIndPrev());
|
|
auto const bTemp(pTemp && pTemp->isFrameAreaSizeValid()
|
|
&& pTemp->isFramePrintAreaValid());
|
|
if ( !lcl_Prev( this ) &&
|
|
!bMovedFwd &&
|
|
( bMoveable || ( bFly && !bTab ) ) &&
|
|
( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
|
|
&& MoveBwd( bDummy ) )
|
|
{
|
|
aRectFnSet.Refresh(this);
|
|
pMoveBwdPre = pTemp;
|
|
isMoveBwdPreValid = bTemp;
|
|
bMovedBwd = true;
|
|
if (isHiddenNow)
|
|
{ // MoveBwd invalidated the size! Validate to prevent Format!
|
|
MakeValidZeroHeight();
|
|
}
|
|
else
|
|
{
|
|
bFormatted = false;
|
|
}
|
|
if (bKeep && bMoveable && !isHiddenNow)
|
|
{
|
|
if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
|
|
{
|
|
bMovedFwd = true;
|
|
bMoveable = IsMoveable();
|
|
aRectFnSet.Refresh(this);
|
|
}
|
|
Point aOldPos = aRectFnSet.GetPos(getFrameArea());
|
|
MakePos();
|
|
if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
|
|
{
|
|
Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
|
|
if ( !isFrameAreaSizeValid() )
|
|
{
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
|
|
aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
|
|
}
|
|
|
|
if ( !isFramePrintAreaValid() )
|
|
{
|
|
const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
|
|
MakePrtArea( rAttrs );
|
|
if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
|
|
Prepare( PrepareHint::FixSizeChanged, nullptr, false );
|
|
}
|
|
if( GetFollow() )
|
|
{
|
|
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
|
|
}
|
|
|
|
setFrameAreaSizeValid(true);
|
|
bFormatted = true;
|
|
Format(getRootFrame()->GetCurrShell()->GetOut());
|
|
}
|
|
}
|
|
SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
|
|
while( pNxt && pNxt->IsSctFrame() )
|
|
{ // Leave empty sections out, go into the other ones.
|
|
if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
|
|
{
|
|
SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
|
|
if( pTmp )
|
|
{
|
|
pNxt = pTmp;
|
|
break;
|
|
}
|
|
}
|
|
pNxt = pNxt->FindNext();
|
|
}
|
|
if ( pNxt )
|
|
{
|
|
pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
|
|
if( isFrameAreaPositionValid() && !GetIndNext() )
|
|
{
|
|
SwSectionFrame *pSct = FindSctFrame();
|
|
if( pSct && !pSct->isFrameAreaSizeValid() )
|
|
{
|
|
SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
|
|
if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
|
|
{
|
|
setFrameAreaPositionValid(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setFrameAreaPositionValid(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// In footnotes, the TextFrame may validate itself, which can lead to the
|
|
// situation that it's position is wrong despite being "valid".
|
|
if ( isFrameAreaPositionValid() )
|
|
{
|
|
// #i59341#
|
|
// Workaround for inadequate layout algorithm:
|
|
// suppress invalidation and calculation of position, if paragraph
|
|
// has formatted itself at least STOP_FLY_FORMAT times and
|
|
// has anchored objects.
|
|
// Thus, the anchored objects get the possibility to format itself
|
|
// and this probably solve the layout loop.
|
|
if ( bFootnote &&
|
|
nFormatCount <= STOP_FLY_FORMAT &&
|
|
!GetDrawObjs() )
|
|
{
|
|
setFrameAreaPositionValid(false);
|
|
MakePos();
|
|
aOldFramePos = aRectFnSet.GetPos(getFrameArea());
|
|
aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
|
|
}
|
|
}
|
|
|
|
// - loop prevention
|
|
{
|
|
if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
|
|
(aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
|
|
{
|
|
++nConsecutiveFormatsWithoutChange;
|
|
}
|
|
else
|
|
{
|
|
nConsecutiveFormatsWithoutChange = 0;
|
|
}
|
|
}
|
|
|
|
// Yet again an invalid value? Repeat from the start...
|
|
if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
continue;
|
|
|
|
// Done?
|
|
// Attention: because height == 0, it's better to use Top()+Height() instead of
|
|
// Bottom(). This might happen with undersized TextFrames on the lower edge of a
|
|
// multi-column section
|
|
const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
|
|
tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
|
|
|
|
// Hide whitespace may require not to insert a new page.
|
|
SwPageFrame* pPageFrame = FindPageFrame();
|
|
const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
|
|
if (!bHeightValid)
|
|
{
|
|
pPageFrame->InvalidateSize();
|
|
nBottomDist = 0;
|
|
}
|
|
|
|
if( nBottomDist >= 0 )
|
|
{
|
|
if (bKeep && bMoveable && !isHiddenNow)
|
|
{
|
|
// We make sure the successor will be formatted the same.
|
|
// This way, we keep control until (almost) everything is stable,
|
|
// allowing us to avoid endless loops caused by ever repeating
|
|
// retries.
|
|
|
|
// bMoveFwdInvalid is required for #38407#. This was originally solved
|
|
// in flowfrm.cxx rev 1.38, but broke the above schema and
|
|
// preferred to play towers of hanoi (#43669#).
|
|
SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
|
|
// For sections we prefer the content, because it can change
|
|
// the page if required.
|
|
while( pNxt && pNxt->IsSctFrame() )
|
|
{
|
|
if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
|
|
{
|
|
pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
|
|
break;
|
|
}
|
|
pNxt = pNxt->FindNext();
|
|
}
|
|
if ( pNxt )
|
|
{
|
|
const bool bMoveFwdInvalid = nullptr != GetIndNext();
|
|
const bool bNxtNew =
|
|
( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
|
|
!pNxt->IsHiddenNow();
|
|
|
|
pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
|
|
|
|
if ( !bMovedBwd &&
|
|
((bMoveFwdInvalid && !GetIndNext()) ||
|
|
bNxtNew) )
|
|
{
|
|
if( bMovedFwd )
|
|
oNotify->SetInvaKeep();
|
|
bMovedFwd = false;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// I don't fit into my parents, so it's time to make changes
|
|
// as constructively as possible.
|
|
|
|
//If I'm NOT allowed to leave the parent Frame, I've got a problem.
|
|
// Following Arthur Dent, we do the only thing that you can do with
|
|
// an unsolvable problem: We ignore it with all our power.
|
|
if ( !bMoveable || IsUndersized() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If there's no way I can make myself fit into my Upper, the situation
|
|
// could still probably be mitigated by splitting up.
|
|
// This situation arises with freshly created Follows that had been moved
|
|
// to the next page but is still too big for it - ie. needs to be split
|
|
// as well.
|
|
|
|
// If I'm unable to split (WouldFit()) and can't be fitted, I'm going
|
|
// to tell my TextFrame part that, if possible, we still need to split despite
|
|
// the "don't split" attribute.
|
|
bool bMoveOrFit = false;
|
|
bool bDontMoveMe = !GetIndPrev();
|
|
if( bDontMoveMe && IsInSct() )
|
|
{
|
|
SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
|
|
bDontMoveMe = !pBoss->IsInSct() ||
|
|
( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
|
|
}
|
|
|
|
// Finally, we are able to split table rows. Therefore, bDontMoveMe
|
|
// can be set to false:
|
|
if( bDontMoveMe && IsInTab() &&
|
|
nullptr != GetNextCellLeaf() )
|
|
bDontMoveMe = false;
|
|
|
|
assert(bMoveable);
|
|
|
|
if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
|
|
aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
|
|
{
|
|
if ( !bFitPromise )
|
|
{
|
|
SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
|
|
aRectFnSet.GetTop(getFramePrintArea());
|
|
bool bSplit = !IsFwdMoveAllowed();
|
|
if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
|
|
{
|
|
Prepare( PrepareHint::WidowsOrphans, nullptr, false );
|
|
setFrameAreaSizeValid(false);
|
|
bFitPromise = true;
|
|
continue;
|
|
}
|
|
/*
|
|
* In earlier days, we never tried to fit TextFrames in
|
|
* frames and sections using bMoveOrFit by ignoring
|
|
* its attributes (Widows, Keep).
|
|
* This should have been done at least for column frames;
|
|
* as it must be tried anyway with linked frames and sections.
|
|
* Exception: If we sit in FormatWidthCols, we must not ignore
|
|
* the attributes.
|
|
*/
|
|
else if ( !bFootnote &&
|
|
( !bFly || !FindFlyFrame()->IsColLocked() ) &&
|
|
( !bSct || !FindSctFrame()->IsColLocked() ) )
|
|
bMoveOrFit = true;
|
|
}
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
else
|
|
{
|
|
OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Let's see if I can find some space somewhere...
|
|
// footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
|
|
SwFrame *pPre = GetIndPrev();
|
|
SwFrame *pOldUp = GetUpper();
|
|
|
|
/* MA 13. Oct. 98: What is this supposed to be!?
|
|
* AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be
|
|
* moved not only to the next column, but probably even to the next page, creating
|
|
* a section-follow there.
|
|
*/
|
|
if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
|
|
pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
|
|
( pPre || pOldUp->GetUpper()->GetPrev() ) &&
|
|
static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
|
|
{
|
|
bMovedFwd = false;
|
|
}
|
|
|
|
const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
|
|
const tools::Long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());
|
|
|
|
if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
|
|
bMakePage = false;
|
|
aRectFnSet.Refresh(this);
|
|
if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
|
|
{ // SwFlowFrame::CutTree() could have formatted and deleted pPre
|
|
auto const pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
|
|
FindFootnoteFrame())->GetMaster());
|
|
bool bReset = true;
|
|
if (pPrevFootnoteFrame)
|
|
{ // use GetIndNext() in case there are sections
|
|
for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
|
|
{
|
|
if (p == pPre)
|
|
{
|
|
bReset = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bReset)
|
|
{
|
|
pPre = nullptr;
|
|
}
|
|
}
|
|
|
|
// If MoveFwd moves the paragraph to the next page, a following
|
|
// paragraph, which contains footnotes can cause the old upper
|
|
// frame to grow. In this case we explicitly allow a new check
|
|
// for MoveBwd. Robust: We also check the bMovedBwd flag again.
|
|
// If pOldUp was a footnote frame, it has been deleted inside MoveFwd.
|
|
// Therefore we only check for growing body frames.
|
|
bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
|
|
aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;
|
|
|
|
bFormatted = false;
|
|
if ( bMoveOrFit && GetUpper() == pOldUp )
|
|
{
|
|
// FME 2007-08-30 #i81146# new loop control
|
|
if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
|
|
{
|
|
Prepare( PrepareHint::MustFit, nullptr, false );
|
|
setFrameAreaSizeValid(false);
|
|
bMustFit = true;
|
|
continue;
|
|
}
|
|
|
|
#if OSL_DEBUG_LEVEL > 0
|
|
OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" );
|
|
#endif
|
|
}
|
|
if ( bMovedBwd && GetUpper() )
|
|
{ // Retire invalidations that have become useless.
|
|
GetUpper()->ResetCompletePaint();
|
|
if( pPre && !pPre->IsSctFrame() )
|
|
::ValidateSz( pPre );
|
|
}
|
|
|
|
} //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
|
|
|
|
// NEW: Looping Louie (Light). Should not be applied in balanced sections.
|
|
// Should only be applied if there is no better solution!
|
|
LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
|
|
(
|
|
|
|
( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||
|
|
|
|
// #i33887#
|
|
( IsInSct() && bKeep )
|
|
|
|
// ... add your conditions here ...
|
|
|
|
),
|
|
static_cast<SwTextFrame&>(*this) );
|
|
|
|
pSaveFootnote.reset();
|
|
|
|
UnlockJoin();
|
|
oDeleteGuard.reset();
|
|
if ( bMovedFwd || bMovedBwd )
|
|
oNotify->SetInvaKeep();
|
|
if ( bMovedFwd )
|
|
{
|
|
oNotify->SetInvalidatePrevPrtArea();
|
|
}
|
|
oNotify.reset();
|
|
SetFlyLock( false );
|
|
}
|
|
|
|
void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
|
|
{
|
|
// fix(25455): Validate, otherwise this leads to a recursion.
|
|
// The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as
|
|
// the Keep may not be considered properly anymore (27417).
|
|
const bool bOldPos = pFrame->isFrameAreaPositionValid();
|
|
const bool bOldSz = pFrame->isFrameAreaSizeValid();
|
|
const bool bOldPrt = pFrame->isFramePrintAreaValid();
|
|
pFrame->setFrameAreaPositionValid(true);
|
|
pFrame->setFrameAreaSizeValid(true);
|
|
pFrame->setFramePrintAreaValid(true);
|
|
|
|
// fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and
|
|
// we recursively end up in here again.
|
|
if ( pNxt->IsContentFrame() )
|
|
{
|
|
SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
|
|
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
|
|
const SwBorderAttrs &rAttrs = *aAccess.Get();
|
|
if ( !pNxt->isFrameAreaSizeValid() )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
|
|
|
|
if( pNxt->IsVertical() )
|
|
{
|
|
aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
|
|
}
|
|
else
|
|
{
|
|
aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
|
|
}
|
|
}
|
|
static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
|
|
pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
|
|
}
|
|
else
|
|
{
|
|
SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
|
|
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
|
|
const SwBorderAttrs &rAttrs = *aAccess.Get();
|
|
if ( !pNxt->isFrameAreaSizeValid() )
|
|
{
|
|
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
|
|
|
|
if( pNxt->IsVertical() )
|
|
{
|
|
aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
|
|
}
|
|
else
|
|
{
|
|
aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
|
|
}
|
|
}
|
|
pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
|
|
}
|
|
|
|
pFrame->setFrameAreaPositionValid(bOldPos);
|
|
pFrame->setFrameAreaSizeValid(bOldSz);
|
|
pFrame->setFramePrintAreaValid(bOldPrt);
|
|
}
|
|
|
|
/// This routine checks whether there are no other FootnoteBosses
|
|
/// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
|
|
static bool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
|
|
{
|
|
assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
|
|
pFrame = pFrame->FindFootnoteBossFrame();
|
|
pNxt = pNxt->FindFootnoteBossFrame();
|
|
// If pFrame is a last column, we use the page instead.
|
|
while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
|
|
pFrame = pFrame->GetUpper()->FindFootnoteBossFrame();
|
|
// If pNxt is a first column, we use the page instead.
|
|
while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
|
|
pNxt = pNxt->GetUpper()->FindFootnoteBossFrame();
|
|
// So... now pFrame and pNxt are either two adjacent pages or columns.
|
|
return pFrame && pNxt && pFrame->GetNext() == pNxt;
|
|
}
|
|
|
|
bool SwContentFrame::WouldFit_( SwTwips nSpace,
|
|
SwLayoutFrame *pNewUpper,
|
|
bool bTstMove,
|
|
const bool bObjsInNewUpper )
|
|
{
|
|
// To have the footnote select its place carefully, it needs
|
|
// to be moved in any case if there is at least one page/column
|
|
// between the footnote and the new Upper.
|
|
SwFootnoteFrame* pFootnoteFrame = nullptr;
|
|
if ( IsInFootnote() )
|
|
{
|
|
if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) )
|
|
return true;
|
|
pFootnoteFrame = FindFootnoteFrame();
|
|
}
|
|
|
|
bool bRet;
|
|
bool bSplit = !pNewUpper->Lower();
|
|
SwContentFrame *pFrame = this;
|
|
const SwFrame *pTmpPrev = pNewUpper->Lower();
|
|
if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
|
|
pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
|
|
{
|
|
SwFrame const* pTmpNonHidden{pTmpPrev && pTmpPrev->IsHiddenNow() ? nullptr : pTmpPrev};
|
|
while (pTmpPrev && pTmpPrev->GetNext())
|
|
{
|
|
pTmpPrev = pTmpPrev->GetNext();
|
|
if (!pTmpPrev->IsHiddenNow())
|
|
{
|
|
pTmpNonHidden = pTmpPrev;
|
|
}
|
|
}
|
|
pTmpPrev = pTmpNonHidden;
|
|
}
|
|
|
|
// tdf#156727 if the previous one has keep-with-next, ignore it on this one!
|
|
bool const isIgnoreKeep(pTmpPrev && pTmpPrev->IsFlowFrame()
|
|
&& SwFlowFrame::CastFlowFrame(pTmpPrev)->IsKeep(
|
|
pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()));
|
|
|
|
do
|
|
{
|
|
if (pFrame->IsHiddenNow())
|
|
{ // shortcut
|
|
assert(pFrame == this);
|
|
bRet = true;
|
|
pFrame = nullptr;
|
|
break;
|
|
}
|
|
|
|
// #i46181#
|
|
SwTwips nSecondCheck = 0;
|
|
SwTwips nOldSpace = nSpace;
|
|
bool bOldSplit = bSplit;
|
|
|
|
if ( bTstMove || IsInFly() || ( IsInSct() &&
|
|
( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
|
|
pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
|
|
{
|
|
// This is going to get a bit insidious now. If you're faint of heart,
|
|
// you'd better look away here. If a Fly contains columns, then the Contents
|
|
// are movable, except ones in the last column (see SwFrame::IsMoveable()).
|
|
// Of course they're allowed to float back. WouldFit() only returns a usable
|
|
// value if the Frame is movable. To fool WouldFit() into believing there's
|
|
// a movable Frame, I'm just going to hang it somewhere else for the time.
|
|
// The same procedure applies for column sections to make SwSectionFrame::Growable()
|
|
// return the proper value.
|
|
// Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if
|
|
// there's no SwFootnoteFrame there.
|
|
SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ?
|
|
static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
|
|
SwLayoutFrame *pUp = pTmpFrame->GetUpper();
|
|
SwFrame *pOldNext = pTmpFrame->GetNext();
|
|
pTmpFrame->RemoveFromLayout();
|
|
pTmpFrame->InsertBefore( pNewUpper, nullptr );
|
|
// tdf#107126 for a section in a footnote, we have only inserted
|
|
// the SwTextFrame but no SwSectionFrame - reset mbInfSct flag
|
|
// to avoid crashing (but perhaps we should create a temp
|
|
// SwSectionFrame here because WidowsAndOrphans checks for that?)
|
|
pTmpFrame->InvalidateInfFlags();
|
|
if ( pFrame->IsTextFrame() &&
|
|
( bTstMove ||
|
|
static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
|
|
( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
|
|
!static_cast<SwTextFrame*>(pFrame)->IsEmpty()
|
|
)
|
|
)
|
|
)
|
|
{
|
|
bTstMove = true;
|
|
bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
|
|
}
|
|
else
|
|
bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
|
|
|
|
pTmpFrame->RemoveFromLayout();
|
|
pTmpFrame->InsertBefore( pUp, pOldNext );
|
|
pTmpFrame->InvalidateInfFlags(); // restore flags
|
|
}
|
|
else
|
|
{
|
|
bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
|
|
nSecondCheck = !bSplit ? 1 : 0;
|
|
}
|
|
|
|
SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
|
|
const SwBorderAttrs &rAttrs = *aAccess.Get();
|
|
|
|
// Sad but true: We need to consider the spacing in our calculation.
|
|
// This already happened in TestFormat.
|
|
if ( bRet && !bTstMove )
|
|
{
|
|
SwTwips nUpper;
|
|
|
|
if ( pTmpPrev )
|
|
{
|
|
nUpper = CalcUpperSpace( nullptr, pTmpPrev );
|
|
|
|
// in balanced columned section frames we do not want the
|
|
// common border
|
|
bool bCommonBorder = true;
|
|
if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
|
|
{
|
|
const SwSectionFrame* pSct = pFrame->FindSctFrame();
|
|
bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
|
|
}
|
|
|
|
// #i46181#
|
|
nSecondCheck = ( 1 == nSecondCheck &&
|
|
pFrame == this &&
|
|
IsTextFrame() &&
|
|
bCommonBorder &&
|
|
!static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
|
|
nUpper :
|
|
0;
|
|
|
|
nUpper += bCommonBorder ?
|
|
rAttrs.GetBottomLine( *pFrame ) :
|
|
rAttrs.CalcBottomLine();
|
|
|
|
}
|
|
else
|
|
{
|
|
// #i46181#
|
|
nSecondCheck = 0;
|
|
|
|
if( pFrame->IsVertical() )
|
|
nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
|
|
else
|
|
nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
|
|
}
|
|
|
|
nSpace -= nUpper;
|
|
|
|
if ( nSpace < 0 )
|
|
{
|
|
bRet = false;
|
|
|
|
// #i46181#
|
|
if ( nSecondCheck > 0 )
|
|
{
|
|
// The following code is intended to solve a (rare) problem
|
|
// causing some frames not to move backward:
|
|
// SwTextFrame::WouldFit() claims that the whole paragraph
|
|
// fits into the given space and subtracts the height of
|
|
// all lines from nSpace. nSpace - nUpper is not a valid
|
|
// indicator if the frame should be allowed to move backward.
|
|
// We do a second check with the original remaining space
|
|
// reduced by the required upper space:
|
|
nOldSpace -= nSecondCheck;
|
|
const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true);
|
|
if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
|
|
{
|
|
bRet = true;
|
|
bSplit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also consider lower spacing in table cells
|
|
IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess());
|
|
if ( bRet && IsInTab() &&
|
|
rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
|
|
{
|
|
nSpace -= rAttrs.GetULSpace().GetLower();
|
|
|
|
if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
|
|
{
|
|
nSpace -= rAttrs.CalcLineSpacing();
|
|
}
|
|
if ( nSpace < 0 )
|
|
{
|
|
bRet = false;
|
|
}
|
|
}
|
|
|
|
if (bRet && !bSplit && !isIgnoreKeep
|
|
&& pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
|
|
{
|
|
if( bTstMove )
|
|
{
|
|
while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
|
|
{
|
|
pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
|
|
}
|
|
// If last follow frame of <this> text frame isn't valid,
|
|
// a formatting of the next content frame doesn't makes sense.
|
|
// Thus, return true.
|
|
if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
|
|
{
|
|
OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" );
|
|
return true;
|
|
}
|
|
}
|
|
SwFrame *const pNxt{pFrame->FindNextIgnoreHidden()};
|
|
if (nullptr != pNxt && pNxt->IsContentFrame() &&
|
|
( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
|
|
pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
|
|
{
|
|
// TestFormat(?) does not like paragraph- or character anchored objects.
|
|
|
|
// current solution for the test formatting doesn't work, if
|
|
// objects are present in the remaining area of the new upper
|
|
if ( bTstMove &&
|
|
( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( !pNxt->isFrameAreaDefinitionValid() )
|
|
{
|
|
MakeNxt( pFrame, pNxt );
|
|
}
|
|
|
|
// Little trick: if the next has a predecessor, then the paragraph
|
|
// spacing has been calculated already, and we don't need to re-calculate
|
|
// it in an expensive way.
|
|
if( lcl_NotHiddenPrev( pNxt ) )
|
|
pTmpPrev = nullptr;
|
|
else
|
|
{
|
|
pTmpPrev = pFrame;
|
|
}
|
|
pFrame = static_cast<SwContentFrame*>(pNxt);
|
|
}
|
|
else
|
|
pFrame = nullptr;
|
|
}
|
|
else
|
|
pFrame = nullptr;
|
|
|
|
} while ( bRet && pFrame );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|