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