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