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. // move the start, and expand the end. boolconst bContained( pEndIdx
? (*pMatchFunc)(nIndex, nHintStart, *pEndIdx)
: (nHintStart == nIndex) ); if (bContained)
{ if (pVector)
{ if (nPreviousIndex < nHintStart)
{
pVector->clear(); // clear hints that are outside pHint
nPreviousIndex = nHintStart;
}
pVector->push_back(pHint);
} else
{
*ppTextAttr = pHint; // and possibly overwrite outer hint
} if (!pEndIdx)
{ break;
}
}
}
}
case RES_TXTATR_INPUTFIELD : case RES_TXTATR_ANNOTATION : if( pOtherDoc != nullptr )
{
static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
static_txtattr_cast<SwTextField*>(pNewHt));
} break;
case RES_TXTATR_TOXMARK : if( pOtherDoc && pDest && pDest->GetpSwpHints()
&& pDest->GetpSwpHints()->Contains( pNewHt ) )
{ // ToXMarks that are copied to different SwDocs must register // at their new ToX (sw::BroadcastingModify).
static_txtattr_cast<SwTextTOXMark*>(pNewHt)->CopyTOXMark(*pOtherDoc);
} break;
case RES_TXTATR_CHARFMT : // For CharacterStyles, the format must be copied too. if( pDest && pDest->GetpSwpHints()
&& pDest->GetpSwpHints()->Contains( pNewHt ) )
{
SwCharFormat* pFormat = pHt->GetCharFormat().GetCharFormat();
if (pOtherDoc)
{
pFormat = pOtherDoc->CopyCharFormat( *pFormat );
} const_cast<SwFormatCharFormat&>(
pNewHt->GetCharFormat() ).SetCharFormat( pFormat );
} break; case RES_TXTATR_INETFMT :
{ // For Hyperlinks, the format must be copied too. if( pOtherDoc && pDest && pDest->GetpSwpHints()
&& pDest->GetpSwpHints()->Contains( pNewHt ) )
{ const SwDoc& rDoc = static_txtattr_cast< const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc(); const SwCharFormats* pCharFormats = rDoc.GetCharFormats(); const SwFormatINetFormat& rFormat = pHt->GetINetFormat();
SwCharFormat* pFormat;
pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetINetFormat() ); if( pFormat )
pOtherDoc->CopyCharFormat( *pFormat );
pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetVisitedFormat() ); if( pFormat )
pOtherDoc->CopyCharFormat( *pFormat );
} //JP 24.04.98: The attribute must point to a text node, so that // the styles can be created.
SwTextINetFormat *const pINetHt = static_txtattr_cast<SwTextINetFormat*>(pNewHt); if ( !pINetHt->GetpTextNode() )
{
pINetHt->ChgTextNode( pDest );
}
//JP 22.10.97: set up link to char style
pINetHt->GetCharFormat(); break;
} case RES_TXTATR_META: case RES_TXTATR_METAFIELD:
OSL_ENSURE( pNewHt, "copying Meta should not fail!" );
OSL_ENSURE( pDest && pNewHt
&& (CH_TXTATR_INWORD == pDest->GetText()[pNewHt->GetStart()]), "missing CH_TXTATR?"); break;
}
}
/// copy attributes at position nTextStartIdx to node pDest // BP 7.6.93: Intentionally copy only attributes _with_ EndIdx! // CopyAttr is usually called when attributes are set on a // node with no text. void SwTextNode::CopyAttr( SwTextNode *pDest, const sal_Int32 nTextStartIdx, const sal_Int32 nOldPos )
{ if ( HasHints() )
{
SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ?
&pDest->GetDoc() : nullptr;
for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
{
SwTextAttr *const pHt = m_pSwpHints->Get(i);
sal_Int32 const nAttrStartIdx = pHt->GetStart(); if ( nTextStartIdx < nAttrStartIdx ) break; // beyond end of text, because nLen == 0
// Fetch end only now, because copying into self updates the start index // and all attributes
nTextStartIdx = rStart.GetIndex(); const sal_Int32 nEnd = nTextStartIdx + nLen;
// 2. copy attributes // Iterate over attribute array until the start of the attribute // is behind the copied range const size_t nSize = m_pSwpHints ? m_pSwpHints->Count() : 0;
// If copying into self, inserting can delete attributes! // Hence first copy into temp-array, and then move that into hints array.
SwpHts aArr;
// Del-Array for all RefMarks without extent
SwpHts aRefMrkArr;
// JP 26.04.94: RefMarks are never copied. If the refmark doesn't have // an extent, there is a dummy char in the text, which // must be removed. So we first copy the attribute, // but remember it, and when we're done delete it, // which also deletes the dummy character! // JP 14.08.95: May RefMarks be moved? constbool bCopyRefMark = RES_TXTATR_REFMARK == nWhich
&& ( bUndoNodes
|| ( !pOtherDoc
? GetDoc().IsCopyIsMove()
: nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) ) );
// Input Fields are only copied, if completely covered by copied text if ( nWhich == RES_TXTATR_INPUTFIELD )
{
assert(pEndIdx != nullptr && "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" ); if ( nAttrStartIdx < nTextStartIdx
|| ( pEndIdx != nullptr
&& *pEndIdx > nEnd ) )
{ continue;
}
}
if (nWhich == RES_TXTATR_METAFIELD)
{ // Skip metadata fields. Also remember the range to strip the text later.
metaFieldRanges.emplace_back(nAttrStartIdx, pEndIdx ? *pEndIdx : nEnd); continue;
}
sal_Int32 nAttrStt = 0;
sal_Int32 nAttrEnd = 0;
if( nAttrStartIdx < nTextStartIdx )
{ // start is before selection // copy hints with end and CH_TXTATR only if dummy char is copied if ( pEndIdx && (*pEndIdx > nTextStartIdx) && !pHt->HasDummyChar() )
{ // attribute with extent and the end is in the selection
nAttrStt = nDestStart;
nAttrEnd = (*pEndIdx > nEnd)
? rDestStart.GetIndex()
: nDestStart + (*pEndIdx) - nTextStartIdx;
} else
{ continue;
}
} else
{ // start is in the selection
nAttrStt = nDestStart + ( nAttrStartIdx - nTextStartIdx ); if( pEndIdx )
{
nAttrEnd = *pEndIdx > nEnd
? rDestStart.GetIndex()
: nDestStart + ( *pEndIdx - nTextStartIdx );
} else
{
nAttrEnd = nAttrStt;
}
}
SwTextAttr * pNewHt = nullptr;
if( pDest == this )
{ // copy the hint here, but insert it later
pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
nAttrStt, nAttrEnd, CopyOrNewType::Copy, pDest );
lcl_CopyHint(nWhich, pHt, pNewHt, nullptr, pDest);
aArr.push_back( pNewHt );
} else
{
pNewHt = pDest->InsertItem(
pHt->GetAttr(),
nAttrStt - nDeletedDummyChars,
nAttrEnd - nDeletedDummyChars,
SetAttrMode::NOTXTATRCHR | SetAttrMode::IS_COPY); if (pNewHt)
{
lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest ); if (nWhich == RES_TXTATR_ANNOTATION)
{ const SwPostItField* annotationField = static_cast<const SwPostItField*>(pHt->GetFormatField().GetField()); // Preparation for EstablishParentChildRelationsOfComments.
idMapForComments[annotationField->GetPostItId()] = static_cast<const SwPostItField*>(pNewHt->GetFormatField().GetField())->GetPostItId();
nameMapForComments[annotationField->GetPostItId()] = static_cast<const SwPostItField*>(pNewHt->GetFormatField().GetField())->GetName();
}
} elseif (pHt->HasDummyChar())
{ // The attribute that has failed to be copied would insert // dummy char, so positions of the following attributes have // to be shifted by one to compensate for that missing char.
++nDeletedDummyChars;
}
}
// Strip the metadata fields, since we don't copy the RDF entries // yet and so they are inconsistent upon copy/pasting. if (!metaFieldRanges.empty())
{ // Reverse to remove without messing the offsets.
std::reverse(metaFieldRanges.begin(), metaFieldRanges.end()); for (constauto& pair : metaFieldRanges)
{ const SwContentIndex aIdx(pDest, pair.first);
pDest->EraseText(aIdx, pair.second - pair.first);
}
}
Update(rIdx, nLen, UpdateMode::Default); // text content changed!
if (nMode & SwInsertFlags::FORCEHINTEXPAND)
{
SetIgnoreDontExpand( bOldExpFlg );
}
if ( HasWriterListeners() )
{ // send this before messing with hints, which will send RES_UPDATE_ATTR auto aInsHint = sw::MakeInsertText(*this, aPos, nLen);
CallSwClientNotify(aInsHint);
}
if ( HasHints() )
{
m_pSwpHints->SortIfNeedBe(); boolconst bHadHints(!m_pSwpHints->CanBeDeleted()); bool bMergePortionsNeeded(false); for ( size_t i = 0; i < m_pSwpHints->Count() &&
rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
{
SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i ); const sal_Int32 * const pEndIdx = pHt->GetEnd(); if( !pEndIdx ) continue;
if( rIdx == *pEndIdx )
{ if ( (nMode & SwInsertFlags::NOHINTEXPAND) ||
(!(nMode & SwInsertFlags::FORCEHINTEXPAND)
&& pHt->DontExpand()) )
{
m_pSwpHints->DeleteAtPos(i); // on empty attributes also adjust Start if( rIdx == pHt->GetStart() )
pHt->SetStart( pHt->GetStart() - nLen );
pHt->SetEnd(*pEndIdx - nLen); // could be that pHt has IsFormatIgnoreEnd set, and it's // not a RSID-only hint - now we have the inserted text // between pHt and its continuation... which we don't know. // punt the job to MergePortions below. if (pHt->IsFormatIgnoreEnd())
{
bMergePortionsNeeded = true;
}
InsertHint( pHt, SetAttrMode::NOHINTADJUST );
} // empty hints at insert position? elseif ( (nMode & SwInsertFlags::EMPTYEXPAND)
&& (*pEndIdx == pHt->GetStart()) )
{
m_pSwpHints->DeleteAtPos(i);
pHt->SetStart( pHt->GetStart() - nLen ); const size_t nCurrentLen = m_pSwpHints->Count();
InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ ); if ( nCurrentLen > m_pSwpHints->Count() && i )
{
--i;
} continue;
} else
{ continue;
}
} if ( !(nMode & SwInsertFlags::NOHINTEXPAND) &&
rIdx == nLen && pHt->GetStart() == rIdx.GetIndex() &&
!pHt->IsDontExpandStartAttr() )
{ // no field, at paragraph start, HintExpand
m_pSwpHints->DeleteAtPos(i);
pHt->SetStart( pHt->GetStart() - nLen ); // no effect on format ignore flags here (para start)
InsertHint( pHt, SetAttrMode::NOHINTADJUST );
}
} if (bMergePortionsNeeded)
{
m_pSwpHints->MergePortions(*this);
}
SAL_WARN_IF(bHadHints && m_pSwpHints->CanBeDeleted(), "sw.core", "SwTextNode::InsertText: unexpected loss of hints");
}
// By inserting a character, the hidden flags // at the TextNode can become invalid:
SetCalcHiddenCharFlags();
// copy hard attributes on whole paragraph if (HasSwAttrSet())
{ bool hasSwAttrSet = pDest->HasSwAttrSet(); if (hasSwAttrSet)
{ // if we have our own property set it doesn't mean // that this set defines any style different to Standard one.
hasSwAttrSet = false;
// so, let's check deeper if property set has defined any property if (pDest->GetpSwAttrSet())
{ // check all items in the property set
SfxItemIter aIter( *pDest->GetpSwAttrSet() ); const SfxPoolItem* pItem = aIter.GetCurItem(); do
{ // check current item const sal_uInt16 nWhich = IsInvalidItem( pItem )
? aIter.GetCurWhich()
: pItem->Which(); if( RES_FRMATR_STYLE_NAME != nWhich &&
RES_FRMATR_CONDITIONAL_STYLE_NAME != nWhich &&
RES_PAGEDESC != nWhich &&
RES_BREAK != nWhich &&
SfxItemState::SET == pDest->GetpSwAttrSet()->GetItemState( nWhich, false ) )
{ // check if parent value (original value in style) has the same value as in [pItem] const SfxPoolItem& rParentItem = pDest->GetpSwAttrSet()->GetParent()->Get( nWhich, true );
hasSwAttrSet = (rParentItem != *pItem);
// property set is not empty => no need to make anymore checks if (hasSwAttrSet) break;
}
// let's check next item
pItem = aIter.NextItem();
} while (pItem);
}
}
// all or just the Char attributes? if( nInitSize || hasSwAttrSet ||
nLen != pDest->GetText().getLength())
{
SfxItemSetFixed<
RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
aCharSet( pDest->GetDoc().GetAttrPool() );
aCharSet.Put( *GetpSwAttrSet() ); if( aCharSet.Count() )
pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
} else
{ // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before // and current SwTextNode can contain not suitable for pDest value
SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_LEVEL - 1,
RES_PARATR_LIST_LEVEL + 1, HINT_END>
aCharSet(pDest->GetDoc().GetAttrPool());
aCharSet.Put(*GetpSwAttrSet()); if (aCharSet.Count())
pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen);
}
}
// notify frames - before moving hints, because footnotes // want to find their anchor text frame in the follow chain // (also ignore fieldmarks, the caller will recreate frames) const sw::InsertText aInsHint(nDestStart, nLen, false, false);
pDest->HandleNonLegacyHint(aInsHint); const sw::MoveText aMoveHint(pDest, nDestStart, nTextStartIdx, nLen);
CallSwClientNotify(aMoveHint); const sw::DeleteText aDelText(nTextStartIdx, nLen);
HandleNonLegacyHint(aDelText);
// 2. move attributes // Iterate over attribute array until the start of the attribute // is behind the moved range bool bMergePortionsNeeded(false);
size_t nAttrCnt = 0; while (m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()))
{
SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt); const sal_Int32 nAttrStartIdx = pHt->GetStart(); if ( nAttrStartIdx >= nEnd ) break; const sal_Int32 * const pEndIdx = pHt->GetEnd(); const sal_uInt16 nWhich = pHt->Which();
SwTextAttr *pNewHt = nullptr;
// if the hint has a dummy character, then it must not be split! if(nAttrStartIdx < nTextStartIdx)
{ // start is before the range if (!pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich
|| bUndoNodes ) && pEndIdx && *pEndIdx > nTextStartIdx)
{ // attribute with extent and end of attribute is in the range
pNewHt = MakeTextAttr( pDest->GetDoc(), pHt->GetAttr(),
nDestStart,
nDestStart + (
*pEndIdx > nEnd
? nLen
: *pEndIdx - nTextStartIdx ) );
}
} else
{ // start is inside the range if (!pEndIdx || *pEndIdx < nEnd ||
(!bUndoNodes && RES_TXTATR_REFMARK == nWhich)
|| pHt->HasDummyChar() )
{ // do not delete note and later add it -> sidebar flickering
SwDocShell* pShell = GetDoc().GetDocShell(); if (pShell)
{
pShell->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
} // move attribute
m_pSwpHints->Delete( pHt ); // reset start/end indexes if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
{
bMergePortionsNeeded = true;
}
pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx)); if (pEndIdx)
{
pHt->SetEnd( nDestStart + (
*pEndIdx > nEnd
? nLen
: *pEndIdx - nTextStartIdx ) );
}
pDest->InsertHint( pHt,
SetAttrMode::NOTXTATRCHR
| SetAttrMode::DONTREPLACE ); if (pShell)
{
pShell->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
} continue; // iterate while loop, no ++ !
} // the end is behind the range elseif (RES_TXTATR_REFMARK != nWhich || bUndoNodes)
{
pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
nDestStart + (nAttrStartIdx - nTextStartIdx),
nDestStart + (*pEndIdx > nEnd
? nLen
: *pEndIdx - nTextStartIdx));
}
} if (pNewHt)
{ constbool bSuccess( pDest->InsertHint( pNewHt,
SetAttrMode::NOTXTATRCHR
| SetAttrMode::DONTREPLACE
| SetAttrMode::IS_COPY) ); if (bSuccess)
{
lcl_CopyHint( nWhich, pHt, pNewHt, nullptr, pDest );
}
}
++nAttrCnt;
} // If there are still empty attributes around, they have a higher priority // than any attributes that become empty due to the move. // So temporarily remove them and Update the array, then re-insert the // removed ones so they overwrite the others. if (m_pSwpHints && nAttrCnt < m_pSwpHints->Count())
{
SwpHts aArr; while (nAttrCnt < m_pSwpHints->Count())
{
SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt); if (nEnd != pHt->GetStart()) break; const sal_Int32 * const pEndIdx = pHt->GetEnd(); if (pEndIdx && *pEndIdx == nEnd)
{
aArr.push_back( pHt );
m_pSwpHints->Delete( pHt );
} else
{
++nAttrCnt;
}
}
Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete);
// Delete the hint if: // 1. The hint ends before the deletion end position or // 2. The hint ends at the deletion end position and // we are not in empty expand mode and // the hint is a [toxmark|refmark|ruby|inputfield] text attribute // 3. deleting exactly the dummy char of an hint with end and dummy // char deletes the hint if ( (*pHtEndIdx < nEndIdx)
|| ( (*pHtEndIdx == nEndIdx) &&
!(SwInsertFlags::EMPTYEXPAND & nMode) &&
( (RES_TXTATR_TOXMARK == nWhich) ||
(RES_TXTATR_REFMARK == nWhich) ||
(RES_TXTATR_CJK_RUBY == nWhich) ||
(RES_TXTATR_INPUTFIELD == nWhich) ) )
|| ( (nHintStart < nEndIdx) &&
pHt->HasDummyChar() )
)
{
m_pSwpHints->DeleteAtPos(i);
DestroyAttr( pHt );
--i;
}
}
OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
// Sending "noop" modify in order to cause invalidations of registered // <SwTextFrame> instances to get the list style change respectively the change // in the list tree reflected in the layout. // Important note:
{
SvxTextLeftMarginItem & rLR = const_cast<SvxTextLeftMarginItem&>(GetSwAttrSet().GetTextLeftMargin());
CallSwClientNotify(sw::LegacyModifyHint(&rLR, &rLR));
}
if ( IsInList() )
{
SwList* pList = GetDoc().getIDocumentListsAccess().getListByName(GetListId()); if (pList)
bResult = pList->IsListLevelMarked(GetActualListLevel());
}
return bResult;
} // <- #i27615#
namespace
{ /// Decides if a list level direct formatting on a paragraph needs copying to a next, new paragraph. bool CopyDirectListLevel(const SwTextNode* pTextNode)
{
SwTextFormatColl* pColl = pTextNode->GetTextColl(); if (!pColl)
{ // No style, so can't have a conflict with a direct formatting. returnfalse;
}
if (&pColl->GetNextTextFormatColl() != pColl)
{ // Style has a custom follow style, changing list level is OK. returnfalse;
}
if (!pColl->IsAssignedToListLevelOfOutlineStyle())
{ // Paragraph style has no own list level, no conflict. returnfalse;
}
// Copy is needed if the old paragraph had a direct formatting, which may be different and has // to be kept during the paragraph split. return pTextNode->HasAttrListLevel();
}
}
if (pRule && IsOutline())
{ if ( bNext )
oNewAttrSet->ClearItem(RES_PARATR_NUMRULE); else
{ // #i75353# // No clear of hard set numbering rule at an outline paragraph at this point. // Only if the paragraph style changes - see below.
bClearHardSetNumRuleWhenFormatCollChanges = true;
}
bRemoveFromCache = true;
}
}
const SwNumRule* pRule = GetNumRule(); if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() )
{ // #i55459# // - correction: parameter <bNext> has to be checked, as it was in the // previous implementation. if ( !bNext && !IsCountedInList() )
SetCountedInList(true);
}
// In case the numbering caused a style from the pool to be assigned to // the new node, don't overwrite that here! if( pColl != pNode->GetTextColl() ||
( bChgFollow && pColl != GetTextColl() )) return pNode; // that ought to be enough?
SwTextFormatColl *pNextColl = &pColl->GetNextTextFormatColl(); // i#101870 perform action on different paragraph styles before applying // the new paragraph style if (pNextColl != pColl)
{ // #i75353# if ( bClearHardSetNumRuleWhenFormatCollChanges )
{ if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE } ) != 0 )
{
InvalidateInSwCache();
}
}
}
ChgFormatColl( pNextColl, bSetListLevel );
return pNode;
}
SwContentNode* SwTextNode::AppendNode( const SwPosition & rPos )
{ // position behind which it will be inserted
SwTextNode* pNew = MakeNewTextNode( *rPos.GetNodes()[rPos.GetNodeIndex() + 1] );
// reset list attributes at appended text node
pNew->ResetAttr( RES_PARATR_LIST_ISRESTART );
pNew->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
pNew->ResetAttr( RES_PARATR_LIST_ISCOUNTED ); if ( pNew->GetNumRule() == nullptr )
{
pNew->ResetAttr( RES_PARATR_LIST_ID );
pNew->ResetAttr( RES_PARATR_LIST_LEVEL );
}
if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
{
AddToList();
}
if( pRule->IsAbsSpaces() )
{
SvxFirstLineIndentItem const& rFirst(GetSwAttrSet().GetFirstLineIndent());
nRet
= nRet - GetSwAttrSet().GetTextLeftMargin().ResolveLeft(rFirst, /*metrics*/ {});
}
} elseif ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
{
::sw::ListLevelIndents const indents(AreListLevelIndentsApplicable()); // note: the result is *always* added to either the left-margin // or the text-left-margin of the node itself by the caller. // so first, subtract what the caller has computed anyway, // and then add the value according to combination of // list/paragraph items. (this is rather inelegant)
SvxFirstLineIndentItem firstLine(GetSwAttrSet().GetFirstLineIndent());
SvxTextLeftMarginItem leftMargin(GetSwAttrSet().GetTextLeftMargin());
nRet = bTextLeft ? -leftMargin.ResolveTextLeft(/*metrics*/ {})
: -leftMargin.ResolveLeft(firstLine, /*metrics*/ {}); if (indents & ::sw::ListLevelIndents::LeftMargin)
{
leftMargin.SetTextLeft(SvxIndentValue::twips(rFormat.GetIndentAt()));
} if (indents & ::sw::ListLevelIndents::FirstLine)
{
firstLine.SetTextFirstLineOffset(
SvxIndentValue{ static_cast<double>(rFormat.GetFirstLineIndent()),
rFormat.GetFirstLineIndentUnit() });
}
nRet += bTextLeft ? leftMargin.ResolveTextLeft(/*metrics*/ {})
: leftMargin.ResolveLeft(firstLine, /*metrics*/ {});
}
}
// set all char attributes with Symbol font if ( HasHints() )
{
sal_Int32 nInsPos = nDestStt - nIdx; for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
{ const SwTextAttr* pHt = m_pSwpHints->Get(i); const sal_Int32 nAttrStartIdx = pHt->GetStart(); const sal_uInt16 nWhich = pHt->Which(); if (nIdx + nLen <= nAttrStartIdx) break; // behind end of text
if (nLen > nInsLen) // short insert
{ // delete up to the point that the user specified const SwContentIndex aNegIdx(rStart, nInsLen);
Update(aNegIdx, nLen - nInsLen, UpdateMode::Negative);
} elseif (nLen < nInsLen) // long insert
{ const SwContentIndex aIdx(rStart, nLen);
Update(aIdx, nInsLen - nLen, UpdateMode::Replace);
}
for (sal_Int32 i = 0; i < nInsLen; i++)
{
++const_cast<SwContentIndex&>(rStart);
}
} else
{
m_Text = m_Text.replaceAt(nStartPos, nLen, u"");
Update(rStart, nLen, UpdateMode::Negative);
// Helper method for special handling of modified attributes at text node. // The following is handled: // (1) on changing the paragraph style - RES_FMT_CHG: // Check, if list style of the text node is changed. If yes, add respectively // remove the text node to the corresponding list. // (2) on changing the attributes - RES_ATTRSET_CHG: // Same as (1). // (3) on changing the list style - RES_PARATR_NUMRULE: // Same as (1). void HandleModifyAtTextNode( SwTextNode& rTextNode, const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue )
{ const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
pNewValue ? pNewValue->Which() : 0 ; bool bNumRuleSet = false; bool bParagraphStyleChanged = false;
UIName sNumRule;
UIName sOldNumRule; switch ( nWhich )
{ case RES_PARATR_NUMRULE:
{ if ( rTextNode.GetNodes().IsDocNodes() )
{ const SwNumRule* pFormerNumRuleAtTextNode =
rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr; if ( pFormerNumRuleAtTextNode )
{
sOldNumRule = pFormerNumRuleAtTextNode->GetName();
}
if ( pNewValue )
{ // #i70748#
rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
bNumRuleSet = true;
} // #i70748# // The new list style set at the paragraph. const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule(); if ( pNumRuleAtTextNode )
{
sNumRule = pNumRuleAtTextNode->GetName();
}
} break;
}
}
HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, bParagraphStyleChanged);
} // End of method <HandleModifyAtTextNode>
if ( pNewValue && pNewValue->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false ) ==
SfxItemState::SET )
{ // #i70748#
rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
bNumRuleSet = true;
} // #i70748# // The new list style set at the paragraph. const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule(); if ( pNumRuleAtTextNode )
{
sNumRule = pNumRuleAtTextNode->GetName();
}
HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, /*bParagraphStyleChanged*/false);
}
// Helper method for special handling of modified attributes at text node. // The following is handled: // (1) on changing the paragraph style - RES_FMT_CHG: // Check, if list style of the text node is changed. If yes, add respectively // remove the text node to the corresponding list. void HandleModifyAtTextNodeFormatChange( SwTextNode& rTextNode )
{ bool bNumRuleSet = false; bool bParagraphStyleChanged = true;
UIName sNumRule;
UIName sOldNumRule; if( rTextNode.GetNodes().IsDocNodes() )
{ const SwNumRule* pFormerNumRuleAtTextNode =
rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr; if ( pFormerNumRuleAtTextNode )
{
sOldNumRule = pFormerNumRuleAtTextNode->GetName();
} if ( rTextNode.IsEmptyListStyleDueToSetOutlineLevelAttr() )
{ const SwNumRuleItem& rNumRuleItem = rTextNode.GetTextColl()->GetNumRule(); if ( !rNumRuleItem.GetValue().isEmpty() )
{
rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
}
} const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule(); if ( pNumRuleAtTextNode )
{
bNumRuleSet = true;
sNumRule = pNumRuleAtTextNode->GetName();
}
}
HandleApplyTextNodeFormatChange(rTextNode, sNumRule, sOldNumRule, bNumRuleSet, bParagraphStyleChanged);
}
void HandleApplyTextNodeFormatChange( SwTextNode& rTextNode, const UIName& sNumRule, const UIName& sOldNumRule, bool bNumRuleSet, bool bParagraphStyleChanged )
{ if ( sNumRule != sOldNumRule )
{ if ( bNumRuleSet )
{ if (sNumRule.isEmpty())
{
rTextNode.RemoveFromList(); if ( bParagraphStyleChanged )
{
lcl_ResetParAttrs(rTextNode);
}
} else
{
rTextNode.RemoveFromList(); // If new list style is the outline style, apply outline // level as the list level. if (sNumRule==SwNumRule::GetOutlineRuleName())
{ // #i70748#
OSL_ENSURE( rTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(), "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." ); constint nNewListLevel =
rTextNode.GetTextColl()->GetAssignedOutlineStyleLevel(); if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
{
rTextNode.SetAttrListLevel( nNewListLevel );
}
}
rTextNode.AddToList();
}
} else// <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
{
rTextNode.RemoveFromList(); if ( bParagraphStyleChanged )
{
lcl_ResetParAttrs(rTextNode); // #i70748# if ( rTextNode.GetAttr( RES_PARATR_OUTLINELEVEL, false ).GetValue() > 0 )
{
rTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
}
}
}
} elseif (!sNumRule.isEmpty() && !rTextNode.IsInList())
{
rTextNode.AddToList();
}
}
}
SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl *pNewColl, bool bSetListLevel )
{
OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && "ChgFormatColl: is not a Text Collection pointer." );
// reset fill information on parent style change if(maFillAttributes)
{
maFillAttributes.reset();
}
}
// only for real nodes-array if( GetNodes().IsDocNodes() )
{
ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl *>(pNewColl), bSetListLevel );
}
return pOldColl;
}
const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
{ // invariant: it's only in list in Hide mode if it's in list in normal mode
assert(mpNodeNum || !mpNodeNumRLHidden); return (pLayout && pLayout->IsHideRedlines()) || SwListRedlineType::HIDDEN == eRedline
? mpNodeNumRLHidden.get()
: ( SwListRedlineType::ORIGTEXT == eRedline ? mpNodeNumOrig.get() : mpNodeNum.get() );
}
void SwTextNode::DoNum(std::function<void (SwNodeNum &)> const& rFunc)
{ // temp. clear because GetActualListLevel() may be called and the assert // there triggered during update, which is unhelpful
std::unique_ptr<SwNodeNum> pBackup = std::move(mpNodeNumRLHidden);
std::unique_ptr<SwNodeNum> pBackup2 = std::move(mpNodeNumOrig);
assert(mpNodeNum);
rFunc(*mpNodeNum); if (pBackup)
{
mpNodeNumRLHidden = std::move(pBackup);
rFunc(*mpNodeNumRLHidden);
} if (pBackup2)
{
mpNodeNumOrig = std::move(pBackup2);
rFunc(*mpNodeNumOrig);
}
}
int SwTextNode::GetActualListLevel(SwListRedlineType eRedline) const
{
assert(SwListRedlineType::SHOW != eRedline ||
!GetNum(nullptr, SwListRedlineType::SHOW) || !mpNodeNumRLHidden || // must be in sync
GetNum(nullptr, SwListRedlineType::SHOW)->GetLevelInListTree() ==
mpNodeNumRLHidden->GetLevelInListTree()); return GetNum(nullptr, eRedline) ? GetNum(nullptr, eRedline)->GetLevelInListTree() : -1;
}
void SwTextNode::SetListRestart( bool bRestart )
{ if ( !bRestart )
{ // attribute not contained in paragraph style's attribute set. Thus, // it can be reset to the attribute pool default by resetting the attribute.
ResetAttr( RES_PARATR_LIST_ISRESTART );
} else
{
SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART, true );
SetAttr( aNewIsRestartItem );
}
}
/** Returns if the paragraph has a visible numbering or bullet. Thisincludesallkindsofnumbering/bullet/outlines. Theconcretelistlabelstringhastobechecked,too.
*/ bool SwTextNode::HasVisibleNumberingOrBullet() const
{ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr; if ( pRule && IsCountedInList())
{ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel())); if (getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY)) // True if we have something in label text or there is a non-empty // FollowedBy separator (space, tab or whatsoever) return rFormat.GetLabelFollowedBy() != SvxNumberFormat::LabelFollowedBy::NOTHING ||
!pRule->MakeNumString(*GetNum()).isEmpty(); else // #i87154# // Correction of #newlistlevelattrs#: // The numbering type has to be checked for bullet lists. return SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ||
!pRule->MakeNumString(*(GetNum())).isEmpty();
}
bool SwTextNode::HasAttrListRestartValue() const
{ return GetpSwAttrSet() &&
GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, false ) == SfxItemState::SET;
}
SwNumberTree::tSwNumTreeNumber SwTextNode::GetAttrListRestartValue() const
{
OSL_ENSURE( HasAttrListRestartValue(), "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
void SwTextNode::SetCountedInList( bool bCounted )
{ if ( bCounted )
{ // attribute not contained in paragraph style's attribute set. Thus, // it can be reset to the attribute pool default by resetting the attribute.
ResetAttr( RES_PARATR_LIST_ISCOUNTED );
} else
{
SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, false );
SetAttr( aIsCountedInListItem );
}
}
static SwList * FindList(SwTextNode *const pNode)
{ const OUString sListId = pNode->GetListId(); if (!sListId.isEmpty())
{ auto & rIDLA(pNode->GetDoc().getIDocumentListsAccess());
SwList* pList = rIDLA.getListByName( sListId ); if ( pList == nullptr )
{ // Create corresponding list.
SwNumRule* pNumRule = pNode->GetNumRule(); if ( pNumRule )
{
pList = rIDLA.createList(sListId, pNode->GetNumRule()->GetName());
}
}
OSL_ENSURE( pList != nullptr, "<SwTextNode::AddToList()> - no list for given list id. Serious defect" ); return pList;
} return nullptr;
}
void SwTextNode::AddToList()
{ if ( IsInList() )
{
OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" ); return;
}
SwList *const pList(FindList(this)); if (!(pList && GetNodes().IsDocNodes())) // not for undo nodes return;
// set redline lists // "default" list: visible items in Show Changes mode (tracked insertions and deletions) // "hidden" list: visible items in Hide Changes mode (tracked insertions, but not deletions) // "orig" list: visible items rejecting all changes (no tracked insertions and deletions)
SwDocShell* pShell = GetDoc().GetDocShell(); bool bRecordChanges = pShell && pShell->IsChangeRecording(); if (!bRecordChanges || GetDoc().IsInXMLImport() || GetDoc().IsInWriterfilterImport() )
{ const SwRedlineTable& rRedTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
SwRedlineTable::size_type nRedlPos = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Insert); // paragraph start is not in a tracked insertion if ( SwRedlineTable::npos == nRedlPos || GetIndex() <= rRedTable[nRedlPos]->Start()->GetNode().GetIndex() )
{
AddToListOrig();
// if the paragraph is not deleted, add to the "hidden" list, too
SwRedlineTable::size_type nRedlPosDel = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Delete); if ( SwRedlineTable::npos == nRedlPosDel )
AddToListRLHidden(); else
{ const SwNodeOffset nNdIdx = GetIndex(); const SwRangeRedline* pTmp = rRedTable[nRedlPosDel]; const SwPosition* pRStt = pTmp->Start(); if (pRStt->GetNodeIndex() >= nNdIdx)
{ // paragraph is partly deleted, add to the "hidden" list, too
AddToListRLHidden();
}
}
} // inserted paragraph, e.g. during file load, add to the "hidden" list elseif ( SwRedlineTable::npos != nRedlPos )
AddToListRLHidden();
} elseif ( bRecordChanges )
AddToListRLHidden();
// iterate all frames & if there's one with hidden layout...
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this); for (SwTextFrame* pFrame = iter.First(); pFrame && !mpNodeNumRLHidden; pFrame = iter.Next())
{ if (pFrame->getRootFrame()->IsHideRedlines())
{ if (pFrame->GetTextNodeForParaProps() == this)
{
AddToListRLHidden();
} break; // assume it's consistent, need to check only once
}
}
}
void SwTextNode::AddToListRLHidden()
{ if (mpNodeNumRLHidden) return;
void SwTextNode::RemoveFromList()
{ // sw_redlinehide: ensure it's removed from the other half too!
RemoveFromListRLHidden();
RemoveFromListOrig(); if ( IsInList() )
{
SwList::RemoveListItem(*mpNodeNum, GetDoc());
mpNodeNum.reset();
SetWordCountDirty( true );
}
}
void SwTextNode::RemoveFromListRLHidden()
{ if (mpNodeNumRLHidden) // direct access because RemoveFromList doesn't have layout
{
assert(mpNodeNumRLHidden->GetParent() || !GetNodes().IsDocNodes());
SwList::RemoveListItem(*mpNodeNumRLHidden, GetDoc());
mpNodeNumRLHidden.reset();
SetWordCountDirty( true );
}
}
void SwTextNode::RemoveFromListOrig()
{ if (mpNodeNumOrig) // direct access because RemoveFromList doesn't have layout
{
assert(mpNodeNumOrig->GetParent() || !GetNodes().IsDocNodes());
SwList::RemoveListItem(*mpNodeNumOrig, GetDoc());
mpNodeNumOrig.reset();
// As long as no explicit list id attribute is set, use the list id of // the list, which has been created for the applied list style. if (sListId.isEmpty())
{
SwNumRule* pRule = GetNumRule(); if ( pRule )
{ return pRule->GetDefaultListId();
}
}
return sListId;
}
/** Determines, if the list level indent attributes can be applied to the paragraph.
if ( !GetNum() || !GetNum()->GetNumRule() )
{ // no list style applied to paragraph
bAreListLevelIndentsApplicable = false;
} elseif ( HasSwAttrSet() &&
GetpSwAttrSet()->GetItemState(nWhich, false) == SfxItemState::SET)
{ // paragraph has hard-set indent attributes
bAreListLevelIndentsApplicable = false;
} elseif ( HasSwAttrSet() &&
GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
{ // list style is directly applied to paragraph and paragraph has no // hard-set indent attributes
bAreListLevelIndentsApplicable = true;
} else
{ // list style is applied through one of the paragraph styles and // paragraph has no hard-set indent attributes
// check, paragraph's const SwTextFormatColl* pColl = GetTextColl(); while ( pColl )
{ if (pColl->GetAttrSet().GetItemState(nWhich, false) == SfxItemState::SET)
{ // indent attributes found in the paragraph style hierarchy.
bAreListLevelIndentsApplicable = false; break;
}
if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
{ // paragraph style with the list style found and until now no // indent attributes are found in the paragraph style hierarchy.
bAreListLevelIndentsApplicable = true; break;
}
pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
OSL_ENSURE( pColl, "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
}
}
return bAreListLevelIndentsApplicable;
}
/** Retrieves the list tab stop position, if the paragraph's list level defines oneandthislisttabstophastomergedintothetapstopsoftheparagraph
if ( getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
{ // tab stop position are treated to be relative to the "before text" // indent value of the paragraph. Thus, adjust <nListTabStopPos>. if (AreListLevelIndentsApplicable() & ::sw::ListLevelIndents::LeftMargin)
{
nListTabStopPosition -= rFormat.GetIndentAt();
} elseif (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
{
SvxTextLeftMarginItem const aItem(GetSwAttrSet().GetTextLeftMargin());
nListTabStopPosition -= aItem.ResolveTextLeft({});
}
}
}
}
namespace { // Helper class for special handling of setting attributes at text node: // In constructor an instance of the helper class recognize whose attributes // are set and perform corresponding actions before the intrinsic set of // attributes has been taken place. // In the destructor - after the attributes have been set at the text // node - corresponding actions are performed. // The following is handled: // (1) When the list style attribute - RES_PARATR_NUMRULE - is set, // (A) list style attribute is empty -> the text node is removed from // its list. // (B) list style attribute is not empty // (a) text node has no list style -> add text node to its list after // the attributes have been set. // (b) text node has list style -> change of list style is notified // after the attributes have been set. // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed, // the text node is removed from its current list before the attributes // are set and added to its new list after the attributes have been set. // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set // and changed after the attributes have been set // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set // and changed after the attributes have been set // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - // is set and changed after the attributes have been set // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set // and changed after the attributes have been set // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL. class HandleSetAttrAtTextNode
{ public:
HandleSetAttrAtTextNode( SwTextNode& rTextNode, const SfxPoolItem& pItem );
HandleSetAttrAtTextNode( SwTextNode& rTextNode, const SfxItemSet& rItemSet );
~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
if (mbUpdateListCount && mrTextNode.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode))
{ // Repaint all text frames that belong to this numbering to avoid outdated generated // numbers. const SwDoc& rDoc(mrTextNode.GetDoc());
mrTextNode.DoNum(
[&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
}
}
// #i70748# if (!mbOutlineLevelSet) return;
mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode); if (mrTextNode.GetAttrOutlineLevel() == 0)
{
mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
} else
{ if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE )
!= SfxItemState::SET )
{
mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
}
}
} // End of class <HandleSetAttrAtTextNode>
}
namespace { // Helper class for special handling of resetting attributes at text node: // In constructor an instance of the helper class recognize whose attributes // are reset and perform corresponding actions before the intrinsic reset of // attributes has been taken place. // In the destructor - after the attributes have been reset at the text // node - corresponding actions are performed. // The following is handled: // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset, // the text is removed from its list before the attributes have been reset. // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset, // the text is removed from its list before the attributes have been reset. // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset. // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset. // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset. // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset. // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset. class HandleResetAttrAtTextNode
{ public:
HandleResetAttrAtTextNode( SwTextNode& rTextNode, const sal_uInt16 nWhich1,
sal_uInt16 nWhich2 );
HandleResetAttrAtTextNode( SwTextNode& rTextNode, const std::vector<sal_uInt16>& rWhichArr ); explicit HandleResetAttrAtTextNode( SwTextNode& rTextNode );
HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
{ if ( mbListStyleOrIdReset && !mrTextNode.IsInList() )
{ // check, if in spite of the reset of the list style or the list id // the paragraph still has to be added to a list. if (mrTextNode.GetNumRule() && !mrTextNode.GetListId().isEmpty())
{ // #i96062# // If paragraph has no list level attribute set and list style // is the outline style, apply outline level as the list level. if ( !mrTextNode.HasAttrListLevel() &&
mrTextNode.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
mrTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
{ int nNewListLevel = mrTextNode.GetTextColl()->GetAssignedOutlineStyleLevel(); if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
{
mrTextNode.SetAttrListLevel( nNewListLevel );
}
}
mrTextNode.AddToList();
} // #i70748# // #i105562# else
{ if (mrTextNode.GetpSwAttrSet()
&& mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false).GetValue() > 0)
{
mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
}
}
}
if ( !mrTextNode.IsInList() ) return;
// just incredibly slow to do this if (comphelper::IsFuzzing()) return;
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.