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