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