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