/* -*- 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 *pNode = new SwTextNode( rWhere, pColl, nullptr );
SwNodeIndex aIdx( *pNode );
// if there is no layout or it is in a hidden section, MakeFrames is not needed const SwSectionNode* pSectNd; if (!bNewFrames ||
!GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
( nullptr != (pSectNd = pNode->FindSectionNode()) &&
pSectNd->GetSection().IsHiddenFlag() )) return pNode;
SwNodeIndex aTmp( rWhere ); do { // max. 2 loops: // 1. take the successor // 2. take the predecessor
case SwNodeType::Text: case SwNodeType::Grf: case SwNodeType::Ole: static_cast<SwContentNode*>(pNd)->MakeFramesForAdjacentContentNode(*pNode); return pNode;
if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
{ // #i101516# // apply paragraph style's assigned outline style list level as // list level of the paragraph, if it has none set already. if ( !HasAttrListLevel() &&
pTextColl && pTextColl->IsAssignedToListLevelOfOutlineStyle() )
{
SetAttrListLevel( pTextColl->GetAssignedOutlineStyleLevel() );
}
AddToList();
}
// call method <UpdateOutlineNode(..)> only for the document nodes array if (GetNodes().IsDocNodes())
GetNodes().UpdateOutlineNode(*this);
}
SwTextNode::~SwTextNode()
{ // delete only removes the pointer not the array elements! if ( m_pSwpHints )
{ // do not delete attributes twice when those delete their content
std::unique_ptr<SwpHints> pTmpHints(std::move(m_pSwpHints));
for( size_t j = pTmpHints->Count(); j; )
{ // first remove the attribute from the array otherwise // if would delete itself
DestroyAttr( pTmpHints->Get( --j ) );
}
}
// must be removed from outline nodes by now #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
SwOutlineNodes::size_type foo;
assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo)); #endif
RemoveFromList();
DelFrames(nullptr); // must be called here while it's still a SwTextNode
DelFrames_TextNodePart();
// If this Node should have Outline Numbering but that state hasn't been // crystallized by SwNodes::UpdateOutlineNode yet, and so it currently isn't // added to SwNodes::m_aOutlineNodes, then set LastOutlineState so it won't // be added if ResetAttr() triggers UpdateOutlineNode() during destruction, // and avoid leaving a dangling pointer in m_aOutlineNodes. if (IsOutline() && !m_bLastOutlineState)
m_bLastOutlineState = true;
#ifdefined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if (!GetDoc().IsInDtor()) #endif
{
ResetAttr(RES_PAGEDESC);
}
InvalidateInSwCache();
}
void SwTextNode::FileLoadedInitHints()
{ if (m_pSwpHints)
{
m_pSwpHints->MergePortions(*this);
}
}
// After a split node, it's necessary to actualize the ref-pointer of the ftnfrms. staticvoid lcl_ChangeFootnoteRef( SwTextNode &rNode )
{
SwpHints *pSwpHints = rNode.GetpSwpHints(); if( !(pSwpHints && rNode.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) ) return;
SwContentFrame* pFrame = nullptr; // OD 07.11.2002 #104840# - local variable to remember first footnote // of node <rNode> in order to invalidate position of its first content. // Thus, in its <MakeAll()> it will checked its position relative to its reference.
SwFootnoteFrame* pFirstFootnoteOfNode = nullptr; for( size_t j = pSwpHints->Count(); j; )
{
SwTextAttr* pHt = pSwpHints->Get(--j); if (RES_TXTATR_FTN == pHt->Which())
{ if( !pFrame )
{
pFrame = SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(rNode).First(); if (!pFrame) return;
}
SwTextFootnote *pAttr = static_cast<SwTextFootnote*>(pHt);
OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
SwContentNode *pNd = aIdx.GetNode().GetContentNode(); if ( !pNd )
pNd = SwNodes::GoNextSection(&aIdx, true, false); if ( !pNd ) continue;
// check if there are flys on the existing frames (now on "pNode") // that need to be moved to the new frames of "this" void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
SwTextNode const& rFirstNode, SwTextNode & rSecondNode, bool isSplitNode)
{ if (!isSplitNode)
{
lcl_ChangeFootnoteRef(rSecondNode);
} for (SwNodeOffset nIndex = rSecondNode.GetIndex() + 1; ; ++nIndex)
{
SwNode *const pTmp(rSecondNode.GetNodes()[nIndex]); if (pTmp->IsCreateFrameWhenHidingRedlines() || pTmp->IsEndNode())
{ break;
} elseif (pTmp->IsStartNode())
{
nIndex = pTmp->EndOfSectionIndex();
} elseif (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst
&& pTmp->IsTextNode())
{
lcl_ChangeFootnoteRef(*pTmp->GetTextNode());
}
} for (SwTextFrame *const pFrame : rFrames)
{ if (SwSortedObjs *const pObjs = pFrame->GetDrawObjs())
{
std::vector<SwAnchoredObject*> objs;
objs.reserve(pObjs->size()); for (SwAnchoredObject *const pObj : *pObjs)
{
objs.push_back(pObj);
} for (SwAnchoredObject *const pObj : objs)
{
SwFrameFormat* pFormat(pObj->GetFrameFormat());
SwFormatAnchor const& rAnchor(pFormat->GetAnchor()); if (rFirstNode.GetIndex() < rAnchor.GetAnchorNode()->GetIndex())
{ // move it to the new frame of "this"
pFormat->CallSwClientNotify(sw::LegacyModifyHint(&rAnchor, &rAnchor)); // note pObjs will be deleted if it becomes empty
assert(!pFrame->GetDrawObjs() || !pObjs->Contains(*pObj));
}
}
}
}
}
// create a node "in front" of me const sal_Int32 nSplitPos = rPos.GetContentIndex(); const sal_Int32 nTextLen = m_Text.getLength();
SwTextNode* const pNode =
MakeNewTextNode( rPos.GetNode(), false, nSplitPos==nTextLen );
// the first paragraph gets the XmlId, // _except_ if it is empty and the second is not empty if (nSplitPos != 0) {
pNode->RegisterAsCopyOf(*this, true); if (nSplitPos == nTextLen)
{
RemoveMetadataReference(); // NB: SwUndoSplitNode will call pNode->JoinNext, // which is sufficient even in this case!
}
}
bool bSplitFly = false;
std::optional<std::vector<SwFrameFormat*>> oFlys = sw::GetFlysAnchoredAt(GetDoc(), GetIndex(), false); if (oFlys.has_value())
{ // See if one of the flys is a split fly. If so, we need to keep // the potentially split text frames unchanged and create a new // text frame at the end. for (constauto& rFly : *oFlys)
{ if (rFly->GetFlySplit().GetValue())
{
bSplitFly = true; break;
}
}
}
if ( HasWriterListeners() && !m_Text.isEmpty() && ((nTextLen / 2) < nSplitPos || bSplitFly) )
{ // optimization for SplitNode: If a split is at the end of a node then // move the frames from the current to the new one and create new ones // for the current one.
// If fly frames are moved, they don't need to destroy their layout // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor. if ( HasHints() )
{
pNode->GetOrCreateSwpHints().SetInSplitNode(true);
}
// Move the first part of the content to the new node and delete // it in the old node.
SwContentIndex aIdx( this );
CutText( pNode, aIdx, nSplitPos );
if ( pNode->HasHints() )
{ if ( pNode->m_pSwpHints->CanBeDeleted() )
{
pNode->m_pSwpHints.reset();
} else
{
pNode->m_pSwpHints->SetInSplitNode(false);
}
// All fly frames anchored as char that are moved to the new // node must have their layout frames deleted. // This comment is sort of silly because we actually delete the // layout frames of those which were not moved? // JP 01.10.96: delete all empty and not-to-be-expanded attributes if ( HasHints() )
{ for ( size_t j = m_pSwpHints->Count(); j; )
{
SwTextAttr* const pHt = m_pSwpHints->Get( --j ); if ( RES_TXTATR_FLYCNT == pHt ->Which() )
{
pHt->GetFlyCnt().GetFrameFormat()->DelFrames();
} elseif ( pHt->DontExpand() )
{ const sal_Int32* const pEnd = pHt->GetEnd(); if (pEnd && pHt->GetStart() == *pEnd )
{ // delete it!
m_pSwpHints->DeleteAtPos( j );
DestroyAttr( pHt );
}
}
}
}
}
if (pContentIndexRestore)
{ // call before making frames and before RegisterToNode
(*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
} if (eOldMergeFlag != SwNode::Merge::None)
{ // clear before making frames and before RegisterToNode
SetRedlineMergeFlag(SwNode::Merge::None);
} // now RegisterToNode will set merge flags in both nodes properly!
if ( HasHints() )
{
MoveTextAttr_To_AttrSet();
pNode->MoveTextAttr_To_AttrSet();
} // in case there are frames, the RegisterToNode has set the merge flag
pNode->MakeFramesForAdjacentContentNode(*this);
lcl_ChangeFootnoteRef( *this ); if (pContentIndexRestore)
{ // call after making frames; listeners will take care of adding to the right frame
(*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, false);
} if (eOldMergeFlag != SwNode::Merge::None)
{
MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
}
} else
{
std::unique_ptr<SwWrongList> pList = ReleaseWrong();
SetWrongDirty(sw::WrongState::TODO);
if (pContentIndexRestore)
{ // call before making frames and before RegisterToNode
(*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
}
std::vector<SwTextFrame*> frames;
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this); for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{
frames.push_back(pFrame); if (pFrame->getRootFrame()->HasMergedParas())
{
isHide = true;
}
} bool bNonMerged(false); bool bRecreateThis(false); for (SwTextFrame * pFrame : frames)
{ // sw_redlinehide: for this to work properly with hidden nodes, // the frame needs to listen on them too. // also: have to check the frame; this->GetRedlineMergeFlag() // is None in case there's a delete redline inside the paragraph, // but that could still result in a merged frame after split... if (pFrame->GetMergedPara())
{ // Can't special case this == First here - that could (if // both nodes are still merged by redline) lead to // duplicate frames on "this". // Update the extents with new node; also inits merge flag, // so the MakeFramesForAdjacentContentNode below respects it
pFrame->RegisterToNode(*pNode); if (nSplitPos == 0)
{ // in this case, it was not // invalidated because Cut didn't sent it any hints, // so we have to invalidate it here!
pFrame->Prepare(PrepareHint::Clear, nullptr, false);
} if (!pFrame->GetMergedPara() ||
!pFrame->GetMergedPara()->listener.IsListeningTo(this))
{ // it's no longer listening - need to recreate frame // (note this is idempotent, can be done once per frame)
SetRedlineMergeFlag(SwNode::Merge::None);
bRecreateThis = true;
}
} else
{
bNonMerged = true;
}
}
assert(!(bNonMerged && bRecreateThis)); // 2 layouts not handled yet - maybe best to simply use the other branch then? if (!frames.empty() && bNonMerged)
{ // the existing frame on "this" should have been updated by Cut
MakeFramesForAdjacentContentNode(*pNode);
lcl_ChangeFootnoteRef(*pNode);
} elseif (bRecreateThis)
{
assert(pNode->HasWriterListeners()); // was just moved there
pNode->MakeFramesForAdjacentContentNode(*this);
lcl_ChangeFootnoteRef(*this);
}
if (pContentIndexRestore)
{ // call after making frames; listeners will take care of adding to the right frame
(*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, nSplitPos == 0);
}
if (bRecreateThis)
{
MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
}
}
// pNode is the previous node, 'this' is the next node from the split. if (nSplitPos == nTextLen && m_pSwpHints)
{ // We just created an empty next node: avoid unwanted superscript in the new node if it's // there.
ResetAttr(RES_CHRATR_ESCAPEMENT);
}
#ifndef NDEBUG if (isHide) // otherwise flags won't be set anyway
{ // First // -> First,NonFirst // -> First,Hidden // -> None,First // Hidden // -> Hidden,Hidden (if still inside merge rl) // -> NonFirst,First (if redline was split) // NonFirst // -> NonFirst,First (if split after end of "incoming" redline & // before start of "outgoing" redline) // -> NonFirst,None (if split after end of "incoming" redline) // -> NonFirst,Hidden (if split after start of "outgoing" redline) // -> Hidden, NonFirst (if split before end of "incoming" redline) // None // -> None,None // -> First,NonFirst (if splitting inside a delete redline)
SwNode::Merge const eFirst(pNode->GetRedlineMergeFlag());
SwNode::Merge const eSecond(GetRedlineMergeFlag()); switch (eOldMergeFlag)
{ case Merge::First:
assert((eFirst == Merge::First && eSecond == Merge::NonFirst)
|| (eFirst == Merge::First && eSecond == Merge::Hidden)
|| (eFirst == Merge::None && eSecond == Merge::First)); break; case Merge::Hidden:
assert((eFirst == Merge::Hidden && eSecond == Merge::Hidden)
|| (eFirst == Merge::NonFirst && eSecond == Merge::First) // next ones can happen temp. in UndoDelete :(
|| (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)
|| (eFirst == Merge::NonFirst && eSecond == Merge::None)); break; case Merge::NonFirst:
assert((eFirst == Merge::NonFirst && eSecond == Merge::First)
|| (eFirst == Merge::NonFirst && eSecond == Merge::None)
|| (eFirst == Merge::NonFirst && eSecond == Merge::Hidden)
|| (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)); break; case Merge::None:
assert((eFirst == Merge::None && eSecond == Merge::None)
|| (eFirst == Merge::First && eSecond == Merge::NonFirst)); break;
}
} #else
(void) isHide; #endif
{ // Send Hint for PageDesc. This should be done in the Layout when // pasting the frames, but that causes other problems that look // expensive to solve. const SwFormatPageDesc *pItem; if(HasWriterListeners() && (pItem = pNode->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC)))
pNode->TriggerNodeUpdate(sw::LegacyModifyHint(pItem, pItem));
} return pNode;
}
void SwTextNode::MoveTextAttr_To_AttrSet()
{
OSL_ENSURE( m_pSwpHints, "MoveTextAttr_To_AttrSet without SwpHints?" ); for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
{
SwTextAttr *pHt = m_pSwpHints->Get(i);
if( pHt->GetStart() ) break;
const sal_Int32* pHtEndIdx = pHt->GetEnd();
if( !pHtEndIdx ) continue;
if (*pHtEndIdx < m_Text.getLength() || pHt->IsCharFormatAttr()) break;
/// if first node is deleted & second survives, then the first node's frame /// will be deleted too; prevent this by moving the frame to the second node /// if necessary. void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode)
{
std::vector<SwTextFrame*> frames;
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rDeletedPrev); for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{ if (pFrame->getRootFrame()->HasMergedParas())
{
frames.push_back(pFrame);
}
}
{ auto frames2(frames);
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIt(rNode); for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
{ if (pFrame->getRootFrame()->HasMergedParas())
{ autoconst it(std::find(frames2.begin(), frames2.end(), pFrame));
assert(it != frames2.end());
frames2.erase(it);
}
}
assert(frames2.empty());
} for (SwTextFrame *const pFrame : frames)
{
pFrame->RegisterToNode(rNode, true);
}
}
/// if first node is First, its frames may need to be moved, never deleted. /// if first node is NonFirst, second node's own frames (First/None) must be deleted void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerged)
{ if (eRecreateMerged != sw::Recreate::No)
{
SwTextNode * pMergeNode(&rNode); if (eRecreateMerged == sw::Recreate::Predecessor // tdf#135018 check that there is a predecessor node, i.e. rNode // isn't the first node after the body start node
&& rNode.GetNodes()[rNode.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
{ for (SwNodeOffset i = rNode.GetIndex() - 1; ; --i)
{
SwNode *const pNode(rNode.GetNodes()[i]);
assert(!pNode->IsStartNode()); if (pNode->IsEndNode())
{
i = pNode->StartOfSectionIndex();
} elseif (pNode->IsTextNode())
{
pMergeNode = pNode->GetTextNode(); // use predecessor to merge break;
}
}
}
std::vector<SwTextFrame*> frames;
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pMergeNode); for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{ if (pFrame->getRootFrame()->HasMergedParas())
{
frames.push_back(pFrame);
}
} auto eMode(sw::FrameMode::Existing); for (SwTextFrame * pFrame : frames)
{
SwTextNode & rFirstNode(pFrame->GetMergedPara()
? *pFrame->GetMergedPara()->pFirstNode
: *pMergeNode);
assert(rFirstNode.GetIndex() <= rNode.GetIndex());
pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
*pFrame, rFirstNode, eMode)); // there is no merged para in case the deleted node had one but // nothing was actually hidden if (pFrame->GetMergedPara())
{
assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex()); // tdf#135978 Join: recreate fly frames anchored to subsequent nodes if (eRecreateMerged == sw::Recreate::ThisNode)
{
AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr);
}
}
eMode = sw::FrameMode::New; // Existing is not idempotent!
}
} elseif (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
{
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode); for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{ if (autoconst pMergedPara = pFrame->GetMergedPara())
{ if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
{
assert(pMergedPara->pFirstNode == &rNode);
rNode.SetRedlineMergeFlag(SwNode::Merge::None);
} break; // checking once is enough
} elseif (pFrame->getRootFrame()->HasMergedParas())
{
rNode.SetRedlineMergeFlag(SwNode::Merge::None); break; // checking once is enough
}
}
}
}
if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
{ // Repaint all text frames that belong to this numbering to avoid outdated generated // numbers.
InvalidateNumRule();
}
CheckResetRedlineMergeFlag(*this, eRecreateMerged);
} else {
OSL_FAIL( "No TextNode." );
}
// create an AttrSet with ranges for Frame-/Para/Char-attributes void SwTextNode::NewAttrSet( SwAttrPool& rPool )
{
OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );
if ( bTextAttrChanged
&& pHint->Which() == RES_TXTATR_INPUTFIELD )
{
SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint); if ( pTextInputField )
aTextInputFields.push_back(pTextInputField);
}
}
//wait until all the attribute positions are correct //before updating the field contents for (SwTextInputField* pTextInputField : aTextInputFields)
{
pTextInputField->UpdateFieldContent();
}
if ( bTextAttrChanged
&& pHint->Which() == RES_TXTATR_INPUTFIELD )
{
SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint); if ( pTextInputField )
aTextInputFields.push_back(pTextInputField);
}
}
//wait until all the attribute positions are correct //before updating the field contents for (SwTextInputField* pTextInputField : aTextInputFields)
{
pTextInputField->UpdateFieldContent();
}
if (bMergePortionsNeeded)
{
m_pSwpHints->MergePortions(*this); // does Resort too
} elseif (bResort)
{
m_pSwpHints->Resort();
}
}
}
bool bSortMarks = false;
SwContentNodeTmp aTmpIdxReg; if (!(eMode & UpdateMode::Negative) && !(eMode & UpdateMode::Delete))
{
o3tl::sorted_vector<SwRangeRedline*> vMyRedlines; // walk the list of SwContentIndex attached to me and see if any of them are redlines const SwContentIndex* pContentNodeIndex = GetFirstIndex(); while (pContentNodeIndex)
{ if (pContentNodeIndex->GetOwner() && pContentNodeIndex->GetOwner()->GetOwnerType() == SwContentIndexOwnerType::Redline)
{ auto pRedl = static_cast<SwRangeRedline*>(pContentNodeIndex->GetOwner()); if (pRedl && (pRedl->HasMark() || this == &pRedl->GetPoint()->GetNode()))
vMyRedlines.insert(pRedl);
}
pContentNodeIndex = pContentNodeIndex->GetNext();
} for (SwRangeRedline* pRedl : vMyRedlines)
{ if ( pRedl->HasMark() )
{
SwPosition* const pEnd = pRedl->End(); if ( *this == pEnd->GetNode() &&
*pRedl->GetPoint() != *pRedl->GetMark() )
{
SwContentIndex & rIdx = pEnd->nContent; if (nChangePos == rIdx.GetIndex())
{
rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
}
}
} elseif ( this == &pRedl->GetPoint()->GetNode() )
{
SwContentIndex & rIdx = pRedl->GetPoint()->nContent; if (nChangePos == rIdx.GetIndex())
{
rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
} // the unused position must not be on a SwTextNode boolconst isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
assert(!pRedl->GetBound(!isOneUsed).GetNode().IsTextNode());
assert(!pRedl->GetBound(!isOneUsed).GetContentNode()); (void)isOneUsed;
}
}
// Bookmarks must never grow to either side, when editing (directly) // to the left or right (i#29942)! Exception: if the bookmark has // 2 positions and start == end, then expand it (tdf#96479) if (!(eMode & UpdateMode::Replace)) // Exception: Replace
{ bool bAtLeastOneBookmarkMoved = false; bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false; // A text node already knows its marks via its SwContentIndexes.
o3tl::sorted_vector<const sw::mark::MarkBase*> aSeenMarks; const SwContentIndex* next; for (const SwContentIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
{
next = pIndex->GetNext(); if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark) continue; autoconst pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner()); // filter out ones that cannot match to reduce the max size of aSeenMarks const SwPosition* pMarkPos1 = &pMark->GetMarkPos(); const SwPosition* pMarkPos2 = pMark->IsExpanded() ? &pMark->GetOtherMarkPos() : nullptr; if (pMarkPos1->nContent.GetIndex() != rPos.GetIndex()
&& (pMarkPos2 == nullptr || pMarkPos2->nContent.GetIndex() != rPos.GetIndex())) continue; // Only handle bookmarks once, if they start and end at this node as well. if (!aSeenMarks.insert(pMark).second) continue; const SwPosition* pEnd = &pMark->GetMarkEnd();
SwContentIndex & rEndIdx = const_cast<SwContentIndex&>(pEnd->nContent); if( *this == pEnd->GetNode() &&
rPos.GetIndex() == rEndIdx.GetIndex() )
{ if (&rEndIdx == next) // nasty corner case:
{ // don't switch to iterating aTmpIdxReg!
next = rEndIdx.GetNext();
} // tdf#96479: if start == end, ignore the other position // so it is moved!
rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
bAtLeastOneBookmarkMoved = true;
} elseif ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
{ if ( pMark->IsExpanded() )
{ const SwPosition* pStart = &pMark->GetMarkStart(); if ( this == &pStart->GetNode()
&& rPos.GetIndex() == pStart->GetContentIndex() )
{
bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
}
}
}
}
// at-char anchored flys shouldn't be moved, either. if (!m_bInUndo)
{
std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys()); for (size_t i = 0; i != rFlys.size(); ++i)
{
SwFrameFormat const*const pFormat = rFlys[i]; const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); const SwNode* pAnchorNode = rAnchor.GetAnchorNode(); if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pAnchorNode)
{ // The fly is at-char anchored and has an anchor position.
SwContentIndex& rEndIdx = const_cast<SwContentIndex&>(rAnchor.GetContentAnchor()->nContent); if (*pAnchorNode == *this && rEndIdx.GetIndex() == rPos.GetIndex())
{ // The anchor position is exactly our insert position.
rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
}
}
}
}
// The cursors of other shells shouldn't be moved, either. if (SwDocShell* pDocShell = GetDoc().GetDocShell())
{ if (pDocShell->GetWrtShell())
{ for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
{ auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell); if (!pWrtShell || pWrtShell == dynamic_cast<SwWrtShell*>(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell())) continue;
SwShellCursor* pCursor = pWrtShell->GetCursor_(); if (!pCursor) continue;
SwContentIndex& rIndex = pCursor->Start()->nContent; if (pCursor->Start()->GetNode() == *this && rIndex.GetIndex() == rPos.GetIndex())
{ // The cursor position of this other shell is exactly our insert position.
rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
}
}
}
}
}
// base class
SwContentIndexReg::Update(rPos, nChangeLen, eMode);
aTmpIdxReg.MoveTo( *this ); if ( bSortMarks )
{
getIDocumentMarkAccess()->assureSortedMarkContainers();
}
//Any drawing objects anchored into this text node may be sorted by their //anchor position which may have changed here, so resort them
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this); for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
{
SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs()); if (pSortedObjs)
{
pSortedObjs->UpdateAll();
} // also sort the objs on the page frame if (SwPageFrame *pPage = pFrame->FindPageFrame())
pSortedObjs = pPage->GetSortedObjs();
if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly
{
pSortedObjs->UpdateAll();
}
}
// Update the paragraph signatures. if (SwEditShell* pEditShell = GetDoc().GetEditShell())
{
pEditShell->ValidateParagraphSignatures(this, true);
}
// Inform LOK clients about change in position of redlines (if any) // Don't emit notifications during save: redline flags are temporarily changed during save, but // it's not useful to let clients know about such changes. if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting()) return;
const SwRedlineTable& rTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable(); for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
{
SwRangeRedline* pRedln = rTable[nRedlnPos]; if (pRedln->HasMark())
{ if (*this == pRedln->End()->GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
{ // Redline is changed only when some change occurs before it if (nChangePos <= pRedln->Start()->GetContentIndex())
{
SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
}
}
} elseif (this == &pRedln->GetPoint()->GetNode())
SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
}
}
void SwTextNode::ChgTextCollUpdateNum(const SwTextFormatColl* pOldColl, const SwTextFormatColl* pNewColl, bool bSetListLevel)
{
SwDoc& rDoc = GetDoc(); // query the OutlineLevel and if it changed, notify the Nodes-Array! constint nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle()
? pOldColl->GetAssignedOutlineStyleLevel()
: MAXLEVEL; constint nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
{ // check the condition of the text node again
ChkCondColl();
}
}
// If positioned exactly at the end of a CharStyle or Hyperlink, // set its DontExpand flag. bool SwTextNode::DontExpandFormat( sal_Int32 nIdx, bool bFlag, bool bFormatToTextAttributes )
{ if (bFormatToTextAttributes && nIdx == m_Text.getLength())
{
FormatToTextAttr( this );
}
staticvoid
lcl_GetTextAttrs(
std::vector<SwTextAttr *> *const pVector,
SwTextAttr **const ppTextAttr,
SwpHints const *const pSwpHints,
sal_Int32 const nIndex, sal_uInt16 const nWhich,
::sw::GetTextAttrMode const eMode)
{
assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END); if (!pSwpHints) return;
size_t const nSize = pSwpHints->Count();
sal_Int32 nPreviousIndex(0); // index of last hint with nWhich bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr; switch (eMode)
{ case ::sw::GetTextAttrMode::Default: pMatchFunc = &lcl_GetTextAttrDefault; break; case ::sw::GetTextAttrMode::Expand: pMatchFunc = &lcl_GetTextAttrExpand; break; case ::sw::GetTextAttrMode::Parent: pMatchFunc = &lcl_GetTextAttrParent; break; default: assert(false);
}
for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
{
SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i); if (pHint->Which() != nWhich) break; // hints are sorted by which&start, so we are done...
sal_Int32 const nHintStart = pHint->GetStart(); if (nIndex < nHintStart) break; // hints are sorted by which&start, so we are done...
sal_Int32 const*const pEndIdx = pHint->GetEnd(); // cannot have hint with no end and no dummy char
assert(pEndIdx || pHint->HasDummyChar()); // If EXPAND is set, simulate the text input behavior, i.e.
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet)
¤
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.