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