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