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