LibreOffice Module sw (master) 1
ndtxt.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <hintids.hxx>
21#include <hints.hxx>
22
23#include <comphelper/lok.hxx>
24#include <comphelper/string.hxx>
25#include <editeng/fontitem.hxx>
27#include <editeng/lrspitem.hxx>
28#include <editeng/rsiditem.hxx>
29#include <sal/log.hxx>
30#include <osl/diagnose.h>
31#include <anchoredobject.hxx>
32#include <txtfld.hxx>
33#include <txtinet.hxx>
34#include <fmtanchr.hxx>
35#include <fmtinfmt.hxx>
36#include <fmtrfmrk.hxx>
37#include <txttxmrk.hxx>
38#include <fchrfmt.hxx>
39#include <txtftn.hxx>
40#include <fmtflcnt.hxx>
41#include <fmtfld.hxx>
42#include <frmatr.hxx>
43#include <ftnidx.hxx>
44#include <ftninfo.hxx>
45#include <fmtftn.hxx>
46#include <charfmt.hxx>
47#include <ndtxt.hxx>
48#include <doc.hxx>
49#include <IDocumentUndoRedo.hxx>
54#include <docary.hxx>
55#include <pam.hxx>
56#include <fldbas.hxx>
57#include <paratr.hxx>
58#include <txtfrm.hxx>
59#include <ftnfrm.hxx>
60#include <pagefrm.hxx>
61#include <rootfrm.hxx>
62#include <expfld.hxx>
63#include <section.hxx>
64#include <mvsave.hxx>
65#include <SwGrammarMarkUp.hxx>
66#include <redline.hxx>
67#include <IMark.hxx>
68#include <scriptinfo.hxx>
69#include <istyleaccess.hxx>
70#include <SwStyleNameMapper.hxx>
71#include <numrule.hxx>
72#include <docsh.hxx>
73#include <SwNodeNum.hxx>
74#include <svl/grabbagitem.hxx>
75#include <svl/intitem.hxx>
76#include <sortedobjs.hxx>
77#include <calbck.hxx>
78#include <attrhint.hxx>
79#include <memory>
80#include <unoparagraph.hxx>
81#include <wrtsh.hxx>
82#include <fmtpdsc.hxx>
84#include <svl/itemiter.hxx>
85
86using namespace ::com::sun::star;
87
88typedef std::vector<SwTextAttr*> SwpHts;
89
90namespace sw {
94 public:
96 : m_rNode(rNode)
97 , m_bWasNotifiable(rNode.m_bNotifiable)
98 {
99 m_rNode.m_bNotifiable = false;
100 }
102 {
104 }
105 };
106}
107
108// unfortunately everyone can change Hints without ensuring order or the linking between them
109#ifdef DBG_UTIL
110#define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \
111 !pNd->GetDoc().IsInReading() ) \
112 pNd->GetpSwpHints()->Check(true); }
113#define CHECK_SWPHINTS_IF_FRM(pNd) { if( pNd->GetpSwpHints() && \
114 !pNd->GetDoc().IsInReading() ) \
115 pNd->GetpSwpHints()->Check(getLayoutFrame(nullptr, nullptr, nullptr) != nullptr); }
116#else
117#define CHECK_SWPHINTS(pNd)
118#define CHECK_SWPHINTS_IF_FRM(pNd)
119#endif
120
122 SwTextFormatColl *pColl, bool const bNewFrames)
123{
124 OSL_ENSURE( pColl, "Collection pointer is 0." );
125
126 SwTextNode *pNode = new SwTextNode( rWhere, pColl, nullptr );
127
128 SwNodeIndex aIdx( *pNode );
129
130 // if there is no layout or it is in a hidden section, MakeFrames is not needed
131 const SwSectionNode* pSectNd;
132 if (!bNewFrames ||
133 !GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
134 ( nullptr != (pSectNd = pNode->FindSectionNode()) &&
135 pSectNd->GetSection().IsHiddenFlag() ))
136 return pNode;
137
138 SwNodeIndex aTmp( rWhere );
139 do {
140 // max. 2 loops:
141 // 1. take the successor
142 // 2. take the predecessor
143
144 SwNode * pNd = & aTmp.GetNode();
145 switch (pNd->GetNodeType())
146 {
148 static_cast<SwTableNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
149 return pNode;
150
152 if( static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() ||
153 static_cast<SwSectionNode*>(pNd)->IsContentHidden() )
154 {
155 pNd = FindPrvNxtFrameNode( *pNode, pNode );
156 if( !pNd )
157 return pNode;
158 aTmp = *pNd;
159 break;
160 }
161 static_cast<SwSectionNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
162 return pNode;
163
164 case SwNodeType::Text:
165 case SwNodeType::Grf:
166 case SwNodeType::Ole:
167 static_cast<SwContentNode*>(pNd)->MakeFramesForAdjacentContentNode(*pNode);
168 return pNode;
169
170 case SwNodeType::End:
171 if( pNd->StartOfSectionNode()->IsSectionNode() &&
172 aTmp.GetIndex() < rWhere.GetIndex() )
173 {
175 {
176 if( !GoPrevSection( &aTmp, true, false ) ||
177 aTmp.GetNode().FindTableNode() !=
178 pNode->FindTableNode() )
179 return pNode;
180 }
181 else
182 aTmp = *pNd->StartOfSectionNode();
183 break;
184 }
185 else if( pNd->StartOfSectionNode()->IsTableNode() &&
186 aTmp.GetIndex() < rWhere.GetIndex() )
187 {
188 // after a table node
189 aTmp = *pNd->StartOfSectionNode();
190 break;
191 }
192 [[fallthrough]];
193 default:
194 if( &rWhere == &aTmp.GetNode() )
195 aTmp -= SwNodeOffset(2);
196 else
197 return pNode;
198 break;
199 }
200 } while( true );
201}
202
203SwTextNode::SwTextNode( SwNode& rWhere, SwTextFormatColl *pTextColl, const SfxItemSet* pAutoAttr )
204: SwContentNode( rWhere, SwNodeType::Text, pTextColl ),
205 m_bContainsHiddenChars(false),
206 m_bHiddenCharsHidePara(false),
207 m_bRecalcHiddenCharFlags(false),
208 m_bLastOutlineState( false ),
209 m_bNotifiable( true ),
210 mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ),
211 mbInSetOrResetAttr( false ),
212 m_bInUndo(false)
213{
214 {
216
217 if( pAutoAttr )
218 SetAttr( *pAutoAttr );
219
220 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
221 {
222 // #i101516#
223 // apply paragraph style's assigned outline style list level as
224 // list level of the paragraph, if it has none set already.
225 if ( !HasAttrListLevel() &&
226 pTextColl && pTextColl->IsAssignedToListLevelOfOutlineStyle() )
227 {
229 }
230 AddToList();
231 }
232
233 // call method <UpdateOutlineNode(..)> only for the document nodes array
234 if (GetNodes().IsDocNodes())
236 }
237
240}
241
243{
244 // delete only removes the pointer not the array elements!
245 if ( m_pSwpHints )
246 {
247 // do not delete attributes twice when those delete their content
248 std::unique_ptr<SwpHints> pTmpHints(std::move(m_pSwpHints));
249
250 for( size_t j = pTmpHints->Count(); j; )
251 {
252 // first remove the attribute from the array otherwise
253 // if would delete itself
254 DestroyAttr( pTmpHints->Get( --j ) );
255 }
256 }
257
258 // must be removed from outline nodes by now
259#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
261 assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo));
262#endif
263
265
266 DelFrames(nullptr); // must be called here while it's still a SwTextNode
268#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
269 if (!GetDoc().IsInDtor())
271#else
273#endif
275}
276
278{
279 if (m_pSwpHints)
280 {
281 m_pSwpHints->MergePortions(*this);
282 }
283}
284
286{
288 return pFrame;
289}
290
291sal_Int32 SwTextNode::Len() const
292{
293 return m_Text.getLength();
294}
295
296// After a split node, it's necessary to actualize the ref-pointer of the ftnfrms.
298{
299 SwpHints *pSwpHints = rNode.GetpSwpHints();
300 if( !(pSwpHints && rNode.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
301 return;
302
303 SwContentFrame* pFrame = nullptr;
304 // OD 07.11.2002 #104840# - local variable to remember first footnote
305 // of node <rNode> in order to invalidate position of its first content.
306 // Thus, in its <MakeAll()> it will checked its position relative to its reference.
307 SwFootnoteFrame* pFirstFootnoteOfNode = nullptr;
308 for( size_t j = pSwpHints->Count(); j; )
309 {
310 SwTextAttr* pHt = pSwpHints->Get(--j);
311 if (RES_TXTATR_FTN == pHt->Which())
312 {
313 if( !pFrame )
314 {
316 if (!pFrame)
317 return;
318 }
319 SwTextFootnote *pAttr = static_cast<SwTextFootnote*>(pHt);
320 OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
321 SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
322 SwContentNode *pNd = aIdx.GetNode().GetContentNode();
323 if ( !pNd )
324 pNd = pFrame->GetAttrSet()->GetDoc()->
325 GetNodes().GoNextSection( &aIdx, true, false );
326 if ( !pNd )
327 continue;
328
330 SwContentFrame* pContent = aIter.First();
331 if( pContent )
332 {
333 OSL_ENSURE( pContent->getRootFrame() == pFrame->getRootFrame(),
334 "lcl_ChangeFootnoteRef: Layout double?" );
335 SwFootnoteFrame *pFootnote = pContent->FindFootnoteFrame();
336 if( pFootnote && pFootnote->GetAttr() == pAttr )
337 {
338 while( pFootnote->GetMaster() )
339 pFootnote = pFootnote->GetMaster();
340 // #104840# - remember footnote frame
341 pFirstFootnoteOfNode = pFootnote;
342 while ( pFootnote )
343 {
344 pFootnote->SetRef( pFrame );
345 pFootnote = pFootnote->GetFollow();
346 static_cast<SwTextFrame*>(pFrame)->SetFootnote( true );
347 }
348 }
349#if OSL_DEBUG_LEVEL > 0
350 while( nullptr != (pContent = aIter.Next()) )
351 {
352 SwFootnoteFrame *pDbgFootnote = pContent->FindFootnoteFrame();
353 OSL_ENSURE( !pDbgFootnote || pDbgFootnote->GetRef() == pFrame,
354 "lcl_ChangeFootnoteRef: Who's that guy?" );
355 }
356#endif
357 }
358 }
359 } // end of for-loop on <SwpHints>
360 // #104840# - invalidate
361 if ( pFirstFootnoteOfNode )
362 {
363 SwContentFrame* pContent = pFirstFootnoteOfNode->ContainsContent();
364 if ( pContent )
365 {
366 pContent->InvalidatePos_();
367 }
368 }
369}
370
371namespace sw {
372
373// check if there are flys on the existing frames (now on "pNode")
374// that need to be moved to the new frames of "this"
375void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
376 SwTextNode const& rFirstNode, SwTextNode & rSecondNode,
377 bool isSplitNode)
378{
379 if (!isSplitNode)
380 {
381 lcl_ChangeFootnoteRef(rSecondNode);
382 }
383 for (SwNodeOffset nIndex = rSecondNode.GetIndex() + 1; ; ++nIndex)
384 {
385 SwNode *const pTmp(rSecondNode.GetNodes()[nIndex]);
386 if (pTmp->IsCreateFrameWhenHidingRedlines() || pTmp->IsEndNode())
387 {
388 break;
389 }
390 else if (pTmp->IsStartNode())
391 {
392 nIndex = pTmp->EndOfSectionIndex();
393 }
395 && pTmp->IsTextNode())
396 {
398 }
399 }
400 for (SwTextFrame *const pFrame : rFrames)
401 {
402 if (SwSortedObjs *const pObjs = pFrame->GetDrawObjs())
403 {
404 std::vector<SwAnchoredObject*> objs;
405 objs.reserve(pObjs->size());
406 for (SwAnchoredObject *const pObj : *pObjs)
407 {
408 objs.push_back(pObj);
409 }
410 for (SwAnchoredObject *const pObj : objs)
411 {
412 SwFrameFormat & rFormat(pObj->GetFrameFormat());
413 SwFormatAnchor const& rAnchor(rFormat.GetAnchor());
414 if (rFirstNode.GetIndex() < rAnchor.GetAnchorNode()->GetIndex())
415 {
416 // move it to the new frame of "this"
417 rFormat.CallSwClientNotify(sw::LegacyModifyHint(&rAnchor, &rAnchor));
418 // note pObjs will be deleted if it becomes empty
419 assert(!pFrame->GetDrawObjs() || !pObjs->Contains(*pObj));
420 }
421 }
422 }
423 }
424}
425
426} // namespace
427
429 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool AtStart)> const*const pContentIndexRestore)
430{
431 bool isHide(false);
432 SwNode::Merge const eOldMergeFlag(GetRedlineMergeFlag());
433 bool parentIsOutline = IsOutline();
434
435 // create a node "in front" of me
436 const sal_Int32 nSplitPos = rPos.GetContentIndex();
437 const sal_Int32 nTextLen = m_Text.getLength();
438 SwTextNode* const pNode =
439 MakeNewTextNode( rPos.GetNode(), false, nSplitPos==nTextLen );
440
441 // the first paragraph gets the XmlId,
442 // _except_ if it is empty and the second is not empty
443 if (nSplitPos != 0) {
444 pNode->RegisterAsCopyOf(*this, true);
445 if (nSplitPos == nTextLen)
446 {
447 RemoveMetadataReference();
448 // NB: SwUndoSplitNode will call pNode->JoinNext,
449 // which is sufficient even in this case!
450 }
451 }
452
456 if ( GetNumRule() == nullptr || (parentIsOutline && !IsOutline()) )
457 {
460 }
461
462 if ( HasWriterListeners() && !m_Text.isEmpty() && (nTextLen / 2) < nSplitPos )
463 {
464 // optimization for SplitNode: If a split is at the end of a node then
465 // move the frames from the current to the new one and create new ones
466 // for the current one.
467
468 // If fly frames are moved, they don't need to destroy their layout
469 // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor.
470 if ( HasHints() )
471 {
472 pNode->GetOrCreateSwpHints().SetInSplitNode(true);
473 }
474
475 // Move the first part of the content to the new node and delete
476 // it in the old node.
477 SwContentIndex aIdx( this );
478 CutText( pNode, aIdx, nSplitPos );
479
480 if( GetWrong() )
481 {
482 pNode->SetWrong( GetWrong()->SplitList( nSplitPos ) );
483 }
485
486 if( GetGrammarCheck() )
487 {
488 pNode->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos ) );
489 }
490 SetGrammarCheckDirty( true );
491
492 SetWordCountDirty( true );
493
494 if( GetSmartTags() )
495 {
496 pNode->SetSmartTags( GetSmartTags()->SplitList( nSplitPos ) );
497 }
498 SetSmartTagDirty( true );
499
502
503 if ( pNode->HasHints() )
504 {
505 if ( pNode->m_pSwpHints->CanBeDeleted() )
506 {
507 pNode->m_pSwpHints.reset();
508 }
509 else
510 {
511 pNode->m_pSwpHints->SetInSplitNode(false);
512 }
513
514 // All fly frames anchored as char that are moved to the new
515 // node must have their layout frames deleted.
516 // This comment is sort of silly because we actually delete the
517 // layout frames of those which were not moved?
518 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
519 if ( HasHints() )
520 {
521 for ( size_t j = m_pSwpHints->Count(); j; )
522 {
523 SwTextAttr* const pHt = m_pSwpHints->Get( --j );
524 if ( RES_TXTATR_FLYCNT == pHt ->Which() )
525 {
527 }
528 else if ( pHt->DontExpand() )
529 {
530 const sal_Int32* const pEnd = pHt->GetEnd();
531 if (pEnd && pHt->GetStart() == *pEnd )
532 {
533 // delete it!
534 m_pSwpHints->DeleteAtPos( j );
535 DestroyAttr( pHt );
536 }
537 }
538 }
539 }
540
541 }
542
543 if (pContentIndexRestore)
544 { // call before making frames and before RegisterToNode
545 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
546 }
547 if (eOldMergeFlag != SwNode::Merge::None)
548 { // clear before making frames and before RegisterToNode
550 } // now RegisterToNode will set merge flags in both nodes properly!
551
552 std::vector<SwTextFrame*> frames;
554 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
555 {
556 if (pFrame->getRootFrame()->HasMergedParas())
557 {
558 isHide = true;
559 }
560 frames.push_back(pFrame);
561 }
562 for (SwTextFrame * pFrame : frames)
563 {
564 pFrame->RegisterToNode( *pNode );
565 if (!pFrame->IsFollow() && pFrame->GetOffset())
566 {
567 pFrame->SetOffset( TextFrameIndex(0) );
568 }
569 }
570
572
573 if ( HasHints() )
574 {
576 }
577 // in case there are frames, the RegisterToNode has set the merge flag
579 lcl_ChangeFootnoteRef( *this );
580 if (pContentIndexRestore)
581 { // call after making frames; listeners will take care of adding to the right frame
582 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, false);
583 }
584 if (eOldMergeFlag != SwNode::Merge::None)
585 {
586 MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
587 }
588 }
589 else
590 {
591 std::unique_ptr<SwWrongList> pList = ReleaseWrong();
593
594 std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
595 SetGrammarCheckDirty( true );
596
597 SetWordCountDirty( true );
598
599 std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
600 SetSmartTagDirty( true );
601
602 SwContentIndex aIdx( this );
603 CutText( pNode, aIdx, nSplitPos );
604
605 // JP 01.10.96: delete all empty and not-to-be-expanded attributes
606 if ( HasHints() )
607 {
608 for ( size_t j = m_pSwpHints->Count(); j; )
609 {
610 SwTextAttr* const pHt = m_pSwpHints->Get( --j );
611 const sal_Int32* const pEnd = pHt->GetEnd();
612 if ( pHt->DontExpand() && pEnd && (pHt->GetStart() == *pEnd) )
613 {
614 // delete it!
615 m_pSwpHints->DeleteAtPos( j );
616 DestroyAttr( pHt );
617 }
618 }
620 }
621
622 if( pList )
623 {
624 pNode->SetWrong( pList->SplitList( nSplitPos ) );
625 SetWrong( std::move(pList) );
626 }
627
628 if( pList3 )
629 {
630 pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) );
631 SetGrammarCheck( std::move(pList3) );
632 }
633
634 if( pList2 )
635 {
636 pNode->SetSmartTags( pList2->SplitList( nSplitPos ) );
637 SetSmartTags( std::move(pList2) );
638 }
639
642
643 if (pContentIndexRestore)
644 { // call before making frames and before RegisterToNode
645 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
646 }
647
648 std::vector<SwTextFrame*> frames;
650 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
651 {
652 frames.push_back(pFrame);
653 if (pFrame->getRootFrame()->HasMergedParas())
654 {
655 isHide = true;
656 }
657 }
658 bool bNonMerged(false);
659 bool bRecreateThis(false);
660 for (SwTextFrame * pFrame : frames)
661 {
662 // sw_redlinehide: for this to work properly with hidden nodes,
663 // the frame needs to listen on them too.
664 // also: have to check the frame; this->GetRedlineMergeFlag()
665 // is None in case there's a delete redline inside the paragraph,
666 // but that could still result in a merged frame after split...
667 if (pFrame->GetMergedPara())
668 {
669 // Can't special case this == First here - that could (if
670 // both nodes are still merged by redline) lead to
671 // duplicate frames on "this".
672 // Update the extents with new node; also inits merge flag,
673 // so the MakeFramesForAdjacentContentNode below respects it
674 pFrame->RegisterToNode(*pNode);
675 if (nSplitPos == 0)
676 {
677 // in this case, it was not
678 // invalidated because Cut didn't sent it any hints,
679 // so we have to invalidate it here!
680 pFrame->Prepare(PrepareHint::Clear, nullptr, false);
681 }
682 if (!pFrame->GetMergedPara() ||
683 !pFrame->GetMergedPara()->listener.IsListeningTo(this))
684 {
685 // it's no longer listening - need to recreate frame
686 // (note this is idempotent, can be done once per frame)
688 bRecreateThis = true;
689 }
690 }
691 else
692 {
693 bNonMerged = true;
694 }
695 }
696 assert(!(bNonMerged && bRecreateThis)); // 2 layouts not handled yet - maybe best to simply use the other branch then?
697 if (!frames.empty() && bNonMerged)
698 {
699 // the existing frame on "this" should have been updated by Cut
701 lcl_ChangeFootnoteRef(*pNode);
702 }
703 else if (bRecreateThis)
704 {
705 assert(pNode->HasWriterListeners()); // was just moved there
708 }
709
710 if (pContentIndexRestore)
711 { // call after making frames; listeners will take care of adding to the right frame
712 (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, nSplitPos == 0);
713 }
714
715 if (bRecreateThis)
716 {
717 MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
718 }
719 }
720
721 // pNode is the previous node, 'this' is the next node from the split.
722 if (nSplitPos == nTextLen && m_pSwpHints)
723 {
724 // We just created an empty next node: avoid unwanted superscript in the new node if it's
725 // there.
726 for (size_t i = 0; i < m_pSwpHints->Count(); ++i)
727 {
728 SwTextAttr* pHt = m_pSwpHints->Get(i);
729 if (pHt->Which() != RES_TXTATR_AUTOFMT)
730 {
731 continue;
732 }
733
734 const sal_Int32* pEnd = pHt->GetEnd();
735 if (!pEnd || pHt->GetStart() != *pEnd)
736 {
737 continue;
738 }
739
740 const std::shared_ptr<SfxItemSet>& pSet = pHt->GetAutoFormat().GetStyleHandle();
741 if (!pSet || pSet->Count() != 1 || !pSet->GetItem(RES_CHRATR_ESCAPEMENT))
742 {
743 continue;
744 }
745
746 m_pSwpHints->DeleteAtPos(i);
747 SwTextAttr::Destroy(pHt, GetDoc().GetAttrPool());
748 --i;
749 }
750 }
751
752#ifndef NDEBUG
753 if (isHide) // otherwise flags won't be set anyway
754 {
755 // First
756 // -> First,NonFirst
757 // -> First,Hidden
758 // -> None,First
759 // Hidden
760 // -> Hidden,Hidden (if still inside merge rl)
761 // -> NonFirst,First (if redline was split)
762 // NonFirst
763 // -> NonFirst,First (if split after end of "incoming" redline &
764 // before start of "outgoing" redline)
765 // -> NonFirst,None (if split after end of "incoming" redline)
766 // -> NonFirst,Hidden (if split after start of "outgoing" redline)
767 // -> Hidden, NonFirst (if split before end of "incoming" redline)
768 // None
769 // -> None,None
770 // -> First,NonFirst (if splitting inside a delete redline)
771 SwNode::Merge const eFirst(pNode->GetRedlineMergeFlag());
772 SwNode::Merge const eSecond(GetRedlineMergeFlag());
773 switch (eOldMergeFlag)
774 {
775 case Merge::First:
776 assert((eFirst == Merge::First && eSecond == Merge::NonFirst)
777 || (eFirst == Merge::First && eSecond == Merge::Hidden)
778 || (eFirst == Merge::None && eSecond == Merge::First));
779 break;
780 case Merge::Hidden:
781 assert((eFirst == Merge::Hidden && eSecond == Merge::Hidden)
782 || (eFirst == Merge::NonFirst && eSecond == Merge::First)
783 // next ones can happen temp. in UndoDelete :(
784 || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)
785 || (eFirst == Merge::NonFirst && eSecond == Merge::None));
786 break;
787 case Merge::NonFirst:
788 assert((eFirst == Merge::NonFirst && eSecond == Merge::First)
789 || (eFirst == Merge::NonFirst && eSecond == Merge::None)
790 || (eFirst == Merge::NonFirst && eSecond == Merge::Hidden)
791 || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst));
792 break;
793 case Merge::None:
794 assert((eFirst == Merge::None && eSecond == Merge::None)
795 || (eFirst == Merge::First && eSecond == Merge::NonFirst));
796 break;
797 }
798 }
799#else
800 (void) isHide;
801#endif
802
803 {
804 // Send Hint for PageDesc. This should be done in the Layout when
805 // pasting the frames, but that causes other problems that look
806 // expensive to solve.
807 const SwFormatPageDesc *pItem;
808 if(HasWriterListeners() && (pItem = pNode->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC)))
809 pNode->TriggerNodeUpdate(sw::LegacyModifyHint(pItem, pItem));
810 }
811 return pNode;
812}
813
815{
816 OSL_ENSURE( m_pSwpHints, "MoveTextAttr_To_AttrSet without SwpHints?" );
817 for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
818 {
819 SwTextAttr *pHt = m_pSwpHints->Get(i);
820
821 if( pHt->GetStart() )
822 break;
823
824 const sal_Int32* pHtEndIdx = pHt->GetEnd();
825
826 if( !pHtEndIdx )
827 continue;
828
829 if (*pHtEndIdx < m_Text.getLength() || pHt->IsCharFormatAttr())
830 break;
831
832 if( !pHt->IsDontMoveAttr() &&
833 SetAttr( pHt->GetAttr() ) )
834 {
835 m_pSwpHints->DeleteAtPos(i);
836 DestroyAttr( pHt );
837 --i;
838 }
839 }
840
841}
842
843namespace sw {
844
848void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode)
849{
850 std::vector<SwTextFrame*> frames;
852 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
853 {
854 if (pFrame->getRootFrame()->HasMergedParas())
855 {
856 frames.push_back(pFrame);
857 }
858 }
859 {
860 auto frames2(frames);
862 for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
863 {
864 if (pFrame->getRootFrame()->HasMergedParas())
865 {
866 auto const it(std::find(frames2.begin(), frames2.end(), pFrame));
867 assert(it != frames2.end());
868 frames2.erase(it);
869 }
870 }
871 assert(frames2.empty());
872 }
873 for (SwTextFrame *const pFrame : frames)
874 {
875 pFrame->RegisterToNode(rNode, true);
876 }
877}
878
879// typical Join:
880// None,Node->None
881// None,First->First
882// First,NonFirst->First
883// NonFirst,First->NonFirst
884// NonFirst,None->NonFirst
885
888void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerged)
889{
890 if (eRecreateMerged != sw::Recreate::No)
891 {
892 SwTextNode * pMergeNode(&rNode);
893 if (eRecreateMerged == sw::Recreate::Predecessor
894 // tdf#135018 check that there is a predecessor node, i.e. rNode
895 // isn't the first node after the body start node
896 && rNode.GetNodes()[rNode.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
897 {
898 for (SwNodeOffset i = rNode.GetIndex() - 1; ; --i)
899 {
900 SwNode *const pNode(rNode.GetNodes()[i]);
901 assert(!pNode->IsStartNode());
902 if (pNode->IsEndNode())
903 {
904 i = pNode->StartOfSectionIndex();
905 }
906 else if (pNode->IsTextNode())
907 {
908 pMergeNode = pNode->GetTextNode(); // use predecessor to merge
909 break;
910 }
911 }
912 }
913 std::vector<SwTextFrame*> frames;
915 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
916 {
917 if (pFrame->getRootFrame()->HasMergedParas())
918 {
919 frames.push_back(pFrame);
920 }
921 }
923 for (SwTextFrame * pFrame : frames)
924 {
925 SwTextNode & rFirstNode(pFrame->GetMergedPara()
926 ? *pFrame->GetMergedPara()->pFirstNode
927 : *pMergeNode);
928 assert(rFirstNode.GetIndex() <= rNode.GetIndex());
929 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
930 *pFrame, rFirstNode, eMode));
931 // there is no merged para in case the deleted node had one but
932 // nothing was actually hidden
933 if (pFrame->GetMergedPara())
934 {
935 assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
936 assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
937 // tdf#135978 Join: recreate fly frames anchored to subsequent nodes
938 if (eRecreateMerged == sw::Recreate::ThisNode)
939 {
940 AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr);
941 }
942 }
943 eMode = sw::FrameMode::New; // Existing is not idempotent!
944 }
945 }
946 else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
947 {
949 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
950 {
951 if (auto const pMergedPara = pFrame->GetMergedPara())
952 {
953 if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
954 {
955 assert(pMergedPara->pFirstNode == &rNode);
957 }
958 break; // checking once is enough
959 }
960 else if (pFrame->getRootFrame()->HasMergedParas())
961 {
963 break; // checking once is enough
964 }
965 }
966 }
967}
968
970{
971 const SwNodeNum* pNodeNum = rTextNode.GetNum();
972 if (!pNodeNum)
973 {
974 return false;
975 }
976
977 const SwNumRule* pNumRule = pNodeNum->GetNumRule();
978 if (!pNumRule)
979 {
980 return false;
981 }
982
983 const SwNumFormat* pFormat
984 = pNumRule->GetNumFormat(o3tl::narrowing<sal_uInt16>(rTextNode.GetAttrListLevel()));
985 if (!pFormat)
986 {
987 return false;
988 }
989
990 switch (pFormat->GetNumberingType())
991 {
994 case SVX_NUM_BITMAP:
995 return false;
996 default:
997 return true;
998 }
999}
1000} // namespace
1001
1003{
1004 SwNodes& rNds = GetNodes();
1005 SwNodeIndex aIdx( *this );
1006 if( SwContentNode::CanJoinNext( &aIdx ) )
1007 {
1008 SwDoc& rDoc = rNds.GetDoc();
1009 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1010 pContentStore->Save(rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
1011 SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
1012 sal_Int32 nOldLen = m_Text.getLength();
1013
1014 // METADATA: merge
1015 JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len());
1016
1017 std::unique_ptr<SwWrongList> pList = ReleaseWrong();
1018 if( pList )
1019 {
1020 pList->JoinList( pTextNode->GetWrong(), nOldLen );
1022 }
1023 else
1024 {
1025 pList = pTextNode->ReleaseWrong();
1026 if( pList )
1027 {
1028 pList->Move( 0, nOldLen );
1030 }
1031 }
1032
1033 std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
1034 if( pList3 )
1035 {
1036 pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen );
1037 SetGrammarCheckDirty( true );
1038 }
1039 else
1040 {
1041 pList3 = pTextNode->ReleaseGrammarCheck();
1042 if( pList3 )
1043 {
1044 pList3->MoveGrammar( 0, nOldLen );
1045 SetGrammarCheckDirty( true );
1046 }
1047 }
1048
1049 std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
1050 if( pList2 )
1051 {
1052 pList2->JoinList( pTextNode->GetSmartTags(), nOldLen );
1053 SetSmartTagDirty( true );
1054 }
1055 else
1056 {
1057 pList2 = pTextNode->ReleaseSmartTags();
1058 if( pList2 )
1059 {
1060 pList2->Move( 0, nOldLen );
1061 SetSmartTagDirty( true );
1062 }
1063 }
1064
1065 { // scope for SwContentIndex
1066 pTextNode->CutText( this, SwContentIndex(pTextNode), pTextNode->Len() );
1067 }
1068 // move all Bookmarks/TOXMarks
1069 if( !pContentStore->Empty())
1070 pContentStore->Restore( rDoc, GetIndex(), nOldLen );
1071
1072 if( pTextNode->HasAnyIndex() )
1073 {
1074 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1075 rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nOldLen, true );
1076 }
1077 SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1078 auto eRecreateMerged(eOldMergeFlag == SwNode::Merge::First
1081 if (eRecreateMerged == sw::Recreate::No)
1082 {
1083 // tdf#137318 if a delete is inside one node, flag is still None!
1085 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
1086 {
1087 if (pFrame->GetMergedPara())
1088 {
1089 eRecreateMerged = sw::Recreate::ThisNode;
1090 break;
1091 }
1092 }
1093 }
1094 bool bOldHasNumberingWhichNeedsLayoutUpdate = HasNumberingWhichNeedsLayoutUpdate(*pTextNode);
1095
1096 rNds.Delete(aIdx);
1097 SetWrong( std::move(pList) );
1098 SetGrammarCheck( std::move(pList3) );
1099 SetSmartTags( std::move(pList2) );
1100
1102
1103 if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
1104 {
1105 // Repaint all text frames that belong to this numbering to avoid outdated generated
1106 // numbers.
1108 }
1109
1110 CheckResetRedlineMergeFlag(*this, eRecreateMerged);
1111 }
1112 else {
1113 OSL_FAIL( "No TextNode." );
1114 }
1115
1116 return this;
1117}
1118
1120{
1121 SwNodes& rNds = GetNodes();
1122 SwNodeIndex aIdx( *this );
1123 if( SwContentNode::CanJoinPrev( &aIdx ) )
1124 {
1125 SwDoc& rDoc = rNds.GetDoc();
1126 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1127 pContentStore->Save( rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
1128 SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
1129 const sal_Int32 nLen = pTextNode->Len();
1130
1131 std::unique_ptr<SwWrongList> pList = pTextNode->ReleaseWrong();
1132 if( pList )
1133 {
1134 pList->JoinList( GetWrong(), Len() );
1136 ClearWrong();
1137 }
1138 else
1139 {
1140 pList = ReleaseWrong();
1141 if( pList )
1142 {
1143 pList->Move( 0, nLen );
1145 }
1146 }
1147
1148 std::unique_ptr<SwGrammarMarkUp> pList3 = pTextNode->ReleaseGrammarCheck();
1149 if( pList3 )
1150 {
1151 pList3->JoinGrammarList( GetGrammarCheck(), Len() );
1152 SetGrammarCheckDirty( true );
1154 }
1155 else
1156 {
1157 pList3 = ReleaseGrammarCheck();
1158 if( pList3 )
1159 {
1160 pList3->MoveGrammar( 0, nLen );
1161 SetGrammarCheckDirty( true );
1162 }
1163 }
1164
1165 std::unique_ptr<SwWrongList> pList2 = pTextNode->ReleaseSmartTags();
1166 if( pList2 )
1167 {
1168 pList2->JoinList( GetSmartTags(), Len() );
1169 SetSmartTagDirty( true );
1171 }
1172 else
1173 {
1174 pList2 = ReleaseSmartTags();
1175 if( pList2 )
1176 {
1177 pList2->Move( 0, nLen );
1178 SetSmartTagDirty( true );
1179 }
1180 }
1181
1182 { // scope for SwContentIndex
1183 pTextNode->CutText( this, SwContentIndex(this), SwContentIndex(pTextNode), nLen );
1184 }
1185 // move all Bookmarks/TOXMarks
1186 if( !pContentStore->Empty() )
1187 pContentStore->Restore( rDoc, GetIndex() );
1188
1189 if( pTextNode->HasAnyIndex() )
1190 {
1191 // move all ShellCursor/StackCursor/UnoCursor out of delete range
1192 rDoc.CorrAbs( aIdx.GetNode(), SwPosition( *this ), nLen, true );
1193 }
1194 SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1195 if (eOldMergeFlag == SwNode::Merge::First
1197 {
1198 sw::MoveDeletedPrevFrames(*pTextNode, *this);
1199 }
1200 rNds.Delete(aIdx);
1201 SetWrong( std::move(pList) );
1202 SetGrammarCheck( std::move(pList3) );
1203 SetSmartTags( std::move(pList2) );
1207 eOldMergeFlag == SwNode::Merge::NonFirst
1210 }
1211 else {
1212 OSL_FAIL( "No TextNode." );
1213 }
1214}
1215
1216// create an AttrSet with ranges for Frame-/Para/Char-attributes
1218{
1219 OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
1220 SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );
1221
1222 // put names of parent style and conditional style:
1223 const SwFormatColl* pAnyFormatColl = &GetAnyFormatColl();
1224 const SwFormatColl* pFormatColl = GetFormatColl();
1225 OUString sVal;
1227 SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
1228 if ( pFormatColl != pAnyFormatColl )
1231 aNewAttrSet.Put( aAnyFormatColl );
1232 aNewAttrSet.Put( aFormatColl );
1233
1234 aNewAttrSet.SetParent( &pAnyFormatColl->GetAttrSet() );
1236}
1237
1238namespace
1239{
1240class SwContentNodeTmp : public SwContentNode
1241{
1242public:
1243 SwContentNodeTmp() : SwContentNode() {}
1244 virtual void NewAttrSet(SwAttrPool&) override {}
1245 virtual SwContentFrame *MakeFrame(SwFrame*) override { return nullptr; }
1246 virtual SwContentNode* MakeCopy(SwDoc&, SwNode&, bool /*bNewFrames*/) const override { return nullptr; };
1247};
1248};
1249
1250// override SwContentIndexReg::Update => text hints do not need SwContentIndex for start/end!
1252 SwContentIndex const & rPos,
1253 const sal_Int32 nChangeLen,
1254 UpdateMode const eMode)
1255{
1256 assert(rPos.GetContentNode() == this);
1258
1259 std::unique_ptr<SwpHts> pCollector;
1260 const sal_Int32 nChangePos = rPos.GetIndex();
1261
1262 if ( HasHints() )
1263 {
1264 if (eMode & UpdateMode::Negative)
1265 {
1266 std::vector<SwTextInputField*> aTextInputFields;
1267
1268 const sal_Int32 nChangeEnd = nChangePos + nChangeLen;
1269 for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1270 {
1271 bool bTextAttrChanged = false;
1272 bool bStartOfTextAttrChanged = false;
1273 SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1274 if ( pHint->GetStart() > nChangePos )
1275 {
1276 if ( pHint->GetStart() > nChangeEnd )
1277 {
1278 pHint->SetStart( pHint->GetStart() - nChangeLen );
1279 }
1280 else
1281 {
1282 pHint->SetStart( nChangePos );
1283 }
1284 bStartOfTextAttrChanged = true;
1285 }
1286
1287 const sal_Int32 * const pEnd = pHint->GetEnd();
1288 if (pEnd && *pEnd > nChangePos )
1289 {
1290 if( *pEnd > nChangeEnd )
1291 {
1292 pHint->SetEnd(*pEnd - nChangeLen);
1293 }
1294 else
1295 {
1296 pHint->SetEnd(nChangePos);
1297 }
1298 bTextAttrChanged = !bStartOfTextAttrChanged;
1299 }
1300
1301 if ( bTextAttrChanged
1302 && pHint->Which() == RES_TXTATR_INPUTFIELD )
1303 {
1304 SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1305 if ( pTextInputField )
1306 aTextInputFields.push_back(pTextInputField);
1307 }
1308 }
1309
1310 //wait until all the attribute positions are correct
1311 //before updating the field contents
1312 for (SwTextInputField* pTextInputField : aTextInputFields)
1313 {
1314 pTextInputField->UpdateFieldContent();
1315 }
1316
1317 m_pSwpHints->MergePortions( *this );
1318 }
1319 else
1320 {
1321 bool bNoExp = false;
1322 bool bResort = false;
1323 bool bMergePortionsNeeded = false;
1324 const int coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
1325 std::vector<SwTextInputField*> aTextInputFields;
1326
1327 bool aDontExp[ coArrSz ] = {};
1328
1329 for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1330 {
1331 bool bTextAttrChanged = false;
1332 SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1333 const sal_Int32 * const pEnd = pHint->GetEnd();
1334 if ( pHint->GetStart() >= nChangePos )
1335 {
1336 pHint->SetStart( pHint->GetStart() + nChangeLen );
1337 if ( pEnd )
1338 {
1339 pHint->SetEnd(*pEnd + nChangeLen);
1340 }
1341 }
1342 else if ( pEnd && (*pEnd >= nChangePos) )
1343 {
1344 if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
1345 {
1346 pHint->SetEnd(*pEnd + nChangeLen);
1347 bTextAttrChanged = true;
1348 }
1349 else // *pEnd == nChangePos
1350 {
1351 const sal_uInt16 nWhich = pHint->Which();
1352
1353 OSL_ENSURE(!isCHRATR(nWhich), "Update: char attr hint?");
1354 if (!(isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)))
1355 continue;
1356
1357 const sal_uInt16 nWhPos = nWhich - RES_CHRATR_BEGIN;
1358
1359 if( aDontExp[ nWhPos ] )
1360 continue;
1361
1362 if ( pHint->DontExpand() )
1363 {
1364 pHint->SetDontExpand( false );
1365 bResort = true;
1366 // could have a continuation with IgnoreStart()...
1367 if (pHint->IsFormatIgnoreEnd())
1368 {
1369 bMergePortionsNeeded = true;
1370 }
1371 if ( pHint->IsCharFormatAttr() )
1372 {
1373 bNoExp = true;
1374 aDontExp[ RES_TXTATR_CHARFMT - RES_CHRATR_BEGIN ] = true;
1375 aDontExp[ RES_TXTATR_INETFMT - RES_CHRATR_BEGIN ] = true;
1376 }
1377 else
1378 aDontExp[ nWhPos ] = true;
1379 }
1380 else if( bNoExp )
1381 {
1382 if (!pCollector)
1383 {
1384 pCollector.reset( new SwpHts );
1385 }
1386 auto it = std::find_if(pCollector->begin(), pCollector->end(),
1387 [nWhich](const SwTextAttr *pTmp) { return nWhich == pTmp->Which(); });
1388 if (it != pCollector->end())
1389 {
1390 SwTextAttr *pTmp = *it;
1391 pCollector->erase( it );
1392 SwTextAttr::Destroy( pTmp, GetDoc().GetAttrPool() );
1393 }
1394 SwTextAttr * const pTmp =
1396 pHint->GetAttr(), nChangePos, nChangePos + nChangeLen);
1397 pCollector->push_back( pTmp );
1398 }
1399 else
1400 {
1401 pHint->SetEnd(*pEnd + nChangeLen);
1402 bTextAttrChanged = true;
1403 }
1404 }
1405 }
1406
1407 if ( bTextAttrChanged
1408 && pHint->Which() == RES_TXTATR_INPUTFIELD )
1409 {
1410 SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1411 if ( pTextInputField )
1412 aTextInputFields.push_back(pTextInputField);
1413 }
1414 }
1415
1416 //wait until all the attribute positions are correct
1417 //before updating the field contents
1418 for (SwTextInputField* pTextInputField : aTextInputFields)
1419 {
1420 pTextInputField->UpdateFieldContent();
1421 }
1422
1423 if (bMergePortionsNeeded)
1424 {
1425 m_pSwpHints->MergePortions(*this); // does Resort too
1426 }
1427 else if (bResort)
1428 {
1429 m_pSwpHints->Resort();
1430 }
1431 }
1432 }
1433
1434 bool bSortMarks = false;
1435 SwContentNodeTmp aTmpIdxReg;
1436 if (!(eMode & UpdateMode::Negative) && !(eMode & UpdateMode::Delete))
1437 {
1438 std::vector<SwRangeRedline*> vMyRedlines;
1439 // walk the list of SwIndex attached to me and see if any of them are redlines
1440 const SwContentIndex* pContentNodeIndex = GetFirstIndex();
1441 while (pContentNodeIndex)
1442 {
1443 SwRangeRedline* pRedl = pContentNodeIndex->GetRedline();
1444 if (pRedl)
1445 vMyRedlines.push_back(pRedl);
1446 pContentNodeIndex = pContentNodeIndex->GetNext();
1447 }
1448 std::sort(vMyRedlines.begin(), vMyRedlines.end());
1449 vMyRedlines.erase( std::unique( vMyRedlines.begin(), vMyRedlines.end() ), vMyRedlines.end() );
1450 for (SwRangeRedline* pRedl : vMyRedlines)
1451 {
1452 if ( pRedl->HasMark() )
1453 {
1454 SwPosition* const pEnd = pRedl->End();
1455 if ( *this == pEnd->GetNode() &&
1456 *pRedl->GetPoint() != *pRedl->GetMark() )
1457 {
1458 SwContentIndex & rIdx = pEnd->nContent;
1459 if (nChangePos == rIdx.GetIndex())
1460 {
1461 rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1462 }
1463 }
1464 }
1465 else if ( this == &pRedl->GetPoint()->GetNode() )
1466 {
1467 SwContentIndex & rIdx = pRedl->GetPoint()->nContent;
1468 if (nChangePos == rIdx.GetIndex())
1469 {
1470 rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1471 }
1472 // the unused position must not be on a SwTextNode
1473 bool const isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
1474 assert(!pRedl->GetBound(!isOneUsed).GetNode().IsTextNode());
1475 assert(!pRedl->GetBound(!isOneUsed).GetContentNode()); (void)isOneUsed;
1476 }
1477 }
1478
1479 // Bookmarks must never grow to either side, when editing (directly)
1480 // to the left or right (i#29942)! Exception: if the bookmark has
1481 // 2 positions and start == end, then expand it (tdf#96479)
1482 if (!(eMode & UpdateMode::Replace)) // Exception: Replace
1483 {
1484 bool bAtLeastOneBookmarkMoved = false;
1485 bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false;
1486 // A text node already knows its marks via its SwContentIndexes.
1488 const SwContentIndex* next;
1489 for (const SwContentIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
1490 {
1491 next = pIndex->GetNext();
1492 const sw::mark::IMark* pMark = pIndex->GetMark();
1493 if (!pMark)
1494 continue;
1495 // Only handle bookmarks once, if they start and end at this node as well.
1496 if (!aSeenMarks.insert(pMark).second)
1497 continue;
1498 const SwPosition* pEnd = &pMark->GetMarkEnd();
1499 SwContentIndex & rEndIdx = const_cast<SwContentIndex&>(pEnd->nContent);
1500 if( *this == pEnd->GetNode() &&
1501 rPos.GetIndex() == rEndIdx.GetIndex() )
1502 {
1503 if (&rEndIdx == next) // nasty corner case:
1504 { // don't switch to iterating aTmpIdxReg!
1505 next = rEndIdx.GetNext();
1506 }
1507 // tdf#96479: if start == end, ignore the other position
1508 // so it is moved!
1509 rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
1510 bAtLeastOneBookmarkMoved = true;
1511 }
1512 else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
1513 {
1514 if ( pMark->IsExpanded() )
1515 {
1516 const SwPosition* pStart = &pMark->GetMarkStart();
1517 if ( this == &pStart->GetNode()
1518 && rPos.GetIndex() == pStart->GetContentIndex() )
1519 {
1520 bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
1521 }
1522 }
1523 }
1524 }
1525
1526 bSortMarks = bAtLeastOneBookmarkMoved && bAtLeastOneExpandedBookmarkAtInsertionPosition;
1527 }
1528
1529 // at-char anchored flys shouldn't be moved, either.
1530 if (!m_bInUndo)
1531 {
1532 std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys());
1533 for (size_t i = 0; i != rFlys.size(); ++i)
1534 {
1535 SwFrameFormat const*const pFormat = rFlys[i];
1536 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1537 const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
1538 if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pAnchorNode)
1539 {
1540 // The fly is at-char anchored and has an anchor position.
1541 SwContentIndex& rEndIdx = const_cast<SwContentIndex&>(rAnchor.GetContentAnchor()->nContent);
1542 if (*pAnchorNode == *this && rEndIdx.GetIndex() == rPos.GetIndex())
1543 {
1544 // The anchor position is exactly our insert position.
1545 rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
1546 }
1547 }
1548 }
1549 }
1550
1551 // The cursors of other shells shouldn't be moved, either.
1552 if (SwDocShell* pDocShell = GetDoc().GetDocShell())
1553 {
1554 if (pDocShell->GetWrtShell())
1555 {
1556 for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
1557 {
1558 auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell);
1559 if (!pWrtShell || pWrtShell == pDocShell->GetWrtShell())
1560 continue;
1561
1562 SwShellCursor* pCursor = pWrtShell->GetCursor_();
1563 if (!pCursor)
1564 continue;
1565
1566 SwContentIndex& rIndex = pCursor->Start()->nContent;
1567 if (pCursor->Start()->GetNode() == *this && rIndex.GetIndex() == rPos.GetIndex())
1568 {
1569 // The cursor position of this other shell is exactly our insert position.
1570 rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
1571 }
1572 }
1573 }
1574 }
1575 }
1576
1577 // base class
1578 SwContentIndexReg::Update(rPos, nChangeLen, eMode);
1579
1580 if (pCollector)
1581 {
1582 const size_t nCount = pCollector->size();
1583 for ( size_t i = 0; i < nCount; ++i )
1584 {
1585 m_pSwpHints->TryInsertHint( (*pCollector)[ i ], *this );
1586 }
1587 }
1588
1589 aTmpIdxReg.MoveTo( *this );
1590 if ( bSortMarks )
1591 {
1593 }
1594
1595 //Any drawing objects anchored into this text node may be sorted by their
1596 //anchor position which may have changed here, so resort them
1598 for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
1599 {
1600 SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs());
1601 if (pSortedObjs)
1602 {
1603 pSortedObjs->UpdateAll();
1604 }
1605 // also sort the objs on the page frame
1606 pSortedObjs = pFrame->FindPageFrame()->GetSortedObjs();
1607 if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly
1608 {
1609 pSortedObjs->UpdateAll();
1610 }
1611 }
1612
1613 // Update the paragraph signatures.
1614 if (SwEditShell* pEditShell = GetDoc().GetEditShell())
1615 {
1616 pEditShell->ValidateParagraphSignatures(this, true);
1617 }
1618
1619 // Inform LOK clients about change in position of redlines (if any)
1620 // Don't emit notifications during save: redline flags are temporarily changed during save, but
1621 // it's not useful to let clients know about such changes.
1622 if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
1623 return;
1624
1626 for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
1627 {
1628 SwRangeRedline* pRedln = rTable[nRedlnPos];
1629 if (pRedln->HasMark())
1630 {
1631 if (*this == pRedln->End()->GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
1632 {
1633 // Redline is changed only when some change occurs before it
1634 if (nChangePos <= pRedln->Start()->GetContentIndex())
1635 {
1637 }
1638 }
1639 }
1640 else if (this == &pRedln->GetPoint()->GetNode())
1642 }
1643}
1644
1646 const SwTextFormatColl *pNewColl)
1647{
1648 SwDoc& rDoc = GetDoc();
1649 // query the OutlineLevel and if it changed, notify the Nodes-Array!
1650 const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle() ?
1652 const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
1654
1655 if ( MAXLEVEL != nNewLevel && -1 != nNewLevel )
1656 {
1657 SetAttrListLevel(nNewLevel);
1658 }
1659 rDoc.GetNodes().UpdateOutlineNode(*this);
1660
1661 SwNodes& rNds = GetNodes();
1662 // If Level 0 (Chapter), update the footnotes!
1663 if( ( !nNewLevel || !nOldLevel) && !rDoc.GetFootnoteIdxs().empty() &&
1665 rNds.IsDocNodes() )
1666 {
1667 rDoc.GetFootnoteIdxs().UpdateFootnote( *rNds[GetIndex()] );
1668 }
1669
1670 if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
1671 {
1672 // check the condition of the text node again
1673 ChkCondColl();
1674 }
1675}
1676
1677// If positioned exactly at the end of a CharStyle or Hyperlink,
1678// set its DontExpand flag.
1679bool SwTextNode::DontExpandFormat( sal_Int32 nIdx, bool bFlag,
1680 bool bFormatToTextAttributes )
1681{
1682 if (bFormatToTextAttributes && nIdx == m_Text.getLength())
1683 {
1684 FormatToTextAttr( this );
1685 }
1686
1687 bool bRet = false;
1688 if ( HasHints() )
1689 {
1690 m_pSwpHints->SortIfNeedBe();
1691 int nPos = m_pSwpHints->GetLastPosSortedByEnd(nIdx);
1692 for ( ; nPos >= 0; --nPos)
1693 {
1694 SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( nPos );
1695 const sal_Int32 *pEnd = pTmp->GetEnd();
1696 if( !pEnd )
1697 continue;
1698 assert( *pEnd <= nIdx );
1699 if( nIdx != *pEnd )
1700 break;
1701 if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag()
1702 && *pEnd > pTmp->GetStart())
1703 {
1704 bRet = true;
1705 m_pSwpHints->NoteInHistory( pTmp );
1706 pTmp->SetDontExpand( bFlag );
1707 }
1708 }
1709 }
1710 return bRet;
1711}
1712
1713static bool lcl_GetTextAttrDefault(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1714{
1715 return ((nHintStart <= nIndex) && (nIndex < nHintEnd));
1716}
1717static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1718{
1719 return ((nHintStart < nIndex) && (nIndex <= nHintEnd));
1720}
1721static bool lcl_GetTextAttrParent(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1722{
1723 return ((nHintStart < nIndex) && (nIndex < nHintEnd));
1724}
1725
1726static void
1728 std::vector<SwTextAttr *> *const pVector,
1729 SwTextAttr **const ppTextAttr,
1730 SwpHints const *const pSwpHints,
1731 sal_Int32 const nIndex, sal_uInt16 const nWhich,
1732 ::sw::GetTextAttrMode const eMode)
1733{
1734 assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1735 if (!pSwpHints)
1736 return;
1737 size_t const nSize = pSwpHints->Count();
1738 sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
1739 bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
1740 switch (eMode)
1741 {
1742 case ::sw::GetTextAttrMode::Default: pMatchFunc = &lcl_GetTextAttrDefault;
1743 break;
1744 case ::sw::GetTextAttrMode::Expand: pMatchFunc = &lcl_GetTextAttrExpand;
1745 break;
1746 case ::sw::GetTextAttrMode::Parent: pMatchFunc = &lcl_GetTextAttrParent;
1747 break;
1748 default: assert(false);
1749 }
1750
1751 for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
1752 {
1753 SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i);
1754 if (pHint->Which() != nWhich)
1755 break; // hints are sorted by which&start, so we are done...
1756
1757 sal_Int32 const nHintStart = pHint->GetStart();
1758 if (nIndex < nHintStart)
1759 break; // hints are sorted by which&start, so we are done...
1760
1761 sal_Int32 const*const pEndIdx = pHint->GetEnd();
1762 // cannot have hint with no end and no dummy char
1763 assert(pEndIdx || pHint->HasDummyChar());
1764 // If EXPAND is set, simulate the text input behavior, i.e.
1765 // move the start, and expand the end.
1766 bool const bContained( pEndIdx
1767 ? (*pMatchFunc)(nIndex, nHintStart, *pEndIdx)
1768 : (nHintStart == nIndex) );
1769 if (bContained)
1770 {
1771 if (pVector)
1772 {
1773 if (nPreviousIndex < nHintStart)
1774 {
1775 pVector->clear(); // clear hints that are outside pHint
1776 nPreviousIndex = nHintStart;
1777 }
1778 pVector->push_back(pHint);
1779 }
1780 else
1781 {
1782 *ppTextAttr = pHint; // and possibly overwrite outer hint
1783 }
1784 if (!pEndIdx)
1785 {
1786 break;
1787 }
1788 }
1789 }
1790}
1791
1792std::vector<SwTextAttr *>
1793SwTextNode::GetTextAttrsAt(sal_Int32 const nIndex, sal_uInt16 const nWhich) const
1794{
1795 assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1796 std::vector<SwTextAttr *> ret;
1797 lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, ::sw::GetTextAttrMode::Default);
1798 return ret;
1799}
1800
1801SwTextAttr *
1802SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich,
1803 ::sw::GetTextAttrMode const eMode) const
1804{
1805 assert( (nWhich == RES_TXTATR_META)
1806 || (nWhich == RES_TXTATR_METAFIELD)
1807 || (nWhich == RES_TXTATR_AUTOFMT)
1808 || (nWhich == RES_TXTATR_INETFMT)
1809 || (nWhich == RES_TXTATR_CJK_RUBY)
1810 || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
1811 || (nWhich == RES_TXTATR_CONTENTCONTROL)
1812 || (nWhich == RES_TXTATR_INPUTFIELD ) );
1813 // "GetTextAttrAt() will give wrong result for this hint!")
1814
1815 SwTextAttr * pRet(nullptr);
1816 lcl_GetTextAttrs(nullptr, & pRet, m_pSwpHints.get(), nIndex, nWhich, eMode);
1817 return pRet;
1818}
1819
1821{
1822 const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent));
1823
1824 if ( pTextInputField == nullptr && rTextAttr.End() != nullptr )
1825 {
1826 pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(*(rTextAttr.End()), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent));
1827 }
1828
1829 return pTextInputField;
1830}
1831
1833{
1834 SetWrong( nullptr );
1836
1837 SetGrammarCheck( nullptr );
1838 SetGrammarCheckDirty( true );
1839
1840 SetSmartTags( nullptr );
1841 SetSmartTagDirty( true );
1842
1843 SetWordCountDirty( true );
1845}
1846
1848 const sal_Int32 nIndex,
1849 ::sw::GetTextAttrMode const eMode) const
1850{
1851 SwTextField* pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_FIELD ));
1852 if ( pTextField == nullptr )
1853 {
1854 pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_ANNOTATION ));
1855 }
1856 if ( pTextField == nullptr )
1857 {
1858 pTextField =
1859 dynamic_cast<SwTextField*>( GetTextAttrAt(
1860 nIndex,
1862 eMode));
1863 }
1864
1865 return pTextField;
1866}
1867
1868static SwCharFormat* lcl_FindCharFormat( const SwCharFormats* pCharFormats, std::u16string_view rName )
1869{
1870 if( !rName.empty() )
1871 {
1872 const size_t nArrLen = pCharFormats->size();
1873 for( size_t i = 1; i < nArrLen; i++ )
1874 {
1875 SwCharFormat* pFormat = (*pCharFormats)[ i ];
1876 if( pFormat->GetName()==rName )
1877 return pFormat;
1878 }
1879 }
1880 return nullptr;
1881}
1882
1883static void lcl_CopyHint(
1884 const sal_uInt16 nWhich,
1885 const SwTextAttr * const pHt,
1886 SwTextAttr *const pNewHt,
1887 SwDoc *const pOtherDoc,
1888 SwTextNode *const pDest )
1889{
1890 assert(nWhich == pHt->Which()); // wrong hint-id
1891 switch( nWhich )
1892 {
1893 // copy nodesarray section with footnote content
1894 case RES_TXTATR_FTN :
1895 assert(pDest); // "lcl_CopyHint: no destination text node?"
1896 static_cast<const SwTextFootnote*>(pHt)->CopyFootnote( *static_cast<SwTextFootnote*>(pNewHt), *pDest);
1897 break;
1898
1899 // Fields that are copied into different SwDocs must be registered
1900 // at their new FieldTypes.
1901
1902 case RES_TXTATR_FIELD :
1903 {
1904 if( pOtherDoc != nullptr )
1905 {
1906 static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1907 static_txtattr_cast<SwTextField*>(pNewHt));
1908 }
1909
1910 // Table Formula must be copied relative.
1911 const SwFormatField& rField = pHt->GetFormatField();
1912 if( SwFieldIds::Table == rField.GetField()->GetTyp()->Which()
1913 && static_cast<const SwTableField*>(rField.GetField())->IsIntrnlName())
1914 {
1915 // convert internal formula to external
1916 const SwTableNode* const pDstTableNd =
1917 static_txtattr_cast<const SwTextField*>(pHt)->GetTextNode().FindTableNode();
1918 if( pDstTableNd )
1919 {
1920 SwTableField* const pTableField =
1921 const_cast<SwTableField*>(static_cast<const SwTableField*>(
1922 pNewHt->GetFormatField().GetField()));
1923 pTableField->PtrToBoxNm( &pDstTableNd->GetTable() );
1924 }
1925 }
1926 }
1927 break;
1928
1931 if( pOtherDoc != nullptr )
1932 {
1933 static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1934 static_txtattr_cast<SwTextField*>(pNewHt));
1935 }
1936 break;
1937
1938 case RES_TXTATR_TOXMARK :
1939 if( pOtherDoc && pDest && pDest->GetpSwpHints()
1940 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1941 {
1942 // ToXMarks that are copied to different SwDocs must register
1943 // at their new ToX (sw::BroadcastingModify).
1944 static_txtattr_cast<SwTextTOXMark*>(pNewHt)->CopyTOXMark(*pOtherDoc);
1945 }
1946 break;
1947
1948 case RES_TXTATR_CHARFMT :
1949 // For CharacterStyles, the format must be copied too.
1950 if( pDest && pDest->GetpSwpHints()
1951 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1952 {
1953 SwCharFormat* pFormat = pHt->GetCharFormat().GetCharFormat();
1954
1955 if (pOtherDoc)
1956 {
1957 pFormat = pOtherDoc->CopyCharFormat( *pFormat );
1958 }
1959 const_cast<SwFormatCharFormat&>(
1960 pNewHt->GetCharFormat() ).SetCharFormat( pFormat );
1961 }
1962 break;
1963 case RES_TXTATR_INETFMT :
1964 {
1965 // For Hyperlinks, the format must be copied too.
1966 if( pOtherDoc && pDest && pDest->GetpSwpHints()
1967 && pDest->GetpSwpHints()->Contains( pNewHt ) )
1968 {
1969 const SwDoc& rDoc = static_txtattr_cast<
1970 const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc();
1971 const SwCharFormats* pCharFormats = rDoc.GetCharFormats();
1972 const SwFormatINetFormat& rFormat = pHt->GetINetFormat();
1973 SwCharFormat* pFormat;
1974 pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetINetFormat() );
1975 if( pFormat )
1976 pOtherDoc->CopyCharFormat( *pFormat );
1977 pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetVisitedFormat() );
1978 if( pFormat )
1979 pOtherDoc->CopyCharFormat( *pFormat );
1980 }
1981 //JP 24.04.98: The attribute must point to a text node, so that
1982 // the styles can be created.
1983 SwTextINetFormat *const pINetHt = static_txtattr_cast<SwTextINetFormat*>(pNewHt);
1984 if ( !pINetHt->GetpTextNode() )
1985 {
1986 pINetHt->ChgTextNode( pDest );
1987 }
1988
1989 //JP 22.10.97: set up link to char style
1990 pINetHt->GetCharFormat();
1991 break;
1992 }
1993 case RES_TXTATR_META:
1995 OSL_ENSURE( pNewHt, "copying Meta should not fail!" );
1996 OSL_ENSURE( pDest
1997 && (CH_TXTATR_INWORD == pDest->GetText()[pNewHt->GetStart()]),
1998 "missing CH_TXTATR?");
1999 break;
2000 }
2001}
2002
2004// BP 7.6.93: Intentionally copy only attributes _with_ EndIdx!
2005// CopyAttr is usually called when attributes are set on a
2006// node with no text.
2007void SwTextNode::CopyAttr( SwTextNode *pDest, const sal_Int32 nTextStartIdx,
2008 const sal_Int32 nOldPos )
2009{
2010 if ( HasHints() )
2011 {
2012 SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ?
2013 &pDest->GetDoc() : nullptr;
2014
2015 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
2016 {
2017 SwTextAttr *const pHt = m_pSwpHints->Get(i);
2018 sal_Int32 const nAttrStartIdx = pHt->GetStart();
2019 if ( nTextStartIdx < nAttrStartIdx )
2020 break; // beyond end of text, because nLen == 0
2021
2022 const sal_Int32 *const pEndIdx = pHt->GetEnd();
2023 if ( pEndIdx && !pHt->HasDummyChar() )
2024 {
2025 sal_uInt16 const nWhich = pHt->Which();
2026 if (RES_TXTATR_INPUTFIELD != nWhich // fdo#74981 skip fields
2027 && ( *pEndIdx > nTextStartIdx
2028 || (*pEndIdx == nTextStartIdx
2029 && nAttrStartIdx == nTextStartIdx)))
2030 {
2031 if ( RES_TXTATR_REFMARK != nWhich )
2032 {
2033 // attribute in the area => copy
2034 SwTextAttr *const pNewHt =
2035 pDest->InsertItem( pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
2036 if ( pNewHt )
2037 {
2038 lcl_CopyHint( nWhich, pHt, pNewHt,
2039 pOtherDoc, pDest );
2040 }
2041 }
2042 else if( !pOtherDoc
2043 ? GetDoc().IsCopyIsMove()
2044 : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) )
2045 {
2046 pDest->InsertItem(
2047 pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
2048 }
2049 }
2050 }
2051 }
2052 }
2053
2054 if( this != pDest )
2055 {
2056 // notify layout frames, to prevent disappearance of footnote numbers
2057 SwUpdateAttr aHint(
2058 nOldPos,
2059 nOldPos,
2060 0);
2061
2062 pDest->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
2063 }
2064}
2065
2068 const SwContentIndex &rStart,
2069 const sal_Int32 nLen,
2070 const bool bForceCopyOfAllAttrs )
2071{
2072 SwContentIndex const aIdx( pDest, pDest->m_Text.getLength() );
2073 CopyText( pDest, aIdx, rStart, nLen, bForceCopyOfAllAttrs );
2074}
2075
2077 const SwContentIndex &rDestStart,
2078 const SwPosition &rStart,
2079 sal_Int32 nLen,
2080 const bool bForceCopyOfAllAttrs )
2081{
2082 CopyText( pDest, rDestStart, rStart.nContent, nLen, bForceCopyOfAllAttrs );
2083}
2084
2086 const SwContentIndex &rDestStart,
2087 const SwContentIndex &rStart,
2088 sal_Int32 nLen,
2089 const bool bForceCopyOfAllAttrs )
2090{
2092 CHECK_SWPHINTS(pDest);
2093 assert(rDestStart.GetContentNode() == pDest);
2094 assert(rStart.GetContentNode() == this);
2095 sal_Int32 nTextStartIdx = rStart.GetIndex();
2096 sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
2097
2098 if (pDest->GetDoc().IsClipBoard() && GetNum())
2099 {
2100 // #i111677# cache expansion of source (for clipboard)
2101 pDest->m_oNumStringCache = (nTextStartIdx != 0)
2102 ? OUString() // fdo#49076: numbering only if copy from para start
2103 : GetNumString();
2104 }
2105
2106 if( !nLen )
2107 {
2108 // if no length is given, copy attributes at position rStart
2109 CopyAttr( pDest, nTextStartIdx, nDestStart );
2110
2111 // copy hard attributes on whole paragraph
2112 if( HasSwAttrSet() )
2113 {
2114 // i#96213 all or just the Char attributes?
2115 if ( !bForceCopyOfAllAttrs &&
2116 ( nDestStart ||
2117 pDest->HasSwAttrSet() ||
2118 nLen != pDest->GetText().getLength()))
2119 {
2124 aCharSet( pDest->GetDoc().GetAttrPool() );
2125 aCharSet.Put( *GetpSwAttrSet() );
2126 if( aCharSet.Count() )
2127 {
2128 pDest->SetAttr( aCharSet, nDestStart, nDestStart );
2129 }
2130 }
2131 else
2132 {
2133 GetpSwAttrSet()->CopyToModify( *pDest );
2134 }
2135 }
2136 return;
2137 }
2138
2139 // 1. copy text
2140 const sal_Int32 oldLen = pDest->m_Text.getLength();
2141 // JP 15.02.96: missing attribute handling at the end!
2142 // hence call InsertText and don't modify m_Text directly
2143 pDest->InsertText( m_Text.copy(nTextStartIdx, nLen), rDestStart,
2145
2146 // update with actual new size
2147 nLen = pDest->m_Text.getLength() - oldLen;
2148 if ( !nLen ) // string not longer?
2149 return;
2150
2151 SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ? &pDest->GetDoc() : nullptr;
2152
2153 // copy hard attributes on whole paragraph
2154 if( HasSwAttrSet() )
2155 {
2156 // i#96213 all or just the Char attributes?
2157 if ( !bForceCopyOfAllAttrs &&
2158 ( nDestStart ||
2159 pDest->HasSwAttrSet() ||
2160 nLen != pDest->GetText().getLength()))
2161 {
2166 aCharSet( pDest->GetDoc().GetAttrPool() );
2167 aCharSet.Put( *GetpSwAttrSet() );
2168 if( aCharSet.Count() )
2169 {
2170 pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2171 }
2172 }
2173 else
2174 {
2175 GetpSwAttrSet()->CopyToModify( *pDest );
2176 }
2177 }
2178
2179 bool const bUndoNodes = !pOtherDoc
2180 && GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2181
2182 // Fetch end only now, because copying into self updates the start index
2183 // and all attributes
2184 nTextStartIdx = rStart.GetIndex();
2185 const sal_Int32 nEnd = nTextStartIdx + nLen;
2186
2187 // 2. copy attributes
2188 // Iterate over attribute array until the start of the attribute
2189 // is behind the copied range
2190 const size_t nSize = m_pSwpHints ? m_pSwpHints->Count() : 0;
2191
2192 // If copying into self, inserting can delete attributes!
2193 // Hence first copy into temp-array, and then move that into hints array.
2194 SwpHts aArr;
2195
2196 // Del-Array for all RefMarks without extent
2197 SwpHts aRefMrkArr;
2198
2199 std::vector<std::pair<sal_Int32, sal_Int32>> metaFieldRanges;
2200 sal_Int32 nDeletedDummyChars(0);
2201 for (size_t n = 0; n < nSize; ++n)
2202 {
2203 SwTextAttr * const pHt = m_pSwpHints->Get(n);
2204
2205 const sal_Int32 nAttrStartIdx = pHt->GetStart();
2206 if ( nAttrStartIdx >= nEnd )
2207 break;
2208
2209 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2210 const sal_uInt16 nWhich = pHt->Which();
2211
2212 // JP 26.04.94: RefMarks are never copied. If the refmark doesn't have
2213 // an extent, there is a dummy char in the text, which
2214 // must be removed. So we first copy the attribute,
2215 // but remember it, and when we're done delete it,
2216 // which also deletes the dummy character!
2217 // JP 14.08.95: May RefMarks be moved?
2218 const bool bCopyRefMark = RES_TXTATR_REFMARK == nWhich
2219 && ( bUndoNodes
2220 || ( !pOtherDoc
2221 ? GetDoc().IsCopyIsMove()
2222 : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) ) );
2223
2224 if ( pEndIdx
2225 && RES_TXTATR_REFMARK == nWhich
2226 && !bCopyRefMark )
2227 {
2228 continue;
2229 }
2230
2231 // Input Fields are only copied, if completely covered by copied text
2232 if ( nWhich == RES_TXTATR_INPUTFIELD )
2233 {
2234 assert(pEndIdx != nullptr &&
2235 "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" );
2236 if ( nAttrStartIdx < nTextStartIdx
2237 || ( pEndIdx != nullptr
2238 && *pEndIdx > nEnd ) )
2239 {
2240 continue;
2241 }
2242 }
2243
2244 if (nWhich == RES_TXTATR_METAFIELD)
2245 {
2246 // Skip metadata fields. Also remember the range to strip the text later.
2247 metaFieldRanges.emplace_back(nAttrStartIdx, pEndIdx ? *pEndIdx : nEnd);
2248 continue;
2249 }
2250
2251 sal_Int32 nAttrStt = 0;
2252 sal_Int32 nAttrEnd = 0;
2253
2254 if( nAttrStartIdx < nTextStartIdx )
2255 {
2256 // start is before selection
2257 // copy hints with end and CH_TXTATR only if dummy char is copied
2258 if ( pEndIdx && (*pEndIdx > nTextStartIdx) && !pHt->HasDummyChar() )
2259 {
2260 // attribute with extent and the end is in the selection
2261 nAttrStt = nDestStart;
2262 nAttrEnd = (*pEndIdx > nEnd)
2263 ? rDestStart.GetIndex()
2264 : nDestStart + (*pEndIdx) - nTextStartIdx;
2265 }
2266 else
2267 {
2268 continue;
2269 }
2270 }
2271 else
2272 {
2273 // start is in the selection
2274 nAttrStt = nDestStart + ( nAttrStartIdx - nTextStartIdx );
2275 if( pEndIdx )
2276 {
2277 nAttrEnd = *pEndIdx > nEnd
2278 ? rDestStart.GetIndex()
2279 : nDestStart + ( *pEndIdx - nTextStartIdx );
2280 }
2281 else
2282 {
2283 nAttrEnd = nAttrStt;
2284 }
2285 }
2286
2287 SwTextAttr * pNewHt = nullptr;
2288
2289 if( pDest == this )
2290 {
2291 // copy the hint here, but insert it later
2292 pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2293 nAttrStt, nAttrEnd, CopyOrNewType::Copy, pDest );
2294
2295 lcl_CopyHint(nWhich, pHt, pNewHt, nullptr, pDest);
2296 aArr.push_back( pNewHt );
2297 }
2298 else
2299 {
2300 pNewHt = pDest->InsertItem(
2301 pHt->GetAttr(),
2302 nAttrStt - nDeletedDummyChars,
2303 nAttrEnd - nDeletedDummyChars,
2305 if (pNewHt)
2306 {
2307 lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest );
2308 }
2309 else if (pHt->HasDummyChar())
2310 {
2311 // The attribute that has failed to be copied would insert
2312 // dummy char, so positions of the following attributes have
2313 // to be shifted by one to compensate for that missing char.
2314 ++nDeletedDummyChars;
2315 }
2316 }
2317
2318 if( RES_TXTATR_REFMARK == nWhich && !pEndIdx && !bCopyRefMark )
2319 {
2320 aRefMrkArr.push_back( pNewHt );
2321 }
2322 }
2323
2324 // Strip the metadata fields, since we don't copy the RDF entries
2325 // yet and so they are inconsistent upon copy/pasting.
2326 if (!metaFieldRanges.empty())
2327 {
2328 // Reverse to remove without messing the offsets.
2329 std::reverse(metaFieldRanges.begin(), metaFieldRanges.end());
2330 for (const auto& pair : metaFieldRanges)
2331 {
2332 const SwContentIndex aIdx(pDest, pair.first);
2333 pDest->EraseText(aIdx, pair.second - pair.first);
2334 }
2335 }
2336
2337 // this can only happen when copying into self
2338 for (SwTextAttr* i : aArr)
2339 {
2341 }
2342
2343 if( pDest->GetpSwpHints() )
2344 {
2345 for (SwTextAttr* pNewHt : aRefMrkArr)
2346 {
2347 if( pNewHt->GetEnd() )
2348 {
2349 pDest->GetpSwpHints()->Delete( pNewHt );
2350 pDest->DestroyAttr( pNewHt );
2351 }
2352 else
2353 {
2354 const SwContentIndex aIdx( pDest, pNewHt->GetStart() );
2355 pDest->EraseText( aIdx, 1 );
2356 }
2357 }
2358 }
2359
2361 CHECK_SWPHINTS(pDest);
2362}
2363
2364OUString SwTextNode::InsertText( const OUString & rStr, const SwPosition & rIdx,
2365 const SwInsertFlags nMode )
2366{
2367 return InsertText(rStr, rIdx.nContent, nMode);
2368}
2369
2370OUString SwTextNode::InsertText( const OUString & rStr, const SwContentIndex & rIdx,
2371 const SwInsertFlags nMode )
2372{
2373 assert(rIdx.GetContentNode() == this);
2374 assert(rIdx <= m_Text.getLength()); // invalid index
2375
2376 const sal_Int32 aPos = rIdx.GetIndex();
2377 sal_Int32 nLen = m_Text.getLength() - aPos;
2378 sal_Int32 const nOverflow(rStr.getLength() - GetSpaceLeft());
2379 SAL_WARN_IF(nOverflow > 0, "sw.core",
2380 "SwTextNode::InsertText: node text with insertion > capacity.");
2381 OUString const sInserted(
2382 (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
2383 if (sInserted.isEmpty())
2384 {
2385 return sInserted;
2386 }
2387 if (aPos == 0 && m_Text.isEmpty())
2388 m_Text = sInserted;
2389 else
2390 m_Text = m_Text.replaceAt(aPos, 0, sInserted);
2391 assert(GetSpaceLeft()>=0);
2392 nLen = m_Text.getLength() - aPos - nLen;
2393 assert(nLen != 0);
2394
2395 bool bOldExpFlg = IsIgnoreDontExpand();
2397 {
2398 SetIgnoreDontExpand( true );
2399 }
2400
2401 Update(rIdx, nLen, UpdateMode::Default); // text content changed!
2402
2404 {
2405 SetIgnoreDontExpand( bOldExpFlg );
2406 }
2407
2408 if ( HasWriterListeners() )
2409 { // send this before messing with hints, which will send RES_UPDATE_ATTR
2410 auto aInsHint = sw::MakeInsertText(*this, aPos, nLen);
2411 CallSwClientNotify(aInsHint);
2412 }
2413
2414 if ( HasHints() )
2415 {
2416 m_pSwpHints->SortIfNeedBe();
2417 bool const bHadHints(!m_pSwpHints->CanBeDeleted());
2418 bool bMergePortionsNeeded(false);
2419 for ( size_t i = 0; i < m_pSwpHints->Count() &&
2420 rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
2421 {
2422 SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i );
2423 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2424 if( !pEndIdx )
2425 continue;
2426
2427 if( rIdx == *pEndIdx )
2428 {
2429 if ( (nMode & SwInsertFlags::NOHINTEXPAND) ||
2431 && pHt->DontExpand()) )
2432 {
2433 m_pSwpHints->DeleteAtPos(i);
2434 // on empty attributes also adjust Start
2435 if( rIdx == pHt->GetStart() )
2436 pHt->SetStart( pHt->GetStart() - nLen );
2437 pHt->SetEnd(*pEndIdx - nLen);
2438 // could be that pHt has IsFormatIgnoreEnd set, and it's
2439 // not a RSID-only hint - now we have the inserted text
2440 // between pHt and its continuation... which we don't know.
2441 // punt the job to MergePortions below.
2442 if (pHt->IsFormatIgnoreEnd())
2443 {
2444 bMergePortionsNeeded = true;
2445 }
2447 }
2448 // empty hints at insert position?
2449 else if ( (nMode & SwInsertFlags::EMPTYEXPAND)
2450 && (*pEndIdx == pHt->GetStart()) )
2451 {
2452 m_pSwpHints->DeleteAtPos(i);
2453 pHt->SetStart( pHt->GetStart() - nLen );
2454 const size_t nCurrentLen = m_pSwpHints->Count();
2455 InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
2456 if ( nCurrentLen > m_pSwpHints->Count() && i )
2457 {
2458 --i;
2459 }
2460 continue;
2461 }
2462 else
2463 {
2464 continue;
2465 }
2466 }
2467 if ( !(nMode & SwInsertFlags::NOHINTEXPAND) &&
2468 rIdx == nLen && pHt->GetStart() == rIdx.GetIndex() &&
2469 !pHt->IsDontExpandStartAttr() )
2470 {
2471 // no field, at paragraph start, HintExpand
2472 m_pSwpHints->DeleteAtPos(i);
2473 pHt->SetStart( pHt->GetStart() - nLen );
2474 // no effect on format ignore flags here (para start)
2476 }
2477 }
2478 if (bMergePortionsNeeded)
2479 {
2480 m_pSwpHints->MergePortions(*this);
2481 }
2482 SAL_WARN_IF(bHadHints && m_pSwpHints->CanBeDeleted(), "sw.core",
2483 "SwTextNode::InsertText: unexpected loss of hints");
2484 }
2485
2486 // By inserting a character, the hidden flags
2487 // at the TextNode can become invalid:
2489
2490 CHECK_SWPHINTS(this);
2491 return sInserted;
2492}
2493
2495 const SwContentIndex & rStart, const sal_Int32 nLen )
2496{
2497 assert(pDest); // Cut requires a destination
2498 SwContentIndex aDestStt(pDest, pDest->GetText().getLength());
2499 CutImpl( pDest, aDestStt, rStart, nLen, false );
2500}
2501
2502void SwTextNode::CutImpl( SwTextNode * const pDest, const SwContentIndex & rDestStart,
2503 const SwContentIndex & rStart, sal_Int32 nLen, const bool bUpdate )
2504{
2505 assert(pDest); // Cut requires a destination
2506
2507 assert(&GetDoc() == &pDest->GetDoc()); // must be same document
2508
2509 assert(pDest != this); // destination must be different node
2510
2511 assert(rDestStart.GetContentNode() == pDest);
2512 assert(rStart.GetContentNode() == this);
2513
2514 if( !nLen )
2515 {
2516 // if no length is given, copy attributes at position rStart
2517 CopyAttr( pDest, rStart.GetIndex(), rDestStart.GetIndex() );
2518 return;
2519 }
2520
2521 sal_Int32 nTextStartIdx = rStart.GetIndex();
2522 sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
2523 const sal_Int32 nInitSize = pDest->m_Text.getLength();
2524
2525 if (pDest->GetSpaceLeft() < nLen)
2526 { // FIXME: could only happen when called from SwRangeRedline::Show.
2527 // unfortunately can't really do anything here to handle that...
2528 abort();
2529 }
2530 pDest->m_Text = pDest->m_Text.replaceAt(nDestStart, 0,
2531 m_Text.subView(nTextStartIdx, nLen));
2532 OUString const newText = m_Text.replaceAt(nTextStartIdx, nLen, u"");
2533 nLen = pDest->m_Text.getLength() - nInitSize; // update w/ current size!
2534 if (!nLen) // String didn't grow?
2535 return;
2536
2537 if (bUpdate)
2538 {
2539 // Update all SwContentIndex
2540 pDest->Update(rDestStart, nLen, UpdateMode::Default);
2541 }
2542
2543 CHECK_SWPHINTS(pDest);
2544
2545 const sal_Int32 nEnd = rStart.GetIndex() + nLen;
2546 bool const bUndoNodes =
2547 GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
2548
2549 // copy hard attributes on whole paragraph
2550 if (HasSwAttrSet())
2551 {
2552 bool hasSwAttrSet = pDest->HasSwAttrSet();
2553 if (hasSwAttrSet)
2554 {
2555 // if we have our own property set it doesn't mean
2556 // that this set defines any style different to Standard one.
2557 hasSwAttrSet = false;
2558
2559 // so, let's check deeper if property set has defined any property
2560 if (pDest->GetpSwAttrSet())
2561 {
2562 // check all items in the property set
2563 SfxItemIter aIter( *pDest->GetpSwAttrSet() );
2564 const SfxPoolItem* pItem = aIter.GetCurItem();
2565 do
2566 {
2567 // check current item
2568 const sal_uInt16 nWhich = IsInvalidItem( pItem )
2569 ? pDest->GetpSwAttrSet()->GetWhichByPos( aIter.GetCurPos() )
2570 : pItem->Which();
2571 if( RES_FRMATR_STYLE_NAME != nWhich &&
2573 RES_PAGEDESC != nWhich &&
2574 RES_BREAK != nWhich &&
2575 SfxItemState::SET == pDest->GetpSwAttrSet()->GetItemState( nWhich, false ) )
2576 {
2577 // check if parent value (original value in style) has the same value as in [pItem]
2578 const SfxPoolItem& rParentItem = pDest->GetpSwAttrSet()->GetParent()->Get( nWhich, true );
2579
2580 hasSwAttrSet = (rParentItem != *pItem);
2581
2582 // property set is not empty => no need to make anymore checks
2583 if (hasSwAttrSet)
2584 break;
2585 }
2586
2587 // let's check next item
2588 pItem = aIter.NextItem();
2589 } while (pItem);
2590 }
2591 }
2592
2593 // all or just the Char attributes?
2594 if( nInitSize || hasSwAttrSet ||
2595 nLen != pDest->GetText().getLength())
2596 {
2601 aCharSet( pDest->GetDoc().GetAttrPool() );
2602 aCharSet.Put( *GetpSwAttrSet() );
2603 if( aCharSet.Count() )
2604 pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2605 }
2606 else
2607 {
2608 // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before
2609 // and current SwTextNode can contain not suitable for pDest value
2612 aCharSet(pDest->GetDoc().GetAttrPool());
2613 aCharSet.Put(*GetpSwAttrSet());
2614 if (aCharSet.Count())
2615 pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen);
2616 }
2617 }
2618
2619 // notify frames - before moving hints, because footnotes
2620 // want to find their anchor text frame in the follow chain
2621 // (also ignore fieldmarks, the caller will recreate frames)
2622 const sw::InsertText aInsHint(nDestStart, nLen, false, false);
2623 pDest->HandleNonLegacyHint(aInsHint);
2624 const sw::MoveText aMoveHint(pDest, nDestStart, nTextStartIdx, nLen);
2625 CallSwClientNotify(aMoveHint);
2626 const sw::DeleteText aDelText(nTextStartIdx, nLen);
2627 HandleNonLegacyHint(aDelText);
2628
2629 // 2. move attributes
2630 // Iterate over attribute array until the start of the attribute
2631 // is behind the moved range
2632 bool bMergePortionsNeeded(false);
2633 size_t nAttrCnt = 0;
2634 while (m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()))
2635 {
2636 SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2637 const sal_Int32 nAttrStartIdx = pHt->GetStart();
2638 if ( nAttrStartIdx >= nEnd )
2639 break;
2640 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2641 const sal_uInt16 nWhich = pHt->Which();
2642 SwTextAttr *pNewHt = nullptr;
2643
2644 // if the hint has a dummy character, then it must not be split!
2645 if(nAttrStartIdx < nTextStartIdx)
2646 {
2647 // start is before the range
2648 if (!pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich
2649 || bUndoNodes ) && pEndIdx && *pEndIdx > nTextStartIdx)
2650 {
2651 // attribute with extent and end of attribute is in the range
2652 pNewHt = MakeTextAttr( pDest->GetDoc(), pHt->GetAttr(),
2653 nDestStart,
2654 nDestStart + (
2655 *pEndIdx > nEnd
2656 ? nLen
2657 : *pEndIdx - nTextStartIdx ) );
2658 }
2659 }
2660 else
2661 {
2662 // start is inside the range
2663 if (!pEndIdx || *pEndIdx < nEnd ||
2664 (!bUndoNodes && RES_TXTATR_REFMARK == nWhich)
2665 || pHt->HasDummyChar() )
2666 {
2667 // do not delete note and later add it -> sidebar flickering
2668 if (GetDoc().GetDocShell())
2669 {
2670 GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2671 }
2672 // move attribute
2673 m_pSwpHints->Delete( pHt );
2674 // reset start/end indexes
2675 if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
2676 {
2677 bMergePortionsNeeded = true;
2678 }
2679 pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx));
2680 if (pEndIdx)
2681 {
2682 pHt->SetEnd( nDestStart + (
2683 *pEndIdx > nEnd
2684 ? nLen
2685 : *pEndIdx - nTextStartIdx ) );
2686 }
2687 pDest->InsertHint( pHt,
2690 if (GetDoc().GetDocShell())
2691 {
2692 GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2693 }
2694 continue; // iterate while loop, no ++ !
2695 }
2696 // the end is behind the range
2697 else if (RES_TXTATR_REFMARK != nWhich || bUndoNodes)
2698 {
2699 pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2700 nDestStart + (nAttrStartIdx - nTextStartIdx),
2701 nDestStart + (*pEndIdx > nEnd
2702 ? nLen
2703 : *pEndIdx - nTextStartIdx));
2704 }
2705 }
2706 if (pNewHt)
2707 {
2708 const bool bSuccess( pDest->InsertHint( pNewHt,
2712 if (bSuccess)
2713 {
2714 lcl_CopyHint( nWhich, pHt, pNewHt, nullptr, pDest );
2715 }
2716 }
2717 ++nAttrCnt;
2718 }
2719 // If there are still empty attributes around, they have a higher priority
2720 // than any attributes that become empty due to the move.
2721 // So temporarily remove them and Update the array, then re-insert the
2722 // removed ones so they overwrite the others.
2723 if (m_pSwpHints && nAttrCnt < m_pSwpHints->Count())
2724 {
2725 SwpHts aArr;
2726 while (nAttrCnt < m_pSwpHints->Count())
2727 {
2728 SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2729 if (nEnd != pHt->GetStart())
2730 break;
2731 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2732 if (pEndIdx && *pEndIdx == nEnd)
2733 {
2734 aArr.push_back( pHt );
2735 m_pSwpHints->Delete( pHt );
2736 }
2737 else
2738 {
2739 ++nAttrCnt;
2740 }
2741 }
2742 Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete);
2743
2744 for (SwTextAttr* pHt : aArr)
2745 {
2746 pHt->SetStart( rStart.GetIndex() );
2747 pHt->SetEnd( rStart.GetIndex() );
2748 InsertHint( pHt );
2749 }
2750 }
2751 else
2752 {
2753 Update(rStart, nLen, UpdateMode::Negative|UpdateMode::Delete);
2754 }
2755
2756 // set after moving hints
2757 m_Text = newText;
2758
2759 if (bMergePortionsNeeded)
2760 {
2761 m_pSwpHints->MergePortions(*this);
2762 }
2763
2764 CHECK_SWPHINTS(this);
2765
2767}
2768
2769void SwTextNode::EraseText(const SwPosition &rIdx, const sal_Int32 nCount,
2770 const SwInsertFlags nMode )
2771{
2772 EraseText(rIdx.nContent, nCount, nMode);
2773}
2774
2775void SwTextNode::EraseText(const SwContentIndex &rIdx, const sal_Int32 nCount,
2776 const SwInsertFlags nMode )
2777{
2778 assert(rIdx.GetContentNode() == this);
2779 assert(rIdx <= m_Text.getLength()); // invalid index
2780
2781 const sal_Int32 nStartIdx = rIdx.GetIndex();
2782 const sal_Int32 nCnt = (nCount==SAL_MAX_INT32)
2783 ? m_Text.getLength() - nStartIdx : nCount;
2784 const sal_Int32 nEndIdx = nStartIdx + nCnt;
2785 if (nEndIdx <= m_Text.getLength())
2786 m_Text = m_Text.replaceAt(nStartIdx, nCnt, u"");
2787
2788 // GCAttr(); don't remove all empty ones, just the ones that are in the
2789 // range but not at the end of the range.
2790
2791 for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
2792 {
2793 SwTextAttr *pHt = m_pSwpHints->Get(i);
2794
2795 const sal_Int32 nHintStart = pHt->GetStart();
2796
2797 if ( nHintStart < nStartIdx )
2798 continue;
2799
2800 if ( nHintStart > nEndIdx )
2801 break; // hints are sorted by end, so break here
2802
2803 const sal_Int32* pHtEndIdx = pHt->GetEnd();
2804 const sal_uInt16 nWhich = pHt->Which();
2805
2806 if( !pHtEndIdx )
2807 {
2808 // attribute with neither end nor CH_TXTATR?
2809 assert(pHt->HasDummyChar());
2810 if (isTXTATR(nWhich) && (nHintStart < nEndIdx))
2811 {
2812 m_pSwpHints->DeleteAtPos(i);
2813 DestroyAttr( pHt );
2814 --i;
2815 }
2816 continue;
2817 }
2818
2819 assert(!( (nHintStart < nEndIdx) && (*pHtEndIdx > nEndIdx)
2820 && pHt->HasDummyChar() )
2821 // next line: deleting exactly dummy char: DeleteAttributes
2822 || ((nHintStart == nStartIdx) && (nHintStart + 1 == nEndIdx)));
2823 // "ERROR: deleting left-overlapped attribute with CH_TXTATR");
2824
2825 // Delete the hint if:
2826 // 1. The hint ends before the deletion end position or
2827 // 2. The hint ends at the deletion end position and
2828 // we are not in empty expand mode and
2829 // the hint is a [toxmark|refmark|ruby|inputfield] text attribute
2830 // 3. deleting exactly the dummy char of an hint with end and dummy
2831 // char deletes the hint
2832 if ( (*pHtEndIdx < nEndIdx)
2833 || ( (*pHtEndIdx == nEndIdx) &&
2834 !(SwInsertFlags::EMPTYEXPAND & nMode) &&
2835 ( (RES_TXTATR_TOXMARK == nWhich) ||
2836 (RES_TXTATR_REFMARK == nWhich) ||
2837 (RES_TXTATR_CJK_RUBY == nWhich) ||
2838 (RES_TXTATR_INPUTFIELD == nWhich) ) )
2839 || ( (nHintStart < nEndIdx) &&
2840 pHt->HasDummyChar() )
2841 )
2842 {
2843 m_pSwpHints->DeleteAtPos(i);
2844 DestroyAttr( pHt );
2845 --i;
2846 }
2847 }
2848
2849 OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2850
2852
2853 Update(rIdx, nCnt, UpdateMode::Negative);
2854
2855 if(1 == nCnt)
2856 {
2857 const auto aHint = sw::DeleteChar(nStartIdx);
2858 CallSwClientNotify(aHint);
2859 } else {
2860 const auto aHint = sw::DeleteText(nStartIdx, nCnt);
2861 CallSwClientNotify(aHint);
2862 }
2863
2864 OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2865
2866 // By deleting a character, the hidden flags
2867 // at the TextNode can become invalid:
2869
2870 CHECK_SWPHINTS(this);
2871}
2872
2874{
2875 if ( !HasHints() )
2876 return;
2877
2878 bool bChanged = false;
2879 sal_Int32 nMin = m_Text.getLength();
2880 sal_Int32 nMax = 0;
2881 const bool bAll = nMin != 0; // on empty paragraphs only remove INetFormats
2882
2883 for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
2884 {
2885 SwTextAttr * const pHt = m_pSwpHints->Get(i);
2886
2887 // if end and start are equal, delete it
2888 const sal_Int32 * const pEndIdx = pHt->GetEnd();
2889 if (pEndIdx && !pHt->HasDummyChar() && (*pEndIdx == pHt->GetStart())
2890 && ( bAll || pHt->Which() == RES_TXTATR_INETFMT ) )
2891 {
2892 bChanged = true;
2893 nMin = std::min( nMin, pHt->GetStart() );
2894 nMax = std::max( nMax, *pHt->GetEnd() );
2895 DestroyAttr( m_pSwpHints->Cut(i) );
2896 --i;
2897 }
2898 else
2899 {
2900 pHt->SetDontExpand( false );
2901 }
2902 }
2904
2905 if(bChanged)
2906 {
2907 // textframes react to aHint, others to aNew
2908 SwUpdateAttr aHint(
2909 nMin,
2910 nMax,
2911 0);
2912
2913 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2914 SwFormatChg aNew( GetTextColl() );
2915 CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
2916 }
2917}
2918
2919SwNumRule* SwTextNode::GetNumRule(bool bInParent) const
2920{
2921 SwNumRule* pRet = nullptr;
2922
2923 const SfxPoolItem* pItem = GetNoCondAttr( RES_PARATR_NUMRULE, bInParent );
2924 bool bNoNumRule = false;
2925 if ( pItem )
2926 {
2927 OUString sNumRuleName =
2928 static_cast<const SwNumRuleItem *>(pItem)->GetValue();
2929 if (!sNumRuleName.isEmpty())
2930 {
2931 pRet = GetDoc().FindNumRulePtr( sNumRuleName );
2932 }
2933 else // numbering is turned off
2934 bNoNumRule = true;
2935 }
2936
2937 if ( !bNoNumRule )
2938 {
2939 if ( pRet && pRet == GetDoc().GetOutlineNumRule() &&
2940 ( !HasSwAttrSet() ||
2941 SfxItemState::SET !=
2942 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) ) )
2943 {
2944 SwTextFormatColl* pColl = GetTextColl();
2945 if ( pColl )
2946 {
2947 const SwNumRuleItem& rDirectItem = pColl->GetNumRule( false );
2948 if ( rDirectItem.GetValue().isEmpty() )
2949 {
2950 pRet = nullptr;
2951 }
2952 }
2953 }
2954 }
2955
2956 return pRet;
2957}
2958
2960{
2961 if ( IsInList() )
2962 {
2963 SwNumRule* pNumRule = GetNumRule();
2964 if ( pNumRule && pNumRule != GetNum()->GetNumRule() )
2965 {
2966 mpNodeNum->ChangeNumRule( *pNumRule );
2968 {
2969 mpNodeNumRLHidden->ChangeNumRule(*pNumRule);
2970 }
2971 }
2972 }
2973
2974 // Sending "noop" modify in order to cause invalidations of registered
2975 // <SwTextFrame> instances to get the list style change respectively the change
2976 // in the list tree reflected in the layout.
2977 // Important note:
2978 {
2979 SvxLRSpaceItem& rLR = const_cast<SvxLRSpaceItem&>(GetSwAttrSet().GetLRSpace());
2980 CallSwClientNotify(sw::LegacyModifyHint(&rLR, &rLR));
2981 }
2982
2983 SetWordCountDirty( true );
2984}
2985
2986// -> #i27615#
2987bool SwTextNode::IsNumbered(SwRootFrame const*const pLayout) const
2988{
2989 SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
2990 return pRule && IsCountedInList();
2991}
2992
2994{
2995 bool bResult = false;
2996
2997 if ( IsInList() )
2998 {
2999 bResult =
3001 }
3002
3003 return bResult;
3004}
3005// <- #i27615#
3006
3008 bool bChgFollow )
3009{
3010 // ignore hard PageBreak/PageDesc/ColumnBreak from Auto-Set
3011 std::optional<SwAttrSet> oNewAttrSet;
3012 // #i75353#
3013 bool bClearHardSetNumRuleWhenFormatCollChanges( false );
3014 if( HasSwAttrSet() )
3015 {
3016 oNewAttrSet.emplace( *GetpSwAttrSet() );
3017 const SfxItemSet* pTmpSet = GetpSwAttrSet();
3018
3019 if (bNext) // successor doesn't inherit breaks!
3020 pTmpSet = &*oNewAttrSet;
3021
3022 // !bNext: remove PageBreaks/PageDesc/ColBreak from this
3023 bool bRemoveFromCache = false;
3024 std::vector<sal_uInt16> aClearWhichIds;
3025 if ( bNext )
3026 bRemoveFromCache = ( 0 != oNewAttrSet->ClearItem( RES_PAGEDESC ) );
3027 else
3028 aClearWhichIds.push_back( RES_PAGEDESC );
3029
3030 if( SfxItemState::SET == pTmpSet->GetItemState( RES_BREAK, false ) )
3031 {
3032 if ( bNext )
3033 oNewAttrSet->ClearItem( RES_BREAK );
3034 else
3035 aClearWhichIds.push_back( RES_BREAK );
3036 bRemoveFromCache = true;
3037 }
3038 if( SfxItemState::SET == pTmpSet->GetItemState( RES_KEEP, false ) )
3039 {
3040 if ( bNext )
3041 oNewAttrSet->ClearItem( RES_KEEP );
3042 else
3043 aClearWhichIds.push_back( RES_KEEP );
3044 bRemoveFromCache = true;
3045 }
3046 if( SfxItemState::SET == pTmpSet->GetItemState( RES_PARATR_SPLIT, false ) )
3047 {
3048 if ( bNext )
3049 oNewAttrSet->ClearItem( RES_PARATR_SPLIT );
3050 else
3051 aClearWhichIds.push_back( RES_PARATR_SPLIT );
3052 bRemoveFromCache = true;
3053 }
3054 if(SfxItemState::SET == pTmpSet->GetItemState(RES_PARATR_NUMRULE, false))
3055 {
3056 SwNumRule * pRule = GetNumRule();
3057
3058 if (pRule && IsOutline())
3059 {
3060 if ( bNext )
3061 oNewAttrSet->ClearItem(RES_PARATR_NUMRULE);
3062 else
3063 {
3064 // #i75353#
3065 // No clear of hard set numbering rule at an outline paragraph at this point.
3066 // Only if the paragraph style changes - see below.
3067 bClearHardSetNumRuleWhenFormatCollChanges = true;
3068 }
3069 bRemoveFromCache = true;
3070 }
3071 }
3072
3073 if ( !aClearWhichIds.empty() )
3074 bRemoveFromCache = 0 != ClearItemsFromAttrSet( aClearWhichIds );
3075
3076 if( !bNext && bRemoveFromCache )
3077 {
3079 }
3080 }
3081 SwNodes& rNds = GetNodes();
3082
3083 SwTextFormatColl* pColl = GetTextColl();
3084
3085 SwTextNode *pNode = new SwTextNode( rPosNd, pColl, oNewAttrSet ? &*oNewAttrSet : nullptr );
3086
3087 oNewAttrSet.reset();
3088
3089 const SwNumRule* pRule = GetNumRule();
3090 if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() )
3091 {
3092 // #i55459#
3093 // - correction: parameter <bNext> has to be checked, as it was in the
3094 // previous implementation.
3095 if ( !bNext && !IsCountedInList() )
3096 SetCountedInList(true);
3097 }
3098
3099 // In case the numbering caused a style from the pool to be assigned to
3100 // the new node, don't overwrite that here!
3101 if( pColl != pNode->GetTextColl() ||
3102 ( bChgFollow && pColl != GetTextColl() ))
3103 return pNode; // that ought to be enough?
3104
3105 pNode->ChgTextCollUpdateNum( nullptr, pColl ); // for numbering/outline
3106 if( bNext || !bChgFollow )
3107 return pNode;
3108
3109 SwTextFormatColl *pNextColl = &pColl->GetNextTextFormatColl();
3110 // i#101870 perform action on different paragraph styles before applying
3111 // the new paragraph style
3112 if (pNextColl != pColl)
3113 {
3114 // #i75353#
3115 if ( bClearHardSetNumRuleWhenFormatCollChanges )
3116 {
3117 if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE } ) != 0 )
3118 {
3120 }
3121 }
3122 }
3123 ChgFormatColl( pNextColl );
3124
3125 return pNode;
3126}
3127
3129{
3130 // position behind which it will be inserted
3131 SwTextNode* pNew = MakeNewTextNode( *rPos.GetNodes()[rPos.GetNodeIndex() + 1] );
3132
3133 // reset list attributes at appended text node
3137 if ( pNew->GetNumRule() == nullptr )
3138 {
3141 }
3142
3143 if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
3144 {
3145 AddToList();
3146 }
3147
3148 if( HasWriterListeners() )
3150 return pNew;
3151}
3152
3154 const sal_Int32 nIndex,
3155 const sal_uInt16 nWhich ) const
3156{
3157 assert(nWhich >= RES_TXTATR_BEGIN && nWhich <= RES_TXTATR_END);
3158 if ( HasHints() )
3159 {
3160 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3161 {
3162 SwTextAttr * const pHint = m_pSwpHints->Get(i);
3163 const sal_Int32 nStartPos = pHint->GetStart();
3164 if ( nIndex < nStartPos )
3165 {
3166 return nullptr;
3167 }
3168 if ( (nIndex == nStartPos) && pHint->HasDummyChar() )
3169 {
3170 return ( RES_TXTATR_END == nWhich || nWhich == pHint->Which() )
3171 ? pHint : nullptr;
3172 }
3173 }
3174 }
3175 return nullptr;
3176}
3177
3178SwTextAttr* SwTextNode::GetTextAttrForEndCharAt(sal_Int32 nIndex, sal_uInt16 nWhich) const
3179{
3181 if (!pAttr)
3182 {
3183 return nullptr;
3184 }
3185
3186 if (!pAttr->End())
3187 {
3188 return nullptr;
3189 }
3190
3191 // The start-end range covers the end dummy character.
3192 if (*pAttr->End() - 1 != nIndex)
3193 {
3194 return nullptr;
3195 }
3196
3197 return pAttr;
3198}
3199
3200namespace
3201{
3202
3203sal_uInt16 lcl_BoundListLevel(const int nActualLevel)
3204{
3205 return o3tl::narrowing<sal_uInt16>( std::clamp( nActualLevel, 0, MAXLEVEL-1 ) );
3206}
3207
3208}
3209
3210// -> #i29560#
3211bool SwTextNode::HasNumber(SwRootFrame const*const pLayout) const
3212{
3213 bool bResult = false;
3214
3215 const SwNumRule *const pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
3216 if ( pRule )
3217 {
3218 const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3219
3220 // #i40041#
3221 bResult = aFormat.IsEnumeration();
3222 }
3223
3224 return bResult;
3225}
3226
3228{
3229 bool bResult = false;
3230
3231 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3232 if ( pRule )
3233 {
3234 const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3235
3236 bResult = aFormat.IsItemize();
3237 }
3238
3239 return bResult;
3240}
3241// <- #i29560#
3242
3243// #128041# - introduce parameter <_bInclPrefixAndSuffixStrings>
3244//i53420 added max outline parameter
3245OUString SwTextNode::GetNumString( const bool _bInclPrefixAndSuffixStrings,
3246 const unsigned int _nRestrictToThisLevel,
3247 SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
3248{
3249 if (GetDoc().IsClipBoard() && m_oNumStringCache)
3250 {
3251 // #i111677# do not expand number strings in clipboard documents
3252 return *m_oNumStringCache;
3253 }
3254 const SwNumRule* pRule = GetNum(pLayout, eRedline) ? GetNum(pLayout, eRedline)->GetNumRule() : nullptr;
3255 if ( pRule &&
3256 IsCountedInList() )
3257 {
3258 SvxNumberType const& rNumberType(
3259 pRule->Get( lcl_BoundListLevel(GetActualListLevel(eRedline)) ) );
3260 if (rNumberType.IsTextFormat() ||
3261
3262 (style::NumberingType::NUMBER_NONE == rNumberType.GetNumberingType()))
3263 {
3264 return pRule->MakeNumString( GetNum(pLayout, eRedline)->GetNumberVector(),
3265 _bInclPrefixAndSuffixStrings,
3266 _nRestrictToThisLevel,
3267 nullptr,
3268 GetLang(0));
3269 }
3270 }
3271
3272 return OUString();
3273}
3274
3276{
3277 tools::Long nRet = 0;
3278 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3279 if( pRule )
3280 {
3281 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3282
3284 {
3285 nRet = rFormat.GetAbsLSpace();
3286
3287 if( !bTextLeft )
3288 {
3289 if( 0 > rFormat.GetFirstLineOffset() &&
3290 nRet > -rFormat.GetFirstLineOffset() )
3291 nRet = nRet + rFormat.GetFirstLineOffset();
3292 else
3293 nRet = 0;
3294 }
3295
3296 if( pRule->IsAbsSpaces() )
3297 nRet = nRet - GetSwAttrSet().GetLRSpace().GetLeft();
3298 }
3300 {
3302 {
3303 nRet = rFormat.GetIndentAt();
3304 // #i90401#
3305 // Only negative first line indents have consider for the left margin
3306 if ( !bTextLeft &&
3307 rFormat.GetFirstLineIndent() < 0 )
3308 {
3309 nRet = nRet + rFormat.GetFirstLineIndent();
3310 }
3311 }
3312 }
3313 }
3314
3315 return nRet;
3316}
3317
3318bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset ) const
3319{
3320 // #i95907#
3321 rFLOffset = 0;
3322
3323 // #i51089#
3324 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3325 if ( pRule )
3326 {
3327 if ( IsCountedInList() )
3328 {
3329 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3331 {
3332 rFLOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
3333
3335 {
3337 rFLOffset = rFLOffset + aItem.GetTextFirstLineOffset();
3338 }
3339 }
3341 {
3343 {
3344 rFLOffset = rFormat.GetFirstLineIndent();
3345 }
3347 {
3349 rFLOffset = aItem.GetTextFirstLineOffset();
3350 }
3351 }
3352 }
3353
3354 return true;
3355 }
3356
3358 return false;
3359}
3360
3362{
3363 SwTwips nAdditionalIndent = 0;
3364
3365 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3366 if ( pRule )
3367 {
3368 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3370 {
3371 nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3372
3374 {
3375 nAdditionalIndent = nAdditionalIndent -
3377 }
3378 }
3380 {
3382 {
3383 nAdditionalIndent = rFormat.GetIndentAt() + rFormat.GetFirstLineIndent();
3384 }
3385 else
3386 {
3387 nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3389 {
3390 nAdditionalIndent = nAdditionalIndent -
3392 }
3393 }
3394 }
3395 }
3396 else
3397 {
3398 nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3399 }
3400
3401 return nAdditionalIndent;
3402}
3403
3404// #i96772#
3405void SwTextNode::ClearLRSpaceItemDueToListLevelIndents( std::shared_ptr<SvxLRSpaceItem>& o_rLRSpaceItem ) const
3406{
3408 {
3409 const SwNumRule* pRule = GetNumRule();
3410 if ( pRule && GetActualListLevel() >= 0 )
3411 {
3412 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3414 {
3415 o_rLRSpaceItem = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
3416 }
3417 }
3418 }
3419}
3420
3421// #i91133#
3423{
3424 tools::Long nLeftMarginForTabCalc = 0;
3425
3426 bool bLeftMarginForTabCalcSetToListLevelIndent( false );
3427 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3428 if( pRule )
3429 {
3430 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3432 {
3434 {
3435 nLeftMarginForTabCalc = rFormat.GetIndentAt();
3436 bLeftMarginForTabCalcSetToListLevelIndent = true;
3437 }
3438 }
3439 }
3440 if ( !bLeftMarginForTabCalcSetToListLevelIndent )
3441 {
3442 nLeftMarginForTabCalc = GetSwAttrSet().GetLRSpace().GetTextLeft();
3443 }
3444
3445 return nLeftMarginForTabCalc;
3446}
3447
3448static void Replace0xFF(
3449 SwTextNode const& rNode,
3450 OUStringBuffer & rText,
3451 sal_Int32 & rTextStt,
3452 sal_Int32 nEndPos )
3453{
3454 if (!rNode.GetpSwpHints())
3455 return;
3456
3458 for( int nSrchIter = 0; 2 > nSrchIter; ++nSrchIter, cSrchChr = CH_TXTATR_INWORD )
3459 {
3460 sal_Int32 nPos = rText.indexOf(cSrchChr);
3461 while (-1 != nPos && nPos < nEndPos)
3462 {
3463 const SwTextAttr* const pAttr =
3464 rNode.GetTextAttrForCharAt(rTextStt + nPos);
3465 if( pAttr )
3466 {
3467 switch( pAttr->Which() )
3468 {
3469 case RES_TXTATR_FIELD:
3471 rText.remove(nPos, 1);
3472 ++rTextStt;
3473 break;
3474
3475 case RES_TXTATR_FTN:
3476 rText.remove(nPos, 1);
3477 ++rTextStt;
3478 break;
3479
3480 default:
3481 rText.remove(nPos, 1);
3482 ++rTextStt;
3483 }
3484 }
3485 else
3486 {
3487 ++nPos;
3488 ++nEndPos;
3489 }
3490 nPos = rText.indexOf(cSrchChr, nPos);
3491 }
3492 }
3493}
3494
3495// Expand fields
3496// #i83479# - handling of new parameters
3497OUString SwTextNode::GetExpandText(SwRootFrame const*const pLayout,
3498 const sal_Int32 nIdx,
3499 const sal_Int32 nLen,
3500 const bool bWithNum,
3501 const bool bAddSpaceAfterListLabelStr,
3502 const bool bWithSpacesForLevel,
3503 const ExpandMode eAdditionalMode) const
3504
3505{
3506 ExpandMode eMode = ExpandMode::ExpandFields | eAdditionalMode;
3507 if (pLayout && pLayout->IsHideRedlines())
3508 {
3510 }
3511
3512 ModelToViewHelper aConversionMap(*this, pLayout, eMode);
3513 const OUString aExpandText = aConversionMap.getViewText();
3514 const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nIdx );
3515 sal_Int32 nEnd = nLen == -1 ? GetText().getLength() : nIdx + nLen;
3516 const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
3517 OUStringBuffer aText(aExpandText.subView(nExpandBegin, nExpandEnd-nExpandBegin));
3518
3519 // remove dummy characters of Input Fields
3523
3524 if( bWithNum )
3525 {
3526 if (!GetNumString(true, MAXLEVEL, pLayout).isEmpty())
3527 {
3528 if ( bAddSpaceAfterListLabelStr )
3529 {
3530 const sal_Unicode aSpace = ' ';
3531 aText.insert(0, aSpace);
3532 }
3533 aText.insert(0, GetNumString(true, MAXLEVEL, pLayout));
3534 }
3535 }
3536
3537 if (bWithSpacesForLevel)
3538 {
3539 const sal_Unicode aSpace = ' ';
3540 for (int nLevel = GetActualListLevel(); nLevel > 0; --nLevel)
3541 {
3542 aText.insert(0, aSpace);
3543 aText.insert(0, aSpace);
3544 }
3545 }
3546
3547 return aText.makeStringAndClear();
3548}
3549
3551 sal_Int32 nIdx, sal_Int32 nLen,
3552 SwRootFrame const*const pLayout, bool bWithNum,
3553 bool bWithFootnote, bool bReplaceTabsWithSpaces ) const
3554{
3555 if( &rDestNd == this )
3556 return false;
3557 assert(!pDestIdx || pDestIdx->GetContentNode() == &rDestNd);
3558
3559 SwContentIndex aDestIdx(&rDestNd, rDestNd.GetText().getLength());
3560 if( pDestIdx )
3561 aDestIdx = *pDestIdx;
3562 const sal_Int32 nDestStt = aDestIdx.GetIndex();
3563
3564 // first, start with the text
3565 OUStringBuffer buf(GetText());
3566 if( bReplaceTabsWithSpaces )
3567 buf.replace('\t', ' ');
3568
3569 // mask hidden characters
3570 const sal_Unicode cChar = CH_TXTATR_BREAKWORD;
3571 SwScriptInfo::MaskHiddenRanges(*this, buf, 0, buf.getLength(), cChar);
3572
3573 buf.remove(0, nIdx);
3574 if (nLen != -1)
3575 {
3576 buf.truncate(nLen);
3577 }
3578 // remove dummy characters of Input Fields
3579 {
3582 }
3583 rDestNd.InsertText(buf.makeStringAndClear(), aDestIdx);
3584 nLen = aDestIdx.GetIndex() - nDestStt;
3585
3586 // set all char attributes with Symbol font
3587 if ( HasHints() )
3588 {
3589 sal_Int32 nInsPos = nDestStt - nIdx;
3590 for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3591 {
3592 const SwTextAttr* pHt = m_pSwpHints->Get(i);
3593 const sal_Int32 nAttrStartIdx = pHt->GetStart();
3594 const sal_uInt16 nWhich = pHt->Which();
3595 if (nIdx + nLen <= nAttrStartIdx)
3596 break; // behind end of text
3597
3598 const sal_Int32 *pEndIdx = pHt->End();
3599 if( pEndIdx && *pEndIdx > nIdx &&
3600 ( RES_CHRATR_FONT == nWhich ||
3601 RES_TXTATR_CHARFMT == nWhich ||
3602 RES_TXTATR_AUTOFMT == nWhich ))
3603 {
3604 const SvxFontItem* const pFont =
3606 if ( pFont && RTL_TEXTENCODING_SYMBOL == pFont->GetCharSet() )
3607 {
3608 // attribute in area => copy
3609 rDestNd.InsertItem( *const_cast<SvxFontItem*>(pFont),
3610 nInsPos + nAttrStartIdx, nInsPos + *pEndIdx );
3611 }
3612 }
3613 else if ( pHt->HasDummyChar() && (nAttrStartIdx >= nIdx) )
3614 {
3615 aDestIdx = nInsPos + nAttrStartIdx;
3616 switch( nWhich )
3617 {
3618 case RES_TXTATR_FIELD:
3620 {
3621 OUString const aExpand(
3622 static_txtattr_cast<SwTextField const*>(pHt)->GetFormatField().GetField()->ExpandField(true, pLayout));
3623 if (!aExpand.isEmpty())
3624 {
3625 ++aDestIdx; // insert behind
3626 OUString const ins(
3627 rDestNd.InsertText( aExpand, aDestIdx));
3628 SAL_INFO_IF(ins.getLength() != aExpand.getLength(),
3629 "sw.core", "GetExpandText lossage");
3630 aDestIdx = nInsPos + nAttrStartIdx;
3631 nInsPos += ins.getLength();
3632 }
3633 rDestNd.EraseText( aDestIdx, 1 );
3634 --nInsPos;
3635 }
3636 break;
3637
3638 case RES_TXTATR_FTN:
3639 {
3640 if ( bWithFootnote )
3641 {
3642 const SwFormatFootnote& rFootnote = pHt->GetFootnote();
3643 OUString sExpand;
3644 auto const number(pLayout && pLayout->IsHideRedlines()
3645 ? rFootnote.GetNumberRLHidden()
3646 : rFootnote.GetNumber());
3647 if( !rFootnote.GetNumStr().isEmpty() )
3648 sExpand = rFootnote.GetNumStr();
3649 else if( rFootnote.IsEndNote() )
3650 sExpand = GetDoc().GetEndNoteInfo().m_aFormat.
3651 GetNumStr(number);
3652 else
3653 sExpand = GetDoc().GetFootnoteInfo().m_aFormat.
3654 GetNumStr(number);
3655 if( !sExpand.isEmpty() )
3656 {
3657 ++aDestIdx; // insert behind
3658 SvxEscapementItem aItem( SvxEscapement::Superscript, RES_CHRATR_ESCAPEMENT );
3659 rDestNd.InsertItem(
3660 aItem,
3661 aDestIdx.GetIndex(),
3662 aDestIdx.GetIndex() );
3663 OUString const ins( rDestNd.InsertText(sExpand, aDestIdx, SwInsertFlags::EMPTYEXPAND));
3664 SAL_INFO_IF(ins.getLength() != sExpand.getLength(),
3665 "sw.core", "GetExpandText lossage");
3666 aDestIdx = nInsPos + nAttrStartIdx;
3667 nInsPos += ins.getLength();
3668 }
3669 }
3670 rDestNd.EraseText( aDestIdx, 1 );
3671 --nInsPos;
3672 }
3673 break;
3674
3675 default:
3676 rDestNd.EraseText( aDestIdx, 1 );
3677 --nInsPos;
3678 }
3679 }
3680 }
3681 }
3682
3683 if( bWithNum )
3684 {
3685 aDestIdx = nDestStt;
3686 rDestNd.InsertText( GetNumString(true, MAXLEVEL, pLayout), aDestIdx );
3687 }
3688
3689 aDestIdx = 0;
3690 sal_Int32 nStartDelete(-1);
3691 while (aDestIdx < rDestNd.GetText().getLength())
3692 {
3693 sal_Unicode const cur(rDestNd.GetText()[aDestIdx.GetIndex()]);
3694 if ( (cChar == cur) // filter substituted hidden text
3695 || (CH_TXT_ATR_FIELDSTART == cur) // filter all fieldmarks
3696 || (CH_TXT_ATR_FIELDSEP == cur)
3697 || (CH_TXT_ATR_FIELDEND == cur)
3698 || (CH_TXT_ATR_FORMELEMENT == cur))
3699 {
3700 if (-1 == nStartDelete)
3701 {
3702 nStartDelete = aDestIdx.GetIndex(); // start deletion range
3703 }
3704 ++aDestIdx;
3705 if (aDestIdx < rDestNd.GetText().getLength())
3706 {
3707 continue;
3708 } // else: end of paragraph => delete, see below
3709 }
3710 else
3711 {
3712 if (-1 == nStartDelete)
3713 {
3714 ++aDestIdx;
3715 continue;
3716 } // else: delete, see below
3717 }
3718 assert(-1 != nStartDelete); // without delete range, would have continued
3719 rDestNd.EraseText(
3720 SwContentIndex(&rDestNd, nStartDelete),
3721 aDestIdx.GetIndex() - nStartDelete);
3722 assert(aDestIdx.GetIndex() == nStartDelete);
3723 nStartDelete = -1; // reset
3724 }
3725
3726 return true;
3727}
3728
3730{
3731 std::vector<sal_Int32> aRedlArr;
3732 const SwDoc& rDoc = GetDoc();
3733 SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete );
3734 if( SwRedlineTable::npos != nRedlPos )
3735 {
3736 // some redline-delete object exists for the node
3737 const SwNodeOffset nNdIdx = GetIndex();
3738 for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
3739 {
3740 const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
3741 if( RedlineType::Delete == pTmp->GetType() )
3742 {
3743 const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
3744 if( pRStt->GetNodeIndex() < nNdIdx )
3745 {
3746 if( pREnd->GetNodeIndex() > nNdIdx )
3747 // paragraph is fully deleted
3748 return OUString();
3749 else if( pREnd->GetNodeIndex() == nNdIdx )
3750 {
3751 // deleted from 0 to nContent
3752 aRedlArr.push_back( 0 );
3753 aRedlArr.push_back( pREnd->GetContentIndex() );
3754 }
3755 }
3756 else if( pRStt->GetNodeIndex() == nNdIdx )
3757 {
3758 //aRedlArr.Insert( pRStt->GetContentIndex(), aRedlArr.Count() );
3759 aRedlArr.push_back( pRStt->GetContentIndex() );
3760 if( pREnd->GetNodeIndex() == nNdIdx )
3761 aRedlArr.push_back( pREnd->GetContentIndex() );
3762 else
3763 {
3764 aRedlArr.push_back(GetText().getLength());
3765 break; // that was all
3766 }
3767 }
3768 else
3769 break; // that was all
3770 }
3771 }
3772 }
3773
3774 OUStringBuffer aText(GetText());
3775
3776 sal_Int32 nTextStt = 0;
3777 sal_Int32 nIdxEnd = aText.getLength();
3778 for( size_t n = 0; n < aRedlArr.size(); n += 2 )
3779 {
3780 sal_Int32 nStt = aRedlArr[ n ];
3781 sal_Int32 nEnd = aRedlArr[ n+1 ];
3782 if( ( 0 <= nStt && nStt <= nIdxEnd ) ||
3783 ( 0 <= nEnd && nEnd <= nIdxEnd ))
3784 {
3785 if( nStt < 0 ) nStt = 0;
3786 if( nIdxEnd < nEnd ) nEnd = nIdxEnd;
3787 const sal_Int32 nDelCnt = nEnd - nStt;
3788 aText.remove(nStt - nTextStt, nDelCnt);
3789 Replace0xFF(*this, aText, nTextStt, nStt - nTextStt);
3790 nTextStt += nDelCnt;
3791 }
3792 else if( nStt >= nIdxEnd )
3793 break;
3794 }
3795 Replace0xFF(*this, aText, nTextStt, aText.getLength());
3796
3797 return aText.makeStringAndClear();
3798}
3799
3800void SwTextNode::ReplaceText( const SwContentIndex& rStart, const sal_Int32 nDelLen,
3801 const OUString & rStr)
3802{
3803 assert(rStart.GetContentNode() == this);
3804 assert( rStart.GetIndex() < m_Text.getLength() // index out of bounds
3805 && rStart.GetIndex() + nDelLen <= m_Text.getLength());
3806
3807 sal_Int32 const nOverflow(rStr.getLength() - nDelLen - GetSpaceLeft());
3808 SAL_WARN_IF(nOverflow > 0, "sw.core",
3809 "SwTextNode::ReplaceText: node text with insertion > node capacity.");
3810 OUString const sInserted(
3811 (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
3812 if (sInserted.isEmpty() && 0 == nDelLen)
3813 {
3814 return; // nothing to do
3815 }
3816
3817 const sal_Int32 nStartPos = rStart.GetIndex();
3818 sal_Int32 nEndPos = nStartPos + nDelLen;
3819 sal_Int32 nLen = nDelLen;
3820 for( sal_Int32 nPos = nStartPos; nPos < nEndPos; ++nPos )
3821 {
3822 if ((CH_TXTATR_BREAKWORD == m_Text[nPos]) ||
3824 {
3825 SwTextAttr *const pHint = GetTextAttrForCharAt( nPos );
3826 if (pHint)
3827 {
3828 assert(!( pHint->GetEnd() && pHint->HasDummyChar()
3829 && (pHint->GetStart() < nEndPos)
3830 && (*pHint->GetEnd() > nEndPos) ));
3831 // "deleting left-overlapped attribute with CH_TXTATR"
3832 DeleteAttribute( pHint );
3833 --nEndPos;
3834 --nLen;
3835 }
3836 }
3837 }
3838
3839 bool bOldExpFlg = IsIgnoreDontExpand();
3840 SetIgnoreDontExpand( true );
3841
3842 if (nLen && sInserted.getLength())
3843 {
3844 // Replace the 1st char, then delete the rest and insert.
3845 // This way the attributes of the 1st char are expanded!
3846 m_Text = m_Text.replaceAt(nStartPos, 1, sInserted.subView(0, 1));
3847
3848 ++const_cast<SwContentIndex&>(rStart);
3849 m_Text = m_Text.replaceAt(rStart.GetIndex(), nLen - 1, u"");
3850 Update(rStart, nLen - 1, UpdateMode::Negative);
3851
3852 std::u16string_view aTmpText( sInserted.subView(1) );
3853 m_Text = m_Text.replaceAt(rStart.GetIndex(), 0, aTmpText);
3854 Update(rStart, aTmpText.size(), UpdateMode::Replace);
3855 }
3856 else
3857 {
3858 m_Text = m_Text.replaceAt(nStartPos, nLen, u"");
3859 Update(rStart, nLen, UpdateMode::Negative);
3860
3861 m_Text = m_Text.replaceAt(nStartPos, 0, sInserted);
3862 Update(rStart, sInserted.getLength(), UpdateMode::Replace);
3863 }
3864
3865 SetIgnoreDontExpand( bOldExpFlg );
3866 auto aDelHint = sw::DeleteText(nStartPos, nDelLen);
3867 CallSwClientNotify(aDelHint);
3868
3869 if (sInserted.getLength())
3870 {
3871 auto aInsHint = sw::MakeInsertText(*this, nStartPos, sInserted.getLength());
3872 CallSwClientNotify(aInsHint);
3873 }
3874}
3875
3876void SwTextNode::ReplaceText( SwPosition& rStart, const sal_Int32 nDelLen,
3877 const OUString & rStr)
3878{
3879 ReplaceText(rStart.nContent, nDelLen, rStr);
3880}
3881
3882namespace {
3883 void lcl_ResetParAttrs( SwTextNode &rTextNode )
3884 {
3889 SwPaM aPam( rTextNode );
3890 // #i96644#
3891 // suppress side effect "send data changed events"
3892 rTextNode.GetDoc().ResetAttrs( aPam, false, aAttrs, false );
3893 }
3894
3895 // Helper method for special handling of modified attributes at text node.
3896 // The following is handled:
3897 // (1) on changing the paragraph style - RES_FMT_CHG:
3898 // Check, if list style of the text node is changed. If yes, add respectively
3899 // remove the text node to the corresponding list.
3900 // (2) on changing the attributes - RES_ATTRSET_CHG:
3901 // Same as (1).
3902 // (3) on changing the list style - RES_PARATR_NUMRULE:
3903 // Same as (1).
3904 void HandleModifyAtTextNode( SwTextNode& rTextNode,
3905 const SfxPoolItem* pOldValue,
3906 const SfxPoolItem* pNewValue )
3907 {
3908 const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
3909 pNewValue ? pNewValue->Which() : 0 ;
3910 bool bNumRuleSet = false;
3911 bool bParagraphStyleChanged = false;
3912 OUString sNumRule;
3913 OUString sOldNumRule;
3914 switch ( nWhich )
3915 {
3916 case RES_FMT_CHG:
3917 {
3918 bParagraphStyleChanged = true;
3919 if( rTextNode.GetNodes().IsDocNodes() )
3920 {
3921 const SwNumRule* pFormerNumRuleAtTextNode =
3922 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3923 if ( pFormerNumRuleAtTextNode )
3924 {
3925 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3926 }
3928 {
3929 const SwNumRuleItem& rNumRuleItem = rTextNode.GetTextColl()->GetNumRule();
3930 if ( !rNumRuleItem.GetValue().isEmpty() )
3931 {
3933 }
3934 }
3935 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3936 if ( pNumRuleAtTextNode )
3937 {
3938 bNumRuleSet = true;
3939 sNumRule = pNumRuleAtTextNode->GetName();
3940 }
3941 }
3942 break;
3943 }
3944 case RES_ATTRSET_CHG:
3945 {
3946 const SwNumRule* pFormerNumRuleAtTextNode =
3947 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3948 if ( pFormerNumRuleAtTextNode )
3949 {
3950 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3951 }
3952
3953 const SwAttrSetChg* pSet = dynamic_cast<const SwAttrSetChg*>(pNewValue);
3954 if ( pSet && pSet->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false ) ==
3955 SfxItemState::SET )
3956 {
3957 // #i70748#
3959 bNumRuleSet = true;
3960 }
3961 // #i70748#
3962 // The new list style set at the paragraph.
3963 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3964 if ( pNumRuleAtTextNode )
3965 {
3966 sNumRule = pNumRuleAtTextNode->GetName();
3967 }
3968 break;
3969 }
3970 case RES_PARATR_NUMRULE:
3971 {
3972 if ( rTextNode.GetNodes().IsDocNodes() )
3973 {
3974 const SwNumRule* pFormerNumRuleAtTextNode =
3975 rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3976 if ( pFormerNumRuleAtTextNode )
3977 {
3978 sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3979 }
3980
3981 if ( pNewValue )
3982 {
3983 // #i70748#
3985 bNumRuleSet = true;
3986 }
3987 // #i70748#
3988 // The new list style set at the paragraph.
3989 const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3990 if ( pNumRuleAtTextNode )
3991 {
3992 sNumRule = pNumRuleAtTextNode->GetName();
3993 }
3994 }
3995 break;
3996 }
3997 }
3998 if ( sNumRule != sOldNumRule )
3999 {
4000 if ( bNumRuleSet )
4001 {
4002 if (sNumRule.isEmpty())
4003 {
4004 rTextNode.RemoveFromList();
4005 if ( bParagraphStyleChanged )
4006 {
4007 lcl_ResetParAttrs(rTextNode);
4008 }
4009 }
4010 else
4011 {
4012 rTextNode.RemoveFromList();
4013 // If new list style is the outline style, apply outline
4014 // level as the list level.
4015 if (sNumRule==SwNumRule::GetOutlineRuleName())
4016 {
4017 // #i70748#
4018 OSL_ENSURE( rTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(),
4019 "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." );
4020 const int nNewListLevel =
4022 if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
4023 {
4024 rTextNode.SetAttrListLevel( nNewListLevel );
4025 }
4026 }
4027 rTextNode.AddToList();
4028 }
4029 }
4030 else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
4031 {
4032 rTextNode.RemoveFromList();
4033 if ( bParagraphStyleChanged )
4034 {
4035 lcl_ResetParAttrs(rTextNode);
4036 // #i70748#
4037 if ( rTextNode.GetAttr( RES_PARATR_OUTLINELEVEL, false ).GetValue() > 0 )
4038 {
4040 }
4041 }
4042 }
4043 }
4044 else if (!sNumRule.isEmpty() && !rTextNode.IsInList())
4045 {
4046 rTextNode.AddToList();
4047 }
4048 }
4049 // End of method <HandleModifyAtTextNode>
4050}
4051
4053{
4054 OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
4055 assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && "ChgFormatColl: is not a Text Collection pointer." );
4056
4057 SwTextFormatColl *pOldColl = GetTextColl();
4058 if( pNewColl != pOldColl )
4059 {
4061 SwContentNode::ChgFormatColl( pNewColl );
4062 OSL_ENSURE( !mbInSetOrResetAttr,
4063 "DEBUG OSL_ENSURE(ON - <SwTextNode::ChgFormatColl(..)> called during <Set/ResetAttr(..)>" );
4064 if ( !mbInSetOrResetAttr )
4065 {
4066 SwFormatChg aTmp1( pOldColl );
4067 SwFormatChg aTmp2( pNewColl );
4068 HandleModifyAtTextNode( *this, &aTmp1, &aTmp2 );
4069 }
4070
4071 // reset fill information on parent style change
4073 {
4074 maFillAttributes.reset();
4075 }
4076 }
4077
4078 // only for real nodes-array
4079 if( GetNodes().IsDocNodes() )
4080 {
4081 ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl *>(pNewColl) );
4082 }
4083
4084 return pOldColl;
4085}
4086
4087const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
4088{
4089 // invariant: it's only in list in Hide mode if it's in list in normal mode
4090 assert(mpNodeNum || !mpNodeNumRLHidden);
4091 return (pLayout && pLayout->IsHideRedlines()) || SwListRedlineType::HIDDEN == eRedline
4092 ? mpNodeNumRLHidden.get()
4093 : ( SwListRedlineType::ORIGTEXT == eRedline ? mpNodeNumOrig.get() : mpNodeNum.get() );
4094}
4095
4096void SwTextNode::DoNum(std::function<void (SwNodeNum &)> const& rFunc)
4097{
4098 // temp. clear because GetActualListLevel() may be called and the assert
4099 // there triggered during update, which is unhelpful
4100 std::unique_ptr<SwNodeNum> pBackup = std::move(mpNodeNumRLHidden);
4101 std::unique_ptr<SwNodeNum> pBackup2 = std::move(mpNodeNumOrig);
4102 assert(mpNodeNum);
4103 rFunc(*mpNodeNum);
4104 if (pBackup)
4105 {
4106 mpNodeNumRLHidden = std::move(pBackup);
4107 rFunc(*mpNodeNumRLHidden);
4108 }
4109 if (pBackup2)
4110 {
4111 mpNodeNumOrig = std::move(pBackup2);
4112 rFunc(*mpNodeNumOrig);
4113 }
4114}
4115
4118{
4119 if (SwNodeNum const*const pNum = GetNum(pLayout, eRedline))
4120 {
4121 return pNum->GetNumberVector();
4122 }
4123 else
4124 {
4126 return aResult;
4127 }
4128}
4129
4131{
4132 bool bResult = false;
4133
4134 if ( GetAttrOutlineLevel() > 0 )
4135 {
4136 bResult = !IsInRedlines();
4137 }
4138 else
4139 {
4140 const SwNumRule* pRule( GetNum() ? GetNum()->GetNumRule() : nullptr );
4141 if ( pRule && pRule->IsOutlineRule() )
4142 {
4143 bResult = !IsInRedlines();
4144 }
4145 }
4146
4147 return bResult;
4148}
4149
4151{
4152 return IsOutline() != m_bLastOutlineState;
4153}
4154
4156{
4158}
4159
4161{
4162 return GetAttr(RES_PARATR_OUTLINELEVEL).GetValue();
4163}
4164
4166{
4167 assert(0 <= nLevel && nLevel <= MAXLEVEL); // Level Out Of Range
4168 if ( 0 <= nLevel && nLevel <= MAXLEVEL )
4169 {
4171 o3tl::narrowing<sal_uInt16>(nLevel) ) );
4172 }
4173}
4174
4175void SwTextNode::GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr)
4176{
4177 const SfxGrabBagItem & rGrabBagItem = GetAttr(RES_PARATR_GRABBAG);
4178 auto it = rGrabBagItem.GetGrabBag().find("OutlineContentVisibleAttr");
4179 if (it != rGrabBagItem.GetGrabBag().end())
4180 it->second >>= bOutlineContentVisibleAttr;
4181}
4182
4184{
4185 SfxGrabBagItem aGrabBagItem(RES_PARATR_GRABBAG);
4186 aGrabBagItem.GetGrabBag()["OutlineContentVisibleAttr"] <<= bVisible;
4187 SetAttr(aGrabBagItem);
4188}
4189
4190// #i70748#
4191
4193{
4195 {
4198 }
4199}
4200
4202{
4204 {
4207 }
4208}
4209
4211{
4212 if ( nLevel < 0 || nLevel >= MAXLEVEL )
4213 {
4214 assert(false); // invalid level
4215 return;
4216 }
4217
4218 SfxInt16Item aNewListLevelItem( RES_PARATR_LIST_LEVEL,
4219 static_cast<sal_Int16>(nLevel) );
4220 SetAttr( aNewListLevelItem );
4221}
4222
4224{
4225 return GetpSwAttrSet() &&
4226 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL, false ) == SfxItemState::SET;
4227}
4228
4230{
4231 int nAttrListLevel = 0;
4232
4233 const SfxInt16Item& aListLevelItem =
4235 nAttrListLevel = static_cast<int>(aListLevelItem.GetValue());
4236
4237 return nAttrListLevel;
4238}
4239
4241{
4242 assert(SwListRedlineType::SHOW != eRedline ||
4243 !GetNum(nullptr, SwListRedlineType::SHOW) || !mpNodeNumRLHidden || // must be in sync
4244 GetNum(nullptr, SwListRedlineType::SHOW)->GetLevelInListTree() ==
4245 mpNodeNumRLHidden->GetLevelInListTree());
4246 return GetNum(nullptr, eRedline) ? GetNum(nullptr, eRedline)->GetLevelInListTree() : -1;
4247}
4248
4249void SwTextNode::SetListRestart( bool bRestart )
4250{
4251 if ( !bRestart )
4252 {
4253 // attribute not contained in paragraph style's attribute set. Thus,
4254 // it can be reset to the attribute pool default by resetting the attribute.
4256 }
4257 else
4258 {
4259 SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART,
4260 true );
4261 SetAttr( aNewIsRestartItem );
4262 }
4263}
4264
4266{
4267 const SfxBoolItem& aIsRestartItem = GetAttr( RES_PARATR_LIST_ISRESTART );
4268
4269 return aIsRestartItem.GetValue();
4270}
4271
4277{
4278 const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4279 if ( pRule && IsCountedInList())
4280 {
4281 const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
4283 // True if we have something in label text or there is a non-empty
4284 // FollowedBy separator (space, tab or whatsoever)
4285 return rFormat.GetLabelFollowedBy() != SvxNumberFormat::LabelFollowedBy::NOTHING ||
4286 !pRule->MakeNumString(*GetNum()).isEmpty();
4287 else
4288 // #i87154#
4289 // Correction of #newlistlevelattrs#:
4290 // The numbering type has to be checked for bullet lists.
4291 return SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ||
4292 !pRule->MakeNumString(*(GetNum())).isEmpty();
4293 }
4294
4295 return false;
4296}
4297
4299{
4300 const bool bChanged( HasAttrListRestartValue()
4301 ? GetAttrListRestartValue() != nNumber
4302 : nNumber != USHRT_MAX );
4303
4304 if ( !bChanged && HasAttrListRestartValue() )
4305 return;
4306
4307 if ( nNumber == USHRT_MAX )
4308 {
4310 }
4311 else
4312 {
4313 SfxInt16Item aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE,
4314 static_cast<sal_Int16>(nNumber) );
4315 SetAttr( aNewListRestartValueItem );
4316 }
4317}
4318
4320{
4321 return GetpSwAttrSet() &&
4322 GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, false ) == SfxItemState::SET;
4323}
4325{
4326 OSL_ENSURE( HasAttrListRestartValue(),
4327 "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
4328
4329 const SfxInt16Item& aListRestartValueItem =
4331 return static_cast<SwNumberTree::tSwNumTreeNumber>(aListRestartValueItem.GetValue());
4332}
4333
4335{
4336 SwNumberTree::tSwNumTreeNumber nListRestartValue = 1;
4337
4339 {
4340 nListRestartValue = GetAttrListRestartValue();
4341 }
4342 else
4343 {
4344 SwNumRule* pRule = GetNumRule();
4345 if ( pRule )
4346 {
4347 const SwNumFormat* pFormat =
4348 pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(GetAttrListLevel()) );
4349 if ( pFormat )
4350 {
4351 nListRestartValue = pFormat->GetStart();
4352 }
4353 }
4354 }
4355
4356 return nListRestartValue;
4357}
4358
4360{
4362}
4363
4365{
4366 const SwDoc& rDoc = GetDoc();
4367 return !rDoc.IsInReading() && !rDoc.IsInDtor();
4368}
4369
4370void SwTextNode::SetCountedInList( bool bCounted )
4371{
4372 if ( bCounted )
4373 {
4374 // attribute not contained in paragraph style's attribute set. Thus,
4375 // it can be reset to the attribute pool default by resetting the attribute.
4377 }
4378 else
4379 {
4380 SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, false );
4381 SetAttr( aIsCountedInListItem );
4382 }
4383}
4384
4386{
4387 const SfxBoolItem& aIsCountedInListItem = GetAttr( RES_PARATR_LIST_ISCOUNTED );
4388
4389 return aIsCountedInListItem.GetValue();
4390}
4391
4392static SwList * FindList(SwTextNode *const pNode)
4393{
4394 const OUString sListId = pNode->GetListId();
4395 if (!sListId.isEmpty())
4396 {
4397 auto & rIDLA(pNode->GetDoc().getIDocumentListsAccess());
4398 SwList* pList = rIDLA.getListByName( sListId );
4399 if ( pList == nullptr )
4400 {
4401 // Create corresponding list.
4402 SwNumRule* pNumRule = pNode->GetNumRule();
4403 if ( pNumRule )
4404 {
4405 pList = rIDLA.createList(sListId, pNode->GetNumRule()->GetName());
4406 }
4407 }
4408 OSL_ENSURE( pList != nullptr,
4409 "<SwTextNode::AddToList()> - no list for given list id. Serious defect" );
4410 return pList;
4411 }
4412 return nullptr;
4413}
4414
4416{
4417 if ( IsInList() )
4418 {
4419 OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" );
4420 return;
4421 }
4422
4423 SwList *const pList(FindList(this));
4424 if (!(pList && GetNodes().IsDocNodes())) // not for undo nodes
4425 return;
4426
4427 assert(!mpNodeNum);
4428 mpNodeNum.reset(new SwNodeNum(this, false));
4430
4431 // set redline lists
4432 // "default" list: visible items in Show Changes mode (tracked insertions and deletions)
4433 // "hidden" list: visible items in Hide Changes mode (tracked insertions, but not deletions)
4434 // "orig" list: visible items rejecting all changes (no tracked insertions and deletions)
4435 bool bRecordChanges = GetDoc().GetDocShell() && GetDoc().GetDocShell()->IsChangeRecording();
4436 if (!bRecordChanges || GetDoc().IsInXMLImport() || GetDoc().IsInWriterfilterImport() )
4437 {
4439 SwRedlineTable::size_type nRedlPos = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Insert);
4440 // paragraph start is not in a tracked insertion
4441 if ( SwRedlineTable::npos == nRedlPos || GetIndex() <= rRedTable[nRedlPos]->Start()->GetNode().GetIndex() )
4442 {
4443 AddToListOrig();
4444
4445 // if the paragraph is not deleted, add to the "hidden" list, too
4446 SwRedlineTable::size_type nRedlPosDel = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Delete);
4447 if ( SwRedlineTable::npos == nRedlPosDel )
4449 }
4450 // inserted paragraph, e.g. during file load, add to the "hidden" list
4451 else if ( SwRedlineTable::npos != nRedlPos )
4453 }
4454 else if ( bRecordChanges )
4456
4457 // iterate all frames & if there's one with hidden layout...
4459 for (SwTextFrame* pFrame = iter.First(); pFrame && !mpNodeNumRLHidden; pFrame = iter.Next())
4460 {
4461 if (pFrame->getRootFrame()->IsHideRedlines())
4462 {
4463 if (pFrame->GetTextNodeForParaProps() == this)
4464 {
4466 }
4467 break; // assume it's consistent, need to check only once
4468 }
4469 }
4470}
4471
4473{
4475 return;
4476
4477 SwList *const pList(FindList(this));
4478 if (pList)
4479 {
4480 assert(!mpNodeNumRLHidden);
4481 mpNodeNumRLHidden.reset(new SwNodeNum(this, true));
4483 }
4484}
4485
4487{
4488 if (mpNodeNumOrig)
4489 return;
4490
4491 SwList *const pList(FindList(this));
4492 if (pList)
4493 {
4494 assert(!mpNodeNumOrig);
4495 mpNodeNumOrig.reset(new SwNodeNum(this, true));
4497 }
4498}
4499
4501{
4502 // sw_redlinehide: ensure it's removed from the other half too!
4505 if ( IsInList() )
4506 {
4508 mpNodeNum.reset();
4509
4510 SetWordCountDirty( true );
4511 }
4512}
4513
4515{
4516 if (mpNodeNumRLHidden) // direct access because RemoveFromList doesn't have layout
4517 {
4518 assert(mpNodeNumRLHidden->GetParent() || !GetNodes().IsDocNodes());
4520 mpNodeNumRLHidden.reset();
4521
4522 SetWordCountDirty( true );
4523 }
4524}
4525
4527{
4528 if (mpNodeNumOrig) // direct access because RemoveFromList doesn't have layout
4529 {
4530 assert(mpNodeNumOrig->GetParent() || !GetNodes().IsDocNodes());
4532 mpNodeNumOrig.reset();
4533
4534 SetWordCountDirty( true );
4535 }
4536}
4537
4539{
4540 return GetNum() != nullptr && GetNum()->GetParent() != nullptr;
4541}
4542
4544{
4545 bool bResult = false;
4546
4547 SwNodeNum const*const pNum(GetNum(&rLayout));
4548 if (pNum && pNum->GetNumRule())
4549 bResult = pNum->IsFirst();
4550
4551 return bResult;
4552}
4553
4554void SwTextNode::SetListId(OUString const& rListId)
4555{
4556 const SfxStringItem& rListIdItem =
4558 if (rListIdItem.GetValue() != rListId)
4559 {
4560 if (rListId.isEmpty())
4561 {
4563 }
4564 else
4565 {
4566 SfxStringItem aNewListIdItem(RES_PARATR_LIST_ID, rListId);
4567 SetAttr( aNewListIdItem );
4568 }
4569 }
4570}
4571
4573{
4574 const SfxStringItem& rListIdItem =
4576 const OUString& sListId {rListIdItem.GetValue()};
4577
4578 // As long as no explicit list id attribute is set, use the list id of
4579 // the list, which has been created for the applied list style.
4580 if (sListId.isEmpty())
4581 {
4582 SwNumRule* pRule = GetNumRule();
4583 if ( pRule )
4584 {
4585 return pRule->GetDefaultListId();
4586 }
4587 }
4588
4589 return sListId;
4590}
4591
4607{
4608 bool bAreListLevelIndentsApplicable( true );
4609
4610 if ( !GetNum() || !GetNum()->GetNumRule() )
4611 {
4612 // no list style applied to paragraph
4613 bAreListLevelIndentsApplicable = false;
4614 }
4615 else if ( HasSwAttrSet() &&
4616 GetpSwAttrSet()->GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
4617 {
4618 // paragraph has hard-set indent attributes
4619 bAreListLevelIndentsApplicable = false;
4620 }
4621 else if ( HasSwAttrSet() &&
4622 GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4623 {
4624 // list style is directly applied to paragraph and paragraph has no
4625 // hard-set indent attributes
4626 bAreListLevelIndentsApplicable = true;
4627 }
4628 else
4629 {
4630 // list style is applied through one of the paragraph styles and
4631 // paragraph has no hard-set indent attributes
4632
4633 // check, paragraph's
4634 const SwTextFormatColl* pColl = GetTextColl();
4635 while ( pColl )
4636 {
4637 if ( pColl->GetAttrSet().GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
4638 {
4639 // indent attributes found in the paragraph style hierarchy.
4640 bAreListLevelIndentsApplicable = false;
4641 break;
4642 }
4643
4644 if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4645 {
4646 // paragraph style with the list style found and until now no
4647 // indent attributes are found in the paragraph style hierarchy.
4648 bAreListLevelIndentsApplicable = true;
4649 break;
4650 }
4651
4652 pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
4653 OSL_ENSURE( pColl,
4654 "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
4655 }
4656 }
4657
4658 return bAreListLevelIndentsApplicable;
4659}
4660
4669bool SwTextNode::GetListTabStopPosition( tools::Long& nListTabStopPosition ) const
4670{
4671 bool bListTabStopPositionProvided(false);
4672
4673 const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4674 if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4675 {
4676 const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4679 {
4680 bListTabStopPositionProvided = true;
4681 nListTabStopPosition = rFormat.GetListtabPos();
4682
4684 {
4685 // tab stop position are treated to be relative to the "before text"
4686 // indent value of the paragraph. Thus, adjust <nListTabStopPos>.
4688 {
4689 nListTabStopPosition -= rFormat.GetIndentAt();
4690 }
4692 {
4694 nListTabStopPosition -= aItem.GetTextLeft();
4695 }
4696 }
4697 }
4698 }
4699
4700 return bListTabStopPositionProvided;
4701}
4702
4704{
4705 const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4706 if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4707 {
4708 const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4710 {
4711 return rFormat.GetLabelFollowedByAsString();
4712 }
4713 }
4714
4715 return OUString();
4716}
4717
4719{
4720 sal_Int32 nStartPos;
4721 sal_Int32 nEndPos;
4722 // Update of the flags is done inside GetBoundsOfHiddenRange()
4723 SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos, nEndPos );
4724}
4725
4726// #i12836# enhanced pdf export
4728{
4730 return true;
4731
4732 const SwSectionNode* pSectNd = FindSectionNode();
4733 return pSectNd && pSectNd->GetSection().IsHiddenFlag();
4734}
4735
4736namespace {
4737 // Helper class for special handling of setting attributes at text node:
4738 // In constructor an instance of the helper class recognize whose attributes
4739 // are set and perform corresponding actions before the intrinsic set of
4740 // attributes has been taken place.
4741 // In the destructor - after the attributes have been set at the text
4742 // node - corresponding actions are performed.
4743 // The following is handled:
4744 // (1) When the list style attribute - RES_PARATR_NUMRULE - is set,
4745 // (A) list style attribute is empty -> the text node is removed from
4746 // its list.
4747 // (B) list style attribute is not empty
4748 // (a) text node has no list style -> add text node to its list after
4749 // the attributes have been set.
4750 // (b) text node has list style -> change of list style is notified
4751 // after the attributes have been set.
4752 // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed,
4753 // the text node is removed from its current list before the attributes
4754 // are set and added to its new list after the attributes have been set.
4755 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set
4756 // and changed after the attributes have been set
4757 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set
4758 // and changed after the attributes have been set
4759 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE -
4760 // is set and changed after the attributes have been set
4761 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set
4762 // and changed after the attributes have been set
4763 // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL.
4764 class HandleSetAttrAtTextNode
4765 {
4766 public:
4767 HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4768 const SfxPoolItem& pItem );
4769 HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4770 const SfxItemSet& rItemSet );
4771 ~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
4772
4773 private:
4774 SwTextNode& mrTextNode;
4775 bool mbAddTextNodeToList;
4776 bool mbUpdateListLevel;
4777 bool mbUpdateListRestart;
4778 bool mbUpdateListCount;
4779 // #i70748#
4780 bool mbOutlineLevelSet;
4781 };
4782
4783 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4784 const SfxPoolItem& pItem )
4785 : mrTextNode( rTextNode ),
4786 mbAddTextNodeToList( false ),
4787 mbUpdateListLevel( false ),
4788 mbUpdateListRestart( false ),
4789 mbUpdateListCount( false ),
4790 // #i70748#
4791 mbOutlineLevelSet( false )
4792 {
4793 switch ( pItem.Which() )
4794 {
4795 // handle RES_PARATR_NUMRULE
4796 case RES_PARATR_NUMRULE:
4797 {
4798 mrTextNode.RemoveFromList();
4799
4800 const SwNumRuleItem& rNumRuleItem =
4801 dynamic_cast<const SwNumRuleItem&>(pItem);
4802 if ( !rNumRuleItem.GetValue().isEmpty() )
4803 {
4804 mbAddTextNodeToList = true;
4805 // #i105562#
4806
4807 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4808 }
4809 }
4810 break;
4811
4812 // handle RES_PARATR_LIST_ID
4813 case RES_PARATR_LIST_ID:
4814 {
4815 const SfxStringItem& rListIdItem =
4816 dynamic_cast<const SfxStringItem&>(pItem);
4817 OSL_ENSURE( rListIdItem.GetValue().getLength() > 0,
4818 "<HandleSetAttrAtTextNode(..)> - empty list id attribute not expected. Serious defect." );
4819 const OUString sListIdOfTextNode = rTextNode.GetListId();
4820 if ( rListIdItem.GetValue() != sListIdOfTextNode )
4821 {
4822 mbAddTextNodeToList = true;
4823 if ( mrTextNode.IsInList() )
4824 {
4825 mrTextNode.RemoveFromList();
4826 }
4827 }
4828 }
4829 break;
4830
4831 // handle RES_PARATR_LIST_LEVEL
4833 {
4834 const SfxInt16Item& aListLevelItem =
4835 dynamic_cast<const SfxInt16Item&>(pItem);
4836 if ( aListLevelItem.GetValue() != mrTextNode.GetAttrListLevel() )
4837 {
4838 mbUpdateListLevel = true;
4839 }
4840 }
4841 break;
4842
4843 // handle RES_PARATR_LIST_ISRESTART
4845 {
4846 const SfxBoolItem& aListIsRestartItem =
4847 dynamic_cast<const SfxBoolItem&>(pItem);
4848 if ( aListIsRestartItem.GetValue() !=
4849 mrTextNode.IsListRestart() )
4850 {
4851 mbUpdateListRestart = true;
4852 }
4853 }
4854 break;
4855
4856 // handle RES_PARATR_LIST_RESTARTVALUE
4858 {
4859 const SfxInt16Item& aListRestartValueItem =
4860 dynamic_cast<const SfxInt16Item&>(pItem);
4861 if ( !mrTextNode.HasAttrListRestartValue() ||
4862 aListRestartValueItem.GetValue() != mrTextNode.GetAttrListRestartValue() )
4863 {
4864 mbUpdateListRestart = true;
4865 }
4866 }
4867 break;
4868
4869 // handle RES_PARATR_LIST_ISCOUNTED
4871 {
4872 const SfxBoolItem& aIsCountedInListItem =
4873 dynamic_cast<const SfxBoolItem&>(pItem);
4874 if ( aIsCountedInListItem.GetValue() !=
4875 mrTextNode.IsCountedInList() )
4876 {
4877 mbUpdateListCount = true;
4878 }
4879 }
4880 break;
4881
4882 // #i70748#
4883 // handle RES_PARATR_OUTLINELEVEL
4885 {
4886 const SfxUInt16Item& aOutlineLevelItem =
4887 dynamic_cast<const SfxUInt16Item&>(pItem);
4888 if ( aOutlineLevelItem.GetValue() != mrTextNode.GetAttrOutlineLevel() )
4889 {
4890 mbOutlineLevelSet = true;
4891 }
4892 }
4893 break;
4894 }
4895
4896 }
4897
4898 HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4899 const SfxItemSet& rItemSet )
4900 : mrTextNode( rTextNode ),
4901 mbAddTextNodeToList( false ),
4902 mbUpdateListLevel( false ),
4903 mbUpdateListRestart( false ),
4904 mbUpdateListCount( false ),
4905 // #i70748#
4906 mbOutlineLevelSet( false )
4907 {
4908 // handle RES_PARATR_NUMRULE
4909 if ( const SwNumRuleItem* pNumRuleItem = rItemSet.GetItemIfSet( RES_PARATR_NUMRULE, false ) )
4910 {
4911 mrTextNode.RemoveFromList();
4912
4913 if ( !pNumRuleItem->GetValue().isEmpty() )
4914 {
4915 mbAddTextNodeToList = true;
4916 // #i70748#
4917 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4918 }
4919 }
4920
4921 // handle RES_PARATR_LIST_ID
4922 if ( const SfxStringItem* pListIdItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ID, false ) )
4923 {
4924 const OUString sListIdOfTextNode = mrTextNode.GetListId();
4925 if ( pListIdItem->GetValue() != sListIdOfTextNode )
4926 {
4927 mbAddTextNodeToList = true;
4928 if ( mrTextNode.IsInList() )
4929 {
4930 mrTextNode.RemoveFromList();
4931 }
4932 }
4933 }
4934
4935 // handle RES_PARATR_LIST_LEVEL
4936 if ( const SfxInt16Item* pListLevelItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_LEVEL, false ) )
4937 {
4938 if (pListLevelItem->GetValue() != mrTextNode.GetAttrListLevel())
4939 {
4940 mbUpdateListLevel = true;
4941 }
4942 }
4943
4944 // handle RES_PARATR_LIST_ISRESTART
4945 if ( const SfxBoolItem* pListIsRestartItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISRESTART, false ) )
4946 {
4947 if (pListIsRestartItem->GetValue() != mrTextNode.IsListRestart())
4948 {
4949 mbUpdateListRestart = true;
4950 }
4951 }
4952
4953 // handle RES_PARATR_LIST_RESTARTVALUE
4954 if ( const SfxInt16Item* pListRestartValueItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_RESTARTVALUE, false ) )
4955 {
4956 if ( !mrTextNode.HasAttrListRestartValue() ||
4957 pListRestartValueItem->GetValue() != mrTextNode.GetAttrListRestartValue() )
4958 {
4959 mbUpdateListRestart = true;
4960 }
4961 }
4962
4963 // handle RES_PARATR_LIST_ISCOUNTED
4964 if ( const SfxBoolItem* pIsCountedInListItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISCOUNTED, false ) )
4965 {
4966 if (pIsCountedInListItem->GetValue() != mrTextNode.IsCountedInList())
4967 {
4968 mbUpdateListCount = true;
4969 }
4970 }
4971
4972 // #i70748#
4973 // handle RES_PARATR_OUTLINELEVEL
4974 if ( const SfxUInt16Item* pOutlineLevelItem = rItemSet.GetItemIfSet( RES_PARATR_OUTLINELEVEL, false ) )
4975 {
4976 if (pOutlineLevelItem->GetValue() != mrTextNode.GetAttrOutlineLevel())
4977 {
4978 mbOutlineLevelSet = true;
4979 }
4980 }
4981 }
4982
4983 HandleSetAttrAtTextNode::~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
4984 {
4985 if ( mbAddTextNodeToList )
4986 {
4987 SwNumRule* pNumRuleAtTextNode = mrTextNode.GetNumRule();
4988 if ( pNumRuleAtTextNode )
4989 {
4990 mrTextNode.AddToList();
4991 }
4992 }
4993 else
4994 {
4995 if ( mbUpdateListLevel && mrTextNode.IsInList() )
4996 {
4997 auto const nLevel(mrTextNode.GetAttrListLevel());
4998 const SwDoc& rDoc(mrTextNode.GetDoc());
4999 mrTextNode.DoNum(
5000 [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
5001 }
5002
5003 if ( mbUpdateListRestart && mrTextNode.IsInList() )
5004 {
5005 const SwDoc& rDoc(mrTextNode.GetDoc());
5006 mrTextNode.DoNum(
5007 [&rDoc](SwNodeNum & rNum) {
5008 rNum.InvalidateMe();
5009 rNum.NotifyInvalidSiblings(rDoc);
5010 });
5011 }
5012
5013 if (mbUpdateListCount && mrTextNode.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode))
5014 {
5015 // Repaint all text frames that belong to this numbering to avoid outdated generated
5016 // numbers.
5017 const SwDoc& rDoc(mrTextNode.GetDoc());
5018 mrTextNode.DoNum(
5019 [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
5020 }
5021 }
5022
5023 // #i70748#
5024 if (!mbOutlineLevelSet)
5025 return;
5026
5027 mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode);
5028 if (mrTextNode.GetAttrOutlineLevel() == 0)
5029 {
5030 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5031 }
5032 else
5033 {
5034 if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE )
5035 != SfxItemState::SET )
5036 {
5037 mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
5038 }
5039 }
5040 }
5041 // End of class <HandleSetAttrAtTextNode>
5042}
5043
5045{
5046 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5047 mbInSetOrResetAttr = true;
5048
5049 HandleSetAttrAtTextNode aHandleSetAttr( *this, pItem );
5050
5051 bool bRet = SwContentNode::SetAttr( pItem );
5052
5053 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5054
5055 return bRet;
5056}
5057
5059{
5060 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5061 mbInSetOrResetAttr = true;
5062
5063 HandleSetAttrAtTextNode aHandleSetAttr( *this, rSet );
5064
5065 bool bRet = SwContentNode::SetAttr( rSet );
5066
5067 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5068
5069 return bRet;
5070}
5071
5072void SwTextNode::SetInSwUndo(bool bInUndo)
5073{
5074 m_bInUndo = bInUndo;
5075}
5076
5077namespace {
5078 // Helper class for special handling of resetting attributes at text node:
5079 // In constructor an instance of the helper class recognize whose attributes
5080 // are reset and perform corresponding actions before the intrinsic reset of
5081 // attributes has been taken place.
5082 // In the destructor - after the attributes have been reset at the text
5083 // node - corresponding actions are performed.
5084 // The following is handled:
5085 // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset,
5086 // the text is removed from its list before the attributes have been reset.
5087 // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset,
5088 // the text is removed from its list before the attributes have been reset.
5089 // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset.
5090 // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset.
5091 // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset.
5092 // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset.
5093 // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset.
5094 class HandleResetAttrAtTextNode
5095 {
5096 public:
5097 HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5098 const sal_uInt16 nWhich1,
5099 sal_uInt16 nWhich2 );
5100 HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5101 const std::vector<sal_uInt16>& rWhichArr );
5102 explicit HandleResetAttrAtTextNode( SwTextNode& rTextNode );
5103
5104 ~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
5105
5106 private:
5107 SwTextNode& mrTextNode;
5108 bool mbListStyleOrIdReset;
5109 bool mbUpdateListLevel;
5110 bool mbUpdateListRestart;
5111 bool mbUpdateListCount;
5112
5113 void init( sal_uInt16 nWhich, bool& rbRemoveFromList );
5114 };
5115
5116 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5117 const sal_uInt16 nWhich1,
5118 sal_uInt16 nWhich2 )
5119 : mrTextNode( rTextNode ),
5120 mbListStyleOrIdReset( false ),
5121 mbUpdateListLevel( false ),
5122 mbUpdateListRestart( false ),
5123 mbUpdateListCount( false )
5124 {
5125 if ( nWhich2 < nWhich1 )
5126 nWhich2 = nWhich1;
5127 bool bRemoveFromList( false );
5128 for ( sal_uInt16 nWhich = nWhich1; nWhich <= nWhich2; ++nWhich )
5129 init( nWhich, bRemoveFromList );
5130 if ( bRemoveFromList && mrTextNode.IsInList() )
5131 mrTextNode.RemoveFromList();
5132 }
5133
5134 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
5135 const std::vector<sal_uInt16>& rWhichArr )
5136 : mrTextNode( rTextNode ),
5137 mbListStyleOrIdReset( false ),
5138 mbUpdateListLevel( false ),
5139 mbUpdateListRestart( false ),
5140 mbUpdateListCount( false )
5141 {
5142 bool bRemoveFromList( false );
5143 for ( sal_uInt16 nWhich : rWhichArr )
5144 init( nWhich, bRemoveFromList );
5145 if ( bRemoveFromList && mrTextNode.IsInList() )
5146 mrTextNode.RemoveFromList();
5147 }
5148
5149 HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode )
5150 : mrTextNode( rTextNode ),
5151 mbListStyleOrIdReset( true ),
5152 mbUpdateListLevel( false ),
5153 mbUpdateListRestart( false ),
5154 mbUpdateListCount( false )
5155 {
5156 if ( rTextNode.IsInList() )
5157 {
5158 rTextNode.RemoveFromList();
5159 }
5160 // #i70748#
5161 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5162 }
5163
5164 void HandleResetAttrAtTextNode::init( sal_uInt16 rWhich, bool& rbRemoveFromList )
5165 {
5166 if ( rWhich == RES_PARATR_NUMRULE )
5167 {
5168 rbRemoveFromList = rbRemoveFromList ||
5169 mrTextNode.GetNumRule() != nullptr;
5170 mbListStyleOrIdReset = true;
5171 }
5172 else if ( rWhich == RES_PARATR_LIST_ID )
5173 {
5174 rbRemoveFromList = rbRemoveFromList ||
5175 ( mrTextNode.GetpSwAttrSet() &&
5176 mrTextNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, false ) == SfxItemState::SET );
5177 mbListStyleOrIdReset = true;
5178 }
5179 else if ( rWhich == RES_PARATR_OUTLINELEVEL )
5180 mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
5181 else if ( rWhich == RES_BACKGROUND )
5182 mrTextNode.ResetAttr( XATTR_FILL_FIRST, XATTR_FILL_LAST );
5183
5184 if ( !rbRemoveFromList )
5185 {
5186 // RES_PARATR_LIST_LEVEL
5187 mbUpdateListLevel = mbUpdateListLevel ||
5188 ( rWhich == RES_PARATR_LIST_LEVEL &&
5189 mrTextNode.HasAttrListLevel() );
5190
5191 // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE
5192 mbUpdateListRestart = mbUpdateListRestart ||
5193 ( rWhich == RES_PARATR_LIST_ISRESTART &&
5194 mrTextNode.IsListRestart() ) ||
5195 ( rWhich == RES_PARATR_LIST_RESTARTVALUE &&
5196 mrTextNode.HasAttrListRestartValue() );
5197
5198 // RES_PARATR_LIST_ISCOUNTED
5199 mbUpdateListCount = mbUpdateListCount ||
5200 ( rWhich == RES_PARATR_LIST_ISCOUNTED &&
5201 !mrTextNode.IsCountedInList() );
5202 }
5203 }
5204
5205 HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5206 {
5207 if ( mbListStyleOrIdReset && !mrTextNode.IsInList() )
5208 {
5209 // check, if in spite of the reset of the list style or the list id
5210 // the paragraph still has to be added to a list.
5211 if (mrTextNode.GetNumRule() && !mrTextNode.GetListId().isEmpty())
5212 {
5213 // #i96062#
5214 // If paragraph has no list level attribute set and list style
5215 // is the outline style, apply outline level as the list level.
5216 if ( !mrTextNode.HasAttrListLevel() &&
5217 mrTextNode.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
5218 mrTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
5219 {
5220 int nNewListLevel = mrTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
5221 if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
5222 {
5223 mrTextNode.SetAttrListLevel( nNewListLevel );
5224 }
5225 }
5226 mrTextNode.AddToList();
5227 }
5228 // #i70748#
5229 // #i105562#
5230 else
5231 {
5232 if (mrTextNode.GetpSwAttrSet()
5233 && mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false).GetValue() > 0)
5234 {
5235 mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
5236 }
5237 }
5238 }
5239
5240 if ( !mrTextNode.IsInList() )
5241 return;
5242
5243 if ( mbUpdateListLevel )
5244 {
5245 auto const nLevel(mrTextNode.GetAttrListLevel());
5246 const SwDoc& rDoc(mrTextNode.GetDoc());
5247 mrTextNode.DoNum(
5248 [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
5249 }
5250
5251 if ( mbUpdateListRestart )
5252 {
5253 const SwDoc& rDoc(mrTextNode.GetDoc());
5254 mrTextNode.DoNum(
5255 [&rDoc](SwNodeNum & rNum) {
5256 rNum.InvalidateMe();
5257 rNum.NotifyInvalidSiblings(rDoc);
5258 });
5259 }
5260
5261 if ( mbUpdateListCount )
5262 {
5263 const SwDoc& rDoc(mrTextNode.GetDoc());
5264 mrTextNode.DoNum(
5265 [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
5266 }
5267 }
5268 // End of class <HandleResetAttrAtTextNode>
5269}
5270
5271bool SwTextNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
5272{
5273 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5274 mbInSetOrResetAttr = true;
5275
5276 HandleResetAttrAtTextNode aHandleResetAttr( *this, nWhich1, nWhich2 );
5277
5278 bool bRet = SwContentNode::ResetAttr( nWhich1, nWhich2 );
5279
5280 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5281
5282 return bRet;
5283}
5284
5285bool SwTextNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
5286{
5287 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5288 mbInSetOrResetAttr = true;
5289
5290 HandleResetAttrAtTextNode aHandleResetAttr( *this, rWhichArr );
5291
5292 bool bRet = SwContentNode::ResetAttr( rWhichArr );
5293
5294 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5295
5296 return bRet;
5297}
5298
5300{
5301 const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5302 mbInSetOrResetAttr = true;
5303
5304 HandleResetAttrAtTextNode aHandleResetAttr( *this );
5305
5306 const sal_uInt16 nRet = SwContentNode::ResetAllAttr();
5307
5308 mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5309
5310 return nRet;
5311}
5312
5314{
5315 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextNode"));
5316 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
5317 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
5318
5319 OUString sText = GetText();
5320 for (int i = 0; i < 32; ++i)
5321 sText = sText.replace(i, '*');
5322 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Text"));
5323 (void)xmlTextWriterWriteString(pWriter, BAD_CAST(sText.toUtf8().getStr()));
5324 (void)xmlTextWriterEndElement(pWriter);
5325
5326 if (GetFormatColl())
5327 {
5328 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
5329 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toUtf8().getStr()));
5330 (void)xmlTextWriterEndElement(pWriter);
5331 }
5332
5333 if (HasSwAttrSet())
5334 {
5335 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAttrSet"));
5336 GetSwAttrSet().dumpAsXml(pWriter);
5337 (void)xmlTextWriterEndElement(pWriter);
5338 }
5339
5340 if (HasHints())
5341 {
5342 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwpHints"));
5343 const SwpHints& rHints = GetSwpHints();
5344 for (size_t i = 0; i < rHints.Count(); ++i)
5345 rHints.Get(i)->dumpAsXml(pWriter);
5346 (void)xmlTextWriterEndElement(pWriter);
5347 }
5348
5349 (void)xmlTextWriterEndElement(pWriter);
5350}
5351
5352sal_uInt32 SwTextNode::GetRsid( sal_Int32 nStt, sal_Int32 nEnd ) const
5353{
5354 SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet( const_cast<SwAttrPool&>((GetDoc().GetAttrPool())) );
5355 if (GetParaAttr(aSet, nStt, nEnd))
5356 {
5357 const SvxRsidItem* pRsid = aSet.GetItem<SvxRsidItem>(RES_CHRATR_RSID);
5358 if( pRsid )
5359 return pRsid->GetValue();
5360 }
5361
5362 return 0;
5363}
5364
5365sal_uInt32 SwTextNode::GetParRsid() const
5366{
5367 return reinterpret_cast<const SvxRsidItem&>(GetAttr( RES_PARATR_RSID )).GetValue();
5368}
5369
5370bool SwTextNode::CompareParRsid( const SwTextNode &rTextNode ) const
5371{
5372 sal_uInt32 nThisRsid = GetParRsid();
5373 sal_uInt32 nRsid = rTextNode.GetParRsid();
5374
5375 return nThisRsid == nRsid;
5376}
5377
5378bool SwTextNode::CompareRsid( const SwTextNode &rTextNode, sal_Int32 nStt1, sal_Int32 nStt2 ) const
5379{
5380 sal_uInt32 nThisRsid = GetRsid( nStt1, nStt1 );
5381 sal_uInt32 nRsid = rTextNode.GetRsid( nStt2, nStt2 );
5382
5383 return nThisRsid == nRsid;
5384}
5385
5386// sw::Metadatable
5388{
5389 return GetDoc().GetXmlIdRegistry();
5390}
5391
5393{
5394 return GetDoc().IsClipBoard();
5395}
5396
5398{
5399 return GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
5400}
5401
5403{
5404 return !GetDoc().IsInHeaderFooter( *this );
5405}
5406
5408{
5409 assert(!dynamic_cast<const sw::LegacyModifyHint*>(&rHint));
5411 CallSwClientNotify(rHint);
5412
5413 SwDoc& rDoc = GetDoc();
5414 // #125329# - assure that text node is in document nodes array
5415 if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5416 {
5417 rDoc.GetNodes().UpdateOutlineNode(*this);
5418 }
5419}
5420
5422{
5423 const auto pOldValue = rHint.m_pOld;
5424 const auto pNewValue = rHint.m_pNew;
5425 {
5427
5428 // Override Modify so that deleting styles works properly (outline
5429 // numbering!).
5430 // Never call ChgTextCollUpdateNum for Nodes in Undo.
5431 if( pOldValue
5432 && pNewValue
5433 && RES_FMT_CHG == pOldValue->Which()
5434 && GetRegisteredIn() == static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat
5435 && GetNodes().IsDocNodes() )
5436 {
5437 assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat));
5438 assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pNewValue)->pChangedFor