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