Crash when fetching clipboard data.
[LibreOffice.git] / sw / source / core / doc / DocumentContentOperationsManager.cxx
blobf99de3cc390ab5769f4cf6a153c4150b55a4a776
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include <DocumentContentOperationsManager.hxx>
20 #include <doc.hxx>
21 #include <IDocumentUndoRedo.hxx>
22 #include <IDocumentMarkAccess.hxx>
23 #include <IDocumentState.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <IDocumentRedlineAccess.hxx>
26 #include <IDocumentStylePoolAccess.hxx>
27 #include <IDocumentSettingAccess.hxx>
28 #include <UndoManager.hxx>
29 #include <docary.hxx>
30 #include <textboxhelper.hxx>
31 #include <dcontact.hxx>
32 #include <grfatr.hxx>
33 #include <numrule.hxx>
34 #include <charfmt.hxx>
35 #include <ndgrf.hxx>
36 #include <ndnotxt.hxx>
37 #include <ndole.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 <frmtool.hxx>
50 #include <unocrsr.hxx>
51 #include <mvsave.hxx>
52 #include <ndtxt.hxx>
53 #include <poolfmt.hxx>
54 #include <paratr.hxx>
55 #include <txatbase.hxx>
56 #include <UndoRedline.hxx>
57 #include <undobj.hxx>
58 #include <UndoBookmark.hxx>
59 #include <UndoDelete.hxx>
60 #include <UndoSplitMove.hxx>
61 #include <UndoOverwrite.hxx>
62 #include <UndoInsert.hxx>
63 #include <UndoAttribute.hxx>
64 #include <rolbck.hxx>
65 #include <acorrect.hxx>
66 #include <bookmark.hxx>
67 #include <ftnidx.hxx>
68 #include <txtftn.hxx>
69 #include <hints.hxx>
70 #include <fmtflcnt.hxx>
71 #include <docedt.hxx>
72 #include <frameformats.hxx>
73 #include <o3tl/safeint.hxx>
74 #include <sal/log.hxx>
75 #include <unotools/charclass.hxx>
76 #include <unotools/configmgr.hxx>
77 #include <unotools/transliterationwrapper.hxx>
78 #include <i18nutil/transliteration.hxx>
79 #include <sfx2/Metadatable.hxx>
80 #include <sot/exchange.hxx>
81 #include <svl/stritem.hxx>
82 #include <svl/itemiter.hxx>
83 #include <svx/svdobj.hxx>
84 #include <svx/svdouno.hxx>
85 #include <tools/globname.hxx>
86 #include <editeng/formatbreakitem.hxx>
87 #include <com/sun/star/i18n/Boundary.hpp>
88 #include <com/sun/star/i18n/WordType.hpp>
89 #include <com/sun/star/i18n/XBreakIterator.hpp>
90 #include <com/sun/star/embed/XEmbeddedObject.hpp>
92 #include <tuple>
93 #include <memory>
95 using namespace ::com::sun::star::i18n;
97 namespace
99 // Copy method from SwDoc
100 // Prevent copying into Flys that are anchored in the range
101 bool lcl_ChkFlyFly( SwDoc& rDoc, SwNodeOffset nSttNd, SwNodeOffset nEndNd,
102 SwNodeOffset nInsNd )
104 const SwFrameFormats& rFrameFormatTable = *rDoc.GetSpzFrameFormats();
106 for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
108 SwFrameFormat const*const pFormat = rFrameFormatTable[n];
109 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
110 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
111 if (pAPos &&
112 ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
113 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
114 (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
115 (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
116 nSttNd <= pAPos->nNode.GetIndex() &&
117 pAPos->nNode.GetIndex() < nEndNd )
119 const SwFormatContent& rContent = pFormat->GetContent();
120 SwStartNode* pSNd;
121 if( !rContent.GetContentIdx() ||
122 nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
123 continue;
125 if( pSNd->GetIndex() < nInsNd &&
126 nInsNd < pSNd->EndOfSectionIndex() )
127 // Do not copy !
128 return true;
130 if( lcl_ChkFlyFly( rDoc, pSNd->GetIndex(),
131 pSNd->EndOfSectionIndex(), nInsNd ) )
132 // Do not copy !
133 return true;
137 return false;
140 SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, SwNodeOffset & rDelCount)
142 SwNodeIndex const& rStart(rSourcePaM.Start()->nNode);
143 // Special handling for SwDoc::AppendDoc
144 if (rSourcePaM.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
145 == rStart.GetIndex())
147 rDelCount = SwNodeOffset(1);
148 return SwNodeIndex(rStart, +1);
150 else
152 rDelCount = SwNodeOffset(0);
153 return rStart;
158 The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
159 array. It is called after a call of the CopyNodes(..) function. But this function does not copy
160 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
161 if the corresponding end/start node is outside the copied pam.
162 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
163 index inside the pam.
164 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
165 of "non-copy" nodes between rPam.Start() and rLastIdx.
166 nNewIdx is the new position of interest.
168 void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const SwNodeOffset nNewIdx, SwNodeOffset& rDelCount )
170 SwNodeOffset nStart = rPam.Start()->nNode.GetIndex();
171 SwNodeOffset nEnd = rPam.End()->nNode.GetIndex();
172 if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
174 // We never copy the StartOfContent node
175 do // count "non-copy" nodes
177 SwNode& rNode = rLastIdx.GetNode();
178 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
179 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
181 ++rDelCount;
183 ++rLastIdx;
185 while( rLastIdx.GetIndex() < nNewIdx );
187 else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
188 // no move backward needed
190 while( rLastIdx.GetIndex() > nNewIdx )
192 SwNode& rNode = rLastIdx.GetNode();
193 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
194 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
196 --rDelCount;
198 rLastIdx--;
203 void lcl_SetCpyPos( const SwPosition& rOrigPos,
204 const SwPosition& rOrigStt,
205 const SwPosition& rCpyStt,
206 SwPosition& rChgPos,
207 SwNodeOffset nDelCount )
209 SwNodeOffset nNdOff = rOrigPos.nNode.GetIndex();
210 nNdOff -= rOrigStt.nNode.GetIndex();
211 nNdOff -= nDelCount;
212 sal_Int32 nContentPos = rOrigPos.nContent.GetIndex();
214 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
215 rChgPos.nNode = nNdOff + rCpyStt.nNode.GetIndex();
216 if( !nNdOff )
218 // just adapt the content index
219 if( nContentPos > rOrigStt.nContent.GetIndex() )
220 nContentPos -= rOrigStt.nContent.GetIndex();
221 else
222 nContentPos = 0;
223 nContentPos += rCpyStt.nContent.GetIndex();
225 rChgPos.nContent.Assign( rChgPos.nNode.GetNode().GetContentNode(), nContentPos );
230 namespace sw
232 // TODO: use SaveBookmark (from DelBookmarks)
233 void CopyBookmarks(const SwPaM& rPam, const SwPosition& rCpyPam)
235 const SwDoc& rSrcDoc = rPam.GetDoc();
236 SwDoc& rDestDoc = rCpyPam.GetDoc();
237 const IDocumentMarkAccess* const pSrcMarkAccess = rSrcDoc.getIDocumentMarkAccess();
238 ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
240 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
241 SwPosition const*const pCpyStt = &rCpyPam;
243 std::vector< const ::sw::mark::IMark* > vMarksToCopy;
244 for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
245 ppMark != pSrcMarkAccess->getAllMarksEnd();
246 ++ppMark )
248 const ::sw::mark::IMark* const pMark = *ppMark;
250 const SwPosition& rMarkStart = pMark->GetMarkStart();
251 const SwPosition& rMarkEnd = pMark->GetMarkEnd();
252 // only include marks that are in the range and not touching both start and end
253 // - not for annotation or checkbox marks.
254 bool const isIncludeStart(
255 (rStt.nContent.GetIndex() == 0 // paragraph start selected?
256 // also: only if inserting at the start - cross reference
257 // marks require index to be 0, and there could be one
258 // on the target node already
259 && rCpyPam.nContent.GetIndex() == 0)
260 || rMarkStart != rStt);
261 bool const isIncludeEnd(
262 (rEnd.nNode.GetNode().IsTextNode() // paragraph end selected?
263 && rEnd.nContent.GetIndex() == rEnd.nNode.GetNode().GetTextNode()->Len())
264 || rMarkEnd != rEnd);
265 const bool bIsNotOnBoundary =
266 pMark->IsExpanded()
267 ? (isIncludeStart || isIncludeEnd) // rMarkStart != rMarkEnd
268 : (isIncludeStart && isIncludeEnd); // rMarkStart == rMarkEnd
269 const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark);
270 if ( rMarkStart >= rStt && rMarkEnd <= rEnd
271 && ( bIsNotOnBoundary
272 || aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
273 || aMarkType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
274 || aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
275 || aMarkType == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
276 || aMarkType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
278 vMarksToCopy.push_back(pMark);
281 // We have to count the "non-copied" nodes...
282 SwNodeOffset nDelCount;
283 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
284 for(const sw::mark::IMark* const pMark : vMarksToCopy)
286 SwPaM aTmpPam(*pCpyStt);
287 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().nNode.GetIndex(), nDelCount);
288 lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
289 if(pMark->IsExpanded())
291 aTmpPam.SetMark();
292 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().nNode.GetIndex(), nDelCount);
293 lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
296 ::sw::mark::IMark* const pNewMark = rDestDoc.getIDocumentMarkAccess()->makeMark(
297 aTmpPam,
298 pMark->GetName(),
299 IDocumentMarkAccess::GetType(*pMark),
300 ::sw::mark::InsertMode::CopyText);
301 // Explicitly try to get exactly the same name as in the source
302 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
303 rDestDoc.getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName());
305 // copying additional attributes for bookmarks or fieldmarks
306 ::sw::mark::IBookmark* const pNewBookmark =
307 dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
308 const ::sw::mark::IBookmark* const pOldBookmark =
309 dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
310 if (pNewBookmark && pOldBookmark)
312 pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
313 pNewBookmark->SetShortName(pOldBookmark->GetShortName());
314 pNewBookmark->Hide(pOldBookmark->IsHidden());
315 pNewBookmark->SetHideCondition(pOldBookmark->GetHideCondition());
317 ::sw::mark::IFieldmark* const pNewFieldmark =
318 dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
319 const ::sw::mark::IFieldmark* const pOldFieldmark =
320 dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
321 if (pNewFieldmark && pOldFieldmark)
323 pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
324 pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
325 ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
326 const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
327 for (const auto& rEntry : *pOldParams )
329 pNewParams->insert( rEntry );
333 ::sfx2::Metadatable const*const pMetadatable(
334 dynamic_cast< ::sfx2::Metadatable const* >(pMark));
335 ::sfx2::Metadatable *const pNewMetadatable(
336 dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
337 if (pMetadatable && pNewMetadatable)
339 pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
343 } // namespace sw
345 namespace
347 void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
349 const SwDoc& rSrcDoc = rPam.GetDoc();
350 const SwRedlineTable& rTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
351 if( rTable.empty() )
352 return;
354 SwDoc& rDestDoc = rCpyPam.GetDoc();
355 SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
356 std::unique_ptr<SwPaM> pDelPam;
357 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
358 // We have to count the "non-copied" nodes
359 SwNodeOffset nDelCount;
360 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
362 SwRedlineTable::size_type n = 0;
363 rSrcDoc.getIDocumentRedlineAccess().GetRedline( *pStt, &n );
364 for( ; n < rTable.size(); ++n )
366 const SwRangeRedline* pRedl = rTable[ n ];
367 if( RedlineType::Delete == pRedl->GetType() && pRedl->IsVisible() )
369 const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
371 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
372 switch( eCmpPos )
374 case SwComparePosition::CollideEnd:
375 case SwComparePosition::Before:
376 // Pos1 is before Pos2
377 break;
379 case SwComparePosition::CollideStart:
380 case SwComparePosition::Behind:
381 // Pos1 is after Pos2
382 n = rTable.size();
383 break;
385 default:
387 pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
388 if( *pStt < *pRStt )
390 lcl_NonCopyCount( rPam, aCorrIdx, pRStt->nNode.GetIndex(), nDelCount );
391 lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
392 *pDelPam->GetPoint(), nDelCount );
394 pDelPam->SetMark();
396 if( *pEnd < *pREnd )
397 *pDelPam->GetPoint() = *pCpyEnd;
398 else
400 lcl_NonCopyCount( rPam, aCorrIdx, pREnd->nNode.GetIndex(), nDelCount );
401 lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
402 *pDelPam->GetPoint(), nDelCount );
405 if (pDelPam->GetNext() && *pDelPam->GetNext()->End() == *pDelPam->Start())
407 *pDelPam->GetNext()->End() = *pDelPam->End();
408 pDelPam.reset(pDelPam->GetNext());
415 if( !pDelPam )
416 return;
418 RedlineFlags eOld = rDestDoc.getIDocumentRedlineAccess().GetRedlineFlags();
419 rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
421 ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
423 do {
424 rDestDoc.getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() );
425 if( !pDelPam->IsMultiSelection() )
426 break;
427 delete pDelPam->GetNext();
428 } while( true );
430 rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
433 void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
435 SwDoc& rSrcDoc = rRg.aStart.GetNode().GetDoc();
436 if( !rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
438 SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
439 SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
440 lcl_DeleteRedlines( aRgTmp, aCpyTmp );
444 void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
446 SwFormatChain aSrc( pSrc->GetChain() );
447 if ( !aSrc.GetNext() )
449 aSrc.SetNext( pDest );
450 pSrc->SetFormatAttr( aSrc );
452 SwFormatChain aDest( pDest->GetChain() );
453 if ( !aDest.GetPrev() )
455 aDest.SetPrev( pSrc );
456 pDest->SetFormatAttr( aDest );
460 // #i86492#
461 bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
463 bool bRet = false;
465 const SwTextNode* pTextNd = rPam.Start()->nNode.GetNode().GetTextNode();
466 const SwTextNode* pEndTextNd = rPam.End()->nNode.GetNode().GetTextNode();
467 if ( pTextNd && pTextNd->IsInList() &&
468 pEndTextNd && pEndTextNd->IsInList() )
470 bRet = true;
471 SwNodeIndex aIdx(rPam.Start()->nNode);
475 ++aIdx;
476 pTextNd = aIdx.GetNode().GetTextNode();
478 if ( !pTextNd || !pTextNd->IsInList() )
480 bRet = false;
481 break;
483 } while (pTextNd != pEndTextNd);
486 return bRet;
489 bool lcl_MarksWholeNode(const SwPaM & rPam)
491 bool bResult = false;
492 const SwPosition* pStt = rPam.Start();
493 const SwPosition* pEnd = rPam.End();
495 if (nullptr != pStt && nullptr != pEnd)
497 const SwTextNode* pSttNd = pStt->nNode.GetNode().GetTextNode();
498 const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
500 if (nullptr != pSttNd && nullptr != pEndNd &&
501 pStt->nContent.GetIndex() == 0 &&
502 pEnd->nContent.GetIndex() == pEndNd->Len())
504 bResult = true;
508 return bResult;
512 //local functions originally from sw/source/core/doc/docedt.cxx
513 namespace sw
515 void CalcBreaks(std::vector<std::pair<SwNodeOffset, sal_Int32>> & rBreaks,
516 SwPaM const & rPam, bool const isOnlyFieldmarks)
518 SwNodeOffset const nStartNode(rPam.Start()->nNode.GetIndex());
519 SwNodeOffset const nEndNode(rPam.End()->nNode.GetIndex());
520 SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
521 IDocumentMarkAccess const& rIDMA(*rPam.GetDoc().getIDocumentMarkAccess());
523 std::stack<std::tuple<sw::mark::IFieldmark const*, bool, SwNodeOffset, sal_Int32>> startedFields;
525 for (SwNodeOffset n = nStartNode; n <= nEndNode; ++n)
527 SwNode *const pNode(rNodes[n]);
528 if (pNode->IsTextNode())
530 SwTextNode & rTextNode(*pNode->GetTextNode());
531 sal_Int32 const nStart(n == nStartNode
532 ? rPam.Start()->nContent.GetIndex()
533 : 0);
534 sal_Int32 const nEnd(n == nEndNode
535 ? rPam.End()->nContent.GetIndex()
536 : rTextNode.Len());
537 for (sal_Int32 i = nStart; i < nEnd; ++i)
539 const sal_Unicode c(rTextNode.GetText()[i]);
540 switch (c)
542 // note: CH_TXT_ATR_FORMELEMENT does not need handling
543 // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
544 case CH_TXTATR_INWORD:
545 case CH_TXTATR_BREAKWORD:
547 // META hints only have dummy char at the start, not
548 // at the end, so no need to check in nStartNode
549 if (n == nEndNode && !isOnlyFieldmarks)
551 SwTextAttr const*const pAttr(rTextNode.GetTextAttrForCharAt(i));
552 if (pAttr && pAttr->End() && (nEnd < *pAttr->End()))
554 assert(pAttr->HasDummyChar());
555 rBreaks.emplace_back(n, i);
558 break;
560 case CH_TXT_ATR_FIELDSTART:
562 auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
563 startedFields.emplace(pFieldMark, false, 0, 0);
564 break;
566 case CH_TXT_ATR_FIELDSEP:
568 if (startedFields.empty())
570 rBreaks.emplace_back(n, i);
572 else
573 { // no way to find the field via MarkManager...
574 assert(std::get<0>(startedFields.top())->IsCoveringPosition(SwPosition(rTextNode, i)));
575 std::get<1>(startedFields.top()) = true;
576 std::get<2>(startedFields.top()) = n;
577 std::get<3>(startedFields.top()) = i;
579 break;
581 case CH_TXT_ATR_FIELDEND:
583 if (startedFields.empty())
585 rBreaks.emplace_back(n, i);
587 else
588 { // fieldmarks must not overlap => stack
589 assert(std::get<0>(startedFields.top()) == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
590 startedFields.pop();
592 break;
597 else if (pNode->IsStartNode())
599 if (pNode->EndOfSectionIndex() <= nEndNode)
600 { // fieldmark cannot overlap node section
601 n = pNode->EndOfSectionIndex();
604 else
605 { // EndNode can actually happen with sections :(
606 assert(pNode->IsEndNode() || pNode->IsNoTextNode());
609 while (!startedFields.empty())
611 SwPosition const& rStart(std::get<0>(startedFields.top())->GetMarkStart());
612 std::pair<SwNodeOffset, sal_Int32> const pos(
613 rStart.nNode.GetIndex(), rStart.nContent.GetIndex());
614 auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos);
615 assert(it == rBreaks.end() || *it != pos);
616 rBreaks.insert(it, pos);
617 if (std::get<1>(startedFields.top()))
619 std::pair<SwNodeOffset, sal_Int32> const posSep(
620 std::get<2>(startedFields.top()),
621 std::get<3>(startedFields.top()));
622 it = std::lower_bound(rBreaks.begin(), rBreaks.end(), posSep);
623 assert(it == rBreaks.end() || *it != posSep);
624 rBreaks.insert(it, posSep);
626 startedFields.pop();
631 namespace
634 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam,
635 bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
637 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
639 sw::CalcBreaks(Breaks, rPam);
641 if (Breaks.empty())
643 return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext);
646 // Deletion must be split into several parts if the text node
647 // contains a text attribute with end and with dummy character
648 // and the selection does not contain the text attribute completely,
649 // but overlaps its start (left), where the dummy character is.
651 SwPosition const & rSelectionEnd( *rPam.End() );
653 bool bRet( true );
654 // iterate from end to start, to avoid invalidating the offsets!
655 auto iter( Breaks.rbegin() );
656 SwNodeOffset nOffset(0);
657 SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
658 SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
659 SwPosition & rEnd( *aPam.End() );
660 SwPosition & rStart( *aPam.Start() );
662 while (iter != Breaks.rend())
664 rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
665 if (rStart < rEnd) // check if part is empty
667 bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
668 nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
670 rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
671 ++iter;
674 rStart = *rPam.Start(); // set to original start
675 if (rStart < rEnd) // check if part is empty
677 bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
680 return bRet;
683 bool lcl_StrLenOverflow( const SwPaM& rPam )
685 // If we try to merge two paragraphs we have to test if afterwards
686 // the string doesn't exceed the allowed string length
687 if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
689 const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
690 const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
691 if( (nullptr != pEndNd) && pStt->nNode.GetNode().IsTextNode() )
693 const sal_uInt64 nSum = pStt->nContent.GetIndex() +
694 pEndNd->GetText().getLength() - pEnd->nContent.GetIndex();
695 return nSum > o3tl::make_unsigned(SAL_MAX_INT32);
698 return false;
701 struct SaveRedline
703 SwRangeRedline* pRedl;
704 SwNodeOffset nStt, nEnd;
705 sal_Int32 nSttCnt;
706 sal_Int32 nEndCnt;
708 SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
709 : pRedl(pR)
710 , nEnd(0)
711 , nEndCnt(0)
713 const SwPosition* pStt = pR->Start(),
714 * pEnd = pR->End();
715 SwNodeOffset nSttIdx = rSttIdx.GetIndex();
716 nStt = pStt->nNode.GetIndex() - nSttIdx;
717 nSttCnt = pStt->nContent.GetIndex();
718 if( pR->HasMark() )
720 nEnd = pEnd->nNode.GetIndex() - nSttIdx;
721 nEndCnt = pEnd->nContent.GetIndex();
724 pRedl->GetPoint()->nNode = SwNodeOffset(0);
725 pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
726 pRedl->GetMark()->nNode = SwNodeOffset(0);
727 pRedl->GetMark()->nContent.Assign( nullptr, 0 );
730 SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
731 : pRedl(pR)
732 , nEnd(0)
733 , nEndCnt(0)
735 const SwPosition* pStt = pR->Start(),
736 * pEnd = pR->End();
737 SwNodeOffset nSttIdx = rPos.nNode.GetIndex();
738 nStt = pStt->nNode.GetIndex() - nSttIdx;
739 nSttCnt = pStt->nContent.GetIndex();
740 if( nStt == SwNodeOffset(0) )
741 nSttCnt = nSttCnt - rPos.nContent.GetIndex();
742 if( pR->HasMark() )
744 nEnd = pEnd->nNode.GetIndex() - nSttIdx;
745 nEndCnt = pEnd->nContent.GetIndex();
746 if( nEnd == SwNodeOffset(0) )
747 nEndCnt = nEndCnt - rPos.nContent.GetIndex();
750 pRedl->GetPoint()->nNode = SwNodeOffset(0);
751 pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
752 pRedl->GetMark()->nNode = SwNodeOffset(0);
753 pRedl->GetMark()->nContent.Assign( nullptr, 0 );
756 void SetPos( SwNodeOffset nInsPos )
758 pRedl->GetPoint()->nNode = nInsPos + nStt;
759 pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt );
760 if( pRedl->HasMark() )
762 pRedl->GetMark()->nNode = nInsPos + nEnd;
763 pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt );
767 void SetPos( const SwPosition& aPos )
769 pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
770 pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt + ( nStt == SwNodeOffset(0) ? aPos.nContent.GetIndex() : 0 ) );
771 if( pRedl->HasMark() )
773 pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
774 pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == SwNodeOffset(0) ? aPos.nContent.GetIndex() : 0 ) );
779 typedef std::vector< SaveRedline > SaveRedlines_t;
781 void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
783 SwDoc& rDoc = aPam.GetNode().GetDoc();
785 const SwPosition* pStart = aPam.Start();
786 const SwPosition* pEnd = aPam.End();
788 // get first relevant redline
789 SwRedlineTable::size_type nCurrentRedline;
790 rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
791 if( nCurrentRedline > 0)
792 nCurrentRedline--;
794 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
795 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
796 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
798 // iterate over relevant redlines and decide for each whether it should
799 // be saved, or split + saved
800 SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
801 for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
803 SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
804 SwComparePosition eCompare =
805 ComparePosition( *pCurrent->Start(), *pCurrent->End(),
806 *pStart, *pEnd);
808 // we must save this redline if it overlaps aPam
809 // (we may have to split it, too)
810 if( eCompare == SwComparePosition::OverlapBehind ||
811 eCompare == SwComparePosition::OverlapBefore ||
812 eCompare == SwComparePosition::Outside ||
813 eCompare == SwComparePosition::Inside ||
814 eCompare == SwComparePosition::Equal )
816 rRedlineTable.Remove( nCurrentRedline-- );
818 // split beginning, if necessary
819 if( eCompare == SwComparePosition::OverlapBefore ||
820 eCompare == SwComparePosition::Outside )
822 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
823 *pNewRedline->End() = *pStart;
824 *pCurrent->Start() = *pStart;
825 rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
828 // split end, if necessary
829 if( eCompare == SwComparePosition::OverlapBehind ||
830 eCompare == SwComparePosition::Outside )
832 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
833 *pNewRedline->Start() = *pEnd;
834 *pCurrent->End() = *pEnd;
835 rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
838 // save the current redline
839 rArr.emplace_back( pCurrent, *pStart );
843 // restore old redline mode
844 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
847 void lcl_RestoreRedlines(SwDoc& rDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
849 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
850 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
852 for(SaveRedline & rSvRedLine : rArr)
854 rSvRedLine.SetPos( rPos );
855 rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
858 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
861 void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
863 SwDoc& rDoc = rRg.aStart.GetNode().GetDoc();
864 SwRedlineTable::size_type nRedlPos;
865 SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
866 aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 );
867 if( rDoc.getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
868 --nRedlPos;
869 else if( nRedlPos >= rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
870 return ;
872 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
873 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
874 SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
876 do {
877 SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
879 const SwPosition* pRStt = pTmp->Start(),
880 * pREnd = pTmp->End();
882 if( pRStt->nNode < rRg.aStart )
884 if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
886 // Create a copy and set the end of the original to the end of the MoveArea.
887 // The copy is moved too.
888 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
889 SwPosition* pTmpPos = pNewRedl->Start();
890 pTmpPos->nNode = rRg.aStart;
891 pTmpPos->nContent.Assign(
892 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
894 rArr.emplace_back(pNewRedl, rRg.aStart);
896 pTmpPos = pTmp->End();
897 pTmpPos->nNode = rRg.aEnd;
898 pTmpPos->nContent.Assign(
899 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
901 else if( pREnd->nNode == rRg.aStart )
903 SwPosition* pTmpPos = pTmp->End();
904 pTmpPos->nNode = rRg.aEnd;
905 pTmpPos->nContent.Assign(
906 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
909 else if( pRStt->nNode < rRg.aEnd )
911 rRedlTable.Remove( nRedlPos-- );
912 if( pREnd->nNode < rRg.aEnd ||
913 ( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
915 // move everything
916 rArr.emplace_back( pTmp, rRg.aStart );
918 else
920 // split
921 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
922 SwPosition* pTmpPos = pNewRedl->End();
923 pTmpPos->nNode = rRg.aEnd;
924 pTmpPos->nContent.Assign(
925 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
927 rArr.emplace_back( pNewRedl, rRg.aStart );
929 pTmpPos = pTmp->Start();
930 pTmpPos->nNode = rRg.aEnd;
931 pTmpPos->nContent.Assign(
932 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
933 rDoc.getIDocumentRedlineAccess().AppendRedline( pTmp, true );
936 else
937 break;
939 } while( ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() );
940 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
943 void lcl_RestoreRedlines(SwDoc& rDoc, SwNodeOffset const nInsPos, SaveRedlines_t& rArr)
945 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
946 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
948 for(SaveRedline & rSvRedLine : rArr)
950 rSvRedLine.SetPos( nInsPos );
951 IDocumentRedlineAccess::AppendResult const result(
952 rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ));
953 if ( IDocumentRedlineAccess::AppendResult::APPENDED == result &&
954 rSvRedLine.pRedl->GetType() == RedlineType::Delete )
956 UpdateFramesForAddDeleteRedline(rDoc, *rSvRedLine.pRedl);
960 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
963 bool lcl_SaveFootnote( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
964 const SwNodeIndex& rInsPos,
965 SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
966 const SwIndex* pSttCnt = nullptr, const SwIndex* pEndCnt = nullptr )
968 bool bUpdateFootnote = false;
969 const SwNodes& rNds = rInsPos.GetNodes();
970 const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
971 rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
972 const bool bSaveFootnote = !bDelFootnote &&
973 rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
974 if( !rFootnoteArr.empty() )
977 size_t nPos = 0;
978 rFootnoteArr.SeekEntry( rSttNd, &nPos );
979 SwTextFootnote* pSrch;
980 const SwNode* pFootnoteNd;
982 // Delete/save all that come after it
983 while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
984 &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
985 <= rEndNd.GetIndex() )
987 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
988 if( ( pEndCnt && pSttCnt )
989 ? (( &rSttNd.GetNode() == pFootnoteNd &&
990 pSttCnt->GetIndex() > nFootnoteSttIdx) ||
991 ( &rEndNd.GetNode() == pFootnoteNd &&
992 nFootnoteSttIdx >= pEndCnt->GetIndex() ))
993 : ( &rEndNd.GetNode() == pFootnoteNd ))
995 ++nPos; // continue searching
997 else
999 // delete it
1000 if( bDelFootnote )
1002 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
1003 SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
1004 rTextNd.EraseText( aIdx, 1 );
1006 else
1008 pSrch->DelFrames(nullptr);
1009 rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
1010 if( bSaveFootnote )
1011 rSaveArr.insert( pSrch );
1013 bUpdateFootnote = true;
1017 while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
1018 GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
1020 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
1021 if( !pEndCnt || !pSttCnt ||
1022 ! (( &rSttNd.GetNode() == pFootnoteNd &&
1023 pSttCnt->GetIndex() > nFootnoteSttIdx ) ||
1024 ( &rEndNd.GetNode() == pFootnoteNd &&
1025 nFootnoteSttIdx >= pEndCnt->GetIndex() )) )
1027 if( bDelFootnote )
1029 // delete it
1030 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
1031 SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
1032 rTextNd.EraseText( aIdx, 1 );
1034 else
1036 pSrch->DelFrames(nullptr);
1037 rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
1038 if( bSaveFootnote )
1039 rSaveArr.insert( pSrch );
1041 bUpdateFootnote = true;
1045 // When moving from redline section into document content section, e.g.
1046 // after loading a document with (delete-)redlines, the footnote array
1047 // has to be adjusted... (#i70572)
1048 if( bSaveFootnote )
1050 SwNodeIndex aIdx( rSttNd );
1051 while( aIdx < rEndNd ) // Check the moved section
1053 SwNode* pNode = &aIdx.GetNode();
1054 if( pNode->IsTextNode() ) // Looking for text nodes...
1056 SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
1057 if( pHints && pHints->HasFootnote() ) //...with footnotes
1059 bUpdateFootnote = true; // Heureka
1060 const size_t nCount = pHints->Count();
1061 for( size_t i = 0; i < nCount; ++i )
1063 SwTextAttr *pAttr = pHints->Get( i );
1064 if ( pAttr->Which() == RES_TXTATR_FTN )
1066 rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
1071 ++aIdx;
1074 return bUpdateFootnote;
1077 bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
1079 sal_Unicode const cChr = pNode->GetText()[nPos];
1080 switch (cChr)
1082 case CH_TXTATR_BREAKWORD:
1083 case CH_TXTATR_INWORD:
1084 return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
1085 case CH_TXT_ATR_INPUTFIELDSTART:
1086 case CH_TXT_ATR_INPUTFIELDEND:
1087 case CH_TXT_ATR_FIELDSTART:
1088 case CH_TXT_ATR_FIELDSEP:
1089 case CH_TXT_ATR_FIELDEND:
1090 case CH_TXT_ATR_FORMELEMENT:
1091 return false;
1092 default:
1093 return true;
1097 void lcl_SkipAttr( const SwTextNode *pNode, SwIndex &rIdx, sal_Int32 &rStart )
1099 if( !lcl_MayOverwrite( pNode, rStart ) )
1101 // skip all special attributes
1102 do {
1103 ++rIdx;
1104 rStart = rIdx.GetIndex();
1105 } while (rStart < pNode->GetText().getLength()
1106 && !lcl_MayOverwrite(pNode, rStart) );
1110 bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
1112 if( bRegExpRplc )
1114 sal_Int32 nPos = 0;
1115 static const OUStringLiteral sPara(u"\\n");
1116 for (;;)
1118 nPos = rStr.indexOf( sPara, nPos );
1119 if (nPos<0)
1121 break;
1123 // Has this been escaped?
1124 if( nPos && '\\' == rStr[nPos-1])
1126 ++nPos;
1127 if( nPos >= rStr.getLength() )
1129 break;
1132 else
1134 rRet = rStr.copy( 0, nPos );
1135 rStr = rStr.copy( nPos + sPara.getLength() );
1136 return true;
1140 rRet = rStr;
1141 rStr.clear();
1142 return false;
1146 namespace //local functions originally from docfmt.cxx
1149 bool lcl_ApplyOtherSet(
1150 SwContentNode & rNode,
1151 SwHistory *const pHistory,
1152 SfxItemSet const& rOtherSet,
1153 SfxItemSet const& rFirstSet,
1154 SfxItemSet const& rPropsSet,
1155 SwRootFrame const*const pLayout,
1156 SwNodeIndex *const o_pIndex = nullptr)
1158 assert(rOtherSet.Count());
1160 bool ret(false);
1161 SwTextNode *const pTNd = rNode.GetTextNode();
1162 sw::MergedPara const* pMerged(nullptr);
1163 if (pLayout && pLayout->HasMergedParas() && pTNd)
1165 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
1166 pTNd->getLayoutFrame(pLayout)));
1167 if (pTextFrame)
1169 pMerged = pTextFrame->GetMergedPara();
1171 if (pMerged)
1173 if (rFirstSet.Count())
1175 if (pHistory)
1177 SwRegHistory aRegH(pMerged->pFirstNode, *pMerged->pFirstNode, pHistory);
1178 ret = pMerged->pFirstNode->SetAttr(rFirstSet);
1180 else
1182 ret = pMerged->pFirstNode->SetAttr(rFirstSet);
1185 if (rPropsSet.Count())
1187 if (pHistory)
1189 SwRegHistory aRegH(pMerged->pParaPropsNode, *pMerged->pParaPropsNode, pHistory);
1190 ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
1192 else
1194 ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
1197 if (o_pIndex)
1199 *o_pIndex = *pMerged->pLastNode; // skip hidden
1204 // input cursor can't be on hidden node, and iteration skips them
1205 assert(!pLayout || !pLayout->HasMergedParas()
1206 || rNode.GetRedlineMergeFlag() != SwNode::Merge::Hidden);
1208 if (!pMerged)
1210 if (pHistory)
1212 SwRegHistory aRegH(&rNode, rNode, pHistory);
1213 ret = rNode.SetAttr( rOtherSet );
1215 else
1217 ret = rNode.SetAttr( rOtherSet );
1220 return ret;
1223 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1225 // set format redline with extra data for lcl_InsAttr()
1226 void lcl_SetRedline(
1227 SwDoc& rDoc,
1228 const SwPaM &rRg)
1230 std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
1232 // check existing redline on the same range, and use its extra data, if it exists
1233 SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos(
1234 rRg.Start()->nNode.GetNode(), RedlineType::Format );
1235 if( SwRedlineTable::npos != nRedlPos )
1237 const SwPosition *pRStt, *pREnd;
1238 do {
1239 SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
1240 pRStt = pTmp->Start();
1241 pREnd = pTmp->End();
1242 SwComparePosition eCompare = ComparePosition( *rRg.Start(), *rRg.End(), *pRStt, *pREnd );
1243 if ( eCompare == SwComparePosition::Inside || eCompare == SwComparePosition::Equal )
1245 if (pTmp->GetExtraData())
1247 const SwRedlineExtraData* pExtraData = pTmp->GetExtraData();
1248 const SwRedlineExtraData_FormatColl* pFormattingChanges =
1249 dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
1250 // Check if the extra data is of type 'formatting changes'
1251 if (pFormattingChanges)
1253 // Get the item set that holds all the changes properties
1254 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
1255 xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, pChangesSet));
1256 break;
1260 } while( pRStt <= rRg.Start() && ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
1263 SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::Format, rRg );
1264 auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline( pRedline, true));
1265 // store original text attributes to reject formatting change
1266 if (IDocumentRedlineAccess::AppendResult::IGNORED == result)
1267 return;
1269 // no existing format redline in the range
1270 if (!xExtra)
1272 // Apply the first character's attributes to the ReplaceText
1273 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
1274 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( rDoc.GetAttrPool() );
1275 SwTextNode * pNode = rRg.Start()->nNode.GetNode().GetTextNode();
1276 pNode->GetParaAttr( aSet, rRg.Start()->nContent.GetIndex() + 1, rRg.End()->nContent.GetIndex() );
1278 aSet.ClearItem( RES_TXTATR_REFMARK );
1279 aSet.ClearItem( RES_TXTATR_TOXMARK );
1280 aSet.ClearItem( RES_TXTATR_CJK_RUBY );
1281 aSet.ClearItem( RES_TXTATR_INETFMT );
1282 aSet.ClearItem( RES_TXTATR_META );
1283 aSet.ClearItem( RES_TXTATR_METAFIELD );
1285 // After GetParaAttr aSet can contain INVALID_POOL_ITEM items, e.g. RES_TXTATR_CHARFMT
1286 // and (a copy of) this SfxItemSet can be passed to MSWordExportBase::OutputItemSet
1287 // which doesn't handle INVALID_POOL_ITEM items so clear InvalidItems here
1288 aSet.ClearInvalidItems();
1290 xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, &aSet));
1293 if (xExtra)
1295 pRedline->SetExtraData(xExtra.get() );
1299 // create format redline(s) for the given range:
1300 // to track the original formatting stored in the
1301 // hints, create redlines for all parts of the
1302 // range partitioned by boundaries of the hints.
1303 void lcl_SetRedlines(
1304 SwDoc& rDoc,
1305 const SwPaM &rRg)
1307 SwNodeIndex aIdx( rRg.Start()->nNode );
1308 const SwNodeIndex aEndNd( rRg.End()->nNode );
1309 while( aIdx <= aEndNd )
1311 SwTextNode *pNode = aIdx.GetNode().GetTextNode();
1312 if( pNode )
1314 const sal_Int32 nStart = aIdx == rRg.Start()->nNode
1315 ? rRg.Start()->nContent.GetIndex()
1316 : 0;
1317 const sal_Int32 nEnd = aIdx < aEndNd
1318 ? pNode->GetText().getLength()
1319 : rRg.End()->nContent.GetIndex();
1321 if( SwpHints *pHints = pNode->GetpSwpHints() )
1323 const size_t nCount = pHints->Count();
1324 sal_Int32 nRedEnd = nStart;
1325 for( size_t i = 0; i < nCount; ++i )
1327 SwTextAttr *pAttr = pHints->Get( i );
1329 if ( pAttr->GetStart() > nEnd )
1331 break; // after the range
1334 if ( !pAttr->GetEnd() || *pAttr->GetEnd() < nStart )
1336 continue; // before the range
1339 // range part before the hint
1340 if ( nRedEnd < pAttr->GetStart() )
1342 SwPaM aPam( *pNode, nRedEnd, *pNode, pAttr->GetStart() );
1343 lcl_SetRedline(rDoc, aPam);
1346 // range part at the hint
1347 sal_Int32 nRedStart = std::max(pAttr->GetStart(), nStart);
1348 nRedEnd = std::min(*pAttr->GetEnd(), nEnd);
1349 SwPaM aPam2( *pNode, nRedStart, *pNode, nRedEnd );
1350 lcl_SetRedline(rDoc, aPam2);
1353 // range part after the last hint
1354 if ( nRedEnd < nEnd )
1356 SwPaM aPam( *pNode, nRedEnd, *pNode, nEnd );
1357 lcl_SetRedline(rDoc, aPam);
1360 else
1362 SwPaM aPam( *pNode, nStart, *pNode, nEnd );
1363 lcl_SetRedline(rDoc, aPam);
1366 ++aIdx;
1370 /// Insert Hints according to content types;
1371 // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1373 bool lcl_InsAttr(
1374 SwDoc& rDoc,
1375 const SwPaM &rRg,
1376 const SfxItemSet& rChgSet,
1377 const SetAttrMode nFlags,
1378 SwUndoAttr *const pUndo,
1379 SwRootFrame const*const pLayout,
1380 SwTextAttr **ppNewTextAttr)
1382 // Divide the Sets (for selections in Nodes)
1383 const SfxItemSet* pCharSet = nullptr;
1384 const SfxItemSet* pOtherSet = nullptr;
1385 bool bDelete = false;
1386 bool bCharAttr = false;
1387 bool bOtherAttr = false;
1389 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1390 if ( 1 == rChgSet.Count() )
1392 SfxItemIter aIter( rChgSet );
1393 const SfxPoolItem* pItem = aIter.GetCurItem();
1394 if (pItem && !IsInvalidItem(pItem))
1396 const sal_uInt16 nWhich = pItem->Which();
1398 if ( isCHRATR(nWhich) ||
1399 (RES_TXTATR_CHARFMT == nWhich) ||
1400 (RES_TXTATR_INETFMT == nWhich) ||
1401 (RES_TXTATR_AUTOFMT == nWhich) ||
1402 (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
1404 pCharSet = &rChgSet;
1405 bCharAttr = true;
1408 if ( isPARATR(nWhich)
1409 || isPARATR_LIST(nWhich)
1410 || isFRMATR(nWhich)
1411 || isGRFATR(nWhich)
1412 || isUNKNOWNATR(nWhich)
1413 || isDrawingLayerAttribute(nWhich) )
1415 pOtherSet = &rChgSet;
1416 bOtherAttr = true;
1421 // Build new itemset if either
1422 // - rChgSet.Count() > 1 or
1423 // - The attribute in rChgSet does not belong to one of the above categories
1424 if ( !bCharAttr && !bOtherAttr )
1426 SfxItemSet* pTmpCharItemSet = new SfxItemSetFixed<
1427 RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
1428 RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT,
1429 RES_TXTATR_UNKNOWN_CONTAINER,
1430 RES_TXTATR_UNKNOWN_CONTAINER>( rDoc.GetAttrPool() );
1432 SfxItemSet* pTmpOtherItemSet = new SfxItemSetFixed<
1433 RES_PARATR_BEGIN, RES_GRFATR_END - 1,
1434 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1,
1435 // FillAttribute support:
1436 XATTR_FILL_FIRST, XATTR_FILL_LAST>( rDoc.GetAttrPool() );
1438 pTmpCharItemSet->Put( rChgSet );
1439 pTmpOtherItemSet->Put( rChgSet );
1441 pCharSet = pTmpCharItemSet;
1442 pOtherSet = pTmpOtherItemSet;
1444 bDelete = true;
1447 SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
1448 bool bRet = false;
1449 const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
1450 SwContentNode* pNode = pStt->nNode.GetNode().GetContentNode();
1452 if( pNode && pNode->IsTextNode() )
1454 // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
1455 if (pLayout && pNode->GetTextNode()->getIDocumentSettingAccess()->
1456 get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
1458 SwContentNode* pEndNode = pEnd->nNode.GetNode().GetContentNode();
1459 SwContentNode* pCurrentNode = pEndNode;
1460 auto nStartIndex = pNode->GetIndex();
1461 auto nEndIndex = pEndNode->GetIndex();
1462 SwNodeIndex aIdx( pEnd->nNode.GetNode() );
1463 while ( pCurrentNode != nullptr && nStartIndex <= pCurrentNode->GetIndex() )
1465 if (pCurrentNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT) &&
1466 // remove character formatting only on wholly selected paragraphs
1467 (nStartIndex < pCurrentNode->GetIndex() || pStt->nContent.GetIndex() == 0) &&
1468 (pCurrentNode->GetIndex() < nEndIndex || pEnd->nContent.GetIndex() == pEndNode->Len()))
1470 pCurrentNode->ResetAttr(RES_PARATR_LIST_AUTOFMT);
1471 // reset also paragraph marker
1472 SwIndex nIdx( pCurrentNode, pCurrentNode->Len() );
1473 pCurrentNode->GetTextNode()->RstTextAttr(nIdx, 1);
1475 pCurrentNode = SwNodes::GoPrevious( &aIdx );
1478 // #i27615#
1479 if (rRg.IsInFrontOfLabel())
1481 SwTextNode * pTextNd = pNode->GetTextNode();
1482 if (pLayout)
1484 pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
1486 SwNumRule * pNumRule = pTextNd->GetNumRule();
1488 if ( !pNumRule )
1490 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1491 DELETECHARSETS
1492 return false;
1495 int nLevel = pTextNd->GetActualListLevel();
1497 if (nLevel < 0)
1498 nLevel = 0;
1500 if (nLevel >= MAXLEVEL)
1501 nLevel = MAXLEVEL - 1;
1503 SwNumFormat aNumFormat = pNumRule->Get(o3tl::narrowing<sal_uInt16>(nLevel));
1504 SwCharFormat * pCharFormat =
1505 rDoc.FindCharFormatByName(aNumFormat.GetCharFormatName());
1507 if (pCharFormat)
1509 if (pHistory)
1510 pHistory->Add(pCharFormat->GetAttrSet(), *pCharFormat);
1512 if ( pCharSet )
1513 pCharFormat->SetFormatAttr(*pCharSet);
1516 DELETECHARSETS
1517 return true;
1520 const SwIndex& rSt = pStt->nContent;
1522 // Attributes without an end do not have a range
1523 if ( !bCharAttr && !bOtherAttr )
1525 SfxItemSetFixed<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1>
1526 aTextSet( rDoc.GetAttrPool() );
1527 aTextSet.Put( rChgSet );
1528 if( aTextSet.Count() )
1530 SwRegHistory history( pNode, *pNode, pHistory );
1531 bRet = history.InsertItems(
1532 aTextSet, rSt.GetIndex(), rSt.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr ) || bRet;
1534 if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
1535 && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1537 SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1,
1538 pStt->nNode, pStt->nContent.GetIndex() );
1540 if( pUndo )
1541 pUndo->SaveRedlineData( aPam, true );
1543 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1544 rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
1545 else
1546 rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
1551 // TextAttributes with an end never expand their range
1552 if ( !bCharAttr && !bOtherAttr )
1554 // CharFormat and URL attributes are treated separately!
1555 // TEST_TEMP ToDo: AutoFormat!
1556 SfxItemSetFixed<
1557 RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD,
1558 RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
1559 RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD>
1560 aTextSet(rDoc.GetAttrPool());
1562 aTextSet.Put( rChgSet );
1563 if( aTextSet.Count() )
1565 const sal_Int32 nInsCnt = rSt.GetIndex();
1566 const sal_Int32 nEnd = pStt->nNode == pEnd->nNode
1567 ? pEnd->nContent.GetIndex()
1568 : pNode->Len();
1569 SwRegHistory history( pNode, *pNode, pHistory );
1570 bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags, ppNewTextAttr )
1571 || bRet;
1573 if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
1574 && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1576 // Was text content inserted? (RefMark/TOXMarks without an end)
1577 bool bTextIns = nInsCnt != rSt.GetIndex();
1578 // Was content inserted or set over the selection?
1579 SwPaM aPam( pStt->nNode, bTextIns ? nInsCnt + 1 : nEnd,
1580 pStt->nNode, nInsCnt );
1581 if( pUndo )
1582 pUndo->SaveRedlineData( aPam, bTextIns );
1584 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1585 rDoc.getIDocumentRedlineAccess().AppendRedline(
1586 new SwRangeRedline(
1587 bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ),
1588 true);
1589 else if( bTextIns )
1590 rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
1596 // We always have to set the auto flag for PageDescs that are set at the Node!
1597 if( pOtherSet && pOtherSet->Count() )
1599 SwTableNode* pTableNd;
1600 const SwFormatPageDesc* pDesc;
1601 if( SfxItemState::SET == pOtherSet->GetItemState( RES_PAGEDESC,
1602 false, reinterpret_cast<const SfxPoolItem**>(&pDesc) ))
1604 if( pNode )
1606 // Set auto flag. Only in the template it's without auto!
1607 SwFormatPageDesc aNew( *pDesc );
1609 // Tables now also know line breaks
1610 if( !(nFlags & SetAttrMode::APICALL) &&
1611 nullptr != ( pTableNd = pNode->FindTableNode() ) )
1613 SwTableNode* pCurTableNd = pTableNd;
1614 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1615 pTableNd = pCurTableNd;
1617 // set the table format
1618 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1619 SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1620 pFormat->SetFormatAttr( aNew );
1621 bRet = true;
1623 else
1625 SwContentNode * pFirstNode(pNode);
1626 if (pLayout && pLayout->HasMergedParas())
1628 pFirstNode = sw::GetFirstAndLastNode(*pLayout, pStt->nNode).first;
1630 SwRegHistory aRegH( pFirstNode, *pFirstNode, pHistory );
1631 bRet = pFirstNode->SetAttr( aNew ) || bRet;
1635 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1636 // we know, that there is only one attribute in pOtherSet. We cannot
1637 // perform the following operations, instead we return:
1638 if ( bOtherAttr )
1639 return bRet;
1641 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
1642 if( !pOtherSet->Count() )
1644 DELETECHARSETS
1645 return bRet;
1649 // Tables now also know line breaks
1650 const SvxFormatBreakItem* pBreak;
1651 if( pNode && !(nFlags & SetAttrMode::APICALL) &&
1652 nullptr != (pTableNd = pNode->FindTableNode() ) &&
1653 SfxItemState::SET == pOtherSet->GetItemState( RES_BREAK,
1654 false, reinterpret_cast<const SfxPoolItem**>(&pBreak) ) )
1656 SwTableNode* pCurTableNd = pTableNd;
1657 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1658 pTableNd = pCurTableNd;
1660 // set the table format
1661 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1662 SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1663 pFormat->SetFormatAttr( *pBreak );
1664 bRet = true;
1666 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1667 // we know, that there is only one attribute in pOtherSet. We cannot
1668 // perform the following operations, instead we return:
1669 if ( bOtherAttr )
1670 return bRet;
1672 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
1673 if( !pOtherSet->Count() )
1675 DELETECHARSETS
1676 return bRet;
1681 // If we have a PoolNumRule, create it if needed
1682 const SwNumRuleItem* pRule;
1683 sal_uInt16 nPoolId=0;
1684 if( SfxItemState::SET == pOtherSet->GetItemState( RES_PARATR_NUMRULE,
1685 false, reinterpret_cast<const SfxPoolItem**>(&pRule) ) &&
1686 !rDoc.FindNumRulePtr( pRule->GetValue() ) &&
1687 USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(),
1688 SwGetPoolIdFromName::NumRule )) )
1689 rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId );
1693 SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(rDoc.GetAttrPool());
1694 if (pOtherSet && pOtherSet->Count())
1695 { // actually only RES_BREAK is possible here...
1696 firstSet.Put(*pOtherSet);
1698 SfxItemSetFixed
1699 <RES_PARATR_BEGIN, RES_PAGEDESC,
1700 RES_BREAK+1, RES_FRMATR_END,
1701 XATTR_FILL_FIRST, XATTR_FILL_LAST+1> propsSet(rDoc.GetAttrPool());
1702 if (pOtherSet && pOtherSet->Count())
1704 propsSet.Put(*pOtherSet);
1707 if( !rRg.HasMark() ) // no range
1709 if( !pNode )
1711 DELETECHARSETS
1712 return bRet;
1715 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1717 SwTextNode* pTextNd = pNode->GetTextNode();
1718 const SwIndex& rSt = pStt->nContent;
1719 sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
1720 const OUString& rStr = pTextNd->GetText();
1722 // Special case: if the Cursor is located within a URL attribute, we take over it's area
1723 SwTextAttr const*const pURLAttr(
1724 pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
1725 if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
1727 nMkPos = pURLAttr->GetStart();
1728 nPtPos = *pURLAttr->End();
1730 else
1732 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1733 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1734 pTextNd->GetText(), nPtPos,
1735 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1736 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
1737 true);
1739 if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
1741 nMkPos = aBndry.startPos;
1742 nPtPos = aBndry.endPos;
1744 else
1745 nPtPos = nMkPos = rSt.GetIndex();
1748 // Remove the overriding attributes from the SwpHintsArray,
1749 // if the selection spans across the whole paragraph.
1750 // These attributes are inserted as FormatAttributes and
1751 // never override the TextAttributes!
1752 if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
1753 pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
1755 SwIndex aSt( pTextNd );
1756 if( pHistory )
1758 // Save all attributes for the Undo.
1759 SwRegHistory aRHst( *pTextNd, pHistory );
1760 pTextNd->GetpSwpHints()->Register( &aRHst );
1761 pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1762 if( pTextNd->GetpSwpHints() )
1763 pTextNd->GetpSwpHints()->DeRegister();
1765 else
1766 pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1769 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
1771 SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
1773 if( pUndo )
1774 pUndo->SaveRedlineData( aPam, false );
1776 lcl_SetRedlines(rDoc, aPam);
1779 // the SwRegHistory inserts the attribute into the TextNode!
1780 SwRegHistory history( pNode, *pNode, pHistory );
1782 bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr )
1783 || bRet;
1786 if( pOtherSet && pOtherSet->Count() )
1788 // Need to check for unique item for DrawingLayer items of type NameOrIndex
1789 // and evtl. correct that item to ensure unique names for that type. This call may
1790 // modify/correct entries inside of the given SfxItemSet
1791 SfxItemSet aTempLocalCopy(*pOtherSet);
1793 rDoc.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
1794 bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet;
1797 DELETECHARSETS
1798 return bRet;
1801 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
1803 if( pUndo )
1804 pUndo->SaveRedlineData( rRg, false );
1806 lcl_SetRedlines(rDoc, rRg);
1809 /* now if range */
1810 sal_uLong nNodes = 0;
1812 SwNodeIndex aSt( rDoc.GetNodes() );
1813 SwNodeIndex aEnd( rDoc.GetNodes() );
1814 SwIndex aCntEnd( pEnd->nContent );
1816 if( pNode )
1818 const sal_Int32 nLen = pNode->Len();
1819 if( pStt->nNode != pEnd->nNode )
1820 aCntEnd.Assign( pNode, nLen );
1822 if( pStt->nContent.GetIndex() != 0 || aCntEnd.GetIndex() != nLen )
1824 // the SwRegHistory inserts the attribute into the TextNode!
1825 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1827 SwRegHistory history( pNode, *pNode, pHistory );
1828 bRet = history.InsertItems(*pCharSet,
1829 pStt->nContent.GetIndex(), aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr)
1830 || bRet;
1833 if( pOtherSet && pOtherSet->Count() )
1835 bRet = lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout) || bRet;
1838 // Only selection in a Node.
1839 if( pStt->nNode == pEnd->nNode )
1841 DELETECHARSETS
1842 return bRet;
1844 ++nNodes;
1845 aSt.Assign( pStt->nNode.GetNode(), +1 );
1847 else
1848 aSt = pStt->nNode;
1849 aCntEnd = pEnd->nContent; // aEnd was changed!
1851 else
1852 aSt.Assign( pStt->nNode.GetNode(), +1 );
1854 // aSt points to the first full Node now
1857 * The selection spans more than one Node.
1859 if( pStt->nNode < pEnd->nNode )
1861 pNode = pEnd->nNode.GetNode().GetContentNode();
1862 if(pNode)
1864 if( aCntEnd.GetIndex() != pNode->Len() )
1866 // the SwRegHistory inserts the attribute into the TextNode!
1867 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1869 SwRegHistory history( pNode, *pNode, pHistory );
1870 (void)history.InsertItems(*pCharSet,
1871 0, aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr);
1874 if( pOtherSet && pOtherSet->Count() )
1876 lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout);
1879 ++nNodes;
1880 aEnd = pEnd->nNode;
1882 else
1883 aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1885 else
1886 aEnd = pEnd->nNode;
1888 else
1889 aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1891 // aEnd points BEHIND the last full node now
1893 /* Edit the fully selected Nodes. */
1894 // Reset all attributes from the set!
1895 if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
1897 ::sw::DocumentContentOperationsManager::ParaRstFormat aPara(
1898 pStt, pEnd, pHistory, pCharSet, pLayout);
1899 rDoc.GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
1902 bool bCreateSwpHints = pCharSet && (
1903 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
1904 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
1906 for (SwNodeIndex current = aSt; current < aEnd; ++current)
1908 SwTextNode *const pTNd = current.GetNode().GetTextNode();
1909 if (!pTNd)
1910 continue;
1912 if (pLayout && pLayout->HasMergedParas()
1913 && pTNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
1914 { // not really sure what to do here, but applying to hidden
1915 continue; // nodes doesn't make sense...
1918 if( pHistory )
1920 SwRegHistory aRegH( pTNd, *pTNd, pHistory );
1922 if (pCharSet && pCharSet->Count())
1924 if (SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
1925 : pTNd->GetpSwpHints())
1927 pSwpHints->Register( &aRegH );
1930 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1932 // re-fetch as it may be deleted by SetAttr
1933 if (SwpHints *pSwpHints = pTNd->GetpSwpHints())
1934 pSwpHints->DeRegister();
1937 else
1939 if (pCharSet && pCharSet->Count())
1940 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1942 ++nNodes;
1945 if (pOtherSet && pOtherSet->Count())
1947 for (; aSt < aEnd; ++aSt)
1949 pNode = aSt.GetNode().GetContentNode();
1950 if (!pNode)
1951 continue;
1953 lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout, &aSt);
1954 ++nNodes;
1958 DELETECHARSETS
1959 return (nNodes != 0) || bRet;
1963 namespace sw
1966 namespace mark
1968 bool IsFieldmarkOverlap(SwPaM const& rPaM)
1970 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
1971 sw::CalcBreaks(Breaks, rPaM);
1972 return !Breaks.empty();
1976 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
1981 * Checks if rStart..rEnd mark a range that makes sense to copy.
1983 * IsMoveToFly means the copy is a move to create a fly
1984 * and so existing flys at the edge must not be copied.
1986 static bool IsEmptyRange(const SwPosition& rStart, const SwPosition& rEnd,
1987 SwCopyFlags const flags)
1989 if (rStart == rEnd)
1990 { // check if a fly anchored there would be copied - then copy...
1991 return !IsDestroyFrameAnchoredAtChar(rStart, rStart, rEnd,
1992 (flags & SwCopyFlags::IsMoveToFly)
1993 ? DelContentType::WriterfilterHack|DelContentType::AllMask
1994 : DelContentType::AllMask);
1996 else
1998 return rEnd < rStart;
2002 // Copy an area into this document or into another document
2003 bool
2004 DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos,
2005 SwCopyFlags const flags) const
2007 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
2009 SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
2010 bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
2012 // Catch if there's no copy to do
2013 if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel))
2014 return false;
2016 // Prevent copying into Flys that are anchored in the source range
2017 if (&rDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly))
2019 // Correct the Start-/EndNode
2020 SwNodeOffset nStt = pStt->nNode.GetIndex(),
2021 nEnd = pEnd->nNode.GetIndex(),
2022 nDiff = nEnd - nStt +1;
2023 SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
2024 if( pNd->IsContentNode() && pStt->nContent.GetIndex() )
2026 ++nStt;
2027 --nDiff;
2029 if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
2030 static_cast<SwContentNode*>(pNd)->Len() != pEnd->nContent.GetIndex() )
2032 --nEnd;
2033 --nDiff;
2035 if( nDiff &&
2036 lcl_ChkFlyFly( rDoc, nStt, nEnd, rPos.nNode.GetIndex() ) )
2038 return false;
2042 SwPaM* pRedlineRange = nullptr;
2043 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
2044 (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
2045 pRedlineRange = new SwPaM( rPos );
2047 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
2049 bool bRet = false;
2051 if( &rDoc != &m_rDoc )
2052 { // ordinary copy
2053 bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
2055 else if( ! ( *pStt <= rPos && rPos < *pEnd &&
2056 ( pStt->nNode != pEnd->nNode ||
2057 !pStt->nNode.GetNode().IsTextNode() )) )
2059 // Copy to a position outside of the area, or copy a single TextNode
2060 // Do an ordinary copy
2061 bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
2063 else
2065 // Copy the range in itself
2066 assert(!"mst: this is assumed to be dead code");
2069 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
2070 if( pRedlineRange )
2072 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2073 rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlineRange ), true);
2074 else
2075 rDoc.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
2076 delete pRedlineRange;
2079 return bRet;
2082 /// Delete a full Section of the NodeArray.
2083 /// The passed Node is located somewhere in the designated Section.
2084 void DocumentContentOperationsManager::DeleteSection( SwNode *pNode )
2086 assert(pNode && "Didn't pass a Node.");
2088 SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
2089 : pNode->StartOfSectionNode();
2090 SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
2092 // delete all Flys, Bookmarks, ...
2093 DelFlyInRange( aSttIdx, aEndIdx );
2094 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, RedlineType::Any );
2095 DelBookmarks(aSttIdx, aEndIdx);
2098 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
2099 SwNodeIndex aMvStt( aSttIdx, 1 );
2100 SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true );
2103 m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
2106 void DocumentContentOperationsManager::DeleteDummyChar(
2107 SwPosition const& rPos, sal_Unicode const cDummy)
2109 SwPaM aPam(rPos, rPos);
2110 ++aPam.GetPoint()->nContent;
2111 assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy);
2112 (void) cDummy;
2114 DeleteRangeImpl(aPam);
2116 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
2117 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
2119 m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
2123 void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam )
2125 lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl );
2127 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
2128 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
2130 m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
2134 bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
2136 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
2137 const SwNode* pNd = &rStt.nNode.GetNode();
2138 SwNodeOffset nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
2139 pNd->StartOfSectionIndex();
2140 SwNodeOffset nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
2142 if ( nSectDiff-SwNodeOffset(2) <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
2143 /* #i9185# Prevent getting the node after the end node (see below) */
2144 rEnd.nNode.GetIndex() + 1 == m_rDoc.GetNodes().Count() )
2146 return false;
2150 SwPaM temp(rPam, nullptr);
2151 if (!temp.HasMark())
2153 temp.SetMark();
2155 if (SwTextNode *const pNode = temp.Start()->nNode.GetNode().GetTextNode())
2156 { // rPam may not have nContent set but IsFieldmarkOverlap requires it
2157 pNode->MakeStartIndex(&temp.Start()->nContent);
2159 if (SwTextNode *const pNode = temp.End()->nNode.GetNode().GetTextNode())
2161 pNode->MakeEndIndex(&temp.End()->nContent);
2163 if (sw::mark::IsFieldmarkOverlap(temp))
2164 { // a bit of a problem: we want to completely remove the nodes
2165 // but then how can the CH_TXT_ATR survive?
2166 return false;
2170 // Move hard page breaks to the following Node.
2171 bool bSavePageBreak = false, bSavePageDesc = false;
2173 /* #i9185# This would lead to a segmentation fault if not caught above. */
2174 SwNodeOffset nNextNd = rEnd.nNode.GetIndex() + 1;
2175 SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
2177 if( pTableNd && pNd->IsContentNode() )
2179 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2182 const SfxPoolItem *pItem;
2183 const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
2184 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
2185 false, &pItem ) )
2187 pTableFormat->SetFormatAttr( *pItem );
2188 bSavePageDesc = true;
2191 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
2192 false, &pItem ) )
2194 pTableFormat->SetFormatAttr( *pItem );
2195 bSavePageBreak = true;
2200 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
2201 if( bDoesUndo )
2203 if( !rPam.HasMark() )
2204 rPam.SetMark();
2205 else if( rPam.GetPoint() == &rStt )
2206 rPam.Exchange();
2207 rPam.GetPoint()->nNode++;
2209 SwContentNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetContentNode();
2210 rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
2211 bool bGoNext = (nullptr == pTmpNode);
2212 pTmpNode = rPam.GetMark()->nNode.GetNode().GetContentNode();
2213 rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
2215 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
2217 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2219 SwPosition aTmpPos( *aDelPam.GetPoint() );
2220 if( bGoNext )
2222 pTmpNode = m_rDoc.GetNodes().GoNext( &aTmpPos.nNode );
2223 aTmpPos.nContent.Assign( pTmpNode, 0 );
2225 ::PaMCorrAbs( aDelPam, aTmpPos );
2228 std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete( aDelPam, true ));
2230 *rPam.GetPoint() = *aDelPam.GetPoint();
2231 pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2232 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2233 rPam.DeleteMark();
2235 else
2237 SwNodeRange aRg( rStt.nNode, rEnd.nNode );
2238 rPam.Normalize(false);
2240 // Try to move past the End
2241 if( !rPam.Move( fnMoveForward, GoInNode ) )
2243 // Fair enough, at the Beginning then
2244 rPam.Exchange();
2245 if( !rPam.Move( fnMoveBackward, GoInNode ))
2247 SAL_WARN("sw.core", "DelFullPara: no more Nodes");
2248 return false;
2251 // move bookmarks, redlines etc.
2252 if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
2254 m_rDoc.CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, true );
2256 else
2258 SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
2261 // What's with Flys?
2263 // If there are FlyFrames left, delete these too
2264 for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
2266 SwFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
2267 const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
2268 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
2269 if (pAPos &&
2270 ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
2271 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
2272 // note: here use <= not < like in
2273 // IsDestroyFrameAnchoredAtChar() because of the increment
2274 // of rPam in the bDoesUndo path above!
2275 aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
2277 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
2278 --n;
2283 rPam.DeleteMark();
2284 m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
2286 m_rDoc.getIDocumentState().SetModified();
2288 return true;
2291 // #i100466# Add handling of new optional parameter <bForceJoinNext>
2292 bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM & rPam,
2293 const bool bForceJoinNext )
2295 if ( lcl_StrLenOverflow( rPam ) )
2296 return false;
2298 bool const ret = lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
2299 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
2300 : &DocumentContentOperationsManager::DeleteAndJoinImpl,
2301 bForceJoinNext );
2303 return ret;
2306 // It seems that this is mostly used by SwDoc internals; the only
2307 // way to call this from the outside seems to be the special case in
2308 // SwDoc::CopyRange (but I have not managed to actually hit that case).
2309 bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
2311 // nothing moved: return
2312 const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
2313 if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
2314 return false;
2316 assert(!sw::mark::IsFieldmarkOverlap(rPaM)); // probably an invalid redline was created?
2318 // Save the paragraph anchored Flys, so that they can be moved.
2319 SaveFlyArr aSaveFlyArr;
2320 SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
2322 // save redlines (if DOC_MOVEREDLINES is used)
2323 SaveRedlines_t aSaveRedl;
2324 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2326 lcl_SaveRedlines( rPaM, aSaveRedl );
2328 // #i17764# unfortunately, code below relies on undos being
2329 // in a particular order, and presence of bookmarks
2330 // will change this order. Hence, we delete bookmarks
2331 // here without undo.
2332 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
2333 DelBookmarks(
2334 pStt->nNode,
2335 pEnd->nNode,
2336 nullptr,
2337 &pStt->nContent,
2338 &pEnd->nContent);
2341 bool bUpdateFootnote = false;
2342 SwFootnoteIdxs aTmpFntIdx;
2344 std::unique_ptr<SwUndoMove> pUndoMove;
2345 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2347 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
2348 pUndoMove.reset(new SwUndoMove( rPaM, rPos ));
2349 pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
2351 else
2353 bUpdateFootnote = lcl_SaveFootnote( pStt->nNode, pEnd->nNode, rPos.nNode,
2354 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
2355 &pStt->nContent, &pEnd->nContent );
2358 bool bSplit = false;
2359 SwPaM aSavePam( rPos, rPos );
2361 // Move the SPoint to the beginning of the range
2362 if( rPaM.GetPoint() == pEnd )
2363 rPaM.Exchange();
2365 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
2366 SwTextNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTextNode();
2367 bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
2369 // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
2370 // However, this does not update the cursor. So we create a TextNode to keep
2371 // updating the indices. After the Move the Node is optionally deleted.
2372 SwTextNode * pTNd = rPos.nNode.GetNode().GetTextNode();
2373 if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
2374 ( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
2376 bSplit = true;
2377 const sal_Int32 nMkContent = rPaM.GetMark()->nContent.GetIndex();
2379 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
2380 pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
2382 SwTextNode * pOrigNode = pTNd;
2383 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
2384 *aSavePam.GetPoint() == rPos);
2385 assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
2386 assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
2387 assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
2389 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
2390 [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool)
2392 if (!pContentStore->Empty())
2394 pContentStore->Restore(m_rDoc, pOrigNode->GetIndex()-SwNodeOffset(1), 0, true, false, eMode);
2397 pTNd = pTNd->SplitContentNode(rPos, &restoreFunc)->GetTextNode();
2399 //A new node was inserted before the orig pTNd and the content up to
2400 //rPos moved into it. The old node is returned with the remainder
2401 //of the content in it.
2403 //aSavePam was created with rPos, it continues to point to the
2404 //old node, but with the *original* content index into the node.
2405 //Seeing as all the orignode content before that index has
2406 //been removed, the new index into the original node should now be set
2407 //to 0 and the content index of rPos should also be adapted to the
2408 //truncated node
2409 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
2410 *aSavePam.GetPoint() == rPos);
2411 assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
2412 assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
2413 assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
2414 aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0);
2415 rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
2417 // correct the PaM!
2418 if( rPos.nNode == rPaM.GetMark()->nNode )
2420 rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-SwNodeOffset(1);
2421 rPaM.GetMark()->nContent.Assign( pTNd, nMkContent );
2425 // Put back the Pam by one "content"; so that it's always outside of
2426 // the manipulated range.
2427 // tdf#99692 don't Move() back if that would end up in another node
2428 // because moving backward is not necessarily the inverse of forward then.
2429 // (but do Move() back if we have split the node)
2430 const bool bNullContent = !bSplit && aSavePam.GetPoint()->nContent == 0;
2431 if( bNullContent )
2433 aSavePam.GetPoint()->nNode--;
2434 aSavePam.GetPoint()->nContent.Assign(aSavePam.GetContentNode(), 0);
2436 else
2438 bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
2439 assert(success);
2440 (void) success;
2443 // Copy all Bookmarks that are within the Move range into an array,
2444 // that saves the position as an offset.
2445 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2446 DelBookmarks(
2447 pStt->nNode,
2448 pEnd->nNode,
2449 &aSaveBkmks,
2450 &pStt->nContent,
2451 &pEnd->nContent);
2453 // If there is no range anymore due to the above deletions (e.g. the
2454 // footnotes got deleted), it's still a valid Move!
2455 if( *rPaM.GetPoint() != *rPaM.GetMark() )
2457 // now do the actual move
2458 m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
2460 // after a MoveRange() the Mark is deleted
2461 if ( rPaM.HasMark() ) // => no Move occurred!
2463 return false;
2466 else
2467 rPaM.DeleteMark();
2469 OSL_ENSURE( *aSavePam.GetMark() == rPos ||
2470 ( aSavePam.GetMark()->nNode.GetNode().GetContentNode() == nullptr ),
2471 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2472 *aSavePam.GetMark() = rPos;
2474 rPaM.SetMark(); // create a Sel. around the new range
2475 pTNd = aSavePam.GetNode().GetTextNode();
2476 assert(!m_rDoc.GetIDocumentUndoRedo().DoesUndo());
2477 bool bRemove = true;
2478 // Do two Nodes have to be joined at the SavePam?
2479 if (bSplit && pTNd)
2481 if (pTNd->CanJoinNext())
2483 // Always join next, because <pTNd> has to stay as it is.
2484 // A join previous from its next would more or less delete <pTNd>
2485 pTNd->JoinNext();
2486 bRemove = false;
2489 if (bNullContent)
2491 aSavePam.GetPoint()->nNode++;
2492 aSavePam.GetPoint()->nContent.Assign( aSavePam.GetContentNode(), 0 );
2494 else if (bRemove) // No move forward after joining with next paragraph
2496 aSavePam.Move( fnMoveForward, GoInContent );
2499 // Insert the Bookmarks back into the Document.
2500 *rPaM.GetMark() = *aSavePam.Start();
2501 for(auto& rBkmk : aSaveBkmks)
2502 rBkmk.SetInDoc(
2503 &m_rDoc,
2504 rPaM.GetMark()->nNode,
2505 &rPaM.GetMark()->nContent);
2506 *rPaM.GetPoint() = *aSavePam.End();
2508 // Move the Flys to the new position.
2509 // note: rPos is at the end here; can't really tell flys that used to be
2510 // at the start of rPam from flys that used to be at the end of rPam
2511 // unfortunately, so some of them are going to end up with wrong anchor...
2512 RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &(rPos.nNode) );
2514 // restore redlines (if DOC_MOVEREDLINES is used)
2515 if( !aSaveRedl.empty() )
2517 lcl_RestoreRedlines( m_rDoc, *aSavePam.Start(), aSaveRedl );
2520 if( bUpdateFootnote )
2522 if( !aTmpFntIdx.empty() )
2524 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2525 aTmpFntIdx.clear();
2528 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
2531 m_rDoc.getIDocumentState().SetModified();
2532 return true;
2535 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
2536 SwMoveFlags eMvFlags )
2538 // Moves all Nodes to the new position.
2539 // Bookmarks are moved too (currently without Undo support).
2541 // If footnotes are being moved to the special section, remove them now.
2543 // Or else delete the Frames for all footnotes that are being moved
2544 // and have it rebuild after the Move (footnotes can change pages).
2545 // Additionally we have to correct the FootnoteIdx array's sorting.
2546 bool bUpdateFootnote = false;
2547 SwFootnoteIdxs aTmpFntIdx;
2549 std::unique_ptr<SwUndoMove> pUndo;
2550 if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2552 pUndo.reset(new SwUndoMove( m_rDoc, rRange, rPos ));
2554 else
2556 bUpdateFootnote = lcl_SaveFootnote( rRange.aStart, rRange.aEnd, rPos,
2557 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
2560 SaveRedlines_t aSaveRedl;
2561 std::vector<SwRangeRedline*> aSavRedlInsPosArr;
2562 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2564 lcl_SaveRedlines( rRange, aSaveRedl );
2566 // Find all RedLines that end at the InsPos.
2567 // These have to be moved back to the "old" position after the Move.
2568 SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rPos.GetNode(), RedlineType::Any );
2569 if( SwRedlineTable::npos != nRedlPos )
2571 const SwPosition *pRStt, *pREnd;
2572 do {
2573 SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
2574 pRStt = pTmp->Start();
2575 pREnd = pTmp->End();
2576 if( pREnd->nNode == rPos && pRStt->nNode < rPos )
2578 aSavRedlInsPosArr.push_back( pTmp );
2580 } while( pRStt->nNode < rPos && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
2584 // Copy all Bookmarks that are within the Move range into an array
2585 // that stores all references to positions as an offset.
2586 // The final mapping happens after the Move.
2587 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2588 DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
2590 // Save the paragraph-bound Flys, so that they can be moved.
2591 SaveFlyArr aSaveFlyArr;
2592 if( !m_rDoc.GetSpzFrameFormats()->empty() )
2593 SaveFlyInRange( rRange, aSaveFlyArr );
2595 // Set it to before the Position, so that it cannot be moved further.
2596 SwNodeIndex aIdx( rPos, -1 );
2598 std::unique_ptr<SwNodeIndex> pSaveInsPos;
2599 if( pUndo )
2600 pSaveInsPos.reset(new SwNodeIndex( rRange.aStart, -1 ));
2602 // move the Nodes
2603 bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
2604 if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rPos, !bNoDelFrames ) )
2606 ++aIdx; // again back to old position
2607 if( pSaveInsPos )
2608 ++(*pSaveInsPos);
2610 else
2612 aIdx = rRange.aStart;
2613 pUndo.reset();
2616 // move the Flys to the new position
2617 if( !aSaveFlyArr.empty() )
2619 SwPosition const tmp(aIdx);
2620 RestFlyInRange(aSaveFlyArr, tmp, nullptr);
2623 // Add the Bookmarks back to the Document
2624 for(auto& rBkmk : aSaveBkmks)
2625 rBkmk.SetInDoc(&m_rDoc, aIdx);
2627 if( !aSavRedlInsPosArr.empty() )
2629 SwNode* pNewNd = &aIdx.GetNode();
2630 for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
2632 if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) )
2634 SwPosition* pEnd = pTmp->End();
2635 pEnd->nNode = aIdx;
2636 pEnd->nContent.Assign( pNewNd->GetContentNode(), 0 );
2641 if( !aSaveRedl.empty() )
2642 lcl_RestoreRedlines( m_rDoc, aIdx.GetIndex(), aSaveRedl );
2644 if( pUndo )
2646 pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
2647 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2650 pSaveInsPos.reset();
2652 if( bUpdateFootnote )
2654 if( !aTmpFntIdx.empty() )
2656 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2657 aTmpFntIdx.clear();
2660 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
2663 m_rDoc.getIDocumentState().SetModified();
2664 return true;
2667 void DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos )
2669 SwNodeIndex aIdx( rPaM.Start()->nNode );
2670 bool bJoinText = aIdx.GetNode().IsTextNode();
2671 bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
2672 aIdx--; // in front of the move area!
2674 bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
2675 if( !bRet || bOneNode )
2676 return;
2678 if( bJoinText )
2679 ++aIdx;
2680 SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
2681 SwNodeIndex aNxtIdx( aIdx );
2682 if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
2684 { // Block so SwIndex into node is deleted before Join
2685 m_rDoc.CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex(pTextNd,
2686 pTextNd->GetText().getLength()) ), 0, true );
2688 pTextNd->JoinNext();
2692 // Overwrite only uses the point of the PaM, the mark is ignored; characters
2693 // are replaced from point until the end of the node; at the end of the node,
2694 // characters are inserted.
2695 bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
2697 assert(rStr.getLength());
2698 SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
2699 if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2701 if( 1 == rStr.getLength() )
2702 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
2703 m_rDoc.DeleteAutoCorrExceptWord();
2706 SwTextNode *pNode = rPt.nNode.GetNode().GetTextNode();
2707 if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
2709 return false;
2712 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2714 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2717 const size_t nOldAttrCnt = pNode->GetpSwpHints()
2718 ? pNode->GetpSwpHints()->Count() : 0;
2719 SwDataChanged aTmp( rRg );
2720 SwIndex& rIdx = rPt.nContent;
2721 sal_Int32 const nActualStart(rIdx.GetIndex());
2722 sal_Int32 nStart = 0;
2724 bool bOldExpFlg = pNode->IsIgnoreDontExpand();
2725 pNode->SetIgnoreDontExpand( true );
2727 for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
2729 // start behind the characters (to fix the attributes!)
2730 nStart = rIdx.GetIndex();
2731 if (nStart < pNode->GetText().getLength())
2733 lcl_SkipAttr( pNode, rIdx, nStart );
2735 sal_Unicode c = rStr[ nCnt ];
2736 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2738 bool bMerged(false);
2739 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
2741 SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
2742 SwUndoOverwrite *const pUndoOW(
2743 dynamic_cast<SwUndoOverwrite *>(pUndo) );
2744 if (pUndoOW)
2746 // if CanGrouping() returns true it's already merged
2747 bMerged = pUndoOW->CanGrouping(m_rDoc, rPt, c);
2750 if (!bMerged)
2752 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2753 std::make_unique<SwUndoOverwrite>(m_rDoc, rPt, c) );
2756 else
2758 // start behind the characters (to fix the attributes!)
2759 if (nStart < pNode->GetText().getLength())
2760 ++rIdx;
2761 pNode->InsertText( OUString(c), rIdx, SwInsertFlags::EMPTYEXPAND );
2762 if( nStart+1 < rIdx.GetIndex() )
2764 rIdx = nStart;
2765 pNode->EraseText( rIdx, 1 );
2766 ++rIdx;
2770 pNode->SetIgnoreDontExpand( bOldExpFlg );
2772 const size_t nNewAttrCnt = pNode->GetpSwpHints()
2773 ? pNode->GetpSwpHints()->Count() : 0;
2774 if( nOldAttrCnt != nNewAttrCnt )
2776 const SwUpdateAttr aHint(0,0,0);
2777 pNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
2780 if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
2781 !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
2783 SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
2784 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
2786 else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2788 // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2789 // characters are also included in aPam
2790 SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
2791 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
2794 m_rDoc.getIDocumentState().SetModified();
2795 return true;
2798 bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
2799 const SwInsertFlags nInsertMode )
2801 // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
2802 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2804 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
2805 m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg );
2806 if (eOld != m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags())
2807 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
2810 // fetching DoesUndo is surprisingly expensive
2811 bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
2812 if (bDoesUndo)
2813 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2815 const SwPosition& rPos = *rRg.GetPoint();
2817 if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
2819 if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
2821 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
2823 m_rDoc.DeleteAutoCorrExceptWord();
2826 SwTextNode *const pNode = rPos.nNode.GetNode().GetTextNode();
2827 if(!pNode)
2828 return false;
2830 SwDataChanged aTmp( rRg );
2832 if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
2834 OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2835 if (bDoesUndo)
2837 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2838 std::make_unique<SwUndoInsert>(rPos.nNode,
2839 rPos.nContent.GetIndex(), ins.getLength(), nInsertMode));
2842 else
2843 { // if Undo and grouping is enabled, everything changes!
2844 SwUndoInsert * pUndo = nullptr;
2846 // don't group the start if hints at the start should be expanded
2847 if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
2849 SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
2850 SwUndoInsert *const pUndoInsert(
2851 dynamic_cast<SwUndoInsert *>(pLastUndo) );
2852 if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
2854 pUndo = pUndoInsert;
2858 CharClass const& rCC = GetAppCharClass();
2859 sal_Int32 nInsPos = rPos.nContent.GetIndex();
2861 if (!pUndo)
2863 pUndo = new SwUndoInsert( rPos.nNode, nInsPos, 0, nInsertMode,
2864 !rCC.isLetterNumeric( rStr, 0 ) );
2865 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
2868 OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2870 for (sal_Int32 i = 0; i < ins.getLength(); ++i)
2872 nInsPos++;
2873 // if CanGrouping() returns true, everything has already been done
2874 if (!pUndo->CanGrouping(ins[i]))
2876 pUndo = new SwUndoInsert(rPos.nNode, nInsPos, 1, nInsertMode,
2877 !rCC.isLetterNumeric(ins, i));
2878 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
2883 // To-Do - add 'SwExtraRedlineTable' also ?
2884 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2886 SwPaM aPam( rPos.nNode, aTmp.GetContent(),
2887 rPos.nNode, rPos.nContent.GetIndex());
2888 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2890 m_rDoc.getIDocumentRedlineAccess().AppendRedline(
2891 new SwRangeRedline( RedlineType::Insert, aPam ), true);
2893 else
2895 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
2899 m_rDoc.getIDocumentState().SetModified();
2900 return true;
2903 void DocumentContentOperationsManager::SetIME(bool bIME)
2905 m_bIME = bIME;
2908 bool DocumentContentOperationsManager::GetIME() const
2910 return m_bIME;
2913 void DocumentContentOperationsManager::TransliterateText(
2914 const SwPaM& rPaM,
2915 utl::TransliterationWrapper& rTrans )
2917 std::unique_ptr<SwUndoTransliterate> pUndo;
2918 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2919 pUndo.reset(new SwUndoTransliterate( rPaM, rTrans ));
2921 const SwPosition* pStt = rPaM.Start(),
2922 * pEnd = rPaM.End();
2923 SwNodeOffset nSttNd = pStt->nNode.GetIndex(),
2924 nEndNd = pEnd->nNode.GetIndex();
2925 sal_Int32 nSttCnt = pStt->nContent.GetIndex();
2926 sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
2928 SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
2929 if( pStt == pEnd && pTNd ) // no selection?
2931 // set current word as 'area of effect'
2933 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
2934 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
2935 pTNd->GetText(), nSttCnt,
2936 g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
2937 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
2938 true);
2940 if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
2942 nSttCnt = aBndry.startPos;
2943 nEndCnt = aBndry.endPos;
2947 bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn();
2948 // as a workaround for a known performance problem, switch off redlining
2949 // to avoid freezing, if transliteration could result too many redlines
2950 if ( bUseRedlining )
2952 const sal_uLong nMaxRedlines = 500;
2953 const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE;
2954 sal_uLong nAffectedNodes = 0;
2955 sal_uLong nAffectedChars = nEndCnt;
2956 SwNodeIndex aIdx( pStt->nNode );
2957 for( ; aIdx.GetIndex() <= nEndNd; ++aIdx )
2959 SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode();
2961 // don't count not text nodes or empty text nodes
2962 if( !pAffectedNode || pAffectedNode->GetText().isEmpty() )
2963 continue;
2965 nAffectedNodes++;
2967 // count characters of the node (the last - maybe partially
2968 // selected - node was counted at initialization of nAffectedChars)
2969 if( aIdx.GetIndex() < nEndNd )
2970 nAffectedChars += pAffectedNode->GetText().getLength();
2972 // transliteration creates n redlines for n nodes, except in the
2973 // case of title case, where it creates n redlines for n words
2974 if( nAffectedNodes > nMaxRedlines ||
2975 // estimate word count based on the character count, where
2976 // 6 = average English word length is ~5 letters + space
2977 ( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) )
2979 bUseRedlining = false;
2980 break;
2985 if( nSttNd != nEndNd ) // is more than one text node involved?
2987 // iterate over all effected text nodes, the first and the last one
2988 // may be incomplete because the selection starts and/or ends there
2990 SwNodeIndex aIdx( pStt->nNode );
2991 if( nSttCnt )
2993 ++aIdx;
2994 if( pTNd )
2996 pTNd->TransliterateText(
2997 rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
3001 for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
3003 pTNd = aIdx.GetNode().GetTextNode();
3004 if (pTNd)
3006 pTNd->TransliterateText(
3007 rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
3011 if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
3013 pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining );
3016 else if( pTNd && nSttCnt < nEndCnt )
3018 pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining );
3020 if( pUndo && pUndo->HasData() )
3022 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
3024 m_rDoc.getIDocumentState().SetModified();
3027 SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic(
3028 const SwPaM &rRg,
3029 const OUString& rGrfName,
3030 const OUString& rFltName,
3031 const Graphic* pGraphic,
3032 const SfxItemSet* pFlyAttrSet,
3033 const SfxItemSet* pGrfAttrSet,
3034 SwFrameFormat* pFrameFormat )
3036 if( !pFrameFormat )
3037 pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
3038 SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
3039 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
3040 rGrfName, rFltName, pGraphic,
3041 m_rDoc.GetDfltGrfFormatColl() );
3042 SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
3043 pFlyAttrSet, pGrfAttrSet, pFrameFormat );
3044 return pSwFlyFrameFormat;
3047 SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphicObject(
3048 const SwPaM &rRg, const GraphicObject& rGrfObj,
3049 const SfxItemSet* pFlyAttrSet,
3050 const SfxItemSet* pGrfAttrSet )
3052 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
3053 SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
3054 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
3055 rGrfObj, m_rDoc.GetDfltGrfFormatColl() );
3056 SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
3057 pFlyAttrSet, pGrfAttrSet, pFrameFormat );
3058 return pSwFlyFrameFormat;
3061 SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject(
3062 const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
3063 SfxItemSet* pFlyAttrSet)
3065 sal_uInt16 nId = RES_POOLFRM_OLE;
3066 if (xObj.is())
3068 SvGlobalName aClassName( xObj->getClassID() );
3069 if (SotExchange::IsMath(aClassName))
3071 nId = RES_POOLFRM_FORMEL;
3075 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId );
3077 return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
3078 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
3079 xObj,
3080 m_rDoc.GetDfltGrfFormatColl() ),
3081 pFlyAttrSet, nullptr,
3082 pFrameFormat );
3085 SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName,
3086 sal_Int64 nAspect,
3087 const SfxItemSet* pFlyAttrSet,
3088 const SfxItemSet* pGrfAttrSet)
3090 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE );
3092 return InsNoTextNode( *rRg.GetPoint(),
3093 m_rDoc.GetNodes().MakeOLENode(
3094 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
3095 rObjName,
3096 nAspect,
3097 m_rDoc.GetDfltGrfFormatColl(),
3098 nullptr ),
3099 pFlyAttrSet, pGrfAttrSet,
3100 pFrameFormat );
3103 void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
3104 const OUString& rFltName, const Graphic* pGraphic )
3106 SwGrfNode *pGrfNd;
3107 if( !(( !rPam.HasMark()
3108 || rPam.GetPoint()->nNode.GetIndex() == rPam.GetMark()->nNode.GetIndex() )
3109 && nullptr != ( pGrfNd = rPam.GetPoint()->nNode.GetNode().GetGrfNode() )) )
3110 return;
3112 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3114 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoReRead>(rPam, *pGrfNd));
3117 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
3118 if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
3119 GetMirrorGrf().GetValue() )
3120 pGrfNd->SetAttr( SwMirrorGrf() );
3122 pGrfNd->ReRead( rGrfName, rFltName, pGraphic );
3123 m_rDoc.getIDocumentState().SetModified();
3126 // Insert drawing object, which has to be already inserted in the DrawModel
3127 SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj(
3128 const SwPaM &rRg,
3129 SdrObject& rDrawObj,
3130 const SfxItemSet& rFlyAttrSet )
3132 SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
3134 const SwFormatAnchor* pAnchor = nullptr;
3135 rFlyAttrSet.GetItemState( RES_ANCHOR, false, reinterpret_cast<const SfxPoolItem**>(&pAnchor) );
3136 pFormat->SetFormatAttr( rFlyAttrSet );
3138 // Didn't set the Anchor yet?
3139 // DrawObjecte must never end up in the Header/Footer!
3140 RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
3141 const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
3143 const SwNodeIndex* pChkIdx = nullptr;
3144 if ( pAnchor == nullptr )
3146 pChkIdx = &rRg.GetPoint()->nNode;
3148 else if ( bIsAtContent )
3150 pChkIdx =
3151 pAnchor->GetContentAnchor() ? &pAnchor->GetContentAnchor()->nNode : &rRg.GetPoint()->nNode;
3154 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
3155 if( pChkIdx != nullptr
3156 && ::CheckControlLayer( &rDrawObj )
3157 && m_rDoc.IsInHeaderFooter( *pChkIdx ) )
3159 // apply at-page anchor format
3160 eAnchorId = RndStdIds::FLY_AT_PAGE;
3161 pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
3163 else if( pAnchor == nullptr
3164 || ( bIsAtContent
3165 && pAnchor->GetContentAnchor() == nullptr ) )
3167 // apply anchor format
3168 SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
3169 eAnchorId = aAnch.GetAnchorId();
3170 if ( eAnchorId == RndStdIds::FLY_AT_FLY )
3172 const SwStartNode* pStartNode = rRg.GetNode().FindFlyStartNode();
3173 assert(pStartNode);
3174 SwPosition aPos(*pStartNode);
3175 aAnch.SetAnchor( &aPos );
3177 else
3179 aAnch.SetAnchor( rRg.GetPoint() );
3180 if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
3182 eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
3183 aAnch.SetType( eAnchorId );
3186 pFormat->SetFormatAttr( aAnch );
3189 // insert text attribute for as-character anchored drawing object
3190 if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
3192 bool bAnchorAtPageAsFallback = true;
3193 const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
3194 if ( rDrawObjAnchorFormat.GetContentAnchor() != nullptr )
3196 SwTextNode* pAnchorTextNode =
3197 rDrawObjAnchorFormat.GetContentAnchor()->nNode.GetNode().GetTextNode();
3198 if ( pAnchorTextNode != nullptr )
3200 const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->nContent.GetIndex();
3201 SwFormatFlyCnt aFormat( pFormat );
3202 pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
3203 bAnchorAtPageAsFallback = false;
3207 if ( bAnchorAtPageAsFallback )
3209 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
3210 pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
3214 SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
3216 // Create Frames if necessary
3217 if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
3219 // create layout representation
3220 pFormat->MakeFrames();
3221 // #i42319# - follow-up of #i35635#
3222 // move object to visible layer
3223 // #i79391#
3224 if ( pContact->GetAnchorFrame() )
3226 pContact->MoveObjToVisibleLayer( &rDrawObj );
3230 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3232 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0) );
3235 m_rDoc.getIDocumentState().SetModified();
3236 return pFormat;
3239 bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
3241 SwContentNode *pNode = rPos.nNode.GetNode().GetContentNode();
3242 if(nullptr == pNode)
3243 return false;
3246 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
3247 // After that they can be before/after the position.
3248 SwDataChanged aTmp( m_rDoc, rPos );
3251 SwUndoSplitNode* pUndo = nullptr;
3252 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3254 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3255 // insert the Undo object (currently only for TextNode)
3256 if( pNode->IsTextNode() )
3258 pUndo = new SwUndoSplitNode( m_rDoc, rPos, bChkTableStart );
3259 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3263 // Update the rsid of the old and the new node unless
3264 // the old node is split at the beginning or at the end
3265 SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
3266 const sal_Int32 nPos = rPos.nContent.GetIndex();
3267 if( pTextNode && nPos && nPos != pTextNode->Len() )
3269 m_rDoc.UpdateParRsid( pTextNode );
3272 //JP 28.01.97: Special case for SplitNode at table start:
3273 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
3274 // then insert a paragraph before it.
3275 if( bChkTableStart && !rPos.nContent.GetIndex() && pNode->IsTextNode() )
3277 SwNodeOffset nPrevPos = rPos.nNode.GetIndex() - 1;
3278 const SwTableNode* pTableNd;
3279 const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
3280 if( pNd->IsStartNode() &&
3281 SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
3282 nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
3283 ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
3284 SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
3285 || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
3286 || pNd->IsContentNode() ))
3288 if( pNd->IsContentNode() )
3290 //JP 30.04.99 Bug 65660:
3291 // There are no page breaks outside of the normal body area,
3292 // so this is not a valid condition to insert a paragraph.
3293 if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
3294 pNd = nullptr;
3295 else
3297 // Only if the table has page breaks!
3298 const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
3299 if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
3300 SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
3301 pNd = nullptr;
3305 if( pNd )
3307 SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
3308 SwNodeIndex( *pTableNd ),
3309 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
3310 if( pTextNd )
3312 const_cast<SwPosition&>(rPos).nNode = pTableNd->GetIndex()-SwNodeOffset(1);
3313 const_cast<SwPosition&>(rPos).nContent.Assign( pTextNd, 0 );
3315 // only add page breaks/styles to the body area
3316 if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
3318 SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
3319 const SfxPoolItem *pItem;
3320 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
3321 false, &pItem ) )
3323 pTextNd->SetAttr( *pItem );
3324 pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
3326 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
3327 false, &pItem ) )
3329 pTextNd->SetAttr( *pItem );
3330 pFrameFormat->ResetFormatAttr( RES_BREAK );
3334 if( pUndo )
3335 pUndo->SetTableFlag();
3336 m_rDoc.getIDocumentState().SetModified();
3337 return true;
3343 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
3344 pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
3345 assert(pNode->IsTextNode());
3346 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool bAtStart)> restoreFunc(
3347 [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool const bAtStart)
3349 if (!pContentStore->Empty())
3350 { // move all bookmarks, TOXMarks, FlyAtCnt
3351 pContentStore->Restore(m_rDoc, rPos.nNode.GetIndex()-SwNodeOffset(1), 0, true, bAtStart && (eMode & sw::mark::RestoreMode::Flys), eMode);
3353 if (eMode & sw::mark::RestoreMode::NonFlys)
3355 // To-Do - add 'SwExtraRedlineTable' also ?
3356 if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
3357 (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() &&
3358 !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))
3360 SwPaM aPam( rPos );
3361 aPam.SetMark();
3362 aPam.Move( fnMoveBackward );
3363 if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
3365 m_rDoc.getIDocumentRedlineAccess().AppendRedline(
3366 new SwRangeRedline(RedlineType::Insert, aPam), true);
3368 else
3370 m_rDoc.getIDocumentRedlineAccess().SplitRedline(aPam);
3375 pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc);
3377 m_rDoc.getIDocumentState().SetModified();
3378 return true;
3381 bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos )
3383 // create new node before EndOfContent
3384 SwTextNode * pCurNode = rPos.nNode.GetNode().GetTextNode();
3385 if( !pCurNode )
3387 // so then one can be created!
3388 SwNodeIndex aIdx( rPos.nNode, 1 );
3389 pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx,
3390 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
3392 else
3393 pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
3395 rPos.nNode++;
3396 rPos.nContent.Assign( pCurNode, 0 );
3398 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3400 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsert>( rPos.nNode ) );
3403 // To-Do - add 'SwExtraRedlineTable' also ?
3404 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
3406 SwPaM aPam( rPos );
3407 aPam.SetMark();
3408 aPam.Move( fnMoveBackward );
3409 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
3410 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
3411 else
3412 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
3415 m_rDoc.getIDocumentState().SetModified();
3416 return true;
3419 bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
3420 const bool bRegExReplace )
3422 // unfortunately replace works slightly differently from delete,
3423 // so we cannot use lcl_DoWithBreaks here...
3425 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
3427 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
3428 aPam.Normalize(false);
3429 if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
3431 aPam.Move(fnMoveBackward);
3433 OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
3435 sw::CalcBreaks(Breaks, aPam);
3437 while (!Breaks.empty() // skip over prefix of dummy chars
3438 && (aPam.GetMark()->nNode.GetIndex() == Breaks.begin()->first)
3439 && (aPam.GetMark()->nContent.GetIndex() == Breaks.begin()->second))
3441 // skip!
3442 ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
3443 Breaks.erase(Breaks.begin());
3445 *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
3447 if (Breaks.empty())
3449 // park aPam somewhere so it does not point to node that is deleted
3450 aPam.DeleteMark();
3451 *aPam.GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
3452 return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
3455 // Deletion must be split into several parts if the text node
3456 // contains a text attribute with end and with dummy character
3457 // and the selection does not contain the text attribute completely,
3458 // but overlaps its start (left), where the dummy character is.
3460 bool bRet( true );
3461 // iterate from end to start, to avoid invalidating the offsets!
3462 auto iter( Breaks.rbegin() );
3463 SwNodeOffset nOffset(0);
3464 SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
3465 OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
3466 SwPosition & rEnd( *aPam.End() );
3467 SwPosition & rStart( *aPam.Start() );
3469 // set end of temp pam to original end (undo Move backward above)
3470 rEnd = *rPam.End();
3471 // after first deletion, rEnd will point into the original text node again!
3473 while (iter != Breaks.rend())
3475 rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
3476 if (rStart < rEnd) // check if part is empty
3478 bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
3479 ? DeleteAndJoinWithRedlineImpl(aPam)
3480 : DeleteAndJoinImpl(aPam, false);
3481 nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
3483 rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
3484 ++iter;
3487 rStart = *rPam.Start(); // set to original start
3488 assert(rStart < rEnd && "replace part empty!");
3489 if (rStart < rEnd) // check if part is empty
3491 bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
3494 rPam = aPam; // update original pam (is this required?)
3496 return bRet;
3499 bool DocumentContentOperationsManager::InsertPoolItem(
3500 const SwPaM &rRg,
3501 const SfxPoolItem &rHt,
3502 const SetAttrMode nFlags,
3503 SwRootFrame const*const pLayout,
3504 SwTextAttr **ppNewTextAttr)
3506 if (utl::ConfigManager::IsFuzzing())
3507 return false;
3509 SwDataChanged aTmp( rRg );
3510 std::unique_ptr<SwUndoAttr> pUndoAttr;
3511 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3513 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3514 pUndoAttr.reset(new SwUndoAttr( rRg, rHt, nFlags ));
3517 SfxItemSet aSet( m_rDoc.GetAttrPool(), rHt.Which(), rHt.Which() );
3518 aSet.Put( rHt );
3519 const bool bRet = lcl_InsAttr(m_rDoc, rRg, aSet, nFlags, pUndoAttr.get(), pLayout, ppNewTextAttr);
3521 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3523 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
3526 if( bRet )
3528 m_rDoc.getIDocumentState().SetModified();
3530 return bRet;
3533 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet,
3534 const SetAttrMode nFlags, SwRootFrame const*const pLayout)
3536 SwDataChanged aTmp( rRg );
3537 std::unique_ptr<SwUndoAttr> pUndoAttr;
3538 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3540 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3541 pUndoAttr.reset(new SwUndoAttr( rRg, rSet, nFlags ));
3544 bool bRet = lcl_InsAttr(m_rDoc, rRg, rSet, nFlags, pUndoAttr.get(), pLayout, /*ppNewTextAttr*/nullptr );
3546 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3548 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
3551 if( bRet )
3552 m_rDoc.getIDocumentState().SetModified();
3555 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos )
3557 const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
3558 if ( !pTNd )
3559 return;
3561 const OUString& rText = pTNd->GetText();
3562 sal_Int32 nIdx = 0;
3563 while (nIdx < rText.getLength())
3565 sal_Unicode const cCh = rText[nIdx];
3566 if (('\t' != cCh) && (' ' != cCh))
3568 break;
3570 ++nIdx;
3573 if ( nIdx > 0 )
3575 SwPaM aPam(rPos);
3576 aPam.GetPoint()->nContent = 0;
3577 aPam.SetMark();
3578 aPam.GetMark()->nContent = nIdx;
3579 DeleteRange( aPam );
3583 // Copy method from SwDoc - "copy Flys in Flys"
3584 /// note: rRg/rInsPos *exclude* a partially selected start text node;
3585 /// pCopiedPaM *includes* a partially selected start text node
3586 void DocumentContentOperationsManager::CopyWithFlyInFly(
3587 const SwNodeRange& rRg,
3588 const SwNodeIndex& rInsPos,
3589 const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
3590 const bool bMakeNewFrames,
3591 const bool bDelRedlines,
3592 const bool bCopyFlyAtFly,
3593 SwCopyFlags const flags) const
3595 assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
3596 assert(!pCopiedPaM || pCopiedPaM->second.nNode <= rInsPos);
3598 SwDoc& rDest = rInsPos.GetNode().GetDoc();
3599 SwNodeIndex aSavePos( rInsPos );
3601 if (rRg.aStart != rRg.aEnd)
3603 bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
3604 --aSavePos;
3605 SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
3607 // insert behind the already copied start node
3608 m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true );
3609 aRedlRest.Restore();
3611 if (bEndIsEqualEndPos)
3613 const_cast<SwNodeIndex&>(rRg.aEnd).Assign(aSavePos.GetNode(), +1);
3617 // Also copy all bookmarks
3618 // guess this must be done before the DelDummyNodes below as that
3619 // deletes nodes so would mess up the index arithmetic
3620 // sw_fieldmarkhide: also needs to be done before making frames
3621 if (m_rDoc.getIDocumentMarkAccess()->getAllMarksCount())
3623 SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
3624 SwPosition targetPos(SwNodeIndex(aSavePos,
3625 SwNodeOffset(rRg.aStart != rRg.aEnd ? +1 : 0)));
3626 if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
3628 // there is 1 (partially selected, maybe) paragraph before
3629 assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->nNode);
3630 // only use the passed in target SwPosition if the source PaM point
3631 // is on a different node; if it was the same node then the target
3632 // position was likely moved along by the copy operation and now
3633 // points to the end of the range!
3634 targetPos = pCopiedPaM->second;
3637 sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, targetPos);
3640 if (rRg.aStart != rRg.aEnd)
3642 bool isRecreateEndNode(false);
3643 if (bMakeNewFrames) // tdf#130685 only after aRedlRest
3644 { // recreate from previous node (could be merged now)
3645 if (SwTextNode *const pNode = aSavePos.GetNode().GetTextNode())
3647 o3tl::sorted_vector<SwTextFrame*> frames;
3648 SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode();
3649 if (pEndNode)
3651 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
3652 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
3654 if (pFrame->getRootFrame()->HasMergedParas())
3656 frames.insert(pFrame);
3660 sw::RecreateStartTextFrames(*pNode);
3661 if (!frames.empty())
3662 { // tdf#132187 check if the end node needs new frames
3663 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
3664 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
3666 if (pFrame->getRootFrame()->HasMergedParas())
3668 auto const it = frames.find(pFrame);
3669 if (it != frames.end())
3671 frames.erase(it);
3675 if (!frames.empty()) // existing frame was deleted
3676 { // all layouts because MakeFrames recreates all layouts
3677 pEndNode->DelFrames(nullptr);
3678 isRecreateEndNode = true;
3683 bool const isAtStartOfSection(aSavePos.GetNode().IsStartNode());
3684 ++aSavePos;
3685 if (bMakeNewFrames)
3687 // it's possible that CheckParaRedlineMerge() deleted frames
3688 // on rInsPos so have to include it, but it must not be included
3689 // if it was the first node in the document so that MakeFrames()
3690 // will find the existing (wasn't deleted) frame on it
3691 SwNodeIndex const end(rInsPos,
3692 SwNodeOffset((!isRecreateEndNode || isAtStartOfSection)
3693 ? 0 : +1));
3694 ::MakeFrames(&rDest, aSavePos, end);
3698 #if OSL_DEBUG_LEVEL > 0
3700 //JP 17.06.99: Bug 66973 - check count only if the selection is in
3701 // the same section or there's no section, because sections that are
3702 // not fully selected are not copied.
3703 const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
3704 SwNodeIndex aTmpI( rRg.aEnd, -1 );
3705 const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
3706 if( pSSectNd == pESectNd &&
3707 !rRg.aStart.GetNode().IsSectionNode() &&
3708 !aTmpI.GetNode().IsEndNode() )
3710 // If the range starts with a SwStartNode, it isn't copied
3711 SwNodeOffset offset( (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0 );
3712 OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
3713 rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
3714 "An insufficient number of nodes were copied!" );
3717 #endif
3720 ::sw::UndoGuard const undoGuard(rDest.GetIDocumentUndoRedo());
3721 CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr,
3722 // see comment below regarding use of pCopiedPaM->second
3723 (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
3724 ? pCopiedPaM->second.nNode
3725 : aSavePos,
3726 bCopyFlyAtFly,
3727 flags);
3730 SwNodeRange aCpyRange( aSavePos, rInsPos );
3732 if( bDelRedlines && ( RedlineFlags::DeleteRedlines & rDest.getIDocumentRedlineAccess().GetRedlineFlags() ))
3733 lcl_DeleteRedlines( rRg, aCpyRange );
3735 rDest.GetNodes().DelDummyNodes( aCpyRange );
3738 // note: for the redline Show/Hide this must be in sync with
3739 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
3740 void DocumentContentOperationsManager::CopyFlyInFlyImpl(
3741 const SwNodeRange& rRg,
3742 SwPaM const*const pCopiedPaM,
3743 const SwNodeIndex& rStartIdx,
3744 const bool bCopyFlyAtFly,
3745 SwCopyFlags const flags) const
3747 assert(!pCopiedPaM || pCopiedPaM->End()->nNode == rRg.aEnd);
3749 // First collect all Flys, sort them according to their ordering number,
3750 // and then only copy them. This maintains the ordering numbers (which are only
3751 // managed in the DrawModel).
3752 SwDoc& rDest = rStartIdx.GetNode().GetDoc();
3753 std::set< ZSortFly > aSet;
3754 const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
3756 SwTextBoxHelper::SavedLink aOldTextBoxes;
3757 SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes);
3759 for ( size_t n = 0; n < nArrLen; ++n )
3761 SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
3762 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
3763 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
3764 if ( !pAPos )
3765 continue;
3766 bool bAdd = false;
3767 SwNodeOffset nSkipAfter = pAPos->nNode.GetIndex();
3768 SwNodeOffset nStart = rRg.aStart.GetIndex();
3769 switch ( pAnchor->GetAnchorId() )
3771 case RndStdIds::FLY_AT_FLY:
3772 if(bCopyFlyAtFly)
3773 ++nSkipAfter;
3774 else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
3775 ++nStart;
3776 break;
3777 case RndStdIds::FLY_AT_PARA:
3779 bAdd = IsSelectFrameAnchoredAtPara(*pAPos,
3780 pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
3781 pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
3782 (flags & SwCopyFlags::IsMoveToFly)
3783 ? DelContentType::AllMask|DelContentType::WriterfilterHack
3784 : DelContentType::AllMask);
3786 break;
3787 case RndStdIds::FLY_AT_CHAR:
3789 bAdd = IsDestroyFrameAnchoredAtChar(*pAPos,
3790 pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
3791 pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
3792 (flags & SwCopyFlags::IsMoveToFly)
3793 ? DelContentType::AllMask|DelContentType::WriterfilterHack
3794 : DelContentType::AllMask);
3796 break;
3797 default:
3798 continue;
3800 if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
3802 if (nStart > nSkipAfter)
3803 continue;
3804 if (pAPos->nNode > rRg.aEnd)
3805 continue;
3806 //frames at the last source node are not always copied:
3807 //- if the node is empty and is the last node of the document or a table cell
3808 // or a text frame then they have to be copied
3809 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
3810 //- to-character bound objects are copied if their index is <= nEndContentIndex
3811 if (pAPos->nNode < rRg.aEnd)
3812 bAdd = true;
3813 if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
3815 if (!bAdd)
3817 // technically old code checked nContent of AT_FLY which is pointless
3818 bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->nContent.GetIndex();
3822 if( bAdd )
3824 aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
3828 // Store all copied (and also the newly created) frames in another array.
3829 // They are stored as matching the originals, so that we will be later
3830 // able to build the chains accordingly.
3831 std::vector< SwFrameFormat* > aVecSwFrameFormat;
3832 std::set< ZSortFly >::const_iterator it=aSet.begin();
3834 while (it != aSet.end())
3836 // #i59964#
3837 // correct determination of new anchor position
3838 SwFormatAnchor aAnchor( *(*it).GetAnchor() );
3839 assert( aAnchor.GetContentAnchor() != nullptr );
3840 SwPosition newPos = *aAnchor.GetContentAnchor();
3841 // for at-paragraph and at-character anchored objects the new anchor
3842 // position can *not* be determined by the difference of the current
3843 // anchor position to the start of the copied range, because not
3844 // complete selected sections in the copied range aren't copied - see
3845 // method <SwNodes::CopyNodes(..)>.
3846 // Thus, the new anchor position in the destination document is found
3847 // by counting the text nodes.
3848 if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
3849 (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
3851 // First, determine number of anchor text node in the copied range.
3852 // Note: The anchor text node *have* to be inside the copied range.
3853 sal_uLong nAnchorTextNdNumInRange( 0 );
3854 bool bAnchorTextNdFound( false );
3855 // start at the first node for which flys are copied
3856 SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->nNode : rRg.aStart);
3857 while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
3859 if ( aIdx.GetNode().IsTextNode() )
3861 ++nAnchorTextNdNumInRange;
3862 bAnchorTextNdFound = aAnchor.GetContentAnchor()->nNode == aIdx;
3865 ++aIdx;
3868 if ( !bAnchorTextNdFound )
3870 // This case can *not* happen, but to be robust take the first
3871 // text node in the destination document.
3872 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
3873 nAnchorTextNdNumInRange = 1;
3875 // Second, search corresponding text node in destination document
3876 // by counting forward from start insert position <rStartIdx> the
3877 // determined number of text nodes.
3878 aIdx = rStartIdx;
3879 SwNodeIndex aAnchorNdIdx( rStartIdx );
3880 const SwNode& aEndOfContentNd =
3881 aIdx.GetNode().GetNodes().GetEndOfContent();
3882 while ( nAnchorTextNdNumInRange > 0 &&
3883 &(aIdx.GetNode()) != &aEndOfContentNd )
3885 if ( aIdx.GetNode().IsTextNode() )
3887 --nAnchorTextNdNumInRange;
3888 aAnchorNdIdx = aIdx;
3891 ++aIdx;
3893 if ( !aAnchorNdIdx.GetNode().IsTextNode() )
3895 // This case can *not* happen, but to be robust take the first
3896 // text node in the destination document.
3897 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
3898 aAnchorNdIdx = rStartIdx;
3899 while ( !aAnchorNdIdx.GetNode().IsTextNode() )
3901 ++aAnchorNdIdx;
3904 // apply found anchor text node as new anchor position
3905 newPos.nNode = aAnchorNdIdx;
3907 else
3909 SwNodeOffset nOffset = newPos.nNode.GetIndex() - rRg.aStart.GetIndex();
3910 SwNodeIndex aIdx( rStartIdx, nOffset );
3911 newPos.nNode = aIdx;
3913 // Set the character bound Flys back at the original character
3914 if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
3915 newPos.nNode.GetNode().IsTextNode() )
3917 // only if pCopiedPaM: care about partially selected start node
3918 sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->nNode == aAnchor.GetContentAnchor()->nNode
3919 ? newPos.nContent.GetIndex() - pCopiedPaM->Start()->nContent.GetIndex()
3920 : newPos.nContent.GetIndex();
3921 newPos.nContent.Assign(newPos.nNode.GetNode().GetTextNode(), nContent);
3923 else
3925 newPos.nContent.Assign( nullptr, 0 );
3927 aAnchor.SetAnchor( &newPos );
3929 // Check recursion: if copying content inside the same frame, then don't copy the format.
3930 if( &rDest == &m_rDoc )
3932 const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
3933 const SwStartNode* pSNd;
3934 if( rContent.GetContentIdx() &&
3935 nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
3936 pSNd->GetIndex() < rStartIdx.GetIndex() &&
3937 rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
3939 it = aSet.erase(it);
3940 continue;
3944 // Ignore TextBoxes, they are already handled in
3945 // sw::DocumentLayoutManager::CopyLayoutFormat().
3946 if (SwTextBoxHelper::isTextBox(it->GetFormat(), RES_FLYFRMFMT))
3948 it = aSet.erase(it);
3949 continue;
3952 // Copy the format and set the new anchor
3953 aVecSwFrameFormat.push_back( rDest.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
3954 aAnchor, false, true ) );
3955 ++it;
3958 // Rebuild as much as possible of all chains that are available in the original,
3959 OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
3960 if ( aSet.size() != aVecSwFrameFormat.size() )
3961 return;
3963 size_t n = 0;
3964 for (const auto& rFlyN : aSet)
3966 const SwFrameFormat *pFormatN = rFlyN.GetFormat();
3967 const SwFormatChain &rChain = pFormatN->GetChain();
3968 int nCnt = int(nullptr != rChain.GetPrev());
3969 nCnt += rChain.GetNext() ? 1: 0;
3970 size_t k = 0;
3971 for (const auto& rFlyK : aSet)
3973 const SwFrameFormat *pFormatK = rFlyK.GetFormat();
3974 if ( rChain.GetPrev() == pFormatK )
3976 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
3977 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
3978 --nCnt;
3980 else if ( rChain.GetNext() == pFormatK )
3982 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
3983 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
3984 --nCnt;
3986 ++k;
3988 ++n;
3991 // Re-create content property of draw formats, knowing how old shapes
3992 // were paired with old fly formats (aOldTextBoxes) and that aSet is
3993 // parallel with aVecSwFrameFormat.
3994 SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes);
3998 * Reset the text's hard formatting
4000 /** @params pArgs contains the document's ChrFormatTable
4001 * Is need for selections at the beginning/end and with no SSelection.
4003 bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode* pNd, void* pArgs )
4005 ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
4006 if (pPara->pLayout && pPara->pLayout->HasMergedParas()
4007 && pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
4009 return true; // skip hidden, since new items aren't applied
4011 SwTextNode * pTextNode = pNd->GetTextNode();
4012 if( pTextNode && pTextNode->GetpSwpHints() )
4014 SwIndex aSt( pTextNode, 0 );
4015 sal_Int32 nEnd = pTextNode->Len();
4017 if( &pPara->pSttNd->nNode.GetNode() == pTextNode &&
4018 pPara->pSttNd->nContent.GetIndex() )
4019 aSt = pPara->pSttNd->nContent.GetIndex();
4021 if( &pPara->pEndNd->nNode.GetNode() == pNd )
4022 nEnd = pPara->pEndNd->nContent.GetIndex();
4024 if( pPara->pHistory )
4026 // Save all attributes for the Undo.
4027 SwRegHistory aRHst( *pTextNode, pPara->pHistory );
4028 pTextNode->GetpSwpHints()->Register( &aRHst );
4029 pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
4030 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
4031 if( pTextNode->GetpSwpHints() )
4032 pTextNode->GetpSwpHints()->DeRegister();
4034 else
4035 pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
4036 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
4038 return true;
4041 DocumentContentOperationsManager::~DocumentContentOperationsManager()
4044 //Private methods
4046 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
4048 assert(m_rDoc.getIDocumentRedlineAccess().IsRedlineOn());
4050 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
4052 if (*rPam.GetPoint() == *rPam.GetMark())
4054 return false; // do not add empty redlines
4057 std::vector<SwRangeRedline*> redlines;
4059 auto pRedline(std::make_unique<SwRangeRedline>(RedlineType::Delete, rPam));
4060 if (pRedline->HasValidRange())
4062 redlines.push_back(pRedline.release());
4064 else // sigh ... why is such a selection even possible...
4065 { // split it up so we get one SwUndoRedlineDelete per inserted RL
4066 redlines = GetAllValidRanges(std::move(pRedline));
4070 if (redlines.empty())
4072 return false;
4075 // tdf#54819 current redlining needs also modification of paragraph style and
4076 // attributes added to the same grouped Undo
4077 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
4078 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
4080 auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
4081 std::vector<std::unique_ptr<SwUndo>> MarkUndos;
4082 for (auto iter = rDMA.getAnnotationMarksBegin();
4083 iter != rDMA.getAnnotationMarksEnd(); )
4085 // tdf#111524 remove annotation marks that have their field
4086 // characters deleted
4087 SwPosition const& rEndPos((**iter).GetMarkEnd());
4088 if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
4090 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4092 MarkUndos.emplace_back(std::make_unique<SwUndoDeleteBookmark>(**iter));
4094 // iter is into annotation mark vector so must be dereferenced!
4095 rDMA.deleteMark(&**iter);
4096 // this invalidates iter, have to start over...
4097 iter = rDMA.getAnnotationMarksBegin();
4099 else
4100 { // marks are sorted by start
4101 if (*rPam.End() < (**iter).GetMarkStart())
4103 break;
4105 ++iter;
4109 // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
4110 if (*rPam.GetPoint() != *rPam.GetMark())
4111 m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam);
4113 std::vector<std::unique_ptr<SwUndoRedlineDelete>> undos;
4114 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4116 // this should no longer happen in calls from the UI but maybe via API
4117 // (randomTest and testTdf54819 triggers it)
4118 SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
4119 "sw.core", "redlines will be moved in DeleteAndJoin");
4120 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
4121 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
4123 for (SwRangeRedline * pRedline : redlines)
4125 assert(pRedline->HasValidRange());
4126 undos.emplace_back(std::make_unique<SwUndoRedlineDelete>(
4127 *pRedline, SwUndoId::DELETE));
4129 const SwRewriter aRewriter = undos.front()->GetRewriter();
4130 // can only group a single undo action
4131 if (MarkUndos.empty() && undos.size() == 1
4132 && m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
4134 SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
4135 SwUndoRedlineDelete *const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete*>(pLastUndo));
4136 bool const bMerged = pUndoRedlineDel
4137 && pUndoRedlineDel->CanGrouping(*undos.front());
4138 if (!bMerged)
4140 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(undos.front()));
4142 undos.clear(); // prevent unmatched EndUndo
4144 else
4146 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter);
4147 for (auto& it : MarkUndos)
4149 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
4151 for (auto & it : undos)
4153 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
4158 for (SwRangeRedline *const pRedline : redlines)
4160 // note: 1. the pRedline can still be merged & deleted
4161 // 2. the impl. can even DeleteAndJoin the range => no plain PaM
4162 std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*pRedline->GetMark()));
4163 pCursor->SetMark();
4164 *pCursor->GetPoint() = *pRedline->GetPoint();
4165 m_rDoc.getIDocumentRedlineAccess().AppendRedline(pRedline, true);
4166 // sw_redlinehide: 2 reasons why this is needed:
4167 // 1. it's the first redline in node => RedlineDelText was sent but ignored
4168 // 2. redline spans multiple nodes => must merge text frames
4169 sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor);
4171 m_rDoc.getIDocumentState().SetModified();
4173 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4175 if (!undos.empty())
4177 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
4179 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
4182 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
4183 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
4185 return true;
4188 bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam,
4189 const bool bForceJoinNext )
4191 bool bJoinText, bJoinPrev;
4192 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
4193 // #i100466#
4194 if ( bForceJoinNext )
4196 bJoinPrev = false;
4200 bool const bSuccess( DeleteRangeImpl( rPam ) );
4201 if (!bSuccess)
4202 return false;
4205 if( bJoinText )
4207 ::sw_JoinText( rPam, bJoinPrev );
4210 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
4211 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
4213 m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
4216 return true;
4219 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, const bool)
4221 // Move all cursors out of the deleted range, but first copy the
4222 // passed PaM, because it could be a cursor that would be moved!
4223 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
4224 ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
4226 bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
4227 if (bSuccess)
4228 { // now copy position from temp copy to given PaM
4229 *rPam.GetPoint() = *aDelPam.GetPoint();
4232 return bSuccess;
4235 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam)
4237 SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
4239 if (!rPam.HasMark()
4240 || (*pStt == *pEnd && !IsFlySelectedByCursor(m_rDoc, *pStt, *pEnd)))
4242 return false;
4245 if( m_rDoc.GetAutoCorrExceptWord() )
4247 // if necessary the saved Word for the exception
4248 if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->nNode != pEnd->nNode ||
4249 pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
4250 !m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt ))
4251 { m_rDoc.DeleteAutoCorrExceptWord(); }
4255 // Delete all empty TextHints at the Mark's position
4256 SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
4257 SwpHints* pHts;
4258 if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
4260 const sal_Int32 nMkCntPos = rPam.GetMark()->nContent.GetIndex();
4261 for( size_t n = pHts->Count(); n; )
4263 const SwTextAttr* pAttr = pHts->Get( --n );
4264 if( nMkCntPos > pAttr->GetStart() )
4265 break;
4267 const sal_Int32 *pEndIdx;
4268 if( nMkCntPos == pAttr->GetStart() &&
4269 nullptr != (pEndIdx = pAttr->End()) &&
4270 *pEndIdx == pAttr->GetStart() )
4271 pTextNd->DestroyAttr( pHts->Cut( n ) );
4277 // Send DataChanged before deletion, so that we still know
4278 // which objects are in the range.
4279 // Afterwards they could be before/after the Position.
4280 SwDataChanged aTmp( rPam );
4283 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4285 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
4286 bool bMerged(false);
4287 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
4289 SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
4290 SwUndoDelete *const pUndoDelete(
4291 dynamic_cast<SwUndoDelete *>(pLastUndo) );
4292 if (pUndoDelete)
4294 bMerged = pUndoDelete->CanGrouping(m_rDoc, rPam);
4295 // if CanGrouping() returns true it's already merged
4298 if (!bMerged)
4300 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelete>( rPam ) );
4303 m_rDoc.getIDocumentState().SetModified();
4305 return true;
4308 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4309 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
4311 // Delete and move all "Flys at the paragraph", which are within the Selection
4312 DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode,
4313 &rPam.GetMark()->nContent, &rPam.GetPoint()->nContent);
4314 DelBookmarks(
4315 pStt->nNode,
4316 pEnd->nNode,
4317 nullptr,
4318 &pStt->nContent,
4319 &pEnd->nContent);
4321 SwNodeIndex aSttIdx( pStt->nNode );
4322 SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
4324 do { // middle checked loop!
4325 if( pCNd )
4327 SwTextNode * pStartTextNode( pCNd->GetTextNode() );
4328 if ( pStartTextNode )
4330 // now move the Content to the new Node
4331 bool bOneNd = pStt->nNode == pEnd->nNode;
4332 const sal_Int32 nLen = ( bOneNd ? pEnd->nContent.GetIndex()
4333 : pCNd->Len() )
4334 - pStt->nContent.GetIndex();
4336 // Don't call again, if already empty
4337 if( nLen )
4339 pStartTextNode->EraseText( pStt->nContent, nLen );
4341 if( !pStartTextNode->Len() )
4343 // METADATA: remove reference if empty (consider node deleted)
4344 pStartTextNode->RemoveMetadataReference();
4348 if( bOneNd ) // that's it
4349 break;
4351 ++aSttIdx;
4353 else
4355 // So that there are no indices left registered when deleted,
4356 // we remove a SwPaM from the Content here.
4357 pStt->nContent.Assign( nullptr, 0 );
4361 pCNd = pEnd->nNode.GetNode().GetContentNode();
4362 if( pCNd )
4364 SwTextNode * pEndTextNode( pCNd->GetTextNode() );
4365 if( pEndTextNode )
4367 // if already empty, don't call again
4368 if( pEnd->nContent.GetIndex() )
4370 SwIndex aIdx( pCNd, 0 );
4371 pEndTextNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
4373 if( !pEndTextNode->Len() )
4375 // METADATA: remove reference if empty (consider node deleted)
4376 pEndTextNode->RemoveMetadataReference();
4380 else
4382 // So that there are no indices left registered when deleted,
4383 // we remove a SwPaM from the Content here.
4384 pEnd->nContent.Assign( nullptr, 0 );
4388 // if the end is not a content node, delete it as well
4389 SwNodeOffset nEnd = pEnd->nNode.GetIndex();
4390 if( pCNd == nullptr )
4391 nEnd++;
4393 if( aSttIdx != nEnd )
4395 // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
4396 SwNode *pTmpNd;
4397 while (pEnd == rPam.GetPoint()
4398 && nEnd + SwNodeOffset(2) < m_rDoc.GetNodes().Count()
4399 && (pTmpNd = m_rDoc.GetNodes()[nEnd + 1])->IsEndNode()
4400 && pTmpNd->StartOfSectionNode()->IsSectionNode()
4401 && aSttIdx <= pTmpNd->StartOfSectionNode()->GetIndex())
4403 SwNodeRange range(*pTmpNd->StartOfSectionNode(), *pTmpNd);
4404 m_rDoc.GetNodes().SectionUp(&range);
4405 --nEnd; // account for deleted start node
4408 // delete the Nodes from the NodesArray
4409 m_rDoc.GetNodes().Delete( aSttIdx, nEnd - aSttIdx.GetIndex() );
4412 // If the Node that contained the Cursor has been deleted,
4413 // the Content has to be assigned to the current Content.
4414 pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
4415 pStt->nContent.GetIndex() );
4417 // If we deleted across Node boundaries we have to correct the PaM,
4418 // because they are in different Nodes now.
4419 // Also, the Selection is revoked.
4420 *pEnd = *pStt;
4421 rPam.DeleteMark();
4423 } while( false );
4425 m_rDoc.getIDocumentState().SetModified();
4427 return true;
4430 // It's possible to call Replace with a PaM that spans 2 paragraphs:
4431 // search with regex for "$", then replace _all_
4432 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr,
4433 const bool bRegExReplace )
4435 if (!rPam.HasMark())
4436 return false;
4438 bool bJoinText, bJoinPrev;
4439 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
4442 // Create a copy of the Cursor in order to move all Pams from
4443 // the other views out of the deletion range.
4444 // Except for itself!
4445 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
4446 ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
4448 SwPosition *pStt = aDelPam.Start(),
4449 *pEnd = aDelPam.End();
4450 bool bOneNode = pStt->nNode == pEnd->nNode;
4452 // Own Undo?
4453 OUString sRepl( rStr );
4454 SwTextNode* pTextNd = pStt->nNode.GetNode().GetTextNode();
4455 sal_Int32 nStt = pStt->nContent.GetIndex();
4456 sal_Int32 nEnd;
4458 SwDataChanged aTmp( aDelPam );
4460 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
4462 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
4463 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4465 // this should no longer happen in calls from the UI but maybe via API
4466 SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
4467 "sw.core", "redlines will be moved in ReplaceRange");
4469 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
4471 // If any Redline will change (split!) the node
4472 const ::sw::mark::IMark* pBkmk =
4473 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
4474 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
4475 ::sw::mark::InsertMode::New);
4477 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
4478 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
4480 *aDelPam.GetPoint() = pBkmk->GetMarkPos();
4481 if(pBkmk->IsExpanded())
4482 *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
4483 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
4484 pStt = aDelPam.Start();
4485 pTextNd = pStt->nNode.GetNode().GetTextNode();
4486 nStt = pStt->nContent.GetIndex();
4489 if( !sRepl.isEmpty() )
4491 // Apply the first character's attributes to the ReplaceText
4492 SfxItemSetFixed
4493 <RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
4494 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( m_rDoc.GetAttrPool() );
4495 pTextNd->GetParaAttr( aSet, nStt+1, nStt+1 );
4497 aSet.ClearItem( RES_TXTATR_REFMARK );
4498 aSet.ClearItem( RES_TXTATR_TOXMARK );
4499 aSet.ClearItem( RES_TXTATR_CJK_RUBY );
4500 aSet.ClearItem( RES_TXTATR_INETFMT );
4501 aSet.ClearItem( RES_TXTATR_META );
4502 aSet.ClearItem( RES_TXTATR_METAFIELD );
4504 if( aDelPam.GetPoint() != aDelPam.End() )
4505 aDelPam.Exchange();
4507 // Remember the End
4508 SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
4509 const sal_Int32 nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
4511 bool bFirst = true;
4512 OUString sIns;
4513 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
4515 InsertString( aDelPam, sIns );
4516 if( bFirst )
4518 SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
4519 const sal_Int32 nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
4521 SplitNode( *aDelPam.GetPoint(), false );
4523 ++aMkNd;
4524 aDelPam.GetMark()->nNode = aMkNd;
4525 aDelPam.GetMark()->nContent.Assign(
4526 aMkNd.GetNode().GetContentNode(), nMkCnt );
4527 bFirst = false;
4529 else
4530 SplitNode( *aDelPam.GetPoint(), false );
4532 if( !sIns.isEmpty() )
4534 InsertString( aDelPam, sIns );
4537 SwPaM aTmpRange( *aDelPam.GetPoint() );
4538 aTmpRange.SetMark();
4540 ++aPtNd;
4541 aDelPam.GetPoint()->nNode = aPtNd;
4542 aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
4543 nPtCnt);
4544 *aTmpRange.GetMark() = *aDelPam.GetPoint();
4546 m_rDoc.RstTextAttrs( aTmpRange );
4547 InsertItemSet( aTmpRange, aSet );
4550 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4552 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
4553 std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE ));
4555 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true);
4557 *rPam.GetMark() = *aDelPam.GetMark();
4558 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
4560 *aDelPam.GetPoint() = *rPam.GetPoint();
4561 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
4563 // If any Redline will change (split!) the node
4564 const ::sw::mark::IMark* pBkmk =
4565 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
4566 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
4567 ::sw::mark::InsertMode::New);
4569 SwIndex& rIdx = aDelPam.GetPoint()->nContent;
4570 rIdx.Assign( nullptr, 0 );
4571 aDelPam.GetMark()->nContent = rIdx;
4572 rPam.GetPoint()->nNode = SwNodeOffset(0);
4573 rPam.GetPoint()->nContent = rIdx;
4574 *rPam.GetMark() = *rPam.GetPoint();
4575 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
4577 *rPam.GetPoint() = pBkmk->GetMarkPos();
4578 if(pBkmk->IsExpanded())
4579 *rPam.GetMark() = pBkmk->GetOtherMarkPos();
4580 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
4582 bJoinText = false;
4584 else
4586 assert((pStt->nNode == pEnd->nNode ||
4587 ( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
4588 !pEnd->nContent.GetIndex() )) &&
4589 "invalid range: Point and Mark on different nodes" );
4591 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4592 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, RedlineType::Any );
4594 SwUndoReplace* pUndoRpl = nullptr;
4595 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
4596 if (bDoesUndo)
4598 pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
4599 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoRpl));
4601 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
4603 if( aDelPam.GetPoint() != pStt )
4604 aDelPam.Exchange();
4606 SwNodeIndex aPtNd( pStt->nNode, -1 );
4607 const sal_Int32 nPtCnt = pStt->nContent.GetIndex();
4609 // Set the values again, if Frames or footnotes on the Text have been removed.
4610 nStt = nPtCnt;
4611 nEnd = bOneNode ? pEnd->nContent.GetIndex()
4612 : pTextNd->GetText().getLength();
4614 bool bFirst = true;
4615 OUString sIns;
4616 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
4618 if (!bFirst || nStt == pTextNd->GetText().getLength())
4620 InsertString( aDelPam, sIns );
4622 else if( nStt < nEnd || !sIns.isEmpty() )
4624 pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4626 SplitNode( *pStt, false);
4627 bFirst = false;
4630 if( bFirst || !sIns.isEmpty() )
4632 if (!bFirst || nStt == pTextNd->GetText().getLength())
4634 InsertString( aDelPam, sIns );
4636 else if( nStt < nEnd || !sIns.isEmpty() )
4638 pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4642 *rPam.GetPoint() = *aDelPam.GetMark();
4643 ++aPtNd;
4644 rPam.GetMark()->nNode = aPtNd;
4645 rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
4646 nPtCnt );
4648 if (bJoinText)
4650 assert(rPam.GetPoint() == rPam.End());
4651 // move so that SetEnd remembers position after sw_JoinText
4652 rPam.Move(fnMoveBackward);
4654 else if (aDelPam.GetPoint() == pStt) // backward selection?
4656 assert(*rPam.GetMark() <= *rPam.GetPoint());
4657 rPam.Exchange(); // swap so that rPam is backwards
4660 if( pUndoRpl )
4662 pUndoRpl->SetEnd(rPam);
4667 bool bRet(true);
4668 if (bJoinText)
4670 bRet = ::sw_JoinText(rPam, bJoinPrev);
4673 m_rDoc.getIDocumentState().SetModified();
4674 return bRet;
4677 SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode,
4678 const SfxItemSet* pFlyAttrSet,
4679 const SfxItemSet* pGrfAttrSet,
4680 SwFrameFormat* pFrameFormat)
4682 SwFlyFrameFormat *pFormat = nullptr;
4683 if( pNode )
4685 pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
4686 pFlyAttrSet, pFrameFormat );
4687 if( pGrfAttrSet )
4688 pNode->SetAttr( *pGrfAttrSet );
4690 return pFormat;
4693 #define NUMRULE_STATE \
4694 std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
4695 std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
4697 #define PUSH_NUMRULE_STATE \
4698 lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
4700 #define POP_NUMRULE_STATE \
4701 lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
4703 static void lcl_PushNumruleState(
4704 std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
4705 std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
4706 const SwTextNode *pDestTextNd )
4708 // Safe numrule item at destination.
4709 // #i86492# - Safe also <ListId> item of destination.
4710 const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
4711 if (pAttrSet == nullptr)
4712 return;
4714 const SfxPoolItem* pItem(nullptr);
4715 if (SfxItemState::SET == pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem))
4717 aNumRuleItemHolderIfSet.reset(&pItem->Clone()->StaticWhichCast(RES_PARATR_NUMRULE));
4720 if (SfxItemState::SET == pAttrSet->GetItemState(RES_PARATR_LIST_ID, false, &pItem))
4722 aListIdItemHolderIfSet.reset(&pItem->Clone()->StaticWhichCast(RES_PARATR_LIST_ID));
4726 static void lcl_PopNumruleState(
4727 const std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
4728 const std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
4729 SwTextNode *pDestTextNd, const SwPaM& rPam )
4731 /* If only a part of one paragraph is copied
4732 restore the numrule at the destination. */
4733 // #i86492# - restore also <ListId> item
4734 if ( lcl_MarksWholeNode(rPam) )
4735 return;
4737 if (aNumRuleItemHolderIfSet)
4739 pDestTextNd->SetAttr(*aNumRuleItemHolderIfSet);
4741 else
4743 pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
4746 if (aListIdItemHolderIfSet)
4748 pDestTextNd->SetAttr(*aListIdItemHolderIfSet);
4750 else
4752 pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
4756 bool DocumentContentOperationsManager::CopyImpl(SwPaM& rPam, SwPosition& rPos,
4757 SwCopyFlags const flags,
4758 SwPaM *const pCopyRange) const
4760 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
4762 sw::CalcBreaks(Breaks, rPam, true);
4764 if (Breaks.empty())
4766 return CopyImplImpl(rPam, rPos, flags, pCopyRange);
4769 SwPosition const & rSelectionEnd( *rPam.End() );
4771 bool bRet(true);
4772 bool bFirst(true);
4773 // iterate from end to start, ... don't think it's necessary here?
4774 auto iter( Breaks.rbegin() );
4775 SwNodeOffset nOffset(0);
4776 SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
4777 SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
4778 SwPosition & rEnd( *aPam.End() );
4779 SwPosition & rStart( *aPam.Start() );
4780 SwPaM copyRange(rPos, rPos);
4782 while (iter != Breaks.rend())
4784 rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
4785 if (rStart < rEnd) // check if part is empty
4787 // pass in copyRange member as rPos; should work ...
4788 bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
4789 nOffset = iter->first - rStart.nNode.GetIndex(); // fly nodes...
4790 if (pCopyRange)
4792 if (bFirst)
4794 pCopyRange->SetMark();
4795 *pCopyRange->GetMark() = *copyRange.End();
4797 *pCopyRange->GetPoint() = *copyRange.Start();
4799 bFirst = false;
4801 rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
4802 ++iter;
4805 rStart = *rPam.Start(); // set to original start
4806 if (rStart < rEnd) // check if part is empty
4808 bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
4809 if (pCopyRange)
4811 if (bFirst)
4813 pCopyRange->SetMark();
4814 *pCopyRange->GetMark() = *copyRange.End();
4816 *pCopyRange->GetPoint() = *copyRange.Start();
4820 return bRet;
4823 bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPos,
4824 SwCopyFlags const flags,
4825 SwPaM *const pCpyRange) const
4827 SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
4828 const bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
4830 SwPosition const*const pStt = rPam.Start();
4831 SwPosition *const pEnd = rPam.End();
4833 // Catch when there's no copy to do.
4834 if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel) ||
4835 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
4836 //JP 15.11.2001: don't test inclusive the end, ever exclusive
4837 ( &rDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
4839 return false;
4842 const bool bEndEqualIns = &rDoc == &m_rDoc && rPos == *pEnd;
4844 // If Undo is enabled, create the UndoCopy object
4845 SwUndoCpyDoc* pUndo = nullptr;
4846 // lcl_DeleteRedlines may delete the start or end node of the cursor when
4847 // removing the redlines so use cursor that is corrected by PaMCorrAbs
4848 std::shared_ptr<SwUnoCursor> const pCopyPam(rDoc.CreateUnoCursor(rPos));
4850 SwTableNumFormatMerge aTNFM( m_rDoc, rDoc );
4851 std::optional<std::vector<SwFrameFormat*>> pFlys;
4852 std::vector<SwFrameFormat*> const* pFlysAtInsPos;
4854 if (rDoc.GetIDocumentUndoRedo().DoesUndo())
4856 pUndo = new SwUndoCpyDoc(*pCopyPam);
4857 pFlysAtInsPos = pUndo->GetFlysAnchoredAt();
4859 else
4861 pFlys = sw::GetFlysAnchoredAt(rDoc, rPos.nNode.GetIndex());
4862 pFlysAtInsPos = pFlys ? &*pFlys : nullptr;
4865 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
4866 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
4868 // Move the PaM one node back from the insert position, so that
4869 // the position doesn't get moved
4870 pCopyPam->SetMark();
4871 bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
4872 // If the position was shifted from more than one node, an end node has been skipped
4873 bool bAfterTable = false;
4874 if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > SwNodeOffset(1))
4876 // First go back to the original place
4877 pCopyPam->GetPoint()->nNode = rPos.nNode;
4878 pCopyPam->GetPoint()->nContent = rPos.nContent;
4880 bCanMoveBack = false;
4881 bAfterTable = true;
4883 if( !bCanMoveBack )
4885 pCopyPam->GetPoint()->nNode--;
4886 assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0);
4889 SwNodeRange aRg( pStt->nNode, pEnd->nNode );
4890 SwNodeIndex aInsPos( rPos.nNode );
4891 const bool bOneNode = pStt->nNode == pEnd->nNode;
4892 SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode();
4893 SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4894 SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
4895 bool bCopyCollFormat = !rDoc.IsInsOnlyTextGlossary() &&
4896 ( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
4897 ( !bOneNode && !rPos.nContent.GetIndex() ) );
4898 bool bCopyBookmarks = true;
4899 bool bCopyPageSource = false;
4900 SwNodeOffset nDeleteTextNodes(0);
4902 // #i104585# copy outline num rule to clipboard (for ASCII filter)
4903 if (rDoc.IsClipBoard() && m_rDoc.GetOutlineNumRule())
4905 rDoc.SetOutlineNumRule(*m_rDoc.GetOutlineNumRule());
4908 // #i86492#
4909 // Correct the search for a previous list:
4910 // First search for non-outline numbering list. Then search for non-outline
4911 // bullet list.
4912 // Keep also the <ListId> value for possible propagation.
4913 OUString aListIdToPropagate;
4914 const SwNumRule* pNumRuleToPropagate =
4915 rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true );
4916 if ( !pNumRuleToPropagate )
4918 pNumRuleToPropagate =
4919 rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true );
4921 // #i86492#
4922 // Do not propagate previous found list, if
4923 // - destination is an empty paragraph which is not in a list and
4924 // - source contains at least one paragraph which is not in a list
4925 if ( pNumRuleToPropagate &&
4926 pDestTextNd && !pDestTextNd->GetText().getLength() &&
4927 !pDestTextNd->IsInList() &&
4928 !lcl_ContainsOnlyParagraphsInList( rPam ) )
4930 pNumRuleToPropagate = nullptr;
4933 // This do/while block is only there so that we can break out of it!
4934 do {
4935 if( pSttTextNd )
4937 ++nDeleteTextNodes; // must be joined in Undo
4938 // Don't copy the beginning completely?
4939 if( !bCopyCollFormat || bColumnSel || pStt->nContent.GetIndex() )
4941 SwIndex aDestIdx( rPos.nContent );
4942 bool bCopyOk = false;
4943 if( !pDestTextNd )
4945 if( pStt->nContent.GetIndex() || bOneNode )
4946 pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
4947 rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
4948 else
4950 pDestTextNd = pSttTextNd->MakeCopy(rDoc, aInsPos, true)->GetTextNode();
4951 bCopyOk = true;
4953 aDestIdx.Assign( pDestTextNd, 0 );
4954 bCopyCollFormat = true;
4956 else if( !bOneNode || bColumnSel )
4958 const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
4960 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
4961 rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
4964 if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
4966 // after the SplitNode, span the CpyPam correctly again
4967 pCopyPam->Move( fnMoveBackward, GoInContent );
4968 pCopyPam->Move( fnMoveBackward, GoInContent );
4971 pDestTextNd = rDoc.GetNodes()[ aInsPos.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
4972 aDestIdx.Assign(
4973 pDestTextNd, pDestTextNd->GetText().getLength());
4975 // Correct the area again
4976 if( bEndEqualIns )
4978 bool bChg = pEnd != rPam.GetPoint();
4979 if( bChg )
4980 rPam.Exchange();
4981 rPam.Move( fnMoveBackward, GoInContent );
4982 if( bChg )
4983 rPam.Exchange();
4985 else if( rPos == *pEnd )
4987 // The end was also moved
4988 pEnd->nNode--;
4989 pEnd->nContent.Assign( pDestTextNd, nContentEnd );
4991 // tdf#63022 always reset pEndTextNd after SplitNode
4992 aRg.aEnd = pEnd->nNode;
4993 pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4996 NUMRULE_STATE
4997 if( bCopyCollFormat && bOneNode )
4999 PUSH_NUMRULE_STATE
5002 if( !bCopyOk )
5004 const sal_Int32 nCpyLen = ( bOneNode
5005 ? pEnd->nContent.GetIndex()
5006 : pSttTextNd->GetText().getLength())
5007 - pStt->nContent.GetIndex();
5008 pSttTextNd->CopyText( pDestTextNd, aDestIdx,
5009 pStt->nContent, nCpyLen );
5010 if( bEndEqualIns )
5011 pEnd->nContent -= nCpyLen;
5014 aRg.aStart++;
5016 if( bOneNode )
5018 if (bCopyCollFormat)
5020 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5021 pSttTextNd->CopyCollFormat(*pDestTextNd, false);
5022 POP_NUMRULE_STATE
5025 // copy at-char flys in rPam
5026 SwNodeIndex temp(*pDestTextNd); // update to new (start) node for flys
5027 // tdf#126626 prevent duplicate Undos
5028 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
5029 CopyFlyInFlyImpl(aRg, &rPam, temp, false);
5031 break;
5035 else if( pDestTextNd )
5037 // Problems with insertion of table selections into "normal" text solved.
5038 // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
5039 // the undo operation will try to merge this node after removing the table.
5040 // If we didn't split a textnode, the PaM should start at the inserted table node
5041 if( rPos.nContent.GetIndex() == pDestTextNd->Len() )
5042 { // Insertion at the last position of a textnode (empty or not)
5043 ++aInsPos; // The table will be inserted behind the text node
5045 else if( rPos.nContent.GetIndex() )
5046 { // Insertion in the middle of a text node, it has to be split
5047 // (and joined from undo)
5048 ++nDeleteTextNodes;
5050 const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
5052 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
5053 rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
5056 if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
5058 // after the SplitNode, span the CpyPam correctly again
5059 pCopyPam->Move( fnMoveBackward, GoInContent );
5060 pCopyPam->Move( fnMoveBackward, GoInContent );
5063 // Correct the area again
5064 if( bEndEqualIns )
5065 aRg.aEnd--;
5066 // The end would also be moved
5067 else if( rPos == *pEnd )
5069 rPos.nNode-=SwNodeOffset(2);
5070 rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
5071 nContentEnd );
5072 rPos.nNode++;
5073 aRg.aEnd--;
5076 else if( bCanMoveBack )
5077 { // Insertion at the first position of a text node. It will not be split, the table
5078 // will be inserted before the text node.
5079 // See below, before the SetInsertRange function of the undo object will be called,
5080 // the CpyPam would be moved to the next content position. This has to be avoided
5081 // We want to be moved to the table node itself thus we have to set bCanMoveBack
5082 // and to manipulate pCopyPam.
5083 bCanMoveBack = false;
5084 pCopyPam->GetPoint()->nNode--;
5088 pDestTextNd = aInsPos.GetNode().GetTextNode();
5089 if (pEndTextNd)
5091 SwIndex aDestIdx( rPos.nContent );
5092 if( !pDestTextNd )
5094 pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
5095 rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
5096 aDestIdx.Assign( pDestTextNd, 0 );
5097 aInsPos--;
5099 // if we have to insert an extra text node
5100 // at the destination, this node will be our new destination
5101 // (text) node, and thus we increment nDeleteTextNodes. This
5102 // will ensure that this node will be deleted during Undo.
5103 ++nDeleteTextNodes; // must be deleted
5106 const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
5108 NUMRULE_STATE
5109 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
5111 PUSH_NUMRULE_STATE
5114 pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ),
5115 pEnd->nContent.GetIndex() );
5117 // Also copy all format templates
5118 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
5120 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5121 pEndTextNd->CopyCollFormat(*pDestTextNd, false);
5122 if ( bOneNode )
5124 POP_NUMRULE_STATE
5129 SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
5130 if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
5132 if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
5134 aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
5135 if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
5136 pDestTextNd->ResetAttr( RES_BREAK );
5137 if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
5138 pDestTextNd->ResetAttr( RES_PAGEDESC );
5143 SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
5144 SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
5145 if (bCanMoveBack)
5146 { // pCopyPam is actually 1 before the copy range so move it fwd
5147 SwPaM temp(*pCopyPam->GetPoint());
5148 temp.Move(fnMoveForward, GoInContent);
5149 startPos = *temp.GetPoint();
5151 assert(startPos.nNode.GetNode().IsContentNode());
5152 std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
5153 if( aInsPos == pEnd->nNode )
5155 SwNodeIndex aSaveIdx( aInsPos, -1 );
5156 assert(pStt->nNode != pEnd->nNode);
5157 pEnd->nContent = 0; // TODO why this?
5158 CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
5159 ++aSaveIdx;
5160 pEnd->nNode = aSaveIdx;
5161 pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
5163 else
5164 CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
5166 bCopyBookmarks = false;
5169 // at-char anchors post SplitNode are on index 0 of 2nd node and will
5170 // remain there - move them back to the start (end would also work?)
5171 // ... also for at-para anchors; here start is preferable because
5172 // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
5173 if (pFlysAtInsPos)
5175 // init *again* - because CopyWithFlyInFly moved startPos
5176 SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
5177 SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
5178 if (bCanMoveBack)
5179 { // pCopyPam is actually 1 before the copy range so move it fwd
5180 SwPaM temp(*pCopyPam->GetPoint());
5181 temp.Move(fnMoveForward, GoInContent);
5182 startPos = *temp.GetPoint();
5184 assert(startPos.nNode.GetNode().IsContentNode());
5185 SwPosition startPosAtPara(startPos);
5186 startPosAtPara.nContent.Assign(nullptr, 0);
5188 for (SwFrameFormat * pFly : *pFlysAtInsPos)
5190 SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
5191 if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
5193 SwFormatAnchor anchor(*pAnchor);
5194 anchor.SetAnchor( &startPos );
5195 pFly->SetFormatAttr(anchor);
5197 else if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
5199 SwFormatAnchor anchor(*pAnchor);
5200 anchor.SetAnchor( &startPosAtPara );
5201 pFly->SetFormatAttr(anchor);
5206 if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
5208 // Put the breaks back into the first node
5209 if( aBrkSet.Count() && nullptr != ( pDestTextNd = rDoc.GetNodes()[
5210 pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
5212 pDestTextNd->SetAttr( aBrkSet );
5213 bCopyPageSource = true;
5216 } while( false );
5219 // it is not possible to make this test when copy from the clipBoard to document
5220 // in this case the PageNum not exist anymore
5221 // tdf#39400 and tdf#97526
5222 // when copy from document to ClipBoard, and it is from the first page
5223 // and not the source has the page break
5224 if (rDoc.IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
5226 if (pDestTextNd)
5228 pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
5229 pDestTextNd->ResetAttr(RES_PAGEDESC);
5234 // Adjust position (in case it was moved / in another node)
5235 rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
5236 rPos.nContent.GetIndex() );
5238 if( rPos.nNode != aInsPos )
5240 pCopyPam->GetMark()->nNode = aInsPos;
5241 if (aInsPos < rPos.nNode)
5242 { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
5243 pCopyPam->GetContentNode(false)->MakeEndIndex(&pCopyPam->GetMark()->nContent);
5245 else // incremented in (!pSttTextNd && pDestTextNd) above
5247 pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0);
5249 rPos = *pCopyPam->GetMark();
5251 else
5252 *pCopyPam->GetMark() = rPos;
5254 if ( !bAfterTable )
5255 pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
5256 else
5258 pCopyPam->GetPoint()->nNode++;
5260 // Reset the offset to 0 as it was before the insertion
5261 pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0);
5262 // If the next node is a start node, then step back: the start node
5263 // has been copied and needs to be in the selection for the undo
5264 if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode())
5265 pCopyPam->GetPoint()->nNode--;
5268 pCopyPam->Exchange();
5270 // Also copy all bookmarks
5271 if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
5273 sw::CopyBookmarks(rPam, *pCopyPam->Start());
5276 if( RedlineFlags::DeleteRedlines & eOld )
5278 assert(*pCopyPam->GetPoint() == rPos);
5279 // the Node rPos points to may be deleted so unregister ...
5280 rPos.nContent.Assign(nullptr, 0);
5281 lcl_DeleteRedlines(rPam, *pCopyPam);
5282 rPos = *pCopyPam->GetPoint(); // ... and restore.
5285 // If Undo is enabled, store the inserted area
5286 if (rDoc.GetIDocumentUndoRedo().DoesUndo())
5288 // append it after styles have been copied when copying nodes
5289 rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
5290 pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes);
5293 if( pCpyRange )
5295 pCpyRange->SetMark();
5296 *pCpyRange->GetPoint() = *pCopyPam->GetPoint();
5297 *pCpyRange->GetMark() = *pCopyPam->GetMark();
5300 if ( pNumRuleToPropagate != nullptr )
5302 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
5303 // Don't reset indent attributes, that would mean loss of direct
5304 // formatting.
5305 rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr,
5306 aListIdToPropagate, true, /*bResetIndentAttrs=*/false );
5309 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
5310 rDoc.getIDocumentState().SetModified();
5312 return true;
5317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */