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