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