LibreOffice Module sw (master)  1
DocumentContentOperationsManager.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  */
20 #include <doc.hxx>
21 #include <IDocumentUndoRedo.hxx>
22 #include <IDocumentMarkAccess.hxx>
23 #include <IDocumentState.hxx>
28 #include <UndoManager.hxx>
29 #include <docary.hxx>
30 #include <textboxhelper.hxx>
31 #include <dcontact.hxx>
32 #include <grfatr.hxx>
33 #include <numrule.hxx>
34 #include <charfmt.hxx>
35 #include <ndgrf.hxx>
36 #include <ndnotxt.hxx>
37 #include <ndole.hxx>
38 #include <breakit.hxx>
39 #include <frmfmt.hxx>
40 #include <fmtanchr.hxx>
41 #include <fmtcntnt.hxx>
42 #include <fmtinfmt.hxx>
43 #include <fmtpdsc.hxx>
44 #include <fmtcnct.hxx>
45 #include <SwStyleNameMapper.hxx>
46 #include <redline.hxx>
47 #include <txtfrm.hxx>
48 #include <rootfrm.hxx>
49 #include <frmtool.hxx>
50 #include <unocrsr.hxx>
51 #include <mvsave.hxx>
52 #include <ndtxt.hxx>
53 #include <poolfmt.hxx>
54 #include <paratr.hxx>
55 #include <txatbase.hxx>
56 #include <UndoRedline.hxx>
57 #include <undobj.hxx>
58 #include <UndoBookmark.hxx>
59 #include <UndoDelete.hxx>
60 #include <UndoSplitMove.hxx>
61 #include <UndoOverwrite.hxx>
62 #include <UndoInsert.hxx>
63 #include <UndoAttribute.hxx>
64 #include <rolbck.hxx>
65 #include <acorrect.hxx>
66 #include <bookmrk.hxx>
67 #include <ftnidx.hxx>
68 #include <txtftn.hxx>
69 #include <hints.hxx>
70 #include <fmtflcnt.hxx>
71 #include <docedt.hxx>
72 #include <frameformats.hxx>
73 #include <o3tl/safeint.hxx>
74 #include <sal/log.hxx>
75 #include <unotools/charclass.hxx>
76 #include <unotools/configmgr.hxx>
77 #include <sfx2/Metadatable.hxx>
78 #include <sot/exchange.hxx>
79 #include <svl/stritem.hxx>
80 #include <svl/itemiter.hxx>
81 #include <svx/svdobj.hxx>
82 #include <svx/svdouno.hxx>
83 #include <tools/globname.hxx>
85 #include <com/sun/star/i18n/Boundary.hpp>
86 #include <com/sun/star/i18n/WordType.hpp>
87 #include <com/sun/star/i18n/XBreakIterator.hpp>
88 #include <com/sun/star/embed/XEmbeddedObject.hpp>
89 
90 #include <tuple>
91 #include <memory>
92 
93 using namespace ::com::sun::star::i18n;
94 
95 namespace
96 {
97  // Copy method from SwDoc
98  // Prevent copying into Flys that are anchored in the range
99  bool lcl_ChkFlyFly( SwDoc& rDoc, sal_uLong nSttNd, sal_uLong nEndNd,
100  sal_uLong nInsNd )
101  {
102  const SwFrameFormats& rFrameFormatTable = *rDoc.GetSpzFrameFormats();
103 
104  for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
105  {
106  SwFrameFormat const*const pFormat = rFrameFormatTable[n];
107  SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
108  SwPosition const*const pAPos = pAnchor->GetContentAnchor();
109  if (pAPos &&
110  ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
111  (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
112  (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
113  (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
114  nSttNd <= pAPos->nNode.GetIndex() &&
115  pAPos->nNode.GetIndex() < nEndNd )
116  {
117  const SwFormatContent& rContent = pFormat->GetContent();
118  SwStartNode* pSNd;
119  if( !rContent.GetContentIdx() ||
120  nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
121  continue;
122 
123  if( pSNd->GetIndex() < nInsNd &&
124  nInsNd < pSNd->EndOfSectionIndex() )
125  // Do not copy !
126  return true;
127 
128  if( lcl_ChkFlyFly( rDoc, pSNd->GetIndex(),
129  pSNd->EndOfSectionIndex(), nInsNd ) )
130  // Do not copy !
131  return true;
132  }
133  }
134 
135  return false;
136  }
137 
138  SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, sal_uLong & rDelCount)
139  {
140  SwNodeIndex const& rStart(rSourcePaM.Start()->nNode);
141  // Special handling for SwDoc::AppendDoc
142  if (rSourcePaM.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
143  == rStart.GetIndex())
144  {
145  rDelCount = 1;
146  return SwNodeIndex(rStart, +1);
147  }
148  else
149  {
150  rDelCount = 0;
151  return rStart;
152  }
153  }
154 
155  /*
156  The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
157  array. It is called after a call of the CopyNodes(..) function. But this function does not copy
158  every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
159  if the corresponding end/start node is outside the copied pam.
160  The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
161  index inside the pam.
162  rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
163  of "non-copy" nodes between rPam.Start() and rLastIdx.
164  nNewIdx is the new position of interest.
165  */
166  void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const sal_uLong nNewIdx, sal_uLong& rDelCount )
167  {
168  sal_uLong nStart = rPam.Start()->nNode.GetIndex();
169  sal_uLong nEnd = rPam.End()->nNode.GetIndex();
170  if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
171  {
172  // We never copy the StartOfContent node
173  do // count "non-copy" nodes
174  {
175  SwNode& rNode = rLastIdx.GetNode();
176  if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
177  || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
178  {
179  ++rDelCount;
180  }
181  ++rLastIdx;
182  }
183  while( rLastIdx.GetIndex() < nNewIdx );
184  }
185  else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
186  // no move backward needed
187  {
188  while( rLastIdx.GetIndex() > nNewIdx )
189  {
190  SwNode& rNode = rLastIdx.GetNode();
191  if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
192  || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
193  {
194  --rDelCount;
195  }
196  rLastIdx--;
197  }
198  }
199  }
200 
201  void lcl_SetCpyPos( const SwPosition& rOrigPos,
202  const SwPosition& rOrigStt,
203  const SwPosition& rCpyStt,
204  SwPosition& rChgPos,
205  sal_uLong nDelCount )
206  {
207  sal_uLong nNdOff = rOrigPos.nNode.GetIndex();
208  nNdOff -= rOrigStt.nNode.GetIndex();
209  nNdOff -= nDelCount;
210  sal_Int32 nContentPos = rOrigPos.nContent.GetIndex();
211 
212  // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
213  rChgPos.nNode = nNdOff + rCpyStt.nNode.GetIndex();
214  if( !nNdOff )
215  {
216  // just adapt the content index
217  if( nContentPos > rOrigStt.nContent.GetIndex() )
218  nContentPos -= rOrigStt.nContent.GetIndex();
219  else
220  nContentPos = 0;
221  nContentPos += rCpyStt.nContent.GetIndex();
222  }
223  rChgPos.nContent.Assign( rChgPos.nNode.GetNode().GetContentNode(), nContentPos );
224  }
225 
226 }
227 
228 namespace sw
229 {
230  // TODO: use SaveBookmark (from DelBookmarks)
231  void CopyBookmarks(const SwPaM& rPam, SwPosition& rCpyPam)
232  {
233  const SwDoc& rSrcDoc = rPam.GetDoc();
234  SwDoc& rDestDoc = rCpyPam.GetDoc();
235  const IDocumentMarkAccess* const pSrcMarkAccess = rSrcDoc.getIDocumentMarkAccess();
236  ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
237 
238  const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
239  SwPosition const*const pCpyStt = &rCpyPam;
240 
241  std::vector< const ::sw::mark::IMark* > vMarksToCopy;
242  for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
243  ppMark != pSrcMarkAccess->getAllMarksEnd();
244  ++ppMark )
245  {
246  const ::sw::mark::IMark* const pMark = *ppMark;
247 
248  const SwPosition& rMarkStart = pMark->GetMarkStart();
249  const SwPosition& rMarkEnd = pMark->GetMarkEnd();
250  // only include marks that are in the range and not touching both start and end
251  // - not for annotation or checkbox marks.
252  const bool bIsNotOnBoundary =
253  pMark->IsExpanded()
254  ? (rMarkStart != rStt || rMarkEnd != rEnd) // rMarkStart != rMarkEnd
255  : (rMarkStart != rStt && rMarkEnd != rEnd); // rMarkStart == rMarkEnd
257  if ( rMarkStart >= rStt && rMarkEnd <= rEnd
258  && ( bIsNotOnBoundary
264  {
265  vMarksToCopy.push_back(pMark);
266  }
267  }
268  // We have to count the "non-copied" nodes...
269  sal_uLong nDelCount;
270  SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
271  for(const sw::mark::IMark* const pMark : vMarksToCopy)
272  {
273  SwPaM aTmpPam(*pCpyStt);
274  lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().nNode.GetIndex(), nDelCount);
275  lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
276  if(pMark->IsExpanded())
277  {
278  aTmpPam.SetMark();
279  lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().nNode.GetIndex(), nDelCount);
280  lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
281  }
282 
283  ::sw::mark::IMark* const pNewMark = rDestDoc.getIDocumentMarkAccess()->makeMark(
284  aTmpPam,
285  pMark->GetName(),
288  // Explicitly try to get exactly the same name as in the source
289  // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
290  rDestDoc.getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName());
291 
292  // copying additional attributes for bookmarks or fieldmarks
293  ::sw::mark::IBookmark* const pNewBookmark =
294  dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
295  const ::sw::mark::IBookmark* const pOldBookmark =
296  dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
297  if (pNewBookmark && pOldBookmark)
298  {
299  pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
300  pNewBookmark->SetShortName(pOldBookmark->GetShortName());
301  pNewBookmark->Hide(pOldBookmark->IsHidden());
302  pNewBookmark->SetHideCondition(pOldBookmark->GetHideCondition());
303  }
304  ::sw::mark::IFieldmark* const pNewFieldmark =
305  dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
306  const ::sw::mark::IFieldmark* const pOldFieldmark =
307  dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
308  if (pNewFieldmark && pOldFieldmark)
309  {
310  pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
311  pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
312  ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
313  const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
314  for (const auto& rEntry : *pOldParams )
315  {
316  pNewParams->insert( rEntry );
317  }
318  }
319 
320  ::sfx2::Metadatable const*const pMetadatable(
321  dynamic_cast< ::sfx2::Metadatable const* >(pMark));
322  ::sfx2::Metadatable *const pNewMetadatable(
323  dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
324  if (pMetadatable && pNewMetadatable)
325  {
326  pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
327  }
328  }
329  }
330 } // namespace sw
331 
332 namespace
333 {
334  void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
335  {
336  const SwDoc& rSrcDoc = rPam.GetDoc();
337  const SwRedlineTable& rTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
338  if( rTable.empty() )
339  return;
340 
341  SwDoc& rDestDoc = rCpyPam.GetDoc();
342  SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
343  std::unique_ptr<SwPaM> pDelPam;
344  const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
345  // We have to count the "non-copied" nodes
346  sal_uLong nDelCount;
347  SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
348 
350  rSrcDoc.getIDocumentRedlineAccess().GetRedline( *pStt, &n );
351  for( ; n < rTable.size(); ++n )
352  {
353  const SwRangeRedline* pRedl = rTable[ n ];
354  if( RedlineType::Delete == pRedl->GetType() && pRedl->IsVisible() )
355  {
356  const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
357 
358  SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
359  switch( eCmpPos )
360  {
363  // Pos1 is before Pos2
364  break;
365 
368  // Pos1 is after Pos2
369  n = rTable.size();
370  break;
371 
372  default:
373  {
374  pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
375  if( *pStt < *pRStt )
376  {
377  lcl_NonCopyCount( rPam, aCorrIdx, pRStt->nNode.GetIndex(), nDelCount );
378  lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
379  *pDelPam->GetPoint(), nDelCount );
380  }
381  pDelPam->SetMark();
382 
383  if( *pEnd < *pREnd )
384  *pDelPam->GetPoint() = *pCpyEnd;
385  else
386  {
387  lcl_NonCopyCount( rPam, aCorrIdx, pREnd->nNode.GetIndex(), nDelCount );
388  lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
389  *pDelPam->GetPoint(), nDelCount );
390  }
391 
392  if (pDelPam->GetNext() && *pDelPam->GetNext()->End() == *pDelPam->Start())
393  {
394  *pDelPam->GetNext()->End() = *pDelPam->End();
395  pDelPam.reset(pDelPam->GetNext());
396  }
397  }
398  }
399  }
400  }
401 
402  if( !pDelPam )
403  return;
404 
407 
408  ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
409 
410  do {
411  rDestDoc.getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() );
412  if( !pDelPam->IsMultiSelection() )
413  break;
414  delete pDelPam->GetNext();
415  } while( true );
416 
418  }
419 
420  void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
421  {
422  SwDoc& rSrcDoc = rRg.aStart.GetNode().GetDoc();
423  if( !rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
424  {
425  SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
426  SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
427  lcl_DeleteRedlines( aRgTmp, aCpyTmp );
428  }
429  }
430 
431  void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
432  {
433  SwFormatChain aSrc( pSrc->GetChain() );
434  if ( !aSrc.GetNext() )
435  {
436  aSrc.SetNext( pDest );
437  pSrc->SetFormatAttr( aSrc );
438  }
439  SwFormatChain aDest( pDest->GetChain() );
440  if ( !aDest.GetPrev() )
441  {
442  aDest.SetPrev( pSrc );
443  pDest->SetFormatAttr( aDest );
444  }
445  }
446 
447  // #i86492#
448  bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
449  {
450  bool bRet = false;
451 
452  const SwTextNode* pTextNd = rPam.Start()->nNode.GetNode().GetTextNode();
453  const SwTextNode* pEndTextNd = rPam.End()->nNode.GetNode().GetTextNode();
454  if ( pTextNd && pTextNd->IsInList() &&
455  pEndTextNd && pEndTextNd->IsInList() )
456  {
457  bRet = true;
458  SwNodeIndex aIdx(rPam.Start()->nNode);
459 
460  do
461  {
462  ++aIdx;
463  pTextNd = aIdx.GetNode().GetTextNode();
464 
465  if ( !pTextNd || !pTextNd->IsInList() )
466  {
467  bRet = false;
468  break;
469  }
470  } while (pTextNd != pEndTextNd);
471  }
472 
473  return bRet;
474  }
475 
476  bool lcl_MarksWholeNode(const SwPaM & rPam)
477  {
478  bool bResult = false;
479  const SwPosition* pStt = rPam.Start();
480  const SwPosition* pEnd = rPam.End();
481 
482  if (nullptr != pStt && nullptr != pEnd)
483  {
484  const SwTextNode* pSttNd = pStt->nNode.GetNode().GetTextNode();
485  const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
486 
487  if (nullptr != pSttNd && nullptr != pEndNd &&
488  pStt->nContent.GetIndex() == 0 &&
489  pEnd->nContent.GetIndex() == pEndNd->Len())
490  {
491  bResult = true;
492  }
493  }
494 
495  return bResult;
496  }
497 }
498 
499 //local functions originally from sw/source/core/doc/docedt.cxx
500 namespace sw
501 {
502  void CalcBreaks(std::vector<std::pair<sal_uLong, sal_Int32>> & rBreaks,
503  SwPaM const & rPam, bool const isOnlyFieldmarks)
504  {
505  sal_uLong const nStartNode(rPam.Start()->nNode.GetIndex());
506  sal_uLong const nEndNode(rPam.End()->nNode.GetIndex());
507  SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
508  IDocumentMarkAccess const& rIDMA(*rPam.GetDoc().getIDocumentMarkAccess());
509 
510  std::stack<std::tuple<sw::mark::IFieldmark const*, bool, sal_uLong, sal_Int32>> startedFields;
511 
512  for (sal_uLong n = nStartNode; n <= nEndNode; ++n)
513  {
514  SwNode *const pNode(rNodes[n]);
515  if (pNode->IsTextNode())
516  {
517  SwTextNode & rTextNode(*pNode->GetTextNode());
518  sal_Int32 const nStart(n == nStartNode
519  ? rPam.Start()->nContent.GetIndex()
520  : 0);
521  sal_Int32 const nEnd(n == nEndNode
522  ? rPam.End()->nContent.GetIndex()
523  : rTextNode.Len());
524  for (sal_Int32 i = nStart; i < nEnd; ++i)
525  {
526  const sal_Unicode c(rTextNode.GetText()[i]);
527  switch (c)
528  {
529  // note: CH_TXT_ATR_FORMELEMENT does not need handling
530  // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
531  case CH_TXTATR_INWORD:
532  case CH_TXTATR_BREAKWORD:
533  {
534  // META hints only have dummy char at the start, not
535  // at the end, so no need to check in nStartNode
536  if (n == nEndNode && !isOnlyFieldmarks)
537  {
538  SwTextAttr const*const pAttr(rTextNode.GetTextAttrForCharAt(i));
539  if (pAttr && pAttr->End() && (nEnd < *pAttr->End()))
540  {
541  assert(pAttr->HasDummyChar());
542  rBreaks.emplace_back(n, i);
543  }
544  }
545  break;
546  }
548  {
549  auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
550  startedFields.emplace(pFieldMark, false, 0, 0);
551  break;
552  }
553  case CH_TXT_ATR_FIELDSEP:
554  {
555  if (startedFields.empty())
556  {
557  rBreaks.emplace_back(n, i);
558  }
559  else
560  { // no way to find the field via MarkManager...
561  assert(std::get<0>(startedFields.top())->IsCoveringPosition(SwPosition(rTextNode, i)));
562  std::get<1>(startedFields.top()) = true;
563  std::get<2>(startedFields.top()) = n;
564  std::get<3>(startedFields.top()) = i;
565  }
566  break;
567  }
568  case CH_TXT_ATR_FIELDEND:
569  {
570  if (startedFields.empty())
571  {
572  rBreaks.emplace_back(n, i);
573  }
574  else
575  { // fieldmarks must not overlap => stack
576  assert(std::get<0>(startedFields.top()) == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
577  startedFields.pop();
578  }
579  break;
580  }
581  }
582  }
583  }
584  else if (pNode->IsStartNode())
585  {
586  if (pNode->EndOfSectionIndex() <= nEndNode)
587  { // fieldmark cannot overlap node section
588  n = pNode->EndOfSectionIndex();
589  }
590  }
591  else
592  { // EndNode can actually happen with sections :(
593  assert(pNode->IsEndNode() || pNode->IsNoTextNode());
594  }
595  }
596  while (!startedFields.empty())
597  {
598  SwPosition const& rStart(std::get<0>(startedFields.top())->GetMarkStart());
599  std::pair<sal_uLong, sal_Int32> const pos(
600  rStart.nNode.GetIndex(), rStart.nContent.GetIndex());
601  auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos);
602  assert(it == rBreaks.end() || *it != pos);
603  rBreaks.insert(it, pos);
604  if (std::get<1>(startedFields.top()))
605  {
606  std::pair<sal_uLong, sal_Int32> const posSep(
607  std::get<2>(startedFields.top()),
608  std::get<3>(startedFields.top()));
609  it = std::lower_bound(rBreaks.begin(), rBreaks.end(), posSep);
610  assert(it == rBreaks.end() || *it != posSep);
611  rBreaks.insert(it, posSep);
612  }
613  startedFields.pop();
614  }
615  }
616 }
617 
618 namespace
619 {
620 
621  bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam,
622  bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
623  {
624  std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
625 
626  sw::CalcBreaks(Breaks, rPam);
627 
628  if (Breaks.empty())
629  {
630  return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext);
631  }
632 
633  // Deletion must be split into several parts if the text node
634  // contains a text attribute with end and with dummy character
635  // and the selection does not contain the text attribute completely,
636  // but overlaps its start (left), where the dummy character is.
637 
638  SwPosition const & rSelectionEnd( *rPam.End() );
639 
640  bool bRet( true );
641  // iterate from end to start, to avoid invalidating the offsets!
642  auto iter( Breaks.rbegin() );
643  sal_uLong nOffset(0);
644  SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
645  SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
646  SwPosition & rEnd( *aPam.End() );
647  SwPosition & rStart( *aPam.Start() );
648 
649  while (iter != Breaks.rend())
650  {
651  rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
652  if (rStart < rEnd) // check if part is empty
653  {
654  bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
655  nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
656  }
657  rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
658  ++iter;
659  }
660 
661  rStart = *rPam.Start(); // set to original start
662  if (rStart < rEnd) // check if part is empty
663  {
664  bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
665  }
666 
667  return bRet;
668  }
669 
670  bool lcl_StrLenOverflow( const SwPaM& rPam )
671  {
672  // If we try to merge two paragraphs we have to test if afterwards
673  // the string doesn't exceed the allowed string length
674  if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
675  {
676  const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
677  const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
678  if( (nullptr != pEndNd) && pStt->nNode.GetNode().IsTextNode() )
679  {
680  const sal_uInt64 nSum = pStt->nContent.GetIndex() +
681  pEndNd->GetText().getLength() - pEnd->nContent.GetIndex();
682  return nSum > o3tl::make_unsigned(SAL_MAX_INT32);
683  }
684  }
685  return false;
686  }
687 
688  struct SaveRedline
689  {
690  SwRangeRedline* pRedl;
691  sal_uInt32 nStt, nEnd;
692  sal_Int32 nSttCnt;
693  sal_Int32 nEndCnt;
694 
695  SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
696  : pRedl(pR)
697  , nEnd(0)
698  , nEndCnt(0)
699  {
700  const SwPosition* pStt = pR->Start(),
701  * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
702  sal_uInt32 nSttIdx = rSttIdx.GetIndex();
703  nStt = pStt->nNode.GetIndex() - nSttIdx;
704  nSttCnt = pStt->nContent.GetIndex();
705  if( pR->HasMark() )
706  {
707  nEnd = pEnd->nNode.GetIndex() - nSttIdx;
708  nEndCnt = pEnd->nContent.GetIndex();
709  }
710 
711  pRedl->GetPoint()->nNode = 0;
712  pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
713  pRedl->GetMark()->nNode = 0;
714  pRedl->GetMark()->nContent.Assign( nullptr, 0 );
715  }
716 
717  SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
718  : pRedl(pR)
719  , nEnd(0)
720  , nEndCnt(0)
721  {
722  const SwPosition* pStt = pR->Start(),
723  * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
724  sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
725  nStt = pStt->nNode.GetIndex() - nSttIdx;
726  nSttCnt = pStt->nContent.GetIndex();
727  if( nStt == 0 )
728  nSttCnt = nSttCnt - rPos.nContent.GetIndex();
729  if( pR->HasMark() )
730  {
731  nEnd = pEnd->nNode.GetIndex() - nSttIdx;
732  nEndCnt = pEnd->nContent.GetIndex();
733  if( nEnd == 0 )
734  nEndCnt = nEndCnt - rPos.nContent.GetIndex();
735  }
736 
737  pRedl->GetPoint()->nNode = 0;
738  pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
739  pRedl->GetMark()->nNode = 0;
740  pRedl->GetMark()->nContent.Assign( nullptr, 0 );
741  }
742 
743  void SetPos( sal_uInt32 nInsPos )
744  {
745  pRedl->GetPoint()->nNode = nInsPos + nStt;
746  pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt );
747  if( pRedl->HasMark() )
748  {
749  pRedl->GetMark()->nNode = nInsPos + nEnd;
750  pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt );
751  }
752  }
753 
754  void SetPos( const SwPosition& aPos )
755  {
756  pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
757  pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
758  if( pRedl->HasMark() )
759  {
760  pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
761  pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
762  }
763  }
764  };
765 
766  typedef std::vector< SaveRedline > SaveRedlines_t;
767 
768  void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
769  {
770  SwDoc& rDoc = aPam.GetNode().GetDoc();
771 
772  const SwPosition* pStart = aPam.Start();
773  const SwPosition* pEnd = aPam.End();
774 
775  // get first relevant redline
776  SwRedlineTable::size_type nCurrentRedline;
777  rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
778  if( nCurrentRedline > 0)
779  nCurrentRedline--;
780 
781  // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
784 
785  // iterate over relevant redlines and decide for each whether it should
786  // be saved, or split + saved
787  SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
788  for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
789  {
790  SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
791  SwComparePosition eCompare =
792  ComparePosition( *pCurrent->Start(), *pCurrent->End(),
793  *pStart, *pEnd);
794 
795  // we must save this redline if it overlaps aPam
796  // (we may have to split it, too)
797  if( eCompare == SwComparePosition::OverlapBehind ||
798  eCompare == SwComparePosition::OverlapBefore ||
799  eCompare == SwComparePosition::Outside ||
800  eCompare == SwComparePosition::Inside ||
801  eCompare == SwComparePosition::Equal )
802  {
803  rRedlineTable.Remove( nCurrentRedline-- );
804 
805  // split beginning, if necessary
806  if( eCompare == SwComparePosition::OverlapBefore ||
807  eCompare == SwComparePosition::Outside )
808  {
809  SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
810  *pNewRedline->End() = *pStart;
811  *pCurrent->Start() = *pStart;
812  rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
813  }
814 
815  // split end, if necessary
816  if( eCompare == SwComparePosition::OverlapBehind ||
817  eCompare == SwComparePosition::Outside )
818  {
819  SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
820  *pNewRedline->Start() = *pEnd;
821  *pCurrent->End() = *pEnd;
822  rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
823  }
824 
825  // save the current redline
826  rArr.emplace_back( pCurrent, *pStart );
827  }
828  }
829 
830  // restore old redline mode
832  }
833 
834  void lcl_RestoreRedlines(SwDoc& rDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
835  {
838 
839  for(SaveRedline & rSvRedLine : rArr)
840  {
841  rSvRedLine.SetPos( rPos );
842  rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
843  }
844 
846  }
847 
848  void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
849  {
850  SwDoc& rDoc = rRg.aStart.GetNode().GetDoc();
851  SwRedlineTable::size_type nRedlPos;
852  SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
853  aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 );
854  if( rDoc.getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
855  --nRedlPos;
856  else if( nRedlPos >= rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
857  return ;
858 
862 
863  do {
864  SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
865 
866  const SwPosition* pRStt = pTmp->Start(),
867  * pREnd = pTmp->GetMark() == pRStt
868  ? pTmp->GetPoint() : pTmp->GetMark();
869 
870  if( pRStt->nNode < rRg.aStart )
871  {
872  if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
873  {
874  // Create a copy and set the end of the original to the end of the MoveArea.
875  // The copy is moved too.
876  SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
877  SwPosition* pTmpPos = pNewRedl->Start();
878  pTmpPos->nNode = rRg.aStart;
879  pTmpPos->nContent.Assign(
880  pTmpPos->nNode.GetNode().GetContentNode(), 0 );
881 
882  rArr.emplace_back(pNewRedl, rRg.aStart);
883 
884  pTmpPos = pTmp->End();
885  pTmpPos->nNode = rRg.aEnd;
886  pTmpPos->nContent.Assign(
887  pTmpPos->nNode.GetNode().GetContentNode(), 0 );
888  }
889  else if( pREnd->nNode == rRg.aStart )
890  {
891  SwPosition* pTmpPos = pTmp->End();
892  pTmpPos->nNode = rRg.aEnd;
893  pTmpPos->nContent.Assign(
894  pTmpPos->nNode.GetNode().GetContentNode(), 0 );
895  }
896  }
897  else if( pRStt->nNode < rRg.aEnd )
898  {
899  rRedlTable.Remove( nRedlPos-- );
900  if( pREnd->nNode < rRg.aEnd ||
901  ( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
902  {
903  // move everything
904  rArr.emplace_back( pTmp, rRg.aStart );
905  }
906  else
907  {
908  // split
909  SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
910  SwPosition* pTmpPos = pNewRedl->End();
911  pTmpPos->nNode = rRg.aEnd;
912  pTmpPos->nContent.Assign(
913  pTmpPos->nNode.GetNode().GetContentNode(), 0 );
914 
915  rArr.emplace_back( pNewRedl, rRg.aStart );
916 
917  pTmpPos = pTmp->Start();
918  pTmpPos->nNode = rRg.aEnd;
919  pTmpPos->nContent.Assign(
920  pTmpPos->nNode.GetNode().GetContentNode(), 0 );
921  rDoc.getIDocumentRedlineAccess().AppendRedline( pTmp, true );
922  }
923  }
924  else
925  break;
926 
927  } while( ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() );
929  }
930 
931  void lcl_RestoreRedlines(SwDoc& rDoc, sal_uInt32 const nInsPos, SaveRedlines_t& rArr)
932  {
935 
936  for(SaveRedline & rSvRedLine : rArr)
937  {
938  rSvRedLine.SetPos( nInsPos );
939  rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
940  if (rSvRedLine.pRedl->GetType() == RedlineType::Delete)
941  {
942  UpdateFramesForAddDeleteRedline(rDoc, *rSvRedLine.pRedl);
943  }
944  }
945 
947  }
948 
949  bool lcl_SaveFootnote( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
950  const SwNodeIndex& rInsPos,
951  SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
952  const SwIndex* pSttCnt = nullptr, const SwIndex* pEndCnt = nullptr )
953  {
954  bool bUpdateFootnote = false;
955  const SwNodes& rNds = rInsPos.GetNodes();
956  const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
957  rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
958  const bool bSaveFootnote = !bDelFootnote &&
959  rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
960  if( !rFootnoteArr.empty() )
961  {
962 
963  size_t nPos = 0;
964  rFootnoteArr.SeekEntry( rSttNd, &nPos );
965  SwTextFootnote* pSrch;
966  const SwNode* pFootnoteNd;
967 
968  // Delete/save all that come after it
969  while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
970  &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
971  <= rEndNd.GetIndex() )
972  {
973  const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
974  if( ( pEndCnt && pSttCnt )
975  ? (( &rSttNd.GetNode() == pFootnoteNd &&
976  pSttCnt->GetIndex() > nFootnoteSttIdx) ||
977  ( &rEndNd.GetNode() == pFootnoteNd &&
978  nFootnoteSttIdx >= pEndCnt->GetIndex() ))
979  : ( &rEndNd.GetNode() == pFootnoteNd ))
980  {
981  ++nPos; // continue searching
982  }
983  else
984  {
985  // delete it
986  if( bDelFootnote )
987  {
988  SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
989  SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
990  rTextNd.EraseText( aIdx, 1 );
991  }
992  else
993  {
994  pSrch->DelFrames(nullptr);
995  rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
996  if( bSaveFootnote )
997  rSaveArr.insert( pSrch );
998  }
999  bUpdateFootnote = true;
1000  }
1001  }
1002 
1003  while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
1004  GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
1005  {
1006  const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
1007  if( !pEndCnt || !pSttCnt ||
1008  ! (( &rSttNd.GetNode() == pFootnoteNd &&
1009  pSttCnt->GetIndex() > nFootnoteSttIdx ) ||
1010  ( &rEndNd.GetNode() == pFootnoteNd &&
1011  nFootnoteSttIdx >= pEndCnt->GetIndex() )) )
1012  {
1013  if( bDelFootnote )
1014  {
1015  // delete it
1016  SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
1017  SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
1018  rTextNd.EraseText( aIdx, 1 );
1019  }
1020  else
1021  {
1022  pSrch->DelFrames(nullptr);
1023  rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
1024  if( bSaveFootnote )
1025  rSaveArr.insert( pSrch );
1026  }
1027  bUpdateFootnote = true;
1028  }
1029  }
1030  }
1031  // When moving from redline section into document content section, e.g.
1032  // after loading a document with (delete-)redlines, the footnote array
1033  // has to be adjusted... (#i70572)
1034  if( bSaveFootnote )
1035  {
1036  SwNodeIndex aIdx( rSttNd );
1037  while( aIdx < rEndNd ) // Check the moved section
1038  {
1039  SwNode* pNode = &aIdx.GetNode();
1040  if( pNode->IsTextNode() ) // Looking for text nodes...
1041  {
1042  SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
1043  if( pHints && pHints->HasFootnote() ) //...with footnotes
1044  {
1045  bUpdateFootnote = true; // Heureka
1046  const size_t nCount = pHints->Count();
1047  for( size_t i = 0; i < nCount; ++i )
1048  {
1049  SwTextAttr *pAttr = pHints->Get( i );
1050  if ( pAttr->Which() == RES_TXTATR_FTN )
1051  {
1052  rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
1053  }
1054  }
1055  }
1056  }
1057  ++aIdx;
1058  }
1059  }
1060  return bUpdateFootnote;
1061  }
1062 
1063  bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
1064  {
1065  sal_Unicode const cChr = pNode->GetText()[nPos];
1066  switch (cChr)
1067  {
1068  case CH_TXTATR_BREAKWORD:
1069  case CH_TXTATR_INWORD:
1070  return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
1073  case CH_TXT_ATR_FIELDSTART:
1074  case CH_TXT_ATR_FIELDSEP:
1075  case CH_TXT_ATR_FIELDEND:
1077  return false;
1078  default:
1079  return true;
1080  }
1081  }
1082 
1083  void lcl_SkipAttr( const SwTextNode *pNode, SwIndex &rIdx, sal_Int32 &rStart )
1084  {
1085  if( !lcl_MayOverwrite( pNode, rStart ) )
1086  {
1087  // skip all special attributes
1088  do {
1089  ++rIdx;
1090  rStart = rIdx.GetIndex();
1091  } while (rStart < pNode->GetText().getLength()
1092  && !lcl_MayOverwrite(pNode, rStart) );
1093  }
1094  }
1095 
1096  bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
1097  {
1098  if( bRegExpRplc )
1099  {
1100  sal_Int32 nPos = 0;
1101  const OUString sPara("\\n");
1102  for (;;)
1103  {
1104  nPos = rStr.indexOf( sPara, nPos );
1105  if (nPos<0)
1106  {
1107  break;
1108  }
1109  // Has this been escaped?
1110  if( nPos && '\\' == rStr[nPos-1])
1111  {
1112  ++nPos;
1113  if( nPos >= rStr.getLength() )
1114  {
1115  break;
1116  }
1117  }
1118  else
1119  {
1120  rRet = rStr.copy( 0, nPos );
1121  rStr = rStr.copy( nPos + sPara.getLength() );
1122  return true;
1123  }
1124  }
1125  }
1126  rRet = rStr;
1127  rStr.clear();
1128  return false;
1129  }
1130 }
1131 
1132 namespace //local functions originally from docfmt.cxx
1133 {
1134 
1135  bool lcl_ApplyOtherSet(
1136  SwContentNode & rNode,
1137  SwHistory *const pHistory,
1138  SfxItemSet const& rOtherSet,
1139  SfxItemSet const& rFirstSet,
1140  SfxItemSet const& rPropsSet,
1141  SwRootFrame const*const pLayout,
1142  SwNodeIndex *const o_pIndex = nullptr)
1143  {
1144  assert(rOtherSet.Count());
1145 
1146  bool ret(false);
1147  SwTextNode *const pTNd = rNode.GetTextNode();
1148  sw::MergedPara const* pMerged(nullptr);
1149  if (pLayout && pLayout->HasMergedParas() && pTNd)
1150  {
1151  SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
1152  pTNd->getLayoutFrame(pLayout)));
1153  if (pTextFrame)
1154  {
1155  pMerged = pTextFrame->GetMergedPara();
1156  }
1157  if (pMerged)
1158  {
1159  if (rFirstSet.Count())
1160  {
1161  if (pHistory)
1162  {
1163  SwRegHistory aRegH(pMerged->pFirstNode, *pMerged->pFirstNode, pHistory);
1164  ret = pMerged->pFirstNode->SetAttr(rFirstSet);
1165  }
1166  else
1167  {
1168  ret = pMerged->pFirstNode->SetAttr(rFirstSet);
1169  }
1170  }
1171  if (rPropsSet.Count())
1172  {
1173  if (pHistory)
1174  {
1175  SwRegHistory aRegH(pMerged->pParaPropsNode, *pMerged->pParaPropsNode, pHistory);
1176  ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
1177  }
1178  else
1179  {
1180  ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
1181  }
1182  }
1183  if (o_pIndex)
1184  {
1185  *o_pIndex = *pMerged->pLastNode; // skip hidden
1186  }
1187  }
1188  }
1189 
1190  // input cursor can't be on hidden node, and iteration skips them
1191  assert(!pLayout || !pLayout->HasMergedParas()
1193 
1194  if (!pMerged)
1195  {
1196  if (pHistory)
1197  {
1198  SwRegHistory aRegH(&rNode, rNode, pHistory);
1199  ret = rNode.SetAttr( rOtherSet );
1200  }
1201  else
1202  {
1203  ret = rNode.SetAttr( rOtherSet );
1204  }
1205  }
1206  return ret;
1207  }
1208 
1209  #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1210 
1212  // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1213 
1214  bool lcl_InsAttr(
1215  SwDoc& rDoc,
1216  const SwPaM &rRg,
1217  const SfxItemSet& rChgSet,
1218  const SetAttrMode nFlags,
1219  SwUndoAttr *const pUndo,
1220  SwRootFrame const*const pLayout,
1221  const bool bExpandCharToPara,
1222  SwTextAttr **ppNewTextAttr)
1223  {
1224  // Divide the Sets (for selections in Nodes)
1225  const SfxItemSet* pCharSet = nullptr;
1226  const SfxItemSet* pOtherSet = nullptr;
1227  bool bDelete = false;
1228  bool bCharAttr = false;
1229  bool bOtherAttr = false;
1230 
1231  // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1232  if ( 1 == rChgSet.Count() )
1233  {
1234  SfxItemIter aIter( rChgSet );
1235  const SfxPoolItem* pItem = aIter.GetCurItem();
1236  if (pItem && !IsInvalidItem(pItem))
1237  {
1238  const sal_uInt16 nWhich = pItem->Which();
1239 
1240  if ( isCHRATR(nWhich) ||
1241  (RES_TXTATR_CHARFMT == nWhich) ||
1242  (RES_TXTATR_INETFMT == nWhich) ||
1243  (RES_TXTATR_AUTOFMT == nWhich) ||
1244  (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
1245  {
1246  pCharSet = &rChgSet;
1247  bCharAttr = true;
1248  }
1249 
1250  if ( isPARATR(nWhich)
1251  || isPARATR_LIST(nWhich)
1252  || isFRMATR(nWhich)
1253  || isGRFATR(nWhich)
1254  || isUNKNOWNATR(nWhich)
1255  || isDrawingLayerAttribute(nWhich) )
1256  {
1257  pOtherSet = &rChgSet;
1258  bOtherAttr = true;
1259  }
1260  }
1261  }
1262 
1263  // Build new itemset if either
1264  // - rChgSet.Count() > 1 or
1265  // - The attribute in rChgSet does not belong to one of the above categories
1266  if ( !bCharAttr && !bOtherAttr )
1267  {
1268  SfxItemSet* pTmpCharItemSet = new SfxItemSet(
1269  rDoc.GetAttrPool(),
1270  svl::Items<
1274  RES_TXTATR_UNKNOWN_CONTAINER>{});
1275 
1276  SfxItemSet* pTmpOtherItemSet = new SfxItemSet(
1277  rDoc.GetAttrPool(),
1278  svl::Items<
1281  // FillAttribute support:
1283 
1284  pTmpCharItemSet->Put( rChgSet );
1285  pTmpOtherItemSet->Put( rChgSet );
1286 
1287  pCharSet = pTmpCharItemSet;
1288  pOtherSet = pTmpOtherItemSet;
1289 
1290  bDelete = true;
1291  }
1292 
1293  SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
1294  bool bRet = false;
1295  const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
1296  SwContentNode* pNode = pStt->nNode.GetNode().GetContentNode();
1297 
1298  if( pNode && pNode->IsTextNode() )
1299  {
1300  // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
1301  if (pLayout && pNode->GetTextNode()->getIDocumentSettingAccess()->
1303  {
1304  SwContentNode* pEndNode = pEnd->nNode.GetNode().GetContentNode();
1305  SwContentNode* pCurrentNode = pEndNode;
1306  auto nStartIndex = pNode->GetIndex();
1307  auto nEndIndex = pEndNode->GetIndex();
1308  SwNodeIndex aIdx( pEnd->nNode.GetNode() );
1309  while ( pCurrentNode != nullptr && nStartIndex <= pCurrentNode->GetIndex() )
1310  {
1311  if (pCurrentNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT) &&
1312  // remove character formatting only on wholly selected paragraphs
1313  (nStartIndex < pCurrentNode->GetIndex() || pStt->nContent.GetIndex() == 0) &&
1314  (pCurrentNode->GetIndex() < nEndIndex || pEnd->nContent.GetIndex() == pEndNode->Len()))
1315  {
1316  pCurrentNode->ResetAttr(RES_PARATR_LIST_AUTOFMT);
1317  // reset also paragraph marker
1318  SwIndex nIdx( pCurrentNode, pCurrentNode->Len() );
1319  pCurrentNode->GetTextNode()->RstTextAttr(nIdx, 1);
1320  }
1321  pCurrentNode = SwNodes::GoPrevious( &aIdx );
1322  }
1323  }
1324  // #i27615#
1325  if (rRg.IsInFrontOfLabel())
1326  {
1327  SwTextNode * pTextNd = pNode->GetTextNode();
1328  if (pLayout)
1329  {
1330  pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
1331  }
1332  SwNumRule * pNumRule = pTextNd->GetNumRule();
1333 
1334  if ( !pNumRule )
1335  {
1336  OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1338  return false;
1339  }
1340 
1341  int nLevel = pTextNd->GetActualListLevel();
1342 
1343  if (nLevel < 0)
1344  nLevel = 0;
1345 
1346  if (nLevel >= MAXLEVEL)
1347  nLevel = MAXLEVEL - 1;
1348 
1349  SwNumFormat aNumFormat = pNumRule->Get(static_cast<sal_uInt16>(nLevel));
1350  SwCharFormat * pCharFormat =
1351  rDoc.FindCharFormatByName(aNumFormat.GetCharFormatName());
1352 
1353  if (pCharFormat)
1354  {
1355  if (pHistory)
1356  pHistory->Add(pCharFormat->GetAttrSet(), *pCharFormat);
1357 
1358  if ( pCharSet )
1359  pCharFormat->SetFormatAttr(*pCharSet);
1360  }
1361 
1363  return true;
1364  }
1365 
1366  const SwIndex& rSt = pStt->nContent;
1367 
1368  // Attributes without an end do not have a range
1369  if ( !bCharAttr && !bOtherAttr )
1370  {
1371  SfxItemSet aTextSet( rDoc.GetAttrPool(),
1373  aTextSet.Put( rChgSet );
1374  if( aTextSet.Count() )
1375  {
1376  SwRegHistory history( pNode, *pNode, pHistory );
1377  bRet = history.InsertItems(
1378  aTextSet, rSt.GetIndex(), rSt.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr ) || bRet;
1379 
1382  {
1383  SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1,
1384  pStt->nNode, pStt->nContent.GetIndex() );
1385 
1386  if( pUndo )
1387  pUndo->SaveRedlineData( aPam, true );
1388 
1389  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1390  rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
1391  else
1392  rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
1393  }
1394  }
1395  }
1396 
1397  // TextAttributes with an end never expand their range
1398  if ( !bCharAttr && !bOtherAttr )
1399  {
1400  // CharFormat and URL attributes are treated separately!
1401  // TEST_TEMP ToDo: AutoFormat!
1402  SfxItemSet aTextSet(
1403  rDoc.GetAttrPool(),
1404  svl::Items<
1407  RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD>{});
1408 
1409  aTextSet.Put( rChgSet );
1410  if( aTextSet.Count() )
1411  {
1412  const sal_Int32 nInsCnt = rSt.GetIndex();
1413  const sal_Int32 nEnd = pStt->nNode == pEnd->nNode
1414  ? pEnd->nContent.GetIndex()
1415  : pNode->Len();
1416  SwRegHistory history( pNode, *pNode, pHistory );
1417  bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags, ppNewTextAttr )
1418  || bRet;
1419 
1422  {
1423  // Was text content inserted? (RefMark/TOXMarks without an end)
1424  bool bTextIns = nInsCnt != rSt.GetIndex();
1425  // Was content inserted or set over the selection?
1426  SwPaM aPam( pStt->nNode, bTextIns ? nInsCnt + 1 : nEnd,
1427  pStt->nNode, nInsCnt );
1428  if( pUndo )
1429  pUndo->SaveRedlineData( aPam, bTextIns );
1430 
1431  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1433  new SwRangeRedline(
1434  bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ),
1435  true);
1436  else if( bTextIns )
1437  rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
1438  }
1439  }
1440  }
1441  }
1442 
1443  // We always have to set the auto flag for PageDescs that are set at the Node!
1444  if( pOtherSet && pOtherSet->Count() )
1445  {
1446  SwTableNode* pTableNd;
1447  const SwFormatPageDesc* pDesc;
1448  if( SfxItemState::SET == pOtherSet->GetItemState( RES_PAGEDESC,
1449  false, reinterpret_cast<const SfxPoolItem**>(&pDesc) ))
1450  {
1451  if( pNode )
1452  {
1453  // Set auto flag. Only in the template it's without auto!
1454  SwFormatPageDesc aNew( *pDesc );
1455 
1456  // Tables now also know line breaks
1457  if( !(nFlags & SetAttrMode::APICALL) &&
1458  nullptr != ( pTableNd = pNode->FindTableNode() ) )
1459  {
1460  SwTableNode* pCurTableNd = pTableNd;
1461  while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1462  pTableNd = pCurTableNd;
1463 
1464  // set the table format
1465  SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1466  SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1467  pFormat->SetFormatAttr( aNew );
1468  bRet = true;
1469  }
1470  else
1471  {
1472  SwContentNode * pFirstNode(pNode);
1473  if (pLayout && pLayout->HasMergedParas())
1474  {
1475  pFirstNode = sw::GetFirstAndLastNode(*pLayout, pStt->nNode).first;
1476  }
1477  SwRegHistory aRegH( pFirstNode, *pFirstNode, pHistory );
1478  bRet = pFirstNode->SetAttr( aNew ) || bRet;
1479  }
1480  }
1481 
1482  // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1483  // we know, that there is only one attribute in pOtherSet. We cannot
1484  // perform the following operations, instead we return:
1485  if ( bOtherAttr )
1486  return bRet;
1487 
1488  const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
1489  if( !pOtherSet->Count() )
1490  {
1492  return bRet;
1493  }
1494  }
1495 
1496  // Tables now also know line breaks
1497  const SvxFormatBreakItem* pBreak;
1498  if( pNode && !(nFlags & SetAttrMode::APICALL) &&
1499  nullptr != (pTableNd = pNode->FindTableNode() ) &&
1500  SfxItemState::SET == pOtherSet->GetItemState( RES_BREAK,
1501  false, reinterpret_cast<const SfxPoolItem**>(&pBreak) ) )
1502  {
1503  SwTableNode* pCurTableNd = pTableNd;
1504  while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1505  pTableNd = pCurTableNd;
1506 
1507  // set the table format
1508  SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1509  SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1510  pFormat->SetFormatAttr( *pBreak );
1511  bRet = true;
1512 
1513  // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1514  // we know, that there is only one attribute in pOtherSet. We cannot
1515  // perform the following operations, instead we return:
1516  if ( bOtherAttr )
1517  return bRet;
1518 
1519  const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
1520  if( !pOtherSet->Count() )
1521  {
1523  return bRet;
1524  }
1525  }
1526 
1527  {
1528  // If we have a PoolNumRule, create it if needed
1529  const SwNumRuleItem* pRule;
1530  sal_uInt16 nPoolId=0;
1531  if( SfxItemState::SET == pOtherSet->GetItemState( RES_PARATR_NUMRULE,
1532  false, reinterpret_cast<const SfxPoolItem**>(&pRule) ) &&
1533  !rDoc.FindNumRulePtr( pRule->GetValue() ) &&
1537  }
1538  }
1539 
1540  SfxItemSet firstSet(rDoc.GetAttrPool(),
1542  if (pOtherSet && pOtherSet->Count())
1543  { // actually only RES_BREAK is possible here...
1544  firstSet.Put(*pOtherSet);
1545  }
1546  SfxItemSet propsSet(rDoc.GetAttrPool(),
1550  if (pOtherSet && pOtherSet->Count())
1551  {
1552  propsSet.Put(*pOtherSet);
1553  }
1554 
1555  if( !rRg.HasMark() ) // no range
1556  {
1557  if( !pNode )
1558  {
1560  return bRet;
1561  }
1562 
1563  if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1564  {
1565  SwTextNode* pTextNd = pNode->GetTextNode();
1566  const SwIndex& rSt = pStt->nContent;
1567  sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
1568  const OUString& rStr = pTextNd->GetText();
1569 
1570  // Special case: if the Cursor is located within a URL attribute, we take over it's area
1571  SwTextAttr const*const pURLAttr(
1572  pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
1573  if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
1574  {
1575  nMkPos = pURLAttr->GetStart();
1576  nPtPos = *pURLAttr->End();
1577  }
1578  else
1579  {
1581  Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1582  pTextNd->GetText(), nPtPos,
1583  g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1584  WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
1585  true);
1586 
1587  if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
1588  {
1589  nMkPos = aBndry.startPos;
1590  nPtPos = aBndry.endPos;
1591  }
1592  else
1593  nPtPos = nMkPos = rSt.GetIndex();
1594  }
1595 
1596  // Remove the overriding attributes from the SwpHintsArray,
1597  // if the selection spans across the whole paragraph.
1598  // These attributes are inserted as FormatAttributes and
1599  // never override the TextAttributes!
1600  if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
1601  pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
1602  {
1603  SwIndex aSt( pTextNd );
1604  if( pHistory )
1605  {
1606  // Save all attributes for the Undo.
1607  SwRegHistory aRHst( *pTextNd, pHistory );
1608  pTextNd->GetpSwpHints()->Register( &aRHst );
1609  pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1610  if( pTextNd->GetpSwpHints() )
1611  pTextNd->GetpSwpHints()->DeRegister();
1612  }
1613  else
1614  pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1615  }
1616 
1617  // the SwRegHistory inserts the attribute into the TextNode!
1618  SwRegHistory history( pNode, *pNode, pHistory );
1619  bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr )
1620  || bRet;
1621 
1622  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1623  {
1624  SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
1625 
1626  if( pUndo )
1627  pUndo->SaveRedlineData( aPam, false );
1628  rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Format, aPam ), true);
1629  }
1630  }
1631  if( pOtherSet && pOtherSet->Count() )
1632  {
1633  // Need to check for unique item for DrawingLayer items of type NameOrIndex
1634  // and evtl. correct that item to ensure unique names for that type. This call may
1635  // modify/correct entries inside of the given SfxItemSet
1636  SfxItemSet aTempLocalCopy(*pOtherSet);
1637 
1638  rDoc.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
1639  bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet;
1640  }
1641 
1643  return bRet;
1644  }
1645 
1646  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
1647  {
1648  if( pUndo )
1649  pUndo->SaveRedlineData( rRg, false );
1650  rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Format, rRg ), true);
1651  }
1652 
1653  /* now if range */
1654  sal_uLong nNodes = 0;
1655 
1656  SwNodeIndex aSt( rDoc.GetNodes() );
1657  SwNodeIndex aEnd( rDoc.GetNodes() );
1658  SwIndex aCntEnd( pEnd->nContent );
1659 
1660  if( pNode )
1661  {
1662  const sal_Int32 nLen = pNode->Len();
1663  if( pStt->nNode != pEnd->nNode )
1664  aCntEnd.Assign( pNode, nLen );
1665 
1666  if( pStt->nContent.GetIndex() != 0 || aCntEnd.GetIndex() != nLen )
1667  {
1668  // the SwRegHistory inserts the attribute into the TextNode!
1669  if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1670  {
1671  SwRegHistory history( pNode, *pNode, pHistory );
1672  bRet = history.InsertItems(*pCharSet,
1673  pStt->nContent.GetIndex(), aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr)
1674  || bRet;
1675  }
1676 
1677  if( pOtherSet && pOtherSet->Count() )
1678  {
1679  bRet = lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout) || bRet;
1680  }
1681 
1682  // Only selection in a Node.
1683  if( pStt->nNode == pEnd->nNode )
1684  {
1685  //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
1686  //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
1687  //current setting attribute set is a character range properties set and comes from a MS Word
1688  //binary file, and the setting range include a paragraph end position (0X0D);
1689  //more specifications, as such property inside the character range properties set recorded in
1690  //MS Word binary file are dealt and inserted into data model (SwDoc) one by one, so we
1691  //only dealing the scenario that the char properties set with 1 item inside;
1692 
1693  if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1 )
1694  {
1695  SwTextNode* pCurrentNd = pStt->nNode.GetNode().GetTextNode();
1696 
1697  if (pCurrentNd)
1698  {
1699  pCurrentNd->TryCharSetExpandToNum(*pCharSet);
1700 
1701  }
1702  }
1704  return bRet;
1705  }
1706  ++nNodes;
1707  aSt.Assign( pStt->nNode.GetNode(), +1 );
1708  }
1709  else
1710  aSt = pStt->nNode;
1711  aCntEnd = pEnd->nContent; // aEnd was changed!
1712  }
1713  else
1714  aSt.Assign( pStt->nNode.GetNode(), +1 );
1715 
1716  // aSt points to the first full Node now
1717 
1718  /*
1719  * The selection spans more than one Node.
1720  */
1721  if( pStt->nNode < pEnd->nNode )
1722  {
1723  pNode = pEnd->nNode.GetNode().GetContentNode();
1724  if(pNode)
1725  {
1726  if( aCntEnd.GetIndex() != pNode->Len() )
1727  {
1728  // the SwRegHistory inserts the attribute into the TextNode!
1729  if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1730  {
1731  SwRegHistory history( pNode, *pNode, pHistory );
1732  (void)history.InsertItems(*pCharSet,
1733  0, aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr);
1734  }
1735 
1736  if( pOtherSet && pOtherSet->Count() )
1737  {
1738  lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout);
1739  }
1740 
1741  ++nNodes;
1742  aEnd = pEnd->nNode;
1743  }
1744  else
1745  aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1746  }
1747  else
1748  aEnd = pEnd->nNode;
1749  }
1750  else
1751  aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1752 
1753  // aEnd points BEHIND the last full node now
1754 
1755  /* Edit the fully selected Nodes. */
1756  // Reset all attributes from the set!
1757  if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
1758  {
1760  pStt, pEnd, pHistory, pCharSet, pLayout);
1762  }
1763 
1764  bool bCreateSwpHints = pCharSet && (
1765  SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
1766  SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
1767 
1768  for (SwNodeIndex current = aSt; current < aEnd; ++current)
1769  {
1770  SwTextNode *const pTNd = current.GetNode().GetTextNode();
1771  if (!pTNd)
1772  continue;
1773 
1774  if (pLayout && pLayout->HasMergedParas()
1776  { // not really sure what to do here, but applying to hidden
1777  continue; // nodes doesn't make sense...
1778  }
1779 
1780  if( pHistory )
1781  {
1782  SwRegHistory aRegH( pTNd, *pTNd, pHistory );
1783 
1784  if (pCharSet && pCharSet->Count())
1785  {
1786  SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
1787  : pTNd->GetpSwpHints();
1788  if( pSwpHints )
1789  pSwpHints->Register( &aRegH );
1790 
1791  pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1792  if( pSwpHints )
1793  pSwpHints->DeRegister();
1794  }
1795  }
1796  else
1797  {
1798  if (pCharSet && pCharSet->Count())
1799  pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1800  }
1801  ++nNodes;
1802  }
1803 
1804  if (pOtherSet && pOtherSet->Count())
1805  {
1806  for (; aSt < aEnd; ++aSt)
1807  {
1808  pNode = aSt.GetNode().GetContentNode();
1809  if (!pNode)
1810  continue;
1811 
1812  lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout, &aSt);
1813  ++nNodes;
1814  }
1815  }
1816 
1817  //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
1818  //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
1819  //current setting attribute set is a character range properties set and comes from a MS Word
1820  //binary file, and the setting range include a paragraph end position (0X0D);
1821  //more specifications, as such property inside the character range properties set recorded in
1822  //MS Word binary file are dealt and inserted into data model (SwDoc) one by one, so we
1823  //only dealing the scenario that the char properties set with 1 item inside;
1824  if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1)
1825  {
1826  SwPosition aStartPos (*rRg.Start());
1827  SwPosition aEndPos (*rRg.End());
1828 
1829  if (aEndPos.nNode.GetNode().GetTextNode() && aEndPos.nContent != aEndPos.nNode.GetNode().GetTextNode()->Len())
1830  aEndPos.nNode--;
1831 
1832  sal_uLong nStart = aStartPos.nNode.GetIndex();
1833  sal_uLong nEnd = aEndPos.nNode.GetIndex();
1834  for(; nStart <= nEnd; ++nStart)
1835  {
1836  SwNode* pNd = rDoc.GetNodes()[ nStart ];
1837  if (!pNd || !pNd->IsTextNode())
1838  continue;
1839  SwTextNode *pCurrentNd = pNd->GetTextNode();
1840  pCurrentNd->TryCharSetExpandToNum(*pCharSet);
1841  }
1842  }
1843 
1845  return (nNodes != 0) || bRet;
1846  }
1847 }
1848 
1849 namespace sw
1850 {
1851 
1852 namespace mark
1853 {
1854  bool IsFieldmarkOverlap(SwPaM const& rPaM)
1855  {
1856  std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
1857  sw::CalcBreaks(Breaks, rPaM);
1858  return !Breaks.empty();
1859  }
1860 }
1861 
1863 {
1864 }
1865 
1872 static bool IsEmptyRange(const SwPosition& rStart, const SwPosition& rEnd,
1873  SwCopyFlags const flags)
1874 {
1875  if (rStart == rEnd)
1876  { // check if a fly anchored there would be copied - then copy...
1877  return !IsDestroyFrameAnchoredAtChar(rStart, rStart, rEnd,
1878  (flags & SwCopyFlags::IsMoveToFly)
1881  }
1882  else
1883  {
1884  return rEnd < rStart;
1885  }
1886 }
1887 
1888 // Copy an area into this document or into another document
1889 bool
1891  SwCopyFlags const flags) const
1892 {
1893  const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
1894 
1895  SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
1896  bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
1897 
1898  // Catch if there's no copy to do
1899  if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel))
1900  return false;
1901 
1902  // Prevent copying into Flys that are anchored in the source range
1903  if (&rDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly))
1904  {
1905  // Correct the Start-/EndNode
1906  sal_uLong nStt = pStt->nNode.GetIndex(),
1907  nEnd = pEnd->nNode.GetIndex(),
1908  nDiff = nEnd - nStt +1;
1909  SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
1910  if( pNd->IsContentNode() && pStt->nContent.GetIndex() )
1911  {
1912  ++nStt;
1913  --nDiff;
1914  }
1915  if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
1916  static_cast<SwContentNode*>(pNd)->Len() != pEnd->nContent.GetIndex() )
1917  {
1918  --nEnd;
1919  --nDiff;
1920  }
1921  if( nDiff &&
1922  lcl_ChkFlyFly( rDoc, nStt, nEnd, rPos.nNode.GetIndex() ) )
1923  {
1924  return false;
1925  }
1926  }
1927 
1928  SwPaM* pRedlineRange = nullptr;
1929  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
1931  pRedlineRange = new SwPaM( rPos );
1932 
1934 
1935  bool bRet = false;
1936 
1937  if( &rDoc != &m_rDoc )
1938  { // ordinary copy
1939  bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
1940  }
1941  else if( ! ( *pStt <= rPos && rPos < *pEnd &&
1942  ( pStt->nNode != pEnd->nNode ||
1943  !pStt->nNode.GetNode().IsTextNode() )) )
1944  {
1945  // Copy to a position outside of the area, or copy a single TextNode
1946  // Do an ordinary copy
1947  bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
1948  }
1949  else
1950  {
1951  // Copy the range in itself
1952  assert(!"mst: this is assumed to be dead code");
1953  }
1954 
1956  if( pRedlineRange )
1957  {
1958  if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1959  rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlineRange ), true);
1960  else
1961  rDoc.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
1962  delete pRedlineRange;
1963  }
1964 
1965  return bRet;
1966 }
1967 
1971 {
1972  assert(pNode && "Didn't pass a Node.");
1973 
1974  SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
1975  : pNode->StartOfSectionNode();
1976  SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
1977 
1978  // delete all Flys, Bookmarks, ...
1979  DelFlyInRange( aSttIdx, aEndIdx );
1980  m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, RedlineType::Any );
1981  DelBookmarks(aSttIdx, aEndIdx);
1982 
1983  {
1984  // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
1985  SwNodeIndex aMvStt( aSttIdx, 1 );
1986  SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true );
1987  }
1988 
1989  m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
1990 }
1991 
1993  SwPosition const& rPos, sal_Unicode const cDummy)
1994 {
1995  SwPaM aPam(rPos, rPos);
1996  ++aPam.GetPoint()->nContent;
1997  assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy);
1998  (void) cDummy;
1999 
2000  DeleteRangeImpl(aPam);
2001 
2004  {
2006  }
2007 }
2008 
2010 {
2011  lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl );
2012 
2015  {
2017  }
2018 }
2019 
2021 {
2022  const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
2023  const SwNode* pNd = &rStt.nNode.GetNode();
2024  sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
2025  pNd->StartOfSectionIndex();
2026  sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
2027 
2028  if ( nSectDiff-2 <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
2029  /* #i9185# Prevent getting the node after the end node (see below) */
2030  rEnd.nNode.GetIndex() + 1 == m_rDoc.GetNodes().Count() )
2031  {
2032  return false;
2033  }
2034 
2035  {
2036  SwPaM temp(rPam, nullptr);
2037  if (!temp.HasMark())
2038  {
2039  temp.SetMark();
2040  }
2041  if (SwTextNode *const pNode = temp.Start()->nNode.GetNode().GetTextNode())
2042  { // rPam may not have nContent set but IsFieldmarkOverlap requires it
2043  pNode->MakeStartIndex(&temp.Start()->nContent);
2044  }
2045  if (SwTextNode *const pNode = temp.End()->nNode.GetNode().GetTextNode())
2046  {
2047  pNode->MakeEndIndex(&temp.End()->nContent);
2048  }
2049  if (sw::mark::IsFieldmarkOverlap(temp))
2050  { // a bit of a problem: we want to completely remove the nodes
2051  // but then how can the CH_TXT_ATR survive?
2052  return false;
2053  }
2054  }
2055 
2056  // Move hard page brakes to the following Node.
2057  bool bSavePageBreak = false, bSavePageDesc = false;
2058 
2059  /* #i9185# This would lead to a segmentation fault if not caught above. */
2060  sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
2061  SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
2062 
2063  if( pTableNd && pNd->IsContentNode() )
2064  {
2065  SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2066 
2067  {
2068  const SfxPoolItem *pItem;
2069  const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
2070  if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
2071  false, &pItem ) )
2072  {
2073  pTableFormat->SetFormatAttr( *pItem );
2074  bSavePageDesc = true;
2075  }
2076 
2077  if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
2078  false, &pItem ) )
2079  {
2080  pTableFormat->SetFormatAttr( *pItem );
2081  bSavePageBreak = true;
2082  }
2083  }
2084  }
2085 
2086  bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
2087  if( bDoesUndo )
2088  {
2089  if( !rPam.HasMark() )
2090  rPam.SetMark();
2091  else if( rPam.GetPoint() == &rStt )
2092  rPam.Exchange();
2093  rPam.GetPoint()->nNode++;
2094 
2095  SwContentNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetContentNode();
2096  rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
2097  bool bGoNext = (nullptr == pTmpNode);
2098  pTmpNode = rPam.GetMark()->nNode.GetNode().GetContentNode();
2099  rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
2100 
2102 
2103  SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2104  {
2105  SwPosition aTmpPos( *aDelPam.GetPoint() );
2106  if( bGoNext )
2107  {
2108  pTmpNode = m_rDoc.GetNodes().GoNext( &aTmpPos.nNode );
2109  aTmpPos.nContent.Assign( pTmpNode, 0 );
2110  }
2111  ::PaMCorrAbs( aDelPam, aTmpPos );
2112  }
2113 
2114  std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete( aDelPam, true ));
2115 
2116  *rPam.GetPoint() = *aDelPam.GetPoint();
2117  pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2118  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2119  rPam.DeleteMark();
2120  }
2121  else
2122  {
2123  SwNodeRange aRg( rStt.nNode, rEnd.nNode );
2124  rPam.Normalize(false);
2125 
2126  // Try to move past the End
2127  if( !rPam.Move( fnMoveForward, GoInNode ) )
2128  {
2129  // Fair enough, at the Beginning then
2130  rPam.Exchange();
2131  if( !rPam.Move( fnMoveBackward, GoInNode ))
2132  {
2133  SAL_WARN("sw.core", "DelFullPara: no more Nodes");
2134  return false;
2135  }
2136  }
2137  // move bookmarks, redlines etc.
2138  if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
2139  {
2140  m_rDoc.CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, true );
2141  }
2142  else
2143  {
2144  SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
2145  }
2146 
2147  // What's with Flys?
2148  {
2149  // If there are FlyFrames left, delete these too
2150  for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
2151  {
2152  SwFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
2153  const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
2154  SwPosition const*const pAPos = pAnchor->GetContentAnchor();
2155  if (pAPos &&
2156  ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
2157  (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
2158  // note: here use <= not < like in
2159  // IsDestroyFrameAnchoredAtChar() because of the increment
2160  // of rPam in the bDoesUndo path above!
2161  aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
2162  {
2164  --n;
2165  }
2166  }
2167  }
2168 
2169  rPam.DeleteMark();
2170  m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
2171  }
2173 
2174  return true;
2175 }
2176 
2177 // #i100466# Add handling of new optional parameter <bForceJoinNext>
2179  const bool bForceJoinNext )
2180 {
2181  if ( lcl_StrLenOverflow( rPam ) )
2182  return false;
2183 
2184  bool const ret = lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
2187  bForceJoinNext );
2188 
2189  return ret;
2190 }
2191 
2192 // It seems that this is mostly used by SwDoc internals; the only
2193 // way to call this from the outside seems to be the special case in
2194 // SwDoc::CopyRange (but I have not managed to actually hit that case).
2196 {
2197  // nothing moved: return
2198  const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
2199  if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
2200  return false;
2201 
2202  assert(!sw::mark::IsFieldmarkOverlap(rPaM)); // probably an invalid redline was created?
2203 
2204  // Save the paragraph anchored Flys, so that they can be moved.
2205  SaveFlyArr aSaveFlyArr;
2206  SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
2207 
2208  // save redlines (if DOC_MOVEREDLINES is used)
2209  SaveRedlines_t aSaveRedl;
2211  {
2212  lcl_SaveRedlines( rPaM, aSaveRedl );
2213 
2214  // #i17764# unfortunately, code below relies on undos being
2215  // in a particular order, and presence of bookmarks
2216  // will change this order. Hence, we delete bookmarks
2217  // here without undo.
2218  ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
2219  DelBookmarks(
2220  pStt->nNode,
2221  pEnd->nNode,
2222  nullptr,
2223  &pStt->nContent,
2224  &pEnd->nContent);
2225  }
2226 
2227  bool bUpdateFootnote = false;
2228  SwFootnoteIdxs aTmpFntIdx;
2229 
2230  std::unique_ptr<SwUndoMove> pUndoMove;
2232  {
2234  pUndoMove.reset(new SwUndoMove( rPaM, rPos ));
2235  pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
2236  }
2237  else
2238  {
2239  bUpdateFootnote = lcl_SaveFootnote( pStt->nNode, pEnd->nNode, rPos.nNode,
2240  m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
2241  &pStt->nContent, &pEnd->nContent );
2242  }
2243 
2244  bool bSplit = false;
2245  SwPaM aSavePam( rPos, rPos );
2246 
2247  // Move the SPoint to the beginning of the range
2248  if( rPaM.GetPoint() == pEnd )
2249  rPaM.Exchange();
2250 
2251  // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
2252  SwTextNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTextNode();
2253  bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
2254 
2255  // If one ore more TextNodes are moved, SwNodes::Move will do a SplitNode.
2256  // However, this does not update the cursor. So we create a TextNode to keep
2257  // updating the indices. After the Move the Node is optionally deleted.
2258  SwTextNode * pTNd = rPos.nNode.GetNode().GetTextNode();
2259  if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
2260  ( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
2261  {
2262  bSplit = true;
2263  const sal_Int32 nMkContent = rPaM.GetMark()->nContent.GetIndex();
2264 
2265  const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
2266  pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
2267 
2268  SwTextNode * pOrigNode = pTNd;
2269  assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
2270  *aSavePam.GetPoint() == rPos);
2271  assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
2272  assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
2273  assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
2274 
2275  std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc(
2276  [&](SwTextNode *const, sw::mark::RestoreMode const eMode)
2277  {
2278  if (!pContentStore->Empty())
2279  {
2280  pContentStore->Restore(m_rDoc, pOrigNode->GetIndex()-1, 0, true, eMode);
2281  }
2282  });
2283  pTNd = pTNd->SplitContentNode(rPos, &restoreFunc)->GetTextNode();
2284 
2285  //A new node was inserted before the orig pTNd and the content up to
2286  //rPos moved into it. The old node is returned with the remainder
2287  //of the content in it.
2288  //
2289  //aSavePam was created with rPos, it continues to point to the
2290  //old node, but with the *original* content index into the node.
2291  //Seeing as all the orignode content before that index has
2292  //been removed, the new index into the original node should now be set
2293  //to 0 and the content index of rPos should also be adapted to the
2294  //truncated node
2295  assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
2296  *aSavePam.GetPoint() == rPos);
2297  assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
2298  assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
2299  assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
2300  aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0);
2301  rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
2302 
2303  // correct the PaM!
2304  if( rPos.nNode == rPaM.GetMark()->nNode )
2305  {
2306  rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
2307  rPaM.GetMark()->nContent.Assign( pTNd, nMkContent );
2308  }
2309  }
2310 
2311  // Put back the Pam by one "content"; so that it's always outside of
2312  // the manipulated range.
2313  // tdf#99692 don't Move() back if that would end up in another node
2314  // because moving backward is not necessarily the inverse of forward then.
2315  // (but do Move() back if we have split the node)
2316  const bool bNullContent = !bSplit && aSavePam.GetPoint()->nContent == 0;
2317  if( bNullContent )
2318  {
2319  aSavePam.GetPoint()->nNode--;
2320  aSavePam.GetPoint()->nContent.Assign(aSavePam.GetContentNode(), 0);
2321  }
2322  else
2323  {
2324  bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
2325  assert(success);
2326  (void) success;
2327  }
2328 
2329  // Copy all Bookmarks that are within the Move range into an array,
2330  // that saves the position as an offset.
2331  std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2332  DelBookmarks(
2333  pStt->nNode,
2334  pEnd->nNode,
2335  &aSaveBkmks,
2336  &pStt->nContent,
2337  &pEnd->nContent);
2338 
2339  // If there is no range anymore due to the above deletions (e.g. the
2340  // footnotes got deleted), it's still a valid Move!
2341  if( *rPaM.GetPoint() != *rPaM.GetMark() )
2342  {
2343  // now do the actual move
2344  m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
2345 
2346  // after a MoveRange() the Mark is deleted
2347  if ( rPaM.HasMark() ) // => no Move occurred!
2348  {
2349  return false;
2350  }
2351  }
2352  else
2353  rPaM.DeleteMark();
2354 
2355  OSL_ENSURE( *aSavePam.GetMark() == rPos ||
2356  ( aSavePam.GetMark()->nNode.GetNode().GetContentNode() == nullptr ),
2357  "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2358  *aSavePam.GetMark() = rPos;
2359 
2360  rPaM.SetMark(); // create a Sel. around the new range
2361  pTNd = aSavePam.GetNode().GetTextNode();
2363  bool bRemove = true;
2364  // Do two Nodes have to be joined at the SavePam?
2365  if (bSplit && pTNd)
2366  {
2367  if (pTNd->CanJoinNext())
2368  {
2369  // Always join next, because <pTNd> has to stay as it is.
2370  // A join previous from its next would more or less delete <pTNd>
2371  pTNd->JoinNext();
2372  bRemove = false;
2373  }
2374  }
2375  if (bNullContent)
2376  {
2377  aSavePam.GetPoint()->nNode++;
2378  aSavePam.GetPoint()->nContent.Assign( aSavePam.GetContentNode(), 0 );
2379  }
2380  else if (bRemove) // No move forward after joining with next paragraph
2381  {
2382  aSavePam.Move( fnMoveForward, GoInContent );
2383  }
2384 
2385  // Insert the Bookmarks back into the Document.
2386  *rPaM.GetMark() = *aSavePam.Start();
2387  for(auto& rBkmk : aSaveBkmks)
2388  rBkmk.SetInDoc(
2389  &m_rDoc,
2390  rPaM.GetMark()->nNode,
2391  &rPaM.GetMark()->nContent);
2392  *rPaM.GetPoint() = *aSavePam.End();
2393 
2394  // Move the Flys to the new position.
2395  // note: rPos is at the end here; can't really tell flys that used to be
2396  // at the start of rPam from flys that used to be at the end of rPam
2397  // unfortunately, so some of them are going to end up with wrong anchor...
2398  RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &(rPos.nNode) );
2399 
2400  // restore redlines (if DOC_MOVEREDLINES is used)
2401  if( !aSaveRedl.empty() )
2402  {
2403  lcl_RestoreRedlines( m_rDoc, *aSavePam.Start(), aSaveRedl );
2404  }
2405 
2406  if( bUpdateFootnote )
2407  {
2408  if( !aTmpFntIdx.empty() )
2409  {
2410  m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2411  aTmpFntIdx.clear();
2412  }
2413 
2415  }
2416 
2418  return true;
2419 }
2420 
2422  SwMoveFlags eMvFlags )
2423 {
2424  // Moves all Nodes to the new position.
2425  // Bookmarks are moved too (currently without Undo support).
2426 
2427  // If footnotes are being moved to the special section, remove them now.
2428 
2429  // Or else delete the Frames for all footnotes that are being moved
2430  // and have it rebuild after the Move (footnotes can change pages).
2431  // Additionally we have to correct the FootnoteIdx array's sorting.
2432  bool bUpdateFootnote = false;
2433  SwFootnoteIdxs aTmpFntIdx;
2434 
2435  std::unique_ptr<SwUndoMove> pUndo;
2437  {
2438  pUndo.reset(new SwUndoMove( m_rDoc, rRange, rPos ));
2439  }
2440  else
2441  {
2442  bUpdateFootnote = lcl_SaveFootnote( rRange.aStart, rRange.aEnd, rPos,
2443  m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
2444  }
2445 
2446  SaveRedlines_t aSaveRedl;
2447  std::vector<SwRangeRedline*> aSavRedlInsPosArr;
2449  {
2450  lcl_SaveRedlines( rRange, aSaveRedl );
2451 
2452  // Find all RedLines that end at the InsPos.
2453  // These have to be moved back to the "old" position after the Move.
2454  SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rPos.GetNode(), RedlineType::Any );
2455  if( SwRedlineTable::npos != nRedlPos )
2456  {
2457  const SwPosition *pRStt, *pREnd;
2458  do {
2460  pRStt = pTmp->Start();
2461  pREnd = pTmp->End();
2462  if( pREnd->nNode == rPos && pRStt->nNode < rPos )
2463  {
2464  aSavRedlInsPosArr.push_back( pTmp );
2465  }
2466  } while( pRStt->nNode < rPos && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
2467  }
2468  }
2469 
2470  // Copy all Bookmarks that are within the Move range into an array
2471  // that stores all references to positions as an offset.
2472  // The final mapping happens after the Move.
2473  std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2474  DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
2475 
2476  // Save the paragraph-bound Flys, so that they can be moved.
2477  SaveFlyArr aSaveFlyArr;
2478  if( !m_rDoc.GetSpzFrameFormats()->empty() )
2479  SaveFlyInRange( rRange, aSaveFlyArr );
2480 
2481  // Set it to before the Position, so that it cannot be moved further.
2482  SwNodeIndex aIdx( rPos, -1 );
2483 
2484  std::unique_ptr<SwNodeIndex> pSaveInsPos;
2485  if( pUndo )
2486  pSaveInsPos.reset(new SwNodeIndex( rRange.aStart, -1 ));
2487 
2488  // move the Nodes
2489  bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
2490  if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rPos, !bNoDelFrames ) )
2491  {
2492  ++aIdx; // again back to old position
2493  if( pSaveInsPos )
2494  ++(*pSaveInsPos);
2495  }
2496  else
2497  {
2498  aIdx = rRange.aStart;
2499  pUndo.reset();
2500  }
2501 
2502  // move the Flys to the new position
2503  if( !aSaveFlyArr.empty() )
2504  {
2505  SwPosition const tmp(aIdx);
2506  RestFlyInRange(aSaveFlyArr, tmp, nullptr);
2507  }
2508 
2509  // Add the Bookmarks back to the Document
2510  for(auto& rBkmk : aSaveBkmks)
2511  rBkmk.SetInDoc(&m_rDoc, aIdx);
2512 
2513  if( !aSavRedlInsPosArr.empty() )
2514  {
2515  SwNode* pNewNd = &aIdx.GetNode();
2516  for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
2517  {
2519  {
2520  SwPosition* pEnd = pTmp->End();
2521  pEnd->nNode = aIdx;
2522  pEnd->nContent.Assign( pNewNd->GetContentNode(), 0 );
2523  }
2524  }
2525  }
2526 
2527  if( !aSaveRedl.empty() )
2528  lcl_RestoreRedlines( m_rDoc, aIdx.GetIndex(), aSaveRedl );
2529 
2530  if( pUndo )
2531  {
2532  pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
2533  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2534  }
2535 
2536  pSaveInsPos.reset();
2537 
2538  if( bUpdateFootnote )
2539  {
2540  if( !aTmpFntIdx.empty() )
2541  {
2542  m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2543  aTmpFntIdx.clear();
2544  }
2545 
2547  }
2548 
2550  return true;
2551 }
2552 
2554 {
2555  SwNodeIndex aIdx( rPaM.Start()->nNode );
2556  bool bJoinText = aIdx.GetNode().IsTextNode();
2557  bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
2558  aIdx--; // in front of the move area!
2559 
2560  bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
2561  if( bRet && !bOneNode )
2562  {
2563  if( bJoinText )
2564  ++aIdx;
2565  SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
2566  SwNodeIndex aNxtIdx( aIdx );
2567  if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
2568  {
2569  { // Block so SwIndex into node is deleted before Join
2570  m_rDoc.CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex(pTextNd,
2571  pTextNd->GetText().getLength()) ), 0, true );
2572  }
2573  pTextNd->JoinNext();
2574  }
2575  }
2576  return bRet;
2577 }
2578 
2579 // Overwrite only uses the point of the PaM, the mark is ignored; characters
2580 // are replaced from point until the end of the node; at the end of the node,
2581 // characters are inserted.
2582 bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
2583 {
2584  assert(rStr.getLength());
2585  SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
2586  if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2587  {
2588  if( 1 == rStr.getLength() )
2589  m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
2591  }
2592 
2593  SwTextNode *pNode = rPt.nNode.GetNode().GetTextNode();
2594  if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
2595  {
2596  return false;
2597  }
2598 
2600  {
2601  m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2602  }
2603 
2604  const size_t nOldAttrCnt = pNode->GetpSwpHints()
2605  ? pNode->GetpSwpHints()->Count() : 0;
2606  SwDataChanged aTmp( rRg );
2607  SwIndex& rIdx = rPt.nContent;
2608  sal_Int32 const nActualStart(rIdx.GetIndex());
2609  sal_Int32 nStart = 0;
2610 
2611  bool bOldExpFlg = pNode->IsIgnoreDontExpand();
2612  pNode->SetIgnoreDontExpand( true );
2613 
2614  for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
2615  {
2616  // start behind the characters (to fix the attributes!)
2617  nStart = rIdx.GetIndex();
2618  if (nStart < pNode->GetText().getLength())
2619  {
2620  lcl_SkipAttr( pNode, rIdx, nStart );
2621  }
2622  sal_Unicode c = rStr[ nCnt ];
2624  {
2625  bool bMerged(false);
2627  {
2628  SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
2629  SwUndoOverwrite *const pUndoOW(
2630  dynamic_cast<SwUndoOverwrite *>(pUndo) );
2631  if (pUndoOW)
2632  {
2633  // if CanGrouping() returns true it's already merged
2634  bMerged = pUndoOW->CanGrouping(m_rDoc, rPt, c);
2635  }
2636  }
2637  if (!bMerged)
2638  {
2640  std::make_unique<SwUndoOverwrite>(m_rDoc, rPt, c) );
2641  }
2642  }
2643  else
2644  {
2645  // start behind the characters (to fix the attributes!)
2646  if (nStart < pNode->GetText().getLength())
2647  ++rIdx;
2648  pNode->InsertText( OUString(c), rIdx, SwInsertFlags::EMPTYEXPAND );
2649  if( nStart+1 < rIdx.GetIndex() )
2650  {
2651  rIdx = nStart;
2652  pNode->EraseText( rIdx, 1 );
2653  ++rIdx;
2654  }
2655  }
2656  }
2657  pNode->SetIgnoreDontExpand( bOldExpFlg );
2658 
2659  const size_t nNewAttrCnt = pNode->GetpSwpHints()
2660  ? pNode->GetpSwpHints()->Count() : 0;
2661  if( nOldAttrCnt != nNewAttrCnt )
2662  {
2663  const SwUpdateAttr aHint(0,0,0);
2664  pNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
2665  }
2666 
2669  {
2670  SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
2671  m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
2672  }
2674  {
2675  // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2676  // characters are also included in aPam
2677  SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
2678  m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
2679  }
2680 
2682  return true;
2683 }
2684 
2685 bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
2686  const SwInsertFlags nInsertMode )
2687 {
2688  // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
2690  {
2695  }
2696 
2697  // fetching DoesUndo is surprisingly expensive
2698  bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
2699  if (bDoesUndo)
2700  m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2701 
2702  const SwPosition& rPos = *rRg.GetPoint();
2703 
2704  if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
2705  {
2706  if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
2707  {
2708  m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
2709  }
2711  }
2712 
2713  SwTextNode *const pNode = rPos.nNode.GetNode().GetTextNode();
2714  if(!pNode)
2715  return false;
2716 
2717  SwDataChanged aTmp( rRg );
2718 
2719  if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
2720  {
2721  OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2722  if (bDoesUndo)
2723  {
2725  std::make_unique<SwUndoInsert>(rPos.nNode,
2726  rPos.nContent.GetIndex(), ins.getLength(), nInsertMode));
2727  }
2728  }
2729  else
2730  { // if Undo and grouping is enabled, everything changes!
2731  SwUndoInsert * pUndo = nullptr;
2732 
2733  // don't group the start if hints at the start should be expanded
2734  if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
2735  {
2736  SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
2737  SwUndoInsert *const pUndoInsert(
2738  dynamic_cast<SwUndoInsert *>(pLastUndo) );
2739  if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
2740  {
2741  pUndo = pUndoInsert;
2742  }
2743  }
2744 
2745  CharClass const& rCC = GetAppCharClass();
2746  sal_Int32 nInsPos = rPos.nContent.GetIndex();
2747 
2748  if (!pUndo)
2749  {
2750  pUndo = new SwUndoInsert( rPos.nNode, nInsPos, 0, nInsertMode,
2751  !rCC.isLetterNumeric( rStr, 0 ) );
2752  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
2753  }
2754 
2755  OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2756 
2757  for (sal_Int32 i = 0; i < ins.getLength(); ++i)
2758  {
2759  nInsPos++;
2760  // if CanGrouping() returns true, everything has already been done
2761  if (!pUndo->CanGrouping(ins[i]))
2762  {
2763  pUndo = new SwUndoInsert(rPos.nNode, nInsPos, 1, nInsertMode,
2764  !rCC.isLetterNumeric(ins, i));
2765  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
2766  }
2767  }
2768  }
2769 
2770  // To-Do - add 'SwExtraRedlineTable' also ?
2772  {
2773  SwPaM aPam( rPos.nNode, aTmp.GetContent(),
2774  rPos.nNode, rPos.nContent.GetIndex());
2776  {
2778  new SwRangeRedline( RedlineType::Insert, aPam ), true);
2779  }
2780  else
2781  {
2783  }
2784  }
2785 
2787  return true;
2788 }
2789 
2791  const SwPaM& rPaM,
2792  utl::TransliterationWrapper& rTrans )
2793 {
2794  std::unique_ptr<SwUndoTransliterate> pUndo;
2796  pUndo.reset(new SwUndoTransliterate( rPaM, rTrans ));
2797 
2798  const SwPosition* pStt = rPaM.Start(),
2799  * pEnd = rPaM.End();
2800  sal_uLong nSttNd = pStt->nNode.GetIndex(),
2801  nEndNd = pEnd->nNode.GetIndex();
2802  sal_Int32 nSttCnt = pStt->nContent.GetIndex();
2803  sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
2804 
2805  SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
2806  if( pStt == pEnd && pTNd ) // no selection?
2807  {
2808  // set current word as 'area of effect'
2809 
2811  Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
2812  pTNd->GetText(), nSttCnt,
2813  g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
2814  WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
2815  true);
2816 
2817  if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
2818  {
2819  nSttCnt = aBndry.startPos;
2820  nEndCnt = aBndry.endPos;
2821  }
2822  }
2823 
2824  if( nSttNd != nEndNd ) // is more than one text node involved?
2825  {
2826  // iterate over all effected text nodes, the first and the last one
2827  // may be incomplete because the selection starts and/or ends there
2828 
2829  SwNodeIndex aIdx( pStt->nNode );
2830  if( nSttCnt )
2831  {
2832  ++aIdx;
2833  if( pTNd )
2834  pTNd->TransliterateText(
2835  rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get());
2836  }
2837 
2838  for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
2839  {
2840  pTNd = aIdx.GetNode().GetTextNode();
2841  if (pTNd)
2842  {
2843  pTNd->TransliterateText(
2844  rTrans, 0, pTNd->GetText().getLength(), pUndo.get());
2845  }
2846  }
2847 
2848  if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
2849  pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get() );
2850  }
2851  else if( pTNd && nSttCnt < nEndCnt )
2852  pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get() );
2853 
2854  if( pUndo && pUndo->HasData() )
2855  {
2856  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2857  }
2859 }
2860 
2862  const SwPaM &rRg,
2863  const OUString& rGrfName,
2864  const OUString& rFltName,
2865  const Graphic* pGraphic,
2866  const SfxItemSet* pFlyAttrSet,
2867  const SfxItemSet* pGrfAttrSet,
2868  SwFrameFormat* pFrameFormat )
2869 {
2870  if( !pFrameFormat )
2872  SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
2874  rGrfName, rFltName, pGraphic,
2876  SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
2877  pFlyAttrSet, pGrfAttrSet, pFrameFormat );
2878  return pSwFlyFrameFormat;
2879 }
2880 
2882  const SwPaM &rRg, const GraphicObject& rGrfObj,
2883  const SfxItemSet* pFlyAttrSet,
2884  const SfxItemSet* pGrfAttrSet )
2885 {
2887  SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
2889  rGrfObj, m_rDoc.GetDfltGrfFormatColl() );
2890  SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
2891  pFlyAttrSet, pGrfAttrSet, pFrameFormat );
2892  return pSwFlyFrameFormat;
2893 }
2894 
2896  const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
2897  SfxItemSet* pFlyAttrSet)
2898 {
2899  sal_uInt16 nId = RES_POOLFRM_OLE;
2900  if (xObj.is())
2901  {
2902  SvGlobalName aClassName( xObj->getClassID() );
2903  if (SotExchange::IsMath(aClassName))
2904  {
2905  nId = RES_POOLFRM_FORMEL;
2906  }
2907  }
2908 
2910 
2911  return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
2913  xObj,
2915  pFlyAttrSet, nullptr,
2916  pFrameFormat );
2917 }
2918 
2920  sal_Int64 nAspect,
2921  const SfxItemSet* pFlyAttrSet,
2922  const SfxItemSet* pGrfAttrSet)
2923 {
2925 
2926  return InsNoTextNode( *rRg.GetPoint(),
2929  rObjName,
2930  nAspect,
2932  nullptr ),
2933  pFlyAttrSet, pGrfAttrSet,
2934  pFrameFormat );
2935 }
2936 
2937 void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
2938  const OUString& rFltName, const Graphic* pGraphic )
2939 {
2940  SwGrfNode *pGrfNd;
2941  if( !(( !rPam.HasMark()
2942  || rPam.GetPoint()->nNode.GetIndex() == rPam.GetMark()->nNode.GetIndex() )
2943  && nullptr != ( pGrfNd = rPam.GetPoint()->nNode.GetNode().GetGrfNode() )) )
2944  return;
2945 
2947  {
2948  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoReRead>(rPam, *pGrfNd));
2949  }
2950 
2951  // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
2952  if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
2953  GetMirrorGrf().GetValue() )
2954  pGrfNd->SetAttr( SwMirrorGrf() );
2955 
2956  pGrfNd->ReRead( rGrfName, rFltName, pGraphic );
2958 }
2959 
2960 // Insert drawing object, which has to be already inserted in the DrawModel
2962  const SwPaM &rRg,
2963  SdrObject& rDrawObj,
2964  const SfxItemSet& rFlyAttrSet )
2965 {
2967 
2968  const SwFormatAnchor* pAnchor = nullptr;
2969  rFlyAttrSet.GetItemState( RES_ANCHOR, false, reinterpret_cast<const SfxPoolItem**>(&pAnchor) );
2970  pFormat->SetFormatAttr( rFlyAttrSet );
2971 
2972  // Didn't set the Anchor yet?
2973  // DrawObjecte must never end up in the Header/Footer!
2974  RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
2975  const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
2976 
2977  const SwNodeIndex* pChkIdx = nullptr;
2978  if ( pAnchor == nullptr )
2979  {
2980  pChkIdx = &rRg.GetPoint()->nNode;
2981  }
2982  else if ( bIsAtContent )
2983  {
2984  pChkIdx =
2985  pAnchor->GetContentAnchor() ? &pAnchor->GetContentAnchor()->nNode : &rRg.GetPoint()->nNode;
2986  }
2987 
2988  // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
2989  if( pChkIdx != nullptr
2990  && ::CheckControlLayer( &rDrawObj )
2991  && m_rDoc.IsInHeaderFooter( *pChkIdx ) )
2992  {
2993  // apply at-page anchor format
2994  eAnchorId = RndStdIds::FLY_AT_PAGE;
2995  pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
2996  }
2997  else if( pAnchor == nullptr
2998  || ( bIsAtContent
2999  && pAnchor->GetContentAnchor() == nullptr ) )
3000  {
3001  // apply anchor format
3002  SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
3003  eAnchorId = aAnch.GetAnchorId();
3004  if ( eAnchorId == RndStdIds::FLY_AT_FLY )
3005  {
3006  const SwStartNode* pStartNode = rRg.GetNode().FindFlyStartNode();
3007  assert(pStartNode);
3008  SwPosition aPos(*pStartNode);
3009  aAnch.SetAnchor( &aPos );
3010  }
3011  else
3012  {
3013  aAnch.SetAnchor( rRg.GetPoint() );
3014  if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
3015  {
3016  eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
3017  aAnch.SetType( eAnchorId );
3018  }
3019  }
3020  pFormat->SetFormatAttr( aAnch );
3021  }
3022 
3023  // insert text attribute for as-character anchored drawing object
3024  if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
3025  {
3026  bool bAnchorAtPageAsFallback = true;
3027  const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
3028  if ( rDrawObjAnchorFormat.GetContentAnchor() != nullptr )
3029  {
3030  SwTextNode* pAnchorTextNode =
3031  rDrawObjAnchorFormat.GetContentAnchor()->nNode.GetNode().GetTextNode();
3032  if ( pAnchorTextNode != nullptr )
3033  {
3034  const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->nContent.GetIndex();
3035  SwFormatFlyCnt aFormat( pFormat );
3036  pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
3037  bAnchorAtPageAsFallback = false;
3038  }
3039  }
3040 
3041  if ( bAnchorAtPageAsFallback )
3042  {
3043  OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
3044  pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
3045  }
3046  }
3047 
3048  SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
3049 
3050  // Create Frames if necessary
3052  {
3053  // create layout representation
3054  pFormat->MakeFrames();
3055  // #i42319# - follow-up of #i35635#
3056  // move object to visible layer
3057  // #i79391#
3058  if ( pContact->GetAnchorFrame() )
3059  {
3060  pContact->MoveObjToVisibleLayer( &rDrawObj );
3061  }
3062  }
3063 
3065  {
3066  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsLayFormat>(pFormat, 0, 0) );
3067  }
3068 
3070  return pFormat;
3071 }
3072 
3073 bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
3074 {
3075  SwContentNode *pNode = rPos.nNode.GetNode().GetContentNode();
3076  if(nullptr == pNode)
3077  return false;
3078 
3079  {
3080  // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
3081  // After that they can be before/after the position.
3082  SwDataChanged aTmp( m_rDoc, rPos );
3083  }
3084 
3085  SwUndoSplitNode* pUndo = nullptr;
3087  {
3089  // insert the Undo object (currently only for TextNode)
3090  if( pNode->IsTextNode() )
3091  {
3092  pUndo = new SwUndoSplitNode( m_rDoc, rPos, bChkTableStart );
3093  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3094  }
3095  }
3096 
3097  // Update the rsid of the old and the new node unless
3098  // the old node is split at the beginning or at the end
3099  SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
3100  const sal_Int32 nPos = rPos.nContent.GetIndex();
3101  if( pTextNode && nPos && nPos != pTextNode->Len() )
3102  {
3103  m_rDoc.UpdateParRsid( pTextNode );
3104  }
3105 
3106  //JP 28.01.97: Special case for SplitNode at table start:
3107  // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
3108  // then insert a paragraph before it.
3109  if( bChkTableStart && !rPos.nContent.GetIndex() && pNode->IsTextNode() )
3110  {
3111  sal_uLong nPrevPos = rPos.nNode.GetIndex() - 1;
3112  const SwTableNode* pTableNd;
3113  const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
3114  if( pNd->IsStartNode() &&
3115  SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
3116  nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
3117  ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
3118  SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
3119  || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
3120  || pNd->IsContentNode() ))
3121  {
3122  if( pNd->IsContentNode() )
3123  {
3124  //JP 30.04.99 Bug 65660:
3125  // There are no page breaks outside of the normal body area,
3126  // so this is not a valid condition to insert a paragraph.
3127  if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
3128  pNd = nullptr;
3129  else
3130  {
3131  // Only if the table has page breaks!
3132  const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
3133  if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
3134  SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
3135  pNd = nullptr;
3136  }
3137  }
3138 
3139  if( pNd )
3140  {
3141  SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
3142  SwNodeIndex( *pTableNd ),
3144  if( pTextNd )
3145  {
3146  const_cast<SwPosition&>(rPos).nNode = pTableNd->GetIndex()-1;
3147  const_cast<SwPosition&>(rPos).nContent.Assign( pTextNd, 0 );
3148 
3149  // only add page breaks/styles to the body area
3150  if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
3151  {
3152  SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
3153  const SfxPoolItem *pItem;
3154  if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
3155  false, &pItem ) )
3156  {
3157  pTextNd->SetAttr( *pItem );
3158  pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
3159  }
3160  if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
3161  false, &pItem ) )
3162  {
3163  pTextNd->SetAttr( *pItem );
3164  pFrameFormat->ResetFormatAttr( RES_BREAK );
3165  }
3166  }
3167 
3168  if( pUndo )
3169  pUndo->SetTableFlag();
3171  return true;
3172  }
3173  }
3174  }
3175  }
3176 
3177  const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
3178  pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
3179  assert(pNode->IsTextNode());
3180  std::function<void (SwTextNode *, sw::mark::RestoreMode)> restoreFunc(
3181  [&](SwTextNode *const, sw::mark::RestoreMode const eMode)
3182  {
3183  if (!pContentStore->Empty())
3184  { // move all bookmarks, TOXMarks, FlyAtCnt
3185  pContentStore->Restore(m_rDoc, rPos.nNode.GetIndex()-1, 0, true, eMode);
3186  }
3187  if (eMode & sw::mark::RestoreMode::NonFlys)
3188  {
3189  // To-Do - add 'SwExtraRedlineTable' also ?
3193  {
3194  SwPaM aPam( rPos );
3195  aPam.SetMark();
3196  aPam.Move( fnMoveBackward );
3198  {
3200  new SwRangeRedline(RedlineType::Insert, aPam), true);
3201  }
3202  else
3203  {
3205  }
3206  }
3207  }
3208  });
3209  pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc);
3210 
3212  return true;
3213 }
3214 
3216 {
3217  // create new node before EndOfContent
3218  SwTextNode * pCurNode = rPos.nNode.GetNode().GetTextNode();
3219  if( !pCurNode )
3220  {
3221  // so then one can be created!
3222  SwNodeIndex aIdx( rPos.nNode, 1 );
3223  pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx,
3225  }
3226  else
3227  pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
3228 
3229  rPos.nNode++;
3230  rPos.nContent.Assign( pCurNode, 0 );
3231 
3233  {
3234  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsert>( rPos.nNode ) );
3235  }
3236 
3237  // To-Do - add 'SwExtraRedlineTable' also ?
3239  {
3240  SwPaM aPam( rPos );
3241  aPam.SetMark();
3242  aPam.Move( fnMoveBackward );
3244  m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
3245  else
3247  }
3248 
3250  return true;
3251 }
3252 
3253 bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
3254  const bool bRegExReplace )
3255 {
3256  // unfortunately replace works slightly differently from delete,
3257  // so we cannot use lcl_DoWithBreaks here...
3258 
3259  std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
3260 
3261  SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
3262  aPam.Normalize(false);
3263  if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
3264  {
3265  aPam.Move(fnMoveBackward);
3266  }
3267  OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
3268 
3269  sw::CalcBreaks(Breaks, aPam);
3270 
3271  while (!Breaks.empty() // skip over prefix of dummy chars
3272  && (aPam.GetMark()->nNode.GetIndex() == Breaks.begin()->first)
3273  && (aPam.GetMark()->nContent.GetIndex() == Breaks.begin()->second))
3274  {
3275  // skip!
3276  ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
3277  Breaks.erase(Breaks.begin());
3278  }
3279  *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
3280 
3281  if (Breaks.empty())
3282  {
3283  // park aPam somewhere so it does not point to node that is deleted
3284  aPam.DeleteMark();
3286  return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
3287  }
3288 
3289  // Deletion must be split into several parts if the text node
3290  // contains a text attribute with end and with dummy character
3291  // and the selection does not contain the text attribute completely,
3292  // but overlaps its start (left), where the dummy character is.
3293 
3294  bool bRet( true );
3295  // iterate from end to start, to avoid invalidating the offsets!
3296  auto iter( Breaks.rbegin() );
3297  sal_uLong nOffset(0);
3298  SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
3299  OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
3300  SwPosition & rEnd( *aPam.End() );
3301  SwPosition & rStart( *aPam.Start() );
3302 
3303  // set end of temp pam to original end (undo Move backward above)
3304  rEnd = *rPam.End();
3305  // after first deletion, rEnd will point into the original text node again!
3306 
3307  while (iter != Breaks.rend())
3308  {
3309  rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
3310  if (rStart < rEnd) // check if part is empty
3311  {
3314  : DeleteAndJoinImpl(aPam, false);
3315  nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
3316  }
3317  rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
3318  ++iter;
3319  }
3320 
3321  rStart = *rPam.Start(); // set to original start
3322  assert(rStart < rEnd && "replace part empty!");
3323  if (rStart < rEnd) // check if part is empty
3324  {
3325  bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
3326  }
3327 
3328  rPam = aPam; // update original pam (is this required?)
3329 
3330  return bRet;
3331 }
3332 
3335  const SwPaM &rRg,
3336  const SfxPoolItem &rHt,
3337  const SetAttrMode nFlags,
3338  SwRootFrame const*const pLayout,
3339  const bool bExpandCharToPara,
3340  SwTextAttr **ppNewTextAttr)
3341 {
3343  return false;
3344 
3345  SwDataChanged aTmp( rRg );
3346  std::unique_ptr<SwUndoAttr> pUndoAttr;
3348  {
3350  pUndoAttr.reset(new SwUndoAttr( rRg, rHt, nFlags ));
3351  }
3352 
3353  SfxItemSet aSet( m_rDoc.GetAttrPool(), {{rHt.Which(), rHt.Which()}} );
3354  aSet.Put( rHt );
3355  const bool bRet = lcl_InsAttr(m_rDoc, rRg, aSet, nFlags, pUndoAttr.get(), pLayout, bExpandCharToPara, ppNewTextAttr);
3356 
3358  {
3359  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
3360  }
3361 
3362  if( bRet )
3363  {
3365  }
3366  return bRet;
3367 }
3368 
3370  const SetAttrMode nFlags, SwRootFrame const*const pLayout)
3371 {
3372  SwDataChanged aTmp( rRg );
3373  std::unique_ptr<SwUndoAttr> pUndoAttr;
3375  {
3377  pUndoAttr.reset(new SwUndoAttr( rRg, rSet, nFlags ));
3378  }
3379 
3380  bool bRet = lcl_InsAttr(m_rDoc, rRg, rSet, nFlags, pUndoAttr.get(), pLayout, /*bExpandCharToPara*/false, /*ppNewTextAttr*/nullptr );
3381 
3383  {
3384  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
3385  }
3386 
3387  if( bRet )
3389 }
3390 
3392 {
3393  const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
3394  if ( !pTNd )
3395  return;
3396 
3397  const OUString& rText = pTNd->GetText();
3398  sal_Int32 nIdx = 0;
3399  while (nIdx < rText.getLength())
3400  {
3401  sal_Unicode const cCh = rText[nIdx];
3402  if (('\t' != cCh) && (' ' != cCh))
3403  {
3404  break;
3405  }
3406  ++nIdx;
3407  }
3408 
3409  if ( nIdx > 0 )
3410  {
3411  SwPaM aPam(rPos);
3412  aPam.GetPoint()->nContent = 0;
3413  aPam.SetMark();
3414  aPam.GetMark()->nContent = nIdx;
3415  DeleteRange( aPam );
3416  }
3417 }
3418 
3419 // Copy method from SwDoc - "copy Flys in Flys"
3423  const SwNodeRange& rRg,
3424  const SwNodeIndex& rInsPos,
3425  const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
3426  const bool bMakeNewFrames,
3427  const bool bDelRedlines,
3428  const bool bCopyFlyAtFly,
3429  SwCopyFlags const flags) const
3430 {
3431  assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
3432  assert(!pCopiedPaM || pCopiedPaM->second.nNode <= rInsPos);
3433 
3434  SwDoc& rDest = rInsPos.GetNode().GetDoc();
3435  SwNodeIndex aSavePos( rInsPos );
3436 
3437  if (rRg.aStart != rRg.aEnd)
3438  {
3439  bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
3440  --aSavePos;
3441  SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
3442 
3443  // insert behind the already copied start node
3444  m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true );
3445  aRedlRest.Restore();
3446 
3447  if (bEndIsEqualEndPos)
3448  {
3449  const_cast<SwNodeIndex&>(rRg.aEnd).Assign(aSavePos.GetNode(), +1);
3450  }
3451  }
3452 
3453  // Also copy all bookmarks
3454  // guess this must be done before the DelDummyNodes below as that
3455  // deletes nodes so would mess up the index arithmetic
3456  // sw_fieldmarkhide: also needs to be done before making frames
3458  {
3459  SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
3460  SwPosition targetPos(SwNodeIndex(aSavePos,
3461  rRg.aStart != rRg.aEnd ? +1 : 0));
3462  if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
3463  {
3464  // there is 1 (partially selected, maybe) paragraph before
3465  assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->nNode);
3466  // only use the passed in target SwPosition if the source PaM point
3467  // is on a different node; if it was the same node then the target
3468  // position was likely moved along by the copy operation and now
3469  // points to the end of the range!
3470  targetPos = pCopiedPaM->second;
3471  }
3472 
3473  sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, targetPos);
3474  }
3475 
3476  if (rRg.aStart != rRg.aEnd)
3477  {
3478  bool isRecreateEndNode(false);
3479  if (bMakeNewFrames) // tdf#130685 only after aRedlRest
3480  { // recreate from previous node (could be merged now)
3481  if (SwTextNode *const pNode = aSavePos.GetNode().GetTextNode())
3482  {
3484  SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode();
3485  if (pEndNode)
3486  {
3488  for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
3489  {
3490  if (pFrame->getRootFrame()->HasMergedParas())
3491  {
3492  frames.insert(pFrame);
3493  }
3494  }
3495  }
3497  if (!frames.empty())
3498  { // tdf#132187 check if the end node needs new frames
3500  for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
3501  {
3502  if (pFrame->getRootFrame()->HasMergedParas())
3503  {
3504  auto const it = frames.find(pFrame);
3505  if (it != frames.end())
3506  {
3507  frames.erase(it);
3508  }
3509  }
3510  }
3511  if (!frames.empty()) // existing frame was deleted
3512  { // all layouts because MakeFrames recreates all layouts
3513  pEndNode->DelFrames(nullptr);
3514  isRecreateEndNode = true;
3515  }
3516  }
3517  }
3518  }
3519  bool const isAtStartOfSection(aSavePos.GetNode().IsStartNode());
3520  ++aSavePos;
3521  if (bMakeNewFrames)
3522  {
3523  // it's possible that CheckParaRedlineMerge() deleted frames
3524  // on rInsPos so have to include it, but it must not be included
3525  // if it was the first node in the document so that MakeFrames()
3526  // will find the existing (wasn't deleted) frame on it
3527  SwNodeIndex const end(rInsPos,
3528  (!isRecreateEndNode || isAtStartOfSection)
3529  ? 0 : +1);
3530  ::MakeFrames(&rDest, aSavePos, end);
3531  }
3532  }
3533 
3534 #if OSL_DEBUG_LEVEL > 0
3535  {
3536  //JP 17.06.99: Bug 66973 - check count only if the selection is in
3537  // the same section or there's no section, because sections that are
3538  // not fully selected are not copied.
3539  const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
3540  SwNodeIndex aTmpI( rRg.aEnd, -1 );
3541  const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
3542  if( pSSectNd == pESectNd &&
3543  !rRg.aStart.GetNode().IsSectionNode() &&
3544  !aTmpI.GetNode().IsEndNode() )
3545  {
3546  // If the range starts with a SwStartNode, it isn't copied
3547  sal_uInt16 offset = (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0;
3548  OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
3549  rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
3550  "An insufficient number of nodes were copied!" );
3551  }
3552  }
3553 #endif
3554 
3555  {
3556  ::sw::UndoGuard const undoGuard(rDest.GetIDocumentUndoRedo());
3557  CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr,
3558  // see comment below regarding use of pCopiedPaM->second
3559  (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
3560  ? pCopiedPaM->second.nNode
3561  : aSavePos,
3562  bCopyFlyAtFly,
3563  flags);
3564  }
3565 
3566  SwNodeRange aCpyRange( aSavePos, rInsPos );
3567 
3568  if( bDelRedlines && ( RedlineFlags::DeleteRedlines & rDest.getIDocumentRedlineAccess().GetRedlineFlags() ))
3569  lcl_DeleteRedlines( rRg, aCpyRange );
3570 
3571  rDest.GetNodes().DelDummyNodes( aCpyRange );
3572 }
3573 
3574 // note: for the redline Show/Hide this must be in sync with
3575 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
3577  const SwNodeRange& rRg,
3578  SwPaM const*const pCopiedPaM,
3579  const SwNodeIndex& rStartIdx,
3580  const bool bCopyFlyAtFly,
3581  SwCopyFlags const flags) const
3582 {
3583  assert(!pCopiedPaM || pCopiedPaM->End()->nNode == rRg.aEnd);
3584 
3585  // First collect all Flys, sort them according to their ordering number,
3586  // and then only copy them. This maintains the ordering numbers (which are only
3587  // managed in the DrawModel).
3588  SwDoc& rDest = rStartIdx.GetNode().GetDoc();
3589  std::set< ZSortFly > aSet;
3590  const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
3591 
3592  SwTextBoxHelper::SavedLink aOldTextBoxes;
3594 
3595  for ( size_t n = 0; n < nArrLen; ++n )
3596  {
3597  SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
3598  SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
3599  SwPosition const*const pAPos = pAnchor->GetContentAnchor();
3600  if ( !pAPos )
3601  continue;
3602  bool bAdd = false;
3603  sal_uLong nSkipAfter = pAPos->nNode.GetIndex();
3604  sal_uLong nStart = rRg.aStart.GetIndex();
3605  switch ( pAnchor->GetAnchorId() )
3606  {
3607  case RndStdIds::FLY_AT_FLY:
3608  if(bCopyFlyAtFly)
3609  ++nSkipAfter;
3611  ++nStart;
3612  break;
3613  case RndStdIds::FLY_AT_PARA:
3614  {
3615  bAdd = IsSelectFrameAnchoredAtPara(*pAPos,
3616  pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
3617  pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
3618  (flags & SwCopyFlags::IsMoveToFly)
3621  }
3622  break;
3623  case RndStdIds::FLY_AT_CHAR:
3624  {
3625  bAdd = IsDestroyFrameAnchoredAtChar(*pAPos,
3626  pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
3627  pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
3628  (flags & SwCopyFlags::IsMoveToFly)
3631  }
3632  break;
3633  default:
3634  continue;
3635  }
3636  if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
3637  {
3638  if (nStart > nSkipAfter)
3639  continue;
3640  if (pAPos->nNode > rRg.aEnd)
3641  continue;
3642  //frames at the last source node are not always copied:
3643  //- if the node is empty and is the last node of the document or a table cell
3644  // or a text frame then they have to be copied
3645  //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
3646  //- to-character bound objects are copied if their index is <= nEndContentIndex
3647  if (pAPos->nNode < rRg.aEnd)
3648  bAdd = true;
3649  if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
3650  {
3651  if (!bAdd)
3652  {
3653  // technically old code checked nContent of AT_FLY which is pointless
3654  bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->nContent.GetIndex();
3655  }
3656  }
3657  }
3658  if( bAdd )
3659  {
3660  aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
3661  }
3662  }
3663 
3664  // Store all copied (and also the newly created) frames in another array.
3665  // They are stored as matching the originals, so that we will be later
3666  // able to build the chains accordingly.
3667  std::vector< SwFrameFormat* > aVecSwFrameFormat;
3668  std::set< ZSortFly >::const_iterator it=aSet.begin();
3669 
3670  while (it != aSet.end())
3671  {
3672  // #i59964#
3673  // correct determination of new anchor position
3674  SwFormatAnchor aAnchor( *(*it).GetAnchor() );
3675  assert( aAnchor.GetContentAnchor() != nullptr );
3676  SwPosition newPos = *aAnchor.GetContentAnchor();
3677  // for at-paragraph and at-character anchored objects the new anchor
3678  // position can *not* be determined by the difference of the current
3679  // anchor position to the start of the copied range, because not
3680  // complete selected sections in the copied range aren't copied - see
3681  // method <SwNodes::CopyNodes(..)>.
3682  // Thus, the new anchor position in the destination document is found
3683  // by counting the text nodes.
3684  if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
3685  (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
3686  {
3687  // First, determine number of anchor text node in the copied range.
3688  // Note: The anchor text node *have* to be inside the copied range.
3689  sal_uLong nAnchorTextNdNumInRange( 0 );
3690  bool bAnchorTextNdFound( false );
3691  // start at the first node for which flys are copied
3692  SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->nNode : rRg.aStart);
3693  while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
3694  {
3695  if ( aIdx.GetNode().IsTextNode() )
3696  {
3697  ++nAnchorTextNdNumInRange;
3698  bAnchorTextNdFound = aAnchor.GetContentAnchor()->nNode == aIdx;
3699  }
3700 
3701  ++aIdx;
3702  }
3703 
3704  if ( !bAnchorTextNdFound )
3705  {
3706  // This case can *not* happen, but to be robust take the first
3707  // text node in the destination document.
3708  OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
3709  nAnchorTextNdNumInRange = 1;
3710  }
3711  // Second, search corresponding text node in destination document
3712  // by counting forward from start insert position <rStartIdx> the
3713  // determined number of text nodes.
3714  aIdx = rStartIdx;
3715  SwNodeIndex aAnchorNdIdx( rStartIdx );
3716  const SwNode& aEndOfContentNd =
3717  aIdx.GetNode().GetNodes().GetEndOfContent();
3718  while ( nAnchorTextNdNumInRange > 0 &&
3719  &(aIdx.GetNode()) != &aEndOfContentNd )
3720  {
3721  if ( aIdx.GetNode().IsTextNode() )
3722  {
3723  --nAnchorTextNdNumInRange;
3724  aAnchorNdIdx = aIdx;
3725  }
3726 
3727  ++aIdx;
3728  }
3729  if ( !aAnchorNdIdx.GetNode().IsTextNode() )
3730  {
3731  // This case can *not* happen, but to be robust take the first
3732  // text node in the destination document.
3733  OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
3734  aAnchorNdIdx = rStartIdx;
3735  while ( !aAnchorNdIdx.GetNode().IsTextNode() )
3736  {
3737  ++aAnchorNdIdx;
3738  }
3739  }
3740  // apply found anchor text node as new anchor position
3741  newPos.nNode = aAnchorNdIdx;
3742  }
3743  else
3744  {
3745  tools::Long nOffset = newPos.nNode.GetIndex() - rRg.aStart.GetIndex();
3746  SwNodeIndex aIdx( rStartIdx, nOffset );
3747  newPos.nNode = aIdx;
3748  }
3749  // Set the character bound Flys back at the original character
3750  if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
3751  newPos.nNode.GetNode().IsTextNode() )
3752  {
3753  // only if pCopiedPaM: care about partially selected start node
3754  sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->nNode == aAnchor.GetContentAnchor()->nNode
3755  ? newPos.nContent.GetIndex() - pCopiedPaM->Start()->nContent.GetIndex()
3756  : newPos.nContent.GetIndex();
3757  newPos.nContent.Assign(newPos.nNode.GetNode().GetTextNode(), nContent);
3758  }
3759  else
3760  {
3761  newPos.nContent.Assign( nullptr, 0 );
3762  }
3763  aAnchor.SetAnchor( &newPos );
3764 
3765  // Check recursion: if copying content inside the same frame, then don't copy the format.
3766  if( &rDest == &m_rDoc )
3767  {
3768  const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
3769  const SwStartNode* pSNd;
3770  if( rContent.GetContentIdx() &&
3771  nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
3772  pSNd->GetIndex() < rStartIdx.GetIndex() &&
3773  rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
3774  {
3775  it = aSet.erase(it);
3776  continue;
3777  }
3778  }
3779 
3780  // Ignore TextBoxes, they are already handled in
3781  // sw::DocumentLayoutManager::CopyLayoutFormat().
3782  if (SwTextBoxHelper::isTextBox(it->GetFormat(), RES_FLYFRMFMT))
3783  {
3784  it = aSet.erase(it);
3785  continue;
3786  }
3787 
3788  // Copy the format and set the new anchor
3789  aVecSwFrameFormat.push_back( rDest.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
3790  aAnchor, false, true ) );
3791  ++it;
3792  }
3793 
3794  // Rebuild as much as possible of all chains that are available in the original,
3795  OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
3796  if ( aSet.size() != aVecSwFrameFormat.size() )
3797  return;
3798 
3799  size_t n = 0;
3800  for (const auto& rFlyN : aSet)
3801  {
3802  const SwFrameFormat *pFormatN = rFlyN.GetFormat();
3803  const SwFormatChain &rChain = pFormatN->GetChain();
3804  int nCnt = int(nullptr != rChain.GetPrev());
3805  nCnt += rChain.GetNext() ? 1: 0;
3806  size_t k = 0;
3807  for (const auto& rFlyK : aSet)
3808  {
3809  const SwFrameFormat *pFormatK = rFlyK.GetFormat();
3810  if ( rChain.GetPrev() == pFormatK )
3811  {
3812  ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
3813  static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
3814  --nCnt;
3815  }
3816  else if ( rChain.GetNext() == pFormatK )
3817  {
3818  ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
3819  static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
3820  --nCnt;
3821  }
3822  ++k;
3823  }
3824  ++n;
3825  }
3826 
3827  // Re-create content property of draw formats, knowing how old shapes
3828  // were paired with old fly formats (aOldTextBoxes) and that aSet is
3829  // parallel with aVecSwFrameFormat.
3830  SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes);
3831 }
3832 
3833 /*
3834  * Reset the text's hard formatting
3835  */
3840 {
3841  ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
3842  if (pPara->pLayout && pPara->pLayout->HasMergedParas()
3844  {
3845  return true; // skip hidden, since new items aren't applied
3846  }
3847  SwTextNode * pTextNode = rpNd->GetTextNode();
3848  if( pTextNode && pTextNode->GetpSwpHints() )
3849  {
3850  SwIndex aSt( pTextNode, 0 );
3851  sal_Int32 nEnd = pTextNode->Len();
3852 
3853  if( &pPara->pSttNd->nNode.GetNode() == pTextNode &&
3854  pPara->pSttNd->nContent.GetIndex() )
3855  aSt = pPara->pSttNd->nContent.GetIndex();
3856 
3857  if( &pPara->pEndNd->nNode.GetNode() == rpNd )
3858  nEnd = pPara->pEndNd->nContent.GetIndex();
3859 
3860  if( pPara->pHistory )
3861  {
3862  // Save all attributes for the Undo.
3863  SwRegHistory aRHst( *pTextNode, pPara->pHistory );
3864  pTextNode->GetpSwpHints()->Register( &aRHst );
3865  pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
3866  pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
3867  if( pTextNode->GetpSwpHints() )
3868  pTextNode->GetpSwpHints()->DeRegister();
3869  }
3870  else
3871  pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
3872  pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
3873  }
3874  return true;
3875 }
3876 
3878 {
3879 }
3880 //Private methods
3881 
3883 {
3885 
3887 
3888  if (*rPam.GetPoint() == *rPam.GetMark())
3889  {
3890  return false; // do not add empty redlines
3891  }
3892 
3893  std::vector<SwRangeRedline*> redlines;
3894  {
3895  auto pRedline(std::make_unique<SwRangeRedline>(RedlineType::Delete, rPam));
3896  if (pRedline->HasValidRange())
3897  {
3898  redlines.push_back(pRedline.release());
3899  }
3900  else // sigh ... why is such a selection even possible...
3901  { // split it up so we get one SwUndoRedlineDelete per inserted RL
3902  redlines = GetAllValidRanges(std::move(pRedline));
3903  }
3904  }
3905 
3906  if (redlines.empty())
3907  {
3908  return false;
3909  }
3910 
3911  // tdf#54819 current redlining needs also modification of paragraph style and
3912  // attributes added to the same grouped Undo
3915 
3916  auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
3917  std::vector<std::unique_ptr<SwUndo>> MarkUndos;
3918  for (auto iter = rDMA.getAnnotationMarksBegin();
3919  iter != rDMA.getAnnotationMarksEnd(); )
3920  {
3921  // tdf#111524 remove annotation marks that have their field
3922  // characters deleted
3923  SwPosition const& rEndPos((**iter).GetMarkEnd());
3924  if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
3925  {
3927  {
3928  MarkUndos.emplace_back(std::make_unique<SwUndoDeleteBookmark>(**iter));
3929  }
3930  // iter is into annotation mark vector so must be dereferenced!
3931  rDMA.deleteMark(&**iter);
3932  // this invalidates iter, have to start over...
3933  iter = rDMA.getAnnotationMarksBegin();
3934  }
3935  else
3936  { // marks are sorted by start
3937  if (*rPam.End() < (**iter).GetMarkStart())
3938  {
3939  break;
3940  }
3941  ++iter;
3942  }
3943  }
3944 
3945  // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
3946  if (*rPam.GetPoint() != *rPam.GetMark())
3948 
3949  std::vector<std::unique_ptr<SwUndoRedlineDelete>> undos;
3951  {
3952  // this should no longer happen in calls from the UI but maybe via API
3953  // (randomTest and testTdf54819 triggers it)
3954  SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
3955  "sw.core", "redlines will be moved in DeleteAndJoin");
3958 
3959  for (SwRangeRedline * pRedline : redlines)
3960  {
3961  assert(pRedline->HasValidRange());
3962  undos.emplace_back(std::make_unique<SwUndoRedlineDelete>(
3963  *pRedline, SwUndoId::DELETE));
3964  }
3965  const SwRewriter aRewriter = undos.front()->GetRewriter();
3966  // can only group a single undo action
3967  if (MarkUndos.empty() && undos.size() == 1
3969  {
3970  SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
3971  SwUndoRedlineDelete *const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete*>(pLastUndo));
3972  bool const bMerged = pUndoRedlineDel
3973  && pUndoRedlineDel->CanGrouping(*undos.front());
3974  if (!bMerged)
3975  {
3976  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(undos.front()));
3977  }
3978  undos.clear(); // prevent unmatched EndUndo
3979  }
3980  else
3981  {
3983  for (auto& it : MarkUndos)
3984  {
3985  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
3986  }
3987  for (auto & it : undos)
3988  {
3989  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
3990  }
3991  }
3992  }
3993 
3994  for (SwRangeRedline *const pRedline : redlines)
3995  {
3996  // note: 1. the pRedline can still be merged & deleted
3997  // 2. the impl. can even DeleteAndJoin the range => no plain PaM
3998  std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*pRedline->GetMark()));
3999  pCursor->SetMark();
4000  *pCursor->GetPoint() = *pRedline->GetPoint();
4002  // sw_redlinehide: 2 reasons why this is needed:
4003  // 1. it's the first redline in node => RedlineDelText was sent but ignored
4004  // 2. redline spans multiple nodes => must merge text frames
4006  }
4008 
4010  {
4011  if (!undos.empty())
4012  {
4014  }
4016  }
4017 
4020 
4021  return true;
4022 }
4023 
4025  const bool bForceJoinNext )
4026 {
4027  bool bJoinText, bJoinPrev;
4028  ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
4029  // #i100466#
4030  if ( bForceJoinNext )
4031  {
4032  bJoinPrev = false;
4033  }
4034 
4035  {
4036  bool const bSuccess( DeleteRangeImpl( rPam ) );
4037  if (!bSuccess)
4038  return false;
4039  }
4040 
4041  if( bJoinText )
4042  {
4043  ::sw_JoinText( rPam, bJoinPrev );
4044  }
4045 
4048  {
4050  }
4051 
4052  return true;
4053 }
4054 
4056 {
4057  // Move all cursors out of the deleted range, but first copy the
4058  // passed PaM, because it could be a cursor that would be moved!
4059  SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
4060  ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
4061 
4062  bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
4063  if (bSuccess)
4064  { // now copy position from temp copy to given PaM
4065  *rPam.GetPoint() = *aDelPam.GetPoint();
4066  }
4067 
4068  return bSuccess;
4069 }
4070 
4072 {
4073  SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
4074 
4075  if (!rPam.HasMark()
4076  || (*pStt == *pEnd && !IsFlySelectedByCursor(m_rDoc, *pStt, *pEnd)))
4077  {
4078  return false;
4079  }
4080 
4082  {
4083  // if necessary the saved Word for the exception
4084  if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->nNode != pEnd->nNode ||
4085  pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
4088  }
4089 
4090  {
4091  // Delete all empty TextHints at the Mark's position
4092  SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
4093  SwpHints* pHts;
4094  if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
4095  {
4096  const sal_Int32 nMkCntPos = rPam.GetMark()->nContent.GetIndex();
4097  for( size_t n = pHts->Count(); n; )
4098  {
4099  const SwTextAttr* pAttr = pHts->Get( --n );
4100  if( nMkCntPos > pAttr->GetStart() )
4101  break;
4102 
4103  const sal_Int32 *pEndIdx;
4104  if( nMkCntPos == pAttr->GetStart() &&
4105  nullptr != (pEndIdx = pAttr->End()) &&
4106  *pEndIdx == pAttr->GetStart() )
4107  pTextNd->DestroyAttr( pHts->Cut( n ) );
4108  }
4109  }
4110  }
4111 
4112  {
4113  // Send DataChanged before deletion, so that we still know
4114  // which objects are in the range.
4115  // Afterwards they could be before/after the Position.
4116  SwDataChanged aTmp( rPam );
4117  }
4118 
4120  {
4122  bool bMerged(false);
4124  {
4125  SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
4126  SwUndoDelete *const pUndoDelete(
4127  dynamic_cast<SwUndoDelete *>(pLastUndo) );
4128  if (pUndoDelete)
4129  {
4130  bMerged = pUndoDelete->CanGrouping(m_rDoc, rPam);
4131  // if CanGrouping() returns true it's already merged
4132  }
4133  }
4134  if (!bMerged)
4135  {
4136  m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelete>( rPam ) );
4137  }
4138 
4140 
4141  return true;
4142  }
4143 
4145  m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
4146 
4147  // Delete and move all "Flys at the paragraph", which are within the Selection
4148  DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode,
4149  &rPam.GetMark()->nContent, &rPam.GetPoint()->nContent);
4150  DelBookmarks(
4151  pStt->nNode,
4152  pEnd->nNode,
4153  nullptr,
4154  &pStt->nContent,
4155  &pEnd->nContent);
4156 
4157  SwNodeIndex aSttIdx( pStt->nNode );
4158  SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
4159 
4160  do { // middle checked loop!
4161  if( pCNd )
4162  {
4163  SwTextNode * pStartTextNode( pCNd->GetTextNode() );
4164  if ( pStartTextNode )
4165  {
4166  // now move the Content to the new Node
4167  bool bOneNd = pStt->nNode == pEnd->nNode;
4168  const sal_Int32 nLen = ( bOneNd ? pEnd->nContent.GetIndex()
4169  : pCNd->Len() )
4170  - pStt->nContent.GetIndex();
4171 
4172  // Don't call again, if already empty
4173  if( nLen )
4174  {
4175  pStartTextNode->EraseText( pStt->nContent, nLen );
4176 
4177  if( !pStartTextNode->Len() )
4178  {
4179  // METADATA: remove reference if empty (consider node deleted)
4180  pStartTextNode->RemoveMetadataReference();
4181  }
4182  }
4183 
4184  if( bOneNd ) // that's it
4185  break;
4186 
4187  ++aSttIdx;
4188  }
4189  else
4190  {
4191  // So that there are no indices left registered when deleted,
4192  // we remove a SwPaM from the Content here.
4193  pStt->nContent.Assign( nullptr, 0 );
4194  }
4195  }
4196 
4197  pCNd = pEnd->nNode.GetNode().GetContentNode();
4198  if( pCNd )
4199  {
4200  SwTextNode * pEndTextNode( pCNd->GetTextNode() );
4201  if( pEndTextNode )
4202  {
4203  // if already empty, don't call again
4204  if( pEnd->nContent.GetIndex() )
4205  {
4206  SwIndex aIdx( pCNd, 0 );
4207  pEndTextNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
4208 
4209  if( !pEndTextNode->Len() )
4210  {
4211  // METADATA: remove reference if empty (consider node deleted)
4212  pEndTextNode->RemoveMetadataReference();
4213  }
4214  }
4215  }
4216  else
4217  {
4218  // So that there are no indices left registered when deleted,
4219  // we remove a SwPaM from the Content here.
4220  pEnd->nContent.Assign( nullptr, 0 );
4221  }
4222  }
4223 
4224  // if the end is not a content node, delete it as well
4225  sal_uInt32 nEnd = pEnd->nNode.GetIndex();
4226  if( pCNd == nullptr )
4227  nEnd++;
4228 
4229  if( aSttIdx != nEnd )
4230  {
4231  // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
4232  SwNode *pTmpNd;
4233  while (pEnd == rPam.GetPoint()
4234  && nEnd + 2 < m_rDoc.GetNodes().Count()
4235  && (pTmpNd = m_rDoc.GetNodes()[nEnd + 1])->IsEndNode()
4236  && pTmpNd->StartOfSectionNode()->IsSectionNode()
4237  && aSttIdx <= pTmpNd->StartOfSectionNode()->GetIndex())
4238  {
4239  SwNodeRange range(*pTmpNd->StartOfSectionNode(), *pTmpNd);
4240  m_rDoc.GetNodes().SectionUp(&range);
4241  --nEnd; // account for deleted start node
4242  }
4243 
4244  // delete the Nodes from the NodesArray
4245  m_rDoc.GetNodes().Delete( aSttIdx, nEnd - aSttIdx.GetIndex() );
4246  }
4247 
4248  // If the Node that contained the Cursor has been deleted,
4249  // the Content has to be assigned to the current Content.
4250  pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
4251  pStt->nContent.GetIndex() );
4252 
4253  // If we deleted across Node boundaries we have to correct the PaM,
4254  // because they are in different Nodes now.
4255  // Also, the Selection is revoked.
4256  *pEnd = *pStt;
4257  rPam.DeleteMark();
4258 
4259  } while( false );
4260 
4262 
4263  return true;
4264 }
4265 
4266 // It's possible to call Replace with a PaM that spans 2 paragraphs:
4267 // search with regex for "$", then replace _all_
4269  const bool bRegExReplace )
4270 {
4271  if (!rPam.HasMark())
4272  return false;
4273 
4274  bool bJoinText, bJoinPrev;
4275  ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
4276 
4277  {
4278  // Create a copy of the Cursor in order to move all Pams from
4279  // the other views out of the deletion range.
4280  // Except for itself!
4281  SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
4282  ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
4283 
4284  SwPosition *pStt = aDelPam.Start(),
4285  *pEnd = aDelPam.End();
4286  bool bOneNode = pStt->nNode == pEnd->nNode;
4287 
4288  // Own Undo?
4289  OUString sRepl( rStr );
4290  SwTextNode* pTextNd = pStt->nNode.GetNode().GetTextNode();
4291  sal_Int32 nStt = pStt->nContent.GetIndex();
4292  sal_Int32 nEnd;
4293 
4294  SwDataChanged aTmp( aDelPam );
4295 
4297  {
4300  {
4301  // this should no longer happen in calls from the UI but maybe via API
4302  SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
4303  "sw.core", "redlines will be moved in ReplaceRange");
4304 
4306 
4307  // If any Redline will change (split!) the node
4308  const ::sw::mark::IMark* pBkmk =
4312 
4315 
4316  *aDelPam.GetPoint() = pBkmk->GetMarkPos();
4317  if(pBkmk->IsExpanded())
4318  *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
4320  pStt = aDelPam.Start();
4321  pTextNd = pStt->nNode.GetNode().GetTextNode();
4322  nStt = pStt->nContent.GetIndex();
4323  }
4324 
4325  if( !sRepl.isEmpty() )
4326  {
4327  // Apply the first character's attributes to the ReplaceText
4328  SfxItemSet aSet( m_rDoc.GetAttrPool(),
4331  pTextNd->GetParaAttr( aSet, nStt+1, nStt+1 );
4332 
4333  aSet.ClearItem( RES_TXTATR_REFMARK );
4334  aSet.ClearItem( RES_TXTATR_TOXMARK );
4335  aSet.ClearItem( RES_TXTATR_CJK_RUBY );
4336  aSet.ClearItem( RES_TXTATR_INETFMT );
4337  aSet.ClearItem( RES_TXTATR_META );
4338  aSet.ClearItem( RES_TXTATR_METAFIELD );
4339 
4340  if( aDelPam.GetPoint() != aDelPam.End() )
4341  aDelPam.Exchange();
4342 
4343  // Remember the End
4344  SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
4345  const sal_Int32 nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
4346 
4347  bool bFirst = true;
4348  OUString sIns;
4349  while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
4350  {
4351  InsertString( aDelPam, sIns );
4352  if( bFirst )
4353  {
4354  SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
4355  const sal_Int32 nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
4356 
4357  SplitNode( *aDelPam.GetPoint(), false );
4358 
4359  ++aMkNd;
4360  aDelPam.GetMark()->nNode = aMkNd;
4361  aDelPam.GetMark()->nContent.Assign(
4362  aMkNd.GetNode().GetContentNode(), nMkCnt );
4363  bFirst = false;
4364  }
4365  else
4366  SplitNode( *aDelPam.GetPoint(), false );
4367  }
4368  if( !sIns.isEmpty() )
4369  {
4370  InsertString( aDelPam, sIns );
4371  }
4372 
4373  SwPaM aTmpRange( *aDelPam.GetPoint() );
4374  aTmpRange.SetMark();
4375 
4376  ++aPtNd;
4377  aDelPam.GetPoint()->nNode = aPtNd;
4378  aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
4379  nPtCnt);
4380  *aTmpRange.GetMark() = *aDelPam.GetPoint();
4381 
4382  m_rDoc.RstTextAttrs( aTmpRange );
4383  InsertItemSet( aTmpRange, aSet );
4384  }
4385 
4387  {
4389  std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE ));
4390  }
4391  m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true);
4392 
4393  *rPam.GetMark() = *aDelPam.GetMark();
4395  {
4396  *aDelPam.GetPoint() = *rPam.GetPoint();
4398 
4399  // If any Redline will change (split!) the node
4400  const ::sw::mark::IMark* pBkmk =
4404 
4405  SwIndex& rIdx = aDelPam.GetPoint()->nContent;
4406  rIdx.Assign( nullptr, 0 );
4407  aDelPam.GetMark()->nContent = rIdx;
4408  rPam.GetPoint()->nNode = 0;
4409  rPam.GetPoint()->nContent = rIdx;
4410  *rPam.GetMark() = *rPam.GetPoint();
4412 
4413  *rPam.GetPoint() = pBkmk->GetMarkPos();
4414  if(pBkmk->IsExpanded())
4415  *rPam.GetMark() = pBkmk->GetOtherMarkPos();
4417  }
4418  bJoinText = false;
4419  }
4420  else
4421  {
4422  assert((pStt->nNode == pEnd->nNode ||
4423  ( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
4424  !pEnd->nContent.GetIndex() )) &&
4425  "invalid range: Point and Mark on different nodes" );
4426 
4428  m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, RedlineType::Any );
4429 
4430  SwUndoReplace* pUndoRpl = nullptr;
4431  bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
4432  if (bDoesUndo)
4433  {
4434  pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
4435  m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoRpl));
4436  }
4437  ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
4438 
4439  if( aDelPam.GetPoint() != pStt )
4440  aDelPam.Exchange();
4441 
4442  SwNodeIndex aPtNd( pStt->nNode, -1 );
4443  const sal_Int32 nPtCnt = pStt->nContent.GetIndex();
4444 
4445  // Set the values again, if Frames or footnotes on the Text have been removed.
4446  nStt = nPtCnt;
4447  nEnd = bOneNode ? pEnd->nContent.GetIndex()
4448  : pTextNd->GetText().getLength();
4449 
4450  bool bFirst = true;
4451  OUString sIns;
4452  while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
4453  {
4454  if (!bFirst || nStt == pTextNd->GetText().getLength())
4455  {
4456  InsertString( aDelPam, sIns );
4457  }
4458  else if( nStt < nEnd || !sIns.isEmpty() )
4459  {
4460  pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4461  }
4462  SplitNode( *pStt, false);
4463  bFirst = false;
4464  }
4465 
4466  if( bFirst || !sIns.isEmpty() )
4467  {
4468  if (!bFirst || nStt == pTextNd->GetText().getLength())
4469  {
4470  InsertString( aDelPam, sIns );
4471  }
4472  else if( nStt < nEnd || !sIns.isEmpty() )
4473  {
4474  pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4475  }
4476  }
4477 
4478  *rPam.GetPoint() = *aDelPam.GetMark();
4479  ++aPtNd;
4480  rPam.GetMark()->nNode = aPtNd;
4481  rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
4482  nPtCnt );
4483 
4484  if (bJoinText)
4485  {
4486  assert(rPam.GetPoint() == rPam.End());
4487  // move so that SetEnd remembers position after sw_JoinText
4488  rPam.Move(fnMoveBackward);
4489  }
4490  else if (aDelPam.GetPoint() == pStt) // backward selection?
4491  {
4492  assert(*rPam.GetMark() <= *rPam.GetPoint());
4493  rPam.Exchange(); // swap so that rPam is backwards
4494  }
4495 
4496  if( pUndoRpl )
4497  {
4498  pUndoRpl->SetEnd(rPam);
4499  }
4500  }
4501  }
4502 
4503  bool bRet(true);
4504  if (bJoinText)
4505  {
4506  bRet = ::sw_JoinText(rPam, bJoinPrev);
4507  }
4508 
4510  return bRet;
4511 }
4512 
4514  const SfxItemSet* pFlyAttrSet,
4515  const SfxItemSet* pGrfAttrSet,
4516  SwFrameFormat* pFrameFormat)
4517 {
4518  SwFlyFrameFormat *pFormat = nullptr;
4519  if( pNode )
4520  {
4521  pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
4522  pFlyAttrSet, pFrameFormat );
4523  if( pGrfAttrSet )
4524  pNode->SetAttr( *pGrfAttrSet );
4525  }
4526  return pFormat;
4527 }
4528 
4529 #define NUMRULE_STATE \
4530  SfxItemState aNumRuleState = SfxItemState::UNKNOWN; \
4531  std::shared_ptr<SwNumRuleItem> aNumRuleItem; \
4532  SfxItemState aListIdState = SfxItemState::UNKNOWN; \
4533  std::shared_ptr<SfxStringItem> aListIdItem; \
4534 
4535 #define PUSH_NUMRULE_STATE \
4536  lcl_PushNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd );
4537 
4538 #define POP_NUMRULE_STATE \
4539  lcl_PopNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd, rPam );
4540 
4542  SfxItemState &aNumRuleState, std::shared_ptr<SwNumRuleItem>& aNumRuleItem,
4543  SfxItemState &aListIdState, std::shared_ptr<SfxStringItem>& aListIdItem,
4544  const SwTextNode *pDestTextNd )
4545 {
4546  // Safe numrule item at destination.
4547  // #i86492# - Safe also <ListId> item of destination.
4548  const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
4549  if (pAttrSet == nullptr)
4550  return;
4551 
4552  const SfxPoolItem * pItem = nullptr;
4553  aNumRuleState = pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem);
4554  if (SfxItemState::SET == aNumRuleState)
4555  {
4556  aNumRuleItem.reset(&pItem->Clone()->StaticWhichCast(RES_PARATR_NUMRULE));
4557  }
4558 
4559  aListIdState = pAttrSet->GetItemState(RES_PARATR_LIST_ID, false, &pItem);
4560  if (SfxItemState::SET == aListIdState)
4561  {
4562  aListIdItem.reset(&pItem->Clone()->StaticWhichCast(RES_PARATR_LIST_ID));
4563  }
4564 }
4565 
4567  SfxItemState aNumRuleState, const std::shared_ptr<SwNumRuleItem>& aNumRuleItem,
4568  SfxItemState aListIdState, const std::shared_ptr<SfxStringItem>& aListIdItem,
4569  SwTextNode *pDestTextNd, const SwPaM& rPam )
4570 {
4571  /* If only a part of one paragraph is copied
4572  restore the numrule at the destination. */
4573  // #i86492# - restore also <ListId> item
4574  if ( lcl_MarksWholeNode(rPam) )
4575  return;
4576 
4577  if (SfxItemState::SET == aNumRuleState)
4578  {
4579  pDestTextNd->SetAttr(*aNumRuleItem);
4580  }
4581  else
4582  {
4583  pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
4584  }
4585  if (SfxItemState::SET == aListIdState)
4586  {
4587  pDestTextNd->SetAttr(*aListIdItem);
4588  }
4589  else
4590  {
4591  pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
4592  }
4593 }
4594 
4596  SwCopyFlags const flags,
4597  SwPaM *const pCopyRange) const
4598 {
4599  std::vector<std::pair<sal_uLong, sal_Int32>> Breaks;
4600 
4601  sw::CalcBreaks(Breaks, rPam, true);
4602 
4603  if (Breaks.empty())
4604  {
4605  return CopyImplImpl(rPam, rPos, flags, pCopyRange);
4606  }
4607 
4608  SwPosition const & rSelectionEnd( *rPam.End() );
4609 
4610  bool bRet(true);
4611  bool bFirst(true);
4612  // iterate from end to start, ... don't think it's necessary here?
4613  auto iter( Breaks.rbegin() );
4614  sal_uLong nOffset(0);
4615  SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
4616  SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
4617  SwPosition & rEnd( *aPam.End() );
4618  SwPosition & rStart( *aPam.Start() );
4619  SwPaM copyRange(rPos, rPos);
4620 
4621  while (iter != Breaks.rend())
4622  {
4623  rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
4624  if (rStart < rEnd) // check if part is empty
4625  {
4626  // pass in copyRange member as rPos; should work ...
4627  bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
4628  nOffset = iter->first - rStart.nNode.GetIndex(); // fly nodes...
4629  if (pCopyRange)
4630  {
4631  if (bFirst)
4632  {
4633  pCopyRange->SetMark();
4634  *pCopyRange->GetMark() = *copyRange.End();
4635  }
4636  *pCopyRange->GetPoint() = *copyRange.Start();
4637  }
4638  bFirst = false;
4639  }
4640  rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
4641  ++iter;
4642  }
4643 
4644  rStart = *rPam.Start(); // set to original start
4645  if (rStart < rEnd) // check if part is empty
4646  {
4647  bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
4648  if (pCopyRange)
4649  {
4650  if (bFirst)
4651  {
4652  pCopyRange->SetMark();
4653  *pCopyRange->GetMark() = *copyRange.End();
4654  }
4655  *pCopyRange->GetPoint() = *copyRange.Start();
4656  }
4657  }
4658 
4659  return bRet;
4660 }
4661 
4663  SwCopyFlags const flags,
4664  SwPaM *const pCpyRange) const
4665 {
4666  SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
4667  const bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
4668 
4669  SwPosition const*const pStt = rPam.Start();
4670  SwPosition *const pEnd = rPam.End();
4671 
4672  // Catch when there's no copy to do.
4673  if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel) ||
4674  //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
4675  //JP 15.11.2001: don't test inclusive the end, ever exclusive
4676  ( &rDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
4677  {
4678  return false;
4679  }
4680 
4681  const bool bEndEqualIns = &rDoc == &m_rDoc && rPos == *pEnd;
4682 
4683  // If Undo is enabled, create the UndoCopy object
4684  SwUndoCpyDoc* pUndo = nullptr;
4685  // lcl_DeleteRedlines may delete the start or end node of the cursor when
4686  // removing the redlines so use cursor that is corrected by PaMCorrAbs
4687  std::shared_ptr<SwUnoCursor> const pCopyPam(rDoc.CreateUnoCursor(rPos));
4688 
4689  SwTableNumFormatMerge aTNFM( m_rDoc, rDoc );
4690  std::unique_ptr<std::vector<SwFrameFormat*>> pFlys;
4691  std::vector<SwFrameFormat*> const* pFlysAtInsPos;
4692 
4693  if (rDoc.GetIDocumentUndoRedo().DoesUndo())
4694  {
4695  pUndo = new SwUndoCpyDoc(*pCopyPam);
4696  pFlysAtInsPos = pUndo->GetFlysAnchoredAt();
4697  }
4698  else
4699  {
4700  pFlys = sw::GetFlysAnchoredAt(rDoc, rPos.nNode.GetIndex());
4701  pFlysAtInsPos = pFlys.get();
4702  }
4703 
4706 
4707  // Move the PaM one node back from the insert position, so that
4708  // the position doesn't get moved
4709  pCopyPam->SetMark();
4710  bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
4711  // If the position was shifted from more than one node, an end node has been skipped
4712  bool bAfterTable = false;
4713  if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > 1)
4714  {
4715  // First go back to the original place
4716  pCopyPam->GetPoint()->nNode = rPos.nNode;
4717  pCopyPam->GetPoint()->nContent = rPos.nContent;
4718 
4719  bCanMoveBack = false;
4720  bAfterTable = true;
4721  }
4722  if( !bCanMoveBack )
4723  {
4724  pCopyPam->GetPoint()->nNode--;
4725  assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0);
4726  }
4727 
4728  SwNodeRange aRg( pStt->nNode, pEnd->nNode );
4729  SwNodeIndex aInsPos( rPos.nNode );
4730  const bool bOneNode = pStt->nNode == pEnd->nNode;
4731  SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode();
4732  SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4733  SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
4734  bool bCopyCollFormat = !rDoc.IsInsOnlyTextGlossary() &&
4735  ( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
4736  ( !bOneNode && !rPos.nContent.GetIndex() ) );
4737  bool bCopyBookmarks = true;
4738  bool bCopyPageSource = false;
4739  int nDeleteTextNodes = 0;
4740 
4741  // #i104585# copy outline num rule to clipboard (for ASCII filter)
4742  if (rDoc.IsClipBoard() && m_rDoc.GetOutlineNumRule())
4743  {
4745  }
4746 
4747  // #i86492#
4748  // Correct the search for a previous list:
4749  // First search for non-outline numbering list. Then search for non-outline
4750  // bullet list.
4751  // Keep also the <ListId> value for possible propagation.
4752  OUString aListIdToPropagate;
4753  const SwNumRule* pNumRuleToPropagate =
4754  rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true );
4755  if ( !pNumRuleToPropagate )
4756  {
4757  pNumRuleToPropagate =
4758  rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true );
4759  }
4760  // #i86492#
4761  // Do not propagate previous found list, if
4762  // - destination is an empty paragraph which is not in a list and
4763  // - source contains at least one paragraph which is not in a list
4764  if ( pNumRuleToPropagate &&
4765  pDestTextNd && !pDestTextNd->GetText().getLength() &&
4766  !pDestTextNd->IsInList() &&
4767  !lcl_ContainsOnlyParagraphsInList( rPam ) )
4768  {
4769  pNumRuleToPropagate = nullptr;
4770  }
4771 
4772  // This do/while block is only there so that we can break out of it!
4773  do {
4774  if( pSttTextNd )
4775  {
4776  ++nDeleteTextNodes; // must be joined in Undo
4777  // Don't copy the beginning completely?
4778  if( !bCopyCollFormat || bColumnSel || pStt->nContent.GetIndex() )
4779  {
4780  SwIndex aDestIdx( rPos.nContent );
4781  bool bCopyOk = false;
4782  if( !pDestTextNd )
4783  {
4784  if( pStt->nContent.GetIndex() || bOneNode )
4785  pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
4787  else
4788  {
4789  pDestTextNd = pSttTextNd->MakeCopy(rDoc, aInsPos, true)->GetTextNode();
4790  bCopyOk = true;
4791  }
4792  aDestIdx.Assign( pDestTextNd, 0 );
4793  bCopyCollFormat = true;
4794  }
4795  else if( !bOneNode || bColumnSel )
4796  {
4797  const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
4798  {
4799  ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
4800  rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
4801  }
4802 
4803  if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
4804  {
4805  // after the SplitNode, span the CpyPam correctly again
4806  pCopyPam->Move( fnMoveBackward, GoInContent );
4807  pCopyPam->Move( fnMoveBackward, GoInContent );
4808  }
4809 
4810  pDestTextNd = rDoc.GetNodes()[ aInsPos.GetIndex()-1 ]->GetTextNode();
4811  aDestIdx.Assign(
4812  pDestTextNd, pDestTextNd->GetText().getLength());
4813 
4814  // Correct the area again
4815  if( bEndEqualIns )
4816  {
4817  bool bChg = pEnd != rPam.GetPoint();
4818  if( bChg )
4819  rPam.Exchange();
4820  rPam.Move( fnMoveBackward, GoInContent );
4821  if( bChg )
4822  rPam.Exchange();
4823  }
4824  else if( rPos == *pEnd )
4825  {
4826  // The end was also moved
4827  pEnd->nNode--;
4828  pEnd->nContent.Assign( pDestTextNd, nContentEnd );
4829  }
4830  // tdf#63022 always reset pEndTextNd after SplitNode
4831  aRg.aEnd = pEnd->nNode;
4832  pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4833  }
4834 
4836  if( bCopyCollFormat && bOneNode )
4837  {
4839  }
4840 
4841  if( !bCopyOk )
4842  {
4843  const sal_Int32 nCpyLen = ( bOneNode
4844  ? pEnd->nContent.GetIndex()
4845  : pSttTextNd->GetText().getLength())
4846  - pStt->nContent.GetIndex();
4847  pSttTextNd->CopyText( pDestTextNd, aDestIdx,
4848  pStt->nContent, nCpyLen );
4849  if( bEndEqualIns )
4850  pEnd->nContent -= nCpyLen;
4851  }
4852 
4853  aRg.aStart++;
4854 
4855  if( bOneNode )
4856  {
4857  if (bCopyCollFormat)
4858  {
4859  // tdf#138897 no Undo for applying style, SwUndoInserts does it
4860  pSttTextNd->CopyCollFormat(*pDestTextNd, false);
4862  }
4863 
4864  // copy at-char flys in rPam
4865  SwNodeIndex temp(*pDestTextNd); // update to new (start) node for flys
4866  // tdf#126626 prevent duplicate Undos
4867  ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
4868  CopyFlyInFlyImpl(aRg, &rPam, temp, false);
4869 
4870  break;
4871  }
4872  }
4873  }
4874  else if( pDestTextNd )
4875  {
4876  // Problems with insertion of table selections into "normal" text solved.
4877  // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
4878  // the undo operation will try to merge this node after removing the table.
4879  // If we didn't split a textnode, the PaM should start at the inserted table node
4880  if( rPos.nContent.GetIndex() == pDestTextNd->Len() )
4881  { // Insertion at the last position of a textnode (empty or not)
4882  ++aInsPos; // The table will be inserted behind the text node
4883  }
4884  else if( rPos.nContent.GetIndex() )
4885  { // Insertion in the middle of a text node, it has to be split
4886  // (and joined from undo)
4887  ++nDeleteTextNodes;
4888 
4889  const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
4890  {
4891  ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
4892  rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
4893  }
4894 
4895  if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
4896  {
4897  // after the SplitNode, span the CpyPam correctly again
4898  pCopyPam->Move( fnMoveBackward, GoInContent );
4899  pCopyPam->Move( fnMoveBackward, GoInContent );
4900  }
4901 
4902  // Correct the area again
4903  if( bEndEqualIns )
4904  aRg.aEnd--;
4905  // The end would also be moved
4906  else if( rPos == *pEnd )
4907  {
4908  rPos.nNode-=2;
4909  rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
4910  nContentEnd );
4911  rPos.nNode++;
4912  aRg.aEnd--;
4913  }
4914  }
4915  else if( bCanMoveBack )
4916  { // Insertion at the first position of a text node. It will not be split, the table
4917  // will be inserted before the text node.
4918  // See below, before the SetInsertRange function of the undo object will be called,
4919  // the CpyPam would be moved to the next content position. This has to be avoided
4920  // We want to be moved to the table node itself thus we have to set bCanMoveBack
4921  // and to manipulate pCopyPam.
4922  bCanMoveBack = false;
4923  pCopyPam->GetPoint()->nNode--;
4924  }
4925  }
4926 
4927  pDestTextNd = aInsPos.GetNode().GetTextNode();
4928  if (pEndTextNd)
4929  {
4930  SwIndex aDestIdx( rPos.nContent );
4931  if( !pDestTextNd )
4932  {
4933  pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
4935  aDestIdx.Assign( pDestTextNd, 0 );
4936  aInsPos--;
4937 
4938  // if we have to insert an extra text node
4939  // at the destination, this node will be our new destination
4940  // (text) node, and thus we increment nDeleteTextNodes. This
4941  // will ensure that this node will be deleted during Undo.
4942  ++nDeleteTextNodes; // must be deleted
4943  }
4944 
4945  const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
4946 
4948  if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
4949  {
4951  }
4952 
4953  pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ),
4954  pEnd->nContent.GetIndex() );
4955 
4956  // Also copy all format templates
4957  if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
4958  {
4959  // tdf#138897 no Undo for applying style, SwUndoInserts does it
4960  pEndTextNd->CopyCollFormat(*pDestTextNd, false);
4961  if ( bOneNode )
4962  {
4964  }
4965  }
4966  }
4967 
4968  SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
4969  if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
4970  {
4971  if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
4972  {
4973  aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
4974  if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
4975  pDestTextNd->ResetAttr( RES_BREAK );
4976  if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
4977  pDestTextNd->ResetAttr( RES_PAGEDESC );
4978  }
4979  }
4980 
4981  {
4982  SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
4983  SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
4984  if (bCanMoveBack)
4985  { // pCopyPam is actually 1 before the copy range so move it fwd
4986  SwPaM temp(*pCopyPam->GetPoint());
4988  startPos = *temp.GetPoint();
4989  }
4990  assert(startPos.nNode.GetNode().IsContentNode());
4991  std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
4992  if( aInsPos == pEnd->nNode )
4993  {
4994  SwNodeIndex aSaveIdx( aInsPos, -1 );
4995  assert(pStt->nNode != pEnd->nNode);
4996  pEnd->nContent = 0; // TODO why this?
4997  CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
4998  ++aSaveIdx;
4999  pEnd->nNode = aSaveIdx;
5000  pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
5001  }
5002  else
5003  CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
5004 
5005  bCopyBookmarks = false;
5006  }
5007 
5008  // at-char anchors post SplitNode are on index 0 of 2nd node and will
5009  // remain there - move them back to the start (end would also work?)
5010  // ... also for at-para anchors; here start is preferable because
5011  // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
5012  if (pFlysAtInsPos)
5013  {
5014  // init *again* - because CopyWithFlyInFly moved startPos
5015  SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
5016  SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
5017  if (bCanMoveBack)
5018  { // pCopyPam is actually 1 before the copy range so move it fwd
5019  SwPaM temp(*pCopyPam->GetPoint());
5021  startPos = *temp.GetPoint();
5022  }
5023  assert(startPos.nNode.GetNode().IsContentNode());
5024  SwPosition startPosAtPara(startPos);
5025  startPosAtPara.nContent.Assign(nullptr, 0);
5026 
5027  for (SwFrameFormat * pFly : *pFlysAtInsPos)
5028  {
5029  SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
5030  if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
5031  {
5032  SwFormatAnchor anchor(*pAnchor);
5033  anchor.SetAnchor( &startPos );
5034  pFly->SetFormatAttr(anchor);
5035  }
5036  else if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
5037  {
5038  SwFormatAnchor anchor(*pAnchor);
5039  anchor.SetAnchor( &startPosAtPara );
5040  pFly->SetFormatAttr(anchor);
5041  }
5042  }
5043  }
5044 
5045  if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
5046  {
5047  // Put the breaks back into the first node
5048  if( aBrkSet.Count() && nullptr != ( pDestTextNd = rDoc.GetNodes()[
5049  pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
5050  {
5051  pDestTextNd->SetAttr( aBrkSet );
5052  bCopyPageSource = true;
5053  }
5054  }
5055  } while( false );
5056 
5057 
5058  // it is not possible to make this test when copy from the clipBoard to document
5059  // in this case the PageNum not exist anymore
5060  // tdf#39400 and tdf#97526
5061  // when copy from document to ClipBoard, and it is from the first page
5062  // and not the source has the page break
5063  if (rDoc.IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
5064  {
5065  pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
5066  pDestTextNd->ResetAttr(RES_PAGEDESC);
5067  }
5068 
5069 
5070  // Adjust position (in case it was moved / in another node)
5071  rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
5072  rPos.nContent.GetIndex() );
5073 
5074  if( rPos.nNode != aInsPos )
5075  {
5076  pCopyPam->GetMark()->nNode = aInsPos;
5077  if (aInsPos < rPos.nNode)
5078  { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
5079  pCopyPam->GetContentNode(false)->MakeEndIndex(&pCopyPam->GetMark()->nContent);
5080  }
5081  else // incremented in (!pSttTextNd && pDestTextNd) above
5082  {
5083  pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0);
5084  }
5085  rPos = *pCopyPam->GetMark();
5086  }
5087  else
5088  *pCopyPam->GetMark() = rPos;
5089 
5090  if ( !bAfterTable )
5091  pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
5092  else
5093  {
5094  pCopyPam->GetPoint()->nNode++;
5095 
5096  // Reset the offset to 0 as it was before the insertion
5097  pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0);
5098  // If the next node is a start node, then step back: the start node
5099  // has been copied and needs to be in the selection for the undo
5100  if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode())
5101  pCopyPam->GetPoint()->nNode--;
5102 
5103  }
5104  pCopyPam->Exchange();
5105 
5106  // Also copy all bookmarks
5107  if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
5108  {
5109  sw::CopyBookmarks(rPam, *pCopyPam->Start());
5110  }
5111 
5112  if( RedlineFlags::DeleteRedlines & eOld )
5113  {
5114  assert(*pCopyPam->GetPoint() == rPos);
5115  // the Node rPos points to may be deleted so unregister ...
5116  rPos.nContent.Assign(nullptr, 0);
5117  lcl_DeleteRedlines(rPam, *pCopyPam);
5118  rPos = *pCopyPam->GetPoint(); // ... and restore.
5119  }
5120 
5121  // If Undo is enabled, store the inserted area
5122  if (rDoc.GetIDocumentUndoRedo().DoesUndo())
5123  {
5124  // append it after styles have been copied when copying nodes
5125  rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
5126  pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes);
5127  }
5128 
5129  if( pCpyRange )
5130  {
5131  pCpyRange->SetMark();
5132  *pCpyRange->GetPoint() = *pCopyPam->GetPoint();
5133  *pCpyRange->GetMark() = *pCopyPam->GetMark();
5134  }
5135 
5136  if ( pNumRuleToPropagate != nullptr )
5137  {
5138  // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
5139  // Don't reset indent attributes, that would mean loss of direct
5140  // formatting.
5141  rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr,
5142  aListIdToPropagate, true, /*bResetIndentAttrs=*/false );
5143  }
5144 
5146  rDoc.getIDocumentState().SetModified();
5147 
5148  return true;
5149 }