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