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