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