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