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