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