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