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