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