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