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