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