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