tdf#106746: pDelPam is a bit special
[LibreOffice.git] / sw / source / core / doc / DocumentContentOperationsManager.cxx
bloba137c0feec6f0ed2dc58e7eb0b86995ab2f534b5
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 <DocumentRedlineManager.hxx>
24 #include <IDocumentState.hxx>
25 #include <IDocumentLayoutAccess.hxx>
26 #include <IDocumentStylePoolAccess.hxx>
27 #include <UndoManager.hxx>
28 #include <docary.hxx>
29 #include <textboxhelper.hxx>
30 #include <dcontact.hxx>
31 #include <grfatr.hxx>
32 #include <numrule.hxx>
33 #include <charfmt.hxx>
34 #include <ndgrf.hxx>
35 #include <ndnotxt.hxx>
36 #include <ndole.hxx>
37 #include <fmtcol.hxx>
38 #include <breakit.hxx>
39 #include <frmfmt.hxx>
40 #include <fmtanchr.hxx>
41 #include <fmtcntnt.hxx>
42 #include <fmtinfmt.hxx>
43 #include <fmtpdsc.hxx>
44 #include <fmtcnct.hxx>
45 #include <SwStyleNameMapper.hxx>
46 #include <redline.hxx>
47 #include <unocrsr.hxx>
48 #include <mvsave.hxx>
49 #include <ndtxt.hxx>
50 #include <poolfmt.hxx>
51 #include <paratr.hxx>
52 #include <txatbase.hxx>
53 #include <UndoRedline.hxx>
54 #include <undobj.hxx>
55 #include <UndoBookmark.hxx>
56 #include <UndoDelete.hxx>
57 #include <UndoSplitMove.hxx>
58 #include <UndoOverwrite.hxx>
59 #include <UndoInsert.hxx>
60 #include <UndoAttribute.hxx>
61 #include <rolbck.hxx>
62 #include <acorrect.hxx>
63 #include <ftnidx.hxx>
64 #include <txtftn.hxx>
65 #include <hints.hxx>
66 #include <crsrsh.hxx>
67 #include <fmtflcnt.hxx>
68 #include <docedt.hxx>
69 #include <unotools/charclass.hxx>
70 #include <unotools/configmgr.hxx>
71 #include <sfx2/Metadatable.hxx>
72 #include <svl/stritem.hxx>
73 #include <svl/itemiter.hxx>
74 #include <svx/svdobj.hxx>
75 #include <svx/svdouno.hxx>
76 #include <tools/globname.hxx>
77 #include <editeng/formatbreakitem.hxx>
78 #include <o3tl/make_unique.hxx>
79 #include <com/sun/star/i18n/Boundary.hpp>
80 #include <memory>
83 using namespace ::com::sun::star::i18n;
85 namespace
87 // Copy method from SwDoc
88 // Prevent copying in Flys that are anchored in the area
89 bool lcl_ChkFlyFly( SwDoc* pDoc, sal_uLong nSttNd, sal_uLong nEndNd,
90 sal_uLong nInsNd )
92 const SwFrameFormats& rFrameFormatTable = *pDoc->GetSpzFrameFormats();
94 for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
96 SwFrameFormat const*const pFormat = rFrameFormatTable[n];
97 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
98 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
99 if (pAPos &&
100 ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
101 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
102 (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
103 (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
104 nSttNd <= pAPos->nNode.GetIndex() &&
105 pAPos->nNode.GetIndex() < nEndNd )
107 const SwFormatContent& rContent = pFormat->GetContent();
108 SwStartNode* pSNd;
109 if( !rContent.GetContentIdx() ||
110 nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
111 continue;
113 if( pSNd->GetIndex() < nInsNd &&
114 nInsNd < pSNd->EndOfSectionIndex() )
115 // Do not copy !
116 return true;
118 if( lcl_ChkFlyFly( pDoc, pSNd->GetIndex(),
119 pSNd->EndOfSectionIndex(), nInsNd ) )
120 // Do not copy !
121 return true;
125 return false;
128 SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, sal_uLong & rDelCount)
130 SwNodeIndex const& rStart(rSourcePaM.Start()->nNode);
131 // Special handling for SwDoc::AppendDoc
132 if (rSourcePaM.GetDoc()->GetNodes().GetEndOfExtras().GetIndex() + 1
133 == rStart.GetIndex())
135 rDelCount = 1;
136 return SwNodeIndex(rStart, +1);
138 else
140 rDelCount = 0;
141 return rStart;
146 The lcl_CopyBookmarks function has to copy bookmarks from the source to the destination nodes
147 array. It is called after a call of the CopyNodes(..) function. But this function does not copy
148 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
149 if the corresponding end/start node is outside the copied pam.
150 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
151 index inside the pam.
152 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
153 of "non-copy" nodes between rPam.Start() and rLastIdx.
154 nNewIdx is the new position of interest.
156 void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const sal_uLong nNewIdx, sal_uLong& rDelCount )
158 sal_uLong nStart = rPam.Start()->nNode.GetIndex();
159 sal_uLong nEnd = rPam.End()->nNode.GetIndex();
160 if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
162 // We never copy the StartOfContent node
163 do // count "non-copy" nodes
165 SwNode& rNode = rLastIdx.GetNode();
166 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
167 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
169 ++rDelCount;
171 ++rLastIdx;
173 while( rLastIdx.GetIndex() < nNewIdx );
175 else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
176 // no move backward needed
178 while( rLastIdx.GetIndex() > nNewIdx )
180 SwNode& rNode = rLastIdx.GetNode();
181 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
182 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
184 --rDelCount;
186 rLastIdx--;
191 void lcl_SetCpyPos( const SwPosition& rOrigPos,
192 const SwPosition& rOrigStt,
193 const SwPosition& rCpyStt,
194 SwPosition& rChgPos,
195 sal_uLong nDelCount )
197 sal_uLong nNdOff = rOrigPos.nNode.GetIndex();
198 nNdOff -= rOrigStt.nNode.GetIndex();
199 nNdOff -= nDelCount;
200 sal_Int32 nContentPos = rOrigPos.nContent.GetIndex();
202 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
203 rChgPos.nNode = nNdOff + rCpyStt.nNode.GetIndex();
204 if( !nNdOff )
206 // just adapt the content index
207 if( nContentPos > rOrigStt.nContent.GetIndex() )
208 nContentPos -= rOrigStt.nContent.GetIndex();
209 else
210 nContentPos = 0;
211 nContentPos += rCpyStt.nContent.GetIndex();
213 rChgPos.nContent.Assign( rChgPos.nNode.GetNode().GetContentNode(), nContentPos );
216 // TODO: use SaveBookmark (from DelBookmarks)
217 void lcl_CopyBookmarks(
218 const SwPaM& rPam,
219 SwPaM& rCpyPam )
221 const SwDoc* pSrcDoc = rPam.GetDoc();
222 SwDoc* pDestDoc = rCpyPam.GetDoc();
223 const IDocumentMarkAccess* const pSrcMarkAccess = pSrcDoc->getIDocumentMarkAccess();
224 ::sw::UndoGuard const undoGuard(pDestDoc->GetIDocumentUndoRedo());
226 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
227 SwPosition* pCpyStt = rCpyPam.Start();
229 typedef std::vector< const ::sw::mark::IMark* > mark_vector_t;
230 mark_vector_t vMarksToCopy;
231 for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
232 ppMark != pSrcMarkAccess->getAllMarksEnd();
233 ++ppMark )
235 const ::sw::mark::IMark* const pMark = ppMark->get();
237 const SwPosition& rMarkStart = pMark->GetMarkStart();
238 const SwPosition& rMarkEnd = pMark->GetMarkEnd();
239 // only include marks that are in the range and not touching both start and end
240 // - not for annotation or checkbox marks.
241 const bool bIsNotOnBoundary =
242 pMark->IsExpanded()
243 ? (rMarkStart != rStt || rMarkEnd != rEnd) // rMarkStart != rMarkEnd
244 : (rMarkStart != rStt && rMarkEnd != rEnd); // rMarkStart == rMarkEnd
245 const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark);
246 if ( rMarkStart >= rStt && rMarkEnd <= rEnd
247 && ( bIsNotOnBoundary
248 || aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
249 || aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK ) )
251 vMarksToCopy.push_back(pMark);
254 // We have to count the "non-copied" nodes..
255 sal_uLong nDelCount;
256 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
257 for(mark_vector_t::const_iterator ppMark = vMarksToCopy.begin();
258 ppMark != vMarksToCopy.end();
259 ++ppMark)
261 const ::sw::mark::IMark* const pMark = *ppMark;
262 SwPaM aTmpPam(*pCpyStt);
263 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().nNode.GetIndex(), nDelCount);
264 lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
265 if(pMark->IsExpanded())
267 aTmpPam.SetMark();
268 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().nNode.GetIndex(), nDelCount);
269 lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
272 ::sw::mark::IMark* const pNewMark = pDestDoc->getIDocumentMarkAccess()->makeMark(
273 aTmpPam,
274 pMark->GetName(),
275 IDocumentMarkAccess::GetType(*pMark),
276 ::sw::mark::InsertMode::CopyText);
277 // Explicitly try to get exactly the same name as in the source
278 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
279 pDestDoc->getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName());
281 // copying additional attributes for bookmarks or fieldmarks
282 ::sw::mark::IBookmark* const pNewBookmark =
283 dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
284 const ::sw::mark::IBookmark* const pOldBookmark =
285 dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
286 if (pNewBookmark && pOldBookmark)
288 pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
289 pNewBookmark->SetShortName(pOldBookmark->GetShortName());
291 ::sw::mark::IFieldmark* const pNewFieldmark =
292 dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
293 const ::sw::mark::IFieldmark* const pOldFieldmark =
294 dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
295 if (pNewFieldmark && pOldFieldmark)
297 pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
298 pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
299 ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
300 const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
301 ::sw::mark::IFieldmark::parameter_map_t::const_iterator pIt = pOldParams->begin();
302 for (; pIt != pOldParams->end(); ++pIt )
304 pNewParams->insert( *pIt );
308 ::sfx2::Metadatable const*const pMetadatable(
309 dynamic_cast< ::sfx2::Metadatable const* >(pMark));
310 ::sfx2::Metadatable *const pNewMetadatable(
311 dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
312 if (pMetadatable && pNewMetadatable)
314 pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
319 void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
321 const SwDoc* pSrcDoc = rPam.GetDoc();
322 const SwRedlineTable& rTable = pSrcDoc->getIDocumentRedlineAccess().GetRedlineTable();
323 if( !rTable.empty() )
325 SwDoc* pDestDoc = rCpyPam.GetDoc();
326 SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
327 std::unique_ptr<SwPaM> pDelPam;
328 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
329 // We have to count the "non-copied" nodes
330 sal_uLong nDelCount;
331 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
333 SwRedlineTable::size_type n = 0;
334 pSrcDoc->getIDocumentRedlineAccess().GetRedline( *pStt, &n );
335 for( ; n < rTable.size(); ++n )
337 const SwRangeRedline* pRedl = rTable[ n ];
338 if( nsRedlineType_t::REDLINE_DELETE == pRedl->GetType() && pRedl->IsVisible() )
340 const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
342 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
343 switch( eCmpPos )
345 case SwComparePosition::CollideEnd:
346 case SwComparePosition::Before:
347 // Pos1 is before Pos2
348 break;
350 case SwComparePosition::CollideStart:
351 case SwComparePosition::Behind:
352 // Pos1 is after Pos2
353 n = rTable.size();
354 break;
356 default:
358 pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
359 if( *pStt < *pRStt )
361 lcl_NonCopyCount( rPam, aCorrIdx, pRStt->nNode.GetIndex(), nDelCount );
362 lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
363 *pDelPam->GetPoint(), nDelCount );
365 pDelPam->SetMark();
367 if( *pEnd < *pREnd )
368 *pDelPam->GetPoint() = *pCpyEnd;
369 else
371 lcl_NonCopyCount( rPam, aCorrIdx, pREnd->nNode.GetIndex(), nDelCount );
372 lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
373 *pDelPam->GetPoint(), nDelCount );
380 if( pDelPam )
382 RedlineFlags eOld = pDestDoc->getIDocumentRedlineAccess().GetRedlineFlags();
383 pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
385 ::sw::UndoGuard const undoGuard(pDestDoc->GetIDocumentUndoRedo());
387 do {
388 pDestDoc->getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() );
389 if( !pDelPam->IsMultiSelection() )
390 break;
391 delete pDelPam->GetNext();
392 } while( true );
394 pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
399 void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
401 SwDoc* pSrcDoc = rRg.aStart.GetNode().GetDoc();
402 if( !pSrcDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
404 SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
405 SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
406 lcl_DeleteRedlines( aRgTmp, aCpyTmp );
410 void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
412 SwFormatChain aSrc( pSrc->GetChain() );
413 if ( !aSrc.GetNext() )
415 aSrc.SetNext( pDest );
416 pSrc->SetFormatAttr( aSrc );
418 SwFormatChain aDest( pDest->GetChain() );
419 if ( !aDest.GetPrev() )
421 aDest.SetPrev( pSrc );
422 pDest->SetFormatAttr( aDest );
426 // #i86492#
427 bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
429 bool bRet = false;
431 const SwTextNode* pTextNd = rPam.Start()->nNode.GetNode().GetTextNode();
432 const SwTextNode* pEndTextNd = rPam.End()->nNode.GetNode().GetTextNode();
433 if ( pTextNd && pTextNd->IsInList() &&
434 pEndTextNd && pEndTextNd->IsInList() )
436 bRet = true;
437 SwNodeIndex aIdx(rPam.Start()->nNode);
441 ++aIdx;
442 pTextNd = aIdx.GetNode().GetTextNode();
444 if ( !pTextNd || !pTextNd->IsInList() )
446 bRet = false;
447 break;
449 } while ( pTextNd && pTextNd != pEndTextNd );
452 return bRet;
455 bool lcl_MarksWholeNode(const SwPaM & rPam)
457 bool bResult = false;
458 const SwPosition* pStt = rPam.Start();
459 const SwPosition* pEnd = rPam.End();
461 if (nullptr != pStt && nullptr != pEnd)
463 const SwTextNode* pSttNd = pStt->nNode.GetNode().GetTextNode();
464 const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
466 if (nullptr != pSttNd && nullptr != pEndNd &&
467 pStt->nContent.GetIndex() == 0 &&
468 pEnd->nContent.GetIndex() == pEndNd->Len())
470 bResult = true;
474 return bResult;
478 //local functions originally from sw/source/core/doc/docedt.cxx
479 namespace
481 void
482 lcl_CalcBreaks( std::vector<sal_Int32> & rBreaks, SwPaM const & rPam )
484 SwTextNode const * const pTextNode(
485 rPam.End()->nNode.GetNode().GetTextNode() );
486 if (!pTextNode)
487 return; // left-overlap only possible at end of selection...
489 const sal_Int32 nStart(rPam.Start()->nContent.GetIndex());
490 const sal_Int32 nEnd (rPam.End ()->nContent.GetIndex());
491 if (nEnd == pTextNode->Len())
492 return; // paragraph selected until the end
494 for (sal_Int32 i = nStart; i < nEnd; ++i)
496 const sal_Unicode c(pTextNode->GetText()[i]);
497 if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
499 SwTextAttr const * const pAttr( pTextNode->GetTextAttrForCharAt(i) );
500 if (pAttr && pAttr->End() && (*pAttr->End() > nEnd))
502 OSL_ENSURE(pAttr->HasDummyChar(), "GetTextAttrForCharAt broken?");
503 rBreaks.push_back(i);
509 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, SwPaM & rPam,
510 bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
512 std::vector<sal_Int32> Breaks;
514 lcl_CalcBreaks(Breaks, rPam);
516 if (Breaks.empty())
518 return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext);
521 // Deletion must be split into several parts if the text node
522 // contains a text attribute with end and with dummy character
523 // and the selection does not contain the text attribute completely,
524 // but overlaps its start (left), where the dummy character is.
526 SwPosition const & rSelectionEnd( *rPam.End() );
528 bool bRet( true );
529 // iterate from end to start, to avoid invalidating the offsets!
530 std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
531 SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
532 SwPosition & rEnd( *aPam.End() );
533 SwPosition & rStart( *aPam.Start() );
535 while (iter != Breaks.rend())
537 rStart.nContent = *iter + 1;
538 if (rEnd.nContent > rStart.nContent) // check if part is empty
540 bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
542 rEnd.nContent = *iter;
543 ++iter;
546 rStart = *rPam.Start(); // set to original start
547 if (rEnd.nContent > rStart.nContent) // check if part is empty
549 bRet &= (rDocumentContentOperations.*pFunc)(aPam, bForceJoinNext);
552 return bRet;
555 bool lcl_StrLenOverflow( const SwPaM& rPam )
557 // If we try to merge two paragraphs we have to test if afterwards
558 // the string doesn't exceed the allowed string length
559 if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
561 const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
562 const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
563 if( (nullptr != pEndNd) && pStt->nNode.GetNode().IsTextNode() )
565 const sal_uInt64 nSum = pStt->nContent.GetIndex() +
566 pEndNd->GetText().getLength() - pEnd->nContent.GetIndex();
567 return nSum > static_cast<sal_uInt64>(SAL_MAX_INT32);
570 return false;
573 struct SaveRedline
575 SwRangeRedline* pRedl;
576 sal_uInt32 nStt, nEnd;
577 sal_Int32 nSttCnt;
578 sal_Int32 nEndCnt;
580 SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
581 : pRedl(pR)
582 , nEnd(0)
583 , nEndCnt(0)
585 const SwPosition* pStt = pR->Start(),
586 * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
587 sal_uInt32 nSttIdx = rSttIdx.GetIndex();
588 nStt = pStt->nNode.GetIndex() - nSttIdx;
589 nSttCnt = pStt->nContent.GetIndex();
590 if( pR->HasMark() )
592 nEnd = pEnd->nNode.GetIndex() - nSttIdx;
593 nEndCnt = pEnd->nContent.GetIndex();
596 pRedl->GetPoint()->nNode = 0;
597 pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
598 pRedl->GetMark()->nNode = 0;
599 pRedl->GetMark()->nContent.Assign( nullptr, 0 );
602 SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
603 : pRedl(pR)
604 , nEnd(0)
605 , nEndCnt(0)
607 const SwPosition* pStt = pR->Start(),
608 * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
609 sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
610 nStt = pStt->nNode.GetIndex() - nSttIdx;
611 nSttCnt = pStt->nContent.GetIndex();
612 if( nStt == 0 )
613 nSttCnt = nSttCnt - rPos.nContent.GetIndex();
614 if( pR->HasMark() )
616 nEnd = pEnd->nNode.GetIndex() - nSttIdx;
617 nEndCnt = pEnd->nContent.GetIndex();
618 if( nEnd == 0 )
619 nEndCnt = nEndCnt - rPos.nContent.GetIndex();
622 pRedl->GetPoint()->nNode = 0;
623 pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
624 pRedl->GetMark()->nNode = 0;
625 pRedl->GetMark()->nContent.Assign( nullptr, 0 );
628 void SetPos( sal_uInt32 nInsPos )
630 pRedl->GetPoint()->nNode = nInsPos + nStt;
631 pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt );
632 if( pRedl->HasMark() )
634 pRedl->GetMark()->nNode = nInsPos + nEnd;
635 pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt );
639 void SetPos( const SwPosition& aPos )
641 pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
642 pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
643 if( pRedl->HasMark() )
645 pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
646 pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
651 typedef std::vector< SaveRedline > SaveRedlines_t;
653 void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
655 SwDoc* pDoc = aPam.GetNode().GetDoc();
657 const SwPosition* pStart = aPam.Start();
658 const SwPosition* pEnd = aPam.End();
660 // get first relevant redline
661 SwRedlineTable::size_type nCurrentRedline;
662 pDoc->getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
663 if( nCurrentRedline > 0)
664 nCurrentRedline--;
666 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
667 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
668 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
670 // iterate over relevant redlines and decide for each whether it should
671 // be saved, or split + saved
672 SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
673 for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
675 SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
676 SwComparePosition eCompare =
677 ComparePosition( *pCurrent->Start(), *pCurrent->End(),
678 *pStart, *pEnd);
680 // we must save this redline if it overlaps aPam
681 // (we may have to split it, too)
682 if( eCompare == SwComparePosition::OverlapBehind ||
683 eCompare == SwComparePosition::OverlapBefore ||
684 eCompare == SwComparePosition::Outside ||
685 eCompare == SwComparePosition::Inside ||
686 eCompare == SwComparePosition::Equal )
688 rRedlineTable.Remove( nCurrentRedline-- );
690 // split beginning, if necessary
691 if( eCompare == SwComparePosition::OverlapBefore ||
692 eCompare == SwComparePosition::Outside )
694 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
695 *pNewRedline->End() = *pStart;
696 *pCurrent->Start() = *pStart;
697 pDoc->getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
700 // split end, if necessary
701 if( eCompare == SwComparePosition::OverlapBehind ||
702 eCompare == SwComparePosition::Outside )
704 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
705 *pNewRedline->Start() = *pEnd;
706 *pCurrent->End() = *pEnd;
707 pDoc->getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
710 // save the current redline
711 rArr.emplace_back( pCurrent, *pStart );
715 // restore old redline mode
716 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
719 void lcl_RestoreRedlines(SwDoc* pDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
721 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
722 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
724 for(SaveRedline & rSvRedLine : rArr)
726 rSvRedLine.SetPos( rPos );
727 pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
730 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
733 void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
735 SwDoc* pDoc = rRg.aStart.GetNode().GetDoc();
736 SwRedlineTable::size_type nRedlPos;
737 SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
738 aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 );
739 if( pDoc->getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
740 --nRedlPos;
741 else if( nRedlPos >= pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() )
742 return ;
744 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
745 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
746 SwRedlineTable& rRedlTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
748 do {
749 SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
751 const SwPosition* pRStt = pTmp->Start(),
752 * pREnd = pTmp->GetMark() == pRStt
753 ? pTmp->GetPoint() : pTmp->GetMark();
755 if( pRStt->nNode < rRg.aStart )
757 if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
759 // Create a copy and set the end of the original to the end of the MoveArea.
760 // The copy is moved too.
761 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
762 SwPosition* pTmpPos = pNewRedl->Start();
763 pTmpPos->nNode = rRg.aStart;
764 pTmpPos->nContent.Assign(
765 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
767 rArr.emplace_back(pNewRedl, rRg.aStart);
769 pTmpPos = pTmp->End();
770 pTmpPos->nNode = rRg.aEnd;
771 pTmpPos->nContent.Assign(
772 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
774 else if( pREnd->nNode == rRg.aStart )
776 SwPosition* pTmpPos = pTmp->End();
777 pTmpPos->nNode = rRg.aEnd;
778 pTmpPos->nContent.Assign(
779 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
782 else if( pRStt->nNode < rRg.aEnd )
784 rRedlTable.Remove( nRedlPos-- );
785 if( pREnd->nNode < rRg.aEnd ||
786 ( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
788 // move everything
789 rArr.emplace_back( pTmp, rRg.aStart );
791 else
793 // split
794 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
795 SwPosition* pTmpPos = pNewRedl->End();
796 pTmpPos->nNode = rRg.aEnd;
797 pTmpPos->nContent.Assign(
798 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
800 rArr.emplace_back( pNewRedl, rRg.aStart );
802 pTmpPos = pTmp->Start();
803 pTmpPos->nNode = rRg.aEnd;
804 pTmpPos->nContent.Assign(
805 pTmpPos->nNode.GetNode().GetContentNode(), 0 );
806 pDoc->getIDocumentRedlineAccess().AppendRedline( pTmp, true );
809 else
810 break;
812 } while( ++nRedlPos < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() );
813 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
816 void lcl_RestoreRedlines(SwDoc *const pDoc, sal_uInt32 const nInsPos, SaveRedlines_t& rArr)
818 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
819 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
821 for(SaveRedline & rSvRedLine : rArr)
823 rSvRedLine.SetPos( nInsPos );
824 pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
827 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
830 bool lcl_SaveFootnote( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
831 const SwNodeIndex& rInsPos,
832 SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
833 const SwIndex* pSttCnt = nullptr, const SwIndex* pEndCnt = nullptr )
835 bool bUpdateFootnote = false;
836 const SwNodes& rNds = rInsPos.GetNodes();
837 const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
838 rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
839 const bool bSaveFootnote = !bDelFootnote &&
840 rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
841 if( !rFootnoteArr.empty() )
844 size_t nPos = 0;
845 rFootnoteArr.SeekEntry( rSttNd, &nPos );
846 SwTextFootnote* pSrch;
847 const SwNode* pFootnoteNd;
849 // Delete/save all that come after it
850 while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
851 &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
852 <= rEndNd.GetIndex() )
854 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
855 if( ( pEndCnt && pSttCnt )
856 ? (( &rSttNd.GetNode() == pFootnoteNd &&
857 pSttCnt->GetIndex() > nFootnoteSttIdx) ||
858 ( &rEndNd.GetNode() == pFootnoteNd &&
859 nFootnoteSttIdx >= pEndCnt->GetIndex() ))
860 : ( &rEndNd.GetNode() == pFootnoteNd ))
862 ++nPos; // continue searching
864 else
866 // delete it
867 if( bDelFootnote )
869 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
870 SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
871 rTextNd.EraseText( aIdx, 1 );
873 else
875 pSrch->DelFrames(nullptr);
876 rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
877 if( bSaveFootnote )
878 rSaveArr.insert( pSrch );
880 bUpdateFootnote = true;
884 while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
885 GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
887 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
888 if( !pEndCnt || !pSttCnt ||
889 ! (( &rSttNd.GetNode() == pFootnoteNd &&
890 pSttCnt->GetIndex() > nFootnoteSttIdx ) ||
891 ( &rEndNd.GetNode() == pFootnoteNd &&
892 nFootnoteSttIdx >= pEndCnt->GetIndex() )) )
894 if( bDelFootnote )
896 // delete it
897 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
898 SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
899 rTextNd.EraseText( aIdx, 1 );
901 else
903 pSrch->DelFrames(nullptr);
904 rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
905 if( bSaveFootnote )
906 rSaveArr.insert( pSrch );
908 bUpdateFootnote = true;
912 // When moving from redline section into document content section, e.g.
913 // after loading a document with (delete-)redlines, the footnote array
914 // has to be adjusted... (#i70572)
915 if( bSaveFootnote )
917 SwNodeIndex aIdx( rSttNd );
918 while( aIdx < rEndNd ) // Check the moved section
920 SwNode* pNode = &aIdx.GetNode();
921 if( pNode->IsTextNode() ) // Looking for text nodes...
923 SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
924 if( pHints && pHints->HasFootnote() ) //...with footnotes
926 bUpdateFootnote = true; // Heureka
927 const size_t nCount = pHints->Count();
928 for( size_t i = 0; i < nCount; ++i )
930 SwTextAttr *pAttr = pHints->Get( i );
931 if ( pAttr->Which() == RES_TXTATR_FTN )
933 rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
938 ++aIdx;
941 return bUpdateFootnote;
944 bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
946 sal_Unicode const cChr = pNode->GetText()[nPos];
947 switch (cChr)
949 case CH_TXTATR_BREAKWORD:
950 case CH_TXTATR_INWORD:
951 return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
952 case CH_TXT_ATR_FIELDSTART:
953 case CH_TXT_ATR_FIELDEND:
954 case CH_TXT_ATR_FORMELEMENT:
955 return false;
956 default:
957 return true;
961 void lcl_SkipAttr( const SwTextNode *pNode, SwIndex &rIdx, sal_Int32 &rStart )
963 if( !lcl_MayOverwrite( pNode, rStart ) )
965 // skip all special attributes
966 do {
967 ++rIdx;
968 rStart = rIdx.GetIndex();
969 } while (rStart < pNode->GetText().getLength()
970 && !lcl_MayOverwrite(pNode, rStart) );
974 bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
976 if( bRegExpRplc )
978 sal_Int32 nPos = 0;
979 const OUString sPara("\\n");
980 for (;;)
982 nPos = rStr.indexOf( sPara, nPos );
983 if (nPos<0)
985 break;
987 // Has this been escaped?
988 if( nPos && '\\' == rStr[nPos-1])
990 ++nPos;
991 if( nPos >= rStr.getLength() )
993 break;
996 else
998 rRet = rStr.copy( 0, nPos );
999 rStr = rStr.copy( nPos + sPara.getLength() );
1000 return true;
1004 rRet = rStr;
1005 rStr.clear();
1006 return false;
1010 namespace //local functions originally from docfmt.cxx
1012 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1014 /// Insert Hints according to content types;
1015 // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1017 bool lcl_InsAttr(
1018 SwDoc *const pDoc,
1019 const SwPaM &rRg,
1020 const SfxItemSet& rChgSet,
1021 const SetAttrMode nFlags,
1022 SwUndoAttr *const pUndo,
1023 const bool bExpandCharToPara=false)
1025 // Divide the Sets (for selections in Nodes)
1026 const SfxItemSet* pCharSet = nullptr;
1027 const SfxItemSet* pOtherSet = nullptr;
1028 bool bDelete = false;
1029 bool bCharAttr = false;
1030 bool bOtherAttr = false;
1032 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1033 if ( 1 == rChgSet.Count() )
1035 SfxItemIter aIter( rChgSet );
1036 const SfxPoolItem* pItem = aIter.FirstItem();
1037 if (pItem && !IsInvalidItem(pItem))
1039 const sal_uInt16 nWhich = pItem->Which();
1041 if ( isCHRATR(nWhich) ||
1042 (RES_TXTATR_CHARFMT == nWhich) ||
1043 (RES_TXTATR_INETFMT == nWhich) ||
1044 (RES_TXTATR_AUTOFMT == nWhich) ||
1045 (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
1047 pCharSet = &rChgSet;
1048 bCharAttr = true;
1051 if ( isPARATR(nWhich)
1052 || isPARATR_LIST(nWhich)
1053 || isFRMATR(nWhich)
1054 || isGRFATR(nWhich)
1055 || isUNKNOWNATR(nWhich)
1056 || isDrawingLayerAttribute(nWhich) )
1058 pOtherSet = &rChgSet;
1059 bOtherAttr = true;
1064 // Build new itemset if either
1065 // - rChgSet.Count() > 1 or
1066 // - The attribute in rChgSet does not belong to one of the above categories
1067 if ( !bCharAttr && !bOtherAttr )
1069 SfxItemSet* pTmpCharItemSet = new SfxItemSet(
1070 pDoc->GetAttrPool(),
1071 svl::Items<
1072 RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
1073 RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT,
1074 RES_TXTATR_UNKNOWN_CONTAINER,
1075 RES_TXTATR_UNKNOWN_CONTAINER>{});
1077 SfxItemSet* pTmpOtherItemSet = new SfxItemSet(
1078 pDoc->GetAttrPool(),
1079 svl::Items<
1080 RES_PARATR_BEGIN, RES_GRFATR_END - 1,
1081 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1,
1082 // FillAttribute support:
1083 XATTR_FILL_FIRST, XATTR_FILL_LAST>{});
1085 pTmpCharItemSet->Put( rChgSet );
1086 pTmpOtherItemSet->Put( rChgSet );
1088 pCharSet = pTmpCharItemSet;
1089 pOtherSet = pTmpOtherItemSet;
1091 bDelete = true;
1094 SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
1095 bool bRet = false;
1096 const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
1097 SwContentNode* pNode = pStt->nNode.GetNode().GetContentNode();
1099 if( pNode && pNode->IsTextNode() )
1101 // #i27615#
1102 if (rRg.IsInFrontOfLabel())
1104 SwTextNode * pTextNd = pNode->GetTextNode();
1105 SwNumRule * pNumRule = pTextNd->GetNumRule();
1107 if ( !pNumRule )
1109 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1110 DELETECHARSETS
1111 return false;
1114 int nLevel = pTextNd->GetActualListLevel();
1116 if (nLevel < 0)
1117 nLevel = 0;
1119 if (nLevel >= MAXLEVEL)
1120 nLevel = MAXLEVEL - 1;
1122 SwNumFormat aNumFormat = pNumRule->Get(static_cast<sal_uInt16>(nLevel));
1123 SwCharFormat * pCharFormat =
1124 pDoc->FindCharFormatByName(aNumFormat.GetCharFormatName());
1126 if (pCharFormat)
1128 if (pHistory)
1129 pHistory->Add(pCharFormat->GetAttrSet(), *pCharFormat);
1131 if ( pCharSet )
1132 pCharFormat->SetFormatAttr(*pCharSet);
1135 DELETECHARSETS
1136 return true;
1139 const SwIndex& rSt = pStt->nContent;
1141 // Attributes without an end do not have a range
1142 if ( !bCharAttr && !bOtherAttr )
1144 SfxItemSet aTextSet( pDoc->GetAttrPool(),
1145 svl::Items<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1>{} );
1146 aTextSet.Put( rChgSet );
1147 if( aTextSet.Count() )
1149 SwRegHistory history( pNode, *pNode, pHistory );
1150 bRet = history.InsertItems(
1151 aTextSet, rSt.GetIndex(), rSt.GetIndex(), nFlags ) || bRet;
1153 if (bRet && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline()
1154 && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty())))
1156 SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1,
1157 pStt->nNode, pStt->nContent.GetIndex() );
1159 if( pUndo )
1160 pUndo->SaveRedlineData( aPam, true );
1162 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
1163 pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
1164 else
1165 pDoc->getIDocumentRedlineAccess().SplitRedline( aPam );
1170 // TextAttributes with an end never expand their range
1171 if ( !bCharAttr && !bOtherAttr )
1173 // CharFormat and URL attributes are treated separately!
1174 // TEST_TEMP ToDo: AutoFormat!
1175 SfxItemSet aTextSet(
1176 pDoc->GetAttrPool(),
1177 svl::Items<
1178 RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD,
1179 RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
1180 RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD>{});
1182 aTextSet.Put( rChgSet );
1183 if( aTextSet.Count() )
1185 const sal_Int32 nInsCnt = rSt.GetIndex();
1186 const sal_Int32 nEnd = pStt->nNode == pEnd->nNode
1187 ? pEnd->nContent.GetIndex()
1188 : pNode->Len();
1189 SwRegHistory history( pNode, *pNode, pHistory );
1190 bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags )
1191 || bRet;
1193 if (bRet && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline()
1194 && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty())))
1196 // Was text content inserted? (RefMark/TOXMarks without an end)
1197 bool bTextIns = nInsCnt != rSt.GetIndex();
1198 // Was content inserted or set over the selection?
1199 SwPaM aPam( pStt->nNode, bTextIns ? nInsCnt + 1 : nEnd,
1200 pStt->nNode, nInsCnt );
1201 if( pUndo )
1202 pUndo->SaveRedlineData( aPam, bTextIns );
1204 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
1205 pDoc->getIDocumentRedlineAccess().AppendRedline(
1206 new SwRangeRedline(
1207 bTextIns ? nsRedlineType_t::REDLINE_INSERT : nsRedlineType_t::REDLINE_FORMAT, aPam ),
1208 true);
1209 else if( bTextIns )
1210 pDoc->getIDocumentRedlineAccess().SplitRedline( aPam );
1216 // We always have to set the auto flag for PageDescs that are set at the Node!
1217 if( pOtherSet && pOtherSet->Count() )
1219 SwTableNode* pTableNd;
1220 const SwFormatPageDesc* pDesc;
1221 if( SfxItemState::SET == pOtherSet->GetItemState( RES_PAGEDESC,
1222 false, reinterpret_cast<const SfxPoolItem**>(&pDesc) ))
1224 if( pNode )
1226 // Set auto flag. Only in the template it's without auto!
1227 SwFormatPageDesc aNew( *pDesc );
1229 // Tables now also know line breaks
1230 if( !(nFlags & SetAttrMode::APICALL) &&
1231 nullptr != ( pTableNd = pNode->FindTableNode() ) )
1233 SwTableNode* pCurTableNd = pTableNd;
1234 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1235 pTableNd = pCurTableNd;
1237 // set the table format
1238 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1239 SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1240 pFormat->SetFormatAttr( aNew );
1241 bRet = true;
1243 else
1245 SwRegHistory aRegH( pNode, *pNode, pHistory );
1246 bRet = pNode->SetAttr( aNew ) || bRet;
1250 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1251 // we know, that there is only one attribute in pOtherSet. We cannot
1252 // perform the following operations, instead we return:
1253 if ( bOtherAttr )
1254 return bRet;
1256 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
1257 if( !pOtherSet->Count() )
1259 DELETECHARSETS
1260 return bRet;
1264 // Tables now also know line breaks
1265 const SvxFormatBreakItem* pBreak;
1266 if( pNode && !(nFlags & SetAttrMode::APICALL) &&
1267 nullptr != (pTableNd = pNode->FindTableNode() ) &&
1268 SfxItemState::SET == pOtherSet->GetItemState( RES_BREAK,
1269 false, reinterpret_cast<const SfxPoolItem**>(&pBreak) ) )
1271 SwTableNode* pCurTableNd = pTableNd;
1272 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
1273 pTableNd = pCurTableNd;
1275 // set the table format
1276 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
1277 SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
1278 pFormat->SetFormatAttr( *pBreak );
1279 bRet = true;
1281 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1282 // we know, that there is only one attribute in pOtherSet. We cannot
1283 // perform the following operations, instead we return:
1284 if ( bOtherAttr )
1285 return bRet;
1287 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
1288 if( !pOtherSet->Count() )
1290 DELETECHARSETS
1291 return bRet;
1296 // If we have a PoolNumRule, create it if needed
1297 const SwNumRuleItem* pRule;
1298 sal_uInt16 nPoolId=0;
1299 if( SfxItemState::SET == pOtherSet->GetItemState( RES_PARATR_NUMRULE,
1300 false, reinterpret_cast<const SfxPoolItem**>(&pRule) ) &&
1301 !pDoc->FindNumRulePtr( pRule->GetValue() ) &&
1302 USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(),
1303 SwGetPoolIdFromName::NumRule )) )
1304 pDoc->getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId );
1308 if( !rRg.HasMark() ) // no range
1310 if( !pNode )
1312 DELETECHARSETS
1313 return bRet;
1316 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1318 SwTextNode* pTextNd = pNode->GetTextNode();
1319 const SwIndex& rSt = pStt->nContent;
1320 sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
1321 const OUString& rStr = pTextNd->GetText();
1323 // Special case: if the Cursor is located within a URL attribute, we take over it's area
1324 SwTextAttr const*const pURLAttr(
1325 pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
1326 if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
1328 nMkPos = pURLAttr->GetStart();
1329 nPtPos = *pURLAttr->End();
1331 else
1333 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
1334 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1335 pTextNd->GetText(), nPtPos,
1336 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1337 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
1338 true);
1340 if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
1342 nMkPos = aBndry.startPos;
1343 nPtPos = aBndry.endPos;
1345 else
1346 nPtPos = nMkPos = rSt.GetIndex();
1349 // Remove the overriding attributes from the SwpHintsArray,
1350 // if the selection spans across the whole paragraph.
1351 // These attributes are inserted as FormatAttributes and
1352 // never override the TextAttributes!
1353 if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
1354 pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
1356 SwIndex aSt( pTextNd );
1357 if( pHistory )
1359 // Save all attributes for the Undo.
1360 SwRegHistory aRHst( *pTextNd, pHistory );
1361 pTextNd->GetpSwpHints()->Register( &aRHst );
1362 pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1363 if( pTextNd->GetpSwpHints() )
1364 pTextNd->GetpSwpHints()->DeRegister();
1366 else
1367 pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
1370 // the SwRegHistory inserts the attribute into the TextNode!
1371 SwRegHistory history( pNode, *pNode, pHistory );
1372 bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags )
1373 || bRet;
1375 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
1377 SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
1379 if( pUndo )
1380 pUndo->SaveRedlineData( aPam, false );
1381 pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_FORMAT, aPam ), true);
1384 if( pOtherSet && pOtherSet->Count() )
1386 SwRegHistory aRegH( pNode, *pNode, pHistory );
1388 // Need to check for unique item for DrawingLayer items of type NameOrIndex
1389 // and evtl. correct that item to ensure unique names for that type. This call may
1390 // modify/correct entries inside of the given SfxItemSet
1391 SfxItemSet aTempLocalCopy(*pOtherSet);
1393 pDoc->CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
1394 bRet = pNode->SetAttr(aTempLocalCopy) || bRet;
1397 DELETECHARSETS
1398 return bRet;
1401 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
1403 if( pUndo )
1404 pUndo->SaveRedlineData( rRg, false );
1405 pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_FORMAT, rRg ), true);
1408 /* now if range */
1409 sal_uLong nNodes = 0;
1411 SwNodeIndex aSt( pDoc->GetNodes() );
1412 SwNodeIndex aEnd( pDoc->GetNodes() );
1413 SwIndex aCntEnd( pEnd->nContent );
1415 if( pNode )
1417 const sal_Int32 nLen = pNode->Len();
1418 if( pStt->nNode != pEnd->nNode )
1419 aCntEnd.Assign( pNode, nLen );
1421 if( pStt->nContent.GetIndex() != 0 || aCntEnd.GetIndex() != nLen )
1423 // the SwRegHistory inserts the attribute into the TextNode!
1424 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1426 SwRegHistory history( pNode, *pNode, pHistory );
1427 bRet = history.InsertItems(*pCharSet,
1428 pStt->nContent.GetIndex(), aCntEnd.GetIndex(), nFlags)
1429 || bRet;
1432 if( pOtherSet && pOtherSet->Count() )
1434 SwRegHistory aRegH( pNode, *pNode, pHistory );
1435 bRet = pNode->SetAttr( *pOtherSet ) || bRet;
1438 // Only selection in a Node.
1439 if( pStt->nNode == pEnd->nNode )
1441 //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
1442 //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
1443 //current setting attribute set is a character range properties set and comes from a MS Word
1444 //binary file, and the setting range include a paragraph end position (0X0D);
1445 //more specifications, as such property inside the character range properties set recorded in
1446 //MS Word binary file are dealed and inserted into data model (SwDoc) one by one, so we
1447 //only dealing the scenario that the char properties set with 1 item inside;
1449 if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1 )
1451 SwTextNode* pCurrentNd = pStt->nNode.GetNode().GetTextNode();
1453 if (pCurrentNd)
1455 pCurrentNd->TryCharSetExpandToNum(*pCharSet);
1459 DELETECHARSETS
1460 return bRet;
1462 ++nNodes;
1463 aSt.Assign( pStt->nNode.GetNode(), +1 );
1465 else
1466 aSt = pStt->nNode;
1467 aCntEnd = pEnd->nContent; // aEnd was changed!
1469 else
1470 aSt.Assign( pStt->nNode.GetNode(), +1 );
1472 // aSt points to the first full Node now
1475 * The selection spans more than one Node.
1477 if( pStt->nNode < pEnd->nNode )
1479 pNode = pEnd->nNode.GetNode().GetContentNode();
1480 if(pNode)
1482 if( aCntEnd.GetIndex() != pNode->Len() )
1484 // the SwRegHistory inserts the attribute into the TextNode!
1485 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
1487 SwRegHistory history( pNode, *pNode, pHistory );
1488 (void)history.InsertItems(*pCharSet,
1489 0, aCntEnd.GetIndex(), nFlags);
1492 if( pOtherSet && pOtherSet->Count() )
1494 SwRegHistory aRegH( pNode, *pNode, pHistory );
1495 pNode->SetAttr( *pOtherSet );
1498 ++nNodes;
1499 aEnd = pEnd->nNode;
1501 else
1502 aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1504 else
1505 aEnd = pEnd->nNode;
1507 else
1508 aEnd.Assign( pEnd->nNode.GetNode(), +1 );
1510 // aEnd points BEHIND the last full node now
1512 /* Edit the fully selected Nodes. */
1513 // Reset all attributes from the set!
1514 if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
1516 ::sw::DocumentContentOperationsManager::ParaRstFormat aPara( pStt, pEnd, pHistory, pCharSet );
1517 pDoc->GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
1520 bool bCreateSwpHints = pCharSet && (
1521 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
1522 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
1524 for(; aSt < aEnd; ++aSt )
1526 pNode = aSt.GetNode().GetContentNode();
1527 if( !pNode )
1528 continue;
1530 SwTextNode* pTNd = pNode->GetTextNode();
1531 if( pHistory )
1533 SwRegHistory aRegH( pNode, *pNode, pHistory );
1535 if( pTNd && pCharSet && pCharSet->Count() )
1537 SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
1538 : pTNd->GetpSwpHints();
1539 if( pSwpHints )
1540 pSwpHints->Register( &aRegH );
1542 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1543 if( pSwpHints )
1544 pSwpHints->DeRegister();
1546 if( pOtherSet && pOtherSet->Count() )
1547 pNode->SetAttr( *pOtherSet );
1549 else
1551 if( pTNd && pCharSet && pCharSet->Count() )
1552 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
1553 if( pOtherSet && pOtherSet->Count() )
1554 pNode->SetAttr( *pOtherSet );
1556 ++nNodes;
1559 //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc,
1560 //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that
1561 //current setting attribute set is a character range properties set and comes from a MS Word
1562 //binary file, and the setting range include a paragraph end position (0X0D);
1563 //more specifications, as such property inside the character range properties set recorded in
1564 //MS Word binary file are dealed and inserted into data model (SwDoc) one by one, so we
1565 //only dealing the scenario that the char properties set with 1 item inside;
1566 if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1)
1568 SwPosition aStartPos (*rRg.Start());
1569 SwPosition aEndPos (*rRg.End());
1571 if (aEndPos.nNode.GetNode().GetTextNode() && aEndPos.nContent != aEndPos.nNode.GetNode().GetTextNode()->Len())
1572 aEndPos.nNode--;
1574 sal_uLong nStart = aStartPos.nNode.GetIndex();
1575 sal_uLong nEnd = aEndPos.nNode.GetIndex();
1576 for(; nStart <= nEnd; ++nStart)
1578 SwNode* pNd = pDoc->GetNodes()[ nStart ];
1579 if (!pNd || !pNd->IsTextNode())
1580 continue;
1581 SwTextNode *pCurrentNd = pNd->GetTextNode();
1582 pCurrentNd->TryCharSetExpandToNum(*pCharSet);
1586 DELETECHARSETS
1587 return (nNodes != 0) || bRet;
1591 namespace sw
1594 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
1598 // Copy an area into this document or into another document
1599 bool
1600 DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos, const bool bCopyAll, bool bCheckPos ) const
1602 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
1604 SwDoc* pDoc = rPos.nNode.GetNode().GetDoc();
1605 bool bColumnSel = pDoc->IsClipBoard() && pDoc->IsColumnSelection();
1607 // Catch if there's no copy to do
1608 if( !rPam.HasMark() || ( *pStt >= *pEnd && !bColumnSel ) )
1609 return false;
1611 // Prevent copying in Flys that are anchored in the area
1612 if( pDoc == &m_rDoc && bCheckPos )
1614 // Correct the Start-/EndNode
1615 sal_uLong nStt = pStt->nNode.GetIndex(),
1616 nEnd = pEnd->nNode.GetIndex(),
1617 nDiff = nEnd - nStt +1;
1618 SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
1619 if( pNd->IsContentNode() && pStt->nContent.GetIndex() )
1621 ++nStt;
1622 --nDiff;
1624 if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
1625 static_cast<SwContentNode*>(pNd)->Len() != pEnd->nContent.GetIndex() )
1627 --nEnd;
1628 --nDiff;
1630 if( nDiff &&
1631 lcl_ChkFlyFly( pDoc, nStt, nEnd, rPos.nNode.GetIndex() ) )
1633 return false;
1637 SwPaM* pRedlineRange = nullptr;
1638 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ||
1639 (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
1640 pRedlineRange = new SwPaM( rPos );
1642 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
1644 bool bRet = false;
1646 if( pDoc != &m_rDoc )
1647 { // ordinary copy
1648 bRet = CopyImpl( rPam, rPos, true, bCopyAll, pRedlineRange );
1650 else if( ! ( *pStt <= rPos && rPos < *pEnd &&
1651 ( pStt->nNode != pEnd->nNode ||
1652 !pStt->nNode.GetNode().IsTextNode() )) )
1654 // Copy to a position outside of the area, or copy a single TextNode
1655 // Do an ordinary copy
1656 bRet = CopyImpl( rPam, rPos, true, bCopyAll, pRedlineRange );
1658 else
1660 // Copy the area in itself
1661 // Special case for handling an area with several nodes,
1662 // or a single node that is not a TextNode
1663 OSL_ENSURE( &m_rDoc == pDoc, " invalid copy branch!" );
1664 assert(!"mst: this is assumed to be dead code");
1665 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
1667 // Then copy the area to the underlying document area
1668 // (with start/end nodes clamped) and move them to
1669 // the desired position.
1671 SwUndoCpyDoc* pUndo = nullptr;
1672 // Save the Undo area
1673 SwPaM aPam( rPos );
1674 if (pDoc->GetIDocumentUndoRedo().DoesUndo())
1676 pDoc->GetIDocumentUndoRedo().ClearRedo();
1677 pUndo = new SwUndoCpyDoc( aPam );
1681 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
1682 SwStartNode* pSttNd = SwNodes::MakeEmptySection(
1683 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ));
1684 aPam.GetPoint()->nNode = *pSttNd->EndOfSectionNode();
1685 // copy without Frames
1686 pDoc->GetDocumentContentOperationsManager().CopyImpl( rPam, *aPam.GetPoint(), false, bCopyAll, nullptr );
1688 aPam.GetPoint()->nNode = pDoc->GetNodes().GetEndOfAutotext();
1689 aPam.SetMark();
1690 SwContentNode* pNode = SwNodes::GoPrevious( &aPam.GetMark()->nNode );
1691 pNode->MakeEndIndex( &aPam.GetMark()->nContent );
1693 aPam.GetPoint()->nNode = *aPam.GetNode().StartOfSectionNode();
1694 pNode = pDoc->GetNodes().GoNext( &aPam.GetPoint()->nNode );
1695 pNode->MakeStartIndex( &aPam.GetPoint()->nContent );
1696 // move to desired position
1697 pDoc->getIDocumentContentOperations().MoveRange( aPam, rPos, SwMoveFlags::DEFAULT );
1699 pNode = aPam.GetContentNode();
1700 *aPam.GetPoint() = rPos; // Move the cursor for Undo
1701 aPam.SetMark(); // also move the Mark
1702 aPam.DeleteMark(); // But don't mark any area
1703 pDoc->getIDocumentContentOperations().DeleteSection( pNode ); // Delete the area again
1706 // if Undo is enabled, store the insertion range
1707 if (pDoc->GetIDocumentUndoRedo().DoesUndo())
1709 pUndo->SetInsertRange( aPam );
1710 pDoc->GetIDocumentUndoRedo().AppendUndo(pUndo);
1713 if( pRedlineRange )
1715 pRedlineRange->SetMark();
1716 *pRedlineRange->GetPoint() = *aPam.GetPoint();
1717 *pRedlineRange->GetMark() = *aPam.GetMark();
1720 pDoc->getIDocumentState().SetModified();
1721 bRet = true;
1724 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
1725 if( pRedlineRange )
1727 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
1728 pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, *pRedlineRange ), true);
1729 else
1730 pDoc->getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
1731 delete pRedlineRange;
1734 return bRet;
1737 /// Delete a full Section of the NodeArray.
1738 /// The passed Node is located somewhere in the designated Section.
1739 void DocumentContentOperationsManager::DeleteSection( SwNode *pNode )
1741 assert(pNode && "Didn't pass a Node.");
1743 SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
1744 : pNode->StartOfSectionNode();
1745 SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
1747 // delete all Flys, Bookmarks, ...
1748 DelFlyInRange( aSttIdx, aEndIdx );
1749 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, USHRT_MAX );
1750 DelBookmarks(aSttIdx, aEndIdx);
1753 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
1754 SwNodeIndex aMvStt( aSttIdx, 1 );
1755 SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true );
1758 m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
1761 void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam )
1763 lcl_DoWithBreaks( *this, rPam, &DocumentContentOperationsManager::DeleteRangeImpl );
1766 bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
1768 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
1769 const SwNode* pNd = &rStt.nNode.GetNode();
1770 sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
1771 pNd->StartOfSectionIndex();
1772 sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
1774 if ( nSectDiff-2 <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
1775 /* #i9185# Prevent getting the node after the end node (see below) */
1776 rEnd.nNode.GetIndex() + 1 == m_rDoc.GetNodes().Count() )
1778 return false;
1781 // Move hard page brakes to the following Node.
1782 bool bSavePageBreak = false, bSavePageDesc = false;
1784 /* #i9185# This whould lead to a segmentation fault if not caught above. */
1785 sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
1786 SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
1788 if( pTableNd && pNd->IsContentNode() )
1790 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
1793 const SfxPoolItem *pItem;
1794 const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
1795 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
1796 false, &pItem ) )
1798 pTableFormat->SetFormatAttr( *pItem );
1799 bSavePageDesc = true;
1802 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
1803 false, &pItem ) )
1805 pTableFormat->SetFormatAttr( *pItem );
1806 bSavePageBreak = true;
1811 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
1812 if( bDoesUndo )
1814 if( !rPam.HasMark() )
1815 rPam.SetMark();
1816 else if( rPam.GetPoint() == &rStt )
1817 rPam.Exchange();
1818 rPam.GetPoint()->nNode++;
1820 SwContentNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetContentNode();
1821 rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
1822 bool bGoNext = (nullptr == pTmpNode);
1823 pTmpNode = rPam.GetMark()->nNode.GetNode().GetContentNode();
1824 rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
1826 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
1828 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
1830 SwPosition aTmpPos( *aDelPam.GetPoint() );
1831 if( bGoNext )
1833 pTmpNode = m_rDoc.GetNodes().GoNext( &aTmpPos.nNode );
1834 aTmpPos.nContent.Assign( pTmpNode, 0 );
1836 ::PaMCorrAbs( aDelPam, aTmpPos );
1839 SwUndoDelete* pUndo = new SwUndoDelete( aDelPam, true );
1841 *rPam.GetPoint() = *aDelPam.GetPoint();
1842 pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
1843 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
1844 rPam.DeleteMark();
1846 else
1848 SwNodeRange aRg( rStt.nNode, rEnd.nNode );
1849 rPam.Normalize(false);
1851 // Try to move past the End
1852 if( !rPam.Move( fnMoveForward, GoInNode ) )
1854 // Fair enough, at the Beginning then
1855 rPam.Exchange();
1856 if( !rPam.Move( fnMoveBackward, GoInNode ))
1858 SAL_WARN("sw.core", "DelFullPara: no more Nodes");
1859 return false;
1862 // move bookmarks, redlines etc.
1863 if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
1865 m_rDoc.CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, true );
1867 else
1869 SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
1872 // What's with Flys?
1874 // If there are FlyFrames left, delete these too
1875 for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
1877 SwFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
1878 const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
1879 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
1880 if (pAPos &&
1881 ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
1882 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
1883 aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
1885 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
1886 --n;
1891 rPam.DeleteMark();
1892 m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
1894 m_rDoc.getIDocumentState().SetModified();
1896 return true;
1899 // #i100466# Add handling of new optional parameter <bForceJoinNext>
1900 bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM & rPam,
1901 const bool bForceJoinNext )
1903 if ( lcl_StrLenOverflow( rPam ) )
1904 return false;
1906 return lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
1907 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
1908 : &DocumentContentOperationsManager::DeleteAndJoinImpl,
1909 bForceJoinNext );
1912 // It seems that this is mostly used by SwDoc internals; the only
1913 // way to call this from the outside seems to be the special case in
1914 // SwDoc::CopyRange (but I have not managed to actually hit that case).
1915 bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
1917 // nothing moved: return
1918 const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
1919 if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
1920 return false;
1922 // Save the paragraph anchored Flys, so that they can be moved.
1923 SaveFlyArr aSaveFlyArr;
1924 SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
1926 // save redlines (if DOC_MOVEREDLINES is used)
1927 SaveRedlines_t aSaveRedl;
1928 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
1930 lcl_SaveRedlines( rPaM, aSaveRedl );
1932 // #i17764# unfortunately, code below relies on undos being
1933 // in a particular order, and presence of bookmarks
1934 // will change this order. Hence, we delete bookmarks
1935 // here without undo.
1936 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
1937 DelBookmarks(
1938 pStt->nNode,
1939 pEnd->nNode,
1940 nullptr,
1941 &pStt->nContent,
1942 &pEnd->nContent);
1945 bool bUpdateFootnote = false;
1946 SwFootnoteIdxs aTmpFntIdx;
1948 SwUndoMove * pUndoMove = nullptr;
1949 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
1951 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
1952 pUndoMove = new SwUndoMove( rPaM, rPos );
1953 pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
1955 else
1957 bUpdateFootnote = lcl_SaveFootnote( pStt->nNode, pEnd->nNode, rPos.nNode,
1958 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
1959 &pStt->nContent, &pEnd->nContent );
1962 bool bSplit = false;
1963 SwPaM aSavePam( rPos, rPos );
1965 // Move the SPoint to the beginning of the range
1966 if( rPaM.GetPoint() == pEnd )
1967 rPaM.Exchange();
1969 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
1970 SwTextNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTextNode();
1971 bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
1973 // If one ore more TextNodes are moved, SwNodes::Move will do a SplitNode.
1974 // However, this does not update the cursor. So we create a TextNode to keep
1975 // updating the indices. After the Move the Node is optionally deleted.
1976 SwTextNode * pTNd = rPos.nNode.GetNode().GetTextNode();
1977 if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
1978 ( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
1980 bSplit = true;
1981 const sal_Int32 nMkContent = rPaM.GetMark()->nContent.GetIndex();
1983 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1984 pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
1986 SwTextNode * pOrigNode = pTNd;
1987 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
1988 *aSavePam.GetPoint() == rPos);
1989 assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
1990 assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
1991 assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
1993 pTNd = pTNd->SplitContentNode( rPos )->GetTextNode();
1995 //A new node was inserted before the orig pTNd and the content up to
1996 //rPos moved into it. The old node is returned with the remainder
1997 //of the content in it.
1999 //aSavePam was created with rPos, it continues to point to the
2000 //old node, but with the *original* content index into the node.
2001 //Seeing as all the orignode content before that index has
2002 //been removed, the new index into the original node should now be set
2003 //to 0 and the content index of rPos should also be adapted to the
2004 //truncated node
2005 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
2006 *aSavePam.GetPoint() == rPos);
2007 assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
2008 assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
2009 assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
2010 aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0);
2011 rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
2013 if( !pContentStore->Empty() )
2014 pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true );
2016 // correct the PaM!
2017 if( rPos.nNode == rPaM.GetMark()->nNode )
2019 rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
2020 rPaM.GetMark()->nContent.Assign( pTNd, nMkContent );
2024 // Put back the Pam by one "content"; so that it's always outside of
2025 // the manipulated range.
2026 // tdf#99692 don't Move() back if that would end up in another node
2027 // because moving backward is not necessarily the inverse of forward then.
2028 // (but do Move() back if we have split the node)
2029 const bool bNullContent = !bSplit && aSavePam.GetPoint()->nContent == 0;
2030 if( bNullContent )
2032 aSavePam.GetPoint()->nNode--;
2033 aSavePam.GetPoint()->nContent.Assign(aSavePam.GetContentNode(), 0);
2035 else
2037 bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
2038 assert(success);
2039 (void) success;
2042 // Copy all Bookmarks that are within the Move range into an array,
2043 // that saves the position as an offset.
2044 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2045 DelBookmarks(
2046 pStt->nNode,
2047 pEnd->nNode,
2048 &aSaveBkmks,
2049 &pStt->nContent,
2050 &pEnd->nContent);
2052 // If there is no range anymore due to the above deletions (e.g. the
2053 // footnotes got deleted), it's still a valid Move!
2054 if( *rPaM.GetPoint() != *rPaM.GetMark() )
2056 // now do the actual move
2057 m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
2059 // after a MoveRange() the Mark is deleted
2060 if ( rPaM.HasMark() ) // => no Move occurred!
2062 delete pUndoMove;
2063 return false;
2066 else
2067 rPaM.DeleteMark();
2069 OSL_ENSURE( *aSavePam.GetMark() == rPos ||
2070 ( aSavePam.GetMark()->nNode.GetNode().GetContentNode() == nullptr ),
2071 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2072 *aSavePam.GetMark() = rPos;
2074 rPaM.SetMark(); // create a Sel. around the new range
2075 pTNd = aSavePam.GetNode().GetTextNode();
2076 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2078 assert(!"mst: this is assumed to be dead code");
2080 // correct the SavePam's Content first
2081 if( bNullContent )
2083 aSavePam.GetPoint()->nContent = 0;
2086 // The method SwEditShell::Move() merges the TextNode after the Move,
2087 // where the rPaM is located.
2088 // If the Content was moved to the back and the SavePam's SPoint is
2089 // in the next Node, we have to deal with this when saving the Undo object!
2090 SwTextNode * pPamTextNd = nullptr;
2092 // Is passed to SwUndoMove, which happens when subsequently calling Undo JoinNext.
2093 // If it's not possible to call Undo JoinNext here.
2094 bool bJoin = bSplit && pTNd;
2095 if( bCorrSavePam )
2097 pPamTextNd = rPaM.GetNode().GetTextNode();
2098 bCorrSavePam = (pPamTextNd != nullptr)
2099 && pPamTextNd->CanJoinNext()
2100 && (*rPaM.GetPoint() <= *aSavePam.GetPoint());
2103 // Do two Nodes have to be joined at the SavePam?
2104 if( bJoin && pTNd->CanJoinNext() )
2106 pTNd->JoinNext();
2107 // No temporary Index when using &&.
2108 // We probably only want to compare the indices.
2109 if( bCorrSavePam && rPaM.GetPoint()->nNode.GetIndex()+1 ==
2110 aSavePam.GetPoint()->nNode.GetIndex() )
2112 aSavePam.GetPoint()->nContent += pPamTextNd->Len();
2114 bJoin = false;
2116 else if ( !aSavePam.Move( fnMoveForward, GoInContent ) )
2118 aSavePam.GetPoint()->nNode++;
2121 // The newly inserted range is now inbetween SPoint and GetMark.
2122 pUndoMove->SetDestRange( aSavePam, *rPaM.GetPoint(),
2123 bJoin, bCorrSavePam );
2124 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoMove );
2126 else
2128 bool bRemove = true;
2129 // Do two Nodes have to be joined at the SavePam?
2130 if( bSplit && pTNd )
2132 if( pTNd->CanJoinNext())
2134 // Always join next, because <pTNd> has to stay as it is.
2135 // A join previous from its next would more or less delete <pTNd>
2136 pTNd->JoinNext();
2137 bRemove = false;
2140 if( bNullContent )
2142 aSavePam.GetPoint()->nNode++;
2143 aSavePam.GetPoint()->nContent.Assign( aSavePam.GetContentNode(), 0 );
2145 else if( bRemove ) // No move forward after joining with next paragraph
2147 aSavePam.Move( fnMoveForward, GoInContent );
2151 // Insert the Bookmarks back into the Document.
2152 *rPaM.GetMark() = *aSavePam.Start();
2153 for(
2154 std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
2155 pBkmk != aSaveBkmks.end();
2156 ++pBkmk)
2157 pBkmk->SetInDoc(
2158 &m_rDoc,
2159 rPaM.GetMark()->nNode,
2160 &rPaM.GetMark()->nContent);
2161 *rPaM.GetPoint() = *aSavePam.End();
2163 // Move the Flys to the new position.
2164 RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
2166 // restore redlines (if DOC_MOVEREDLINES is used)
2167 if( !aSaveRedl.empty() )
2169 lcl_RestoreRedlines( &m_rDoc, *aSavePam.Start(), aSaveRedl );
2172 if( bUpdateFootnote )
2174 if( !aTmpFntIdx.empty() )
2176 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2177 aTmpFntIdx.clear();
2180 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
2183 m_rDoc.getIDocumentState().SetModified();
2184 return true;
2187 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
2188 SwMoveFlags eMvFlags )
2190 // Moves all Nodes to the new position.
2191 // Bookmarks are moved too (currently without Undo support).
2193 // If footnotes are being moved to the special section, remove them now.
2195 // Or else delete the Frames for all footnotes that are being moved
2196 // and have it rebuild after the Move (footnotes can change pages).
2197 // Additionally we have to correct the FootnoteIdx array's sorting.
2198 bool bUpdateFootnote = false;
2199 SwFootnoteIdxs aTmpFntIdx;
2201 SwUndoMove* pUndo = nullptr;
2202 if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2204 pUndo = new SwUndoMove( &m_rDoc, rRange, rPos );
2206 else
2208 bUpdateFootnote = lcl_SaveFootnote( rRange.aStart, rRange.aEnd, rPos,
2209 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
2212 SaveRedlines_t aSaveRedl;
2213 std::vector<SwRangeRedline*> aSavRedlInsPosArr;
2214 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2216 lcl_SaveRedlines( rRange, aSaveRedl );
2218 // Find all RedLines that end at the InsPos.
2219 // These have to be moved back to the "old" position after the Move.
2220 SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rPos.GetNode(), USHRT_MAX );
2221 if( SwRedlineTable::npos != nRedlPos )
2223 const SwPosition *pRStt, *pREnd;
2224 do {
2225 SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
2226 pRStt = pTmp->Start();
2227 pREnd = pTmp->End();
2228 if( pREnd->nNode == rPos && pRStt->nNode < rPos )
2230 aSavRedlInsPosArr.push_back( pTmp );
2232 } while( pRStt->nNode < rPos && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
2236 // Copy all Bookmarks that are within the Move range into an array
2237 // that stores all references to positions as an offset.
2238 // The final mapping happens after the Move.
2239 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
2240 DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
2242 // Save the paragraph-bound Flys, so that they can be moved.
2243 SaveFlyArr aSaveFlyArr;
2244 if( !m_rDoc.GetSpzFrameFormats()->empty() )
2245 SaveFlyInRange( rRange, aSaveFlyArr );
2247 // Set it to before the Position, so that it cannot be moved further.
2248 SwNodeIndex aIdx( rPos, -1 );
2250 SwNodeIndex* pSaveInsPos = nullptr;
2251 if( pUndo )
2252 pSaveInsPos = new SwNodeIndex( rRange.aStart, -1 );
2254 // move the Nodes
2255 bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
2256 if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rPos, !bNoDelFrames ) )
2258 ++aIdx; // again back to old position
2259 if( pSaveInsPos )
2260 ++(*pSaveInsPos);
2262 else
2264 aIdx = rRange.aStart;
2265 delete pUndo;
2266 pUndo = nullptr;
2269 // move the Flys to the new position
2270 if( !aSaveFlyArr.empty() )
2271 RestFlyInRange( aSaveFlyArr, aIdx, nullptr );
2273 // Add the Bookmarks back to the Document
2274 for(
2275 std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
2276 pBkmk != aSaveBkmks.end();
2277 ++pBkmk)
2278 pBkmk->SetInDoc(&m_rDoc, aIdx);
2280 if( !aSavRedlInsPosArr.empty() )
2282 SwNode* pNewNd = &aIdx.GetNode();
2283 for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
2285 if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) )
2287 SwPosition* pEnd = pTmp->End();
2288 pEnd->nNode = aIdx;
2289 pEnd->nContent.Assign( pNewNd->GetContentNode(), 0 );
2294 if( !aSaveRedl.empty() )
2295 lcl_RestoreRedlines( &m_rDoc, aIdx.GetIndex(), aSaveRedl );
2297 if( pUndo )
2299 pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
2300 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
2303 delete pSaveInsPos;
2305 if( bUpdateFootnote )
2307 if( !aTmpFntIdx.empty() )
2309 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
2310 aTmpFntIdx.clear();
2313 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
2316 m_rDoc.getIDocumentState().SetModified();
2317 return true;
2320 bool DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos )
2322 SwNodeIndex aIdx( rPaM.Start()->nNode );
2323 bool bJoinText = aIdx.GetNode().IsTextNode();
2324 bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
2325 aIdx--; // in front of the move area!
2327 bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
2328 if( bRet && !bOneNode )
2330 if( bJoinText )
2331 ++aIdx;
2332 SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
2333 SwNodeIndex aNxtIdx( aIdx );
2334 if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
2336 { // Block so SwIndex into node is deleted before Join
2337 m_rDoc.CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex(pTextNd,
2338 pTextNd->GetText().getLength()) ), 0, true );
2340 pTextNd->JoinNext();
2343 return bRet;
2346 bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
2348 SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
2349 if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2351 if( 1 == rStr.getLength() )
2352 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
2353 m_rDoc.DeleteAutoCorrExceptWord();
2356 SwTextNode *pNode = rPt.nNode.GetNode().GetTextNode();
2357 if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
2359 return false;
2362 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2364 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2367 const size_t nOldAttrCnt = pNode->GetpSwpHints()
2368 ? pNode->GetpSwpHints()->Count() : 0;
2369 SwDataChanged aTmp( rRg );
2370 SwIndex& rIdx = rPt.nContent;
2371 sal_Int32 nStart = 0;
2373 bool bOldExpFlg = pNode->IsIgnoreDontExpand();
2374 pNode->SetIgnoreDontExpand( true );
2376 for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
2378 // start behind the characters (to fix the attributes!)
2379 nStart = rIdx.GetIndex();
2380 if (nStart < pNode->GetText().getLength())
2382 lcl_SkipAttr( pNode, rIdx, nStart );
2384 sal_Unicode c = rStr[ nCnt ];
2385 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2387 bool bMerged(false);
2388 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
2390 SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
2391 SwUndoOverwrite *const pUndoOW(
2392 dynamic_cast<SwUndoOverwrite *>(pUndo) );
2393 if (pUndoOW)
2395 // if CanGrouping() returns true it's already merged
2396 bMerged = pUndoOW->CanGrouping( &m_rDoc, rPt, c );
2399 if (!bMerged)
2401 SwUndo *const pUndoOW( new SwUndoOverwrite(&m_rDoc, rPt, c) );
2402 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoOW);
2405 else
2407 // start behind the characters (to fix the attributes!)
2408 if (nStart < pNode->GetText().getLength())
2409 ++rIdx;
2410 pNode->InsertText( OUString(c), rIdx, SwInsertFlags::EMPTYEXPAND );
2411 if( nStart+1 < rIdx.GetIndex() )
2413 rIdx = nStart;
2414 pNode->EraseText( rIdx, 1 );
2415 ++rIdx;
2419 pNode->SetIgnoreDontExpand( bOldExpFlg );
2421 const size_t nNewAttrCnt = pNode->GetpSwpHints()
2422 ? pNode->GetpSwpHints()->Count() : 0;
2423 if( nOldAttrCnt != nNewAttrCnt )
2425 SwUpdateAttr aHint(0,0,0);
2426 pNode->ModifyBroadcast(nullptr, &aHint);
2429 if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
2430 !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
2432 SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
2433 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, USHRT_MAX );
2435 else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2437 // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2438 // characters are also included in aPam
2439 SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
2440 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
2443 m_rDoc.getIDocumentState().SetModified();
2444 return true;
2447 bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
2448 const SwInsertFlags nInsertMode )
2450 // fetching DoesUndo is surprisingly expensive
2451 bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
2452 if (bDoesUndo)
2453 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2455 const SwPosition& rPos = *rRg.GetPoint();
2457 if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
2459 if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
2461 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
2463 m_rDoc.DeleteAutoCorrExceptWord();
2466 SwTextNode *const pNode = rPos.nNode.GetNode().GetTextNode();
2467 if(!pNode)
2468 return false;
2470 SwDataChanged aTmp( rRg );
2472 if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
2474 OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2475 if (bDoesUndo)
2477 SwUndoInsert * const pUndo( new SwUndoInsert(rPos.nNode,
2478 rPos.nContent.GetIndex(), ins.getLength(), nInsertMode));
2479 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
2482 else
2483 { // if Undo and grouping is enabled, everything changes!
2484 SwUndoInsert * pUndo = nullptr;
2486 // don't group the start if hints at the start should be expanded
2487 if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
2489 SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
2490 SwUndoInsert *const pUndoInsert(
2491 dynamic_cast<SwUndoInsert *>(pLastUndo) );
2492 if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
2494 pUndo = pUndoInsert;
2498 CharClass const& rCC = GetAppCharClass();
2499 sal_Int32 nInsPos = rPos.nContent.GetIndex();
2501 if (!pUndo)
2503 pUndo = new SwUndoInsert( rPos.nNode, nInsPos, 0, nInsertMode,
2504 !rCC.isLetterNumeric( rStr, 0 ) );
2505 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
2508 OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
2510 for (sal_Int32 i = 0; i < ins.getLength(); ++i)
2512 nInsPos++;
2513 // if CanGrouping() returns true, everything has already been done
2514 if (!pUndo->CanGrouping(ins[i]))
2516 pUndo = new SwUndoInsert(rPos.nNode, nInsPos, 1, nInsertMode,
2517 !rCC.isLetterNumeric(ins, i));
2518 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
2523 // To-Do - add 'SwExtraRedlineTable' also ?
2524 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2526 SwPaM aPam( rPos.nNode, aTmp.GetContent(),
2527 rPos.nNode, rPos.nContent.GetIndex());
2528 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2530 m_rDoc.getIDocumentRedlineAccess().AppendRedline(
2531 new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
2533 else
2535 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
2539 m_rDoc.getIDocumentState().SetModified();
2540 return true;
2543 void DocumentContentOperationsManager::TransliterateText(
2544 const SwPaM& rPaM,
2545 utl::TransliterationWrapper& rTrans )
2547 SwUndoTransliterate *const pUndo = (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2548 ? new SwUndoTransliterate( rPaM, rTrans )
2549 : nullptr;
2551 const SwPosition* pStt = rPaM.Start(),
2552 * pEnd = rPaM.End();
2553 sal_uLong nSttNd = pStt->nNode.GetIndex(),
2554 nEndNd = pEnd->nNode.GetIndex();
2555 sal_Int32 nSttCnt = pStt->nContent.GetIndex();
2556 sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
2558 SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
2559 if( pStt == pEnd && pTNd ) // no selection?
2561 // set current word as 'area of effect'
2563 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
2564 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
2565 pTNd->GetText(), nSttCnt,
2566 g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
2567 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
2568 true);
2570 if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
2572 nSttCnt = aBndry.startPos;
2573 nEndCnt = aBndry.endPos;
2577 if( nSttNd != nEndNd ) // is more than one text node involved?
2579 // iterate over all effected text nodes, the first and the last one
2580 // may be incomplete because the selection starts and/or ends there
2582 SwNodeIndex aIdx( pStt->nNode );
2583 if( nSttCnt )
2585 ++aIdx;
2586 if( pTNd )
2587 pTNd->TransliterateText(
2588 rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo);
2591 for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
2593 pTNd = aIdx.GetNode().GetTextNode();
2594 if (pTNd)
2596 pTNd->TransliterateText(
2597 rTrans, 0, pTNd->GetText().getLength(), pUndo);
2601 if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
2602 pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo );
2604 else if( pTNd && nSttCnt < nEndCnt )
2605 pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo );
2607 if( pUndo )
2609 if( pUndo->HasData() )
2611 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
2613 else
2614 delete pUndo;
2616 m_rDoc.getIDocumentState().SetModified();
2619 SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic(
2620 const SwPaM &rRg,
2621 const OUString& rGrfName,
2622 const OUString& rFltName,
2623 const Graphic* pGraphic,
2624 const SfxItemSet* pFlyAttrSet,
2625 const SfxItemSet* pGrfAttrSet,
2626 SwFrameFormat* pFrameFormat )
2628 if( !pFrameFormat )
2629 pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
2630 SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
2631 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
2632 rGrfName, rFltName, pGraphic,
2633 m_rDoc.GetDfltGrfFormatColl() );
2634 SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
2635 pFlyAttrSet, pGrfAttrSet, pFrameFormat );
2636 return pSwFlyFrameFormat;
2639 SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphicObject(
2640 const SwPaM &rRg, const GraphicObject& rGrfObj,
2641 const SfxItemSet* pFlyAttrSet,
2642 const SfxItemSet* pGrfAttrSet,
2643 SwFrameFormat* pFrameFormat )
2645 if( !pFrameFormat )
2646 pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
2647 SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
2648 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
2649 rGrfObj, m_rDoc.GetDfltGrfFormatColl() );
2650 SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
2651 pFlyAttrSet, pGrfAttrSet, pFrameFormat );
2652 return pSwFlyFrameFormat;
2655 SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject(
2656 const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
2657 const SfxItemSet* pFlyAttrSet)
2659 sal_uInt16 nId = RES_POOLFRM_OLE;
2660 if (xObj.is())
2662 SvGlobalName aClassName( xObj->getClassID() );
2663 if (SotExchange::IsMath(aClassName))
2664 nId = RES_POOLFRM_FORMEL;
2667 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId );
2669 return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
2670 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
2671 xObj,
2672 m_rDoc.GetDfltGrfFormatColl() ),
2673 pFlyAttrSet, nullptr,
2674 pFrameFormat );
2677 SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName,
2678 sal_Int64 nAspect,
2679 const SfxItemSet* pFlyAttrSet,
2680 const SfxItemSet* pGrfAttrSet)
2682 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE );
2684 return InsNoTextNode( *rRg.GetPoint(),
2685 m_rDoc.GetNodes().MakeOLENode(
2686 SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
2687 rObjName,
2688 nAspect,
2689 m_rDoc.GetDfltGrfFormatColl(),
2690 nullptr ),
2691 pFlyAttrSet, pGrfAttrSet,
2692 pFrameFormat );
2695 void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
2696 const OUString& rFltName, const Graphic* pGraphic,
2697 const GraphicObject* pGrafObj )
2699 SwGrfNode *pGrfNd;
2700 if( ( !rPam.HasMark()
2701 || rPam.GetPoint()->nNode.GetIndex() == rPam.GetMark()->nNode.GetIndex() )
2702 && nullptr != ( pGrfNd = rPam.GetPoint()->nNode.GetNode().GetGrfNode() ) )
2704 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2706 m_rDoc.GetIDocumentUndoRedo().AppendUndo(new SwUndoReRead(rPam, *pGrfNd));
2709 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
2710 if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
2711 GetMirrorGrf().GetValue() )
2712 pGrfNd->SetAttr( SwMirrorGrf() );
2714 pGrfNd->ReRead( rGrfName, rFltName, pGraphic, pGrafObj );
2715 m_rDoc.getIDocumentState().SetModified();
2719 // Insert drawing object, which has to be already inserted in the DrawModel
2720 SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj(
2721 const SwPaM &rRg,
2722 SdrObject& rDrawObj,
2723 const SfxItemSet& rFlyAttrSet )
2725 SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
2727 const SwFormatAnchor* pAnchor = nullptr;
2728 rFlyAttrSet.GetItemState( RES_ANCHOR, false, reinterpret_cast<const SfxPoolItem**>(&pAnchor) );
2729 pFormat->SetFormatAttr( rFlyAttrSet );
2731 // Didn't set the Anchor yet?
2732 // DrawObjecte must never end up in the Header/Footer!
2733 RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
2734 const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
2736 const SwNodeIndex* pChkIdx = nullptr;
2737 if ( pAnchor == nullptr )
2739 pChkIdx = &rRg.GetPoint()->nNode;
2741 else if ( bIsAtContent )
2743 pChkIdx =
2744 pAnchor->GetContentAnchor() ? &pAnchor->GetContentAnchor()->nNode : &rRg.GetPoint()->nNode;
2747 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
2748 if( pChkIdx != nullptr
2749 && ::CheckControlLayer( &rDrawObj )
2750 && m_rDoc.IsInHeaderFooter( *pChkIdx ) )
2752 // apply at-page anchor format
2753 eAnchorId = RndStdIds::FLY_AT_PAGE;
2754 pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
2756 else if( pAnchor == nullptr
2757 || ( bIsAtContent
2758 && pAnchor->GetContentAnchor() == nullptr ) )
2760 // apply anchor format
2761 SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
2762 eAnchorId = aAnch.GetAnchorId();
2763 if ( eAnchorId == RndStdIds::FLY_AT_FLY )
2765 SwPosition aPos( *rRg.GetNode().FindFlyStartNode() );
2766 aAnch.SetAnchor( &aPos );
2768 else
2770 aAnch.SetAnchor( rRg.GetPoint() );
2771 if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
2773 eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
2774 aAnch.SetType( eAnchorId );
2777 pFormat->SetFormatAttr( aAnch );
2780 // insert text attribute for as-character anchored drawing object
2781 if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
2783 bool bAnchorAtPageAsFallback = true;
2784 const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
2785 if ( rDrawObjAnchorFormat.GetContentAnchor() != nullptr )
2787 SwTextNode* pAnchorTextNode =
2788 rDrawObjAnchorFormat.GetContentAnchor()->nNode.GetNode().GetTextNode();
2789 if ( pAnchorTextNode != nullptr )
2791 const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->nContent.GetIndex();
2792 SwFormatFlyCnt aFormat( pFormat );
2793 pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
2794 bAnchorAtPageAsFallback = false;
2798 if ( bAnchorAtPageAsFallback )
2800 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
2801 pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
2805 SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
2807 // Create Frames if necessary
2808 if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
2810 // create layout representation
2811 pFormat->MakeFrames();
2812 // #i42319# - follow-up of #i35635#
2813 // move object to visible layer
2814 // #i79391#
2815 if ( pContact->GetAnchorFrame() )
2817 pContact->MoveObjToVisibleLayer( &rDrawObj );
2821 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2823 m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoInsLayFormat(pFormat, 0, 0) );
2826 m_rDoc.getIDocumentState().SetModified();
2827 return pFormat;
2830 bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
2832 SwContentNode *pNode = rPos.nNode.GetNode().GetContentNode();
2833 if(nullptr == pNode)
2834 return false;
2837 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
2838 // After that they can be before/after the position.
2839 SwDataChanged aTmp( &m_rDoc, rPos );
2842 SwUndoSplitNode* pUndo = nullptr;
2843 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2845 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
2846 // insert the Undo object (currently only for TextNode)
2847 if( pNode->IsTextNode() )
2849 pUndo = new SwUndoSplitNode( &m_rDoc, rPos, bChkTableStart );
2850 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndo);
2854 // Update the rsid of the old and the new node unless
2855 // the old node is split at the beginning or at the end
2856 SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
2857 const sal_Int32 nPos = rPos.nContent.GetIndex();
2858 if( pTextNode && nPos && nPos != pTextNode->Len() )
2860 m_rDoc.UpdateParRsid( pTextNode );
2863 //JP 28.01.97: Special case for SplitNode at table start:
2864 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
2865 // then insert a paragraph before it.
2866 if( bChkTableStart && !rPos.nContent.GetIndex() && pNode->IsTextNode() )
2868 sal_uLong nPrevPos = rPos.nNode.GetIndex() - 1;
2869 const SwTableNode* pTableNd;
2870 const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
2871 if( pNd->IsStartNode() &&
2872 SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
2873 nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
2874 ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
2875 SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
2876 || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
2877 || pNd->IsContentNode() ))
2879 if( pNd->IsContentNode() )
2881 //JP 30.04.99 Bug 65660:
2882 // There are no page breaks outside of the normal body area,
2883 // so this is not a valid condition to insert a paragraph.
2884 if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
2885 pNd = nullptr;
2886 else
2888 // Only if the table has page breaks!
2889 const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
2890 if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
2891 SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
2892 pNd = nullptr;
2896 if( pNd )
2898 SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
2899 SwNodeIndex( *pTableNd ),
2900 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
2901 if( pTextNd )
2903 const_cast<SwPosition&>(rPos).nNode = pTableNd->GetIndex()-1;
2904 const_cast<SwPosition&>(rPos).nContent.Assign( pTextNd, 0 );
2906 // only add page breaks/styles to the body area
2907 if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
2909 SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
2910 const SfxPoolItem *pItem;
2911 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
2912 false, &pItem ) )
2914 pTextNd->SetAttr( *pItem );
2915 pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
2917 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
2918 false, &pItem ) )
2920 pTextNd->SetAttr( *pItem );
2921 pFrameFormat->ResetFormatAttr( RES_BREAK );
2925 if( pUndo )
2926 pUndo->SetTableFlag();
2927 m_rDoc.getIDocumentState().SetModified();
2928 return true;
2934 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
2935 pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
2936 // FIXME: only SwTextNode has a valid implementation of SplitContentNode!
2937 OSL_ENSURE(pNode->IsTextNode(), "splitting non-text node?");
2938 pNode = pNode->SplitContentNode( rPos );
2939 if (pNode)
2941 // move all bookmarks, TOXMarks, FlyAtCnt
2942 if( !pContentStore->Empty() )
2943 pContentStore->Restore( &m_rDoc, rPos.nNode.GetIndex()-1, 0, true );
2945 // To-Do - add 'SwExtraRedlineTable' also ?
2946 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2948 SwPaM aPam( rPos );
2949 aPam.SetMark();
2950 aPam.Move( fnMoveBackward );
2951 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2952 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
2953 else
2954 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
2958 m_rDoc.getIDocumentState().SetModified();
2959 return true;
2962 bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos )
2964 // create new node before EndOfContent
2965 SwTextNode * pCurNode = rPos.nNode.GetNode().GetTextNode();
2966 if( !pCurNode )
2968 // so then one can be created!
2969 SwNodeIndex aIdx( rPos.nNode, 1 );
2970 pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx,
2971 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
2973 else
2974 pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
2976 rPos.nNode++;
2977 rPos.nContent.Assign( pCurNode, 0 );
2979 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2981 m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoInsert( rPos.nNode ) );
2984 // To-Do - add 'SwExtraRedlineTable' also ?
2985 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2987 SwPaM aPam( rPos );
2988 aPam.SetMark();
2989 aPam.Move( fnMoveBackward );
2990 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
2991 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
2992 else
2993 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
2996 m_rDoc.getIDocumentState().SetModified();
2997 return true;
3000 bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
3001 const bool bRegExReplace )
3003 // unfortunately replace works slightly differently from delete,
3004 // so we cannot use lcl_DoWithBreaks here...
3006 std::vector<sal_Int32> Breaks;
3008 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
3009 aPam.Normalize(false);
3010 if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
3012 aPam.Move(fnMoveBackward);
3014 OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
3016 lcl_CalcBreaks(Breaks, aPam);
3018 while (!Breaks.empty() // skip over prefix of dummy chars
3019 && (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
3021 // skip!
3022 ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
3023 Breaks.erase(Breaks.begin());
3025 *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
3027 if (Breaks.empty())
3029 // park aPam somewhere so it does not point to node that is deleted
3030 aPam.DeleteMark();
3031 *aPam.GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
3032 return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
3035 // Deletion must be split into several parts if the text node
3036 // contains a text attribute with end and with dummy character
3037 // and the selection does not contain the text attribute completely,
3038 // but overlaps its start (left), where the dummy character is.
3040 bool bRet( true );
3041 // iterate from end to start, to avoid invalidating the offsets!
3042 std::vector<sal_Int32>::reverse_iterator iter( Breaks.rbegin() );
3043 OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
3044 SwPosition & rEnd( *aPam.End() );
3045 SwPosition & rStart( *aPam.Start() );
3047 // set end of temp pam to original end (undo Move backward above)
3048 rEnd = *rPam.End();
3049 // after first deletion, rEnd will point into the original text node again!
3051 while (iter != Breaks.rend())
3053 rStart.nContent = *iter + 1;
3054 if (rEnd.nContent != rStart.nContent) // check if part is empty
3056 bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
3057 ? DeleteAndJoinWithRedlineImpl(aPam)
3058 : DeleteAndJoinImpl(aPam, false);
3060 rEnd.nContent = *iter;
3061 ++iter;
3064 rStart = *rPam.Start(); // set to original start
3065 OSL_ENSURE(rEnd.nContent > rStart.nContent, "replace part empty!");
3066 if (rEnd.nContent > rStart.nContent) // check if part is empty
3068 bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
3071 rPam = aPam; // update original pam (is this required?)
3073 return bRet;
3076 ///Add a para for the char attribute exp...
3077 bool DocumentContentOperationsManager::InsertPoolItem(
3078 const SwPaM &rRg,
3079 const SfxPoolItem &rHt,
3080 const SetAttrMode nFlags,
3081 const bool bExpandCharToPara)
3083 if (utl::ConfigManager::IsFuzzing())
3084 return false;
3086 SwDataChanged aTmp( rRg );
3087 SwUndoAttr* pUndoAttr = nullptr;
3088 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3090 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3091 pUndoAttr = new SwUndoAttr( rRg, rHt, nFlags );
3094 SfxItemSet aSet( m_rDoc.GetAttrPool(), {{rHt.Which(), rHt.Which()}} );
3095 aSet.Put( rHt );
3096 const bool bRet = lcl_InsAttr( &m_rDoc, rRg, aSet, nFlags, pUndoAttr, bExpandCharToPara );
3098 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3100 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoAttr );
3103 if( bRet )
3105 m_rDoc.getIDocumentState().SetModified();
3107 return bRet;
3110 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet,
3111 const SetAttrMode nFlags )
3113 SwDataChanged aTmp( rRg );
3114 SwUndoAttr* pUndoAttr = nullptr;
3115 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3117 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3118 pUndoAttr = new SwUndoAttr( rRg, rSet, nFlags );
3121 bool bRet = lcl_InsAttr( &m_rDoc, rRg, rSet, nFlags, pUndoAttr );
3123 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3125 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndoAttr );
3128 if( bRet )
3129 m_rDoc.getIDocumentState().SetModified();
3132 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos )
3134 const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
3135 if ( pTNd )
3137 const OUString& rText = pTNd->GetText();
3138 sal_Int32 nIdx = 0;
3139 while (nIdx < rText.getLength())
3141 sal_Unicode const cCh = rText[nIdx];
3142 if (('\t' != cCh) && (' ' != cCh))
3144 break;
3146 ++nIdx;
3149 if ( nIdx > 0 )
3151 SwPaM aPam(rPos);
3152 aPam.GetPoint()->nContent = 0;
3153 aPam.SetMark();
3154 aPam.GetMark()->nContent = nIdx;
3155 DeleteRange( aPam );
3160 // Copy method from SwDoc - "copy Flys in Flys"
3161 void DocumentContentOperationsManager::CopyWithFlyInFly(
3162 const SwNodeRange& rRg,
3163 const sal_Int32 nEndContentIndex,
3164 const SwNodeIndex& rInsPos,
3165 const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
3166 const bool bMakeNewFrames,
3167 const bool bDelRedlines,
3168 const bool bCopyFlyAtFly ) const
3170 assert(!pCopiedPaM || pCopiedPaM->first.End()->nContent == nEndContentIndex);
3171 assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
3173 SwDoc* pDest = rInsPos.GetNode().GetDoc();
3175 SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
3177 SwNodeIndex aSavePos( rInsPos, -1 );
3178 bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
3179 m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, bMakeNewFrames, true );
3180 ++aSavePos;
3181 if( bEndIsEqualEndPos )
3182 const_cast<SwNodeIndex&>(rRg.aEnd) = aSavePos;
3184 aRedlRest.Restore();
3186 #if OSL_DEBUG_LEVEL > 0
3188 //JP 17.06.99: Bug 66973 - check count only if the selection is in
3189 // the same section or there's no section, because sections that are
3190 // not fully selected are not copied.
3191 const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
3192 SwNodeIndex aTmpI( rRg.aEnd, -1 );
3193 const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
3194 if( pSSectNd == pESectNd &&
3195 !rRg.aStart.GetNode().IsSectionNode() &&
3196 !aTmpI.GetNode().IsEndNode() )
3198 // If the range starts with a SwStartNode, it isn't copied
3199 sal_uInt16 offset = (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0;
3200 OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
3201 rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
3202 "An insufficient number of nodes were copied!" );
3205 #endif
3208 ::sw::UndoGuard const undoGuard(pDest->GetIDocumentUndoRedo());
3209 CopyFlyInFlyImpl( rRg, nEndContentIndex, aSavePos, bCopyFlyAtFly );
3212 SwNodeRange aCpyRange( aSavePos, rInsPos );
3214 // Also copy all bookmarks
3215 // guess this must be done before the DelDummyNodes below as that
3216 // deletes nodes so would mess up the index arithmetic
3217 if( m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
3219 SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
3220 SwPaM aCpyPaM(aCpyRange.aStart, aCpyRange.aEnd);
3221 if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
3223 // there is 1 (partially selected, maybe) paragraph before
3224 assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->nNode);
3225 // only use the passed in target SwPosition if the source PaM point
3226 // is on a different node; if it was the same node then the target
3227 // position was likely moved along by the copy operation and now
3228 // points to the end of the range!
3229 *aCpyPaM.GetPoint() = pCopiedPaM->second;
3232 lcl_CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, aCpyPaM);
3235 if( bDelRedlines && ( RedlineFlags::DeleteRedlines & pDest->getIDocumentRedlineAccess().GetRedlineFlags() ))
3236 lcl_DeleteRedlines( rRg, aCpyRange );
3238 pDest->GetNodes().DelDummyNodes( aCpyRange );
3241 // TODO: there is a limitation here in that it's not possible to pass a start
3242 // content index - which means that at-character anchored frames inside
3243 // partial 1st paragraph of redline is not copied.
3244 // But the DelFlyInRange() that is called from DelCopyOfSection() does not
3245 // delete it either, and it also does not delete those on partial last para of
3246 // redline, so copying those is suppressed here too ...
3247 void DocumentContentOperationsManager::CopyFlyInFlyImpl(
3248 const SwNodeRange& rRg,
3249 const sal_Int32 nEndContentIndex,
3250 const SwNodeIndex& rStartIdx,
3251 const bool bCopyFlyAtFly ) const
3253 // First collect all Flys, sort them according to their ordering number,
3254 // and then only copy them. This maintains the ordering numbers (which are only
3255 // managed in the DrawModel).
3256 SwDoc *const pDest = rStartIdx.GetNode().GetDoc();
3257 std::set< ZSortFly > aSet;
3258 const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
3260 SwTextBoxHelper::SavedLink aOldTextBoxes;
3261 SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes);
3262 SwTextBoxHelper::SavedContent aOldContent;
3264 for ( size_t n = 0; n < nArrLen; ++n )
3266 SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
3267 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
3268 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
3269 bool bAtContent = (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA);
3270 if ( !pAPos )
3271 continue;
3272 sal_uLong nSkipAfter = pAPos->nNode.GetIndex();
3273 sal_uLong nStart = rRg.aStart.GetIndex();
3274 switch ( pAnchor->GetAnchorId() )
3276 case RndStdIds::FLY_AT_FLY:
3277 if(bCopyFlyAtFly)
3278 ++nSkipAfter;
3279 else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
3280 ++nStart;
3281 break;
3282 case RndStdIds::FLY_AT_CHAR:
3283 case RndStdIds::FLY_AT_PARA:
3284 if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
3285 ++nStart;
3286 break;
3287 default:
3288 continue;
3290 if ( nStart > nSkipAfter )
3291 continue;
3292 if ( pAPos->nNode > rRg.aEnd )
3293 continue;
3294 //frames at the last source node are not always copied:
3295 //- if the node is empty and is the last node of the document or a table cell
3296 // or a text frame then they have to be copied
3297 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
3298 //- to-character bound objects are copied if their index is <= nEndContentIndex
3299 bool bAdd = false;
3300 if( pAPos->nNode < rRg.aEnd )
3301 bAdd = true;
3302 if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
3304 bool bEmptyNode = false;
3305 bool bLastNode = false;
3306 // is the node empty?
3307 const SwNodes& rNodes = pAPos->nNode.GetNodes();
3308 SwTextNode* pTextNode;
3309 if( nullptr != ( pTextNode = pAPos->nNode.GetNode().GetTextNode() ))
3311 bEmptyNode = pTextNode->GetText().isEmpty();
3312 if( bEmptyNode )
3314 //last node information is only necessary to know for the last TextNode
3315 SwNodeIndex aTmp( pAPos->nNode );
3316 ++aTmp;//goto next node
3317 while (aTmp.GetNode().IsEndNode())
3319 if( aTmp == rNodes.GetEndOfContent().GetIndex() )
3321 bLastNode = true;
3322 break;
3324 ++aTmp;
3328 bAdd = bLastNode && bEmptyNode;
3329 if( !bAdd )
3331 if( bAtContent )
3332 bAdd = nEndContentIndex > 0;
3333 else
3334 bAdd = pAPos->nContent <= nEndContentIndex;
3337 if( bAdd )
3339 // Make sure draw formats don't refer to content, so that such
3340 // content can be removed without problems.
3341 SwTextBoxHelper::resetLink(pFormat, aOldContent);
3342 aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
3346 // Store all copied (and also the newly created) frames in another array.
3347 // They are stored as matching the originals, so that we will be later
3348 // able to build the chains accordingly.
3349 std::vector< SwFrameFormat* > aVecSwFrameFormat;
3350 std::set< ZSortFly >::const_iterator it=aSet.begin();
3352 while (it != aSet.end())
3354 // #i59964#
3355 // correct determination of new anchor position
3356 SwFormatAnchor aAnchor( *(*it).GetAnchor() );
3357 assert( aAnchor.GetContentAnchor() != nullptr );
3358 SwPosition newPos = *aAnchor.GetContentAnchor();
3359 // for at-paragraph and at-character anchored objects the new anchor
3360 // position can *not* be determined by the difference of the current
3361 // anchor position to the start of the copied range, because not
3362 // complete selected sections in the copied range aren't copied - see
3363 // method <SwNodes::CopyNodes(..)>.
3364 // Thus, the new anchor position in the destination document is found
3365 // by counting the text nodes.
3366 if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
3367 (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
3369 // First, determine number of anchor text node in the copied range.
3370 // Note: The anchor text node *have* to be inside the copied range.
3371 sal_uLong nAnchorTextNdNumInRange( 0 );
3372 bool bAnchorTextNdFound( false );
3373 SwNodeIndex aIdx( rRg.aStart );
3374 while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
3376 if ( aIdx.GetNode().IsTextNode() )
3378 ++nAnchorTextNdNumInRange;
3379 bAnchorTextNdFound = aAnchor.GetContentAnchor()->nNode == aIdx;
3382 ++aIdx;
3385 if ( !bAnchorTextNdFound )
3387 // This case can *not* happen, but to be robust take the first
3388 // text node in the destination document.
3389 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
3390 nAnchorTextNdNumInRange = 1;
3392 // Second, search corresponding text node in destination document
3393 // by counting forward from start insert position <rStartIdx> the
3394 // determined number of text nodes.
3395 aIdx = rStartIdx;
3396 SwNodeIndex aAnchorNdIdx( rStartIdx );
3397 const SwNode& aEndOfContentNd =
3398 aIdx.GetNode().GetNodes().GetEndOfContent();
3399 while ( nAnchorTextNdNumInRange > 0 &&
3400 &(aIdx.GetNode()) != &aEndOfContentNd )
3402 if ( aIdx.GetNode().IsTextNode() )
3404 --nAnchorTextNdNumInRange;
3405 aAnchorNdIdx = aIdx;
3408 ++aIdx;
3410 if ( !aAnchorNdIdx.GetNode().IsTextNode() )
3412 // This case can *not* happen, but to be robust take the first
3413 // text node in the destination document.
3414 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
3415 aAnchorNdIdx = rStartIdx;
3416 while ( !aAnchorNdIdx.GetNode().IsTextNode() )
3418 ++aAnchorNdIdx;
3421 // apply found anchor text node as new anchor position
3422 newPos.nNode = aAnchorNdIdx;
3424 else
3426 long nOffset = newPos.nNode.GetIndex() - rRg.aStart.GetIndex();
3427 SwNodeIndex aIdx( rStartIdx, nOffset );
3428 newPos.nNode = aIdx;
3430 // Set the character bound Flys back at the original character
3431 if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
3432 newPos.nNode.GetNode().IsTextNode() )
3434 newPos.nContent.Assign( newPos.nNode.GetNode().GetTextNode(), newPos.nContent.GetIndex() );
3436 else
3438 newPos.nContent.Assign( nullptr, 0 );
3440 aAnchor.SetAnchor( &newPos );
3442 // Check recursion: copy content in its own frame, then don't copy it.
3443 if( pDest == &m_rDoc )
3445 const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
3446 const SwStartNode* pSNd;
3447 if( rContent.GetContentIdx() &&
3448 nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
3449 pSNd->GetIndex() < rStartIdx.GetIndex() &&
3450 rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
3452 it = aSet.erase(it);
3453 continue;
3457 // Copy the format and set the new anchor
3458 aVecSwFrameFormat.push_back( pDest->getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
3459 aAnchor, false, true ) );
3460 ++it;
3463 // Rebuild as much as possible of all chains that are available in the original,
3464 OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
3465 if ( aSet.size() == aVecSwFrameFormat.size() )
3467 size_t n = 0;
3468 for (std::set< ZSortFly >::const_iterator nIt=aSet.begin() ; nIt != aSet.end(); ++nIt, ++n )
3470 const SwFrameFormat *pFormatN = (*nIt).GetFormat();
3471 const SwFormatChain &rChain = pFormatN->GetChain();
3472 int nCnt = int(nullptr != rChain.GetPrev());
3473 nCnt += rChain.GetNext() ? 1: 0;
3474 size_t k = 0;
3475 for (std::set< ZSortFly >::const_iterator kIt=aSet.begin() ; kIt != aSet.end(); ++kIt, ++k )
3477 const SwFrameFormat *pFormatK = (*kIt).GetFormat();
3478 if ( rChain.GetPrev() == pFormatK )
3480 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
3481 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
3482 --nCnt;
3484 else if ( rChain.GetNext() == pFormatK )
3486 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
3487 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
3488 --nCnt;
3493 // Re-create content property of draw formats, knowing how old shapes
3494 // were paired with old fly formats (aOldTextBoxes) and that aSet is
3495 // parallel with aVecSwFrameFormat.
3496 SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes, aOldContent);
3501 * Reset the text's hard formatting
3503 /** @params pArgs contains the document's ChrFormatTable
3504 * Is need for selections at the beginning/end and with no SSelection.
3506 bool DocumentContentOperationsManager::lcl_RstTextAttr( const SwNodePtr& rpNd, void* pArgs )
3508 ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
3509 SwTextNode * pTextNode = rpNd->GetTextNode();
3510 if( pTextNode && pTextNode->GetpSwpHints() )
3512 SwIndex aSt( pTextNode, 0 );
3513 sal_Int32 nEnd = pTextNode->Len();
3515 if( &pPara->pSttNd->nNode.GetNode() == pTextNode &&
3516 pPara->pSttNd->nContent.GetIndex() )
3517 aSt = pPara->pSttNd->nContent.GetIndex();
3519 if( &pPara->pEndNd->nNode.GetNode() == rpNd )
3520 nEnd = pPara->pEndNd->nContent.GetIndex();
3522 if( pPara->pHistory )
3524 // Save all attributes for the Undo.
3525 SwRegHistory aRHst( *pTextNode, pPara->pHistory );
3526 pTextNode->GetpSwpHints()->Register( &aRHst );
3527 pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
3528 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
3529 if( pTextNode->GetpSwpHints() )
3530 pTextNode->GetpSwpHints()->DeRegister();
3532 else
3533 pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
3534 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
3536 return true;
3539 DocumentContentOperationsManager::~DocumentContentOperationsManager()
3542 //Private methods
3544 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
3546 OSL_ENSURE( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn(), "DeleteAndJoinWithRedline: redline off" );
3549 SwUndoRedlineDelete* pUndo = nullptr;
3550 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
3551 m_rDoc.GetDocumentRedlineManager().checkRedlining( eOld );
3553 auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
3554 std::vector<std::unique_ptr<SwUndo>> MarkUndos;
3555 for (auto iter = rDMA.getAnnotationMarksBegin();
3556 iter != rDMA.getAnnotationMarksEnd(); )
3558 // tdf#111524 remove annotation marks that have their field
3559 // characters deleted
3560 SwPosition const& rEndPos((**iter).GetMarkEnd());
3561 if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
3563 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3565 MarkUndos.emplace_back(o3tl::make_unique<SwUndoDeleteBookmark>(**iter));
3567 // iter is into annotation mark vector so must be dereferenced!
3568 rDMA.deleteMark(&**iter);
3569 // this invalidates iter, have to start over...
3570 iter = rDMA.getAnnotationMarksBegin();
3572 else
3573 { // marks are sorted by start
3574 if (*rPam.End() < (**iter).GetMarkStart())
3576 break;
3578 ++iter;
3582 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3585 /* please don't translate -- for cultural reasons this comment is protected
3586 until the redline implementation is finally fixed some day */
3587 //JP 06.01.98: MUSS noch optimiert werden!!!
3588 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
3589 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
3590 pUndo = new SwUndoRedlineDelete( rPam, SwUndoId::DELETE );
3591 const SwRewriter aRewriter = pUndo->GetRewriter();
3592 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::DELETE, &aRewriter );
3593 for (auto& it : MarkUndos)
3595 m_rDoc.GetIDocumentUndoRedo().AppendUndo(it.release());
3597 m_rDoc.GetIDocumentUndoRedo().AppendUndo( pUndo );
3600 if ( *rPam.GetPoint() != *rPam.GetMark() )
3601 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_DELETE, rPam ), true );
3602 m_rDoc.getIDocumentState().SetModified();
3604 if ( pUndo )
3606 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::EMPTY, nullptr );
3607 // ??? why the hell is the AppendUndo not below the
3608 // CanGrouping, so this hideous cleanup wouldn't be necessary?
3609 // bah, this is redlining, probably changing this would break it...
3610 if ( m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo() )
3612 SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
3613 SwUndoRedlineDelete * const pUndoRedlineDel( dynamic_cast< SwUndoRedlineDelete* >( pLastUndo ) );
3614 if ( pUndoRedlineDel )
3616 bool const bMerged = pUndoRedlineDel->CanGrouping( *pUndo );
3617 if ( bMerged )
3619 ::sw::UndoGuard const undoGuard( m_rDoc.GetIDocumentUndoRedo() );
3620 SwUndo const* const pDeleted = m_rDoc.GetUndoManager().RemoveLastUndo();
3621 OSL_ENSURE( pDeleted == pUndo, "DeleteAndJoinWithRedlineImpl: "
3622 "undo removed is not undo inserted?" );
3623 delete pDeleted;
3627 //JP 06.01.98: MUSS noch optimiert werden!!!
3628 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
3630 return true;
3634 bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM & rPam,
3635 const bool bForceJoinNext )
3637 bool bJoinText, bJoinPrev;
3638 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
3639 // #i100466#
3640 if ( bForceJoinNext )
3642 bJoinPrev = false;
3646 bool const bSuccess( DeleteRangeImpl( rPam ) );
3647 if (!bSuccess)
3648 return false;
3651 if( bJoinText )
3653 ::sw_JoinText( rPam, bJoinPrev );
3656 return true;
3659 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, const bool)
3661 // Move all cursors out of the deleted range, but first copy the
3662 // passed PaM, because it could be a cursor that would be moved!
3663 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
3664 ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
3666 bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
3667 if (bSuccess)
3668 { // now copy position from temp copy to given PaM
3669 *rPam.GetPoint() = *aDelPam.GetPoint();
3672 return bSuccess;
3675 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam)
3677 SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
3679 if( !rPam.HasMark() || *pStt >= *pEnd )
3680 return false;
3682 if( m_rDoc.GetAutoCorrExceptWord() )
3684 // if necessary the saved Word for the exception
3685 if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->nNode != pEnd->nNode ||
3686 pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
3687 !m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt ))
3688 { m_rDoc.DeleteAutoCorrExceptWord(); }
3692 // Delete all empty TextHints at the Mark's position
3693 SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
3694 SwpHints* pHts;
3695 if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
3697 const sal_Int32 nMkCntPos = rPam.GetMark()->nContent.GetIndex();
3698 for( size_t n = pHts->Count(); n; )
3700 const SwTextAttr* pAttr = pHts->Get( --n );
3701 if( nMkCntPos > pAttr->GetStart() )
3702 break;
3704 const sal_Int32 *pEndIdx;
3705 if( nMkCntPos == pAttr->GetStart() &&
3706 nullptr != (pEndIdx = pAttr->End()) &&
3707 *pEndIdx == pAttr->GetStart() )
3708 pTextNd->DestroyAttr( pHts->Cut( n ) );
3714 // Send DataChanged before deletion, so that we still know
3715 // which objects are in the range.
3716 // Afterwards they could be before/after the Position.
3717 SwDataChanged aTmp( rPam );
3720 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3722 m_rDoc.GetIDocumentUndoRedo().ClearRedo();
3723 bool bMerged(false);
3724 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
3726 SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
3727 SwUndoDelete *const pUndoDelete(
3728 dynamic_cast<SwUndoDelete *>(pLastUndo) );
3729 if (pUndoDelete)
3731 bMerged = pUndoDelete->CanGrouping( &m_rDoc, rPam );
3732 // if CanGrouping() returns true it's already merged
3735 if (!bMerged)
3737 m_rDoc.GetIDocumentUndoRedo().AppendUndo( new SwUndoDelete( rPam ) );
3740 m_rDoc.getIDocumentState().SetModified();
3742 return true;
3745 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
3746 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, USHRT_MAX );
3748 // Delete and move all "Flys at the paragraph", which are within the Selection
3749 DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
3750 DelBookmarks(
3751 pStt->nNode,
3752 pEnd->nNode,
3753 nullptr,
3754 &pStt->nContent,
3755 &pEnd->nContent);
3757 SwNodeIndex aSttIdx( pStt->nNode );
3758 SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
3760 do { // middle checked loop!
3761 if( pCNd )
3763 SwTextNode * pStartTextNode( pCNd->GetTextNode() );
3764 if ( pStartTextNode )
3766 // now move the Content to the new Node
3767 bool bOneNd = pStt->nNode == pEnd->nNode;
3768 const sal_Int32 nLen = ( bOneNd ? pEnd->nContent.GetIndex()
3769 : pCNd->Len() )
3770 - pStt->nContent.GetIndex();
3772 // Don't call again, if already empty
3773 if( nLen )
3775 pStartTextNode->EraseText( pStt->nContent, nLen );
3777 if( !pStartTextNode->Len() )
3779 // METADATA: remove reference if empty (consider node deleted)
3780 pStartTextNode->RemoveMetadataReference();
3784 if( bOneNd ) // that's it
3785 break;
3787 ++aSttIdx;
3789 else
3791 // So that there are no indices left registered when deleted,
3792 // we remove a SwPaM from the Content here.
3793 pStt->nContent.Assign( nullptr, 0 );
3797 pCNd = pEnd->nNode.GetNode().GetContentNode();
3798 if( pCNd )
3800 SwTextNode * pEndTextNode( pCNd->GetTextNode() );
3801 if( pEndTextNode )
3803 // if already empty, don't call again
3804 if( pEnd->nContent.GetIndex() )
3806 SwIndex aIdx( pCNd, 0 );
3807 pEndTextNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
3809 if( !pEndTextNode->Len() )
3811 // METADATA: remove reference if empty (consider node deleted)
3812 pEndTextNode->RemoveMetadataReference();
3816 else
3818 // So that there are no indices left registered when deleted,
3819 // we remove a SwPaM from the Content here.
3820 pEnd->nContent.Assign( nullptr, 0 );
3824 // if the end is not a content node, delete it as well
3825 sal_uInt32 nEnde = pEnd->nNode.GetIndex();
3826 if( pCNd == nullptr )
3827 nEnde++;
3829 if( aSttIdx != nEnde )
3831 // delete the Nodes into the NodesArary
3832 m_rDoc.GetNodes().Delete( aSttIdx, nEnde - aSttIdx.GetIndex() );
3835 // If the Node that contained the Cursor has been deleted,
3836 // the Content has to be assigned to the current Content.
3837 pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
3838 pStt->nContent.GetIndex() );
3840 // If we deleted across Node boundaries we have to correct the PaM,
3841 // because they are in different Nodes now.
3842 // Also, the Selection is revoked.
3843 *pEnd = *pStt;
3844 rPam.DeleteMark();
3846 } while( false );
3848 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
3849 m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
3850 m_rDoc.getIDocumentState().SetModified();
3852 return true;
3855 // It's possible to call Replace with a PaM that spans 2 paragraphs:
3856 // search with regex for "$", then replace _all_
3857 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr,
3858 const bool bRegExReplace )
3860 if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() )
3861 return false;
3863 bool bJoinText, bJoinPrev;
3864 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
3867 // Create a copy of the Cursor in order to move all Pams from
3868 // the other views out of the deletion range.
3869 // Except for itself!
3870 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
3871 ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
3873 SwPosition *pStt = aDelPam.Start(),
3874 *pEnd = aDelPam.End();
3875 OSL_ENSURE( pStt->nNode == pEnd->nNode ||
3876 ( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
3877 !pEnd->nContent.GetIndex() ),
3878 "invalid range: Point and Mark on different nodes" );
3879 bool bOneNode = pStt->nNode == pEnd->nNode;
3881 // Own Undo?
3882 OUString sRepl( rStr );
3883 SwTextNode* pTextNd = pStt->nNode.GetNode().GetTextNode();
3884 sal_Int32 nStt = pStt->nContent.GetIndex();
3885 sal_Int32 nEnd;
3887 SwDataChanged aTmp( aDelPam );
3889 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
3891 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
3892 m_rDoc.GetDocumentRedlineManager().checkRedlining(eOld);
3893 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3895 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
3897 // If any Redline will change (split!) the node
3898 const ::sw::mark::IMark* pBkmk =
3899 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
3900 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
3901 ::sw::mark::InsertMode::New);
3903 //JP 06.01.98: MUSS noch optimiert werden!!!
3904 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
3905 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
3907 *aDelPam.GetPoint() = pBkmk->GetMarkPos();
3908 if(pBkmk->IsExpanded())
3909 *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
3910 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
3911 pStt = aDelPam.Start();
3912 pTextNd = pStt->nNode.GetNode().GetTextNode();
3913 nStt = pStt->nContent.GetIndex();
3916 if( !sRepl.isEmpty() )
3918 // Apply the first character's attributes to the ReplaceText
3919 SfxItemSet aSet( m_rDoc.GetAttrPool(),
3920 svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
3921 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>{} );
3922 pTextNd->GetAttr( aSet, nStt+1, nStt+1 );
3924 aSet.ClearItem( RES_TXTATR_REFMARK );
3925 aSet.ClearItem( RES_TXTATR_TOXMARK );
3926 aSet.ClearItem( RES_TXTATR_CJK_RUBY );
3927 aSet.ClearItem( RES_TXTATR_INETFMT );
3928 aSet.ClearItem( RES_TXTATR_META );
3929 aSet.ClearItem( RES_TXTATR_METAFIELD );
3931 if( aDelPam.GetPoint() != aDelPam.End() )
3932 aDelPam.Exchange();
3934 // Remember the End
3935 SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
3936 const sal_Int32 nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
3938 bool bFirst = true;
3939 OUString sIns;
3940 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
3942 InsertString( aDelPam, sIns );
3943 if( bFirst )
3945 SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
3946 const sal_Int32 nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
3948 SplitNode( *aDelPam.GetPoint(), false );
3950 ++aMkNd;
3951 aDelPam.GetMark()->nNode = aMkNd;
3952 aDelPam.GetMark()->nContent.Assign(
3953 aMkNd.GetNode().GetContentNode(), nMkCnt );
3954 bFirst = false;
3956 else
3957 SplitNode( *aDelPam.GetPoint(), false );
3959 if( !sIns.isEmpty() )
3961 InsertString( aDelPam, sIns );
3964 SwPaM aTmpRange( *aDelPam.GetPoint() );
3965 aTmpRange.SetMark();
3967 ++aPtNd;
3968 aDelPam.GetPoint()->nNode = aPtNd;
3969 aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
3970 nPtCnt);
3971 *aTmpRange.GetMark() = *aDelPam.GetPoint();
3973 m_rDoc.RstTextAttrs( aTmpRange );
3974 InsertItemSet( aTmpRange, aSet );
3977 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3979 SwUndo *const pUndoRD =
3980 new SwUndoRedlineDelete( aDelPam, SwUndoId::REPLACE );
3981 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoRD);
3983 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_DELETE, aDelPam ), true);
3985 *rPam.GetMark() = *aDelPam.GetMark();
3986 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
3988 *aDelPam.GetPoint() = *rPam.GetPoint();
3989 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
3991 // If any Redline will change (split!) the node
3992 const ::sw::mark::IMark* pBkmk =
3993 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
3994 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
3995 ::sw::mark::InsertMode::New);
3997 SwIndex& rIdx = aDelPam.GetPoint()->nContent;
3998 rIdx.Assign( nullptr, 0 );
3999 aDelPam.GetMark()->nContent = rIdx;
4000 rPam.GetPoint()->nNode = 0;
4001 rPam.GetPoint()->nContent = rIdx;
4002 *rPam.GetMark() = *rPam.GetPoint();
4003 //JP 06.01.98: MUSS noch optimiert werden!!!
4004 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
4006 *rPam.GetPoint() = pBkmk->GetMarkPos();
4007 if(pBkmk->IsExpanded())
4008 *rPam.GetMark() = pBkmk->GetOtherMarkPos();
4009 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
4011 bJoinText = false;
4013 else
4015 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
4016 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, USHRT_MAX );
4018 SwUndoReplace* pUndoRpl = nullptr;
4019 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
4020 if (bDoesUndo)
4022 pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
4023 m_rDoc.GetIDocumentUndoRedo().AppendUndo(pUndoRpl);
4025 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
4027 if( aDelPam.GetPoint() != pStt )
4028 aDelPam.Exchange();
4030 SwNodeIndex aPtNd( pStt->nNode, -1 );
4031 const sal_Int32 nPtCnt = pStt->nContent.GetIndex();
4033 // Set the values again, if Frames or footnotes on the Text have been removed.
4034 nStt = nPtCnt;
4035 nEnd = bOneNode ? pEnd->nContent.GetIndex()
4036 : pTextNd->GetText().getLength();
4038 bool bFirst = true;
4039 OUString sIns;
4040 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
4042 if (!bFirst || nStt == pTextNd->GetText().getLength())
4044 InsertString( aDelPam, sIns );
4046 else if( nStt < nEnd || !sIns.isEmpty() )
4048 pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4050 SplitNode( *pStt, false);
4051 bFirst = false;
4054 if( bFirst || !sIns.isEmpty() )
4056 if (!bFirst || nStt == pTextNd->GetText().getLength())
4058 InsertString( aDelPam, sIns );
4060 else if( nStt < nEnd || !sIns.isEmpty() )
4062 pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
4066 *rPam.GetPoint() = *aDelPam.GetMark();
4067 ++aPtNd;
4068 rPam.GetMark()->nNode = aPtNd;
4069 rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
4070 nPtCnt );
4072 if (bJoinText)
4074 assert(rPam.GetPoint() == rPam.End());
4075 // move so that SetEnd remembers position after sw_JoinText
4076 rPam.Move(fnMoveBackward);
4078 else if (aDelPam.GetPoint() == pStt) // backward selection?
4080 assert(*rPam.GetMark() <= *rPam.GetPoint());
4081 rPam.Exchange(); // swap so that rPam is backwards
4084 if( pUndoRpl )
4086 pUndoRpl->SetEnd(rPam);
4091 bool bRet(true);
4092 if (bJoinText)
4094 bRet = ::sw_JoinText(rPam, bJoinPrev);
4097 m_rDoc.getIDocumentState().SetModified();
4098 return bRet;
4101 SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode,
4102 const SfxItemSet* pFlyAttrSet,
4103 const SfxItemSet* pGrfAttrSet,
4104 SwFrameFormat* pFrameFormat)
4106 SwFlyFrameFormat *pFormat = nullptr;
4107 if( pNode )
4109 pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
4110 pFlyAttrSet, pFrameFormat );
4111 if( pGrfAttrSet )
4112 pNode->SetAttr( *pGrfAttrSet );
4114 return pFormat;
4117 #define NUMRULE_STATE \
4118 SfxItemState aNumRuleState = SfxItemState::UNKNOWN; \
4119 SwNumRuleItem aNumRuleItem; \
4120 SfxItemState aListIdState = SfxItemState::UNKNOWN; \
4121 SfxStringItem aListIdItem( RES_PARATR_LIST_ID, OUString() ); \
4123 #define PUSH_NUMRULE_STATE \
4124 lcl_PushNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd );
4126 #define POP_NUMRULE_STATE \
4127 lcl_PopNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd, rPam );
4129 static void lcl_PushNumruleState( SfxItemState &aNumRuleState, SwNumRuleItem &aNumRuleItem,
4130 SfxItemState &aListIdState, SfxStringItem &aListIdItem,
4131 const SwTextNode *pDestTextNd )
4133 // Safe numrule item at destination.
4134 // #i86492# - Safe also <ListId> item of destination.
4135 const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
4136 if (pAttrSet != nullptr)
4138 const SfxPoolItem * pItem = nullptr;
4139 aNumRuleState = pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem);
4140 if (SfxItemState::SET == aNumRuleState)
4141 aNumRuleItem = *static_cast<const SwNumRuleItem *>( pItem);
4143 aListIdState =
4144 pAttrSet->GetItemState(RES_PARATR_LIST_ID, false, &pItem);
4145 if (SfxItemState::SET == aListIdState)
4147 aListIdItem.SetValue( static_cast<const SfxStringItem*>(pItem)->GetValue() );
4152 static void lcl_PopNumruleState( SfxItemState aNumRuleState, const SwNumRuleItem &aNumRuleItem,
4153 SfxItemState aListIdState, const SfxStringItem &aListIdItem,
4154 SwTextNode *pDestTextNd, const SwPaM& rPam )
4156 /* If only a part of one paragraph is copied
4157 restore the numrule at the destination. */
4158 // #i86492# - restore also <ListId> item
4159 if ( !lcl_MarksWholeNode(rPam) )
4161 if (SfxItemState::SET == aNumRuleState)
4163 pDestTextNd->SetAttr(aNumRuleItem);
4165 else
4167 pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
4169 if (SfxItemState::SET == aListIdState)
4171 pDestTextNd->SetAttr(aListIdItem);
4173 else
4175 pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
4180 bool DocumentContentOperationsManager::CopyImpl( SwPaM& rPam, SwPosition& rPos,
4181 const bool bMakeNewFrames, const bool bCopyAll,
4182 SwPaM *const pCpyRange ) const
4184 SwDoc* pDoc = rPos.nNode.GetNode().GetDoc();
4185 const bool bColumnSel = pDoc->IsClipBoard() && pDoc->IsColumnSelection();
4187 SwPosition* pStt = rPam.Start();
4188 SwPosition* pEnd = rPam.End();
4190 // Catch when there's no copy to do.
4191 if( !rPam.HasMark() || ( *pStt >= *pEnd && !bColumnSel ) ||
4192 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
4193 //JP 15.11.2001: don't test inclusive the end, ever exclusive
4194 ( pDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
4196 return false;
4199 const bool bEndEqualIns = pDoc == &m_rDoc && rPos == *pEnd;
4201 // If Undo is enabled, create the UndoCopy object
4202 SwUndoCpyDoc* pUndo = nullptr;
4203 // lcl_DeleteRedlines may delete the start or end node of the cursor when
4204 // removing the redlines so use cursor that is corrected by PaMCorrAbs
4205 std::shared_ptr<SwUnoCursor> const pCopyPam(pDoc->CreateUnoCursor(rPos));
4207 SwTableNumFormatMerge aTNFM( m_rDoc, *pDoc );
4209 if (pDoc->GetIDocumentUndoRedo().DoesUndo())
4211 pUndo = new SwUndoCpyDoc(*pCopyPam);
4212 pDoc->GetIDocumentUndoRedo().AppendUndo( pUndo );
4215 RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
4216 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
4218 // Move the PaM one node back from the insert position, so that
4219 // the position doesn't get moved
4220 pCopyPam->SetMark();
4221 bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
4222 // If the position was shifted from more than one node, an end node has been skipped
4223 bool bAfterTable = false;
4224 if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > 1)
4226 // First go back to the original place
4227 pCopyPam->GetPoint()->nNode = rPos.nNode;
4228 pCopyPam->GetPoint()->nContent = rPos.nContent;
4230 bCanMoveBack = false;
4231 bAfterTable = true;
4233 if( !bCanMoveBack )
4234 pCopyPam->GetPoint()->nNode--;
4236 SwNodeRange aRg( pStt->nNode, pEnd->nNode );
4237 SwNodeIndex aInsPos( rPos.nNode );
4238 const bool bOneNode = pStt->nNode == pEnd->nNode;
4239 SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode();
4240 SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4241 SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
4242 bool bCopyCollFormat = !pDoc->IsInsOnlyTextGlossary() &&
4243 ( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
4244 ( !bOneNode && !rPos.nContent.GetIndex() ) );
4245 bool bCopyBookmarks = true;
4246 bool bCopyPageSource = false;
4247 bool bStartIsTextNode = nullptr != pSttTextNd;
4249 // #i104585# copy outline num rule to clipboard (for ASCII filter)
4250 if (pDoc->IsClipBoard() && m_rDoc.GetOutlineNumRule())
4252 pDoc->SetOutlineNumRule(*m_rDoc.GetOutlineNumRule());
4255 // #i86492#
4256 // Correct the search for a previous list:
4257 // First search for non-outline numbering list. Then search for non-outline
4258 // bullet list.
4259 // Keep also the <ListId> value for possible propagation.
4260 OUString aListIdToPropagate;
4261 const SwNumRule* pNumRuleToPropagate =
4262 pDoc->SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, true );
4263 if ( !pNumRuleToPropagate )
4265 pNumRuleToPropagate =
4266 pDoc->SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, true );
4268 // #i86492#
4269 // Do not propagate previous found list, if
4270 // - destination is an empty paragraph which is not in a list and
4271 // - source contains at least one paragraph which is not in a list
4272 if ( pNumRuleToPropagate &&
4273 pDestTextNd && !pDestTextNd->GetText().getLength() &&
4274 !pDestTextNd->IsInList() &&
4275 !lcl_ContainsOnlyParagraphsInList( rPam ) )
4277 pNumRuleToPropagate = nullptr;
4280 // This do/while block is only there so that we can break out of it!
4281 do {
4282 if( pSttTextNd )
4284 // Don't copy the beginning completely?
4285 if( !bCopyCollFormat || bColumnSel || pStt->nContent.GetIndex() )
4287 SwIndex aDestIdx( rPos.nContent );
4288 bool bCopyOk = false;
4289 if( !pDestTextNd )
4291 if( pStt->nContent.GetIndex() || bOneNode )
4292 pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos,
4293 pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
4294 else
4296 pDestTextNd = pSttTextNd->MakeCopy( pDoc, aInsPos )->GetTextNode();
4297 bCopyOk = true;
4299 aDestIdx.Assign( pDestTextNd, 0 );
4300 bCopyCollFormat = true;
4302 else if( !bOneNode || bColumnSel )
4304 const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
4306 ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
4307 pDoc->getIDocumentContentOperations().SplitNode( rPos, false );
4310 if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
4312 // after the SplitNode, span the CpyPam correctly again
4313 pCopyPam->Move( fnMoveBackward, GoInContent );
4314 pCopyPam->Move( fnMoveBackward, GoInContent );
4317 pDestTextNd = pDoc->GetNodes()[ aInsPos.GetIndex()-1 ]->GetTextNode();
4318 aDestIdx.Assign(
4319 pDestTextNd, pDestTextNd->GetText().getLength());
4321 // Correct the area again
4322 if( bEndEqualIns )
4324 bool bChg = pEnd != rPam.GetPoint();
4325 if( bChg )
4326 rPam.Exchange();
4327 rPam.Move( fnMoveBackward, GoInContent );
4328 if( bChg )
4329 rPam.Exchange();
4331 else if( rPos == *pEnd )
4333 // The end was also moved
4334 pEnd->nNode--;
4335 pEnd->nContent.Assign( pDestTextNd, nContentEnd );
4337 // tdf#63022 always reset pEndTextNd after SplitNode
4338 aRg.aEnd = pEnd->nNode;
4339 pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
4342 NUMRULE_STATE
4343 if( bCopyCollFormat && bOneNode )
4345 PUSH_NUMRULE_STATE
4348 if( !bCopyOk )
4350 const sal_Int32 nCpyLen = ( bOneNode
4351 ? pEnd->nContent.GetIndex()
4352 : pSttTextNd->GetText().getLength())
4353 - pStt->nContent.GetIndex();
4354 pSttTextNd->CopyText( pDestTextNd, aDestIdx,
4355 pStt->nContent, nCpyLen );
4356 if( bEndEqualIns )
4357 pEnd->nContent -= nCpyLen;
4360 if( bOneNode )
4362 if (bCopyCollFormat)
4364 pSttTextNd->CopyCollFormat( *pDestTextNd );
4365 POP_NUMRULE_STATE
4368 break;
4371 aRg.aStart++;
4374 else if( pDestTextNd )
4376 // Problems with insertion of table selections into "normal" text solved.
4377 // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
4378 // the undo operation will try to merge this node after removing the table.
4379 // If we didn't split a textnode, the PaM should start at the inserted table node
4380 if( rPos.nContent.GetIndex() == pDestTextNd->Len() )
4381 { // Insertion at the last position of a textnode (empty or not)
4382 ++aInsPos; // The table will be inserted behind the text node
4384 else if( rPos.nContent.GetIndex() )
4385 { // Insertion in the middle of a text node, it has to be split
4386 // (and joined from undo)
4387 bStartIsTextNode = true;
4389 const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
4391 ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
4392 pDoc->getIDocumentContentOperations().SplitNode( rPos, false );
4395 if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
4397 // after the SplitNode, span the CpyPam correctly again
4398 pCopyPam->Move( fnMoveBackward, GoInContent );
4399 pCopyPam->Move( fnMoveBackward, GoInContent );
4402 // Correct the area again
4403 if( bEndEqualIns )
4404 aRg.aEnd--;
4405 // The end would also be moved
4406 else if( rPos == *pEnd )
4408 rPos.nNode-=2;
4409 rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
4410 nContentEnd );
4411 rPos.nNode++;
4412 aRg.aEnd--;
4415 else if( bCanMoveBack )
4416 { //Insertion at the first position of a text node. It will not be splitted, the table
4417 // will be inserted before the text node.
4418 // See below, before the SetInsertRange function of the undo object will be called,
4419 // the CpyPam would be moved to the next content position. This has to be avoided
4420 // We want to be moved to the table node itself thus we have to set bCanMoveBack
4421 // and to manipulate pCopyPam.
4422 bCanMoveBack = false;
4423 pCopyPam->GetPoint()->nNode--;
4427 pDestTextNd = aInsPos.GetNode().GetTextNode();
4428 if (pEndTextNd)
4430 SwIndex aDestIdx( rPos.nContent );
4431 if( !pDestTextNd )
4433 pDestTextNd = pDoc->GetNodes().MakeTextNode( aInsPos,
4434 pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
4435 aDestIdx.Assign( pDestTextNd, 0 );
4436 aInsPos--;
4438 // if we have to insert an extra text node
4439 // at the destination, this node will be our new destination
4440 // (text) node, and thus we set bStartisTextNode to true. This
4441 // will ensure that this node will be deleted during Undo
4442 // using JoinNext.
4443 OSL_ENSURE( !bStartIsTextNode, "Oops, undo may be instable now." );
4444 bStartIsTextNode = true;
4447 const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
4449 NUMRULE_STATE
4450 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
4452 PUSH_NUMRULE_STATE
4455 pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ),
4456 pEnd->nContent.GetIndex() );
4458 // Also copy all format templates
4459 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
4461 pEndTextNd->CopyCollFormat( *pDestTextNd );
4462 if ( bOneNode )
4464 POP_NUMRULE_STATE
4469 if( bCopyAll || aRg.aStart != aRg.aEnd )
4471 SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange );
4472 if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
4474 aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
4475 if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
4476 pDestTextNd->ResetAttr( RES_BREAK );
4477 if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
4478 pDestTextNd->ResetAttr( RES_PAGEDESC );
4481 SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
4482 SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
4483 if (bCanMoveBack)
4484 { // pCopyPam is actually 1 before the copy range so move it fwd
4485 SwPaM temp(*pCopyPam->GetPoint());
4486 temp.Move(fnMoveForward, GoInContent);
4487 startPos = *temp.GetPoint();
4489 assert(startPos.nNode.GetNode().IsContentNode());
4490 std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
4491 if( aInsPos == pEnd->nNode )
4493 SwNodeIndex aSaveIdx( aInsPos, -1 );
4494 CopyWithFlyInFly( aRg, 0, aInsPos, &tmp, bMakeNewFrames, false );
4495 ++aSaveIdx;
4496 pEnd->nNode = aSaveIdx;
4497 pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
4499 else
4500 CopyWithFlyInFly( aRg, pEnd->nContent.GetIndex(), aInsPos, &tmp, bMakeNewFrames, false );
4502 bCopyBookmarks = false;
4504 // Put the breaks back into the first node
4505 if( aBrkSet.Count() && nullptr != ( pDestTextNd = pDoc->GetNodes()[
4506 pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
4508 pDestTextNd->SetAttr( aBrkSet );
4509 bCopyPageSource = true;
4512 } while( false );
4515 // it is not possible to make this test when copy from the clipBoard to document
4516 // in this case the PageNum not exist anymore
4517 // tdf#39400 and tdf#97526
4518 // when copy from document to ClipBoard, and it is from the first page
4519 // and not the source has the page break
4520 if (pDoc->IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
4522 pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
4523 pDestTextNd->ResetAttr(RES_PAGEDESC);
4527 // Adjust position (in case it was moved / in another node)
4528 rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
4529 rPos.nContent.GetIndex() );
4531 if( rPos.nNode != aInsPos )
4533 pCopyPam->GetMark()->nNode = aInsPos;
4534 pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0);
4535 rPos = *pCopyPam->GetMark();
4537 else
4538 *pCopyPam->GetMark() = rPos;
4540 if ( !bAfterTable )
4541 pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
4542 else
4544 // Reset the offset to 0 as it was before the insertion
4545 pCopyPam->GetPoint()->nContent = 0;
4547 pCopyPam->GetPoint()->nNode++;
4548 // If the next node is a start node, then step back: the start node
4549 // has been copied and needs to be in the selection for the undo
4550 if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode())
4551 pCopyPam->GetPoint()->nNode--;
4554 pCopyPam->Exchange();
4556 // Also copy all bookmarks
4557 if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
4558 lcl_CopyBookmarks( rPam, *pCopyPam );
4560 if( RedlineFlags::DeleteRedlines & eOld )
4562 assert(*pCopyPam->GetPoint() == rPos);
4563 // the Node rPos points to may be deleted so unregister ...
4564 rPos.nContent.Assign(nullptr, 0);
4565 lcl_DeleteRedlines(rPam, *pCopyPam);
4566 rPos = *pCopyPam->GetPoint(); // ... and restore.
4569 // If Undo is enabled, store the inserted area
4570 if (pDoc->GetIDocumentUndoRedo().DoesUndo())
4572 pUndo->SetInsertRange( *pCopyPam, true, bStartIsTextNode );
4575 if( pCpyRange )
4577 pCpyRange->SetMark();
4578 *pCpyRange->GetPoint() = *pCopyPam->GetPoint();
4579 *pCpyRange->GetMark() = *pCopyPam->GetMark();
4582 if ( pNumRuleToPropagate != nullptr )
4584 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
4585 pDoc->SetNumRule( *pCopyPam, *pNumRuleToPropagate, false,
4586 aListIdToPropagate, true, true );
4589 pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
4590 pDoc->getIDocumentState().SetModified();
4592 return true;
4597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */