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 -= 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 (sal_uLong 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() != 0)
847  {
848  for (sal_uLong 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  assert(pFrame->GetMergedPara());
882  assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
883  assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
884  eMode = sw::FrameMode::New; // Existing is not idempotent!
885  }
886  }
887  else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
888  {
890  for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
891  {
892  if (auto const pMergedPara = pFrame->GetMergedPara())
893  {
894  if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
895  {
896  assert(pMergedPara->pFirstNode == &rNode);
898  }
899  break; // checking once is enough
900  }
901  else if (pFrame->getRootFrame()->HasMergedParas())
902  {
904  break; // checking once is enough
905  }
906  }
907  }
908 }
909 
911 {
912  const SwNodeNum* pNodeNum = rTextNode.GetNum();
913  if (!pNodeNum)
914  {
915  return false;
916  }
917 
918  const SwNumRule* pNumRule = pNodeNum->GetNumRule();
919  if (!pNumRule)
920  {
921  return false;
922  }
923 
924  const SwNumFormat* pFormat
925  = pNumRule->GetNumFormat(o3tl::narrowing<sal_uInt16>(rTextNode.GetAttrListLevel()));
926  if (!pFormat)
927  {
928  return false;
929  }
930 
931  switch (pFormat->GetNumberingType())
932  {
933  case SVX_NUM_NUMBER_NONE:
935  case SVX_NUM_BITMAP:
936  return false;
937  default:
938  return true;
939  }
940 }
941 } // namespace
942 
944 {
945  SwNodes& rNds = GetNodes();
946  SwNodeIndex aIdx( *this );
947  if( SwContentNode::CanJoinNext( &aIdx ) )
948  {
949  SwDoc& rDoc = rNds.GetDoc();
950  const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
951  pContentStore->Save(rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
952  SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
953  sal_Int32 nOldLen = m_Text.getLength();
954 
955  // METADATA: merge
956  JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len());
957 
958  std::unique_ptr<SwWrongList> pList = ReleaseWrong();
959  if( pList )
960  {
961  pList->JoinList( pTextNode->GetWrong(), nOldLen );
963  }
964  else
965  {
966  pList = pTextNode->ReleaseWrong();
967  if( pList )
968  {
969  pList->Move( 0, nOldLen );
971  }
972  }
973 
974  std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
975  if( pList3 )
976  {
977  pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen );
978  SetGrammarCheckDirty( true );
979  }
980  else
981  {
982  pList3 = pTextNode->ReleaseGrammarCheck();
983  if( pList3 )
984  {
985  pList3->MoveGrammar( 0, nOldLen );
986  SetGrammarCheckDirty( true );
987  }
988  }
989 
990  std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
991  if( pList2 )
992  {
993  pList2->JoinList( pTextNode->GetSmartTags(), nOldLen );
994  SetSmartTagDirty( true );
995  }
996  else
997  {
998  pList2 = pTextNode->ReleaseSmartTags();
999  if( pList2 )
1000  {
1001  pList2->Move( 0, nOldLen );
1002  SetSmartTagDirty( true );
1003  }
1004  }
1005 
1006  { // scope for SwIndex
1007  pTextNode->CutText( this, SwIndex(pTextNode), pTextNode->Len() );
1008  }
1009  // move all Bookmarks/TOXMarks
1010  if( !pContentStore->Empty())
1011  pContentStore->Restore( rDoc, GetIndex(), nOldLen );
1012 
1013  if( pTextNode->HasAnyIndex() )
1014  {
1015  // move all ShellCursor/StackCursor/UnoCursor out of delete range
1016  rDoc.CorrAbs( aIdx, SwPosition( *this ), nOldLen, true );
1017  }
1018  SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1019  bool bOldHasNumberingWhichNeedsLayoutUpdate = HasNumberingWhichNeedsLayoutUpdate(*pTextNode);
1020 
1021  rNds.Delete(aIdx);
1022  SetWrong( std::move(pList) );
1023  SetGrammarCheck( std::move(pList3) );
1024  SetSmartTags( std::move(pList2) );
1025 
1026  if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
1027  {
1028  // Repaint all text frames that belong to this numbering to avoid outdated generated
1029  // numbers.
1031  }
1032 
1033  CheckResetRedlineMergeFlag(*this, eOldMergeFlag == SwNode::Merge::First
1035  : sw::Recreate::No);
1036  }
1037  else {
1038  OSL_FAIL( "No TextNode." );
1039  }
1040 
1041  return this;
1042 }
1043 
1045 {
1046  SwNodes& rNds = GetNodes();
1047  SwNodeIndex aIdx( *this );
1048  if( SwContentNode::CanJoinPrev( &aIdx ) )
1049  {
1050  SwDoc& rDoc = rNds.GetDoc();
1051  const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1052  pContentStore->Save( rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
1053  SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
1054  const sal_Int32 nLen = pTextNode->Len();
1055 
1056  std::unique_ptr<SwWrongList> pList = pTextNode->ReleaseWrong();
1057  if( pList )
1058  {
1059  pList->JoinList( GetWrong(), Len() );
1061  ClearWrong();
1062  }
1063  else
1064  {
1065  pList = ReleaseWrong();
1066  if( pList )
1067  {
1068  pList->Move( 0, nLen );
1070  }
1071  }
1072 
1073  std::unique_ptr<SwGrammarMarkUp> pList3 = pTextNode->ReleaseGrammarCheck();
1074  if( pList3 )
1075  {
1076  pList3->JoinGrammarList( GetGrammarCheck(), Len() );
1077  SetGrammarCheckDirty( true );
1079  }
1080  else
1081  {
1082  pList3 = ReleaseGrammarCheck();
1083  if( pList3 )
1084  {
1085  pList3->MoveGrammar( 0, nLen );
1086  SetGrammarCheckDirty( true );
1087  }
1088  }
1089 
1090  std::unique_ptr<SwWrongList> pList2 = pTextNode->ReleaseSmartTags();
1091  if( pList2 )
1092  {
1093  pList2->JoinList( GetSmartTags(), Len() );
1094  SetSmartTagDirty( true );
1095  ClearSmartTags();
1096  }
1097  else
1098  {
1099  pList2 = ReleaseSmartTags();
1100  if( pList2 )
1101  {
1102  pList2->Move( 0, nLen );
1103  SetSmartTagDirty( true );
1104  }
1105  }
1106 
1107  { // scope for SwIndex
1108  pTextNode->CutText( this, SwIndex(this), SwIndex(pTextNode), nLen );
1109  }
1110  // move all Bookmarks/TOXMarks
1111  if( !pContentStore->Empty() )
1112  pContentStore->Restore( rDoc, GetIndex() );
1113 
1114  if( pTextNode->HasAnyIndex() )
1115  {
1116  // move all ShellCursor/StackCursor/UnoCursor out of delete range
1117  rDoc.CorrAbs( aIdx, SwPosition( *this ), nLen, true );
1118  }
1119  SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
1120  if (eOldMergeFlag == SwNode::Merge::First
1122  {
1123  sw::MoveDeletedPrevFrames(*pTextNode, *this);
1124  }
1125  rNds.Delete(aIdx);
1126  SetWrong( std::move(pList) );
1127  SetGrammarCheck( std::move(pList3) );
1128  SetSmartTags( std::move(pList2) );
1131  eOldMergeFlag == SwNode::Merge::NonFirst
1133  : sw::Recreate::No);
1134  }
1135  else {
1136  OSL_FAIL( "No TextNode." );
1137  }
1138 }
1139 
1140 // create an AttrSet with ranges for Frame-/Para/Char-attributes
1142 {
1143  OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
1144  SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );
1145 
1146  // put names of parent style and conditional style:
1147  const SwFormatColl* pAnyFormatColl = &GetAnyFormatColl();
1148  const SwFormatColl* pFormatColl = GetFormatColl();
1149  OUString sVal;
1151  SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
1152  if ( pFormatColl != pAnyFormatColl )
1154  SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
1155  aNewAttrSet.Put( aAnyFormatColl );
1156  aNewAttrSet.Put( aFormatColl );
1157 
1158  aNewAttrSet.SetParent( &pAnyFormatColl->GetAttrSet() );
1160 }
1161 
1162 // override SwIndexReg::Update => text hints do not need SwIndex for start/end!
1164  SwIndex const & rPos,
1165  const sal_Int32 nChangeLen,
1166  const bool bNegative,
1167  const bool bDelete )
1168 {
1169  SetAutoCompleteWordDirty( true );
1170 
1171  std::unique_ptr<SwpHts> pCollector;
1172  const sal_Int32 nChangePos = rPos.GetIndex();
1173 
1174  if ( HasHints() )
1175  {
1176  if ( bNegative )
1177  {
1178  std::vector<SwTextInputField*> aTextInputFields;
1179 
1180  const sal_Int32 nChangeEnd = nChangePos + nChangeLen;
1181  for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1182  {
1183  bool bTextAttrChanged = false;
1184  bool bStartOfTextAttrChanged = false;
1185  SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1186  if ( pHint->GetStart() > nChangePos )
1187  {
1188  if ( pHint->GetStart() > nChangeEnd )
1189  {
1190  pHint->SetStart( pHint->GetStart() - nChangeLen );
1191  }
1192  else
1193  {
1194  pHint->SetStart( nChangePos );
1195  }
1196  bStartOfTextAttrChanged = true;
1197  }
1198 
1199  const sal_Int32 * const pEnd = pHint->GetEnd();
1200  if (pEnd && *pEnd > nChangePos )
1201  {
1202  if( *pEnd > nChangeEnd )
1203  {
1204  pHint->SetEnd(*pEnd - nChangeLen);
1205  }
1206  else
1207  {
1208  pHint->SetEnd(nChangePos);
1209  }
1210  bTextAttrChanged = !bStartOfTextAttrChanged;
1211  }
1212 
1213  if ( bTextAttrChanged
1214  && pHint->Which() == RES_TXTATR_INPUTFIELD )
1215  {
1216  SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1217  if ( pTextInputField )
1218  aTextInputFields.push_back(pTextInputField);
1219  }
1220  }
1221 
1222  //wait until all the attribute positions are correct
1223  //before updating the field contents
1224  for (SwTextInputField* pTextInputField : aTextInputFields)
1225  {
1226  pTextInputField->UpdateFieldContent();
1227  }
1228 
1229  m_pSwpHints->MergePortions( *this );
1230  }
1231  else
1232  {
1233  bool bNoExp = false;
1234  bool bResort = false;
1235  bool bMergePortionsNeeded = false;
1236  const int coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
1237  std::vector<SwTextInputField*> aTextInputFields;
1238 
1239  bool aDontExp[ coArrSz ] = {};
1240 
1241  for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
1242  {
1243  bool bTextAttrChanged = false;
1244  SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
1245  const sal_Int32 * const pEnd = pHint->GetEnd();
1246  if ( pHint->GetStart() >= nChangePos )
1247  {
1248  pHint->SetStart( pHint->GetStart() + nChangeLen );
1249  if ( pEnd )
1250  {
1251  pHint->SetEnd(*pEnd + nChangeLen);
1252  }
1253  }
1254  else if ( pEnd && (*pEnd >= nChangePos) )
1255  {
1256  if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
1257  {
1258  pHint->SetEnd(*pEnd + nChangeLen);
1259  bTextAttrChanged = true;
1260  }
1261  else // *pEnd == nChangePos
1262  {
1263  const sal_uInt16 nWhich = pHint->Which();
1264 
1265  OSL_ENSURE(!isCHRATR(nWhich), "Update: char attr hint?");
1266  if (!(isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)))
1267  continue;
1268 
1269  const sal_uInt16 nWhPos = nWhich - RES_CHRATR_BEGIN;
1270 
1271  if( aDontExp[ nWhPos ] )
1272  continue;
1273 
1274  if ( pHint->DontExpand() )
1275  {
1276  pHint->SetDontExpand( false );
1277  bResort = true;
1278  // could have a continuation with IgnoreStart()...
1279  if (pHint->IsFormatIgnoreEnd())
1280  {
1281  bMergePortionsNeeded = true;
1282  }
1283  if ( pHint->IsCharFormatAttr() )
1284  {
1285  bNoExp = true;
1286  aDontExp[ RES_TXTATR_CHARFMT - RES_CHRATR_BEGIN ] = true;
1287  aDontExp[ RES_TXTATR_INETFMT - RES_CHRATR_BEGIN ] = true;
1288  }
1289  else
1290  aDontExp[ nWhPos ] = true;
1291  }
1292  else if( bNoExp )
1293  {
1294  if (!pCollector)
1295  {
1296  pCollector.reset( new SwpHts );
1297  }
1298  auto it = std::find_if(pCollector->begin(), pCollector->end(),
1299  [nWhich](const SwTextAttr *pTmp) { return nWhich == pTmp->Which(); });
1300  if (it != pCollector->end())
1301  {
1302  SwTextAttr *pTmp = *it;
1303  pCollector->erase( it );
1304  SwTextAttr::Destroy( pTmp, GetDoc().GetAttrPool() );
1305  }
1306  SwTextAttr * const pTmp =
1307  MakeTextAttr( GetDoc(),
1308  pHint->GetAttr(), nChangePos, nChangePos + nChangeLen);
1309  pCollector->push_back( pTmp );
1310  }
1311  else
1312  {
1313  pHint->SetEnd(*pEnd + nChangeLen);
1314  bTextAttrChanged = true;
1315  }
1316  }
1317  }
1318 
1319  if ( bTextAttrChanged
1320  && pHint->Which() == RES_TXTATR_INPUTFIELD )
1321  {
1322  SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
1323  if ( pTextInputField )
1324  aTextInputFields.push_back(pTextInputField);
1325  }
1326  }
1327 
1328  //wait until all the attribute positions are correct
1329  //before updating the field contents
1330  for (SwTextInputField* pTextInputField : aTextInputFields)
1331  {
1332  pTextInputField->UpdateFieldContent();
1333  }
1334 
1335  if (bMergePortionsNeeded)
1336  {
1337  m_pSwpHints->MergePortions(*this); // does Resort too
1338  }
1339  else if (bResort)
1340  {
1341  m_pSwpHints->Resort();
1342  }
1343  }
1344  }
1345 
1346  bool bSortMarks = false;
1347  SwIndexReg aTmpIdxReg;
1348  if ( !bNegative && !bDelete )
1349  {
1351  for (SwRangeRedline* pRedl : rTable)
1352  {
1353  if ( pRedl->HasMark() )
1354  {
1355  SwPosition* const pEnd = pRedl->End();
1356  if ( this == &pEnd->nNode.GetNode() &&
1357  *pRedl->GetPoint() != *pRedl->GetMark() )
1358  {
1359  SwIndex & rIdx = pEnd->nContent;
1360  if (nChangePos == rIdx.GetIndex())
1361  {
1362  rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1363  }
1364  }
1365  }
1366  else if ( this == &pRedl->GetPoint()->nNode.GetNode() )
1367  {
1368  SwIndex & rIdx = pRedl->GetPoint()->nContent;
1369  if (nChangePos == rIdx.GetIndex())
1370  {
1371  rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
1372  }
1373  // the unused position must not be on a SwTextNode
1374  bool const isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
1375  assert(!pRedl->GetBound(!isOneUsed).nNode.GetNode().IsTextNode());
1376  assert(!pRedl->GetBound(!isOneUsed).nContent.GetIdxReg()); (void)isOneUsed;
1377  }
1378  }
1379 
1380  // Bookmarks must never grow to either side, when editing (directly)
1381  // to the left or right (i#29942)! Exception: if the bookmark has
1382  // 2 positions and start == end, then expand it (tdf#96479)
1383  {
1384  bool bAtLeastOneBookmarkMoved = false;
1385  bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false;
1386  // A text node already knows its marks via its SwIndexes.
1388  const SwIndex* next;
1389  for (const SwIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
1390  {
1391  next = pIndex->GetNext();
1392  const sw::mark::IMark* pMark = pIndex->GetMark();
1393  if (!pMark)
1394  continue;
1395  // Only handle bookmarks once, if they start and end at this node as well.
1396  if (!aSeenMarks.insert(pMark).second)
1397  continue;
1398  const SwPosition* pEnd = &pMark->GetMarkEnd();
1399  SwIndex & rEndIdx = const_cast<SwIndex&>(pEnd->nContent);
1400  if( this == &pEnd->nNode.GetNode() &&
1401  rPos.GetIndex() == rEndIdx.GetIndex() )
1402  {
1403  if (&rEndIdx == next) // nasty corner case:
1404  { // don't switch to iterating aTmpIdxReg!
1405  next = rEndIdx.GetNext();
1406  }
1407  // tdf#96479: if start == end, ignore the other position
1408  // so it is moved!
1409  rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
1410  bAtLeastOneBookmarkMoved = true;
1411  }
1412  else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
1413  {
1414  if ( pMark->IsExpanded() )
1415  {
1416  const SwPosition* pStart = &pMark->GetMarkStart();
1417  if ( this == &pStart->nNode.GetNode()
1418  && rPos.GetIndex() == pStart->nContent.GetIndex() )
1419  {
1420  bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
1421  }
1422  }
1423  }
1424  }
1425 
1426  bSortMarks = bAtLeastOneBookmarkMoved && bAtLeastOneExpandedBookmarkAtInsertionPosition;
1427  }
1428 
1429  // at-char anchored flys shouldn't be moved, either.
1430  if (!m_bInUndo)
1431  {
1432  std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys());
1433  for (size_t i = 0; i != rFlys.size(); ++i)
1434  {
1435  SwFrameFormat const*const pFormat = rFlys[i];
1436  const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1437  const SwPosition* pContentAnchor = rAnchor.GetContentAnchor();
1438  if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pContentAnchor)
1439  {
1440  // The fly is at-char anchored and has an anchor position.
1441  SwIndex& rEndIdx = const_cast<SwIndex&>(pContentAnchor->nContent);
1442  if (&pContentAnchor->nNode.GetNode() == this && rEndIdx.GetIndex() == rPos.GetIndex())
1443  {
1444  // The anchor position is exactly our insert position.
1445  rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
1446  }
1447  }
1448  }
1449  }
1450 
1451  // The cursors of other shells shouldn't be moved, either.
1452  if (SwDocShell* pDocShell = GetDoc().GetDocShell())
1453  {
1454  if (pDocShell->GetWrtShell())
1455  {
1456  for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
1457  {
1458  auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell);
1459  if (!pWrtShell || pWrtShell == pDocShell->GetWrtShell())
1460  continue;
1461 
1462  SwShellCursor* pCursor = pWrtShell->GetCursor_();
1463  if (!pCursor)
1464  continue;
1465 
1466  SwIndex& rIndex = pCursor->Start()->nContent;
1467  if (&pCursor->Start()->nNode.GetNode() == this && rIndex.GetIndex() == rPos.GetIndex())
1468  {
1469  // The cursor position of this other shell is exactly our insert position.
1470  rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
1471  }
1472  }
1473  }
1474  }
1475  }
1476 
1477  // base class
1478  SwIndexReg::Update( rPos, nChangeLen, bNegative, bDelete );
1479 
1480  if (pCollector)
1481  {
1482  const size_t nCount = pCollector->size();
1483  for ( size_t i = 0; i < nCount; ++i )
1484  {
1485  m_pSwpHints->TryInsertHint( (*pCollector)[ i ], *this );
1486  }
1487  }
1488 
1489  aTmpIdxReg.MoveTo( *this );
1490  if ( bSortMarks )
1491  {
1493  }
1494 
1495  //Any drawing objects anchored into this text node may be sorted by their
1496  //anchor position which may have changed here, so resort them
1497  SwContentFrame* pContentFrame = getLayoutFrame(GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
1498  SwSortedObjs* pSortedObjs = pContentFrame ? pContentFrame->GetDrawObjs() : nullptr;
1499  if (pSortedObjs)
1500  pSortedObjs->UpdateAll();
1501 
1502  // Update the paragraph signatures.
1503  if (SwEditShell* pEditShell = GetDoc().GetEditShell())
1504  {
1505  pEditShell->ValidateParagraphSignatures(this, true);
1506  }
1507 
1508  // Inform LOK clients about change in position of redlines (if any)
1509  // Don't emit notifications during save: redline flags are temporarily changed during save, but
1510  // it's not useful to let clients know about such changes.
1511  if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
1512  return;
1513 
1515  for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
1516  {
1517  SwRangeRedline* pRedln = rTable[nRedlnPos];
1518  if (pRedln->HasMark())
1519  {
1520  if (this == &pRedln->End()->nNode.GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
1521  {
1522  // Redline is changed only when some change occurs before it
1523  if (nChangePos <= pRedln->Start()->nContent.GetIndex())
1524  {
1526  }
1527  }
1528  }
1529  else if (this == &pRedln->GetPoint()->nNode.GetNode())
1531  }
1532 }
1533 
1535  const SwTextFormatColl *pNewColl)
1536 {
1537  SwDoc& rDoc = GetDoc();
1538  // query the OutlineLevel and if it changed, notify the Nodes-Array!
1539  const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle() ?
1540  pOldColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
1541  const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
1542  pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
1543 
1544  if ( MAXLEVEL != nNewLevel && -1 != nNewLevel )
1545  {
1546  SetAttrListLevel(nNewLevel);
1547  }
1548  rDoc.GetNodes().UpdateOutlineNode(*this);
1549 
1550  SwNodes& rNds = GetNodes();
1551  // If Level 0 (Chapter), update the footnotes!
1552  if( ( !nNewLevel || !nOldLevel) && !rDoc.GetFootnoteIdxs().empty() &&
1554  rNds.IsDocNodes() )
1555  {
1556  SwNodeIndex aTmpIndex( rNds, GetIndex());
1557 
1558  rDoc.GetFootnoteIdxs().UpdateFootnote( aTmpIndex);
1559  }
1560 
1561  if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
1562  {
1563  // check the condition of the text node again
1564  ChkCondColl();
1565  }
1566 }
1567 
1568 // If positioned exactly at the end of a CharStyle or Hyperlink,
1569 // set its DontExpand flag.
1570 bool SwTextNode::DontExpandFormat( const SwIndex& rIdx, bool bFlag,
1571  bool bFormatToTextAttributes )
1572 {
1573  const sal_Int32 nIdx = rIdx.GetIndex();
1574  if (bFormatToTextAttributes && nIdx == m_Text.getLength())
1575  {
1576  FormatToTextAttr( this );
1577  }
1578 
1579  bool bRet = false;
1580  if ( HasHints() )
1581  {
1582  m_pSwpHints->SortIfNeedBe();
1583  int nPos = m_pSwpHints->GetLastPosSortedByEnd(nIdx);
1584  for ( ; nPos >= 0; --nPos)
1585  {
1586  SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( nPos );
1587  const sal_Int32 *pEnd = pTmp->GetEnd();
1588  if( !pEnd )
1589  continue;
1590  assert( *pEnd <= nIdx );
1591  if( nIdx != *pEnd )
1592  break;
1593  if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag()
1594  && *pEnd > pTmp->GetStart())
1595  {
1596  bRet = true;
1597  m_pSwpHints->NoteInHistory( pTmp );
1598  pTmp->SetDontExpand( bFlag );
1599  }
1600  }
1601  }
1602  return bRet;
1603 }
1604 
1605 static bool lcl_GetTextAttrDefault(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1606 {
1607  return ((nHintStart <= nIndex) && (nIndex < nHintEnd));
1608 }
1609 static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1610 {
1611  return ((nHintStart < nIndex) && (nIndex <= nHintEnd));
1612 }
1613 static bool lcl_GetTextAttrParent(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
1614 {
1615  return ((nHintStart < nIndex) && (nIndex < nHintEnd));
1616 }
1617 
1618 static void
1620  std::vector<SwTextAttr *> *const pVector,
1621  SwTextAttr **const ppTextAttr,
1622  SwpHints const *const pSwpHints,
1623  sal_Int32 const nIndex, sal_uInt16 const nWhich,
1624  enum SwTextNode::GetTextAttrMode const eMode)
1625 {
1626  assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1627  if (!pSwpHints)
1628  return;
1629  size_t const nSize = pSwpHints->Count();
1630  sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
1631  bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
1632  switch (eMode)
1633  {
1634  case SwTextNode::DEFAULT: pMatchFunc = &lcl_GetTextAttrDefault; break;
1635  case SwTextNode::EXPAND: pMatchFunc = &lcl_GetTextAttrExpand; break;
1636  case SwTextNode::PARENT: pMatchFunc = &lcl_GetTextAttrParent; break;
1637  default: assert(false);
1638  }
1639 
1640  for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
1641  {
1642  SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i);
1643  if (pHint->Which() != nWhich)
1644  break; // hints are sorted by which&start, so we are done...
1645 
1646  sal_Int32 const nHintStart = pHint->GetStart();
1647  if (nIndex < nHintStart)
1648  break; // hints are sorted by which&start, so we are done...
1649 
1650  sal_Int32 const*const pEndIdx = pHint->GetEnd();
1651  // cannot have hint with no end and no dummy char
1652  assert(pEndIdx || pHint->HasDummyChar());
1653  // If EXPAND is set, simulate the text input behavior, i.e.
1654  // move the start, and expand the end.
1655  bool const bContained( pEndIdx
1656  ? (*pMatchFunc)(nIndex, nHintStart, *pEndIdx)
1657  : (nHintStart == nIndex) );
1658  if (bContained)
1659  {
1660  if (pVector)
1661  {
1662  if (nPreviousIndex < nHintStart)
1663  {
1664  pVector->clear(); // clear hints that are outside pHint
1665  nPreviousIndex = nHintStart;
1666  }
1667  pVector->push_back(pHint);
1668  }
1669  else
1670  {
1671  *ppTextAttr = pHint; // and possibly overwrite outer hint
1672  }
1673  if (!pEndIdx)
1674  {
1675  break;
1676  }
1677  }
1678  }
1679 }
1680 
1681 std::vector<SwTextAttr *>
1682 SwTextNode::GetTextAttrsAt(sal_Int32 const nIndex, sal_uInt16 const nWhich) const
1683 {
1684  assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
1685  std::vector<SwTextAttr *> ret;
1686  lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, DEFAULT);
1687  return ret;
1688 }
1689 
1690 SwTextAttr *
1691 SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich,
1692  enum GetTextAttrMode const eMode) const
1693 {
1694  assert( (nWhich == RES_TXTATR_META)
1695  || (nWhich == RES_TXTATR_METAFIELD)
1696  || (nWhich == RES_TXTATR_AUTOFMT)
1697  || (nWhich == RES_TXTATR_INETFMT)
1698  || (nWhich == RES_TXTATR_CJK_RUBY)
1699  || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
1700  || (nWhich == RES_TXTATR_INPUTFIELD ) );
1701  // "GetTextAttrAt() will give wrong result for this hint!")
1702 
1703  SwTextAttr * pRet(nullptr);
1704  lcl_GetTextAttrs(nullptr, & pRet, m_pSwpHints.get(), nIndex, nWhich, eMode);
1705  return pRet;
1706 }
1707 
1709 {
1710  const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, PARENT ));
1711 
1712  if ( pTextInputField == nullptr && rTextAttr.End() != nullptr )
1713  {
1714  pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( *(rTextAttr.End()), RES_TXTATR_INPUTFIELD, PARENT ));
1715  }
1716 
1717  return pTextInputField;
1718 }
1719 
1721 {
1722  SetWrong( nullptr );
1724 
1725  SetGrammarCheck( nullptr );
1726  SetGrammarCheckDirty( true );
1727 
1728  SetSmartTags( nullptr );
1729  SetSmartTagDirty( true );
1730 
1731  SetWordCountDirty( true );
1732  SetAutoCompleteWordDirty( true );
1733 }
1734 
1736  const sal_Int32 nIndex,
1737  const bool bIncludeInputFieldAtStart ) const
1738 {
1739  SwTextField* pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_FIELD ));
1740  if ( pTextField == nullptr )
1741  {
1742  pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_ANNOTATION ));
1743  }
1744  if ( pTextField == nullptr )
1745  {
1746  pTextField =
1747  dynamic_cast<SwTextField*>( GetTextAttrAt(
1748  nIndex,
1750  bIncludeInputFieldAtStart ? DEFAULT : EXPAND ));
1751  }
1752 
1753  return pTextField;
1754 }
1755 
1756 static SwCharFormat* lcl_FindCharFormat( const SwCharFormats* pCharFormats, std::u16string_view rName )
1757 {
1758  if( !rName.empty() )
1759  {
1760  const size_t nArrLen = pCharFormats->size();
1761  for( size_t i = 1; i < nArrLen; i++ )
1762  {
1763  SwCharFormat* pFormat = (*pCharFormats)[ i ];
1764  if( pFormat->GetName()==rName )
1765  return pFormat;
1766  }
1767  }
1768  return nullptr;
1769 }
1770 
1771 static void lcl_CopyHint(
1772  const sal_uInt16 nWhich,
1773  const SwTextAttr * const pHt,
1774  SwTextAttr *const pNewHt,
1775  SwDoc *const pOtherDoc,
1776  SwTextNode *const pDest )
1777 {
1778  assert(nWhich == pHt->Which()); // wrong hint-id
1779  switch( nWhich )
1780  {
1781  // copy nodesarray section with footnote content
1782  case RES_TXTATR_FTN :
1783  assert(pDest); // "lcl_CopyHint: no destination text node?"
1784  static_cast<const SwTextFootnote*>(pHt)->CopyFootnote( *static_cast<SwTextFootnote*>(pNewHt), *pDest);
1785  break;
1786 
1787  // Fields that are copied into different SwDocs must be registered
1788  // at their new FieldTypes.
1789 
1790  case RES_TXTATR_FIELD :
1791  {
1792  if( pOtherDoc != nullptr )
1793  {
1794  static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1795  static_txtattr_cast<SwTextField*>(pNewHt));
1796  }
1797 
1798  // Table Formula must be copied relative.
1799  const SwFormatField& rField = pHt->GetFormatField();
1800  if( SwFieldIds::Table == rField.GetField()->GetTyp()->Which()
1801  && static_cast<const SwTableField*>(rField.GetField())->IsIntrnlName())
1802  {
1803  // convert internal formula to external
1804  const SwTableNode* const pDstTableNd =
1805  static_txtattr_cast<const SwTextField*>(pHt)->GetTextNode().FindTableNode();
1806  if( pDstTableNd )
1807  {
1808  SwTableField* const pTableField =
1809  const_cast<SwTableField*>(static_cast<const SwTableField*>(
1810  pNewHt->GetFormatField().GetField()));
1811  pTableField->PtrToBoxNm( &pDstTableNd->GetTable() );
1812  }
1813  }
1814  }
1815  break;
1816 
1817  case RES_TXTATR_INPUTFIELD :
1818  case RES_TXTATR_ANNOTATION :
1819  if( pOtherDoc != nullptr )
1820  {
1821  static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
1822  static_txtattr_cast<SwTextField*>(pNewHt));
1823  }
1824  break;
1825 
1826  case RES_TXTATR_TOXMARK :
1827  if( pOtherDoc && pDest && pDest->GetpSwpHints()
1828  && pDest->GetpSwpHints()->Contains( pNewHt ) )
1829  {
1830  // ToXMarks that are copied to different SwDocs must register
1831  // at their new ToX (sw::BroadcastingModify).
1832  static_txtattr_cast<SwTextTOXMark*>(pNewHt)->CopyTOXMark(*pOtherDoc);
1833  }
1834  break;
1835 
1836  case RES_TXTATR_CHARFMT :
1837  // For CharacterStyles, the format must be copied too.
1838  if( pDest && pDest->GetpSwpHints()
1839  && pDest->GetpSwpHints()->Contains( pNewHt ) )
1840  {
1841  SwCharFormat* pFormat = pHt->GetCharFormat().GetCharFormat();
1842 
1843  if (pOtherDoc)
1844  {
1845  pFormat = pOtherDoc->CopyCharFormat( *pFormat );
1846  }
1847  const_cast<SwFormatCharFormat&>(
1848  pNewHt->GetCharFormat() ).SetCharFormat( pFormat );
1849  }
1850  break;
1851  case RES_TXTATR_INETFMT :
1852  {
1853  // For Hyperlinks, the format must be copied too.
1854  if( pOtherDoc && pDest && pDest->GetpSwpHints()
1855  && pDest->GetpSwpHints()->Contains( pNewHt ) )
1856  {
1857  const SwDoc& rDoc = static_txtattr_cast<
1858  const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc();
1859  const SwCharFormats* pCharFormats = rDoc.GetCharFormats();
1860  const SwFormatINetFormat& rFormat = pHt->GetINetFormat();
1861  SwCharFormat* pFormat;
1862  pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetINetFormat() );
1863  if( pFormat )
1864  pOtherDoc->CopyCharFormat( *pFormat );
1865  pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetVisitedFormat() );
1866  if( pFormat )
1867  pOtherDoc->CopyCharFormat( *pFormat );
1868  }
1869  //JP 24.04.98: The attribute must point to a text node, so that
1870  // the styles can be created.
1871  SwTextINetFormat *const pINetHt = static_txtattr_cast<SwTextINetFormat*>(pNewHt);
1872  if ( !pINetHt->GetpTextNode() )
1873  {
1874  pINetHt->ChgTextNode( pDest );
1875  }
1876 
1877  //JP 22.10.97: set up link to char style
1878  pINetHt->GetCharFormat();
1879  break;
1880  }
1881  case RES_TXTATR_META:
1882  case RES_TXTATR_METAFIELD:
1883  OSL_ENSURE( pNewHt, "copying Meta should not fail!" );
1884  OSL_ENSURE( pDest
1885  && (CH_TXTATR_INWORD == pDest->GetText()[pNewHt->GetStart()]),
1886  "missing CH_TXTATR?");
1887  break;
1888  }
1889 }
1890 
1892 // BP 7.6.93: Intentionally copy only attributes _with_ EndIdx!
1893 // CopyAttr is usually called when attributes are set on a
1894 // node with no text.
1895 void SwTextNode::CopyAttr( SwTextNode *pDest, const sal_Int32 nTextStartIdx,
1896  const sal_Int32 nOldPos )
1897 {
1898  if ( HasHints() )
1899  {
1900  SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ?
1901  &pDest->GetDoc() : nullptr;
1902 
1903  for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
1904  {
1905  SwTextAttr *const pHt = m_pSwpHints->Get(i);
1906  sal_Int32 const nAttrStartIdx = pHt->GetStart();
1907  if ( nTextStartIdx < nAttrStartIdx )
1908  break; // beyond end of text, because nLen == 0
1909 
1910  const sal_Int32 *const pEndIdx = pHt->GetEnd();
1911  if ( pEndIdx && !pHt->HasDummyChar() )
1912  {
1913  sal_uInt16 const nWhich = pHt->Which();
1914  if (RES_TXTATR_INPUTFIELD != nWhich // fdo#74981 skip fields
1915  && ( *pEndIdx > nTextStartIdx
1916  || (*pEndIdx == nTextStartIdx
1917  && nAttrStartIdx == nTextStartIdx)))
1918  {
1919  if ( RES_TXTATR_REFMARK != nWhich )
1920  {
1921  // attribute in the area => copy
1922  SwTextAttr *const pNewHt =
1923  pDest->InsertItem( pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
1924  if ( pNewHt )
1925  {
1926  lcl_CopyHint( nWhich, pHt, pNewHt,
1927  pOtherDoc, pDest );
1928  }
1929  }
1930  else if( !pOtherDoc
1931  ? GetDoc().IsCopyIsMove()
1932  : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) )
1933  {
1934  pDest->InsertItem(
1935  pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
1936  }
1937  }
1938  }
1939  }
1940  }
1941 
1942  if( this != pDest )
1943  {
1944  // notify layout frames, to prevent disappearance of footnote numbers
1945  SwUpdateAttr aHint(
1946  nOldPos,
1947  nOldPos,
1948  0);
1949 
1950  pDest->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
1951  }
1952 }
1953 
1956  const SwIndex &rStart,
1957  const sal_Int32 nLen,
1958  const bool bForceCopyOfAllAttrs )
1959 {
1960  SwIndex const aIdx( pDest, pDest->m_Text.getLength() );
1961  CopyText( pDest, aIdx, rStart, nLen, bForceCopyOfAllAttrs );
1962 }
1963 
1965  const SwIndex &rDestStart,
1966  const SwIndex &rStart,
1967  sal_Int32 nLen,
1968  const bool bForceCopyOfAllAttrs )
1969 {
1970  CHECK_SWPHINTS_IF_FRM(this);
1971  CHECK_SWPHINTS(pDest);
1972  sal_Int32 nTextStartIdx = rStart.GetIndex();
1973  sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
1974 
1975  if (pDest->GetDoc().IsClipBoard() && GetNum())
1976  {
1977  // #i111677# cache expansion of source (for clipboard)
1978  pDest->m_oNumStringCache = (nTextStartIdx != 0)
1979  ? OUString() // fdo#49076: numbering only if copy from para start
1980  : GetNumString();
1981  }
1982 
1983  if( !nLen )
1984  {
1985  // if no length is given, copy attributes at position rStart
1986  CopyAttr( pDest, nTextStartIdx, nDestStart );
1987 
1988  // copy hard attributes on whole paragraph
1989  if( HasSwAttrSet() )
1990  {
1991  // i#96213 all or just the Char attributes?
1992  if ( !bForceCopyOfAllAttrs &&
1993  ( nDestStart ||
1994  pDest->HasSwAttrSet() ||
1995  nLen != pDest->GetText().getLength()))
1996  {
2001  aCharSet( pDest->GetDoc().GetAttrPool() );
2002  aCharSet.Put( *GetpSwAttrSet() );
2003  if( aCharSet.Count() )
2004  {
2005  pDest->SetAttr( aCharSet, nDestStart, nDestStart );
2006  }
2007  }
2008  else
2009  {
2010  GetpSwAttrSet()->CopyToModify( *pDest );
2011  }
2012  }
2013  return;
2014  }
2015 
2016  // 1. copy text
2017  const sal_Int32 oldLen = pDest->m_Text.getLength();
2018  // JP 15.02.96: missing attribute handling at the end!
2019  // hence call InsertText and don't modify m_Text directly
2020  pDest->InsertText( m_Text.copy(nTextStartIdx, nLen), rDestStart,
2022 
2023  // update with actual new size
2024  nLen = pDest->m_Text.getLength() - oldLen;
2025  if ( !nLen ) // string not longer?
2026  return;
2027 
2028  SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ? &pDest->GetDoc() : nullptr;
2029 
2030  // copy hard attributes on whole paragraph
2031  if( HasSwAttrSet() )
2032  {
2033  // i#96213 all or just the Char attributes?
2034  if ( !bForceCopyOfAllAttrs &&
2035  ( nDestStart ||
2036  pDest->HasSwAttrSet() ||
2037  nLen != pDest->GetText().getLength()))
2038  {
2043  aCharSet( pDest->GetDoc().GetAttrPool() );
2044  aCharSet.Put( *GetpSwAttrSet() );
2045  if( aCharSet.Count() )
2046  {
2047  pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2048  }
2049  }
2050  else
2051  {
2052  GetpSwAttrSet()->CopyToModify( *pDest );
2053  }
2054  }
2055 
2056  bool const bUndoNodes = !pOtherDoc
2058 
2059  // Fetch end only now, because copying into self updates the start index
2060  // and all attributes
2061  nTextStartIdx = rStart.GetIndex();
2062  const sal_Int32 nEnd = nTextStartIdx + nLen;
2063 
2064  // 2. copy attributes
2065  // Iterate over attribute array until the start of the attribute
2066  // is behind the copied range
2067  const size_t nSize = m_pSwpHints ? m_pSwpHints->Count() : 0;
2068 
2069  // If copying into self, inserting can delete attributes!
2070  // Hence first copy into temp-array, and then move that into hints array.
2071  SwpHts aArr;
2072 
2073  // Del-Array for all RefMarks without extent
2074  SwpHts aRefMrkArr;
2075 
2076  std::vector<std::pair<sal_Int32, sal_Int32>> metaFieldRanges;
2077  sal_Int32 nDeletedDummyChars(0);
2078  for (size_t n = 0; n < nSize; ++n)
2079  {
2080  SwTextAttr * const pHt = m_pSwpHints->Get(n);
2081 
2082  const sal_Int32 nAttrStartIdx = pHt->GetStart();
2083  if ( nAttrStartIdx >= nEnd )
2084  break;
2085 
2086  const sal_Int32 * const pEndIdx = pHt->GetEnd();
2087  const sal_uInt16 nWhich = pHt->Which();
2088 
2089  // JP 26.04.94: RefMarks are never copied. If the refmark doesn't have
2090  // an extent, there is a dummy char in the text, which
2091  // must be removed. So we first copy the attribute,
2092  // but remember it, and when we're done delete it,
2093  // which also deletes the dummy character!
2094  // JP 14.08.95: May RefMarks be moved?
2095  const bool bCopyRefMark = RES_TXTATR_REFMARK == nWhich
2096  && ( bUndoNodes
2097  || ( !pOtherDoc
2098  ? GetDoc().IsCopyIsMove()
2099  : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) ) );
2100 
2101  if ( pEndIdx
2102  && RES_TXTATR_REFMARK == nWhich
2103  && !bCopyRefMark )
2104  {
2105  continue;
2106  }
2107 
2108  // Input Fields are only copied, if completely covered by copied text
2109  if ( nWhich == RES_TXTATR_INPUTFIELD )
2110  {
2111  assert(pEndIdx != nullptr &&
2112  "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" );
2113  if ( nAttrStartIdx < nTextStartIdx
2114  || ( pEndIdx != nullptr
2115  && *pEndIdx > nEnd ) )
2116  {
2117  continue;
2118  }
2119  }
2120 
2121  if (nWhich == RES_TXTATR_METAFIELD)
2122  {
2123  // Skip metadata fields. Also remember the range to strip the text later.
2124  metaFieldRanges.emplace_back(nAttrStartIdx, pEndIdx ? *pEndIdx : nEnd);
2125  continue;
2126  }
2127 
2128  sal_Int32 nAttrStt = 0;
2129  sal_Int32 nAttrEnd = 0;
2130 
2131  if( nAttrStartIdx < nTextStartIdx )
2132  {
2133  // start is before selection
2134  // copy hints with end and CH_TXTATR only if dummy char is copied
2135  if ( pEndIdx && (*pEndIdx > nTextStartIdx) && !pHt->HasDummyChar() )
2136  {
2137  // attribute with extent and the end is in the selection
2138  nAttrStt = nDestStart;
2139  nAttrEnd = (*pEndIdx > nEnd)
2140  ? rDestStart.GetIndex()
2141  : nDestStart + (*pEndIdx) - nTextStartIdx;
2142  }
2143  else
2144  {
2145  continue;
2146  }
2147  }
2148  else
2149  {
2150  // start is in the selection
2151  nAttrStt = nDestStart + ( nAttrStartIdx - nTextStartIdx );
2152  if( pEndIdx )
2153  {
2154  nAttrEnd = *pEndIdx > nEnd
2155  ? rDestStart.GetIndex()
2156  : nDestStart + ( *pEndIdx - nTextStartIdx );
2157  }
2158  else
2159  {
2160  nAttrEnd = nAttrStt;
2161  }
2162  }
2163 
2164  SwTextAttr * pNewHt = nullptr;
2165 
2166  if( pDest == this )
2167  {
2168  // copy the hint here, but insert it later
2169  pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2170  nAttrStt, nAttrEnd, CopyOrNewType::Copy, pDest );
2171 
2172  lcl_CopyHint(nWhich, pHt, pNewHt, nullptr, pDest);
2173  aArr.push_back( pNewHt );
2174  }
2175  else
2176  {
2177  pNewHt = pDest->InsertItem(
2178  pHt->GetAttr(),
2179  nAttrStt - nDeletedDummyChars,
2180  nAttrEnd - nDeletedDummyChars,
2182  if (pNewHt)
2183  {
2184  lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest );
2185  }
2186  else if (pHt->HasDummyChar())
2187  {
2188  // The attribute that has failed to be copied would insert
2189  // dummy char, so positions of the following attributes have
2190  // to be shifted by one to compensate for that missing char.
2191  ++nDeletedDummyChars;
2192  }
2193  }
2194 
2195  if( RES_TXTATR_REFMARK == nWhich && !pEndIdx && !bCopyRefMark )
2196  {
2197  aRefMrkArr.push_back( pNewHt );
2198  }
2199  }
2200 
2201  // Strip the metadata fields, since we don't copy the RDF entries
2202  // yet and so they are inconsistent upon copy/pasting.
2203  if (!metaFieldRanges.empty())
2204  {
2205  // Reverse to remove without messing the offsets.
2206  std::reverse(metaFieldRanges.begin(), metaFieldRanges.end());
2207  for (const auto& pair : metaFieldRanges)
2208  {
2209  const SwIndex aIdx(pDest, pair.first);
2210  pDest->EraseText(aIdx, pair.second - pair.first);
2211  }
2212  }
2213 
2214  // this can only happen when copying into self
2215  for (SwTextAttr* i : aArr)
2216  {
2218  }
2219 
2220  if( pDest->GetpSwpHints() )
2221  {
2222  for (SwTextAttr* pNewHt : aRefMrkArr)
2223  {
2224  if( pNewHt->GetEnd() )
2225  {
2226  pDest->GetpSwpHints()->Delete( pNewHt );
2227  pDest->DestroyAttr( pNewHt );
2228  }
2229  else
2230  {
2231  const SwIndex aIdx( pDest, pNewHt->GetStart() );
2232  pDest->EraseText( aIdx, 1 );
2233  }
2234  }
2235  }
2236 
2237  CHECK_SWPHINTS_IF_FRM(this);
2238  CHECK_SWPHINTS(pDest);
2239 }
2240 
2241 OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
2242  const SwInsertFlags nMode )
2243 {
2244  assert(rIdx <= m_Text.getLength()); // invalid index
2245 
2246  const sal_Int32 aPos = rIdx.GetIndex();
2247  sal_Int32 nLen = m_Text.getLength() - aPos;
2248  sal_Int32 const nOverflow(rStr.getLength() - GetSpaceLeft());
2249  SAL_WARN_IF(nOverflow > 0, "sw.core",
2250  "SwTextNode::InsertText: node text with insertion > capacity.");
2251  OUString const sInserted(
2252  (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
2253  if (sInserted.isEmpty())
2254  {
2255  return sInserted;
2256  }
2257  m_Text = m_Text.replaceAt(aPos, 0, sInserted);
2258  assert(GetSpaceLeft()>=0);
2259  nLen = m_Text.getLength() - aPos - nLen;
2260  assert(nLen != 0);
2261 
2262  bool bOldExpFlg = IsIgnoreDontExpand();
2263  if (nMode & SwInsertFlags::FORCEHINTEXPAND)
2264  {
2265  SetIgnoreDontExpand( true );
2266  }
2267 
2268  Update( rIdx, nLen ); // text content changed!
2269 
2270  if (nMode & SwInsertFlags::FORCEHINTEXPAND)
2271  {
2272  SetIgnoreDontExpand( bOldExpFlg );
2273  }
2274 
2275  if ( HasWriterListeners() )
2276  { // send this before messing with hints, which will send RES_UPDATE_ATTR
2277  SwInsText aHint( aPos, nLen );
2278  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2279  }
2280 
2281  if ( HasHints() )
2282  {
2283  m_pSwpHints->SortIfNeedBe();
2284  bool const bHadHints(!m_pSwpHints->CanBeDeleted());
2285  bool bMergePortionsNeeded(false);
2286  for ( size_t i = 0; i < m_pSwpHints->Count() &&
2287  rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
2288  {
2289  SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i );
2290  const sal_Int32 * const pEndIdx = pHt->GetEnd();
2291  if( !pEndIdx )
2292  continue;
2293 
2294  if( rIdx == *pEndIdx )
2295  {
2296  if ( (nMode & SwInsertFlags::NOHINTEXPAND) ||
2297  (!(nMode & SwInsertFlags::FORCEHINTEXPAND)
2298  && pHt->DontExpand()) )
2299  {
2300  m_pSwpHints->DeleteAtPos(i);
2301  // on empty attributes also adjust Start
2302  if( rIdx == pHt->GetStart() )
2303  pHt->SetStart( pHt->GetStart() - nLen );
2304  pHt->SetEnd(*pEndIdx - nLen);
2305  // could be that pHt has IsFormatIgnoreEnd set, and it's
2306  // not a RSID-only hint - now we have the inserted text
2307  // between pHt and its continuation... which we don't know.
2308  // punt the job to MergePortions below.
2309  if (pHt->IsFormatIgnoreEnd())
2310  {
2311  bMergePortionsNeeded = true;
2312  }
2314  }
2315  // empty hints at insert position?
2316  else if ( (nMode & SwInsertFlags::EMPTYEXPAND)
2317  && (*pEndIdx == pHt->GetStart()) )
2318  {
2319  m_pSwpHints->DeleteAtPos(i);
2320  pHt->SetStart( pHt->GetStart() - nLen );
2321  const size_t nCurrentLen = m_pSwpHints->Count();
2322  InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
2323  if ( nCurrentLen > m_pSwpHints->Count() && i )
2324  {
2325  --i;
2326  }
2327  continue;
2328  }
2329  else
2330  {
2331  continue;
2332  }
2333  }
2334  if ( !(nMode & SwInsertFlags::NOHINTEXPAND) &&
2335  rIdx == nLen && pHt->GetStart() == rIdx.GetIndex() &&
2336  !pHt->IsDontExpandStartAttr() )
2337  {
2338  // no field, at paragraph start, HintExpand
2339  m_pSwpHints->DeleteAtPos(i);
2340  pHt->SetStart( pHt->GetStart() - nLen );
2341  // no effect on format ignore flags here (para start)
2343  }
2344  }
2345  if (bMergePortionsNeeded)
2346  {
2347  m_pSwpHints->MergePortions(*this);
2348  }
2349  SAL_WARN_IF(bHadHints && m_pSwpHints->CanBeDeleted(), "sw.core",
2350  "SwTextNode::InsertText: unexpected loss of hints");
2351  }
2352 
2353  // By inserting a character, the hidden flags
2354  // at the TextNode can become invalid:
2356 
2357  CHECK_SWPHINTS(this);
2358  return sInserted;
2359 }
2360 
2361 void SwTextNode::CutText( SwTextNode * const pDest,
2362  const SwIndex & rStart, const sal_Int32 nLen )
2363 {
2364  assert(pDest); // Cut requires a destination
2365  SwIndex aDestStt(pDest, pDest->GetText().getLength());
2366  CutImpl( pDest, aDestStt, rStart, nLen, false );
2367 }
2368 
2369 void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart,
2370  const SwIndex & rStart, sal_Int32 nLen, const bool bUpdate )
2371 {
2372  assert(pDest); // Cut requires a destination
2373 
2374  assert(&GetDoc() == &pDest->GetDoc()); // must be same document
2375 
2376  assert(pDest != this); // destination must be different node
2377 
2378  if( !nLen )
2379  {
2380  // if no length is given, copy attributes at position rStart
2381  CopyAttr( pDest, rStart.GetIndex(), rDestStart.GetIndex() );
2382  return;
2383  }
2384 
2385  sal_Int32 nTextStartIdx = rStart.GetIndex();
2386  sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
2387  const sal_Int32 nInitSize = pDest->m_Text.getLength();
2388 
2389  if (pDest->GetSpaceLeft() < nLen)
2390  { // FIXME: could only happen when called from SwRangeRedline::Show.
2391  // unfortunately can't really do anything here to handle that...
2392  abort();
2393  }
2394  pDest->m_Text = pDest->m_Text.replaceAt(nDestStart, 0,
2395  m_Text.copy(nTextStartIdx, nLen));
2396  OUString const newText = m_Text.replaceAt(nTextStartIdx, nLen, "");
2397  nLen = pDest->m_Text.getLength() - nInitSize; // update w/ current size!
2398  if (!nLen) // String didn't grow?
2399  return;
2400 
2401  if (bUpdate)
2402  {
2403  // Update all SwIndex
2404  pDest->Update( rDestStart, nLen, false, false/*??? why was it true*/);
2405  }
2406 
2407  CHECK_SWPHINTS(pDest);
2408 
2409  const sal_Int32 nEnd = rStart.GetIndex() + nLen;
2410  bool const bUndoNodes =
2412 
2413  // copy hard attributes on whole paragraph
2414  if (HasSwAttrSet())
2415  {
2416  bool hasSwAttrSet = pDest->HasSwAttrSet();
2417  if (hasSwAttrSet)
2418  {
2419  // if we have our own property set it doesn't mean
2420  // that this set defines any style different to Standard one.
2421  hasSwAttrSet = false;
2422 
2423  // so, let's check deeper if property set has defined any property
2424  if (pDest->GetpSwAttrSet())
2425  {
2426  // check all items in the property set
2427  SfxItemIter aIter( *pDest->GetpSwAttrSet() );
2428  const SfxPoolItem* pItem = aIter.GetCurItem();
2429  do
2430  {
2431  // check current item
2432  const sal_uInt16 nWhich = IsInvalidItem( pItem )
2433  ? pDest->GetpSwAttrSet()->GetWhichByPos( aIter.GetCurPos() )
2434  : pItem->Which();
2435  if( RES_FRMATR_STYLE_NAME != nWhich &&
2437  RES_PAGEDESC != nWhich &&
2438  RES_BREAK != nWhich &&
2439  SfxItemState::SET == pDest->GetpSwAttrSet()->GetItemState( nWhich, false ) )
2440  {
2441  // check if parent value (original value in style) has the same value as in [pItem]
2442  const SfxPoolItem& rParentItem = pDest->GetpSwAttrSet()->GetParent()->Get( nWhich, true );
2443 
2444  hasSwAttrSet = (rParentItem != *pItem);
2445 
2446  // property set is not empty => no need to make anymore checks
2447  if (hasSwAttrSet)
2448  break;
2449  }
2450 
2451  // let's check next item
2452  pItem = aIter.NextItem();
2453  } while (pItem);
2454  }
2455  }
2456 
2457  // all or just the Char attributes?
2458  if( nInitSize || hasSwAttrSet ||
2459  nLen != pDest->GetText().getLength())
2460  {
2465  aCharSet( pDest->GetDoc().GetAttrPool() );
2466  aCharSet.Put( *GetpSwAttrSet() );
2467  if( aCharSet.Count() )
2468  pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
2469  }
2470  else
2471  {
2472  // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before
2473  // and current SwTextNode can contain not suitable for pDest value
2475  RES_PARATR_LIST_LEVEL + 1, HINT_END>
2476  aCharSet(pDest->GetDoc().GetAttrPool());
2477  aCharSet.Put(*GetpSwAttrSet());
2478  if (aCharSet.Count())
2479  pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen);
2480  }
2481  }
2482 
2483  // notify frames - before moving hints, because footnotes
2484  // want to find their anchor text frame in the follow chain
2485  SwInsText aInsHint(nDestStart, nLen);
2486  pDest->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, &aInsHint));
2487  const sw::MoveText aMoveHint(pDest, nDestStart, nTextStartIdx, nLen);
2488  CallSwClientNotify(aMoveHint);
2489  const SwDelText aDelHint(nTextStartIdx, nLen);
2490  TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, &aDelHint));
2491 
2492  // 2. move attributes
2493  // Iterate over attribute array until the start of the attribute
2494  // is behind the moved range
2495  bool bMergePortionsNeeded(false);
2496  size_t nAttrCnt = 0;
2497  while (m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()))
2498  {
2499  SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2500  const sal_Int32 nAttrStartIdx = pHt->GetStart();
2501  if ( nAttrStartIdx >= nEnd )
2502  break;
2503  const sal_Int32 * const pEndIdx = pHt->GetEnd();
2504  const sal_uInt16 nWhich = pHt->Which();
2505  SwTextAttr *pNewHt = nullptr;
2506 
2507  // if the hint has a dummy character, then it must not be split!
2508  if(nAttrStartIdx < nTextStartIdx)
2509  {
2510  // start is before the range
2511  if (!pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich
2512  || bUndoNodes ) && pEndIdx && *pEndIdx > nTextStartIdx)
2513  {
2514  // attribute with extent and end of attribute is in the range
2515  pNewHt = MakeTextAttr( pDest->GetDoc(), pHt->GetAttr(),
2516  nDestStart,
2517  nDestStart + (
2518  *pEndIdx > nEnd
2519  ? nLen
2520  : *pEndIdx - nTextStartIdx ) );
2521  }
2522  }
2523  else
2524  {
2525  // start is inside the range
2526  if (!pEndIdx || *pEndIdx < nEnd ||
2527  (!bUndoNodes && RES_TXTATR_REFMARK == nWhich)
2528  || pHt->HasDummyChar() )
2529  {
2530  // do not delete note and later add it -> sidebar flickering
2531  if (GetDoc().GetDocShell())
2532  {
2533  GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2534  }
2535  // move attribute
2536  m_pSwpHints->Delete( pHt );
2537  // reset start/end indexes
2538  if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
2539  {
2540  bMergePortionsNeeded = true;
2541  }
2542  pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx));
2543  if (pEndIdx)
2544  {
2545  pHt->SetEnd( nDestStart + (
2546  *pEndIdx > nEnd
2547  ? nLen
2548  : *pEndIdx - nTextStartIdx ) );
2549  }
2550  pDest->InsertHint( pHt,
2553  if (GetDoc().GetDocShell())
2554  {
2555  GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
2556  }
2557  continue; // iterate while loop, no ++ !
2558  }
2559  // the end is behind the range
2560  else if (RES_TXTATR_REFMARK != nWhich || bUndoNodes)
2561  {
2562  pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
2563  nDestStart + (nAttrStartIdx - nTextStartIdx),
2564  nDestStart + (*pEndIdx > nEnd
2565  ? nLen
2566  : *pEndIdx - nTextStartIdx));
2567  }
2568  }
2569  if (pNewHt)
2570  {
2571  const bool bSuccess( pDest->InsertHint( pNewHt,
2574  | SetAttrMode::IS_COPY) );
2575  if (bSuccess)
2576  {
2577  lcl_CopyHint( nWhich, pHt, pNewHt, nullptr, pDest );
2578  }
2579  }
2580  ++nAttrCnt;
2581  }
2582  // If there are still empty attributes around, they have a higher priority
2583  // than any attributes that become empty due to the move.
2584  // So temporarily remove them and Update the array, then re-insert the
2585  // removed ones so they overwrite the others.
2586  if (m_pSwpHints && nAttrCnt < m_pSwpHints->Count())
2587  {
2588  SwpHts aArr;
2589  while (nAttrCnt < m_pSwpHints->Count())
2590  {
2591  SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
2592  if (nEnd != pHt->GetStart())
2593  break;
2594  const sal_Int32 * const pEndIdx = pHt->GetEnd();
2595  if (pEndIdx && *pEndIdx == nEnd)
2596  {
2597  aArr.push_back( pHt );
2598  m_pSwpHints->Delete( pHt );
2599  }
2600  else
2601  {
2602  ++nAttrCnt;
2603  }
2604  }
2605  Update( rStart, nLen, true, true );
2606 
2607  for (SwTextAttr* pHt : aArr)
2608  {
2609  pHt->SetStart( rStart.GetIndex() );
2610  pHt->SetEnd( rStart.GetIndex() );
2611  InsertHint( pHt );
2612  }
2613  }
2614  else
2615  {
2616  Update( rStart, nLen, true, true );
2617  }
2618 
2619  // set after moving hints
2620  m_Text = newText;
2621 
2622  if (bMergePortionsNeeded)
2623  {
2624  m_pSwpHints->MergePortions(*this);
2625  }
2626 
2627  CHECK_SWPHINTS(this);
2628 
2630 }
2631 
2632 void SwTextNode::EraseText(const SwIndex &rIdx, const sal_Int32 nCount,
2633  const SwInsertFlags nMode )
2634 {
2635  assert(rIdx <= m_Text.getLength()); // invalid index
2636 
2637  const sal_Int32 nStartIdx = rIdx.GetIndex();
2638  const sal_Int32 nCnt = (nCount==SAL_MAX_INT32)
2639  ? m_Text.getLength() - nStartIdx : nCount;
2640  const sal_Int32 nEndIdx = nStartIdx + nCnt;
2641  if (nEndIdx <= m_Text.getLength())
2642  m_Text = m_Text.replaceAt(nStartIdx, nCnt, "");
2643 
2644  // GCAttr(); don't remove all empty ones, just the ones that are in the
2645  // range but not at the end of the range.
2646 
2647  for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
2648  {
2649  SwTextAttr *pHt = m_pSwpHints->Get(i);
2650 
2651  const sal_Int32 nHintStart = pHt->GetStart();
2652 
2653  if ( nHintStart < nStartIdx )
2654  continue;
2655 
2656  if ( nHintStart > nEndIdx )
2657  break; // hints are sorted by end, so break here
2658 
2659  const sal_Int32* pHtEndIdx = pHt->GetEnd();
2660  const sal_uInt16 nWhich = pHt->Which();
2661 
2662  if( !pHtEndIdx )
2663  {
2664  // attribute with neither end nor CH_TXTATR?
2665  assert(pHt->HasDummyChar());
2666  if (isTXTATR(nWhich) && (nHintStart < nEndIdx))
2667  {
2668  m_pSwpHints->DeleteAtPos(i);
2669  DestroyAttr( pHt );
2670  --i;
2671  }
2672  continue;
2673  }
2674 
2675  assert(!( (nHintStart < nEndIdx) && (*pHtEndIdx > nEndIdx)
2676  && pHt->HasDummyChar() )
2677  // next line: deleting exactly dummy char: DeleteAttributes
2678  || ((nHintStart == nStartIdx) && (nHintStart + 1 == nEndIdx)));
2679  // "ERROR: deleting left-overlapped attribute with CH_TXTATR");
2680 
2681  // Delete the hint if:
2682  // 1. The hint ends before the deletion end position or
2683  // 2. The hint ends at the deletion end position and
2684  // we are not in empty expand mode and
2685  // the hint is a [toxmark|refmark|ruby|inputfield] text attribute
2686  // 3. deleting exactly the dummy char of an hint with end and dummy
2687  // char deletes the hint
2688  if ( (*pHtEndIdx < nEndIdx)
2689  || ( (*pHtEndIdx == nEndIdx) &&
2690  !(SwInsertFlags::EMPTYEXPAND & nMode) &&
2691  ( (RES_TXTATR_TOXMARK == nWhich) ||
2692  (RES_TXTATR_REFMARK == nWhich) ||
2693  (RES_TXTATR_CJK_RUBY == nWhich) ||
2694  (RES_TXTATR_INPUTFIELD == nWhich) ) )
2695  || ( (nHintStart < nEndIdx) &&
2696  pHt->HasDummyChar() )
2697  )
2698  {
2699  m_pSwpHints->DeleteAtPos(i);
2700  DestroyAttr( pHt );
2701  --i;
2702  }
2703  }
2704 
2705  OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2706 
2708 
2709  Update( rIdx, nCnt, true );
2710 
2711  if( 1 == nCnt )
2712  {
2713  SwDelChr aHint( nStartIdx );
2714  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2715  }
2716  else
2717  {
2718  SwDelText aHint( nStartIdx, nCnt );
2719  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2720  }
2721 
2722  OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
2723 
2724  // By deleting a character, the hidden flags
2725  // at the TextNode can become invalid:
2727 
2728  CHECK_SWPHINTS(this);
2729 }
2730 
2732 {
2733  if ( !HasHints() )
2734  return;
2735 
2736  bool bChanged = false;
2737  sal_Int32 nMin = m_Text.getLength();
2738  sal_Int32 nMax = 0;
2739  const bool bAll = nMin != 0; // on empty paragraphs only remove INetFormats
2740 
2741  for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
2742  {
2743  SwTextAttr * const pHt = m_pSwpHints->Get(i);
2744 
2745  // if end and start are equal, delete it
2746  const sal_Int32 * const pEndIdx = pHt->GetEnd();
2747  if (pEndIdx && !pHt->HasDummyChar() && (*pEndIdx == pHt->GetStart())
2748  && ( bAll || pHt->Which() == RES_TXTATR_INETFMT ) )
2749  {
2750  bChanged = true;
2751  nMin = std::min( nMin, pHt->GetStart() );
2752  nMax = std::max( nMax, *pHt->GetEnd() );
2753  DestroyAttr( m_pSwpHints->Cut(i) );
2754  --i;
2755  }
2756  else
2757  {
2758  pHt->SetDontExpand( false );
2759  }
2760  }
2762 
2763  if(bChanged)
2764  {
2765  // textframes react to aHint, others to aNew
2766  SwUpdateAttr aHint(
2767  nMin,
2768  nMax,
2769  0);
2770 
2771  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
2772  SwFormatChg aNew( GetTextColl() );
2773  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
2774  }
2775 }
2776 
2777 SwNumRule* SwTextNode::GetNumRule(bool bInParent) const
2778 {
2779  SwNumRule* pRet = nullptr;
2780 
2781  const SfxPoolItem* pItem = GetNoCondAttr( RES_PARATR_NUMRULE, bInParent );
2782  bool bNoNumRule = false;
2783  if ( pItem )
2784  {
2785  OUString sNumRuleName =
2786  static_cast<const SwNumRuleItem *>(pItem)->GetValue();
2787  if (!sNumRuleName.isEmpty())
2788  {
2789  pRet = GetDoc().FindNumRulePtr( sNumRuleName );
2790  }
2791  else // numbering is turned off
2792  bNoNumRule = true;
2793  }
2794 
2795  if ( !bNoNumRule )
2796  {
2797  if ( pRet && pRet == GetDoc().GetOutlineNumRule() &&
2798  ( !HasSwAttrSet() ||
2799  SfxItemState::SET !=
2800  GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) ) )
2801  {
2802  SwTextFormatColl* pColl = GetTextColl();
2803  if ( pColl )
2804  {
2805  const SwNumRuleItem& rDirectItem = pColl->GetNumRule( false );
2806  if ( rDirectItem.GetValue().isEmpty() )
2807  {
2808  pRet = nullptr;
2809  }
2810  }
2811  }
2812  }
2813 
2814  return pRet;
2815 }
2816 
2818 {
2819  if ( IsInList() )
2820  {
2821  SwNumRule* pNumRule = GetNumRule();
2822  if ( pNumRule && pNumRule != GetNum()->GetNumRule() )
2823  {
2824  mpNodeNum->ChangeNumRule( *pNumRule );
2825  if (mpNodeNumRLHidden)
2826  {
2827  mpNodeNumRLHidden->ChangeNumRule(*pNumRule);
2828  }
2829  }
2830  }
2831 
2832  // Sending "noop" modify in order to cause invalidations of registered
2833  // <SwTextFrame> instances to get the list style change respectively the change
2834  // in the list tree reflected in the layout.
2835  // Important note:
2836  {
2837  SvxLRSpaceItem& rLR = const_cast<SvxLRSpaceItem&>(GetSwAttrSet().GetLRSpace());
2839  }
2840 
2841  SetWordCountDirty( true );
2842 }
2843 
2844 // -> #i27615#
2845 bool SwTextNode::IsNumbered(SwRootFrame const*const pLayout) const
2846 {
2847  SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
2848  return pRule && IsCountedInList();
2849 }
2850 
2852 {
2853  bool bResult = false;
2854 
2855  if ( IsInList() )
2856  {
2857  bResult =
2859  }
2860 
2861  return bResult;
2862 }
2863 // <- #i27615#
2864 
2866  bool bChgFollow )
2867 {
2868  // ignore hard PageBreak/PageDesc/ColumnBreak from Auto-Set
2869  std::unique_ptr<SwAttrSet> pNewAttrSet;
2870  // #i75353#
2871  bool bClearHardSetNumRuleWhenFormatCollChanges( false );
2872  if( HasSwAttrSet() )
2873  {
2874  pNewAttrSet.reset(new SwAttrSet( *GetpSwAttrSet() ));
2875  const SfxItemSet* pTmpSet = GetpSwAttrSet();
2876 
2877  if (bNext) // successor doesn't inherit breaks!
2878  pTmpSet = pNewAttrSet.get();
2879 
2880  // !bNext: remove PageBreaks/PageDesc/ColBreak from this
2881  bool bRemoveFromCache = false;
2882  std::vector<sal_uInt16> aClearWhichIds;
2883  if ( bNext )
2884  bRemoveFromCache = ( 0 != pNewAttrSet->ClearItem( RES_PAGEDESC ) );
2885  else
2886  aClearWhichIds.push_back( RES_PAGEDESC );
2887 
2888  if( SfxItemState::SET == pTmpSet->GetItemState( RES_BREAK, false ) )
2889  {
2890  if ( bNext )
2891  pNewAttrSet->ClearItem( RES_BREAK );
2892  else
2893  aClearWhichIds.push_back( RES_BREAK );
2894  bRemoveFromCache = true;
2895  }
2896  if( SfxItemState::SET == pTmpSet->GetItemState( RES_KEEP, false ) )
2897  {
2898  if ( bNext )
2899  pNewAttrSet->ClearItem( RES_KEEP );
2900  else
2901  aClearWhichIds.push_back( RES_KEEP );
2902  bRemoveFromCache = true;
2903  }
2904  if( SfxItemState::SET == pTmpSet->GetItemState( RES_PARATR_SPLIT, false ) )
2905  {
2906  if ( bNext )
2907  pNewAttrSet->ClearItem( RES_PARATR_SPLIT );
2908  else
2909  aClearWhichIds.push_back( RES_PARATR_SPLIT );
2910  bRemoveFromCache = true;
2911  }
2912  if(SfxItemState::SET == pTmpSet->GetItemState(RES_PARATR_NUMRULE, false))
2913  {
2914  SwNumRule * pRule = GetNumRule();
2915 
2916  if (pRule && IsOutline())
2917  {
2918  if ( bNext )
2919  pNewAttrSet->ClearItem(RES_PARATR_NUMRULE);
2920  else
2921  {
2922  // #i75353#
2923  // No clear of hard set numbering rule at an outline paragraph at this point.
2924  // Only if the paragraph style changes - see below.
2925  bClearHardSetNumRuleWhenFormatCollChanges = true;
2926  }
2927  bRemoveFromCache = true;
2928  }
2929  }
2930 
2931  if ( !aClearWhichIds.empty() )
2932  bRemoveFromCache = 0 != ClearItemsFromAttrSet( aClearWhichIds );
2933 
2934  if( !bNext && bRemoveFromCache )
2935  {
2937  }
2938  }
2939  SwNodes& rNds = GetNodes();
2940 
2941  SwTextFormatColl* pColl = GetTextColl();
2942 
2943  SwTextNode *pNode = new SwTextNode( rPos, pColl, pNewAttrSet.get() );
2944 
2945  pNewAttrSet.reset();
2946 
2947  const SwNumRule* pRule = GetNumRule();
2948  if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() )
2949  {
2950  // #i55459#
2951  // - correction: parameter <bNext> has to be checked, as it was in the
2952  // previous implementation.
2953  if ( !bNext && !IsCountedInList() )
2954  SetCountedInList(true);
2955  }
2956 
2957  // In case the numbering caused a style from the pool to be assigned to
2958  // the new node, don't overwrite that here!
2959  if( pColl != pNode->GetTextColl() ||
2960  ( bChgFollow && pColl != GetTextColl() ))
2961  return pNode; // that ought to be enough?
2962 
2963  pNode->ChgTextCollUpdateNum( nullptr, pColl ); // for numbering/outline
2964  if( bNext || !bChgFollow )
2965  return pNode;
2966 
2967  SwTextFormatColl *pNextColl = &pColl->GetNextTextFormatColl();
2968  // i#101870 perform action on different paragraph styles before applying
2969  // the new paragraph style
2970  if (pNextColl != pColl)
2971  {
2972  // #i75353#
2973  if ( bClearHardSetNumRuleWhenFormatCollChanges )
2974  {
2975  if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE } ) != 0 )
2976  {
2978  }
2979  }
2980  }
2981  ChgFormatColl( pNextColl );
2982 
2983  return pNode;
2984 }
2985 
2987 {
2988  // position behind which it will be inserted
2989  SwNodeIndex aIdx( rPos.nNode, 1 );
2990  SwTextNode* pNew = MakeNewTextNode( aIdx );
2991 
2992  // reset list attributes at appended text node
2996  if ( pNew->GetNumRule() == nullptr )
2997  {
2998  pNew->ResetAttr( RES_PARATR_LIST_ID );
3000  }
3001 
3002  if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
3003  {
3004  AddToList();
3005  }
3006 
3007  if( HasWriterListeners() )
3009  return pNew;
3010 }
3011 
3013  const sal_Int32 nIndex,
3014  const sal_uInt16 nWhich ) const
3015 {
3016  assert(nWhich >= RES_TXTATR_BEGIN && nWhich <= RES_TXTATR_END);
3017  if ( HasHints() )
3018  {
3019  for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3020  {
3021  SwTextAttr * const pHint = m_pSwpHints->Get(i);
3022  const sal_Int32 nStartPos = pHint->GetStart();
3023  if ( nIndex < nStartPos )
3024  {
3025  return nullptr;
3026  }
3027  if ( (nIndex == nStartPos) && pHint->HasDummyChar() )
3028  {
3029  return ( RES_TXTATR_END == nWhich || nWhich == pHint->Which() )
3030  ? pHint : nullptr;
3031  }
3032  }
3033  }
3034  return nullptr;
3035 }
3036 
3037 namespace
3038 {
3039 
3040 sal_uInt16 lcl_BoundListLevel(const int nActualLevel)
3041 {
3042  return o3tl::narrowing<sal_uInt16>( std::clamp( nActualLevel, 0, MAXLEVEL-1 ) );
3043 }
3044 
3045 }
3046 
3047 // -> #i29560#
3049 {
3050  bool bResult = false;
3051 
3052  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3053  if ( pRule )
3054  {
3055  const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3056 
3057  // #i40041#
3058  bResult = aFormat.IsEnumeration() &&
3059  SVX_NUM_NUMBER_NONE != aFormat.GetNumberingType();
3060  }
3061 
3062  return bResult;
3063 }
3064 
3066 {
3067  bool bResult = false;
3068 
3069  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3070  if ( pRule )
3071  {
3072  const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
3073 
3074  bResult = aFormat.IsItemize();
3075  }
3076 
3077  return bResult;
3078 }
3079 // <- #i29560#
3080 
3081 // #128041# - introduce parameter <_bInclPrefixAndSuffixStrings>
3082 //i53420 added max outline parameter
3083 OUString SwTextNode::GetNumString( const bool _bInclPrefixAndSuffixStrings,
3084  const unsigned int _nRestrictToThisLevel,
3085  SwRootFrame const*const pLayout) const
3086 {
3087  if (GetDoc().IsClipBoard() && m_oNumStringCache)
3088  {
3089  // #i111677# do not expand number strings in clipboard documents
3090  return *m_oNumStringCache;
3091  }
3092  const SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
3093  if ( pRule &&
3094  IsCountedInList() )
3095  {
3096  SvxNumberType const& rNumberType(
3097  pRule->Get( lcl_BoundListLevel(GetActualListLevel()) ) );
3098  if (rNumberType.IsTextFormat() ||
3099 
3100  (style::NumberingType::NUMBER_NONE == rNumberType.GetNumberingType()))
3101  {
3102  return pRule->MakeNumString( GetNum(pLayout)->GetNumberVector(),
3103  _bInclPrefixAndSuffixStrings,
3104  false,
3105  _nRestrictToThisLevel,
3106  nullptr,
3107  GetLang(0));
3108  }
3109  }
3110 
3111  return OUString();
3112 }
3113 
3115 {
3116  tools::Long nRet = 0;
3117  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3118  if( pRule )
3119  {
3120  const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3121 
3123  {
3124  nRet = rFormat.GetAbsLSpace();
3125 
3126  if( !bTextLeft )
3127  {
3128  if( 0 > rFormat.GetFirstLineOffset() &&
3129  nRet > -rFormat.GetFirstLineOffset() )
3130  nRet = nRet + rFormat.GetFirstLineOffset();
3131  else
3132  nRet = 0;
3133  }
3134 
3135  if( pRule->IsAbsSpaces() )
3136  nRet = nRet - GetSwAttrSet().GetLRSpace().GetLeft();
3137  }
3139  {
3141  {
3142  nRet = rFormat.GetIndentAt();
3143  // #i90401#
3144  // Only negative first line indents have consider for the left margin
3145  if ( !bTextLeft &&
3146  rFormat.GetFirstLineIndent() < 0 )
3147  {
3148  nRet = nRet + rFormat.GetFirstLineIndent();
3149  }
3150  }
3151  }
3152  }
3153 
3154  return nRet;
3155 }
3156 
3157 bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset ) const
3158 {
3159  // #i95907#
3160  rFLOffset = 0;
3161 
3162  // #i51089#
3163  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3164  if ( pRule )
3165  {
3166  if ( IsCountedInList() )
3167  {
3168  const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3170  {
3171  rFLOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
3172 
3174  {
3176  rFLOffset = rFLOffset + aItem.GetTextFirstLineOffset();
3177  }
3178  }
3180  {
3182  {
3183  rFLOffset = rFormat.GetFirstLineIndent();
3184  }
3186  {
3188  rFLOffset = aItem.GetTextFirstLineOffset();
3189  }
3190  }
3191  }
3192 
3193  return true;
3194  }
3195 
3197  return false;
3198 }
3199 
3201 {
3202  SwTwips nAdditionalIndent = 0;
3203 
3204  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3205  if ( pRule )
3206  {
3207  const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3209  {
3210  nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3211 
3213  {
3214  nAdditionalIndent = nAdditionalIndent -
3216  }
3217  }
3219  {
3221  {
3222  nAdditionalIndent = rFormat.GetIndentAt() + rFormat.GetFirstLineIndent();
3223  }
3224  else
3225  {
3226  nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3228  {
3229  nAdditionalIndent = nAdditionalIndent -
3231  }
3232  }
3233  }
3234  }
3235  else
3236  {
3237  nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
3238  }
3239 
3240  return nAdditionalIndent;
3241 }
3242 
3243 // #i96772#
3244 void SwTextNode::ClearLRSpaceItemDueToListLevelIndents( std::shared_ptr<SvxLRSpaceItem>& o_rLRSpaceItem ) const
3245 {
3247  {
3248  const SwNumRule* pRule = GetNumRule();
3249  if ( pRule && GetActualListLevel() >= 0 )
3250  {
3251  const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3253  {
3254  o_rLRSpaceItem = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
3255  }
3256  }
3257  }
3258 }
3259 
3260 // #i91133#
3262 {
3263  tools::Long nLeftMarginForTabCalc = 0;
3264 
3265  bool bLeftMarginForTabCalcSetToListLevelIndent( false );
3266  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
3267  if( pRule )
3268  {
3269  const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
3271  {
3273  {
3274  nLeftMarginForTabCalc = rFormat.GetIndentAt();
3275  bLeftMarginForTabCalcSetToListLevelIndent = true;
3276  }
3277  }
3278  }
3279  if ( !bLeftMarginForTabCalcSetToListLevelIndent )
3280  {
3281  nLeftMarginForTabCalc = GetSwAttrSet().GetLRSpace().GetTextLeft();
3282  }
3283 
3284  return nLeftMarginForTabCalc;
3285 }
3286 
3287 static void Replace0xFF(
3288  SwTextNode const& rNode,
3289  OUStringBuffer & rText,
3290  sal_Int32 & rTextStt,
3291  sal_Int32 nEndPos )
3292 {
3293  if (!rNode.GetpSwpHints())
3294  return;
3295 
3296  sal_Unicode cSrchChr = CH_TXTATR_BREAKWORD;
3297  for( int nSrchIter = 0; 2 > nSrchIter; ++nSrchIter, cSrchChr = CH_TXTATR_INWORD )
3298  {
3299  sal_Int32 nPos = rText.indexOf(cSrchChr);
3300  while (-1 != nPos && nPos < nEndPos)
3301  {
3302  const SwTextAttr* const pAttr =
3303  rNode.GetTextAttrForCharAt(rTextStt + nPos);
3304  if( pAttr )
3305  {
3306  switch( pAttr->Which() )
3307  {
3308  case RES_TXTATR_FIELD:
3309  case RES_TXTATR_ANNOTATION:
3310  rText.remove(nPos, 1);
3311  ++rTextStt;
3312  break;
3313 
3314  case RES_TXTATR_FTN:
3315  rText.remove(nPos, 1);
3316  ++rTextStt;
3317  break;
3318 
3319  default:
3320  rText.remove(nPos, 1);
3321  ++rTextStt;
3322  }
3323  }
3324  else
3325  {
3326  ++nPos;
3327  ++nEndPos;
3328  }
3329  nPos = rText.indexOf(cSrchChr, nPos);
3330  }
3331  }
3332 }
3333 
3334 // Expand fields
3335 // #i83479# - handling of new parameters
3336 OUString SwTextNode::GetExpandText(SwRootFrame const*const pLayout,
3337  const sal_Int32 nIdx,
3338  const sal_Int32 nLen,
3339  const bool bWithNum,
3340  const bool bAddSpaceAfterListLabelStr,
3341  const bool bWithSpacesForLevel,
3342  const ExpandMode eAdditionalMode) const
3343 
3344 {
3345  ExpandMode eMode = ExpandMode::ExpandFields | eAdditionalMode;
3346  if (pLayout && pLayout->IsHideRedlines())
3347  {
3348  eMode |= ExpandMode::HideDeletions;
3349  }
3350 
3351  ModelToViewHelper aConversionMap(*this, pLayout, eMode);
3352  const OUString aExpandText = aConversionMap.getViewText();
3353  const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nIdx );
3354  sal_Int32 nEnd = nLen == -1 ? GetText().getLength() : nIdx + nLen;
3355  const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
3356  OUStringBuffer aText(aExpandText.subView(nExpandBegin, nExpandEnd-nExpandBegin));
3357 
3358  // remove dummy characters of Input Fields
3361 
3362  if( bWithNum )
3363  {
3364  if (!GetNumString(true, MAXLEVEL, pLayout).isEmpty())
3365  {
3366  if ( bAddSpaceAfterListLabelStr )
3367  {
3368  const sal_Unicode aSpace = ' ';
3369  aText.insert(0, aSpace);
3370  }
3371  aText.insert(0, GetNumString(true, MAXLEVEL, pLayout));
3372  }
3373  }
3374 
3375  if (bWithSpacesForLevel)
3376  {
3377  const sal_Unicode aSpace = ' ';
3378  for (int nLevel = GetActualListLevel(); nLevel > 0; --nLevel)
3379  {
3380  aText.insert(0, aSpace);
3381  aText.insert(0, aSpace);
3382  }
3383  }
3384 
3385  return aText.makeStringAndClear();
3386 }
3387 
3388 bool SwTextNode::CopyExpandText(SwTextNode& rDestNd, const SwIndex* pDestIdx,
3389  sal_Int32 nIdx, sal_Int32 nLen,
3390  SwRootFrame const*const pLayout, bool bWithNum,
3391  bool bWithFootnote, bool bReplaceTabsWithSpaces ) const
3392 {
3393  if( &rDestNd == this )
3394  return false;
3395 
3396  SwIndex aDestIdx(&rDestNd, rDestNd.GetText().getLength());
3397  if( pDestIdx )
3398  aDestIdx = *pDestIdx;
3399  const sal_Int32 nDestStt = aDestIdx.GetIndex();
3400 
3401  // first, start with the text
3402  OUStringBuffer buf(GetText());
3403  if( bReplaceTabsWithSpaces )
3404  buf.replace('\t', ' ');
3405 
3406  // mask hidden characters
3407  const sal_Unicode cChar = CH_TXTATR_BREAKWORD;
3408  SwScriptInfo::MaskHiddenRanges(*this, buf, 0, buf.getLength(), cChar);
3409 
3410  buf.remove(0, nIdx);
3411  if (nLen != -1)
3412  {
3413  buf.truncate(nLen);
3414  }
3415  // remove dummy characters of Input Fields
3416  {
3419  }
3420  rDestNd.InsertText(buf.makeStringAndClear(), aDestIdx);
3421  nLen = aDestIdx.GetIndex() - nDestStt;
3422 
3423  // set all char attributes with Symbol font
3424  if ( HasHints() )
3425  {
3426  sal_Int32 nInsPos = nDestStt - nIdx;
3427  for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
3428  {
3429  const SwTextAttr* pHt = m_pSwpHints->Get(i);
3430  const sal_Int32 nAttrStartIdx = pHt->GetStart();
3431  const sal_uInt16 nWhich = pHt->Which();
3432  if (nIdx + nLen <= nAttrStartIdx)
3433  break; // behind end of text
3434 
3435  const sal_Int32 *pEndIdx = pHt->End();
3436  if( pEndIdx && *pEndIdx > nIdx &&
3437  ( RES_CHRATR_FONT == nWhich ||
3438  RES_TXTATR_CHARFMT == nWhich ||
3439  RES_TXTATR_AUTOFMT == nWhich ))
3440  {
3441  const SvxFontItem* const pFont =
3443  if ( pFont && RTL_TEXTENCODING_SYMBOL == pFont->GetCharSet() )
3444  {
3445  // attribute in area => copy
3446  rDestNd.InsertItem( *const_cast<SvxFontItem*>(pFont),
3447  nInsPos + nAttrStartIdx, nInsPos + *pEndIdx );
3448  }
3449  }
3450  else if ( pHt->HasDummyChar() && (nAttrStartIdx >= nIdx) )
3451  {
3452  aDestIdx = nInsPos + nAttrStartIdx;
3453  switch( nWhich )
3454  {
3455  case RES_TXTATR_FIELD:
3456  case RES_TXTATR_ANNOTATION:
3457  {
3458  OUString const aExpand(
3459  static_txtattr_cast<SwTextField const*>(pHt)->GetFormatField().GetField()->ExpandField(true, pLayout));
3460  if (!aExpand.isEmpty())
3461  {
3462  ++aDestIdx; // insert behind
3463  OUString const ins(
3464  rDestNd.InsertText( aExpand, aDestIdx));
3465  SAL_INFO_IF(ins.getLength() != aExpand.getLength(),
3466  "sw.core", "GetExpandText lossage");
3467  aDestIdx = nInsPos + nAttrStartIdx;
3468  nInsPos += ins.getLength();
3469  }
3470  rDestNd.EraseText( aDestIdx, 1 );
3471  --nInsPos;
3472  }
3473  break;
3474 
3475  case RES_TXTATR_FTN:
3476  {
3477  if ( bWithFootnote )
3478  {
3479  const SwFormatFootnote& rFootnote = pHt->GetFootnote();
3480  OUString sExpand;
3481  auto const number(pLayout && pLayout->IsHideRedlines()
3482  ? rFootnote.GetNumberRLHidden()
3483  : rFootnote.GetNumber());
3484  if( !rFootnote.GetNumStr().isEmpty() )
3485  sExpand = rFootnote.GetNumStr();
3486  else if( rFootnote.IsEndNote() )
3487  sExpand = GetDoc().GetEndNoteInfo().m_aFormat.
3488  GetNumStr(number);
3489  else
3490  sExpand = GetDoc().GetFootnoteInfo().m_aFormat.
3491  GetNumStr(number);
3492  if( !sExpand.isEmpty() )
3493  {
3494  ++aDestIdx; // insert behind
3495  SvxEscapementItem aItem( SvxEscapement::Superscript, RES_CHRATR_ESCAPEMENT );
3496  rDestNd.InsertItem(
3497  aItem,
3498  aDestIdx.GetIndex(),
3499  aDestIdx.GetIndex() );
3500  OUString const ins( rDestNd.InsertText(sExpand, aDestIdx, SwInsertFlags::EMPTYEXPAND));
3501  SAL_INFO_IF(ins.getLength() != sExpand.getLength(),
3502  "sw.core", "GetExpandText lossage");
3503  aDestIdx = nInsPos + nAttrStartIdx;
3504  nInsPos += ins.getLength();
3505  }
3506  }
3507  rDestNd.EraseText( aDestIdx, 1 );
3508  --nInsPos;
3509  }
3510  break;
3511 
3512  default:
3513  rDestNd.EraseText( aDestIdx, 1 );
3514  --nInsPos;
3515  }
3516  }
3517  }
3518  }
3519 
3520  if( bWithNum )
3521  {
3522  aDestIdx = nDestStt;
3523  rDestNd.InsertText( GetNumString(true, MAXLEVEL, pLayout), aDestIdx );
3524  }
3525 
3526  aDestIdx = 0;
3527  sal_Int32 nStartDelete(-1);
3528  while (aDestIdx < rDestNd.GetText().getLength())
3529  {
3530  sal_Unicode const cur(rDestNd.GetText()[aDestIdx.GetIndex()]);
3531  if ( (cChar == cur) // filter substituted hidden text
3532  || (CH_TXT_ATR_FIELDSTART == cur) // filter all fieldmarks
3533  || (CH_TXT_ATR_FIELDSEP == cur)
3534  || (CH_TXT_ATR_FIELDEND == cur)
3535  || (CH_TXT_ATR_FORMELEMENT == cur))
3536  {
3537  if (-1 == nStartDelete)
3538  {
3539  nStartDelete = aDestIdx.GetIndex(); // start deletion range
3540  }
3541  ++aDestIdx;
3542  if (aDestIdx < rDestNd.GetText().getLength())
3543  {
3544  continue;
3545  } // else: end of paragraph => delete, see below
3546  }
3547  else
3548  {
3549  if (-1 == nStartDelete)
3550  {
3551  ++aDestIdx;
3552  continue;
3553  } // else: delete, see below
3554  }
3555  assert(-1 != nStartDelete); // without delete range, would have continued
3556  rDestNd.EraseText(
3557  SwIndex(&rDestNd, nStartDelete),
3558  aDestIdx.GetIndex() - nStartDelete);
3559  assert(aDestIdx.GetIndex() == nStartDelete);
3560  nStartDelete = -1; // reset
3561  }
3562 
3563  return true;
3564 }
3565 
3567 {
3568  std::vector<sal_Int32> aRedlArr;
3569  const SwDoc& rDoc = GetDoc();
3570  SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete );
3571  if( SwRedlineTable::npos != nRedlPos )
3572  {
3573  // some redline-delete object exists for the node
3574  const sal_uLong nNdIdx = GetIndex();
3575  for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
3576  {
3577  const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
3578  if( RedlineType::Delete == pTmp->GetType() )
3579  {
3580  const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
3581  if( pRStt->nNode < nNdIdx )
3582  {
3583  if( pREnd->nNode > nNdIdx )
3584  // paragraph is fully deleted
3585  return OUString();
3586  else if( pREnd->nNode == nNdIdx )
3587  {
3588  // deleted from 0 to nContent
3589  aRedlArr.push_back( 0 );
3590  aRedlArr.push_back( pREnd->nContent.GetIndex() );
3591  }
3592  }
3593  else if( pRStt->nNode == nNdIdx )
3594  {
3595  //aRedlArr.Insert( pRStt->nContent.GetIndex(), aRedlArr.Count() );
3596  aRedlArr.push_back( pRStt->nContent.GetIndex() );
3597  if( pREnd->nNode == nNdIdx )
3598  aRedlArr.push_back( pREnd->nContent.GetIndex() );
3599  else
3600  {
3601  aRedlArr.push_back(GetText().getLength());
3602  break; // that was all
3603  }
3604  }
3605  else
3606  break; // that was all
3607  }
3608  }
3609  }
3610 
3611  OUStringBuffer aText(GetText());
3612 
3613  sal_Int32 nTextStt = 0;
3614  sal_Int32 nIdxEnd = aText.getLength();
3615  for( size_t n = 0; n < aRedlArr.size(); n += 2 )
3616  {
3617  sal_Int32 nStt = aRedlArr[ n ];
3618  sal_Int32 nEnd = aRedlArr[ n+1 ];
3619  if( ( 0 <= nStt && nStt <= nIdxEnd ) ||
3620  ( 0 <= nEnd && nEnd <= nIdxEnd ))
3621  {
3622  if( nStt < 0 ) nStt = 0;
3623  if( nIdxEnd < nEnd ) nEnd = nIdxEnd;
3624  const sal_Int32 nDelCnt = nEnd - nStt;
3625  aText.remove(nStt - nTextStt, nDelCnt);
3626  Replace0xFF(*this, aText, nTextStt, nStt - nTextStt);
3627  nTextStt += nDelCnt;
3628  }
3629  else if( nStt >= nIdxEnd )
3630  break;
3631  }
3632  Replace0xFF(*this, aText, nTextStt, aText.getLength());
3633 
3634  return aText.makeStringAndClear();
3635 }
3636 
3637 void SwTextNode::ReplaceText( const SwIndex& rStart, const sal_Int32 nDelLen,
3638  const OUString & rStr)
3639 {
3640  assert( rStart.GetIndex() < m_Text.getLength() // index out of bounds
3641  && rStart.GetIndex() + nDelLen <= m_Text.getLength());
3642 
3643  sal_Int32 const nOverflow(rStr.getLength() - nDelLen - GetSpaceLeft());
3644  SAL_WARN_IF(nOverflow > 0, "sw.core",
3645  "SwTextNode::ReplaceText: node text with insertion > node capacity.");
3646  OUString const sInserted(
3647  (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
3648  if (sInserted.isEmpty() && 0 == nDelLen)
3649  {
3650  return; // nothing to do
3651  }
3652 
3653  const sal_Int32 nStartPos = rStart.GetIndex();
3654  sal_Int32 nEndPos = nStartPos + nDelLen;
3655  sal_Int32 nLen = nDelLen;
3656  for( sal_Int32 nPos = nStartPos; nPos < nEndPos; ++nPos )
3657  {
3658  if ((CH_TXTATR_BREAKWORD == m_Text[nPos]) ||
3659  (CH_TXTATR_INWORD == m_Text[nPos]))
3660  {
3661  SwTextAttr *const pHint = GetTextAttrForCharAt( nPos );
3662  if (pHint)
3663  {
3664  assert(!( pHint->GetEnd() && pHint->HasDummyChar()
3665  && (pHint->GetStart() < nEndPos)
3666  && (*pHint->GetEnd() > nEndPos) ));
3667  // "deleting left-overlapped attribute with CH_TXTATR"
3668  DeleteAttribute( pHint );
3669  --nEndPos;
3670  --nLen;
3671  }
3672  }
3673  }
3674 
3675  bool bOldExpFlg = IsIgnoreDontExpand();
3676  SetIgnoreDontExpand( true );
3677 
3678  if (nLen && sInserted.getLength())
3679  {
3680  // Replace the 1st char, then delete the rest and insert.
3681  // This way the attributes of the 1st char are expanded!
3682  m_Text = m_Text.replaceAt(nStartPos, 1, sInserted.copy(0, 1));
3683 
3684  ++const_cast<SwIndex&>(rStart);
3685  m_Text = m_Text.replaceAt(rStart.GetIndex(), nLen - 1, "");
3686  Update( rStart, nLen - 1, true );
3687 
3688  OUString aTmpText( sInserted.copy(1) );
3689  m_Text = m_Text.replaceAt(rStart.GetIndex(), 0, aTmpText);
3690  Update( rStart, aTmpText.getLength() );
3691  }
3692  else
3693  {
3694  m_Text = m_Text.replaceAt(nStartPos, nLen, "");
3695  Update( rStart, nLen, true );
3696 
3697  m_Text = m_Text.replaceAt(nStartPos, 0, sInserted);
3698  Update( rStart, sInserted.getLength() );
3699  }
3700 
3701  SetIgnoreDontExpand( bOldExpFlg );
3702  SwDelText aDelHint( nStartPos, nDelLen );
3703  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aDelHint));
3704 
3705  if (sInserted.getLength())
3706  {
3707  SwInsText aHint( nStartPos, sInserted.getLength() );
3708  CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
3709  }
3710 }
3711 
3712 namespace {
3713  void lcl_ResetParAttrs( SwTextNode &rTextNode )
3714  {
3719  SwPaM aPam( rTextNode );
3720  // #i96644#
3721  // suppress side effect "send data changed events"
3722  rTextNode.GetDoc().ResetAttrs( aPam, false, aAttrs, false );
3723  }
3724 
3725  // Helper method for special handling of modified attributes at text node.
3726  // The following is handled:
3727  // (1) on changing the paragraph style - RES_FMT_CHG:
3728  // Check, if list style of the text node is changed. If yes, add respectively
3729  // remove the text node to the corresponding list.
3730  // (2) on changing the attributes - RES_ATTRSET_CHG:
3731  // Same as (1).
3732  // (3) on changing the list style - RES_PARATR_NUMRULE:
3733  // Same as (1).
3734  void HandleModifyAtTextNode( SwTextNode& rTextNode,
3735  const SfxPoolItem* pOldValue,
3736  const SfxPoolItem* pNewValue )
3737  {
3738  const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
3739  pNewValue ? pNewValue->Which() : 0 ;
3740  bool bNumRuleSet = false;
3741  bool bParagraphStyleChanged = false;
3742  OUString sNumRule;
3743  OUString sOldNumRule;
3744  switch ( nWhich )
3745  {
3746  case RES_FMT_CHG:
3747  {
3748  bParagraphStyleChanged = true;
3749  if( rTextNode.GetNodes().IsDocNodes() )
3750  {
3751  const SwNumRule* pFormerNumRuleAtTextNode =
3752  rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3753  if ( pFormerNumRuleAtTextNode )
3754  {
3755  sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3756  }
3757  if ( rTextNode.IsEmptyListStyleDueToSetOutlineLevelAttr() )
3758  {
3759  const SwNumRuleItem& rNumRuleItem = rTextNode.GetTextColl()->GetNumRule();
3760  if ( !rNumRuleItem.GetValue().isEmpty() )
3761  {
3763  }
3764  }
3765  const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3766  if ( pNumRuleAtTextNode )
3767  {
3768  bNumRuleSet = true;
3769  sNumRule = pNumRuleAtTextNode->GetName();
3770  }
3771  }
3772  break;
3773  }
3774  case RES_ATTRSET_CHG:
3775  {
3776  const SfxPoolItem* pItem = nullptr;
3777  const SwNumRule* pFormerNumRuleAtTextNode =
3778  rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3779  if ( pFormerNumRuleAtTextNode )
3780  {
3781  sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3782  }
3783 
3784  const SwAttrSetChg* pSet = dynamic_cast<const SwAttrSetChg*>(pNewValue);
3785  if ( pSet && pSet->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false, &pItem ) ==
3786  SfxItemState::SET )
3787  {
3788  // #i70748#
3790  bNumRuleSet = true;
3791  }
3792  // #i70748#
3793  // The new list style set at the paragraph.
3794  const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3795  if ( pNumRuleAtTextNode )
3796  {
3797  sNumRule = pNumRuleAtTextNode->GetName();
3798  }
3799  break;
3800  }
3801  case RES_PARATR_NUMRULE:
3802  {
3803  if ( rTextNode.GetNodes().IsDocNodes() )
3804  {
3805  const SwNumRule* pFormerNumRuleAtTextNode =
3806  rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
3807  if ( pFormerNumRuleAtTextNode )
3808  {
3809  sOldNumRule = pFormerNumRuleAtTextNode->GetName();
3810  }
3811 
3812  if ( pNewValue )
3813  {
3814  // #i70748#
3816  bNumRuleSet = true;
3817  }
3818  // #i70748#
3819  // The new list style set at the paragraph.
3820  const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
3821  if ( pNumRuleAtTextNode )
3822  {
3823  sNumRule = pNumRuleAtTextNode->GetName();
3824  }
3825  }
3826  break;
3827  }
3828  }
3829  if ( sNumRule != sOldNumRule )
3830  {
3831  if ( bNumRuleSet )
3832  {
3833  if (sNumRule.isEmpty())
3834  {
3835  rTextNode.RemoveFromList();
3836  if ( bParagraphStyleChanged )
3837  {
3838  lcl_ResetParAttrs(rTextNode);
3839  }
3840  }
3841  else
3842  {
3843  rTextNode.RemoveFromList();
3844  // If new list style is the outline style, apply outline
3845  // level as the list level.
3846  if (sNumRule==SwNumRule::GetOutlineRuleName())
3847  {
3848  // #i70748#
3849  OSL_ENSURE( rTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(),
3850  "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." );
3851  const int nNewListLevel =
3853  if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
3854  {
3855  rTextNode.SetAttrListLevel( nNewListLevel );
3856  }
3857  }
3858  rTextNode.AddToList();
3859  }
3860  }
3861  else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
3862  {
3863  rTextNode.RemoveFromList();
3864  if ( bParagraphStyleChanged )
3865  {
3866  lcl_ResetParAttrs(rTextNode);
3867  // #i70748#
3868  if ( dynamic_cast<const SfxUInt16Item &>(rTextNode.GetAttr( RES_PARATR_OUTLINELEVEL, false )).GetValue() > 0 )
3869  {
3871  }
3872  }
3873  }
3874  }
3875  else if (!sNumRule.isEmpty() && !rTextNode.IsInList())
3876  {
3877  rTextNode.AddToList();
3878  }
3879  }
3880  // End of method <HandleModifyAtTextNode>
3881 }
3882 
3884 {
3885  OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
3886  assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && "ChgFormatColl: is not a Text Collection pointer." );
3887 
3888  SwTextFormatColl *pOldColl = GetTextColl();
3889  if( pNewColl != pOldColl )
3890  {
3892  SwContentNode::ChgFormatColl( pNewColl );
3893  OSL_ENSURE( !mbInSetOrResetAttr,
3894  "DEBUG OSL_ENSURE(ON - <SwTextNode::ChgFormatColl(..)> called during <Set/ResetAttr(..)>" );
3895  if ( !mbInSetOrResetAttr )
3896  {
3897  SwFormatChg aTmp1( pOldColl );
3898  SwFormatChg aTmp2( pNewColl );
3899  HandleModifyAtTextNode( *this, &aTmp1, &aTmp2 );
3900  }
3901 
3902  // reset fill information on parent style change
3903  if(maFillAttributes)
3904  {
3905  maFillAttributes.reset();
3906  }
3907  }
3908 
3909  // only for real nodes-array
3910  if( GetNodes().IsDocNodes() )
3911  {
3912  ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl *>(pNewColl) );
3913  }
3914 
3915  return pOldColl;
3916 }
3917 
3918 const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout) const
3919 {
3920  // invariant: it's only in list in Hide mode if it's in list in normal mode
3921  assert(mpNodeNum || !mpNodeNumRLHidden);
3922  return pLayout && pLayout->IsHideRedlines() ? mpNodeNumRLHidden.get() : mpNodeNum.get();
3923 }
3924 
3925 void SwTextNode::DoNum(std::function<void (SwNodeNum &)> const& rFunc)
3926 {
3927  // temp. clear because GetActualListLevel() may be called and the assert
3928  // there triggered during update, which is unhelpful
3929  std::unique_ptr<SwNodeNum> pBackup = std::move(mpNodeNumRLHidden);
3930  assert(mpNodeNum);
3931  rFunc(*mpNodeNum);
3932  if (pBackup)
3933  {
3934  mpNodeNumRLHidden = std::move(pBackup);
3935  rFunc(*mpNodeNumRLHidden);
3936  }
3937 }
3938 
3940 SwTextNode::GetNumberVector(SwRootFrame const*const pLayout) const
3941 {
3942  if (SwNodeNum const*const pNum = GetNum(pLayout))
3943  {
3944  return pNum->GetNumberVector();
3945  }
3946  else
3947  {
3949  return aResult;
3950  }
3951 }
3952 
3954 {
3955  bool bResult = false;
3956 
3957  if ( GetAttrOutlineLevel() > 0 )
3958  {
3959  bResult = !IsInRedlines();
3960  }
3961  else
3962  {
3963  const SwNumRule* pRule( GetNum() ? GetNum()->GetNumRule() : nullptr );
3964  if ( pRule && pRule->IsOutlineRule() )
3965  {
3966  bResult = !IsInRedlines();
3967  }
3968  }
3969 
3970  return bResult;
3971 }
3972 
3974 {
3975  return IsOutline() != m_bLastOutlineState;
3976 }
3977 
3979 {
3981 }
3982 
3984 {
3986 }
3987 
3989 {
3990  assert(0 <= nLevel && nLevel <= MAXLEVEL); // Level Out Of Range
3991  if ( 0 <= nLevel && nLevel <= MAXLEVEL )
3992  {
3994  o3tl::narrowing<sal_uInt16>(nLevel) ) );
3995  }
3996 }
3997 
3998 void SwTextNode::GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr)
3999 {
4000  const SfxGrabBagItem & rGrabBagItem = dynamic_cast<const SfxGrabBagItem&>(GetAttr(RES_PARATR_GRABBAG));
4001  auto it = rGrabBagItem.GetGrabBag().find("OutlineContentVisibleAttr");
4002  if (it != rGrabBagItem.GetGrabBag().end())
4003  it->second >>= bOutlineContentVisibleAttr;
4004 }
4005 
4007 {
4008  SfxGrabBagItem aGrabBagItem(RES_PARATR_GRABBAG);
4009  aGrabBagItem.GetGrabBag()["OutlineContentVisibleAttr"] <<= bVisible;
4010  SetAttr(aGrabBagItem);
4011 }
4012 
4013 // #i70748#
4014 
4016 {
4018  {
4019  SetAttr( SwNumRuleItem() );
4021  }
4022 }
4023 
4025 {
4027  {
4030  }
4031 }
4032 
4034 {
4035  if ( nLevel < 0 || nLevel >= MAXLEVEL )
4036  {
4037  assert(false); // invalid level
4038  return;
4039  }
4040 
4041  SfxInt16Item aNewListLevelItem( RES_PARATR_LIST_LEVEL,
4042  static_cast<sal_Int16>(nLevel) );
4043  SetAttr( aNewListLevelItem );
4044 }
4045 
4047 {
4048  return GetpSwAttrSet() &&
4049  GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL, false ) == SfxItemState::SET;
4050 }
4051 
4053 {
4054  int nAttrListLevel = 0;
4055 
4056  const SfxInt16Item& aListLevelItem =
4057  dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_LEVEL ));
4058  nAttrListLevel = static_cast<int>(aListLevelItem.GetValue());
4059 
4060  return nAttrListLevel;
4061 }
4062 
4064 {
4065  assert(!GetNum() || !mpNodeNumRLHidden || // must be in sync
4066  GetNum()->GetLevelInListTree() == mpNodeNumRLHidden->GetLevelInListTree());
4067  return GetNum() ? GetNum()->GetLevelInListTree() : -1;
4068 }
4069 
4070 void SwTextNode::SetListRestart( bool bRestart )
4071 {
4072  if ( !bRestart )
4073  {
4074  // attribute not contained in paragraph style's attribute set. Thus,
4075  // it can be reset to the attribute pool default by resetting the attribute.
4077  }
4078  else
4079  {
4080  SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART,
4081  true );
4082  SetAttr( aNewIsRestartItem );
4083  }
4084 }
4085 
4087 {
4088  const SfxBoolItem& aIsRestartItem =
4090 
4091  return aIsRestartItem.GetValue();
4092 }
4093 
4099 {
4100  const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4101  if ( pRule && IsCountedInList())
4102  {
4103  // #i87154#
4104  // Correction of #newlistlevelattrs#:
4105  // The numbering type has to be checked for bullet lists.
4106  const SwNumFormat& rFormat = pRule->Get( lcl_BoundListLevel(GetActualListLevel()) );
4107  return SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ||
4108  !pRule->MakeNumString( *(GetNum()) ).isEmpty();
4109  }
4110 
4111  return false;
4112 }
4113 
4115 {
4116  const bool bChanged( HasAttrListRestartValue()
4117  ? GetAttrListRestartValue() != nNumber
4118  : nNumber != USHRT_MAX );
4119 
4120  if ( !bChanged && HasAttrListRestartValue() )
4121  return;
4122 
4123  if ( nNumber == USHRT_MAX )
4124  {
4126  }
4127  else
4128  {
4129  SfxInt16Item aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE,
4130  static_cast<sal_Int16>(nNumber) );
4131  SetAttr( aNewListRestartValueItem );
4132  }
4133 }
4134 
4136 {
4137  return GetpSwAttrSet() &&
4138  GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, false ) == SfxItemState::SET;
4139 }
4141 {
4142  OSL_ENSURE( HasAttrListRestartValue(),
4143  "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
4144 
4145  const SfxInt16Item& aListRestartValueItem =
4146  dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_RESTARTVALUE ));
4147  return static_cast<SwNumberTree::tSwNumTreeNumber>(aListRestartValueItem.GetValue());
4148 }
4149 
4151 {
4152  SwNumberTree::tSwNumTreeNumber nListRestartValue = 1;
4153 
4155  {
4156  nListRestartValue = GetAttrListRestartValue();
4157  }
4158  else
4159  {
4160  SwNumRule* pRule = GetNumRule();
4161  if ( pRule )
4162  {
4163  const SwNumFormat* pFormat =
4164  pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(GetAttrListLevel()) );
4165  if ( pFormat )
4166  {
4167  nListRestartValue = pFormat->GetStart();
4168  }
4169  }
4170  }
4171 
4172  return nListRestartValue;
4173 }
4174 
4176 {
4178 }
4179 
4181 {
4182  const SwDoc& rDoc = GetDoc();
4183  return !rDoc.IsInReading() && !rDoc.IsInDtor();
4184 }
4185 
4186 void SwTextNode::SetCountedInList( bool bCounted )
4187 {
4188  if ( bCounted )
4189  {
4190  // attribute not contained in paragraph style's attribute set. Thus,
4191  // it can be reset to the attribute pool default by resetting the attribute.
4193  }
4194  else
4195  {
4196  SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, false );
4197  SetAttr( aIsCountedInListItem );
4198  }
4199 }
4200 
4202 {
4203  const SfxBoolItem& aIsCountedInListItem =
4205 
4206  return aIsCountedInListItem.GetValue();
4207 }
4208 
4209 static SwList * FindList(SwTextNode *const pNode)
4210 {
4211  const OUString sListId = pNode->GetListId();
4212  if (!sListId.isEmpty())
4213  {
4214  auto & rIDLA(pNode->GetDoc().getIDocumentListsAccess());
4215  SwList* pList = rIDLA.getListByName( sListId );
4216  if ( pList == nullptr )
4217  {
4218  // Create corresponding list.
4219  SwNumRule* pNumRule = pNode->GetNumRule();
4220  if ( pNumRule )
4221  {
4222  pList = rIDLA.createList(sListId, pNode->GetNumRule()->GetName());
4223  }
4224  }
4225  OSL_ENSURE( pList != nullptr,
4226  "<SwTextNode::AddToList()> - no list for given list id. Serious defect" );
4227  return pList;
4228  }
4229  return nullptr;
4230 }
4231 
4233 {
4234  if ( IsInList() )
4235  {
4236  OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" );
4237  return;
4238  }
4239 
4240  SwList *const pList(FindList(this));
4241  if (!(pList && GetNodes().IsDocNodes())) // not for undo nodes
4242  return;
4243 
4244  assert(!mpNodeNum);
4245  mpNodeNum.reset(new SwNodeNum(this, false));
4246  pList->InsertListItem(*mpNodeNum, false, GetAttrListLevel(), GetDoc());
4247  // iterate all frames & if there's one with hidden layout...
4249  for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
4250  {
4251  if (pFrame->getRootFrame()->IsHideRedlines())
4252  {
4253  if (pFrame->GetTextNodeForParaProps() == this)
4254  {
4256  }
4257  break; // assume it's consistent, need to check only once
4258  }
4259  }
4260 }
4261 
4263 {
4264  if (mpNodeNumRLHidden)
4265  {
4266  assert(false);
4267  OSL_FAIL( "<SwTextNode::AddToListRLHidden()> - the text node is already added to a list. Serious defect" );
4268  return;
4269  }
4270 
4271  SwList *const pList(FindList(this));
4272  if (pList)
4273  {
4274  assert(!mpNodeNumRLHidden);
4275  mpNodeNumRLHidden.reset(new SwNodeNum(this, true));
4277  }
4278 }
4279 
4281 {
4282  // sw_redlinehide: ensure it's removed from the other half too!
4284  if ( IsInList() )
4285  {
4287  mpNodeNum.reset();
4288 
4289  SetWordCountDirty( true );
4290  }
4291 }
4292 
4294 {
4295  if (mpNodeNumRLHidden) // direct access because RemoveFromList doesn't have layout
4296  {
4297  assert(mpNodeNumRLHidden->GetParent() || !GetNodes().IsDocNodes());
4299  mpNodeNumRLHidden.reset();
4300 
4301  SetWordCountDirty( true );
4302  }
4303 }
4304 
4306 {
4307  return GetNum() != nullptr && GetNum()->GetParent() != nullptr;
4308 }
4309 
4310 bool SwTextNode::IsFirstOfNumRule(SwRootFrame const& rLayout) const
4311 {
4312  bool bResult = false;
4313 
4314  SwNodeNum const*const pNum(GetNum(&rLayout));
4315  if (pNum && pNum->GetNumRule())
4316  bResult = pNum->IsFirst();
4317 
4318  return bResult;
4319 }
4320 
4321 void SwTextNode::SetListId(OUString const& rListId)
4322 {
4323  const SfxStringItem& rListIdItem =
4324  dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID ));
4325  if (rListIdItem.GetValue() != rListId)
4326  {
4327  if (rListId.isEmpty())
4328  {
4330  }
4331  else
4332  {
4333  SfxStringItem aNewListIdItem(RES_PARATR_LIST_ID, rListId);
4334  SetAttr( aNewListIdItem );
4335  }
4336  }
4337 }
4338 
4339 OUString SwTextNode::GetListId() const
4340 {
4341  const SfxStringItem& rListIdItem =
4342  dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID ));
4343  const OUString& sListId {rListIdItem.GetValue()};
4344 
4345  // As long as no explicit list id attribute is set, use the list id of
4346  // the list, which has been created for the applied list style.
4347  if (sListId.isEmpty())
4348  {
4349  SwNumRule* pRule = GetNumRule();
4350  if ( pRule )
4351  {
4352  return pRule->GetDefaultListId();
4353  }
4354  }
4355 
4356  return sListId;
4357 }
4358 
4374 {
4375  bool bAreListLevelIndentsApplicable( true );
4376 
4377  if ( !GetNum() || !GetNum()->GetNumRule() )
4378  {
4379  // no list style applied to paragraph
4380  bAreListLevelIndentsApplicable = false;
4381  }
4382  else if ( HasSwAttrSet() &&
4383  GetpSwAttrSet()->GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
4384  {
4385  // paragraph has hard-set indent attributes
4386  bAreListLevelIndentsApplicable = false;
4387  }
4388  else if ( HasSwAttrSet() &&
4389  GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4390  {
4391  // list style is directly applied to paragraph and paragraph has no
4392  // hard-set indent attributes
4393  bAreListLevelIndentsApplicable = true;
4394  }
4395  else
4396  {
4397  // list style is applied through one of the paragraph styles and
4398  // paragraph has no hard-set indent attributes
4399 
4400  // check, paragraph's
4401  const SwTextFormatColl* pColl = GetTextColl();
4402  while ( pColl )
4403  {
4404  if ( pColl->GetAttrSet().GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
4405  {
4406  // indent attributes found in the paragraph style hierarchy.
4407  bAreListLevelIndentsApplicable = false;
4408  break;
4409  }
4410 
4411  if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
4412  {
4413  // paragraph style with the list style found and until now no
4414  // indent attributes are found in the paragraph style hierarchy.
4415  bAreListLevelIndentsApplicable = true;
4416  break;
4417  }
4418 
4419  pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
4420  OSL_ENSURE( pColl,
4421  "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
4422  }
4423  }
4424 
4425  return bAreListLevelIndentsApplicable;
4426 }
4427 
4436 bool SwTextNode::GetListTabStopPosition( tools::Long& nListTabStopPosition ) const
4437 {
4438  bool bListTabStopPositionProvided(false);
4439 
4440  const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4441  if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4442  {
4443  const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4446  {
4447  bListTabStopPositionProvided = true;
4448  nListTabStopPosition = rFormat.GetListtabPos();
4449 
4451  {
4452  // tab stop position are treated to be relative to the "before text"
4453  // indent value of the paragraph. Thus, adjust <nListTabStopPos>.
4455  {
4456  nListTabStopPosition -= rFormat.GetIndentAt();
4457  }
4459  {
4461  nListTabStopPosition -= aItem.GetTextLeft();
4462  }
4463  }
4464  }
4465  }
4466 
4467  return bListTabStopPositionProvided;
4468 }
4469 
4471 {
4472  const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
4473  if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
4474  {
4475  const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
4477  {
4478  return rFormat.GetLabelFollowedByAsString();
4479  }
4480  }
4481 
4482  return OUString();
4483 }
4484 
4486 {
4487  sal_Int32 nStartPos;
4488  sal_Int32 nEndPos;
4489  // Update of the flags is done inside GetBoundsOfHiddenRange()
4490  SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos, nEndPos );
4491 }
4492 
4493 // #i12836# enhanced pdf export
4495 {
4496  if ( IsHiddenByParaField() || HasHiddenCharAttribute( true ) )
4497  return true;
4498 
4499  const SwSectionNode* pSectNd = FindSectionNode();
4500  return pSectNd && pSectNd->GetSection().IsHiddenFlag();
4501 }
4502 
4503 namespace {
4504  // Helper class for special handling of setting attributes at text node:
4505  // In constructor an instance of the helper class recognize whose attributes
4506  // are set and perform corresponding actions before the intrinsic set of
4507  // attributes has been taken place.
4508  // In the destructor - after the attributes have been set at the text
4509  // node - corresponding actions are performed.
4510  // The following is handled:
4511  // (1) When the list style attribute - RES_PARATR_NUMRULE - is set,
4512  // (A) list style attribute is empty -> the text node is removed from
4513  // its list.
4514  // (B) list style attribute is not empty
4515  // (a) text node has no list style -> add text node to its list after
4516  // the attributes have been set.
4517  // (b) text node has list style -> change of list style is notified
4518  // after the attributes have been set.
4519  // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed,
4520  // the text node is removed from its current list before the attributes
4521  // are set and added to its new list after the attributes have been set.
4522  // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set
4523  // and changed after the attributes have been set
4524  // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set
4525  // and changed after the attributes have been set
4526  // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE -
4527  // is set and changed after the attributes have been set
4528  // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set
4529  // and changed after the attributes have been set
4530  // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL.
4531  class HandleSetAttrAtTextNode
4532  {
4533  public:
4534  HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4535  const SfxPoolItem& pItem );
4536  HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4537  const SfxItemSet& rItemSet );
4538  ~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
4539 
4540  private:
4541  SwTextNode& mrTextNode;
4542  bool mbAddTextNodeToList;
4543  bool mbUpdateListLevel;
4544  bool mbUpdateListRestart;
4545  bool mbUpdateListCount;
4546  // #i70748#
4547  bool mbOutlineLevelSet;
4548  };
4549 
4550  HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4551  const SfxPoolItem& pItem )
4552  : mrTextNode( rTextNode ),
4553  mbAddTextNodeToList( false ),
4554  mbUpdateListLevel( false ),
4555  mbUpdateListRestart( false ),
4556  mbUpdateListCount( false ),
4557  // #i70748#
4558  mbOutlineLevelSet( false )
4559  {
4560  switch ( pItem.Which() )
4561  {
4562  // handle RES_PARATR_NUMRULE
4563  case RES_PARATR_NUMRULE:
4564  {
4565  mrTextNode.RemoveFromList();
4566 
4567  const SwNumRuleItem& rNumRuleItem =
4568  dynamic_cast<const SwNumRuleItem&>(pItem);
4569  if ( !rNumRuleItem.GetValue().isEmpty() )
4570  {
4571  mbAddTextNodeToList = true;
4572  // #i105562#
4573 
4574  mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4575  }
4576  }
4577  break;
4578 
4579  // handle RES_PARATR_LIST_ID
4580  case RES_PARATR_LIST_ID:
4581  {
4582  const SfxStringItem& rListIdItem =
4583  dynamic_cast<const SfxStringItem&>(pItem);
4584  OSL_ENSURE( rListIdItem.GetValue().getLength() > 0,
4585  "<HandleSetAttrAtTextNode(..)> - empty list id attribute not expected. Serious defect." );
4586  const OUString sListIdOfTextNode = rTextNode.GetListId();
4587  if ( rListIdItem.GetValue() != sListIdOfTextNode )
4588  {
4589  mbAddTextNodeToList = true;
4590  if ( mrTextNode.IsInList() )
4591  {
4592  mrTextNode.RemoveFromList();
4593  }
4594  }
4595  }
4596  break;
4597 
4598  // handle RES_PARATR_LIST_LEVEL
4599  case RES_PARATR_LIST_LEVEL:
4600  {
4601  const SfxInt16Item& aListLevelItem =
4602  dynamic_cast<const SfxInt16Item&>(pItem);
4603  if ( aListLevelItem.GetValue() != mrTextNode.GetAttrListLevel() )
4604  {
4605  mbUpdateListLevel = true;
4606  }
4607  }
4608  break;
4609 
4610  // handle RES_PARATR_LIST_ISRESTART
4612  {
4613  const SfxBoolItem& aListIsRestartItem =
4614  dynamic_cast<const SfxBoolItem&>(pItem);
4615  if ( aListIsRestartItem.GetValue() !=
4616  mrTextNode.IsListRestart() )
4617  {
4618  mbUpdateListRestart = true;
4619  }
4620  }
4621  break;
4622 
4623  // handle RES_PARATR_LIST_RESTARTVALUE
4625  {
4626  const SfxInt16Item& aListRestartValueItem =
4627  dynamic_cast<const SfxInt16Item&>(pItem);
4628  if ( !mrTextNode.HasAttrListRestartValue() ||
4629  aListRestartValueItem.GetValue() != mrTextNode.GetAttrListRestartValue() )
4630  {
4631  mbUpdateListRestart = true;
4632  }
4633  }
4634  break;
4635 
4636  // handle RES_PARATR_LIST_ISCOUNTED
4638  {
4639  const SfxBoolItem& aIsCountedInListItem =
4640  dynamic_cast<const SfxBoolItem&>(pItem);
4641  if ( aIsCountedInListItem.GetValue() !=
4642  mrTextNode.IsCountedInList() )
4643  {
4644  mbUpdateListCount = true;
4645  }
4646  }
4647  break;
4648 
4649  // #i70748#
4650  // handle RES_PARATR_OUTLINELEVEL
4652  {
4653  const SfxUInt16Item& aOutlineLevelItem =
4654  dynamic_cast<const SfxUInt16Item&>(pItem);
4655  if ( aOutlineLevelItem.GetValue() != mrTextNode.GetAttrOutlineLevel() )
4656  {
4657  mbOutlineLevelSet = true;
4658  }
4659  }
4660  break;
4661  }
4662 
4663  }
4664 
4665  HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
4666  const SfxItemSet& rItemSet )
4667  : mrTextNode( rTextNode ),
4668  mbAddTextNodeToList( false ),
4669  mbUpdateListLevel( false ),
4670  mbUpdateListRestart( false ),
4671  mbUpdateListCount( false ),
4672  // #i70748#
4673  mbOutlineLevelSet( false )
4674  {
4675  const SfxPoolItem* pItem = nullptr;
4676  // handle RES_PARATR_NUMRULE
4677  if ( rItemSet.GetItemState( RES_PARATR_NUMRULE, false, &pItem ) == SfxItemState::SET )
4678  {
4679  mrTextNode.RemoveFromList();
4680 
4681  const SwNumRuleItem* pNumRuleItem =
4682  dynamic_cast<const SwNumRuleItem*>(pItem);
4683  assert(pNumRuleItem);
4684  if ( !pNumRuleItem->GetValue().isEmpty() )
4685  {
4686  mbAddTextNodeToList = true;
4687  // #i70748#
4688  mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4689  }
4690  }
4691 
4692  // handle RES_PARATR_LIST_ID
4693  if ( rItemSet.GetItemState( RES_PARATR_LIST_ID, false, &pItem ) == SfxItemState::SET )
4694  {
4695  const SfxStringItem* pListIdItem =
4696  dynamic_cast<const SfxStringItem*>(pItem);
4697  const OUString sListIdOfTextNode = mrTextNode.GetListId();
4698  if ( pListIdItem &&
4699  pListIdItem->GetValue() != sListIdOfTextNode )
4700  {
4701  mbAddTextNodeToList = true;
4702  if ( mrTextNode.IsInList() )
4703  {
4704  mrTextNode.RemoveFromList();
4705  }
4706  }
4707  }
4708 
4709  // handle RES_PARATR_LIST_LEVEL
4710  if ( rItemSet.GetItemState( RES_PARATR_LIST_LEVEL, false, &pItem ) == SfxItemState::SET )
4711  {
4712  const SfxInt16Item* pListLevelItem =
4713  dynamic_cast<const SfxInt16Item*>(pItem);
4714  if (pListLevelItem && pListLevelItem->GetValue() != mrTextNode.GetAttrListLevel())
4715  {
4716  mbUpdateListLevel = true;
4717  }
4718  }
4719 
4720  // handle RES_PARATR_LIST_ISRESTART
4721  if ( rItemSet.GetItemState( RES_PARATR_LIST_ISRESTART, false, &pItem ) == SfxItemState::SET )
4722  {
4723  const SfxBoolItem* pListIsRestartItem =
4724  dynamic_cast<const SfxBoolItem*>(pItem);
4725  if (pListIsRestartItem && pListIsRestartItem->GetValue() != mrTextNode.IsListRestart())
4726  {
4727  mbUpdateListRestart = true;
4728  }
4729  }
4730 
4731  // handle RES_PARATR_LIST_RESTARTVALUE
4732  if ( rItemSet.GetItemState( RES_PARATR_LIST_RESTARTVALUE, false, &pItem ) == SfxItemState::SET )
4733  {
4734  const SfxInt16Item* pListRestartValueItem =
4735  dynamic_cast<const SfxInt16Item*>(pItem);
4736  if ( !mrTextNode.HasAttrListRestartValue() || (pListRestartValueItem &&
4737  pListRestartValueItem->GetValue() != mrTextNode.GetAttrListRestartValue()) )
4738  {
4739  mbUpdateListRestart = true;
4740  }
4741  }
4742 
4743  // handle RES_PARATR_LIST_ISCOUNTED
4744  if ( rItemSet.GetItemState( RES_PARATR_LIST_ISCOUNTED, false, &pItem ) == SfxItemState::SET )
4745  {
4746  const SfxBoolItem* pIsCountedInListItem =
4747  dynamic_cast<const SfxBoolItem*>(pItem);
4748  if (pIsCountedInListItem && pIsCountedInListItem->GetValue() !=
4749  mrTextNode.IsCountedInList())
4750  {
4751  mbUpdateListCount = true;
4752  }
4753  }
4754 
4755  // #i70748#
4756  // handle RES_PARATR_OUTLINELEVEL
4757  if ( rItemSet.GetItemState( RES_PARATR_OUTLINELEVEL, false, &pItem ) == SfxItemState::SET )
4758  {
4759  const SfxUInt16Item* pOutlineLevelItem =
4760  dynamic_cast<const SfxUInt16Item*>(pItem);
4761  if (pOutlineLevelItem && pOutlineLevelItem->GetValue() !=
4762  mrTextNode.GetAttrOutlineLevel())
4763  {
4764  mbOutlineLevelSet = true;
4765  }
4766  }
4767  }
4768 
4769  HandleSetAttrAtTextNode::~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
4770  {
4771  if ( mbAddTextNodeToList )
4772  {
4773  SwNumRule* pNumRuleAtTextNode = mrTextNode.GetNumRule();
4774  if ( pNumRuleAtTextNode )
4775  {
4776  mrTextNode.AddToList();
4777  }
4778  }
4779  else
4780  {
4781  if ( mbUpdateListLevel && mrTextNode.IsInList() )
4782  {
4783  auto const nLevel(mrTextNode.GetAttrListLevel());
4784  const SwDoc& rDoc(mrTextNode.GetDoc());
4785  mrTextNode.DoNum(
4786  [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
4787  }
4788 
4789  if ( mbUpdateListRestart && mrTextNode.IsInList() )
4790  {
4791  const SwDoc& rDoc(mrTextNode.GetDoc());
4792  mrTextNode.DoNum(
4793  [&rDoc](SwNodeNum & rNum) {
4794  rNum.InvalidateMe();
4795  rNum.NotifyInvalidSiblings(rDoc);
4796  });
4797  }
4798 
4799  if (mbUpdateListCount && mrTextNode.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode))
4800  {
4801  // Repaint all text frames that belong to this numbering to avoid outdated generated
4802  // numbers.
4803  const SwDoc& rDoc(mrTextNode.GetDoc());
4804  mrTextNode.DoNum(
4805  [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
4806  }
4807  }
4808 
4809  // #i70748#
4810  if (!mbOutlineLevelSet)
4811  return;
4812 
4813  mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode);
4814  if (mrTextNode.GetAttrOutlineLevel() == 0)
4815  {
4816  mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4817  }
4818  else
4819  {
4820  const SfxPoolItem* pItem = nullptr;
4821  if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE,
4822  true, &pItem )
4823  != SfxItemState::SET )
4824  {
4825  mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
4826  }
4827  }
4828  }
4829  // End of class <HandleSetAttrAtTextNode>
4830 }
4831 
4832 bool SwTextNode::SetAttr( const SfxPoolItem& pItem )
4833 {
4834  const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
4835  mbInSetOrResetAttr = true;
4836 
4837  HandleSetAttrAtTextNode aHandleSetAttr( *this, pItem );
4838 
4839  bool bRet = SwContentNode::SetAttr( pItem );
4840 
4841  mbInSetOrResetAttr = bOldIsSetOrResetAttr;
4842 
4843  return bRet;
4844 }
4845 
4846 bool SwTextNode::SetAttr( const SfxItemSet& rSet )
4847 {
4848  const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
4849  mbInSetOrResetAttr = true;
4850 
4851  HandleSetAttrAtTextNode aHandleSetAttr( *this, rSet );
4852 
4853  bool bRet = SwContentNode::SetAttr( rSet );
4854 
4855  mbInSetOrResetAttr = bOldIsSetOrResetAttr;
4856 
4857  return bRet;
4858 }
4859 
4860 void SwTextNode::SetInSwUndo(bool bInUndo)
4861 {
4862  m_bInUndo = bInUndo;
4863 }
4864 
4865 namespace {
4866  // Helper class for special handling of resetting attributes at text node:
4867  // In constructor an instance of the helper class recognize whose attributes
4868  // are reset and perform corresponding actions before the intrinsic reset of
4869  // attributes has been taken place.
4870  // In the destructor - after the attributes have been reset at the text
4871  // node - corresponding actions are performed.
4872  // The following is handled:
4873  // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset,
4874  // the text is removed from its list before the attributes have been reset.
4875  // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset,
4876  // the text is removed from its list before the attributes have been reset.
4877  // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset.
4878  // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset.
4879  // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset.
4880  // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset.
4881  // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset.
4882  class HandleResetAttrAtTextNode
4883  {
4884  public:
4885  HandleResetAttrAtTextNode( SwTextNode& rTextNode,
4886  const sal_uInt16 nWhich1,
4887  sal_uInt16 nWhich2 );
4888  HandleResetAttrAtTextNode( SwTextNode& rTextNode,
4889  const std::vector<sal_uInt16>& rWhichArr );
4890  explicit HandleResetAttrAtTextNode( SwTextNode& rTextNode );
4891 
4892  ~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
4893 
4894  private:
4895  SwTextNode& mrTextNode;
4896  bool mbListStyleOrIdReset;
4897  bool mbUpdateListLevel;
4898  bool mbUpdateListRestart;
4899  bool mbUpdateListCount;
4900 
4901  void init( const std::vector<sal_uInt16>& rWhichArr );
4902  };
4903 
4904  HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
4905  const sal_uInt16 nWhich1,
4906  sal_uInt16 nWhich2 )
4907  : mrTextNode( rTextNode ),
4908  mbListStyleOrIdReset( false ),
4909  mbUpdateListLevel( false ),
4910  mbUpdateListRestart( false ),
4911  mbUpdateListCount( false )
4912  {
4913  if ( nWhich2 < nWhich1 )
4914  nWhich2 = nWhich1;
4915  std::vector<sal_uInt16> rWhichArr;
4916  for ( sal_uInt16 nWhich = nWhich1; nWhich <= nWhich2; ++nWhich )
4917  rWhichArr.push_back( nWhich );
4918 
4919  init( rWhichArr );
4920  }
4921 
4922  HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
4923  const std::vector<sal_uInt16>& rWhichArr )
4924  : mrTextNode( rTextNode ),
4925  mbListStyleOrIdReset( false ),
4926  mbUpdateListLevel( false ),
4927  mbUpdateListRestart( false ),
4928  mbUpdateListCount( false )
4929  {
4930  init( rWhichArr );
4931  }
4932 
4933  HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode )
4934  : mrTextNode( rTextNode ),
4935  mbListStyleOrIdReset( true ),
4936  mbUpdateListLevel( false ),
4937  mbUpdateListRestart( false ),
4938  mbUpdateListCount( false )
4939  {
4940  if ( rTextNode.IsInList() )
4941  {
4942  rTextNode.RemoveFromList();
4943  }
4944  // #i70748#
4945  mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4946  }
4947 
4948  void HandleResetAttrAtTextNode::init( const std::vector<sal_uInt16>& rWhichArr )
4949  {
4950  bool bRemoveFromList( false );
4951  {
4952  for (const auto& rWhich : rWhichArr)
4953  {
4954  if ( rWhich == RES_PARATR_NUMRULE )
4955  {
4956  bRemoveFromList = bRemoveFromList ||
4957  mrTextNode.GetNumRule() != nullptr;
4958  mbListStyleOrIdReset = true;
4959  }
4960  else if ( rWhich == RES_PARATR_LIST_ID )
4961  {
4962  bRemoveFromList = bRemoveFromList ||
4963  ( mrTextNode.GetpSwAttrSet() &&
4964  mrTextNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, false ) == SfxItemState::SET );
4965  mbListStyleOrIdReset = true;
4966  }
4967  else if ( rWhich == RES_PARATR_OUTLINELEVEL )
4968  mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
4969  else if ( rWhich == RES_BACKGROUND )
4970  mrTextNode.ResetAttr( XATTR_FILL_FIRST, XATTR_FILL_LAST );
4971 
4972  if ( !bRemoveFromList )
4973  {
4974  // RES_PARATR_LIST_LEVEL
4975  mbUpdateListLevel = mbUpdateListLevel ||
4976  ( rWhich == RES_PARATR_LIST_LEVEL &&
4977  mrTextNode.HasAttrListLevel() );
4978 
4979  // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE
4980  mbUpdateListRestart = mbUpdateListRestart ||
4981  ( rWhich == RES_PARATR_LIST_ISRESTART &&
4982  mrTextNode.IsListRestart() ) ||
4983  ( rWhich == RES_PARATR_LIST_RESTARTVALUE &&
4984  mrTextNode.HasAttrListRestartValue() );
4985 
4986  // RES_PARATR_LIST_ISCOUNTED
4987  mbUpdateListCount = mbUpdateListCount ||
4988  ( rWhich == RES_PARATR_LIST_ISCOUNTED &&
4989  !mrTextNode.IsCountedInList() );
4990  }
4991  }
4992  }
4993 
4994  if ( bRemoveFromList && mrTextNode.IsInList() )
4995  {
4996  mrTextNode.RemoveFromList();
4997  }
4998  }
4999 
5000  HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
5001  {
5002  if ( mbListStyleOrIdReset && !mrTextNode.IsInList() )
5003  {
5004  // check, if in spite of the reset of the list style or the list id
5005  // the paragraph still has to be added to a list.
5006  if (mrTextNode.GetNumRule() && !mrTextNode.GetListId().isEmpty())
5007  {
5008  // #i96062#
5009  // If paragraph has no list level attribute set and list style
5010  // is the outline style, apply outline level as the list level.
5011  if ( !mrTextNode.HasAttrListLevel() &&
5012  mrTextNode.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
5013  mrTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
5014  {
5015  int nNewListLevel = mrTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
5016  if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
5017  {
5018  mrTextNode.SetAttrListLevel( nNewListLevel );
5019  }
5020  }
5021  mrTextNode.AddToList();
5022  }
5023  // #i70748#
5024  // #i105562#
5025  else
5026  {
5027  assert(!mrTextNode.GetpSwAttrSet()
5028  || dynamic_cast<const SfxUInt16Item*>(
5029  &mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false)));
5030  if (mrTextNode.GetpSwAttrSet()
5031  && static_cast<const SfxUInt16Item&>(
5032  mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false)).GetValue() > 0)
5033  {
5034  mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
5035  }
5036  }
5037  }
5038 
5039  if ( !mrTextNode.IsInList() )
5040  return;
5041 
5042  if ( mbUpdateListLevel )
5043  {
5044  auto const nLevel(mrTextNode.GetAttrListLevel());
5045  const SwDoc& rDoc(mrTextNode.GetDoc());
5046  mrTextNode.DoNum(
5047  [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
5048  }
5049 
5050  if ( mbUpdateListRestart )
5051  {
5052  const SwDoc& rDoc(mrTextNode.GetDoc());
5053  mrTextNode.DoNum(
5054  [&rDoc](SwNodeNum & rNum) {
5055  rNum.InvalidateMe();
5056  rNum.NotifyInvalidSiblings(rDoc);
5057  });
5058  }
5059 
5060  if ( mbUpdateListCount )
5061  {
5062  const SwDoc& rDoc(mrTextNode.GetDoc());
5063  mrTextNode.DoNum(
5064  [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
5065  }
5066  }
5067  // End of class <HandleResetAttrAtTextNode>
5068 }
5069 
5070 bool SwTextNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
5071 {
5072  const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5073  mbInSetOrResetAttr = true;
5074 
5075  HandleResetAttrAtTextNode aHandleResetAttr( *this, nWhich1, nWhich2 );
5076 
5077  bool bRet = SwContentNode::ResetAttr( nWhich1, nWhich2 );
5078 
5079  mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5080 
5081  return bRet;
5082 }
5083 
5084 bool SwTextNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
5085 {
5086  const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5087  mbInSetOrResetAttr = true;
5088 
5089  HandleResetAttrAtTextNode aHandleResetAttr( *this, rWhichArr );
5090 
5091  bool bRet = SwContentNode::ResetAttr( rWhichArr );
5092 
5093  mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5094 
5095  return bRet;
5096 }
5097 
5099 {
5100  const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
5101  mbInSetOrResetAttr = true;
5102 
5103  HandleResetAttrAtTextNode aHandleResetAttr( *this );
5104 
5105  const sal_uInt16 nRet = SwContentNode::ResetAllAttr();
5106 
5107  mbInSetOrResetAttr = bOldIsSetOrResetAttr;
5108 
5109  return nRet;
5110 }
5111 
5113 {
5114  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextNode"));
5115  (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
5116  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(GetIndex()).getStr()));
5117 
5118  OUString sText = GetText();
5119  for (int i = 0; i < 32; ++i)
5120  sText = sText.replace(i, '*');
5121  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Text"));
5122  (void)xmlTextWriterWriteString(pWriter, BAD_CAST(sText.toUtf8().getStr()));
5123  (void)xmlTextWriterEndElement(pWriter);
5124 
5125  if (GetFormatColl())
5126  {
5127  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
5128  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toUtf8().getStr()));
5129  (void)xmlTextWriterEndElement(pWriter);
5130  }
5131 
5132  if (HasSwAttrSet())
5133  {
5134  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAttrSet"));
5135  GetSwAttrSet().dumpAsXml(pWriter);
5136  (void)xmlTextWriterEndElement(pWriter);
5137  }
5138 
5139  if (HasHints())
5140  {
5141  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwpHints"));
5142  const SwpHints& rHints = GetSwpHints();
5143  for (size_t i = 0; i < rHints.Count(); ++i)
5144  rHints.Get(i)->dumpAsXml(pWriter);
5145  (void)xmlTextWriterEndElement(pWriter);
5146  }
5147 
5148  if (GetNumRule())
5149  GetNumRule()->dumpAsXml(pWriter);
5150 
5151  (void)xmlTextWriterEndElement(pWriter);
5152 }
5153 
5154 sal_uInt32 SwTextNode::GetRsid( sal_Int32 nStt, sal_Int32 nEnd ) const
5155 {
5156  SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet( const_cast<SwAttrPool&>((GetDoc().GetAttrPool())) );
5157  if (GetParaAttr(aSet, nStt, nEnd))
5158  {
5159  const SvxRsidItem* pRsid = aSet.GetItem<SvxRsidItem>(RES_CHRATR_RSID);
5160  if( pRsid )
5161  return pRsid->GetValue();
5162  }
5163 
5164  return 0;
5165 }
5166 
5167 sal_uInt32 SwTextNode::GetParRsid() const
5168 {
5169  return reinterpret_cast<const SvxRsidItem&>(GetAttr( RES_PARATR_RSID )).GetValue();
5170 }
5171 
5172 bool SwTextNode::CompareParRsid( const SwTextNode &rTextNode ) const
5173 {
5174  sal_uInt32 nThisRsid = GetParRsid();
5175  sal_uInt32 nRsid = rTextNode.GetParRsid();
5176 
5177  return nThisRsid == nRsid;
5178 }
5179 
5180 bool SwTextNode::CompareRsid( const SwTextNode &rTextNode, sal_Int32 nStt1, sal_Int32 nStt2 ) const
5181 {
5182  sal_uInt32 nThisRsid = GetRsid( nStt1, nStt1 );
5183  sal_uInt32 nRsid = rTextNode.GetRsid( nStt2, nStt2 );
5184 
5185  return nThisRsid == nRsid;
5186 }
5187 
5188 // sw::Metadatable
5190 {
5191  return GetDoc().GetXmlIdRegistry();
5192 }
5193 
5195 {
5196  return GetDoc().IsClipBoard();
5197 }
5198 
5200 {
5202 }
5203 
5205 {
5206  return !GetDoc().IsInHeaderFooter( SwNodeIndex(*this) );
5207 }
5208 
5210 {
5211  const auto pOldValue = rHint.m_pOld;
5212  const auto pNewValue = rHint.m_pNew;
5213  bool bWasNotifiable = m_bNotifiable;
5214  m_bNotifiable = false;
5215 
5216  // Override Modify so that deleting styles works properly (outline
5217  // numbering!).
5218  // Never call ChgTextCollUpdateNum for Nodes in Undo.
5219  if( pOldValue
5220  && pNewValue
5221  && RES_FMT_CHG == pOldValue->Which()
5222  && GetRegisteredIn() == static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat
5223  && GetNodes().IsDocNodes() )
5224  {
5225  assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat));
5226  assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat));
5228  static_cast<const SwTextFormatColl*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat),
5229  static_cast<const SwTextFormatColl*>(static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat) );
5230  }
5231 
5232  // reset fill information
5233  if (maFillAttributes && pNewValue)
5234  {
5235  const sal_uInt16 nWhich = pNewValue->Which();
5236  bool bReset(RES_FMT_CHG == nWhich); // ..on format change (e.g. style changed)
5237 
5238  if(!bReset && RES_ATTRSET_CHG == nWhich) // ..on ItemChange from DrawingLayer FillAttributes
5239  {
5240  SfxItemIter aIter(*static_cast<const SwAttrSetChg*>(pNewValue)->GetChgSet());
5241 
5242  for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem && !bReset; pItem = aIter.NextItem())
5243  {
5244  bReset = !IsInvalidItem(pItem) && pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST;
5245  }
5246  }
5247 
5248  if(bReset)
5249  {
5250  maFillAttributes.reset();
5251  }
5252  }
5253 
5254  if ( !mbInSetOrResetAttr )
5255  {
5256  HandleModifyAtTextNode( *this, pOldValue, pNewValue );
5257  }
5258 
5259  SwContentNode::SwClientNotify(*this, rHint);
5260 
5261  SwDoc& rDoc = GetDoc();
5262  // #125329# - assure that text node is in document nodes array
5263  if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
5264  {
5265  rDoc.GetNodes().UpdateOutlineNode(*this);
5266  }
5267 
5268  m_bNotifiable = bWasNotifiable;
5269 
5270  if (pOldValue && (RES_REMOVE_UNO_OBJECT == pOldValue->Which()))
5271  { // invalidate cached uno object
5272  SetXParagraph(css::uno::Reference<css::text::XTextContent>(nullptr));
5273  }
5274 }
5275 
5276 void SwTextNode::SwClientNotify( const SwModify& rModify, const SfxHint& rHint )
5277 {
5278  if (rHint.GetId() == SfxHintId::SwLegacyModify)
5279  {
5280  auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
5281  TriggerNodeUpdate(*pLegacyHint);
5282  }
5283  else if (dynamic_cast<const SwAttrHint*>(&rHint))
5284  {
5285  if (&rModify == GetRegisteredIn())
5286  ChkCondColl();
5287  }
5288 }
5289 
5290 uno::Reference< rdf::XMetadatable >
5292 {
5293  const uno::Reference<rdf::XMetadatable> xMeta(
5294  SwXParagraph::CreateXParagraph(GetDoc(), this), uno::UNO_QUERY);
5295  return xMeta;
5296 }
5297 
5299 {
5300  // create SdrAllFillAttributesHelper on demand
5301  if(!maFillAttributes)
5302  {
5303  const_cast< SwTextNode* >(this)->maFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(GetSwAttrSet());
5304  }
5305 
5306  return maFillAttributes;
5307 }
5308 
5309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Recreate
Definition: txtfrm.hxx:128
void SetWordCountDirty(bool bNew) const
Definition: txtedt.cxx:2272
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:983
SvxNumType GetNumberingType() const
Base class of the Writer layout elements.
Definition: frame.hxx:315
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:158
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:1108
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:4070
sal_uLong GetIndex() const
Definition: node.hxx:291
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:3065
Represents the style of a paragraph.
Definition: fmtcol.hxx:56
void SetWrong(std::unique_ptr< SwWrongList > pNew)
Definition: txtedt.cxx:2187
Marks a position in the document model.
Definition: pam.hxx:35
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:645
const SwField * GetField() const
Definition: fmtfld.hxx:110
constexpr TypedWhichId< SvxEscapementItem > RES_CHRATR_ESCAPEMENT(6)
const SwNodeNum * GetNum(SwRootFrame const *pLayout=nullptr) const
Definition: ndtxt.cxx:3918
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:4470
The inserted item is a copy – intended for use in ndtxt.cxx.
bool isCHRATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:476
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_METAFIELD(49)
OUString MakeNumString(const SwNodeNum &, bool bInclStrings=true) const
Definition: number.cxx:628
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:3953
virtual sal_uInt16 ResetAllAttr() override
Definition: ndtxt.cxx:5098
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:4832
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:226
std::string GetValue
bool isTXTATR_WITHEND(const sal_uInt16 nWhich)
Definition: hintids.hxx:480
bool isTXTATR(const sal_uInt16 nWhich)
Definition: hintids.hxx:488
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:5209
const SwNumFormat * GetNumFormat(sal_uInt16 i) const
Definition: number.cxx:89
void DelFrames_TextNodePart()
Definition: ndtxt.cxx:1720
constexpr TypedWhichId< SvxFormatKeepItem > RES_KEEP(110)
SwNodeIndex nNode
Definition: pam.hxx:37
void AddToList()
Definition: ndtxt.cxx:4232
SwDoc & GetDoc()
Which Doc contains the nodes-array?
Definition: ndarr.hxx:299
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:3157
virtual const sal_Int32 * GetEnd() const
end position
Definition: txatbase.cxx:48
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:278
sal_uIntPtr sal_uLong
#define SAL_INFO_IF(condition, area, stream)
tools::Long GetLeftMarginForTabCalculation() const
return left margin for tab stop position calculation
Definition: ndtxt.cxx:3261
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:5167
const SwPosition * GetMark() const
Definition: pam.hxx:209
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:2731
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:1570
void ClearGrammarCheck()
Definition: txtedt.cxx:2221
SwContentFrame * getLayoutFrame(const SwRootFrame *, const SwPosition *pPos=nullptr, std::pair< Point, bool > const *pViewPosAndCalcFrame=nullptr) const
Definition: node.cxx:1213
sal_Int64 n
OUString GetListId() const
Definition: ndtxt.cxx:4339
static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
Definition: ndtxt.cxx:1609
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:1968
void CutText(SwTextNode *const pDest, const SwIndex &rStart, const sal_Int32 nLen)
Definition: ndtxt.cxx:2361
TElementType * Next()
Definition: calbck.hxx:365
const SfxPoolItem * m_pNew
Definition: calbck.hxx:78
static SwList * FindList(SwTextNode *const pNode)
Definition: ndtxt.cxx:4209
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:1765
SwSectionNode is derived from SwStartNode.
constexpr TypedWhichId< SwFormatFlyCnt > RES_TXTATR_FLYCNT(57)
std::unique_ptr< SwGrammarMarkUp > ReleaseGrammarCheck()
Definition: txtedt.cxx:2227
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:4436
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:119
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:4373
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:1691
OUString GetRedlineText() const
Definition: ndtxt.cxx:3566
bool HasNumber() const
Returns if this text node has a number.
Definition: ndtxt.cxx:3048
bool IsListRestart() const
Definition: ndtxt.cxx:4086
bool HasWriterListeners() const
Definition: calbck.hxx:203
void SetGrammarCheckDirty(bool bNew) const
Definition: txtedt.cxx:2303
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(59)
The root element of a Writer document layout.
Definition: rootfrm.hxx:82
const SwFormatRefMark & GetRefMark() const
Definition: txatbase.hxx:222
bool CanJoinNext(SwNodeIndex *pIdx=nullptr) const
Is it possible to join two nodes? In pIdx the second position can be returned.
Definition: node.cxx:1823
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:177
virtual SwContentN