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