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