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