/* -*- 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 .
*/
SwTextNode const& GetAttrMerged(SfxItemSet & rFormatSet,
SwTextNode const& rNode, SwRootFrame const*const pLayout)
{
rNode.SwContentNode::GetAttr(rFormatSet); if (pLayout && pLayout->HasMergedParas())
{ auto pFrame = static_cast<SwTextFrame*>(rNode.getLayoutFrame(pLayout)); if (sw::MergedPara const*const pMerged = pFrame ? pFrame->GetMergedPara() : nullptr)
{ if (pMerged->pFirstNode != &rNode)
{
rFormatSet.ClearItem(RES_PAGEDESC);
rFormatSet.ClearItem(RES_BREAK);
static_assert(RES_PAGEDESC + 1 == sal_uInt16(RES_BREAK), "first-node items must be adjacent");
SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(*rFormatSet.GetPool());
pMerged->pFirstNode->SwContentNode::GetAttr(firstSet);
rFormatSet.Put(firstSet);
} if (pMerged->pParaPropsNode != &rNode)
{ for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
{ if (i != RES_PAGEDESC && i != RES_BREAK)
{
rFormatSet.ClearItem(i);
}
} for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
{
rFormatSet.ClearItem(i);
}
SfxItemSetFixed<RES_PARATR_BEGIN, RES_PAGEDESC,
RES_BREAK+1, RES_FRMATR_END,
XATTR_FILL_FIRST, XATTR_FILL_LAST+1>
propsSet(*rFormatSet.GetPool());
pMerged->pParaPropsNode->SwContentNode::GetAttr(propsSet);
rFormatSet.Put(propsSet); return *pMerged->pParaPropsNode;
} // keep all the CHRATR/UNKNOWNATR anyway...
}
} return rNode;
}
} // namespace sw
/// Switches width and height of the text frame void SwTextFrame::SwapWidthAndHeight()
{
{
SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
/** * Calculates the coordinates of a rectangle when switching from * horizontal to vertical layout.
*/ void SwTextFrame::SwitchHorizontalToVertical( SwRect& rRect ) const
{ // calc offset inside frame
tools::Long nOfstX, nOfstY; if ( IsVertLR() )
{ if (IsVertLRBT())
{ // X and Y offsets here mean the position of the point that will be the top left corner // after the switch.
nOfstX = rRect.Left() + rRect.Width() - getFrameArea().Left();
nOfstY = rRect.Top() - getFrameArea().Top();
} else
{
nOfstX = rRect.Left() - getFrameArea().Left();
nOfstY = rRect.Top() - getFrameArea().Top();
}
} else
{
nOfstX = rRect.Left() - getFrameArea().Left();
nOfstY = rRect.Top() + rRect.Height() - getFrameArea().Top();
}
/** * Calculates the coordinates of a point when switching from * horizontal to vertical layout.
*/ void SwTextFrame::SwitchHorizontalToVertical( Point& rPoint ) const
{ if (IsVertLRBT())
{ // The horizontal origo is the top left corner, the LRBT origo is the // bottom left corner. Finally x and y has to be swapped.
SAL_WARN_IF(!mbIsSwapped, "sw.core", "SwTextFrame::SwitchHorizontalToVertical, IsVertLRBT, not swapped");
Point aPoint(rPoint);
rPoint.setX(getFrameArea().Left() + (aPoint.Y() - getFrameArea().Top())); // This would be bottom - x delta, but bottom is top + height, finally // width (and not height), as it's swapped.
rPoint.setY(getFrameArea().Top() + getFrameArea().Width()
- (aPoint.X() - getFrameArea().Left())); return;
}
/** * Calculates the a limit value when switching from * horizontal to vertical layout.
*/
tools::Long SwTextFrame::SwitchHorizontalToVertical( tools::Long nLimit ) const
{
Point aTmp( 0, nLimit );
SwitchHorizontalToVertical( aTmp ); return aTmp.X();
}
/** * Calculates the coordinates of a rectangle when switching from * vertical to horizontal layout.
*/ void SwTextFrame::SwitchVerticalToHorizontal( SwRect& rRect ) const
{
tools::Long nOfstX;
/** * Calculates the coordinates of a point when switching from * vertical to horizontal layout.
*/ void SwTextFrame::SwitchVerticalToHorizontal( Point& rPoint ) const
{
tools::Long nOfstX;
// calc offset inside frame if ( IsVertLR() ) // X offset is Y - left.
nOfstX = rPoint.X() - getFrameArea().Left(); else
{ // X offset is right - X. if ( mbIsSwapped )
nOfstX = getFrameArea().Left() + getFrameArea().Height() - rPoint.X(); else
nOfstX = getFrameArea().Left() + getFrameArea().Width() - rPoint.X();
}
tools::Long nOfstY; if (IsVertLRBT())
{ // Y offset is bottom - Y. if (mbIsSwapped)
nOfstY = getFrameArea().Top() + getFrameArea().Width() - rPoint.Y(); else
nOfstY = getFrameArea().Top() + getFrameArea().Height() - rPoint.Y();
} else // Y offset is Y - top.
nOfstY = rPoint.Y() - getFrameArea().Top();
/** * Calculates the a limit value when switching from * vertical to horizontal layout.
*/
tools::Long SwTextFrame::SwitchVerticalToHorizontal( tools::Long nLimit ) const
{
Point aTmp( nLimit, 0 );
SwitchVerticalToHorizontal( aTmp ); return aTmp.Y();
}
void SwTextFrame::Init()
{
OSL_ENSURE( !IsLocked(), "+SwTextFrame::Init: this is locked." ); if( !IsLocked() )
{
ClearPara();
SetHasRotatedPortions(false); // set flags directly to save a ResetPreps call, // and thereby an unnecessary GetPara call // don't set bOrphan, bLocked or bWait to false! // bOrphan = bFlag7 = bFlag8 = false;
}
}
void RemoveFootnotesForNode(
SwRootFrame const& rLayout, SwTextNode const& rTextNode,
std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents)
{ if (pExtents && pExtents->empty())
{ return; // nothing to do
} const SwFootnoteIdxs &rFootnoteIdxs = rTextNode.GetDoc().GetFootnoteIdxs();
size_t nPos = 0;
SwNodeOffset const nIndex = rTextNode.GetIndex();
rFootnoteIdxs.SeekEntry( rTextNode, &nPos ); if (nPos < rFootnoteIdxs.size())
{ while (nPos > 0 && rTextNode == (rFootnoteIdxs[ nPos ]->GetTextNode()))
--nPos; if (nPos || rTextNode != (rFootnoteIdxs[ nPos ]->GetTextNode()))
++nPos;
}
size_t iter(0); for ( ; nPos < rFootnoteIdxs.size(); ++nPos)
{
SwTextFootnote* pTextFootnote = rFootnoteIdxs[ nPos ]; if (pTextFootnote->GetTextNode().GetIndex() > nIndex) break; if (pExtents)
{ while ((*pExtents)[iter].second <= pTextFootnote->GetStart())
{
++iter; if (iter == pExtents->size())
{ return;
}
} if (pTextFootnote->GetStart() < (*pExtents)[iter].first)
{ continue;
}
}
pTextFootnote->DelFrames(&rLayout);
}
}
} // namespace sw
void SwTextFrame::DestroyImpl()
{ // Remove associated SwParaPortion from s_pTextCache
ClearPara();
assert(!GetDoc().IsInDtor()); // this shouldn't be happening with ViewShell owning layout if (!GetDoc().IsInDtor() && HasFootnote())
{ if (m_pMergedPara)
{
SwTextNode const* pNode(nullptr); for (autoconst& e : m_pMergedPara->extents)
{ if (e.pNode != pNode)
{
pNode = e.pNode; // sw_redlinehide: not sure if it's necessary to check // if the nodes are still alive here, which would require // accessing WriterMultiListener::m_vDepends
sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
}
}
} else
{
SwTextNode *const pNode(static_cast<SwTextNode*>(GetDep())); if (pNode)
{
sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
}
}
}
if (!GetDoc().IsInDtor())
{ if (SwView* pView = GetActiveView())
pView->GetEditWin().GetFrameControlsManager().RemoveControls(this);
}
SwTextNode const* SwTextFrame::GetTextNodeForParaProps() const
{ // FIXME can GetPara be 0 ? yes... this is needed in SwContentNotify::SwContentNotify() which is called before any formatting is started //nope assert(GetPara());
sw::MergedPara const*const pMerged(GetMergedPara()); if (pMerged)
{ // assert(pMerged->pFirstNode == pMerged->pParaPropsNode); // surprising news! return pMerged->pParaPropsNode;
} else returnstatic_cast<SwTextNode const*>(SwFrame::GetDep());
}
// TODO: what is the above check good for and can it be removed? return IsHiddenNowImpl();
}
bool SwTextFrame::IsHiddenNowImpl() const
{ if (SwContentFrame::IsHiddenNow()) returntrue;
bool bHiddenCharsHidePara(false); bool bHiddenParaField(false); if (m_pMergedPara)
{
TextFrameIndex nHiddenStart(COMPLETE_STRING);
TextFrameIndex nHiddenEnd(0); bool hasHidden{false}; if (autoconst pScriptInfo = GetScriptInfo())
{
hasHidden = pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0),
nHiddenStart, nHiddenEnd);
} else// ParaPortion is created in Format, but this is called earlier
{
SwScriptInfo aInfo;
aInfo.InitScriptInfoHidden(*m_pMergedPara->pFirstNode, m_pMergedPara.get());
hasHidden = aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0),
nHiddenStart, nHiddenEnd);
} if ((TextFrameIndex(0) == nHiddenStart
&& TextFrameIndex(GetText().getLength()) <= nHiddenEnd) // special case: GetBoundsOfHiddenRange doesn't assign! // but it does return that there *is* something hidden, in case // the frame is empty then the whole thing must be hidden
|| (hasHidden && m_pMergedPara->mergedText.isEmpty()))
{
bHiddenCharsHidePara = true;
}
sw::MergedAttrIter iter(*this);
SwTextNode const* pNode(nullptr); int nNewResultWeight = 0; for (SwTextAttr const* pHint = iter.NextAttr(&pNode); pHint; pHint = iter.NextAttr(&pNode))
{ if (pHint->Which() == RES_TXTATR_FIELD)
{ // see also SwpHints::CalcHiddenParaField() const SwFormatField& rField = pHint->GetFormatField(); int nCurWeight = pNode->GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which()); if (nCurWeight > nNewResultWeight)
{
nNewResultWeight = nCurWeight;
bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
} elseif (nCurWeight == nNewResultWeight && bHiddenParaField)
{ // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide" // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only // care about fields of higher weight.
bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
}
}
}
} else
{
bHiddenCharsHidePara = static_cast<SwTextNode const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
bHiddenParaField = static_cast<SwTextNode const*>(SwFrame::GetDep())->IsHiddenByParaField();
} if (bHiddenCharsHidePara && GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
{ // apparently in Word it's always the last para marker that determines hidden? // even in case when they are merged by delete redline (it's obvious when they are merged by hidden-attribute
SwTextNode const*const pNode{ m_pMergedPara
? m_pMergedPara->pLastNode
: static_cast<SwTextNode const*>(SwFrame::GetDep()) }; // Word ignores hidden formatting on the cell end marker bool isLastInCell{false}; if (SwLayoutFrame const*const pCellFrame{FindCellFrame(this)})
{
SwContentFrame const* pNext{GetNextContentFrame()}; // skip frame in hidden section ("this" is *not* in hidden section!) while (pNext && pNext->SwContentFrame::IsHiddenNow())
{
pNext = pNext->GetNextContentFrame();
}
isLastInCell = pNext == nullptr || !pCellFrame->IsAnLower(pNext);
} if (!isLastInCell)
{
SwFormatAutoFormat const& rListAutoFormat{pNode->GetAttr(RES_PARATR_LIST_AUTOFMT)};
std::shared_ptr<SfxItemSet> const pSet{rListAutoFormat.GetStyleHandle()};
SvxCharHiddenItem const* pItem{pSet ? pSet->GetItemIfSet(RES_CHRATR_HIDDEN) : nullptr}; if (!pItem)
{ // don't use node's mpAttrSet, it doesn't apply to para marker
SwFormatColl const*const pStyle{pNode->GetFormatColl()}; if (pStyle)
{
pItem = &pStyle->GetFormatAttr(RES_CHRATR_HIDDEN);
}
} if (!pItem || !pItem->GetValue())
{
bHiddenCharsHidePara = false;
}
}
} const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
if (
( bHiddenParaField &&
( !pVsh->GetViewOptions()->IsShowHiddenPara() &&
!pVsh->GetViewOptions()->IsFieldName() ) ) ||
( bHiddenCharsHidePara &&
!pVsh->GetViewOptions()->IsShowHiddenChar() ) )
{ // in order to put the cursor in the body text, one paragraph must // be visible - check this for the 1st body paragraph if (IsInDocBody() && FindPrevCnt() == nullptr)
{ for (SwContentFrame const* pNext = FindNextCnt(true);
pNext != nullptr; pNext = pNext->FindNextCnt(true))
{ if (!pNext->IsHiddenNow()) returntrue;
}
SAL_INFO("sw.core", "unhiding one body paragraph"); returnfalse;
} returntrue;
}
}
returnfalse;
}
/// Removes Textfrm's attachments, when it's hidden void SwTextFrame::HideHidden()
{
OSL_ENSURE( !GetFollow() && IsHiddenNow(), "HideHidden on visible frame of hidden frame has follow" );
/** * as-character anchored graphics, which are used for a graphic bullet list. * As long as these graphic bullet list aren't imported, do not hide a * at-character anchored object, if * (a) the document is an imported WW8 document - * checked by checking certain compatibility options - * (b) the paragraph is the last content in the document and * (c) the anchor character is an as-character anchored graphic.
*/ bool sw_HideObj( const SwTextFrame& _rFrame, const RndStdIds _eAnchorType,
SwFormatAnchor const& rFormatAnchor,
SwAnchoredObject* _pAnchoredObj )
{ bool bRet( true );
/** * Hide/show objects * * Method hides respectively shows objects, which are anchored at paragraph, * at/as a character of the paragraph, corresponding to the paragraph and * paragraph portion visibility. * * - is called from HideHidden() - should hide objects in hidden paragraphs and * - from Format_() - should hide/show objects in partly visible paragraphs
*/ void SwTextFrame::HideAndShowObjects()
{ if ( GetDrawObjs() )
{ if ( IsHiddenNow() )
{ // complete paragraph is hidden. Thus, hide all objects for (SwAnchoredObject* i : *GetDrawObjs())
{
SdrObject* pObj = i->DrawObj();
SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); // under certain conditions const RndStdIds eAnchorType( pContact->GetAnchorId() ); if ((eAnchorType != RndStdIds::FLY_AT_CHAR) ||
sw_HideObj(*this, eAnchorType, pContact->GetAnchorFormat(),
i ))
{
pContact->MoveObjToInvisibleLayer( pObj );
}
}
} else
{ // paragraph is visible, but can contain hidden text portion. // first we check if objects are allowed to be hidden: const SwViewShell* pVsh = getRootFrame()->GetCurrShell(); constbool bShouldBeHidden = !pVsh || !pVsh->GetWin() ||
!pVsh->GetViewOptions()->IsShowHiddenChar();
// Thus, show all objects, which are anchored at paragraph and // hide/show objects, which are anchored at/as character, according // to the visibility of the anchor character. for (SwAnchoredObject* i : *GetDrawObjs())
{
SdrObject* pObj = i->DrawObj();
SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); // Determine anchor type only once const RndStdIds eAnchorType( pContact->GetAnchorId() );
if (eAnchorType == RndStdIds::FLY_AT_PARA)
{
pContact->MoveObjToVisibleLayer( pObj );
} elseif ((eAnchorType == RndStdIds::FLY_AT_CHAR) ||
(eAnchorType == RndStdIds::FLY_AS_CHAR))
{
sal_Int32 nHiddenStart;
sal_Int32 nHiddenEnd; const SwFormatAnchor& rAnchorFormat = pContact->GetAnchorFormat(); const SwNode* pNode = rAnchorFormat.GetAnchorNode(); // When the object was already removed from text, but the layout hasn't been // updated yet, this can be nullptr: if (!pNode) continue;
SwScriptInfo::GetBoundsOfHiddenRange(
*pNode->GetTextNode(),
rAnchorFormat.GetAnchorContentOffset(), nHiddenStart, nHiddenEnd); // Under certain conditions if ( nHiddenStart != COMPLETE_STRING && bShouldBeHidden &&
sw_HideObj(*this, eAnchorType, rAnchorFormat, i))
{
pContact->MoveObjToInvisibleLayer( pObj );
} else
pContact->MoveObjToVisibleLayer( pObj );
} else
{
OSL_FAIL( "<SwTextFrame::HideAndShowObjects()> - object not anchored at/inside paragraph!?" );
}
}
}
}
if (IsFollow())
{
SwTextFrame *pMaster = FindMaster();
OSL_ENSURE(pMaster, "SwTextFrame without master"); if (pMaster)
pMaster->HideAndShowObjects();
}
}
/** * Returns the first possible break point in the current line. * This method is used in SwTextFrame::Format() to decide whether the previous * line has to be formatted as well. * nFound is <= nEndLine.
*/
TextFrameIndex SwTextFrame::FindBrk(std::u16string_view aText, const TextFrameIndex nStart, const TextFrameIndex nEnd)
{
sal_Int32 nFound = sal_Int32(nStart); const sal_Int32 nEndLine = std::min(sal_Int32(nEnd), sal_Int32(aText.size()) - 1);
// Skip all leading blanks. while( nFound <= nEndLine && ' ' == aText[nFound] )
{
nFound++;
}
// A tricky situation with the TextAttr-Dummy-character (in this case "$"): // "Dr.$Meyer" at the beginning of the second line. Typing a blank after that // doesn't result in the word moving into first line, even though that would work. // For this reason we don't skip the dummy char. while( nFound <= nEndLine && ' ' != aText[nFound] )
{
nFound++;
}
return TextFrameIndex(nFound);
}
bool SwTextFrame::IsIdxInside(TextFrameIndex const nPos, TextFrameIndex const nLen) const
{ if (nPos == TextFrameIndex(COMPLETE_STRING)) // the "not found" range returnfalse; // Silence over-eager warning emitted at least by GCC trunk towards 6: #ifdefined __GNUC__ && !defined __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-overflow" #endif if (nLen != TextFrameIndex(COMPLETE_STRING) && GetOffset() > nPos + nLen) // the range preceded us #ifdefined __GNUC__ && !defined __clang__ #pragma GCC diagnostic pop #endif returnfalse;
if( !GetFollow() ) // the range doesn't precede us, returntrue; // nobody follows us.
// either the range overlap or our text has been deleted // sw_redlinehide: GetText() should be okay here because it has already // been updated in the INS/DEL hint case if (nMax > nPos || nMax > TextFrameIndex(GetText().getLength())) returntrue;
// changes made in the first line of a follow can modify the master const SwParaPortion* pPara = GetFollow()->GetPara(); return pPara && ( nPos <= nMax + pPara->GetLen() );
}
SwTextFrameBreak aBreak( this ); if( GetFollow() || aBreak.IsBreakNow( aLine ) )
{ // if there is a Follow() or if we need to break here, reformat
Init();
} else
{ // everything is business as usual...
pPara->SetPrepAdjust();
pPara->SetPrep();
}
}
// Note: for now this overrides SwClient::SwClientNotify; the intermediary // classes still override SwClient::Modify, which should continue to work // as their implementation of SwClientNotify is SwClient's which calls Modify. // Therefore we also don't need to call SwClient::SwClientNotify(rModify, rHint) // because that's all it does, and this implementation calls // SwContentFrame::SwClientNotify() when appropriate. void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
{
SfxPoolItem const* pOld(nullptr);
SfxPoolItem const* pNew(nullptr);
sw::MoveText const* pMoveText(nullptr);
sw::InsertText const* pInsertText(nullptr);
sw::DeleteText const* pDeleteText(nullptr);
sw::DeleteChar const* pDeleteChar(nullptr);
sw::RedlineDelText const* pRedlineDelText(nullptr);
sw::RedlineUnDelText const* pRedlineUnDelText(nullptr);
SwFormatChangeHint const * pFormatChangedHint(nullptr);
sw::AttrSetChangeHint const* pAttrSetChangeHint(nullptr);
sw::UpdateAttrHint const* pUpdateAttrHint(nullptr);
// while locked ignore all modifications if( IsLocked() ) return;
// save stack // warning: one has to ensure that all variables are set
TextFrameIndex nPos;
TextFrameIndex nLen; bool bSetFieldsDirty = false; bool bRecalcFootnoteFlag = false;
if (pRedlineDelText)
{ if (m_pMergedPara)
{
sal_Int32 const nNPos = pRedlineDelText->nStart;
sal_Int32 const nNLen = pRedlineDelText->nLen;
nPos = MapModelToView(&rNode, nNPos); // update merged before doing anything else
nLen = UpdateMergedParaForDelete(*m_pMergedPara,
getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(), false, rNode, nNPos, nNLen); const sal_Int32 m = -nNLen; if (nLen && IsIdxInside(nPos, nLen))
{
InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
}
lcl_SetWrong( *this, rNode, nNPos, m, false ); if (nLen)
{
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = bRecalcFootnoteFlag = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
}
}
} elseif (pRedlineUnDelText)
{ if (m_pMergedPara)
{
sal_Int32 const nNPos = pRedlineUnDelText->nStart;
sal_Int32 const nNLen = pRedlineUnDelText->nLen;
nPos = MapModelToView(&rNode, nNPos);
nLen = UpdateMergedParaForInsert(*m_pMergedPara,
getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(), false, rNode, nNPos, nNLen); if (IsIdxInside(nPos, nLen))
{ if (!nLen)
{ // Refresh NumPortions even when line is empty! if (nPos)
InvalidateSize(); else
Prepare();
} else
InvalidateRange_( SwCharRange( nPos, nLen ), nNLen );
}
lcl_SetWrong( *this, rNode, nNPos, nNLen, false );
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>);
}
} elseif (pMoveText)
{ if (m_pMergedPara
&& m_pMergedPara->pFirstNode->GetIndex() <= pMoveText->pDestNode->GetIndex()
&& pMoveText->pDestNode->GetIndex() <= m_pMergedPara->pLastNode->GetIndex())
{ // if it's not 2 nodes in merged frame, assume the target node doesn't have frames at all
assert(abs(rNode.GetIndex() - pMoveText->pDestNode->GetIndex()) == SwNodeOffset(1));
UpdateMergedParaForMove(*m_pMergedPara,
*this,
bRecalcFootnoteFlag,
*pMoveText->pDestNode, rNode,
pMoveText->nDestStart,
pMoveText->nSourceStart,
pMoveText->nLen);
} else
{ // there is a situation where this is okay: from JoinNext, which will then call CheckResetRedlineMergeFlag, which will then create merged from scratch for this frame // assert(!m_pMergedPara || !getRootFrame()->IsHideRedlines() || !pMoveText->pDestNode->getLayoutFrame(getRootFrame()));
}
} elseif (pInsertText)
{
nPos = MapModelToView(&rNode, pInsertText->nPos); // unlike redlines, inserting into fieldmark must be explicitly handled bool isHidden(false); switch (getRootFrame()->GetFieldmarkMode())
{ case sw::FieldmarkMode::ShowCommand:
isHidden = pInsertText->isInsideFieldmarkResult; break; case sw::FieldmarkMode::ShowResult:
isHidden = pInsertText->isInsideFieldmarkCommand; break; case sw::FieldmarkMode::ShowBoth: // just to avoid the warning break;
} if (!isHidden)
{
nLen = TextFrameIndex(pInsertText->nLen); if (m_pMergedPara)
{
UpdateMergedParaForInsert(*m_pMergedPara,
getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(), true, rNode, pInsertText->nPos, pInsertText->nLen);
} if( IsIdxInside( nPos, nLen ) )
{ if( !nLen )
{ // Refresh NumPortions even when line is empty! if( nPos )
InvalidateSize(); else
Prepare();
} else
InvalidateRange_( SwCharRange( nPos, nLen ), pInsertText->nLen );
}
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>);
}
lcl_SetWrong( *this, rNode, pInsertText->nPos, pInsertText->nLen, true );
} elseif (pDeleteText)
{
nPos = MapModelToView(&rNode, pDeleteText->nStart); if (m_pMergedPara)
{ // update merged before doing anything else
nLen = UpdateMergedParaForDelete(*m_pMergedPara,
getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(), true, rNode, pDeleteText->nStart, pDeleteText->nLen);
} else
{
nLen = TextFrameIndex(pDeleteText->nLen);
} const sal_Int32 m = -pDeleteText->nLen; if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
{ if( !nLen )
InvalidateSize(); else
InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
}
lcl_SetWrong( *this, rNode, pDeleteText->nStart, m, true ); if (nLen)
{
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = bRecalcFootnoteFlag = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
}
} elseif (pDeleteChar)
{
nPos = MapModelToView(&rNode, pDeleteChar->m_nPos); if (m_pMergedPara)
{
nLen = UpdateMergedParaForDelete(*m_pMergedPara,
getRootFrame()->GetParagraphBreakMode(), GetScriptInfo(), true, rNode, pDeleteChar->m_nPos, 1);
} else
{
nLen = TextFrameIndex(1);
}
lcl_SetWrong( *this, rNode, pDeleteChar->m_nPos, -1, true ); if (nLen)
{
InvalidateRange( SwCharRange(nPos, nLen), -1 );
lcl_SetScriptInval( *this, nPos );
bSetFieldsDirty = bRecalcFootnoteFlag = true;
lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
}
} elseif (pAttrSetChangeHint)
{
InvalidateLineNum();
// i#11859 // (1) Also invalidate next frame on next page/column. // (2) Skip empty sections and hidden paragraphs // Thus, use method <InvalidateNextPrtArea()>
InvalidateNextPrtArea();
SetCompletePaint();
}
nClear |= 0x04; if ( bLineSpace )
{
--nCount; if ((!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
&& IsInSct() && !GetPrev())
{
SwSectionFrame *pSect = FindSctFrame(); if( pSect->ContainsAny() == this )
pSect->InvalidatePrt();
}
} if ( bRegister )
--nCount;
} if ( SfxItemState::SET == rNewSet.GetItemState( RES_PARATR_SPLIT, false ))
{ if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
{ if (GetPrev())
CheckKeep();
Prepare();
InvalidateSize();
}
nClear |= 0x08;
--nCount;
}
if( SfxItemState::SET == rNewSet.GetItemState( RES_BACKGROUND, false)
&& (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
&& !IsFollow() && GetDrawObjs() )
{
SwSortedObjs *pObjs = GetDrawObjs(); for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
{
SwAnchoredObject* pAnchoredObj = (*pObjs)[i]; if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
{ if( !pFly->IsFlyInContentFrame() )
{ const SvxBrushItem &rBack =
pFly->GetAttrSet()->GetBackground(); // #GetTransChg# // following condition determines, if the fly frame // "inherites" the background color of text frame. // This is the case, if fly frame background // color is "no fill"/"auto fill" and if the fly frame // has no background graphic. // Thus, check complete fly frame background // color and *not* only its transparency value if ( (rBack.GetColor() == COL_TRANSPARENT) &&
rBack.GetGraphicPos() == GPOS_NONE )
{
pFly->SetCompletePaint();
pFly->InvalidatePage();
}
}
}
}
}
// i#11859 // (1) Also invalidate next frame on next page/column. // (2) Skip empty sections and hidden paragraphs // Thus, use method <InvalidateNextPrtArea()>
InvalidateNextPrtArea();
SetCompletePaint();
} break;
case RES_TXTATR_FIELD: case RES_TXTATR_ANNOTATION:
{
sal_Int32 const nNPos = static_cast<const SwFormatField*>(pNew)->GetTextField()->GetStart();
nPos = MapModelToView(&rNode, nNPos); if (IsIdxInside(nPos, TextFrameIndex(1)))
{ if (SfxPoolItem::areSame( pNew, pOld ))
{ // only repaint // opt: invalidate window?
InvalidatePage();
SetCompletePaint();
} else
InvalidateRange_(SwCharRange(nPos, TextFrameIndex(1)));
}
bSetFieldsDirty = true; // ST2 if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() )
lcl_SetWrong( *this, rNode, nNPos, nNPos + 1, false );
} break;
case RES_TXTATR_FTN :
{ if (!IsInFootnote())
{ // the hint may be sent from the anchor node, or from a // node in the footnote; the anchor index is only valid in the // anchor node!
assert(rNode == static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetTextNode());
nPos = MapModelToView(&rNode, static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetStart());
} #ifdef _MSC_VER else nPos = TextFrameIndex(42); // shut up MSVC 2017 spurious warning C4701 #endif if (IsInFootnote() || IsIdxInside(nPos, TextFrameIndex(1)))
Prepare( PrepareHint::FootnoteInvalidation, static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote() ); break;
}
case RES_PARATR_SPLIT: if ( GetPrev() )
CheckKeep();
Prepare();
bSetFieldsDirty = true; break; case RES_FRAMEDIR :
assert(false); // should have been forwarded to SwContentFrame
SetDerivedR2L( false );
CheckDirChange(); break; default:
{
Prepare();
InvalidatePrt_(); if ( !nWhich )
{ // is called by e. g. HiddenPara with 0
SwFrame *pNxt = FindNext(); if ( nullptr != pNxt )
pNxt->InvalidatePrt();
}
}
} // switch
// If it's certain that we can yield lines, the Master needs // to check the widow rule if( !nHave )
{ bool bSplit = true; if( !IsFollow() ) // only a master decides about orphans
{ const WidowsAndOrphans aWidOrp( this );
bSplit = ( aLine.GetLineNr() >= aWidOrp.GetOrphansLines() &&
aLine.GetLineNr() >= aLine.GetDropLines() );
}
if ( IsEmpty() )
{ switch ( ePrep )
{ case PrepareHint::BossChanged:
SetInvalidVert( true ); // Test
[[fallthrough]]; case PrepareHint::WidowsOrphans: case PrepareHint::Widows: case PrepareHint::FootnoteInvalidationGone : return bParaPossiblyInvalid;
case PrepareHint::FramePositionChanged :
{ // We also need an InvalidateSize for Areas (with and without columns), // so that we format and bUndersized is set (if needed) if( IsInFly() || IsInSct() )
{
SwTwips nTmpBottom = GetUpper()->getFrameArea().Top() +
GetUpper()->getFramePrintArea().Bottom(); if( nTmpBottom < getFrameArea().Bottom() ) break;
} // Are there any free-flying frames on this page?
SwTextFly aTextFly( this ); if( aTextFly.IsOn() )
{ // Does any free-flying frame overlap? if ( aTextFly.Relax() || IsUndersized() ) break;
} if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue()) break;
SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame())); if (pGrid && GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue()) break;
// Split fly anchors are technically empty (have no SwParaPortion), but otherwise behave like // other split text frames, which are non-empty. bool bSplitFlyAnchor = GetOffset() == TextFrameIndex(0) && HasFollow()
&& GetFollow()->GetOffset() == TextFrameIndex(0);
if( !HasPara() && !bSplitFlyAnchor && PrepareHint::MustFit != ePrep )
{
OSL_ENSURE( !IsLocked(), "SwTextFrame::Prepare: three of a perfect pair" ); // check while ignoring frame width (testParagraphMarkInCell) // because it's called from InvalidateAllContent() if (!IsHiddenNowImpl())
{
SetInvalidVert( true ); // Test if ( bNotify )
InvalidateSize(); else
InvalidateSize_();
} return bParaPossiblyInvalid;
}
// Get object from cache while locking
SwTextLineAccess aAccess( this );
SwParaPortion *pPara = aAccess.GetPara();
InvalidatePrt_();
InvalidateSize_();
[[fallthrough]]; case PrepareHint::AdjustSizeWithoutFormatting :
pPara->SetPrepAdjust(); if( IsFootnoteNumFrame() != pPara->IsFootnoteNum() ||
IsUndersized() )
{
InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1); if( GetOffset() && !IsFollow() )
SetOffset_(TextFrameIndex(0));
} break; case PrepareHint::MustFit :
pPara->SetPrepMustFit(true);
[[fallthrough]]; case PrepareHint::WidowsOrphans :
pPara->SetPrepAdjust(); break; case PrepareHint::Widows : // MustFit is stronger than anything else if( pPara->IsPrepMustFit() ) return bParaPossiblyInvalid; // see comment in WidowsAndOrphans::FindOrphans and CalcPreps()
PrepWidows( *static_cast<const sal_uInt16 *>(pVoid), bNotify ); break;
case PrepareHint::FootnoteInvalidation :
{
SwTextFootnote const *pFootnote = static_cast<SwTextFootnote const *>(pVoid); if( IsInFootnote() )
{ // Am I the first TextFrame of a footnote? if( !GetPrev() ) // So we're a TextFrame of the footnote, which has // to display the footnote number or the ErgoSum text
InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
if( !GetNext() )
{ // We're the last Footnote; we need to update the // QuoVadis texts now const SwFootnoteInfo &rFootnoteInfo = GetDoc().GetFootnoteInfo(); if( !pPara->UpdateQuoVadis( rFootnoteInfo.m_aQuoVadis ) )
{
TextFrameIndex nPos = pPara->GetParLen(); if( nPos )
--nPos;
InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), 1);
}
}
} else
{ // We are the TextFrame _with_ the footnote
TextFrameIndex const nPos = MapModelToView(
&pFootnote->GetTextNode(), pFootnote->GetStart());
InvalidateRange(SwCharRange(nPos, TextFrameIndex(1)), 1);
} break;
} case PrepareHint::BossChanged :
{ // Test
{
SetInvalidVert( false ); bool bOld = IsVertical();
SetInvalidVert( true ); if( bOld != IsVertical() )
InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(COMPLETE_STRING)));
}
if( HasFollow() )
{
TextFrameIndex nNxtOfst = GetFollow()->GetOffset(); if( nNxtOfst )
--nNxtOfst;
InvalidateRange(SwCharRange( nNxtOfst, TextFrameIndex(1)), 1);
} if( IsInFootnote() )
{
TextFrameIndex nPos; if( lcl_ErgoVadis( this, nPos, PrepareHint::QuoVadis ) )
InvalidateRange( SwCharRange( nPos, TextFrameIndex(1)) ); if( lcl_ErgoVadis( this, nPos, PrepareHint::ErgoSum ) )
InvalidateRange( SwCharRange( nPos, TextFrameIndex(1)) );
} // If we have a page number field, we must invalidate those spots
SwTextNode const* pNode(nullptr);
sw::MergedAttrIter iter(*this);
TextFrameIndex const nEnd = GetFollow()
? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING); for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
{
TextFrameIndex const nStart(MapModelToView(pNode, pHt->GetStart())); if (nStart >= GetOffset())
{ if (nStart >= nEnd) break;
// If we're flowing back and own a Footnote, the Footnote also flows // with us. So that it doesn't obstruct us, we send ourselves // an ADJUST_FRM. // pVoid != 0 means MoveBwd() const sal_uInt16 nWhich = pHt->Which(); if (RES_TXTATR_FIELD == nWhich ||
(HasFootnote() && pVoid && RES_TXTATR_FTN == nWhich))
InvalidateRange(SwCharRange(nStart, TextFrameIndex(1)), 1);
}
} // A new boss, a new chance for growing if( IsUndersized() )
{
InvalidateSize_();
InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(1)), 1);
} break;
}
case PrepareHint::FramePositionChanged :
{ if ( isFramePrintAreaValid() )
{
SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame())); if (pGrid && GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
InvalidatePrt();
}
// If we don't overlap with anybody: // did any free-flying frame overlapped _before_ the position change? bool bFormat = pPara->HasFly(); if( !bFormat )
{ if( IsInFly() )
{
SwTwips nTmpBottom = GetUpper()->getFrameArea().Top() +
GetUpper()->getFramePrintArea().Bottom(); if( nTmpBottom < getFrameArea().Bottom() )
bFormat = true;
} if( !bFormat )
{ if ( GetDrawObjs() )
{ const size_t nCnt = GetDrawObjs()->size(); for ( size_t i = 0; i < nCnt; ++i )
{
SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i]; // i#28701 - consider all // to-character anchored objects if ( pAnchoredObj->GetFrameFormat()->GetAnchor().GetAnchorId()
== RndStdIds::FLY_AT_CHAR )
{
bFormat = true; break;
}
}
} if( !bFormat )
{ // Are there any free-flying frames on this page?
SwTextFly aTextFly( this ); if( aTextFly.IsOn() )
{ // Does any free-flying frame overlap? constbool bRelaxed = aTextFly.Relax();
bFormat = bRelaxed || IsUndersized(); if (bRelaxed)
{ // It's possible that pPara was deleted above; retrieve it again
pPara = aAccess.GetPara();
}
}
}
}
}
if( bFormat )
{ if( !IsLocked() )
{ if( pPara->GetRepaint().HasArea() )
SetCompletePaint();
Init();
pPara = nullptr;
InvalidateSize_();
}
} else
{ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
bParaPossiblyInvalid = Prepare( PrepareHint::Register, nullptr, bNotify ); // The Frames need to be readjusted, which caused by changes // in position elseif( HasFootnote() )
{
bParaPossiblyInvalid = Prepare( PrepareHint::AdjustSizeWithoutFormatting, nullptr, bNotify );
InvalidateSize_();
} else return bParaPossiblyInvalid; // So that there's no SetPrep()
if (bParaPossiblyInvalid)
{ // It's possible that pPara was deleted above; retrieve it again
pPara = aAccess.GetPara();
}
} break;
} case PrepareHint::Register: if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
{
pPara->SetPrepAdjust();
CalcLineSpace();
// It's possible that pPara was deleted above; retrieve it again
bParaPossiblyInvalid = true;
pPara = aAccess.GetPara();
InvalidateSize();
InvalidatePrt_();
SwFrame* pNxt = GetIndNext(); if ( nullptr != pNxt )
{
pNxt->InvalidatePrt_(); if ( pNxt->IsLayoutFrame() )
pNxt->InvalidatePage();
}
SetCompletePaint();
} break; case PrepareHint::FootnoteInvalidationGone :
{ // If a Follow is calling us, because a footnote is being deleted, our last // line has to be formatted, so that the first line of the Follow can flow up. // Which had flowed to the next page to be together with the footnote (this is // especially true for areas with columns)
OSL_ENSURE( GetFollow(), "PrepareHint::FootnoteInvalidationGone may only be called by Follow" );
TextFrameIndex nPos = GetFollow()->GetOffset(); if( IsFollow() && GetOffset() == nPos ) // If we don't have a mass of text, we call our
FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone ); // Master's Prepare if( nPos )
--nPos; // The char preceding our Follow
InvalidateRange(SwCharRange(nPos, TextFrameIndex(1))); return bParaPossiblyInvalid;
} case PrepareHint::ErgoSum: case PrepareHint::QuoVadis:
{
TextFrameIndex nPos; if( lcl_ErgoVadis( this, nPos, ePrep ) )
InvalidateRange(SwCharRange(nPos, TextFrameIndex(1)));
} break; case PrepareHint::FlyFrameAttributesChanged:
{ if( pVoid )
{
TextFrameIndex const nWhere = CalcFlyPos( static_cast<SwFrameFormat const *>(pVoid) );
OSL_ENSURE( TextFrameIndex(COMPLETE_STRING) != nWhere, "Prepare: Why me?" );
InvalidateRange(SwCharRange(nWhere, TextFrameIndex(1))); return bParaPossiblyInvalid;
}
[[fallthrough]]; // else: continue with default case block
} case PrepareHint::Clear: default:
{ if( IsLocked() )
{ if( PrepareHint::FlyFrameArrive == ePrep || PrepareHint::FlyFrameLeave == ePrep )
{
TextFrameIndex const nLen = (GetFollow()
? GetFollow()->GetOffset()
: TextFrameIndex(COMPLETE_STRING))
- GetOffset();
InvalidateRange( SwCharRange( GetOffset(), nLen ) );
}
} else
{ if( pPara->GetRepaint().HasArea() )
SetCompletePaint();
Init();
pPara = nullptr; if( GetOffset() && !IsFollow() )
SetOffset_( TextFrameIndex(0) ); if ( bNotify )
InvalidateSize(); else
InvalidateSize_();
} return bParaPossiblyInvalid; // no SetPrep() happened
}
} if( pPara )
{
pPara->SetPrep();
}
/** * Small Helper class: * Prepares a test format. * The frame is changed in size and position, its SwParaPortion is moved aside * and a new one is created. * To achieve this, run formatting with bTestFormat flag set. * In the destructor the TextFrame is reset to its original state.
*/ class SwTestFormat
{
SwTextFrame *pFrame;
SwParaPortion *pOldPara;
SwRect aOldFrame, aOldPrt; public:
SwTestFormat( SwTextFrame* pTextFrame, const SwFrame* pPrv, SwTwips nMaxHeight );
~SwTestFormat();
};
/** * We should not and don't need to reformat. * We assume that we already formatted and that the formatting * data is still current. * * We also assume that the frame width of the Master and Follow * are the same. That's why we're not calling FindBreak() for * FindOrphans(). * The required height is coming from nMaxHeight. * * @returns true if I can split
*/ bool SwTextFrame::WouldFit(SwTwips &rMaxHeight, bool &bSplit, bool bTst, bool bMoveBwd)
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::WouldFit with swapped frame" );
SwRectFnSet aRectFnSet(this);
if( IsLocked() ) returnfalse;
// it can happen that the IdleCollector removed the cached information if( !IsEmpty() )
GetFormatted();
// i#27801 - correction: 'short cut' for empty paragraph // can *not* be applied, if test format is in progress. The test format doesn't // adjust the frame and the printing area - see method <SwTextFrame::Format_(..)>, // which is called in <SwTextFrame::TestFormat(..)> if ( IsEmpty() && !bTst )
{
bSplit = false;
SwTwips nHeight = aRectFnSet.IsVert() ? getFramePrintArea().SSize().Width() : getFramePrintArea().SSize().Height(); if( rMaxHeight < nHeight ) returnfalse; else
{
rMaxHeight -= nHeight; returntrue;
}
}
// GetPara can still be 0 in edge cases // We return true in order to be reformatted on the new Page
OSL_ENSURE( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" ); if( !HasPara() || ( !aRectFnSet.GetHeight(getFrameArea()) && IsHiddenNow() ) ) returntrue;
// Because the Orphan flag only exists for a short moment, we also check // whether the Framesize is set to very huge by CalcPreps, in order to // force a MoveFwd if (IsWidow() || (aRectFnSet.IsVert()
? (0 == getFrameArea().Left())
: (sw::WIDOW_MAGIC - 20000 < getFrameArea().Bottom())))
{
SetWidow(false); if ( GetFollow() )
{ // If we've ended up here due to a Widow request by our Follow, we check // whether there's a Follow with a real height at all. // Else (e.g. for newly created SctFrames) we ignore the IsWidow() and // still check if we can find enough room if (((!aRectFnSet.IsVert() && getFrameArea().Bottom() <= sw::WIDOW_MAGIC - 20000) ||
( aRectFnSet.IsVert() && 0 < getFrameArea().Left() ) ) &&
( GetFollow()->IsVertical() ?
!GetFollow()->getFrameArea().Width() :
!GetFollow()->getFrameArea().Height() ) )
{
SwTextFrame* pFoll = GetFollow()->GetFollow(); while( pFoll &&
( pFoll->IsVertical() ?
!pFoll->getFrameArea().Width() :
!pFoll->getFrameArea().Height() ) )
pFoll = pFoll->GetFollow(); if( pFoll ) returnfalse;
} else returnfalse;
}
}
SwSwapIfNotSwapped swap( this );
SwTextSizeInfo aInf( this );
SwTextMargin aLine( this, &aInf );
aLine.Bottom(); // is breaking necessary?
bSplit = !aFrameBreak.IsInside( aLine ); if ( bSplit )
bRet = !aFrameBreak.IsKeepAlways() && aFrameBreak.WouldFit(aLine, rMaxHeight, bTst, bMoveBwd); else
{ // we need the total height including the current line
aLine.Top(); do
{
rMaxHeight -= aLine.GetLineHeight();
} while ( aLine.Next() );
}
if( !HasPara() )
{ // For non-empty paragraphs this is a special case // For UnderSized we can simply just ask 1 Twip more
SwTwips nRet = getFramePrintArea().SSize().Height(); if( IsUndersized() )
{ if( IsEmpty() || GetText().isEmpty() )
nRet = EmptyHeight(); else
++nRet;
} return nRet;
}
/** * @returns this _always_ in the formatted state!
*/
SwTextFrame* SwTextFrame::GetFormatted( bool bForceQuickFormat )
{
vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
SwSwapIfSwapped swap( this );
// In case the SwLineLayout was cleared out of the s_pTextCache, recreate it // Not for empty paragraphs if( !HasPara() && !(isFrameAreaDefinitionValid() && IsEmpty()) )
{ // Calc() must be called, because frame position can be wrong constbool bFormat = isFrameAreaSizeValid();
Calc(pRenderContext); // calls Format() if invalid
// If the flags were valid (hence bFormat=true), Calc did nothing, // so Format() must be called manually in order to recreate // the SwLineLayout that has been deleted from the // SwTextFrame::s_pTextCache (hence !HasPara() above). // Optimization with FormatQuick() if( bFormat && !FormatQuick( bForceQuickFormat ) )
Format(getRootFrame()->GetCurrShell()->GetOut());
}
returnthis;
}
SwTwips SwTextFrame::CalcFitToContent()
{ // i#31490 // If we are currently locked, we better return with a // fairly reasonable value: if ( IsLocked() ) return getFramePrintArea().Width();
// i#25422 objects anchored as character in RTL if ( IsRightToLeft() )
{
SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
aFrm.Pos().AdjustX(nOldFrameWidth - nPageWidth );
}
/** * Simulate format for a list item paragraph, whose list level attributes * are in LABEL_ALIGNMENT mode, in order to determine additional first * line offset for the real text formatting due to the value of label * adjustment attribute of the list level.
*/ void SwTextFrame::CalcAdditionalFirstLineOffset()
{ if ( IsLocked() ) return;
// reset additional first line offset
mnAdditionalFirstLineOffset = 0;
const SwTextNode* pTextNode( GetTextNodeForParaProps() ); // sw_redlinehide: check that pParaPropsNode is the correct one
assert(pTextNode->IsNumbered(getRootFrame()) == pTextNode->IsNumbered(nullptr)); if (!(pTextNode->IsNumbered(getRootFrame()) &&
pTextNode->IsCountedInList() && pTextNode->GetNumRule())) return;
/** * Determine the height of the last line for the calculation of * the proportional line spacing * * Height of last line will be stored in new member * mnHeightOfLastLine and can be accessed via method * GetHeightOfLastLine() * * @param _bUseFont force the usage of the former algorithm to * determine the height of the last line, which * uses the font
*/ void SwTextFrame::CalcHeightOfLastLine( constbool _bUseFont )
{ // i#71281 // Invalidate printing area, if height of last line changes const SwTwips nOldHeightOfLastLine( mnHeightOfLastLine );
// i#78921 // There could be no <SwViewShell> instance in the case of loading a binary // StarOffice file format containing an embedded Writer document. if ( !pVsh )
{ return;
}
OutputDevice* pOut = pVsh->GetOut(); const IDocumentSettingAccess *const pIDSA = &GetDoc().getIDocumentSettingAccess(); if ( !pVsh->GetViewOptions()->getBrowseMode() ||
pVsh->GetViewOptions()->IsPrtFormat() )
{
pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
}
OSL_ENSURE( pOut, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" );
if ( !pOut )
{ return;
}
// determine height of last line if ( _bUseFont || pIDSA->get(DocumentSettingId::OLD_LINE_SPACING ) )
{ // former determination of last line height for proportional line // spacing - take height of font set at the paragraph // FIXME actually... must the font match across all nodes?
SwFont aFont( &GetTextNodeForParaProps()->GetSwAttrSet(), pIDSA );
// we must ensure that the font is restored correctly on the OutputDevice // otherwise Last!=Owner could occur if ( pLastFont )
{
SwFntObj *pOldFont = pLastFont;
pLastFont = nullptr;
aFont.SetFntChg( true );
aFont.ChgPhysFnt( pVsh, *pOut );
mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
assert(pLastFont && "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
pLastFont->Unlock();
pLastFont = pOldFont;
pLastFont->SetDevFont( pVsh, *pOut );
} else
{
vcl::Font aOldFont = pOut->GetFont();
aFont.SetFntChg( true );
aFont.ChgPhysFnt( pVsh, *pOut );
mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
assert(pLastFont && "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
pLastFont->Unlock();
pLastFont = nullptr;
pOut->SetFont( aOldFont );
}
} else
{ // new determination of last line height - take actually height of last line // i#89000 // assure same results, if paragraph is undersized if ( IsUndersized() )
{
mnHeightOfLastLine = 0;
} else
{ bool bCalcHeightOfLastLine = true; if ( ( !HasPara() && IsEmpty( ) ) || GetText().isEmpty() )
{
mnHeightOfLastLine = EmptyHeight();
bCalcHeightOfLastLine = false;
}
if ( bCalcHeightOfLastLine )
{
OSL_ENSURE( HasPara(), "<SwTextFrame::CalcHeightOfLastLine()> - missing paragraph portions." ); const SwLineLayout* pLineLayout = GetPara(); while ( pLineLayout && pLineLayout->GetNext() )
{ // iteration to last line
pLineLayout = pLineLayout->GetNext();
} if ( pLineLayout )
{
SwTwips nAscent, nDescent, nDummy1, nDummy2; // i#47162 - suppress consideration of // fly content portions and the line portion.
pLineLayout->MaxAscentDescent( nAscent, nDescent,
nDummy1, nDummy2,
nullptr, true ); // i#71281 // Suppress wrong invalidation of printing area, if method is // called recursive. // Thus, member <mnHeightOfLastLine> is only set directly, if // no recursive call is needed. const SwTwips nNewHeightOfLastLine = nAscent + nDescent; // i#47162 - if last line only contains // fly content portions, <mnHeightOfLastLine> is zero. // In this case determine height of last line by the font if ( nNewHeightOfLastLine == 0 )
{
CalcHeightOfLastLine( true );
} else
{
mnHeightOfLastLine = nNewHeightOfLastLine;
}
}
}
}
} // i#71281 // invalidate printing area, if height of last line changes if ( mnHeightOfLastLine != nOldHeightOfLastLine )
{
InvalidatePrt();
}
}
/** * Method returns the value of the inter line spacing for a text frame. * Such a value exists for proportional line spacings ("1,5 Lines", * "Double", "Proportional" and for leading line spacing ("Leading"). * * @param _bNoPropLineSpacing (default = false) control whether the * value of a proportional line spacing is * returned or not
*/
tools::Long SwTextFrame::GetLineSpace( constbool _bNoPropLineSpace ) const
{
tools::Long nRet = 0;
// tdf#146500 Lines with only fly overlap cannot be "moved", so the idea // here is to continue until there's some text. // FIXME ideally we want to count a fly to the line in which it is anchored // - it may even be anchored in some other paragraph! SwFlyPortion doesn't // have a pointer sadly so no way to find out.
SwTwips nHeight(0); for (SwLineLayout const* pLine = pPara; pLine; pLine = pLine->GetNext())
{
nHeight += pLine->Height(); if (::sw::FindNonFlyPortion(*pLine))
{ break;
}
} return nHeight;
}
void SwTextFrame::ChgThisLines()
{ // not necessary to format here (GetFormatted etc.), because we have to come from there!
sal_Int32 nNew = 0; const SwLineNumberInfo &rInf = GetDoc().GetLineNumberInfo(); if ( !GetText().isEmpty() && HasPara() )
{
SwTextSizeInfo aInf( this );
SwTextMargin aLine( this, &aInf ); if ( rInf.IsCountBlankLines() )
{
aLine.Bottom();
nNew = aLine.GetLineNr();
} else
{ do
{ if( aLine.GetCurr()->HasContent() )
++nNew;
} while ( aLine.NextLine() );
}
} elseif ( rInf.IsCountBlankLines() )
nNew = 1;
// Extend repaint to the bottom. if ( HasPara() )
{
SwRepaint& rRepaint = GetPara()->GetRepaint();
rRepaint.Bottom( std::max( rRepaint.Bottom(),
getFrameArea().Top()+getFramePrintArea().Bottom()));
}
} else// Paragraphs which are not counted should not manipulate the AllLines.
mnThisLines = nNew;
}
if ( !IsFollow() && rLineNum.GetStartValue() && rLineNum.IsCount() )
nNewNum = rLineNum.GetStartValue() - 1; // If it is a follow or not has not be considered if it is a restart at each page; the // restart should also take effect at follows. elseif ( bRestart && FindPageFrame()->FindFirstBodyContent() == this )
{
nNewNum = 0;
} else
{
SwContentFrame *pPrv = GetPrevContentFrame(); while ( pPrv &&
(pPrv->IsInTab() || pPrv->IsInDocBody() != IsInDocBody()) )
pPrv = pPrv->GetPrevContentFrame();
// i#78254 Restart line numbering at page change // First body content may be in table! if ( bRestart && pPrv && pPrv->FindPageFrame() != FindPageFrame() )
pPrv = nullptr;
// Get first 'real' line and adjust position and height of line rectangle. // Correct behaviour if no 'real' line exists // (empty paragraph with and without a dummy portion)
SwTwips nFlyAnchorVertOfstNoWrap = 0;
{
SwTwips nTop = aRectFnSet.GetTop(aFlyRect); const SwLineLayout* pLay = GetPara();
SwTwips nLineHeight = 200; while( pLay && pLay->IsDummy() && pLay->GetNext() )
{
nTop += pLay->Height();
nFlyAnchorVertOfstNoWrap += pLay->Height();
pLay = pLay->GetNext();
} if ( pLay )
{
nLineHeight = pLay->Height();
}
aRectFnSet.SetTopAndHeight( aFlyRect, nTop, nLineHeight );
}
SwTextFly aTextFly( this );
aTextFly.SetIgnoreCurrentFrame( true );
aTextFly.SetIgnoreContour( true ); // ignore objects in page header|footer for // text frames not in page header|footer
aTextFly.SetIgnoreObjsInHeaderFooter( true );
SwTwips nRet1 = lcl_CalcFlyBasePos( *this, aFlyRect, aTextFly );
aTextFly.SetIgnoreCurrentFrame( false );
SwTwips nRet2 = lcl_CalcFlyBasePos( *this, aFlyRect, aTextFly );
// make values relative to frame start position
SwTwips nLeft = IsRightToLeft() ?
aRectFnSet.GetRight(getFrameArea()) :
aRectFnSet.GetLeft(getFrameArea());
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.79Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.