1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include <DocumentContentOperationsManager.hxx>
20 #include <DocumentRedlineManager.hxx>
23 #include <IDocumentUndoRedo.hxx>
24 #include <IDocumentMarkAccess.hxx>
25 #include <IDocumentState.hxx>
26 #include <IDocumentLayoutAccess.hxx>
27 #include <IDocumentRedlineAccess.hxx>
28 #include <IDocumentStylePoolAccess.hxx>
29 #include <IDocumentSettingAccess.hxx>
30 #include <UndoManager.hxx>
32 #include <textboxhelper.hxx>
33 #include <dcontact.hxx>
35 #include <numrule.hxx>
36 #include <charfmt.hxx>
38 #include <ndnotxt.hxx>
40 #include <breakit.hxx>
42 #include <fmtanchr.hxx>
43 #include <fmtcntnt.hxx>
44 #include <fmtinfmt.hxx>
45 #include <fmtpdsc.hxx>
46 #include <fmtcnct.hxx>
47 #include <SwStyleNameMapper.hxx>
48 #include <redline.hxx>
50 #include <rootfrm.hxx>
51 #include <frmtool.hxx>
52 #include <unocrsr.hxx>
55 #include <poolfmt.hxx>
57 #include <txatbase.hxx>
58 #include <UndoRedline.hxx>
60 #include <UndoBookmark.hxx>
61 #include <UndoDelete.hxx>
62 #include <UndoSplitMove.hxx>
63 #include <UndoOverwrite.hxx>
64 #include <UndoInsert.hxx>
65 #include <UndoAttribute.hxx>
67 #include <acorrect.hxx>
68 #include <bookmark.hxx>
72 #include <fmtflcnt.hxx>
74 #include <frameformats.hxx>
75 #include <formatflysplit.hxx>
76 #include <o3tl/safeint.hxx>
77 #include <sal/log.hxx>
78 #include <unotools/charclass.hxx>
79 #include <unotools/configmgr.hxx>
80 #include <unotools/transliterationwrapper.hxx>
81 #include <i18nutil/transliteration.hxx>
82 #include <sfx2/Metadatable.hxx>
83 #include <sot/exchange.hxx>
84 #include <svl/stritem.hxx>
85 #include <svl/itemiter.hxx>
86 #include <svx/svdobj.hxx>
87 #include <svx/svdouno.hxx>
88 #include <tools/globname.hxx>
89 #include <editeng/formatbreakitem.hxx>
90 #include <com/sun/star/i18n/Boundary.hpp>
91 #include <com/sun/star/i18n/WordType.hpp>
92 #include <com/sun/star/i18n/XBreakIterator.hpp>
93 #include <com/sun/star/embed/XEmbeddedObject.hpp>
98 using namespace ::com::sun::star::i18n
;
102 // Copy method from SwDoc
103 // Prevent copying into Flys that are anchored in the range
104 bool lcl_ChkFlyFly( SwDoc
& rDoc
, SwNodeOffset nSttNd
, SwNodeOffset nEndNd
,
105 SwNodeOffset nInsNd
)
108 for(sw::SpzFrameFormat
* pFormat
: *rDoc
.GetSpzFrameFormats())
110 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
111 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
113 ((RndStdIds::FLY_AS_CHAR
== pAnchor
->GetAnchorId()) ||
114 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()) ||
115 (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId()) ||
116 (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId())) &&
117 nSttNd
<= pAnchorNode
->GetIndex() &&
118 pAnchorNode
->GetIndex() < nEndNd
)
120 const SwFormatContent
& rContent
= pFormat
->GetContent();
122 if( !rContent
.GetContentIdx() ||
123 nullptr == ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ))
126 if( pSNd
->GetIndex() < nInsNd
&&
127 nInsNd
< pSNd
->EndOfSectionIndex() )
131 if( lcl_ChkFlyFly( rDoc
, pSNd
->GetIndex(),
132 pSNd
->EndOfSectionIndex(), nInsNd
) )
141 SwNodeIndex
InitDelCount(SwPaM
const& rSourcePaM
, SwNodeOffset
& rDelCount
)
143 SwPosition
const& rStart(*rSourcePaM
.Start());
144 // Special handling for SwDoc::AppendDoc
145 if (rSourcePaM
.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
146 == rStart
.GetNodeIndex())
148 rDelCount
= SwNodeOffset(1);
149 return SwNodeIndex(rStart
.GetNode(), +1);
153 rDelCount
= SwNodeOffset(0);
154 return SwNodeIndex(rStart
.GetNode());
159 The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
160 array. It is called after a call of the CopyNodes(..) function. But this function does not copy
161 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
162 if the corresponding end/start node is outside the copied pam.
163 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
164 index inside the pam.
165 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
166 of "non-copy" nodes between rPam.Start() and rLastIdx.
167 nNewIdx is the new position of interest.
169 void lcl_NonCopyCount( const SwPaM
& rPam
, SwNodeIndex
& rLastIdx
, const SwNodeOffset nNewIdx
, SwNodeOffset
& rDelCount
)
171 SwNodeOffset nStart
= rPam
.Start()->GetNodeIndex();
172 SwNodeOffset nEnd
= rPam
.End()->GetNodeIndex();
173 if( rLastIdx
.GetIndex() < nNewIdx
) // Moving forward?
175 // We never copy the StartOfContent node
176 do // count "non-copy" nodes
178 SwNode
& rNode
= rLastIdx
.GetNode();
179 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
180 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
186 while( rLastIdx
.GetIndex() < nNewIdx
);
188 else if( rDelCount
) // optimization: if there are no "non-copy" nodes until now,
189 // no move backward needed
191 while( rLastIdx
.GetIndex() > nNewIdx
)
193 SwNode
& rNode
= rLastIdx
.GetNode();
194 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
195 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
204 void lcl_SetCpyPos( const SwPosition
& rOrigPos
,
205 const SwPosition
& rOrigStt
,
206 const SwPosition
& rCpyStt
,
208 SwNodeOffset nDelCount
)
210 SwNodeOffset nNdOff
= rOrigPos
.GetNodeIndex();
211 nNdOff
-= rOrigStt
.GetNodeIndex();
213 sal_Int32 nContentPos
= rOrigPos
.GetContentIndex();
215 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
216 rChgPos
.Assign( nNdOff
+ rCpyStt
.GetNodeIndex() );
217 if (!rChgPos
.GetNode().GetContentNode())
221 // just adapt the content index
222 if( nContentPos
> rOrigStt
.GetContentIndex() )
223 nContentPos
-= rOrigStt
.GetContentIndex();
226 nContentPos
+= rCpyStt
.GetContentIndex();
228 rChgPos
.SetContent( nContentPos
);
235 // TODO: use SaveBookmark (from DelBookmarks)
236 void CopyBookmarks(const SwPaM
& rPam
, const SwPosition
& rCpyPam
, SwCopyFlags flags
)
238 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
239 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
240 const IDocumentMarkAccess
* const pSrcMarkAccess
= rSrcDoc
.getIDocumentMarkAccess();
241 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
243 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
244 SwPosition
const*const pCpyStt
= &rCpyPam
;
246 std::vector
< const ::sw::mark::IMark
* > vMarksToCopy
;
247 for ( IDocumentMarkAccess::const_iterator_t ppMark
= pSrcMarkAccess
->getAllMarksBegin();
248 ppMark
!= pSrcMarkAccess
->getAllMarksEnd();
251 const ::sw::mark::IMark
* const pMark
= *ppMark
;
253 const SwPosition
& rMarkStart
= pMark
->GetMarkStart();
254 const SwPosition
& rMarkEnd
= pMark
->GetMarkEnd();
255 // only include marks that are in the range and not touching both start and end
256 // - not for annotation or checkbox marks.
257 bool const isIncludeStart(
258 (rStt
.GetContentIndex() == 0 // paragraph start selected?
259 // also: only if inserting at the start - cross reference
260 // marks require index to be 0, and there could be one
261 // on the target node already
262 && rCpyPam
.GetContentIndex() == 0)
263 || rMarkStart
!= rStt
);
264 bool const isIncludeEnd(
265 (rEnd
.GetNode().IsTextNode() // paragraph end selected?
266 && rEnd
.GetContentIndex() == rEnd
.GetNode().GetTextNode()->Len())
267 || rMarkEnd
!= rEnd
);
268 const bool bIsNotOnBoundary
=
270 ? (isIncludeStart
|| isIncludeEnd
) // rMarkStart != rMarkEnd
271 : (isIncludeStart
&& isIncludeEnd
); // rMarkStart == rMarkEnd
272 const IDocumentMarkAccess::MarkType aMarkType
= IDocumentMarkAccess::GetType(*pMark
);
273 if ( rMarkStart
>= rStt
&& rMarkEnd
<= rEnd
274 && ( bIsNotOnBoundary
275 || aMarkType
== IDocumentMarkAccess::MarkType::ANNOTATIONMARK
276 || aMarkType
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
277 || aMarkType
== IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
278 || aMarkType
== IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
279 || aMarkType
== IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
281 vMarksToCopy
.push_back(pMark
);
284 // We have to count the "non-copied" nodes...
285 SwNodeOffset nDelCount
;
286 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
287 for(const sw::mark::IMark
* const pMark
: vMarksToCopy
)
289 SwPaM
aTmpPam(*pCpyStt
);
290 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetMarkPos().GetNodeIndex(), nDelCount
);
291 lcl_SetCpyPos( pMark
->GetMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetPoint(), nDelCount
);
292 if(pMark
->IsExpanded())
295 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetOtherMarkPos().GetNodeIndex(), nDelCount
);
296 lcl_SetCpyPos(pMark
->GetOtherMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetMark(), nDelCount
);
299 OUString sRequestedName
= pMark
->GetName();
300 if (flags
& SwCopyFlags::IsMoveToFly
)
302 assert(&rSrcDoc
== &rDestDoc
);
303 // Ensure the name can be given to NewMark, since this is ultimately a move
304 auto pSoonToBeDeletedMark
= const_cast<sw::mark::IMark
*>(pMark
);
305 rDestDoc
.getIDocumentMarkAccess()->renameMark(pSoonToBeDeletedMark
,
306 sRequestedName
+ "COPY_IS_MOVE");
309 ::sw::mark::IMark
* const pNewMark
= rDestDoc
.getIDocumentMarkAccess()->makeMark(
312 IDocumentMarkAccess::GetType(*pMark
),
313 ::sw::mark::InsertMode::CopyText
);
314 // Explicitly try to get exactly the same name as in the source
315 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
316 if (pNewMark
== nullptr)
318 assert(IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
319 || IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
);
320 continue; // can't insert duplicate cross reference mark
322 rDestDoc
.getIDocumentMarkAccess()->renameMark(pNewMark
, sRequestedName
);
324 // copying additional attributes for bookmarks or fieldmarks
325 ::sw::mark::IBookmark
* const pNewBookmark
=
326 dynamic_cast< ::sw::mark::IBookmark
* const >(pNewMark
);
327 const ::sw::mark::IBookmark
* const pOldBookmark
=
328 dynamic_cast< const ::sw::mark::IBookmark
* >(pMark
);
329 if (pNewBookmark
&& pOldBookmark
)
331 pNewBookmark
->SetKeyCode(pOldBookmark
->GetKeyCode());
332 pNewBookmark
->SetShortName(pOldBookmark
->GetShortName());
333 pNewBookmark
->Hide(pOldBookmark
->IsHidden());
334 pNewBookmark
->SetHideCondition(pOldBookmark
->GetHideCondition());
336 ::sw::mark::IFieldmark
* const pNewFieldmark
=
337 dynamic_cast< ::sw::mark::IFieldmark
* const >(pNewMark
);
338 const ::sw::mark::IFieldmark
* const pOldFieldmark
=
339 dynamic_cast< const ::sw::mark::IFieldmark
* >(pMark
);
340 if (pNewFieldmark
&& pOldFieldmark
)
342 pNewFieldmark
->SetFieldname(pOldFieldmark
->GetFieldname());
343 pNewFieldmark
->SetFieldHelptext(pOldFieldmark
->GetFieldHelptext());
344 ::sw::mark::IFieldmark::parameter_map_t
* pNewParams
= pNewFieldmark
->GetParameters();
345 const ::sw::mark::IFieldmark::parameter_map_t
* pOldParams
= pOldFieldmark
->GetParameters();
346 for (const auto& rEntry
: *pOldParams
)
348 pNewParams
->insert( rEntry
);
352 ::sfx2::Metadatable
const*const pMetadatable(
353 dynamic_cast< ::sfx2::Metadatable
const* >(pMark
));
354 ::sfx2::Metadatable
*const pNewMetadatable(
355 dynamic_cast< ::sfx2::Metadatable
* >(pNewMark
));
356 if (pMetadatable
&& pNewMetadatable
)
358 pNewMetadatable
->RegisterAsCopyOf(*pMetadatable
);
366 void lcl_DeleteRedlines( const SwPaM
& rPam
, SwPaM
& rCpyPam
)
368 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
369 const SwRedlineTable
& rTable
= rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable();
373 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
374 SwPosition
* pCpyStt
= rCpyPam
.Start(), *pCpyEnd
= rCpyPam
.End();
375 std::unique_ptr
<SwPaM
> pDelPam
;
376 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
377 // We have to count the "non-copied" nodes
378 SwNodeOffset nDelCount
;
379 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
381 SwRedlineTable::size_type n
= 0;
382 rSrcDoc
.getIDocumentRedlineAccess().GetRedline( *pStt
, &n
);
383 for( ; n
< rTable
.size(); ++n
)
385 const SwRangeRedline
* pRedl
= rTable
[ n
];
386 if( RedlineType::Delete
== pRedl
->GetType() && pRedl
->IsVisible() )
388 auto [pRStt
, pREnd
] = pRedl
->StartEnd(); // SwPosition*
390 SwComparePosition eCmpPos
= ComparePosition( *pStt
, *pEnd
, *pRStt
, *pREnd
);
393 case SwComparePosition::CollideEnd
:
394 case SwComparePosition::Before
:
395 // Pos1 is before Pos2
398 case SwComparePosition::CollideStart
:
399 case SwComparePosition::Behind
:
400 // Pos1 is after Pos2
406 pDelPam
.reset(new SwPaM( *pCpyStt
, pDelPam
.release() ));
409 lcl_NonCopyCount( rPam
, aCorrIdx
, pRStt
->GetNodeIndex(), nDelCount
);
410 lcl_SetCpyPos( *pRStt
, *pStt
, *pCpyStt
,
411 *pDelPam
->GetPoint(), nDelCount
);
416 *pDelPam
->GetPoint() = *pCpyEnd
;
419 lcl_NonCopyCount( rPam
, aCorrIdx
, pREnd
->GetNodeIndex(), nDelCount
);
420 lcl_SetCpyPos( *pREnd
, *pStt
, *pCpyStt
,
421 *pDelPam
->GetPoint(), nDelCount
);
424 if (pDelPam
->GetNext() != pDelPam
.get()
425 && *pDelPam
->GetNext()->End() == *pDelPam
->Start())
427 *pDelPam
->GetNext()->End() = *pDelPam
->End();
428 pDelPam
.reset(pDelPam
->GetNext());
438 RedlineFlags eOld
= rDestDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
439 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
| RedlineFlags::Ignore
);
441 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
444 rDestDoc
.getIDocumentContentOperations().DeleteAndJoin( *pDelPam
->GetNext() );
445 if( !pDelPam
->IsMultiSelection() )
447 delete pDelPam
->GetNext();
450 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
453 void lcl_DeleteRedlines( const SwNodeRange
& rRg
, SwNodeRange
const & rCpyRg
)
455 SwDoc
& rSrcDoc
= rRg
.aStart
.GetNode().GetDoc();
456 if( !rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
458 SwPaM
aRgTmp( rRg
.aStart
, rRg
.aEnd
);
459 SwPaM
aCpyTmp( rCpyRg
.aStart
, rCpyRg
.aEnd
);
460 lcl_DeleteRedlines( aRgTmp
, aCpyTmp
);
464 void lcl_ChainFormats( SwFlyFrameFormat
*pSrc
, SwFlyFrameFormat
*pDest
)
466 SwFormatChain
aSrc( pSrc
->GetChain() );
467 if ( !aSrc
.GetNext() )
469 aSrc
.SetNext( pDest
);
470 pSrc
->SetFormatAttr( aSrc
);
472 SwFormatChain
aDest( pDest
->GetChain() );
473 if ( !aDest
.GetPrev() )
475 aDest
.SetPrev( pSrc
);
476 pDest
->SetFormatAttr( aDest
);
481 bool lcl_ContainsOnlyParagraphsInList( const SwPaM
& rPam
)
485 const SwTextNode
* pTextNd
= rPam
.Start()->GetNode().GetTextNode();
486 const SwTextNode
* pEndTextNd
= rPam
.End()->GetNode().GetTextNode();
487 if ( pTextNd
&& pTextNd
->IsInList() &&
488 pEndTextNd
&& pEndTextNd
->IsInList() )
491 SwNodeIndex
aIdx(rPam
.Start()->GetNode());
496 pTextNd
= aIdx
.GetNode().GetTextNode();
498 if ( !pTextNd
|| !pTextNd
->IsInList() )
503 } while (pTextNd
!= pEndTextNd
);
509 bool lcl_MarksWholeNode(const SwPaM
& rPam
)
511 bool bResult
= false;
512 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
514 if (nullptr != pStt
&& nullptr != pEnd
)
516 const SwTextNode
* pSttNd
= pStt
->GetNode().GetTextNode();
517 const SwTextNode
* pEndNd
= pEnd
->GetNode().GetTextNode();
519 if (nullptr != pSttNd
&& nullptr != pEndNd
&&
520 pStt
->GetContentIndex() == 0 &&
521 pEnd
->GetContentIndex() == pEndNd
->Len())
531 //local functions originally from sw/source/core/doc/docedt.cxx
534 void CalcBreaks(std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> & rBreaks
,
535 SwPaM
const & rPam
, bool const isOnlyFieldmarks
)
537 SwNodeOffset
const nStartNode(rPam
.Start()->GetNodeIndex());
538 SwNodeOffset
const nEndNode(rPam
.End()->GetNodeIndex());
539 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
540 IDocumentMarkAccess
const& rIDMA(*rPam
.GetDoc().getIDocumentMarkAccess());
542 std::stack
<std::tuple
<sw::mark::IFieldmark
const*, bool, SwNodeOffset
, sal_Int32
>> startedFields
;
544 for (SwNodeOffset n
= nStartNode
; n
<= nEndNode
; ++n
)
546 SwNode
*const pNode(rNodes
[n
]);
547 if (pNode
->IsTextNode())
549 SwTextNode
& rTextNode(*pNode
->GetTextNode());
550 sal_Int32
const nStart(n
== nStartNode
551 ? rPam
.Start()->GetContentIndex()
553 sal_Int32
const nEnd(n
== nEndNode
554 ? rPam
.End()->GetContentIndex()
556 for (sal_Int32 i
= nStart
; i
< nEnd
; ++i
)
558 const sal_Unicode
c(rTextNode
.GetText()[i
]);
561 // note: CH_TXT_ATR_FORMELEMENT does not need handling
562 // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
563 case CH_TXTATR_INWORD
:
564 case CH_TXTATR_BREAKWORD
:
566 // META hints only have dummy char at the start, not
567 // at the end, so no need to check in nStartNode
568 if (n
== nEndNode
&& !isOnlyFieldmarks
)
570 SwTextAttr
const* pAttr(rTextNode
.GetTextAttrForCharAt(i
));
571 if (pAttr
&& pAttr
->End() && (nEnd
< *pAttr
->End()))
573 assert(pAttr
->HasDummyChar());
574 rBreaks
.emplace_back(n
, i
);
579 // See if this is an end dummy character for a content control.
580 pAttr
= rTextNode
.GetTextAttrForEndCharAt(i
, RES_TXTATR_CONTENTCONTROL
);
581 if (pAttr
&& (nStart
> pAttr
->GetStart()))
583 rBreaks
.emplace_back(n
, i
);
589 case CH_TXT_ATR_FIELDSTART
:
591 auto const pFieldMark(rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
592 startedFields
.emplace(pFieldMark
, false, 0, 0);
595 case CH_TXT_ATR_FIELDSEP
:
597 if (startedFields
.empty())
599 rBreaks
.emplace_back(n
, i
);
602 { // no way to find the field via MarkManager...
603 assert(std::get
<0>(startedFields
.top())->IsCoveringPosition(SwPosition(rTextNode
, i
)));
604 std::get
<1>(startedFields
.top()) = true;
605 std::get
<2>(startedFields
.top()) = n
;
606 std::get
<3>(startedFields
.top()) = i
;
610 case CH_TXT_ATR_FIELDEND
:
612 if (startedFields
.empty())
614 rBreaks
.emplace_back(n
, i
);
617 { // fieldmarks must not overlap => stack
618 assert(std::get
<0>(startedFields
.top()) == rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
626 else if (pNode
->IsStartNode())
628 if (pNode
->EndOfSectionIndex() <= nEndNode
)
629 { // fieldmark cannot overlap node section
630 n
= pNode
->EndOfSectionIndex();
634 { // EndNode can actually happen with sections :(
635 assert(pNode
->IsEndNode() || pNode
->IsNoTextNode());
638 while (!startedFields
.empty())
640 if (const sw::mark::IFieldmark
* pMark
= std::get
<0>(startedFields
.top()))
642 SwPosition
const& rStart(pMark
->GetMarkStart());
643 std::pair
<SwNodeOffset
, sal_Int32
> const pos(
644 rStart
.GetNodeIndex(), rStart
.GetContentIndex());
645 auto it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), pos
);
646 assert(it
== rBreaks
.end() || *it
!= pos
);
647 rBreaks
.insert(it
, pos
);
649 if (std::get
<1>(startedFields
.top()))
651 std::pair
<SwNodeOffset
, sal_Int32
> const posSep(
652 std::get
<2>(startedFields
.top()),
653 std::get
<3>(startedFields
.top()));
654 auto it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), posSep
);
655 assert(it
== rBreaks
.end() || *it
!= posSep
);
656 rBreaks
.insert(it
, posSep
);
666 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager
& rDocumentContentOperations
,
667 SwPaM
& rPam
, SwDeleteFlags
const flags
,
668 bool (::sw::DocumentContentOperationsManager::*pFunc
)(SwPaM
&, SwDeleteFlags
))
670 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
672 sw::CalcBreaks(Breaks
, rPam
);
676 return (rDocumentContentOperations
.*pFunc
)(rPam
, flags
);
679 // Deletion must be split into several parts if the text node
680 // contains a text attribute with end and with dummy character
681 // and the selection does not contain the text attribute completely,
682 // but overlaps its start (left), where the dummy character is.
684 SwPosition
const & rSelectionEnd( *rPam
.End() );
687 // iterate from end to start, to avoid invalidating the offsets!
688 auto iter( Breaks
.rbegin() );
689 SwNodeOffset
nOffset(0);
690 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
691 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
692 SwPosition
& rEnd( *aPam
.End() );
693 SwPosition
& rStart( *aPam
.Start() );
695 while (iter
!= Breaks
.rend())
697 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
698 if (rStart
< rEnd
) // check if part is empty
700 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, flags
);
701 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // deleted fly nodes...
703 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
707 rStart
= *rPam
.Start(); // set to original start
708 if (rStart
< rEnd
) // check if part is empty
710 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, flags
);
716 bool lcl_StrLenOverflow( const SwPaM
& rPam
)
718 // If we try to merge two paragraphs we have to test if afterwards
719 // the string doesn't exceed the allowed string length
720 if( rPam
.GetPoint()->GetNode() != rPam
.GetMark()->GetNode() )
722 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
723 const SwTextNode
* pEndNd
= pEnd
->GetNode().GetTextNode();
724 if( (nullptr != pEndNd
) && pStt
->GetNode().IsTextNode() )
726 const sal_uInt64 nSum
= pStt
->GetContentIndex() +
727 pEndNd
->GetText().getLength() - pEnd
->GetContentIndex();
728 return nSum
> o3tl::make_unsigned(SAL_MAX_INT32
);
736 SwRangeRedline
* pRedl
;
737 SwNodeOffset nStt
, nEnd
;
741 SaveRedline( SwRangeRedline
* pR
, const SwNodeIndex
& rSttIdx
)
746 auto [pStt
, pEnd
] = pR
->StartEnd(); // SwPosition*
747 SwNodeOffset nSttIdx
= rSttIdx
.GetIndex();
748 nStt
= pStt
->GetNodeIndex() - nSttIdx
;
749 nSttCnt
= pStt
->GetContentIndex();
752 nEnd
= pEnd
->GetNodeIndex() - nSttIdx
;
753 nEndCnt
= pEnd
->GetContentIndex();
756 pRedl
->GetPoint()->Assign( SwNodeOffset(0) );
757 pRedl
->GetMark()->Assign( SwNodeOffset(0) );
760 SaveRedline( SwRangeRedline
* pR
, const SwPosition
& rPos
)
765 auto [pStt
, pEnd
] = pR
->StartEnd(); // SwPosition*
766 SwNodeOffset nSttIdx
= rPos
.GetNodeIndex();
767 nStt
= pStt
->GetNodeIndex() - nSttIdx
;
768 nSttCnt
= pStt
->GetContentIndex();
769 if( nStt
== SwNodeOffset(0) )
770 nSttCnt
= nSttCnt
- rPos
.GetContentIndex();
773 nEnd
= pEnd
->GetNodeIndex() - nSttIdx
;
774 nEndCnt
= pEnd
->GetContentIndex();
775 if( nEnd
== SwNodeOffset(0) )
776 nEndCnt
= nEndCnt
- rPos
.GetContentIndex();
779 pRedl
->GetPoint()->Assign( SwNodeOffset(0) );
780 pRedl
->GetMark()->Assign( SwNodeOffset(0) );
783 void SetPos( SwNodeOffset nInsPos
)
785 pRedl
->GetPoint()->Assign( nInsPos
+ nStt
, nSttCnt
);
786 if( pRedl
->HasMark() )
788 pRedl
->GetMark()->Assign( nInsPos
+ nEnd
, nEndCnt
);
792 void SetPos( const SwPosition
& aPos
)
794 pRedl
->GetPoint()->Assign( aPos
.GetNodeIndex() + nStt
,
795 nSttCnt
+ ( nStt
== SwNodeOffset(0) ? aPos
.GetContentIndex() : 0 ) );
796 if( pRedl
->HasMark() )
798 pRedl
->GetMark()->Assign( aPos
.GetNodeIndex() + nEnd
,
799 nEndCnt
+ ( nEnd
== SwNodeOffset(0) ? aPos
.GetContentIndex() : 0 ) );
804 typedef std::vector
< SaveRedline
> SaveRedlines_t
;
806 void lcl_SaveRedlines(const SwPaM
& aPam
, SaveRedlines_t
& rArr
)
808 SwDoc
& rDoc
= aPam
.GetPointNode().GetDoc();
810 auto [pStart
, pEnd
] = aPam
.StartEnd(); // SwPosition*
812 // get first relevant redline
813 SwRedlineTable::size_type nCurrentRedline
;
814 rDoc
.getIDocumentRedlineAccess().GetRedline( *pStart
, &nCurrentRedline
);
815 if( nCurrentRedline
> 0)
818 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
819 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
820 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
822 // iterate over relevant redlines and decide for each whether it should
823 // be saved, or split + saved
824 SwRedlineTable
& rRedlineTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
825 for( ; nCurrentRedline
< rRedlineTable
.size(); nCurrentRedline
++ )
827 SwRangeRedline
* pCurrent
= rRedlineTable
[ nCurrentRedline
];
828 SwComparePosition eCompare
=
829 ComparePosition( *pCurrent
->Start(), *pCurrent
->End(),
832 // we must save this redline if it overlaps aPam
833 // (we may have to split it, too)
834 if( eCompare
== SwComparePosition::OverlapBehind
||
835 eCompare
== SwComparePosition::OverlapBefore
||
836 eCompare
== SwComparePosition::Outside
||
837 eCompare
== SwComparePosition::Inside
||
838 eCompare
== SwComparePosition::Equal
)
840 rRedlineTable
.Remove( nCurrentRedline
-- );
842 // split beginning, if necessary
843 if( eCompare
== SwComparePosition::OverlapBefore
||
844 eCompare
== SwComparePosition::Outside
)
846 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
847 *pNewRedline
->End() = *pStart
;
848 *pCurrent
->Start() = *pStart
;
849 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
852 // split end, if necessary
853 if( eCompare
== SwComparePosition::OverlapBehind
||
854 eCompare
== SwComparePosition::Outside
)
856 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
857 *pNewRedline
->Start() = *pEnd
;
858 *pCurrent
->End() = *pEnd
;
859 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
862 // save the current redline
863 rArr
.emplace_back( pCurrent
, *pStart
);
867 // restore old redline mode
868 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
871 void lcl_RestoreRedlines(SwDoc
& rDoc
, const SwPosition
& rPos
, SaveRedlines_t
& rArr
)
873 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
874 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
876 for(SaveRedline
& rSvRedLine
: rArr
)
878 rSvRedLine
.SetPos( rPos
);
879 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true );
882 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
885 void lcl_SaveRedlines(const SwNodeRange
& rRg
, SaveRedlines_t
& rArr
)
887 SwDoc
& rDoc
= rRg
.aStart
.GetNode().GetDoc();
888 SwRedlineTable::size_type nRedlPos
;
889 SwPosition
aSrchPos( rRg
.aStart
);
890 aSrchPos
.Adjust(SwNodeOffset(-1));
891 if( rDoc
.getIDocumentRedlineAccess().GetRedline( aSrchPos
, &nRedlPos
) && nRedlPos
)
893 else if( nRedlPos
>= rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() )
896 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
897 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
898 SwRedlineTable
& rRedlTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
901 SwRangeRedline
* pTmp
= rRedlTable
[ nRedlPos
];
903 auto [pRStt
, pREnd
] = pTmp
->StartEnd(); // SwPosition*
905 if( pRStt
->GetNode() < rRg
.aStart
.GetNode() )
907 if( pREnd
->GetNode() > rRg
.aStart
.GetNode() && pREnd
->GetNode() < rRg
.aEnd
.GetNode() )
909 // Create a copy and set the end of the original to the end of the MoveArea.
910 // The copy is moved too.
911 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
912 SwPosition
* pTmpPos
= pNewRedl
->Start();
913 pTmpPos
->Assign(rRg
.aStart
);
915 rArr
.emplace_back(pNewRedl
, rRg
.aStart
);
917 pTmpPos
= pTmp
->End();
918 pTmpPos
->Assign(rRg
.aEnd
);
920 else if( pREnd
->GetNode() == rRg
.aStart
.GetNode() )
922 SwPosition
* pTmpPos
= pTmp
->End();
923 pTmpPos
->Assign(rRg
.aEnd
);
926 else if( pRStt
->GetNode() < rRg
.aEnd
.GetNode() )
928 rRedlTable
.Remove( nRedlPos
-- );
929 if( pREnd
->GetNode() < rRg
.aEnd
.GetNode() ||
930 ( pREnd
->GetNode() == rRg
.aEnd
.GetNode() && !pREnd
->GetContentIndex()) )
933 rArr
.emplace_back( pTmp
, rRg
.aStart
);
938 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
939 SwPosition
* pTmpPos
= pNewRedl
->End();
940 pTmpPos
->Assign(rRg
.aEnd
);
942 rArr
.emplace_back( pNewRedl
, rRg
.aStart
);
944 pTmpPos
= pTmp
->Start();
945 pTmpPos
->Assign(rRg
.aEnd
);
946 rDoc
.getIDocumentRedlineAccess().AppendRedline( pTmp
, true );
952 } while( ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() );
953 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
956 void lcl_RestoreRedlines(SwDoc
& rDoc
, SwNodeOffset
const nInsPos
, SaveRedlines_t
& rArr
)
958 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
959 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
961 for(SaveRedline
& rSvRedLine
: rArr
)
963 rSvRedLine
.SetPos( nInsPos
);
964 IDocumentRedlineAccess::AppendResult
const result(
965 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true ));
966 if ( IDocumentRedlineAccess::AppendResult::APPENDED
== result
&&
967 rSvRedLine
.pRedl
->GetType() == RedlineType::Delete
)
969 UpdateFramesForAddDeleteRedline(rDoc
, *rSvRedLine
.pRedl
);
973 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
976 bool lcl_SaveFootnote( const SwNode
& rSttNd
, const SwNode
& rEndNd
,
977 const SwNode
& rInsPos
,
978 SwFootnoteIdxs
& rFootnoteArr
, SwFootnoteIdxs
& rSaveArr
,
979 std::optional
<sal_Int32
> oSttCnt
= std::nullopt
, std::optional
<sal_Int32
> oEndCnt
= std::nullopt
)
981 bool bUpdateFootnote
= false;
982 const SwNodes
& rNds
= rInsPos
.GetNodes();
983 const bool bDelFootnote
= rInsPos
.GetIndex() < rNds
.GetEndOfAutotext().GetIndex() &&
984 rSttNd
.GetIndex() >= rNds
.GetEndOfAutotext().GetIndex();
985 const bool bSaveFootnote
= !bDelFootnote
&&
986 rInsPos
.GetIndex() >= rNds
.GetEndOfExtras().GetIndex();
987 if( !rFootnoteArr
.empty() )
991 rFootnoteArr
.SeekEntry( rSttNd
, &nPos
);
992 SwTextFootnote
* pSrch
;
993 const SwNode
* pFootnoteNd
;
995 // Delete/save all that come after it
996 while( nPos
< rFootnoteArr
.size() && ( pFootnoteNd
=
997 &( pSrch
= rFootnoteArr
[ nPos
] )->GetTextNode())->GetIndex()
998 <= rEndNd
.GetIndex() )
1000 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
1001 if( ( oEndCnt
&& oSttCnt
)
1002 ? (( &rSttNd
== pFootnoteNd
&&
1003 *oSttCnt
> nFootnoteSttIdx
) ||
1004 ( &rEndNd
== pFootnoteNd
&&
1005 nFootnoteSttIdx
>= *oEndCnt
))
1006 : ( &rEndNd
== pFootnoteNd
))
1008 ++nPos
; // continue searching
1015 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1016 SwContentIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1017 rTextNd
.EraseText( aIdx
, 1 );
1021 pSrch
->DelFrames(nullptr);
1022 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1024 rSaveArr
.insert( pSrch
);
1026 bUpdateFootnote
= true;
1030 while( nPos
-- && ( pFootnoteNd
= &( pSrch
= rFootnoteArr
[ nPos
] )->
1031 GetTextNode())->GetIndex() >= rSttNd
.GetIndex() )
1033 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
1034 if( !oEndCnt
|| !oSttCnt
||
1035 ! (( &rSttNd
== pFootnoteNd
&&
1036 *oSttCnt
> nFootnoteSttIdx
) ||
1037 ( &rEndNd
== pFootnoteNd
&&
1038 nFootnoteSttIdx
>= *oEndCnt
)) )
1043 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1044 SwContentIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1045 rTextNd
.EraseText( aIdx
, 1 );
1049 pSrch
->DelFrames(nullptr);
1050 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1052 rSaveArr
.insert( pSrch
);
1054 bUpdateFootnote
= true;
1058 // When moving from redline section into document content section, e.g.
1059 // after loading a document with (delete-)redlines, the footnote array
1060 // has to be adjusted... (#i70572)
1063 SwNodeIndex
aIdx( rSttNd
);
1064 while( aIdx
< rEndNd
) // Check the moved section
1066 SwNode
* pNode
= &aIdx
.GetNode();
1067 if( pNode
->IsTextNode() ) // Looking for text nodes...
1069 SwpHints
*pHints
= pNode
->GetTextNode()->GetpSwpHints();
1070 if( pHints
&& pHints
->HasFootnote() ) //...with footnotes
1072 bUpdateFootnote
= true; // Heureka
1073 const size_t nCount
= pHints
->Count();
1074 for( size_t i
= 0; i
< nCount
; ++i
)
1076 SwTextAttr
*pAttr
= pHints
->Get( i
);
1077 if ( pAttr
->Which() == RES_TXTATR_FTN
)
1079 rSaveArr
.insert( static_cast<SwTextFootnote
*>(pAttr
) );
1087 return bUpdateFootnote
;
1090 bool lcl_MayOverwrite( const SwTextNode
*pNode
, const sal_Int32 nPos
)
1092 sal_Unicode
const cChr
= pNode
->GetText()[nPos
];
1095 case CH_TXTATR_BREAKWORD
:
1096 case CH_TXTATR_INWORD
:
1097 return !pNode
->GetTextAttrForCharAt(nPos
);// how could there be none?
1098 case CH_TXT_ATR_INPUTFIELDSTART
:
1099 case CH_TXT_ATR_INPUTFIELDEND
:
1100 case CH_TXT_ATR_FIELDSTART
:
1101 case CH_TXT_ATR_FIELDSEP
:
1102 case CH_TXT_ATR_FIELDEND
:
1103 case CH_TXT_ATR_FORMELEMENT
:
1110 void lcl_SkipAttr( const SwTextNode
*pNode
, SwPosition
&rIdx
, sal_Int32
&rStart
)
1112 if( !lcl_MayOverwrite( pNode
, rStart
) )
1114 // skip all special attributes
1116 rIdx
.AdjustContent(+1);
1117 rStart
= rIdx
.GetContentIndex();
1118 } while (rStart
< pNode
->GetText().getLength()
1119 && !lcl_MayOverwrite(pNode
, rStart
) );
1123 bool lcl_GetTokenToParaBreak( OUString
& rStr
, OUString
& rRet
, bool bRegExpRplc
)
1128 static const OUStringLiteral
sPara(u
"\\n");
1131 nPos
= rStr
.indexOf( sPara
, nPos
);
1136 // Has this been escaped?
1137 if( nPos
&& '\\' == rStr
[nPos
-1])
1140 if( nPos
>= rStr
.getLength() )
1147 rRet
= rStr
.copy( 0, nPos
);
1148 rStr
= rStr
.copy( nPos
+ sPara
.getLength() );
1159 namespace //local functions originally from docfmt.cxx
1162 bool lcl_ApplyOtherSet(
1163 SwContentNode
& rNode
,
1164 SwHistory
*const pHistory
,
1165 SfxItemSet
const& rOtherSet
,
1166 SfxItemSet
const& rFirstSet
,
1167 SfxItemSet
const& rPropsSet
,
1168 SwRootFrame
const*const pLayout
,
1169 SwNodeIndex
*const o_pIndex
= nullptr)
1171 assert(rOtherSet
.Count());
1174 SwTextNode
*const pTNd
= rNode
.GetTextNode();
1175 sw::MergedPara
const* pMerged(nullptr);
1176 if (pLayout
&& pLayout
->HasMergedParas() && pTNd
)
1178 SwTextFrame
const*const pTextFrame(static_cast<SwTextFrame
const*>(
1179 pTNd
->getLayoutFrame(pLayout
)));
1182 pMerged
= pTextFrame
->GetMergedPara();
1186 if (rFirstSet
.Count())
1190 SwRegHistory
aRegH(pMerged
->pFirstNode
, *pMerged
->pFirstNode
, pHistory
);
1191 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1195 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1198 if (rPropsSet
.Count())
1202 SwRegHistory
aRegH(pMerged
->pParaPropsNode
, *pMerged
->pParaPropsNode
, pHistory
);
1203 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1207 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1212 *o_pIndex
= *pMerged
->pLastNode
; // skip hidden
1217 // input cursor can't be on hidden node, and iteration skips them
1218 assert(!pLayout
|| !pLayout
->HasMergedParas()
1219 || rNode
.GetRedlineMergeFlag() != SwNode::Merge::Hidden
);
1225 SwRegHistory
aRegH(&rNode
, rNode
, pHistory
);
1226 ret
= rNode
.SetAttr( rOtherSet
);
1230 ret
= rNode
.SetAttr( rOtherSet
);
1236 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1238 // set format redline with extra data for lcl_InsAttr()
1239 void lcl_SetRedline(
1243 std::unique_ptr
<SwRedlineExtraData_FormatColl
> xExtra
;
1245 // check existing redline on the same range, and use its extra data, if it exists
1246 SwRedlineTable::size_type nRedlPos
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos(
1247 rRg
.Start()->GetNode(), RedlineType::Format
);
1248 if( SwRedlineTable::npos
!= nRedlPos
)
1250 const SwPosition
*pRStt
, *pREnd
;
1252 SwRangeRedline
* pTmp
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
1253 pRStt
= pTmp
->Start();
1254 pREnd
= pTmp
->End();
1255 SwComparePosition eCompare
= ComparePosition( *rRg
.Start(), *rRg
.End(), *pRStt
, *pREnd
);
1256 if ( eCompare
== SwComparePosition::Inside
|| eCompare
== SwComparePosition::Equal
)
1258 if (pTmp
->GetExtraData())
1260 const SwRedlineExtraData
* pExtraData
= pTmp
->GetExtraData();
1261 const SwRedlineExtraData_FormatColl
* pFormattingChanges
=
1262 dynamic_cast<const SwRedlineExtraData_FormatColl
*>(pExtraData
);
1263 // Check if the extra data is of type 'formatting changes'
1264 if (pFormattingChanges
)
1266 // Get the item set that holds all the changes properties
1267 const SfxItemSet
*pChangesSet
= pFormattingChanges
->GetItemSet();
1268 xExtra
.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX
, pChangesSet
));
1273 } while( pRStt
<= rRg
.Start() && ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
1276 SwRangeRedline
* pRedline
= new SwRangeRedline( RedlineType::Format
, rRg
);
1277 auto const result(rDoc
.getIDocumentRedlineAccess().AppendRedline( pRedline
, true));
1278 // store original text attributes to reject formatting change
1279 if (IDocumentRedlineAccess::AppendResult::IGNORED
== result
)
1282 // no existing format redline in the range
1285 // Apply the first character's attributes to the ReplaceText
1286 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
1287 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( rDoc
.GetAttrPool() );
1288 SwTextNode
* pNode
= rRg
.Start()->GetNode().GetTextNode();
1289 pNode
->GetParaAttr( aSet
, rRg
.Start()->GetContentIndex() + 1, rRg
.End()->GetContentIndex() );
1291 aSet
.ClearItem( RES_TXTATR_REFMARK
);
1292 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
1293 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
1294 aSet
.ClearItem( RES_TXTATR_INETFMT
);
1295 aSet
.ClearItem( RES_TXTATR_META
);
1296 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
1298 // After GetParaAttr aSet can contain INVALID_POOL_ITEM items, e.g. RES_TXTATR_CHARFMT
1299 // and (a copy of) this SfxItemSet can be passed to MSWordExportBase::OutputItemSet
1300 // which doesn't handle INVALID_POOL_ITEM items so clear InvalidItems here
1301 aSet
.ClearInvalidItems();
1303 xExtra
.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX
, &aSet
));
1308 pRedline
->SetExtraData(xExtra
.get() );
1312 // create format redline(s) for the given range:
1313 // to track the original formatting stored in the
1314 // hints, create redlines for all parts of the
1315 // range partitioned by boundaries of the hints.
1316 void lcl_SetRedlines(
1320 SwNodeIndex
aIdx( rRg
.Start()->GetNode() );
1321 const SwNodeIndex
aEndNd( rRg
.End()->GetNode() );
1322 while( aIdx
<= aEndNd
)
1324 SwTextNode
*pNode
= aIdx
.GetNode().GetTextNode();
1327 const sal_Int32 nStart
= aIdx
== rRg
.Start()->GetNode()
1328 ? rRg
.Start()->GetContentIndex()
1330 const sal_Int32 nEnd
= aIdx
< aEndNd
1331 ? pNode
->GetText().getLength()
1332 : rRg
.End()->GetContentIndex();
1334 if( SwpHints
*pHints
= pNode
->GetpSwpHints() )
1336 const size_t nCount
= pHints
->Count();
1337 sal_Int32 nRedEnd
= nStart
;
1338 for( size_t i
= 0; i
< nCount
; ++i
)
1340 SwTextAttr
*pAttr
= pHints
->Get( i
);
1342 if ( pAttr
->GetStart() > nEnd
)
1344 break; // after the range
1347 if ( !pAttr
->GetEnd() || *pAttr
->GetEnd() < nStart
)
1349 continue; // before the range
1352 // range part before the hint
1353 if ( nRedEnd
< pAttr
->GetStart() )
1355 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, pAttr
->GetStart() );
1356 lcl_SetRedline(rDoc
, aPam
);
1359 // range part at the hint
1360 sal_Int32 nRedStart
= std::max(pAttr
->GetStart(), nStart
);
1361 nRedEnd
= std::min(*pAttr
->GetEnd(), nEnd
);
1362 SwPaM
aPam2( *pNode
, nRedStart
, *pNode
, nRedEnd
);
1363 lcl_SetRedline(rDoc
, aPam2
);
1366 // range part after the last hint
1367 if ( nRedEnd
< nEnd
)
1369 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, nEnd
);
1370 lcl_SetRedline(rDoc
, aPam
);
1375 SwPaM
aPam( *pNode
, nStart
, *pNode
, nEnd
);
1376 lcl_SetRedline(rDoc
, aPam
);
1383 /// Insert Hints according to content types;
1384 // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1389 const SfxItemSet
& rChgSet
,
1390 const SetAttrMode nFlags
,
1391 SwUndoAttr
*const pUndo
,
1392 SwRootFrame
const*const pLayout
,
1393 SwTextAttr
**ppNewTextAttr
)
1395 // Divide the Sets (for selections in Nodes)
1396 const SfxItemSet
* pCharSet
= nullptr;
1397 const SfxItemSet
* pOtherSet
= nullptr;
1398 bool bDelete
= false;
1399 bool bCharAttr
= false;
1400 bool bOtherAttr
= false;
1402 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1403 if ( 1 == rChgSet
.Count() )
1405 SfxItemIter
aIter( rChgSet
);
1406 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
1407 if (pItem
&& !IsInvalidItem(pItem
))
1409 const sal_uInt16 nWhich
= pItem
->Which();
1411 if ( isCHRATR(nWhich
) ||
1412 (RES_TXTATR_CHARFMT
== nWhich
) ||
1413 (RES_TXTATR_INETFMT
== nWhich
) ||
1414 (RES_TXTATR_AUTOFMT
== nWhich
) ||
1415 (RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
) )
1417 pCharSet
= &rChgSet
;
1421 if ( isPARATR(nWhich
)
1422 || isPARATR_LIST(nWhich
)
1425 || isUNKNOWNATR(nWhich
)
1426 || isDrawingLayerAttribute(nWhich
) )
1428 pOtherSet
= &rChgSet
;
1434 // Build new itemset if either
1435 // - rChgSet.Count() > 1 or
1436 // - The attribute in rChgSet does not belong to one of the above categories
1437 if ( !bCharAttr
&& !bOtherAttr
)
1439 SfxItemSet
* pTmpCharItemSet
= new SfxItemSetFixed
<
1440 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
1441 RES_TXTATR_AUTOFMT
, RES_TXTATR_CHARFMT
,
1442 RES_TXTATR_UNKNOWN_CONTAINER
,
1443 RES_TXTATR_UNKNOWN_CONTAINER
>( rDoc
.GetAttrPool() );
1445 SfxItemSet
* pTmpOtherItemSet
= new SfxItemSetFixed
<
1446 RES_PARATR_BEGIN
, RES_GRFATR_END
- 1,
1447 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1,
1448 // FillAttribute support:
1449 XATTR_FILL_FIRST
, XATTR_FILL_LAST
>( rDoc
.GetAttrPool() );
1451 pTmpCharItemSet
->Put( rChgSet
);
1452 pTmpOtherItemSet
->Put( rChgSet
);
1454 pCharSet
= pTmpCharItemSet
;
1455 pOtherSet
= pTmpOtherItemSet
;
1460 SwHistory
* pHistory
= pUndo
? &pUndo
->GetHistory() : nullptr;
1462 const SwPosition
*pStt
= rRg
.Start(), *pEnd
= rRg
.End();
1463 SwContentNode
* pNode
= pStt
->GetNode().GetContentNode();
1465 if( pNode
&& pNode
->IsTextNode() )
1467 // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
1468 if (pLayout
&& pNode
->GetTextNode()->getIDocumentSettingAccess()->
1469 get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING
))
1471 SwContentNode
* pEndNode
= pEnd
->GetNode().GetContentNode();
1472 SwContentNode
* pCurrentNode
= pEndNode
;
1473 auto nStartIndex
= pNode
->GetIndex();
1474 auto nEndIndex
= pEndNode
->GetIndex();
1475 SwNodeIndex
aIdx( pEnd
->GetNode() );
1476 while ( pCurrentNode
!= nullptr && nStartIndex
<= pCurrentNode
->GetIndex() )
1478 if (pCurrentNode
->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT
) &&
1479 // remove character formatting only on wholly selected paragraphs
1480 (nStartIndex
< pCurrentNode
->GetIndex() || pStt
->GetContentIndex() == 0) &&
1481 (pCurrentNode
->GetIndex() < nEndIndex
|| pEnd
->GetContentIndex() == pEndNode
->Len()))
1483 pCurrentNode
->ResetAttr(RES_PARATR_LIST_AUTOFMT
);
1484 // reset also paragraph marker
1485 pCurrentNode
->GetTextNode()->RstTextAttr(pCurrentNode
->Len(), 1);
1487 pCurrentNode
= SwNodes::GoPrevious( &aIdx
);
1491 if (rRg
.IsInFrontOfLabel())
1493 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1496 pTextNd
= sw::GetParaPropsNode(*pLayout
, *pTextNd
);
1498 SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
1502 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1507 int nLevel
= pTextNd
->GetActualListLevel();
1512 if (nLevel
>= MAXLEVEL
)
1513 nLevel
= MAXLEVEL
- 1;
1515 SwNumFormat aNumFormat
= pNumRule
->Get(o3tl::narrowing
<sal_uInt16
>(nLevel
));
1516 SwCharFormat
* pCharFormat
=
1517 rDoc
.FindCharFormatByName(aNumFormat
.GetCharFormatName());
1522 pHistory
->Add(pCharFormat
->GetAttrSet(), *pCharFormat
);
1525 pCharFormat
->SetFormatAttr(*pCharSet
);
1532 // Attributes without an end do not have a range
1533 if ( !bCharAttr
&& !bOtherAttr
)
1535 SfxItemSetFixed
<RES_TXTATR_NOEND_BEGIN
, RES_TXTATR_NOEND_END
-1>
1536 aTextSet( rDoc
.GetAttrPool() );
1537 aTextSet
.Put( rChgSet
);
1538 if( aTextSet
.Count() )
1540 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1541 bRet
= history
.InsertItems(
1542 aTextSet
, pStt
->GetContentIndex(), pStt
->GetContentIndex(), nFlags
, /*ppNewTextAttr*/nullptr ) || bRet
;
1544 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1545 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1547 SwPaM
aPam( pStt
->GetNode(), pStt
->GetContentIndex()-1,
1548 pStt
->GetNode(), pStt
->GetContentIndex() );
1551 pUndo
->SaveRedlineData( aPam
, true );
1553 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1554 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
1556 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1561 // TextAttributes with an end never expand their range
1562 if ( !bCharAttr
&& !bOtherAttr
)
1564 // CharFormat and URL attributes are treated separately!
1565 // TEST_TEMP ToDo: AutoFormat!
1567 RES_TXTATR_REFMARK
, RES_TXTATR_METAFIELD
,
1568 RES_TXTATR_CJK_RUBY
, RES_TXTATR_CJK_RUBY
,
1569 RES_TXTATR_INPUTFIELD
, RES_TXTATR_CONTENTCONTROL
>
1570 aTextSet(rDoc
.GetAttrPool());
1572 aTextSet
.Put( rChgSet
);
1573 if( aTextSet
.Count() )
1575 const sal_Int32 nInsCnt
= pStt
->GetContentIndex();
1576 const sal_Int32 nEnd
= pStt
->GetNode() == pEnd
->GetNode()
1577 ? pEnd
->GetContentIndex()
1579 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1580 bRet
= history
.InsertItems( aTextSet
, nInsCnt
, nEnd
, nFlags
, ppNewTextAttr
)
1583 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1584 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1586 // Was text content inserted? (RefMark/TOXMarks without an end)
1587 bool bTextIns
= nInsCnt
!= pStt
->GetContentIndex();
1588 // Was content inserted or set over the selection?
1589 SwPaM
aPam( pStt
->GetNode(), bTextIns
? nInsCnt
+ 1 : nEnd
,
1590 pStt
->GetNode(), nInsCnt
);
1592 pUndo
->SaveRedlineData( aPam
, bTextIns
);
1594 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1595 rDoc
.getIDocumentRedlineAccess().AppendRedline(
1597 bTextIns
? RedlineType::Insert
: RedlineType::Format
, aPam
),
1600 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1606 // We always have to set the auto flag for PageDescs that are set at the Node!
1607 if( pOtherSet
&& pOtherSet
->Count() )
1609 SwTableNode
* pTableNd
;
1610 const SwFormatPageDesc
* pDesc
= pOtherSet
->GetItemIfSet( RES_PAGEDESC
, false );
1615 // Set auto flag. Only in the template it's without auto!
1616 SwFormatPageDesc
aNew( *pDesc
);
1618 // Tables now also know line breaks
1619 if( !(nFlags
& SetAttrMode::APICALL
) &&
1620 nullptr != ( pTableNd
= pNode
->FindTableNode() ) )
1622 SwTableNode
* pCurTableNd
= pTableNd
;
1623 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1624 pTableNd
= pCurTableNd
;
1626 // set the table format
1627 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1628 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1629 pFormat
->SetFormatAttr( aNew
);
1634 SwContentNode
* pFirstNode(pNode
);
1635 if (pLayout
&& pLayout
->HasMergedParas())
1637 pFirstNode
= sw::GetFirstAndLastNode(*pLayout
, pStt
->GetNode()).first
;
1639 SwRegHistory
aRegH( pFirstNode
, *pFirstNode
, pHistory
);
1640 bRet
= pFirstNode
->SetAttr( aNew
) || bRet
;
1644 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1645 // we know, that there is only one attribute in pOtherSet. We cannot
1646 // perform the following operations, instead we return:
1650 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_PAGEDESC
);
1651 if( !pOtherSet
->Count() )
1658 // Tables now also know line breaks
1659 const SvxFormatBreakItem
* pBreak
;
1660 if( pNode
&& !(nFlags
& SetAttrMode::APICALL
) &&
1661 nullptr != (pTableNd
= pNode
->FindTableNode() ) &&
1662 (pBreak
= pOtherSet
->GetItemIfSet( RES_BREAK
, false )) )
1664 SwTableNode
* pCurTableNd
= pTableNd
;
1665 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1666 pTableNd
= pCurTableNd
;
1668 // set the table format
1669 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1670 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1671 pFormat
->SetFormatAttr( *pBreak
);
1674 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1675 // we know, that there is only one attribute in pOtherSet. We cannot
1676 // perform the following operations, instead we return:
1680 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_BREAK
);
1681 if( !pOtherSet
->Count() )
1689 // If we have a PoolNumRule, create it if needed
1690 sal_uInt16 nPoolId
=0;
1691 const SwNumRuleItem
* pRule
= pOtherSet
->GetItemIfSet( RES_PARATR_NUMRULE
, false );
1693 !rDoc
.FindNumRulePtr( pRule
->GetValue() ) &&
1694 USHRT_MAX
!= (nPoolId
= SwStyleNameMapper::GetPoolIdFromUIName ( pRule
->GetValue(),
1695 SwGetPoolIdFromName::NumRule
)) )
1696 rDoc
.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId
);
1700 SfxItemSetFixed
<RES_PAGEDESC
, RES_BREAK
> firstSet(rDoc
.GetAttrPool());
1701 if (pOtherSet
&& pOtherSet
->Count())
1702 { // actually only RES_BREAK is possible here...
1703 firstSet
.Put(*pOtherSet
);
1706 <RES_PARATR_BEGIN
, RES_PAGEDESC
,
1707 RES_BREAK
+1, RES_FRMATR_END
,
1708 XATTR_FILL_FIRST
, XATTR_FILL_LAST
+1> propsSet(rDoc
.GetAttrPool());
1709 if (pOtherSet
&& pOtherSet
->Count())
1711 propsSet
.Put(*pOtherSet
);
1714 if( !rRg
.HasMark() ) // no range
1722 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1724 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1725 sal_Int32 nMkPos
, nPtPos
= pStt
->GetContentIndex();
1726 const OUString
& rStr
= pTextNd
->GetText();
1728 // Special case: if the Cursor is located within a URL attribute, we take over it's area
1729 SwTextAttr
const*const pURLAttr(
1730 pTextNd
->GetTextAttrAt(pStt
->GetContentIndex(), RES_TXTATR_INETFMT
));
1731 if (pURLAttr
&& !pURLAttr
->GetINetFormat().GetValue().isEmpty())
1733 nMkPos
= pURLAttr
->GetStart();
1734 nPtPos
= *pURLAttr
->End();
1738 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
1739 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1740 pTextNd
->GetText(), nPtPos
,
1741 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1742 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
1745 if( aBndry
.startPos
< nPtPos
&& nPtPos
< aBndry
.endPos
)
1747 nMkPos
= aBndry
.startPos
;
1748 nPtPos
= aBndry
.endPos
;
1751 nPtPos
= nMkPos
= pStt
->GetContentIndex();
1754 // Remove the overriding attributes from the SwpHintsArray,
1755 // if the selection spans across the whole paragraph.
1756 // These attributes are inserted as FormatAttributes and
1757 // never override the TextAttributes!
1758 if( !(nFlags
& SetAttrMode::DONTREPLACE
) &&
1759 pTextNd
->HasHints() && !nMkPos
&& nPtPos
== rStr
.getLength())
1763 // Save all attributes for the Undo.
1764 SwRegHistory
aRHst( *pTextNd
, pHistory
);
1765 pTextNd
->GetpSwpHints()->Register( &aRHst
);
1766 pTextNd
->RstTextAttr( 0, nPtPos
, 0, pCharSet
);
1767 if( pTextNd
->GetpSwpHints() )
1768 pTextNd
->GetpSwpHints()->DeRegister();
1771 pTextNd
->RstTextAttr( 0, nPtPos
, 0, pCharSet
);
1774 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1776 SwPaM
aPam( *pNode
, nMkPos
, *pNode
, nPtPos
);
1779 pUndo
->SaveRedlineData( aPam
, false );
1781 lcl_SetRedlines(rDoc
, aPam
);
1784 // the SwRegHistory inserts the attribute into the TextNode!
1785 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1787 bRet
= history
.InsertItems( *pCharSet
, nMkPos
, nPtPos
, nFlags
, /*ppNewTextAttr*/nullptr )
1791 if( pOtherSet
&& pOtherSet
->Count() )
1793 // Need to check for unique item for DrawingLayer items of type NameOrIndex
1794 // and evtl. correct that item to ensure unique names for that type. This call may
1795 // modify/correct entries inside of the given SfxItemSet
1796 SfxItemSet
aTempLocalCopy(*pOtherSet
);
1798 rDoc
.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy
);
1799 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, aTempLocalCopy
, firstSet
, propsSet
, pLayout
) || bRet
;
1806 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet
&& pCharSet
->Count() )
1809 pUndo
->SaveRedlineData( rRg
, false );
1811 lcl_SetRedlines(rDoc
, rRg
);
1815 sal_uLong nNodes
= 0;
1817 SwNodeIndex
aSt( rDoc
.GetNodes() );
1818 SwNodeIndex
aEnd( rDoc
.GetNodes() );
1819 SwContentIndex
aCntEnd( pEnd
->GetContentNode(), pEnd
->GetContentIndex() );
1823 const sal_Int32 nLen
= pNode
->Len();
1824 if( pStt
->GetNode() != pEnd
->GetNode() )
1825 aCntEnd
.Assign( pNode
, nLen
);
1827 if( pStt
->GetContentIndex() != 0 || aCntEnd
.GetIndex() != nLen
)
1829 // the SwRegHistory inserts the attribute into the TextNode!
1830 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1832 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1833 bRet
= history
.InsertItems(*pCharSet
,
1834 pStt
->GetContentIndex(), aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr)
1838 if( pOtherSet
&& pOtherSet
->Count() )
1840 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
) || bRet
;
1843 // Only selection in a Node.
1844 if( pStt
->GetNode() == pEnd
->GetNode() )
1850 aSt
.Assign( pStt
->GetNode(), +1 );
1853 aSt
= pStt
->GetNode();
1854 aCntEnd
.Assign(pEnd
->GetContentNode(), pEnd
->GetContentIndex()); // aEnd was changed!
1857 aSt
.Assign( pStt
->GetNode(), +1 );
1859 // aSt points to the first full Node now
1862 * The selection spans more than one Node.
1864 if( pStt
->GetNode() < pEnd
->GetNode() )
1866 pNode
= pEnd
->GetNode().GetContentNode();
1869 if( aCntEnd
.GetIndex() != pNode
->Len() )
1871 // the SwRegHistory inserts the attribute into the TextNode!
1872 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1874 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1875 (void)history
.InsertItems(*pCharSet
,
1876 0, aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr);
1879 if( pOtherSet
&& pOtherSet
->Count() )
1881 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
);
1885 aEnd
= pEnd
->GetNode();
1888 aEnd
.Assign( pEnd
->GetNode(), +1 );
1891 aEnd
= pEnd
->GetNode();
1894 aEnd
.Assign( pEnd
->GetNode(), +1 );
1896 // aEnd points BEHIND the last full node now
1898 /* Edit the fully selected Nodes. */
1899 // Reset all attributes from the set!
1900 if( pCharSet
&& pCharSet
->Count() && !( SetAttrMode::DONTREPLACE
& nFlags
) )
1902 ::sw::DocumentContentOperationsManager::ParaRstFormat
aPara(
1903 pStt
, pEnd
, pHistory
, pCharSet
, pLayout
);
1904 rDoc
.GetNodes().ForEach( aSt
, aEnd
, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr
, &aPara
);
1907 bool bCreateSwpHints
= pCharSet
&& (
1908 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_CHARFMT
, false ) ||
1909 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_INETFMT
, false ) );
1911 for (SwNodeIndex current
= aSt
; current
< aEnd
; ++current
)
1913 SwTextNode
*const pTNd
= current
.GetNode().GetTextNode();
1917 if (pLayout
&& pLayout
->HasMergedParas()
1918 && pTNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
1919 { // not really sure what to do here, but applying to hidden
1920 continue; // nodes doesn't make sense...
1925 SwRegHistory
aRegH( pTNd
, *pTNd
, pHistory
);
1927 if (pCharSet
&& pCharSet
->Count())
1929 if (SwpHints
*pSwpHints
= bCreateSwpHints
? &pTNd
->GetOrCreateSwpHints()
1930 : pTNd
->GetpSwpHints())
1932 pSwpHints
->Register( &aRegH
);
1935 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1937 // re-fetch as it may be deleted by SetAttr
1938 if (SwpHints
*pSwpHints
= pTNd
->GetpSwpHints())
1939 pSwpHints
->DeRegister();
1944 if (pCharSet
&& pCharSet
->Count())
1945 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1950 if (pOtherSet
&& pOtherSet
->Count())
1952 for (; aSt
< aEnd
; ++aSt
)
1954 pNode
= aSt
.GetNode().GetContentNode();
1958 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
, &aSt
);
1964 return (nNodes
!= 0) || bRet
;
1973 bool IsFieldmarkOverlap(SwPaM
const& rPaM
)
1975 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
1976 sw::CalcBreaks(Breaks
, rPaM
);
1977 return !Breaks
.empty();
1981 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc
& i_rSwdoc
) : m_rDoc( i_rSwdoc
)
1986 * Checks if rStart..rEnd mark a range that makes sense to copy.
1988 * IsMoveToFly means the copy is a move to create a fly
1989 * and so existing flys at the edge must not be copied.
1991 static bool IsEmptyRange(const SwPosition
& rStart
, const SwPosition
& rEnd
,
1992 SwCopyFlags
const flags
)
1995 { // check if a fly anchored there would be copied - then copy...
1996 return !IsDestroyFrameAnchoredAtChar(rStart
, rStart
, rEnd
,
1997 (flags
& SwCopyFlags::IsMoveToFly
)
1998 ? DelContentType::WriterfilterHack
|DelContentType::AllMask
1999 : DelContentType::AllMask
);
2003 return rEnd
< rStart
;
2007 // Copy an area into this document or into another document
2009 DocumentContentOperationsManager::CopyRange( SwPaM
& rPam
, SwPosition
& rPos
,
2010 SwCopyFlags
const flags
) const
2012 const SwPosition
*pStt
= rPam
.Start(), *pEnd
= rPam
.End();
2014 SwDoc
& rDoc
= rPos
.GetNode().GetDoc();
2015 bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
2017 // Catch if there's no copy to do
2018 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
))
2021 // Prevent copying into Flys that are anchored in the source range
2022 if (&rDoc
== &m_rDoc
&& (flags
& SwCopyFlags::CheckPosInFly
))
2024 // Correct the Start-/EndNode
2025 SwNodeOffset nStt
= pStt
->GetNodeIndex(),
2026 nEnd
= pEnd
->GetNodeIndex(),
2027 nDiff
= nEnd
- nStt
+1;
2028 SwNode
* pNd
= m_rDoc
.GetNodes()[ nStt
];
2029 if( pNd
->IsContentNode() && pStt
->GetContentIndex() )
2034 if( (pNd
= m_rDoc
.GetNodes()[ nEnd
])->IsContentNode() &&
2035 static_cast<SwContentNode
*>(pNd
)->Len() != pEnd
->GetContentIndex() )
2041 lcl_ChkFlyFly( rDoc
, nStt
, nEnd
, rPos
.GetNodeIndex() ) )
2047 SwPaM
* pRedlineRange
= nullptr;
2048 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2049 (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
2050 pRedlineRange
= new SwPaM( rPos
);
2052 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2056 if( &rDoc
!= &m_rDoc
)
2058 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
);
2060 else if( ! ( *pStt
<= rPos
&& rPos
< *pEnd
&&
2061 ( pStt
->GetNode() != pEnd
->GetNode() ||
2062 !pStt
->GetNode().IsTextNode() )) )
2064 // Copy to a position outside of the area, or copy a single TextNode
2065 // Do an ordinary copy
2066 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
);
2070 // Copy the range in itself
2071 assert(!"mst: this is assumed to be dead code");
2074 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
2077 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2078 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, *pRedlineRange
), true);
2080 rDoc
.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange
);
2081 delete pRedlineRange
;
2087 static auto GetCorrPosition(SwPaM
const& rPam
) -> SwPosition
2089 // tdf#152710 target position must be on node that survives deletion
2090 // so that PaMCorrAbs can invalidate SwUnoCursors properly
2091 return rPam
.GetPoint()->GetNode().IsContentNode()
2093 : rPam
.GetMark()->GetNode().IsContentNode()
2095 // this would be the result in SwNodes::RemoveNode()
2096 : SwPosition(rPam
.End()->GetNode(), SwNodeOffset(+1));
2099 /// Delete a full Section of the NodeArray.
2100 /// The passed Node is located somewhere in the designated Section.
2101 void DocumentContentOperationsManager::DeleteSection( SwNode
*pNode
)
2103 assert(pNode
&& "Didn't pass a Node.");
2105 SwStartNode
* pSttNd
= pNode
->IsStartNode() ? static_cast<SwStartNode
*>(pNode
)
2106 : pNode
->StartOfSectionNode();
2107 SwNodeIndex
aSttIdx( *pSttNd
), aEndIdx( *pNode
->EndOfSectionNode() );
2109 // delete all Flys, Bookmarks, ...
2110 DelFlyInRange( aSttIdx
.GetNode(), aEndIdx
.GetNode() );
2111 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( *pSttNd
, true, RedlineType::Any
);
2112 DelBookmarks(aSttIdx
.GetNode(), aEndIdx
.GetNode());
2115 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
2116 SwPaM
const range(aSttIdx
, aEndIdx
);
2117 SwPosition
const pos(GetCorrPosition(range
));
2118 ::PaMCorrAbs(range
, pos
);
2121 m_rDoc
.GetNodes().DelNodes( aSttIdx
, aEndIdx
.GetIndex() - aSttIdx
.GetIndex() + 1 );
2124 void DocumentContentOperationsManager::DeleteDummyChar(
2125 SwPosition
const& rPos
, sal_Unicode
const cDummy
)
2127 SwPaM
aPam(rPos
, rPos
);
2128 aPam
.GetPoint()->AdjustContent(+1);
2129 assert(aPam
.GetText().getLength() == 1 && aPam
.GetText()[0] == cDummy
);
2132 DeleteRangeImpl(aPam
, SwDeleteFlags::Default
);
2134 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2135 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2137 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2141 void DocumentContentOperationsManager::DeleteRange( SwPaM
& rPam
)
2143 lcl_DoWithBreaks(*this, rPam
, SwDeleteFlags::Default
, &DocumentContentOperationsManager::DeleteRangeImpl
);
2145 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2146 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2148 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2152 bool DocumentContentOperationsManager::DelFullPara( SwPaM
& rPam
)
2154 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
2155 const SwNode
* pNd
= &rStt
.GetNode();
2156 SwNodeOffset nSectDiff
= pNd
->StartOfSectionNode()->EndOfSectionIndex() -
2157 pNd
->StartOfSectionIndex();
2158 SwNodeOffset nNodeDiff
= rEnd
.GetNodeIndex() - rStt
.GetNodeIndex();
2160 if ( nSectDiff
-SwNodeOffset(2) <= nNodeDiff
|| m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2161 /* #i9185# Prevent getting the node after the end node (see below) */
2162 rEnd
.GetNodeIndex() + 1 == m_rDoc
.GetNodes().Count() )
2168 SwPaM
temp(rPam
, nullptr);
2169 if (!temp
.HasMark())
2173 if (SwTextNode
*const pNode
= temp
.Start()->GetNode().GetTextNode())
2174 { // rPam may not have nContent set but IsFieldmarkOverlap requires it
2175 temp
.Start()->AssignStartIndex(*pNode
);
2177 if (SwTextNode
*const pNode
= temp
.End()->GetNode().GetTextNode())
2179 temp
.End()->AssignEndIndex(*pNode
);
2181 if (sw::mark::IsFieldmarkOverlap(temp
))
2182 { // a bit of a problem: we want to completely remove the nodes
2183 // but then how can the CH_TXT_ATR survive?
2188 // Move hard page breaks to the following Node.
2189 bool bSavePageBreak
= false, bSavePageDesc
= false;
2191 /* #i9185# This would lead to a segmentation fault if not caught above. */
2192 SwNodeOffset nNextNd
= rEnd
.GetNodeIndex() + 1;
2193 SwTableNode
*const pTableNd
= m_rDoc
.GetNodes()[ nNextNd
]->GetTableNode();
2195 if( pTableNd
&& pNd
->IsContentNode() )
2197 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
2200 const SfxPoolItem
*pItem
;
2201 const SfxItemSet
* pSet
= static_cast<const SwContentNode
*>(pNd
)->GetpSwAttrSet();
2202 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_PAGEDESC
,
2205 pTableFormat
->SetFormatAttr( *pItem
);
2206 bSavePageDesc
= true;
2209 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_BREAK
,
2212 pTableFormat
->SetFormatAttr( *pItem
);
2213 bSavePageBreak
= true;
2218 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2221 if( !rPam
.HasMark() )
2223 else if( rPam
.GetPoint() == &rStt
)
2225 rPam
.GetPoint()->Adjust(SwNodeOffset(1));
2227 SwContentNode
*pTmpNode
= rPam
.GetPoint()->GetNode().GetContentNode();
2228 bool bGoNext
= (nullptr == pTmpNode
);
2230 if (rPam
.GetMark()->GetContentNode())
2231 rPam
.GetMark()->SetContent( 0 );
2233 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2235 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
2237 SwPosition
aTmpPos( *aDelPam
.GetPoint() );
2240 m_rDoc
.GetNodes().GoNext( &aTmpPos
);
2242 ::PaMCorrAbs( aDelPam
, aTmpPos
);
2245 std::unique_ptr
<SwUndoDelete
> pUndo(new SwUndoDelete(aDelPam
, SwDeleteFlags::Default
, true));
2247 *rPam
.GetPoint() = *aDelPam
.GetPoint();
2248 pUndo
->SetPgBrkFlags( bSavePageBreak
, bSavePageDesc
);
2249 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2254 SwNodeRange
aRg( rStt
.GetNode(), rEnd
.GetNode() );
2255 rPam
.Normalize(false);
2257 // Try to move past the End
2258 if( !rPam
.Move( fnMoveForward
, GoInNode
) )
2260 // Fair enough, at the Beginning then
2262 if( !rPam
.Move( fnMoveBackward
, GoInNode
))
2264 SAL_WARN("sw.core", "DelFullPara: no more Nodes");
2269 // must delete all fieldmarks before CorrAbs(), or they'll remain
2270 // moved to wrong node without their CH_TXT_ATR_FIELD*
2271 // (note: deleteMarks() doesn't help here, in case of partially
2272 // selected fieldmarks; let's delete these as re-inserting their chars
2273 // elsewhere looks difficult)
2274 ::std::set
<::sw::mark::IFieldmark
*> fieldmarks
;
2275 for (SwNodeIndex i
= aRg
.aStart
; i
<= aRg
.aEnd
; ++i
)
2277 if (SwTextNode
*const pTextNode
= i
.GetNode().GetTextNode())
2279 for (sal_Int32 j
= 0; j
< pTextNode
->GetText().getLength(); ++j
)
2281 switch (pTextNode
->GetText()[j
])
2283 case CH_TXT_ATR_FIELDSTART
:
2284 case CH_TXT_ATR_FIELDEND
:
2285 fieldmarks
.insert(m_rDoc
.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode
, j
)));
2287 case CH_TXT_ATR_FIELDSEP
:
2288 fieldmarks
.insert(m_rDoc
.getIDocumentMarkAccess()->getInnerFieldmarkFor(SwPosition(*pTextNode
, j
)));
2294 for (auto const pFieldMark
: fieldmarks
)
2296 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pFieldMark
);
2299 // move bookmarks, redlines etc.
2300 if (aRg
.aStart
== aRg
.aEnd
) // only first CorrAbs variant handles this
2302 m_rDoc
.CorrAbs( aRg
.aStart
.GetNode(), *rPam
.GetPoint(), 0, true );
2306 SwDoc::CorrAbs( aRg
.aStart
, aRg
.aEnd
, *rPam
.GetPoint(), true );
2309 // What's with Flys?
2311 // If there are FlyFrames left, delete these too
2312 for( size_t n
= 0; n
< m_rDoc
.GetSpzFrameFormats()->size(); ++n
)
2314 sw::SpzFrameFormat
* pFly
= (*m_rDoc
.GetSpzFrameFormats())[n
];
2315 const SwFormatAnchor
* pAnchor
= &pFly
->GetAnchor();
2316 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
2318 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
2319 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
2320 // note: here use <= not < like in
2321 // IsDestroyFrameAnchoredAtChar() because of the increment
2322 // of rPam in the bDoesUndo path above!
2323 aRg
.aStart
<= *pAnchorNode
&& *pAnchorNode
<= aRg
.aEnd
.GetNode() )
2325 m_rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFly
);
2332 m_rDoc
.GetNodes().Delete( aRg
.aStart
, nNodeDiff
+1 );
2335 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2336 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2338 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2341 m_rDoc
.getIDocumentState().SetModified();
2346 bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM
& rPam
, SwDeleteFlags
const flags
)
2348 if ( lcl_StrLenOverflow( rPam
) )
2351 bool const ret
= lcl_DoWithBreaks( *this, rPam
, flags
, (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
2352 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
2353 : &DocumentContentOperationsManager::DeleteAndJoinImpl
);
2358 // It seems that this is mostly used by SwDoc internals; the only
2359 // way to call this from the outside seems to be the special case in
2360 // SwDoc::CopyRange (but I have not managed to actually hit that case).
2361 bool DocumentContentOperationsManager::MoveRange( SwPaM
& rPaM
, SwPosition
& rPos
, SwMoveFlags eMvFlags
)
2363 // nothing moved: return
2364 const SwPosition
*pStt
= rPaM
.Start(), *pEnd
= rPaM
.End();
2365 if( !rPaM
.HasMark() || *pStt
>= *pEnd
|| (*pStt
<= rPos
&& rPos
< *pEnd
))
2368 assert(!sw::mark::IsFieldmarkOverlap(rPaM
)); // probably an invalid redline was created?
2370 // Save the paragraph anchored Flys, so that they can be moved.
2371 SaveFlyArr aSaveFlyArr
;
2372 SaveFlyInRange( rPaM
, rPos
, aSaveFlyArr
, bool( SwMoveFlags::ALLFLYS
& eMvFlags
) );
2374 // save redlines (if DOC_MOVEREDLINES is used)
2375 SaveRedlines_t aSaveRedl
;
2376 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2378 lcl_SaveRedlines( rPaM
, aSaveRedl
);
2380 // #i17764# unfortunately, code below relies on undos being
2381 // in a particular order, and presence of bookmarks
2382 // will change this order. Hence, we delete bookmarks
2383 // here without undo.
2384 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
2389 pStt
->GetContentIndex(),
2390 pEnd
->GetContentIndex());
2393 bool bUpdateFootnote
= false;
2394 SwFootnoteIdxs aTmpFntIdx
;
2396 std::unique_ptr
<SwUndoMove
> pUndoMove
;
2397 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2399 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2400 pUndoMove
.reset(new SwUndoMove( rPaM
, rPos
));
2401 pUndoMove
->SetMoveRedlines( eMvFlags
== SwMoveFlags::REDLINES
);
2405 bUpdateFootnote
= lcl_SaveFootnote( pStt
->GetNode(), pEnd
->GetNode(), rPos
.GetNode(),
2406 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
,
2407 pStt
->GetContentIndex(), pEnd
->GetContentIndex() );
2410 bool bSplit
= false;
2411 SwPaM
aSavePam( rPos
, rPos
);
2413 // Move the SPoint to the beginning of the range
2414 if( rPaM
.GetPoint() == pEnd
)
2417 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
2418 SwTextNode
* pSrcNd
= rPaM
.GetPoint()->GetNode().GetTextNode();
2419 bool bCorrSavePam
= pSrcNd
&& pStt
->GetNode() != pEnd
->GetNode();
2421 // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
2422 // However, this does not update the cursor. So we create a TextNode to keep
2423 // updating the indices. After the Move the Node is optionally deleted.
2424 SwTextNode
* pTNd
= rPos
.GetNode().GetTextNode();
2425 if( pTNd
&& rPaM
.GetPoint()->GetNode() != rPaM
.GetMark()->GetNode() &&
2426 ( rPos
.GetContentIndex() || ( pTNd
->Len() && bCorrSavePam
)) )
2429 const sal_Int32 nMkContent
= rPaM
.GetMark()->GetContentIndex();
2431 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
2432 pContentStore
->Save( m_rDoc
, rPos
.GetNodeIndex(), rPos
.GetContentIndex(), true );
2434 SwTextNode
* pOrigNode
= pTNd
;
2435 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2436 *aSavePam
.GetPoint() == rPos
);
2437 assert(aSavePam
.GetPoint()->GetContentNode() == pOrigNode
);
2438 assert(aSavePam
.GetPoint()->GetNode() == rPos
.GetNode());
2439 assert(rPos
.GetNodeIndex() == pOrigNode
->GetIndex());
2441 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool)> restoreFunc(
2442 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool)
2444 if (!pContentStore
->Empty())
2446 pContentStore
->Restore(m_rDoc
, pOrigNode
->GetIndex()-SwNodeOffset(1), 0, true, false, eMode
);
2449 pTNd
->SplitContentNode(rPos
, &restoreFunc
);
2451 //A new node was inserted before the orig pTNd and the content up to
2452 //rPos moved into it. The old node is returned with the remainder
2453 //of the content in it.
2455 //aSavePam was created with rPos, it continues to point to the
2456 //old node, but with the *original* content index into the node.
2457 //Seeing as all the orignode content before that index has
2458 //been removed, the new index into the original node should now be set
2459 //to 0 and the content index of rPos should also be adapted to the
2461 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2462 *aSavePam
.GetPoint() == rPos
);
2463 assert(aSavePam
.GetPoint()->GetContentNode() == pOrigNode
);
2464 assert(aSavePam
.GetPoint()->GetNode() == rPos
.GetNode());
2465 assert(rPos
.GetNodeIndex() == pOrigNode
->GetIndex());
2466 aSavePam
.GetPoint()->SetContent(0);
2467 rPos
= *aSavePam
.GetMark() = *aSavePam
.GetPoint();
2470 if( rPos
.GetNode() == rPaM
.GetMark()->GetNode() )
2472 rPaM
.GetMark()->Assign( rPos
.GetNodeIndex() - SwNodeOffset(1) );
2473 rPaM
.GetMark()->SetContent( nMkContent
);
2477 // Put back the Pam by one "content"; so that it's always outside of
2478 // the manipulated range.
2479 // tdf#99692 don't Move() back if that would end up in another node
2480 // because moving backward is not necessarily the inverse of forward then.
2481 // (but do Move() back if we have split the node)
2482 const bool bNullContent
= !bSplit
&& aSavePam
.GetPoint()->GetContentIndex() == 0;
2485 aSavePam
.GetPoint()->Adjust(SwNodeOffset(-1));
2489 bool const success(aSavePam
.Move(fnMoveBackward
, GoInContent
));
2494 // Copy all Bookmarks that are within the Move range into an array,
2495 // that saves the position as an offset.
2496 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2501 pStt
->GetContentIndex(),
2502 pEnd
->GetContentIndex());
2504 // If there is no range anymore due to the above deletions (e.g. the
2505 // footnotes got deleted), it's still a valid Move!
2506 if( *rPaM
.GetPoint() != *rPaM
.GetMark() )
2508 // now do the actual move
2509 m_rDoc
.GetNodes().MoveRange( rPaM
, rPos
, m_rDoc
.GetNodes() );
2511 // after a MoveRange() the Mark is deleted
2512 if ( rPaM
.HasMark() ) // => no Move occurred!
2520 OSL_ENSURE( *aSavePam
.GetMark() == rPos
||
2521 ( aSavePam
.GetMark()->GetNode().GetContentNode() == nullptr ),
2522 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2523 *aSavePam
.GetMark() = rPos
;
2525 rPaM
.SetMark(); // create a Sel. around the new range
2526 pTNd
= aSavePam
.GetPointNode().GetTextNode();
2527 assert(!m_rDoc
.GetIDocumentUndoRedo().DoesUndo());
2528 bool bRemove
= true;
2529 // Do two Nodes have to be joined at the SavePam?
2532 if (pTNd
->CanJoinNext())
2534 // Always join next, because <pTNd> has to stay as it is.
2535 // A join previous from its next would more or less delete <pTNd>
2542 aSavePam
.GetPoint()->Adjust(SwNodeOffset(1));
2544 else if (bRemove
) // No move forward after joining with next paragraph
2546 aSavePam
.Move( fnMoveForward
, GoInContent
);
2549 // Insert the Bookmarks back into the Document.
2550 *rPaM
.GetMark() = *aSavePam
.Start();
2551 for(auto& rBkmk
: aSaveBkmks
)
2554 rPaM
.GetMark()->GetNode(),
2555 rPaM
.GetMark()->GetContentIndex());
2556 *rPaM
.GetPoint() = *aSavePam
.End();
2558 // Move the Flys to the new position.
2559 // note: rPos is at the end here; can't really tell flys that used to be
2560 // at the start of rPam from flys that used to be at the end of rPam
2561 // unfortunately, so some of them are going to end up with wrong anchor...
2562 RestFlyInRange( aSaveFlyArr
, *rPaM
.Start(), &rPos
.GetNode() );
2564 // restore redlines (if DOC_MOVEREDLINES is used)
2565 if( !aSaveRedl
.empty() )
2567 lcl_RestoreRedlines( m_rDoc
, *aSavePam
.Start(), aSaveRedl
);
2570 if( bUpdateFootnote
)
2572 if( !aTmpFntIdx
.empty() )
2574 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2578 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2581 m_rDoc
.getIDocumentState().SetModified();
2585 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange
& rRange
, SwNode
& rDestNd
,
2586 SwMoveFlags eMvFlags
)
2588 // Moves all Nodes to the new position.
2589 // Bookmarks are moved too (currently without Undo support).
2591 // If footnotes are being moved to the special section, remove them now.
2593 // Or else delete the Frames for all footnotes that are being moved
2594 // and have it rebuild after the Move (footnotes can change pages).
2595 // Additionally we have to correct the FootnoteIdx array's sorting.
2596 bool bUpdateFootnote
= false;
2597 SwFootnoteIdxs aTmpFntIdx
;
2599 std::unique_ptr
<SwUndoMove
> pUndo
;
2600 if ((SwMoveFlags::CREATEUNDOOBJ
& eMvFlags
) && m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2602 pUndo
.reset(new SwUndoMove( m_rDoc
, rRange
, rDestNd
));
2606 bUpdateFootnote
= lcl_SaveFootnote( rRange
.aStart
.GetNode(), rRange
.aEnd
.GetNode(), rDestNd
,
2607 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
);
2610 SaveRedlines_t aSaveRedl
;
2611 std::vector
<SwRangeRedline
*> aSavRedlInsPosArr
;
2612 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2614 lcl_SaveRedlines( rRange
, aSaveRedl
);
2616 // Find all RedLines that end at the InsPos.
2617 // These have to be moved back to the "old" position after the Move.
2618 SwRedlineTable::size_type nRedlPos
= m_rDoc
.getIDocumentRedlineAccess().GetRedlinePos( rDestNd
, RedlineType::Any
);
2619 if( SwRedlineTable::npos
!= nRedlPos
)
2621 const SwPosition
*pRStt
, *pREnd
;
2623 SwRangeRedline
* pTmp
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
2624 pRStt
= pTmp
->Start();
2625 pREnd
= pTmp
->End();
2626 if( pREnd
->GetNode() == rDestNd
&& pRStt
->GetNode() < rDestNd
)
2628 aSavRedlInsPosArr
.push_back( pTmp
);
2630 } while( pRStt
->GetNode() < rDestNd
&& ++nRedlPos
< m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
2634 // Copy all Bookmarks that are within the Move range into an array
2635 // that stores all references to positions as an offset.
2636 // The final mapping happens after the Move.
2637 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2638 DelBookmarks(rRange
.aStart
.GetNode(), rRange
.aEnd
.GetNode(), &aSaveBkmks
);
2640 // Save the paragraph-bound Flys, so that they can be moved.
2641 SaveFlyArr aSaveFlyArr
;
2642 if( !m_rDoc
.GetSpzFrameFormats()->empty() )
2643 SaveFlyInRange( rRange
, aSaveFlyArr
);
2645 // Set it to before the Position, so that it cannot be moved further.
2646 SwNodeIndex
aIdx( rDestNd
, -1 );
2648 std::optional
<SwNodeIndex
> oSaveInsPos
;
2650 oSaveInsPos
.emplace(rRange
.aStart
, -1 );
2653 bool bNoDelFrames
= bool(SwMoveFlags::NO_DELFRMS
& eMvFlags
);
2654 if( m_rDoc
.GetNodes().MoveNodes( rRange
, m_rDoc
.GetNodes(), rDestNd
, !bNoDelFrames
) )
2656 ++aIdx
; // again back to old position
2662 aIdx
= rRange
.aStart
;
2666 // move the Flys to the new position
2667 if( !aSaveFlyArr
.empty() )
2669 SwPosition
const tmp(aIdx
);
2670 RestFlyInRange(aSaveFlyArr
, tmp
, nullptr);
2673 // Add the Bookmarks back to the Document
2674 for(auto& rBkmk
: aSaveBkmks
)
2675 rBkmk
.SetInDoc(&m_rDoc
, aIdx
.GetNode());
2677 if( !aSavRedlInsPosArr
.empty() )
2679 for(SwRangeRedline
* pTmp
: aSavRedlInsPosArr
)
2681 if( m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp
) )
2683 SwPosition
* pEnd
= pTmp
->End();
2689 if( !aSaveRedl
.empty() )
2690 lcl_RestoreRedlines( m_rDoc
, aIdx
.GetIndex(), aSaveRedl
);
2694 pUndo
->SetDestRange( aIdx
.GetNode(), rDestNd
, *oSaveInsPos
);
2695 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2698 oSaveInsPos
.reset();
2700 if( bUpdateFootnote
)
2702 if( !aTmpFntIdx
.empty() )
2704 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2708 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2711 m_rDoc
.getIDocumentState().SetModified();
2715 void DocumentContentOperationsManager::MoveAndJoin( SwPaM
& rPaM
, SwPosition
& rPos
)
2717 SwNodeIndex
aIdx( rPaM
.Start()->GetNode() );
2718 bool bJoinText
= aIdx
.GetNode().IsTextNode();
2719 bool bOneNode
= rPaM
.GetPoint()->GetNode() == rPaM
.GetMark()->GetNode();
2720 --aIdx
; // in front of the move area!
2722 bool bRet
= MoveRange( rPaM
, rPos
, SwMoveFlags::DEFAULT
);
2723 if( !bRet
|| bOneNode
)
2728 SwTextNode
* pTextNd
= aIdx
.GetNode().GetTextNode();
2729 SwNodeIndex
aNxtIdx( aIdx
);
2730 if( pTextNd
&& pTextNd
->CanJoinNext( &aNxtIdx
) )
2732 { // Block so SwContentIndex into node is deleted before Join
2733 m_rDoc
.CorrRel( aNxtIdx
.GetNode(),
2734 SwPosition( *pTextNd
, pTextNd
->GetText().getLength() ),
2737 pTextNd
->JoinNext();
2741 // Overwrite only uses the point of the PaM, the mark is ignored; characters
2742 // are replaced from point until the end of the node; at the end of the node,
2743 // characters are inserted.
2744 bool DocumentContentOperationsManager::Overwrite( const SwPaM
&rRg
, const OUString
&rStr
)
2746 assert(rStr
.getLength());
2747 SwPosition
& rPt
= *const_cast<SwPosition
*>(rRg
.GetPoint());
2748 if( m_rDoc
.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2750 if( 1 == rStr
.getLength() )
2751 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPt
, rStr
[ 0 ] );
2752 m_rDoc
.DeleteAutoCorrExceptWord();
2755 SwTextNode
*pNode
= rPt
.GetNode().GetTextNode();
2756 if (!pNode
|| rStr
.getLength() > pNode
->GetSpaceLeft()) // worst case: no erase
2761 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2763 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2766 const size_t nOldAttrCnt
= pNode
->GetpSwpHints()
2767 ? pNode
->GetpSwpHints()->Count() : 0;
2768 SwDataChanged
aTmp( rRg
);
2769 sal_Int32
const nActualStart(rPt
.GetContentIndex());
2770 sal_Int32 nStart
= 0;
2772 bool bOldExpFlg
= pNode
->IsIgnoreDontExpand();
2773 pNode
->SetIgnoreDontExpand( true );
2775 for( sal_Int32 nCnt
= 0; nCnt
< rStr
.getLength(); ++nCnt
)
2777 // start behind the characters (to fix the attributes!)
2778 nStart
= rPt
.GetContentIndex();
2779 if (nStart
< pNode
->GetText().getLength())
2781 lcl_SkipAttr( pNode
, rPt
, nStart
);
2783 sal_Unicode c
= rStr
[ nCnt
];
2784 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2786 bool bMerged(false);
2787 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2789 SwUndo
*const pUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2790 SwUndoOverwrite
*const pUndoOW(
2791 dynamic_cast<SwUndoOverwrite
*>(pUndo
) );
2794 // if CanGrouping() returns true it's already merged
2795 bMerged
= pUndoOW
->CanGrouping(m_rDoc
, rPt
, c
);
2800 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2801 std::make_unique
<SwUndoOverwrite
>(m_rDoc
, rPt
, c
) );
2806 // start behind the characters (to fix the attributes!)
2807 if (nStart
< pNode
->GetText().getLength())
2808 rPt
.AdjustContent(+1);
2809 pNode
->InsertText( OUString(c
), rPt
, SwInsertFlags::EMPTYEXPAND
);
2810 if( nStart
+1 < rPt
.GetContentIndex() )
2812 rPt
.SetContent(nStart
);
2813 pNode
->EraseText( rPt
, 1 );
2814 rPt
.AdjustContent(+1);
2818 pNode
->SetIgnoreDontExpand( bOldExpFlg
);
2820 const size_t nNewAttrCnt
= pNode
->GetpSwpHints()
2821 ? pNode
->GetpSwpHints()->Count() : 0;
2822 if( nOldAttrCnt
!= nNewAttrCnt
)
2824 const SwUpdateAttr
aHint(0,0,0);
2825 pNode
->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint
, &aHint
));
2828 if (!m_rDoc
.GetIDocumentUndoRedo().DoesUndo() &&
2829 !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2831 SwPaM
aPam(rPt
.GetNode(), nActualStart
, rPt
.GetNode(), rPt
.GetContentIndex());
2832 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aPam
, true, RedlineType::Any
);
2834 else if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2836 // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2837 // characters are also included in aPam
2838 SwPaM
aPam(rPt
.GetNode(), nActualStart
, rPt
.GetNode(), rPt
.GetContentIndex());
2839 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2842 m_rDoc
.getIDocumentState().SetModified();
2846 bool DocumentContentOperationsManager::InsertString( const SwPaM
&rRg
, const OUString
&rStr
,
2847 const SwInsertFlags nInsertMode
)
2849 // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
2850 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2852 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2853 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg
);
2854 if (eOld
!= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags())
2855 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
2858 // fetching DoesUndo is surprisingly expensive
2859 bool bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2861 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2863 const SwPosition
& rPos
= *rRg
.GetPoint();
2865 if( m_rDoc
.GetAutoCorrExceptWord() ) // add to auto correction
2867 if( 1 == rStr
.getLength() && m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() )
2869 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPos
, rStr
[ 0 ] );
2871 m_rDoc
.DeleteAutoCorrExceptWord();
2874 SwTextNode
*const pNode
= rPos
.GetNode().GetTextNode();
2878 SwDataChanged
aTmp( rRg
);
2880 if (!bDoesUndo
|| !m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2882 OUString
const ins(pNode
->InsertText(rStr
, rPos
, nInsertMode
));
2885 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2886 std::make_unique
<SwUndoInsert
>(rPos
.GetNode(),
2887 rPos
.GetContentIndex(), ins
.getLength(), nInsertMode
));
2891 { // if Undo and grouping is enabled, everything changes!
2892 SwUndoInsert
* pUndo
= nullptr;
2894 // don't group the start if hints at the start should be expanded
2895 if (!(nInsertMode
& SwInsertFlags::FORCEHINTEXPAND
))
2897 SwUndo
*const pLastUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2898 SwUndoInsert
*const pUndoInsert(
2899 dynamic_cast<SwUndoInsert
*>(pLastUndo
) );
2900 if (pUndoInsert
&& pUndoInsert
->CanGrouping(rPos
))
2902 pUndo
= pUndoInsert
;
2906 CharClass
const& rCC
= GetAppCharClass();
2907 sal_Int32 nInsPos
= rPos
.GetContentIndex();
2911 pUndo
= new SwUndoInsert( rPos
.GetNode(), nInsPos
, 0, nInsertMode
,
2912 !rCC
.isLetterNumeric( rStr
, 0 ) );
2913 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2916 OUString
const ins(pNode
->InsertText(rStr
, rPos
, nInsertMode
));
2918 for (sal_Int32 i
= 0; i
< ins
.getLength(); ++i
)
2921 // if CanGrouping() returns true, everything has already been done
2922 if (!pUndo
->CanGrouping(ins
[i
]))
2924 pUndo
= new SwUndoInsert(rPos
.GetNode(), nInsPos
, 1, nInsertMode
,
2925 !rCC
.isLetterNumeric(ins
, i
));
2926 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2931 // To-Do - add 'SwExtraRedlineTable' also ?
2932 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2934 SwPaM
aPam( rPos
.GetNode(), aTmp
.GetContent(),
2935 rPos
.GetNode(), rPos
.GetContentIndex());
2936 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2938 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
2939 new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2943 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
2947 m_rDoc
.getIDocumentState().SetModified();
2951 void DocumentContentOperationsManager::SetIME(bool bIME
)
2956 bool DocumentContentOperationsManager::GetIME() const
2961 void DocumentContentOperationsManager::TransliterateText(
2963 utl::TransliterationWrapper
& rTrans
)
2965 std::unique_ptr
<SwUndoTransliterate
> pUndo
;
2966 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2967 pUndo
.reset(new SwUndoTransliterate( rPaM
, rTrans
));
2969 auto [pStt
, pEnd
] = rPaM
.StartEnd(); // SwPosition*
2970 SwNodeOffset nSttNd
= pStt
->GetNodeIndex(),
2971 nEndNd
= pEnd
->GetNodeIndex();
2972 sal_Int32 nSttCnt
= pStt
->GetContentIndex();
2973 sal_Int32 nEndCnt
= pEnd
->GetContentIndex();
2975 SwTextNode
* pTNd
= pStt
->GetNode().GetTextNode();
2976 if( (pStt
== pEnd
) && pTNd
) // no selection?
2978 /* Check if cursor is inside of a word */
2979 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
2980 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
2981 pTNd
->GetText(), nSttCnt
,
2982 g_pBreakIt
->GetLocale( pTNd
->GetLang( nSttCnt
) ),
2983 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
2986 if( aBndry
.startPos
< nSttCnt
&& nSttCnt
< aBndry
.endPos
)
2988 /* Cursor is inside of a word */
2989 if (rTrans
.getType() == TransliterationFlags::SENTENCE_CASE
) {
2990 /* set current sentence as 'area of effect' */
2991 nSttCnt
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
2992 pTNd
->GetText(), nSttCnt
,
2993 g_pBreakIt
->GetLocale( pTNd
->GetLang( nSttCnt
) ) );
2994 nEndCnt
= g_pBreakIt
->GetBreakIter()->endOfSentence(
2995 pTNd
->GetText(), nEndCnt
,
2996 g_pBreakIt
->GetLocale( pTNd
->GetLang( nEndCnt
) ) );
2998 /* Set current word as 'area of effect' */
2999 nSttCnt
= aBndry
.startPos
;
3000 nEndCnt
= aBndry
.endPos
;
3003 /* Cursor is not inside of a word. Nothing should happen. */
3004 /* Except in the case of change tracking, when the cursor is at the end of the change */
3005 /* Recognize and reject the previous deleted and inserted words to allow to cycle */
3006 IDocumentRedlineAccess
& rIDRA
= m_rDoc
.getIDocumentRedlineAccess();
3007 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA
.GetRedlineFlags() ) &&
3008 pStt
->GetContentIndex() > 0 )
3010 SwPosition
aPos(*pStt
->GetContentNode(), pStt
->GetContentIndex() - 1);
3011 SwRedlineTable::size_type n
= 0;
3013 const SwRangeRedline
* pFnd
=
3014 rIDRA
.GetRedlineTable().FindAtPosition( aPos
, n
);
3015 if ( pFnd
&& RedlineType::Insert
== pFnd
->GetType() && n
> 0 )
3017 const SwRangeRedline
* pFnd2
= rIDRA
.GetRedlineTable()[n
-1];
3018 if ( RedlineType::Delete
== pFnd2
->GetType() &&
3019 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() &&
3020 *pFnd2
->End() == *pFnd
->Start() &&
3021 pFnd
->GetAuthor() == pFnd2
->GetAuthor() )
3023 SwPosition
aPos2(*pFnd2
->End());
3024 rIDRA
.RejectRedline(*pFnd
, true);
3025 rIDRA
.RejectRedline(*pFnd2
, true);
3026 // positionate the text cursor inside the changed word to allow to cycle
3027 if ( SwWrtShell
*pWrtShell
= dynamic_cast<SwWrtShell
*>(
3028 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell()) )
3030 pWrtShell
->GetCursor()->GetPoint()->
3031 Assign(*aPos2
.GetContentNode(), aPos2
.GetContentIndex() - 1);
3041 bool bHasTrackedChange
= false;
3042 IDocumentRedlineAccess
& rIDRA
= m_rDoc
.getIDocumentRedlineAccess();
3043 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA
.GetRedlineFlags() ) &&
3044 pEnd
->GetContentIndex() > 0 )
3046 SwPosition
aPos(*pEnd
->GetContentNode(), pEnd
->GetContentIndex() - 1);
3047 SwRedlineTable::size_type n
= 0;
3049 const SwRangeRedline
* pFnd
=
3050 rIDRA
.GetRedlineTable().FindAtPosition( aPos
, n
);
3051 if ( pFnd
&& RedlineType::Insert
== pFnd
->GetType() && n
> 0 )
3053 const SwRangeRedline
* pFnd2
= rIDRA
.GetRedlineTable()[n
-1];
3054 if ( RedlineType::Delete
== pFnd2
->GetType() &&
3055 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() &&
3056 *pFnd2
->End() == *pFnd
->Start() &&
3057 pFnd
->GetAuthor() == pFnd2
->GetAuthor() )
3059 bHasTrackedChange
= true;
3060 SwPosition
aPos2(*pFnd2
->Start());
3061 rIDRA
.RejectRedline(*pFnd
, true);
3063 rIDRA
.RejectRedline(*pFnd2
, true);
3064 // positionate the text cursor before the changed word to select it
3065 if ( SwWrtShell
*pWrtShell
= dynamic_cast<SwWrtShell
*>(
3066 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell()) )
3068 pWrtShell
->GetCursor()->GetPoint()->
3069 Assign(*aPos2
.GetContentNode(), aPos2
.GetContentIndex());
3074 if ( bHasTrackedChange
)
3078 bool bUseRedlining
= m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn();
3079 // as a workaround for a known performance problem, switch off redlining
3080 // to avoid freezing, if transliteration could result too many redlines
3081 if ( bUseRedlining
)
3083 const sal_uLong nMaxRedlines
= 500;
3084 const bool bIsTitleCase
= rTrans
.getType() == TransliterationFlags::TITLE_CASE
;
3085 sal_uLong nAffectedNodes
= 0;
3086 sal_uLong nAffectedChars
= nEndCnt
;
3087 SwNodeIndex
aIdx( pStt
->GetNode() );
3088 for( ; aIdx
.GetIndex() <= nEndNd
; ++aIdx
)
3090 SwTextNode
* pAffectedNode
= aIdx
.GetNode().GetTextNode();
3092 // don't count not text nodes or empty text nodes
3093 if( !pAffectedNode
|| pAffectedNode
->GetText().isEmpty() )
3098 // count characters of the node (the last - maybe partially
3099 // selected - node was counted at initialization of nAffectedChars)
3100 if( aIdx
.GetIndex() < nEndNd
)
3101 nAffectedChars
+= pAffectedNode
->GetText().getLength();
3103 // transliteration creates n redlines for n nodes, except in the
3104 // case of title case, where it creates n redlines for n words
3105 if( nAffectedNodes
> nMaxRedlines
||
3106 // estimate word count based on the character count, where
3107 // 6 = average English word length is ~5 letters + space
3108 ( bIsTitleCase
&& (nAffectedChars
- nSttCnt
)/6 > nMaxRedlines
) )
3110 bUseRedlining
= false;
3116 if( nSttNd
!= nEndNd
) // is more than one text node involved?
3118 // iterate over all affected text nodes, the first and the last one
3119 // may be incomplete because the selection starts and/or ends there
3121 SwNodeIndex
aIdx( pStt
->GetNode() );
3127 pTNd
->TransliterateText(
3128 rTrans
, nSttCnt
, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3132 for( ; aIdx
.GetIndex() < nEndNd
; ++aIdx
)
3134 pTNd
= aIdx
.GetNode().GetTextNode();
3137 pTNd
->TransliterateText(
3138 rTrans
, 0, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3142 if( nEndCnt
&& nullptr != ( pTNd
= pEnd
->GetNode().GetTextNode() ))
3144 pTNd
->TransliterateText( rTrans
, 0, nEndCnt
, pUndo
.get(), bUseRedlining
);
3147 else if( pTNd
&& nSttCnt
< nEndCnt
)
3149 pTNd
->TransliterateText( rTrans
, nSttCnt
, nEndCnt
, pUndo
.get(), bUseRedlining
);
3151 if( pUndo
&& pUndo
->HasData() )
3153 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
3155 m_rDoc
.getIDocumentState().SetModified();
3158 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertGraphic(
3160 const OUString
& rGrfName
,
3161 const OUString
& rFltName
,
3162 const Graphic
* pGraphic
,
3163 const SfxItemSet
* pFlyAttrSet
,
3164 const SfxItemSet
* pGrfAttrSet
,
3165 SwFrameFormat
* pFrameFormat
)
3168 pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC
);
3169 SwGrfNode
* pSwGrfNode
= SwNodes::MakeGrfNode(
3170 m_rDoc
.GetNodes().GetEndOfAutotext(),
3171 rGrfName
, rFltName
, pGraphic
,
3172 m_rDoc
.GetDfltGrfFormatColl() );
3173 SwFlyFrameFormat
* pSwFlyFrameFormat
= InsNoTextNode( *rRg
.GetPoint(), pSwGrfNode
,
3174 pFlyAttrSet
, pGrfAttrSet
, pFrameFormat
);
3175 return pSwFlyFrameFormat
;
3178 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertEmbObject(
3179 const SwPaM
&rRg
, const svt::EmbeddedObjectRef
& xObj
,
3180 SfxItemSet
* pFlyAttrSet
)
3182 sal_uInt16 nId
= RES_POOLFRM_OLE
;
3185 SvGlobalName
aClassName( xObj
->getClassID() );
3186 if (SotExchange::IsMath(aClassName
))
3188 nId
= RES_POOLFRM_FORMEL
;
3192 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId
);
3194 return InsNoTextNode( *rRg
.GetPoint(), m_rDoc
.GetNodes().MakeOLENode(
3195 m_rDoc
.GetNodes().GetEndOfAutotext(),
3197 m_rDoc
.GetDfltGrfFormatColl() ),
3198 pFlyAttrSet
, nullptr,
3202 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertOLE(const SwPaM
&rRg
, const OUString
& rObjName
,
3204 const SfxItemSet
* pFlyAttrSet
,
3205 const SfxItemSet
* pGrfAttrSet
)
3207 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE
);
3209 return InsNoTextNode( *rRg
.GetPoint(),
3210 m_rDoc
.GetNodes().MakeOLENode(
3211 m_rDoc
.GetNodes().GetEndOfAutotext(),
3214 m_rDoc
.GetDfltGrfFormatColl(),
3216 pFlyAttrSet
, pGrfAttrSet
,
3220 void DocumentContentOperationsManager::ReRead( SwPaM
& rPam
, const OUString
& rGrfName
,
3221 const OUString
& rFltName
, const Graphic
* pGraphic
)
3224 if( !(( !rPam
.HasMark()
3225 || rPam
.GetPoint()->GetNodeIndex() == rPam
.GetMark()->GetNodeIndex() )
3226 && nullptr != ( pGrfNd
= rPam
.GetPoint()->GetNode().GetGrfNode() )) )
3229 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3231 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoReRead
>(rPam
, *pGrfNd
));
3234 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
3235 if( MirrorGraph::Dont
!= pGrfNd
->GetSwAttrSet().
3236 GetMirrorGrf().GetValue() )
3237 pGrfNd
->SetAttr( SwMirrorGrf() );
3239 pGrfNd
->ReRead( rGrfName
, rFltName
, pGraphic
);
3240 m_rDoc
.getIDocumentState().SetModified();
3243 // Insert drawing object, which has to be already inserted in the DrawModel
3244 SwDrawFrameFormat
* DocumentContentOperationsManager::InsertDrawObj(
3246 SdrObject
& rDrawObj
,
3247 const SfxItemSet
& rFlyAttrSet
)
3249 SwDrawFrameFormat
* pFormat
= m_rDoc
.MakeDrawFrameFormat( OUString(), m_rDoc
.GetDfltFrameFormat() );
3251 const SwFormatAnchor
* pAnchor
= rFlyAttrSet
.GetItemIfSet( RES_ANCHOR
, false );
3252 pFormat
->SetFormatAttr( rFlyAttrSet
);
3254 // Didn't set the Anchor yet?
3255 // DrawObjecte must never end up in the Header/Footer!
3256 RndStdIds eAnchorId
= pAnchor
!= nullptr ? pAnchor
->GetAnchorId() : pFormat
->GetAnchor().GetAnchorId();
3257 const bool bIsAtContent
= (RndStdIds::FLY_AT_PAGE
!= eAnchorId
);
3259 const SwPosition
* pChkPos
= nullptr;
3260 if ( pAnchor
== nullptr )
3262 pChkPos
= rRg
.GetPoint();
3264 else if ( bIsAtContent
)
3267 pAnchor
->GetContentAnchor() ? pAnchor
->GetContentAnchor() : rRg
.GetPoint();
3270 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
3271 if( pChkPos
!= nullptr
3272 && ::CheckControlLayer( &rDrawObj
)
3273 && m_rDoc
.IsInHeaderFooter( pChkPos
->GetNode() ) )
3275 // apply at-page anchor format
3276 eAnchorId
= RndStdIds::FLY_AT_PAGE
;
3277 pFormat
->SetFormatAttr( SwFormatAnchor( eAnchorId
) );
3279 else if( pAnchor
== nullptr
3281 && pAnchor
->GetAnchorNode() == nullptr ) )
3283 // apply anchor format
3284 SwFormatAnchor
aAnch( pAnchor
!= nullptr ? *pAnchor
: pFormat
->GetAnchor() );
3285 eAnchorId
= aAnch
.GetAnchorId();
3286 if ( eAnchorId
== RndStdIds::FLY_AT_FLY
)
3288 const SwStartNode
* pStartNode
= rRg
.GetPointNode().FindFlyStartNode();
3290 SwPosition
aPos(*pStartNode
);
3291 aAnch
.SetAnchor( &aPos
);
3295 aAnch
.SetAnchor( rRg
.GetPoint() );
3296 if ( eAnchorId
== RndStdIds::FLY_AT_PAGE
)
3298 eAnchorId
= dynamic_cast<const SdrUnoObj
*>( &rDrawObj
) != nullptr ? RndStdIds::FLY_AS_CHAR
: RndStdIds::FLY_AT_PARA
;
3299 aAnch
.SetType( eAnchorId
);
3302 pFormat
->SetFormatAttr( aAnch
);
3305 // insert text attribute for as-character anchored drawing object
3306 if ( eAnchorId
== RndStdIds::FLY_AS_CHAR
)
3308 bool bAnchorAtPageAsFallback
= true;
3309 const SwFormatAnchor
& rDrawObjAnchorFormat
= pFormat
->GetAnchor();
3310 if ( rDrawObjAnchorFormat
.GetAnchorNode() != nullptr )
3312 SwTextNode
* pAnchorTextNode
=
3313 rDrawObjAnchorFormat
.GetAnchorNode()->GetTextNode();
3314 if ( pAnchorTextNode
!= nullptr )
3316 const sal_Int32 nStt
= rDrawObjAnchorFormat
.GetContentAnchor()->GetContentIndex();
3317 SwFormatFlyCnt
aFormat( pFormat
);
3318 pAnchorTextNode
->InsertItem( aFormat
, nStt
, nStt
);
3319 bAnchorAtPageAsFallback
= false;
3323 if ( bAnchorAtPageAsFallback
)
3325 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
3326 pFormat
->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE
) );
3330 SwDrawContact
* pContact
= new SwDrawContact( pFormat
, &rDrawObj
);
3332 // Create Frames if necessary
3333 if( m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() )
3335 // create layout representation
3336 pFormat
->MakeFrames();
3337 // #i42319# - follow-up of #i35635#
3338 // move object to visible layer
3340 if ( pContact
->GetAnchorFrame() )
3342 pContact
->MoveObjToVisibleLayer( &rDrawObj
);
3346 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3348 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsLayFormat
>(pFormat
, SwNodeOffset(0), 0) );
3351 m_rDoc
.getIDocumentState().SetModified();
3355 bool DocumentContentOperationsManager::SplitNode( const SwPosition
&rPos
, bool bChkTableStart
)
3357 SwContentNode
*pNode
= rPos
.GetNode().GetContentNode();
3358 if(nullptr == pNode
)
3362 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
3363 // After that they can be before/after the position.
3364 SwDataChanged
aTmp( m_rDoc
, rPos
);
3367 SwUndoSplitNode
* pUndo
= nullptr;
3368 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3370 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3371 // insert the Undo object (currently only for TextNode)
3372 if( pNode
->IsTextNode() )
3374 pUndo
= new SwUndoSplitNode( m_rDoc
, rPos
, bChkTableStart
);
3375 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3379 // Update the rsid of the old and the new node unless
3380 // the old node is split at the beginning or at the end
3381 SwTextNode
*pTextNode
= rPos
.GetNode().GetTextNode();
3382 const sal_Int32 nPos
= rPos
.GetContentIndex();
3383 if( pTextNode
&& nPos
&& nPos
!= pTextNode
->Len() )
3385 m_rDoc
.UpdateParRsid( pTextNode
);
3388 //JP 28.01.97: Special case for SplitNode at table start:
3389 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
3390 // then insert a paragraph before it.
3391 if( bChkTableStart
&& !rPos
.GetContentIndex() && pNode
->IsTextNode() )
3393 SwNodeOffset nPrevPos
= rPos
.GetNodeIndex() - 1;
3394 SwTableNode
* pTableNd
;
3395 const SwNode
* pNd
= m_rDoc
.GetNodes()[ nPrevPos
];
3396 if( pNd
->IsStartNode() &&
3397 SwTableBoxStartNode
== static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() &&
3398 nullptr != ( pTableNd
= m_rDoc
.GetNodes()[ --nPrevPos
]->GetTableNode() ) &&
3399 ((( pNd
= m_rDoc
.GetNodes()[ --nPrevPos
])->IsStartNode() &&
3400 SwTableBoxStartNode
!= static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() )
3401 || ( pNd
->IsEndNode() && pNd
->StartOfSectionNode()->IsTableNode() )
3402 || pNd
->IsContentNode() ))
3404 if( pNd
->IsContentNode() )
3406 //JP 30.04.99 Bug 65660:
3407 // There are no page breaks outside of the normal body area,
3408 // so this is not a valid condition to insert a paragraph.
3409 if( nPrevPos
< m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3413 // Only if the table has page breaks!
3414 const SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3415 if( SfxItemState::SET
!= pFrameFormat
->GetItemState(RES_PAGEDESC
, false) &&
3416 SfxItemState::SET
!= pFrameFormat
->GetItemState( RES_BREAK
, false ) )
3423 SwTextNode
* pTextNd
= m_rDoc
.GetNodes().MakeTextNode(
3425 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT
));
3428 const_cast<SwPosition
&>(rPos
).Assign( pTableNd
->GetIndex() - SwNodeOffset(1) );
3430 // only add page breaks/styles to the body area
3431 if( nPrevPos
> m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3433 SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3434 const SfxPoolItem
*pItem
;
3435 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_PAGEDESC
,
3438 pTextNd
->SetAttr( *pItem
);
3439 pFrameFormat
->ResetFormatAttr( RES_PAGEDESC
);
3441 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_BREAK
,
3444 pTextNd
->SetAttr( *pItem
);
3445 pFrameFormat
->ResetFormatAttr( RES_BREAK
);
3450 pUndo
->SetTableFlag();
3451 m_rDoc
.getIDocumentState().SetModified();
3458 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
3459 pContentStore
->Save( m_rDoc
, rPos
.GetNodeIndex(), rPos
.GetContentIndex(), true );
3460 assert(pNode
->IsTextNode());
3461 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool bAtStart
)> restoreFunc(
3462 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool const bAtStart
)
3464 if (!pContentStore
->Empty())
3465 { // move all bookmarks, TOXMarks, FlyAtCnt
3466 pContentStore
->Restore(m_rDoc
, rPos
.GetNodeIndex()-SwNodeOffset(1), 0, true, bAtStart
&& (eMode
& sw::mark::RestoreMode::Flys
), eMode
);
3468 if (eMode
& sw::mark::RestoreMode::NonFlys
)
3470 // To-Do - add 'SwExtraRedlineTable' also ?
3471 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
3472 (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() &&
3473 !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty()))
3477 aPam
.Move( fnMoveBackward
);
3478 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3480 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
3481 new SwRangeRedline(RedlineType::Insert
, aPam
), true);
3485 m_rDoc
.getIDocumentRedlineAccess().SplitRedline(aPam
);
3490 pNode
->GetTextNode()->SplitContentNode(rPos
, &restoreFunc
);
3492 m_rDoc
.getIDocumentState().SetModified();
3496 bool DocumentContentOperationsManager::AppendTextNode( SwPosition
& rPos
)
3498 // create new node before EndOfContent
3499 SwTextNode
* pCurNode
= rPos
.GetNode().GetTextNode();
3502 // so then one can be created!
3503 SwNodeIndex
aIdx( rPos
.GetNode(), 1 );
3504 pCurNode
= m_rDoc
.GetNodes().MakeTextNode( aIdx
.GetNode(),
3505 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
));
3508 pCurNode
= pCurNode
->AppendNode( rPos
)->GetTextNode();
3510 rPos
.Adjust(SwNodeOffset(1));
3512 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3514 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsert
>( rPos
.GetNode() ) );
3517 // To-Do - add 'SwExtraRedlineTable' also ?
3518 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
3522 aPam
.Move( fnMoveBackward
);
3523 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
3524 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
3526 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
3529 m_rDoc
.getIDocumentState().SetModified();
3533 bool DocumentContentOperationsManager::ReplaceRange( SwPaM
& rPam
, const OUString
& rStr
,
3534 const bool bRegExReplace
)
3536 // unfortunately replace works slightly differently from delete,
3537 // so we cannot use lcl_DoWithBreaks here...
3539 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
3541 SwPaM
aPam( *rPam
.GetMark(), *rPam
.GetPoint() );
3542 aPam
.Normalize(false);
3543 if (aPam
.GetPoint()->GetNode() != aPam
.GetMark()->GetNode())
3545 aPam
.Move(fnMoveBackward
);
3547 OSL_ENSURE((aPam
.GetPoint()->GetNode() == aPam
.GetMark()->GetNode()), "invalid pam?");
3549 sw::CalcBreaks(Breaks
, aPam
);
3551 while (!Breaks
.empty() // skip over prefix of dummy chars
3552 && (aPam
.GetMark()->GetNodeIndex() == Breaks
.begin()->first
)
3553 && (aPam
.GetMark()->GetContentIndex() == Breaks
.begin()->second
))
3556 aPam
.GetMark()->AdjustContent(+1); // always in bounds if Breaks valid
3557 Breaks
.erase(Breaks
.begin());
3559 *rPam
.Start() = *aPam
.GetMark(); // update start of original pam w/ prefix
3563 // park aPam somewhere so it does not point to node that is deleted
3565 aPam
.GetPoint()->Assign(m_rDoc
.GetNodes().GetEndOfContent());
3566 return ReplaceRangeImpl(rPam
, rStr
, bRegExReplace
); // original pam!
3569 // Deletion must be split into several parts if the text node
3570 // contains a text attribute with end and with dummy character
3571 // and the selection does not contain the text attribute completely,
3572 // but overlaps its start (left), where the dummy character is.
3575 // iterate from end to start, to avoid invalidating the offsets!
3576 auto iter( Breaks
.rbegin() );
3577 SwNodeOffset
nOffset(0);
3578 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
3579 OSL_ENSURE(aPam
.GetPoint() == aPam
.End(), "wrong!");
3580 SwPosition
& rEnd( *aPam
.End() );
3581 SwPosition
& rStart( *aPam
.Start() );
3583 // set end of temp pam to original end (undo Move backward above)
3585 // after first deletion, rEnd will point into the original text node again!
3587 while (iter
!= Breaks
.rend())
3589 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
3590 if (rStart
< rEnd
) // check if part is empty
3592 bRet
&= (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3593 ? DeleteAndJoinWithRedlineImpl(aPam
, SwDeleteFlags::Default
)
3594 : DeleteAndJoinImpl(aPam
, SwDeleteFlags::Default
);
3595 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // deleted fly nodes...
3597 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
3601 rStart
= *rPam
.Start(); // set to original start
3602 assert(rStart
< rEnd
&& "replace part empty!");
3603 if (rStart
< rEnd
) // check if part is empty
3605 bRet
&= ReplaceRangeImpl(aPam
, rStr
, bRegExReplace
);
3608 rPam
= aPam
; // update original pam (is this required?)
3613 bool DocumentContentOperationsManager::InsertPoolItem(
3615 const SfxPoolItem
&rHt
,
3616 const SetAttrMode nFlags
,
3617 SwRootFrame
const*const pLayout
,
3618 SwTextAttr
**ppNewTextAttr
)
3620 SwDataChanged
aTmp( rRg
);
3621 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3622 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3624 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3625 pUndoAttr
.reset(new SwUndoAttr( rRg
, rHt
, nFlags
));
3628 SfxItemSet
aSet( m_rDoc
.GetAttrPool(), rHt
.Which(), rHt
.Which() );
3630 const bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, aSet
, nFlags
, pUndoAttr
.get(), pLayout
, ppNewTextAttr
);
3632 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3634 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3639 m_rDoc
.getIDocumentState().SetModified();
3644 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM
&rRg
, const SfxItemSet
&rSet
,
3645 const SetAttrMode nFlags
, SwRootFrame
const*const pLayout
)
3647 SwDataChanged
aTmp( rRg
);
3648 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3649 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3651 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3652 pUndoAttr
.reset(new SwUndoAttr( rRg
, rSet
, nFlags
));
3655 bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, rSet
, nFlags
, pUndoAttr
.get(), pLayout
, /*ppNewTextAttr*/nullptr );
3657 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3659 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3663 m_rDoc
.getIDocumentState().SetModified();
3666 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition
& rPos
)
3668 const SwTextNode
* pTNd
= rPos
.GetNode().GetTextNode();
3672 const OUString
& rText
= pTNd
->GetText();
3674 while (nIdx
< rText
.getLength())
3676 sal_Unicode
const cCh
= rText
[nIdx
];
3677 if (('\t' != cCh
) && (' ' != cCh
))
3687 aPam
.GetPoint()->SetContent(0);
3689 aPam
.GetMark()->SetContent(nIdx
);
3690 DeleteRange( aPam
);
3694 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(SwPaM
& rPaM
)
3696 for (SwPaM
& rSel
:rPaM
.GetRingContainer())
3698 SwNodeOffset nStt
= rSel
.Start()->GetNodeIndex();
3699 SwNodeOffset nEnd
= rSel
.End()->GetNodeIndex();
3700 for (SwNodeOffset nPos
= nStt
; nPos
<=nEnd
; nPos
++)
3701 RemoveLeadingWhiteSpace(SwPosition(rSel
.GetBound().GetNodes(), nPos
));
3705 // Copy method from SwDoc - "copy Flys in Flys"
3706 /// note: rRg/rInsPos *exclude* a partially selected start text node;
3707 /// pCopiedPaM *includes* a partially selected start text node
3708 void DocumentContentOperationsManager::CopyWithFlyInFly(
3709 const SwNodeRange
& rRg
,
3711 const std::pair
<const SwPaM
&, const SwPosition
&>* pCopiedPaM
/*and real insert pos*/,
3712 const bool bMakeNewFrames
,
3713 const bool bDelRedlines
,
3714 const bool bCopyFlyAtFly
,
3715 SwCopyFlags
const flags
) const
3717 assert(!pCopiedPaM
|| pCopiedPaM
->first
.End()->GetNode() == rRg
.aEnd
.GetNode());
3718 assert(!pCopiedPaM
|| pCopiedPaM
->second
.GetNode() <= rInsPos
);
3720 SwDoc
& rDest
= rInsPos
.GetDoc();
3721 SwNodeIndex
aSavePos( rInsPos
);
3723 if (rRg
.aStart
!= rRg
.aEnd
)
3725 bool bEndIsEqualEndPos
= rInsPos
== rRg
.aEnd
.GetNode();
3727 SaveRedlEndPosForRestore
aRedlRest( rInsPos
, 0 );
3729 // insert behind the already copied start node
3730 m_rDoc
.GetNodes().CopyNodes( rRg
, rInsPos
, false, true );
3731 aRedlRest
.Restore();
3733 if (bEndIsEqualEndPos
)
3735 const_cast<SwNodeIndex
&>(rRg
.aEnd
).Assign(aSavePos
.GetNode(), +1);
3739 // Also copy all bookmarks
3740 // guess this must be done before the DelDummyNodes below as that
3741 // deletes nodes so would mess up the index arithmetic
3742 // sw_fieldmarkhide: also needs to be done before making frames
3743 if (m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount())
3745 SwPaM
aRgTmp( rRg
.aStart
, rRg
.aEnd
);
3746 SwPosition
targetPos(aSavePos
, SwNodeOffset(rRg
.aStart
!= rRg
.aEnd
? +1 : 0));
3747 if (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->GetNode())
3749 // there is 1 (partially selected, maybe) paragraph before
3750 assert(SwNodeIndex(rRg
.aStart
, -1) == pCopiedPaM
->first
.Start()->GetNode());
3751 // only use the passed in target SwPosition if the source PaM point
3752 // is on a different node; if it was the same node then the target
3753 // position was likely moved along by the copy operation and now
3754 // points to the end of the range!
3755 targetPos
= pCopiedPaM
->second
;
3758 sw::CopyBookmarks(pCopiedPaM
? pCopiedPaM
->first
: aRgTmp
, targetPos
, flags
);
3761 if (rRg
.aStart
!= rRg
.aEnd
)
3763 bool isRecreateEndNode(false);
3764 if (bMakeNewFrames
) // tdf#130685 only after aRedlRest
3765 { // recreate from previous node (could be merged now)
3766 o3tl::sorted_vector
<SwTextFrame
*> frames
;
3767 SwTextNode
* pNode
= aSavePos
.GetNode().GetTextNode();
3768 SwTextNode
*const pEndNode
= rInsPos
.GetTextNode();
3771 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3772 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3774 if (pFrame
->getRootFrame()->HasMergedParas())
3776 frames
.insert(pFrame
);
3777 // tdf#135061 check if end node is merged to a preceding node
3778 if (pNode
== nullptr && pFrame
->GetMergedPara()
3779 && pFrame
->GetMergedPara()->pFirstNode
->GetIndex() < aSavePos
.GetIndex())
3781 pNode
= pFrame
->GetMergedPara()->pFirstNode
;
3786 if (pNode
!= nullptr)
3788 sw::RecreateStartTextFrames(*pNode
);
3789 if (!frames
.empty())
3790 { // tdf#132187 check if the end node needs new frames
3791 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3792 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3794 if (pFrame
->getRootFrame()->HasMergedParas())
3796 auto const it
= frames
.find(pFrame
);
3797 if (it
!= frames
.end())
3803 if (!frames
.empty()) // existing frame was deleted
3804 { // all layouts because MakeFrames recreates all layouts
3805 pEndNode
->DelFrames(nullptr);
3806 isRecreateEndNode
= true;
3811 bool const isAtStartOfSection(aSavePos
.GetNode().IsStartNode());
3815 // it's possible that CheckParaRedlineMerge() deleted frames
3816 // on rInsPos so have to include it, but it must not be included
3817 // if it was the first node in the document so that MakeFrames()
3818 // will find the existing (wasn't deleted) frame on it
3819 SwNodeIndex
const end(rInsPos
,
3820 SwNodeOffset((!isRecreateEndNode
|| isAtStartOfSection
)
3822 ::MakeFrames(&rDest
, aSavePos
.GetNode(), end
.GetNode());
3826 #if OSL_DEBUG_LEVEL > 0
3828 //JP 17.06.99: Bug 66973 - check count only if the selection is in
3829 // the same section or there's no section, because sections that are
3830 // not fully selected are not copied.
3831 const SwSectionNode
* pSSectNd
= rRg
.aStart
.GetNode().FindSectionNode();
3832 SwNodeIndex
aTmpI( rRg
.aEnd
, -1 );
3833 const SwSectionNode
* pESectNd
= aTmpI
.GetNode().FindSectionNode();
3834 if( pSSectNd
== pESectNd
&&
3835 !rRg
.aStart
.GetNode().IsSectionNode() &&
3836 !aTmpI
.GetNode().IsEndNode() )
3838 // If the range starts with a SwStartNode, it isn't copied
3839 SwNodeOffset
offset( (rRg
.aStart
.GetNode().GetNodeType() != SwNodeType::Start
) ? 1 : 0 );
3840 OSL_ENSURE( rInsPos
.GetIndex() - aSavePos
.GetIndex() ==
3841 rRg
.aEnd
.GetIndex() - rRg
.aStart
.GetIndex() - 1 + offset
,
3842 "An insufficient number of nodes were copied!" );
3848 ::sw::UndoGuard
const undoGuard(rDest
.GetIDocumentUndoRedo());
3849 CopyFlyInFlyImpl(rRg
, pCopiedPaM
? &pCopiedPaM
->first
: nullptr,
3850 // see comment below regarding use of pCopiedPaM->second
3851 (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->GetNode())
3852 ? pCopiedPaM
->second
.GetNode()
3853 : aSavePos
.GetNode(),
3858 SwNodeRange
aCpyRange( aSavePos
.GetNode(), rInsPos
);
3860 if( bDelRedlines
&& ( RedlineFlags::DeleteRedlines
& rDest
.getIDocumentRedlineAccess().GetRedlineFlags() ))
3861 lcl_DeleteRedlines( rRg
, aCpyRange
);
3863 rDest
.GetNodes().DelDummyNodes( aCpyRange
);
3866 // note: for the redline Show/Hide this must be in sync with
3867 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
3868 void DocumentContentOperationsManager::CopyFlyInFlyImpl(
3869 const SwNodeRange
& rRg
,
3870 SwPaM
const*const pCopiedPaM
,
3872 const bool bCopyFlyAtFly
,
3873 SwCopyFlags
const flags
) const
3875 assert(!pCopiedPaM
|| pCopiedPaM
->End()->GetNode() == rRg
.aEnd
.GetNode());
3877 // First collect all Flys, sort them according to their ordering number,
3878 // and then only copy them. This maintains the ordering numbers (which are only
3879 // managed in the DrawModel).
3880 SwDoc
& rDest
= rStartIdx
.GetDoc();
3881 std::set
< ZSortFly
> aSet
;
3882 const size_t nArrLen
= m_rDoc
.GetSpzFrameFormats()->size();
3884 SwTextBoxHelper::SavedLink aOldTextBoxes
;
3885 SwTextBoxHelper::saveLinks(*m_rDoc
.GetSpzFrameFormats(), aOldTextBoxes
);
3887 for ( size_t n
= 0; n
< nArrLen
; ++n
)
3889 SwFrameFormat
* pFormat
= (*m_rDoc
.GetSpzFrameFormats())[n
];
3890 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
3891 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
3895 SwNodeOffset nSkipAfter
= pAnchorNode
->GetIndex();
3896 SwNodeOffset nStart
= rRg
.aStart
.GetIndex();
3897 switch ( pAnchor
->GetAnchorId() )
3899 case RndStdIds::FLY_AT_FLY
:
3902 else if(m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove())
3905 case RndStdIds::FLY_AT_PARA
:
3907 bAdd
= IsSelectFrameAnchoredAtPara(*pAnchor
->GetContentAnchor(),
3908 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
3909 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
3910 (flags
& SwCopyFlags::IsMoveToFly
)
3911 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
3912 : DelContentType::AllMask
);
3915 case RndStdIds::FLY_AT_CHAR
:
3917 bAdd
= IsDestroyFrameAnchoredAtChar(*pAnchor
->GetContentAnchor(),
3918 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
3919 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
3920 (flags
& SwCopyFlags::IsMoveToFly
)
3921 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
3922 : DelContentType::AllMask
);
3928 if (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId())
3930 if (nStart
> nSkipAfter
)
3932 if (*pAnchorNode
> rRg
.aEnd
.GetNode())
3934 //frames at the last source node are not always copied:
3935 //- if the node is empty and is the last node of the document or a table cell
3936 // or a text frame then they have to be copied
3937 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
3938 //- to-character bound objects are copied if their index is <= nEndContentIndex
3939 if (*pAnchorNode
< rRg
.aEnd
.GetNode())
3941 if (!bAdd
&& !m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
3945 // technically old code checked nContent of AT_FLY which is pointless
3946 bAdd
= pCopiedPaM
&& 0 < pCopiedPaM
->End()->GetContentIndex();
3952 aSet
.insert( ZSortFly( pFormat
, pAnchor
, nArrLen
+ aSet
.size() ));
3956 // Store all copied (and also the newly created) frames in another array.
3957 // They are stored as matching the originals, so that we will be later
3958 // able to build the chains accordingly.
3959 std::vector
< SwFrameFormat
* > aVecSwFrameFormat
;
3960 std::set
< ZSortFly
>::const_iterator it
=aSet
.begin();
3962 while (it
!= aSet
.end())
3965 // correct determination of new anchor position
3966 SwFormatAnchor
aAnchor( *(*it
).GetAnchor() );
3967 assert( aAnchor
.GetContentAnchor() != nullptr );
3968 SwPosition newPos
= *aAnchor
.GetContentAnchor();
3969 // for at-paragraph and at-character anchored objects the new anchor
3970 // position can *not* be determined by the difference of the current
3971 // anchor position to the start of the copied range, because not
3972 // complete selected sections in the copied range aren't copied - see
3973 // method <SwNodes::CopyNodes(..)>.
3974 // Thus, the new anchor position in the destination document is found
3975 // by counting the text nodes.
3976 if ((aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PARA
) ||
3977 (aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
) )
3979 // First, determine number of anchor text node in the copied range.
3980 // Note: The anchor text node *have* to be inside the copied range.
3981 sal_uLong
nAnchorTextNdNumInRange( 0 );
3982 bool bAnchorTextNdFound( false );
3983 // start at the first node for which flys are copied
3984 SwNodeIndex
aIdx(pCopiedPaM
? pCopiedPaM
->Start()->GetNode() : rRg
.aStart
.GetNode());
3985 while ( !bAnchorTextNdFound
&& aIdx
<= rRg
.aEnd
)
3987 if ( aIdx
.GetNode().IsTextNode() )
3989 ++nAnchorTextNdNumInRange
;
3990 bAnchorTextNdFound
= *aAnchor
.GetAnchorNode() == aIdx
.GetNode();
3996 if ( !bAnchorTextNdFound
)
3998 // This case can *not* happen, but to be robust take the first
3999 // text node in the destination document.
4000 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
4001 nAnchorTextNdNumInRange
= 1;
4003 // Second, search corresponding text node in destination document
4004 // by counting forward from start insert position <rStartIdx> the
4005 // determined number of text nodes.
4007 SwNodeIndex
aAnchorNdIdx( rStartIdx
);
4008 const SwNode
& aEndOfContentNd
=
4009 aIdx
.GetNode().GetNodes().GetEndOfContent();
4010 while ( nAnchorTextNdNumInRange
> 0 &&
4011 aIdx
.GetNode() != aEndOfContentNd
)
4013 if ( aIdx
.GetNode().IsTextNode() )
4015 --nAnchorTextNdNumInRange
;
4016 aAnchorNdIdx
= aIdx
;
4021 if ( !aAnchorNdIdx
.GetNode().IsTextNode() )
4023 // This case can *not* happen, but to be robust take the first
4024 // text node in the destination document.
4025 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
4026 aAnchorNdIdx
= rStartIdx
;
4027 while ( !aAnchorNdIdx
.GetNode().IsTextNode() )
4032 // apply found anchor text node as new anchor position
4033 newPos
.Assign( aAnchorNdIdx
);
4037 SwNodeOffset nOffset
= newPos
.GetNodeIndex() - rRg
.aStart
.GetIndex();
4038 newPos
.Assign( rStartIdx
, nOffset
);
4040 // Set the character bound Flys back at the original character
4041 if ((RndStdIds::FLY_AT_CHAR
== aAnchor
.GetAnchorId()) &&
4042 newPos
.GetNode().IsTextNode() )
4044 // only if pCopiedPaM: care about partially selected start node
4045 sal_Int32
const nContent
= pCopiedPaM
&& pCopiedPaM
->Start()->GetNode() == *aAnchor
.GetAnchorNode()
4046 ? newPos
.GetContentIndex() - pCopiedPaM
->Start()->GetContentIndex()
4047 : newPos
.GetContentIndex();
4048 newPos
.SetContent(nContent
);
4050 aAnchor
.SetAnchor( &newPos
);
4052 // Check recursion: if copying content inside the same frame, then don't copy the format.
4053 if( &rDest
== &m_rDoc
)
4055 const SwFormatContent
& rContent
= (*it
).GetFormat()->GetContent();
4056 const SwStartNode
* pSNd
;
4057 if( rContent
.GetContentIdx() &&
4058 nullptr != ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ) &&
4059 pSNd
->GetIndex() < rStartIdx
.GetIndex() &&
4060 rStartIdx
.GetIndex() < pSNd
->EndOfSectionIndex() )
4062 it
= aSet
.erase(it
);
4067 // Ignore TextBoxes, they are already handled in
4068 // sw::DocumentLayoutManager::CopyLayoutFormat().
4069 if (SwTextBoxHelper::isTextBox(it
->GetFormat(), RES_FLYFRMFMT
))
4071 it
= aSet
.erase(it
);
4075 // Copy the format and set the new anchor
4076 aVecSwFrameFormat
.push_back( rDest
.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it
).GetFormat(),
4077 aAnchor
, false, true ) );
4081 // Rebuild as much as possible of all chains that are available in the original,
4082 OSL_ENSURE( aSet
.size() == aVecSwFrameFormat
.size(), "Missing new Flys" );
4083 if ( aSet
.size() != aVecSwFrameFormat
.size() )
4087 for (const auto& rFlyN
: aSet
)
4089 const SwFrameFormat
*pFormatN
= rFlyN
.GetFormat();
4090 const SwFormatChain
&rChain
= pFormatN
->GetChain();
4092 for (const auto& rFlyK
: aSet
)
4094 const SwFrameFormat
*pFormatK
= rFlyK
.GetFormat();
4095 if ( rChain
.GetPrev() == pFormatK
)
4097 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]),
4098 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]) );
4100 else if ( rChain
.GetNext() == pFormatK
)
4102 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]),
4103 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]) );
4110 // Re-create content property of draw formats, knowing how old shapes
4111 // were paired with old fly formats (aOldTextBoxes) and that aSet is
4112 // parallel with aVecSwFrameFormat.
4113 SwTextBoxHelper::restoreLinks(aSet
, aVecSwFrameFormat
, aOldTextBoxes
);
4117 * Reset the text's hard formatting
4119 /** @params pArgs contains the document's ChrFormatTable
4120 * Is need for selections at the beginning/end and with no SSelection.
4122 bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode
* pNd
, void* pArgs
)
4124 ParaRstFormat
* pPara
= static_cast<ParaRstFormat
*>(pArgs
);
4125 if (pPara
->pLayout
&& pPara
->pLayout
->HasMergedParas()
4126 && pNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
4128 return true; // skip hidden, since new items aren't applied
4130 SwTextNode
* pTextNode
= pNd
->GetTextNode();
4131 if( pTextNode
&& pTextNode
->GetpSwpHints() )
4133 SwContentIndex
aSt( pTextNode
, 0 );
4134 sal_Int32 nEnd
= pTextNode
->Len();
4136 if( &pPara
->pSttNd
->GetNode() == pTextNode
&&
4137 pPara
->pSttNd
->GetContentIndex() )
4138 aSt
= pPara
->pSttNd
->GetContentIndex();
4140 if( &pPara
->pEndNd
->GetNode() == pNd
)
4141 nEnd
= pPara
->pEndNd
->GetContentIndex();
4143 if( pPara
->pHistory
)
4145 // Save all attributes for the Undo.
4146 SwRegHistory
aRHst( *pTextNode
, pPara
->pHistory
);
4147 pTextNode
->GetpSwpHints()->Register( &aRHst
);
4148 pTextNode
->RstTextAttr( aSt
.GetIndex(), nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4149 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4150 if( pTextNode
->GetpSwpHints() )
4151 pTextNode
->GetpSwpHints()->DeRegister();
4154 pTextNode
->RstTextAttr( aSt
.GetIndex(), nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4155 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4160 DocumentContentOperationsManager::~DocumentContentOperationsManager()
4165 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4167 assert(m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn());
4169 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4171 if (*rPam
.GetPoint() == *rPam
.GetMark())
4173 return false; // do not add empty redlines
4176 std::vector
<std::unique_ptr
<SwRangeRedline
>> redlines
;
4178 auto pRedline(std::make_unique
<SwRangeRedline
>(RedlineType::Delete
, rPam
));
4179 if (pRedline
->HasValidRange())
4181 redlines
.push_back(std::move(pRedline
));
4183 else // sigh ... why is such a selection even possible...
4184 { // split it up so we get one SwUndoRedlineDelete per inserted RL
4185 redlines
= GetAllValidRanges(std::move(pRedline
));
4189 if (redlines
.empty())
4194 // tdf#54819 current redlining needs also modification of paragraph style and
4195 // attributes added to the same grouped Undo
4196 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4197 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4199 auto & rDMA(*m_rDoc
.getIDocumentMarkAccess());
4200 std::vector
<std::unique_ptr
<SwUndo
>> MarkUndos
;
4201 for (auto iter
= rDMA
.getAnnotationMarksBegin();
4202 iter
!= rDMA
.getAnnotationMarksEnd(); )
4204 // tdf#111524 remove annotation marks that have their field
4205 // characters deleted
4206 SwPosition
const& rEndPos((**iter
).GetMarkEnd());
4207 if (*rPam
.Start() < rEndPos
&& rEndPos
<= *rPam
.End())
4209 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4211 MarkUndos
.emplace_back(std::make_unique
<SwUndoDeleteBookmark
>(**iter
));
4213 // iter is into annotation mark vector so must be dereferenced!
4214 rDMA
.deleteMark(&**iter
);
4215 // this invalidates iter, have to start over...
4216 iter
= rDMA
.getAnnotationMarksBegin();
4219 { // marks are sorted by start
4220 if (*rPam
.End() < (**iter
).GetMarkStart())
4228 // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
4229 if (*rPam
.GetPoint() != *rPam
.GetMark())
4230 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam
);
4232 std::vector
<std::unique_ptr
<SwUndoRedlineDelete
>> undos
;
4233 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4235 // this should no longer happen in calls from the UI but maybe via API
4236 // (randomTest and testTdf54819 triggers it)
4237 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4238 "sw.core", "redlines will be moved in DeleteAndJoin");
4239 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4240 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4242 for (std::unique_ptr
<SwRangeRedline
> & pRedline
: redlines
)
4244 assert(pRedline
->HasValidRange());
4245 undos
.emplace_back(std::make_unique
<SwUndoRedlineDelete
>(
4246 *pRedline
, SwUndoId::DELETE
, flags
));
4248 const SwRewriter aRewriter
= undos
.front()->GetRewriter();
4249 // can only group a single undo action
4250 if (MarkUndos
.empty() && undos
.size() == 1
4251 && m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4253 SwUndo
* const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4254 SwUndoRedlineDelete
*const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete
*>(pLastUndo
));
4255 bool const bMerged
= pUndoRedlineDel
4256 && pUndoRedlineDel
->CanGrouping(*undos
.front());
4259 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(undos
.front()));
4261 undos
.clear(); // prevent unmatched EndUndo
4265 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE
, &aRewriter
);
4266 for (auto& it
: MarkUndos
)
4268 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4270 for (auto & it
: undos
)
4272 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4277 for (std::unique_ptr
<SwRangeRedline
> & pRedline
: redlines
)
4279 // note: 1. the pRedline can still be merged & deleted
4280 // 2. the impl. can even DeleteAndJoin the range => no plain PaM
4281 std::shared_ptr
<SwUnoCursor
> const pCursor(m_rDoc
.CreateUnoCursor(*pRedline
->GetMark()));
4283 *pCursor
->GetPoint() = *pRedline
->GetPoint();
4284 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(pRedline
.release(), true);
4285 // sw_redlinehide: 2 reasons why this is needed:
4286 // 1. it's the first redline in node => RedlineDelText was sent but ignored
4287 // 2. redline spans multiple nodes => must merge text frames
4288 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, *pCursor
);
4290 m_rDoc
.getIDocumentState().SetModified();
4292 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4296 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4298 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4301 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4302 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4307 bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4309 bool bJoinText
, bJoinPrev
;
4310 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4312 bool const bSuccess( DeleteRangeImpl(rPam
, flags
) );
4318 ::sw_JoinText( rPam
, bJoinPrev
);
4321 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
4322 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
4324 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
4330 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4332 // Move all cursors out of the deleted range, but first copy the
4333 // passed PaM, because it could be a cursor that would be moved!
4334 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4336 SwPosition
const pos(GetCorrPosition(aDelPam
));
4337 ::PaMCorrAbs(aDelPam
, pos
);
4340 bool const bSuccess( DeleteRangeImplImpl(aDelPam
, flags
) );
4342 { // now copy position from temp copy to given PaM
4343 *rPam
.GetPoint() = *aDelPam
.GetPoint();
4349 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4351 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
4354 || (*pStt
== *pEnd
&& !IsFlySelectedByCursor(m_rDoc
, *pStt
, *pEnd
)))
4359 if( m_rDoc
.GetAutoCorrExceptWord() )
4361 // if necessary the saved Word for the exception
4362 if( m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() || pStt
->GetNode() != pEnd
->GetNode() ||
4363 pStt
->GetContentIndex() + 1 != pEnd
->GetContentIndex() ||
4364 !m_rDoc
.GetAutoCorrExceptWord()->CheckDelChar( *pStt
))
4365 { m_rDoc
.DeleteAutoCorrExceptWord(); }
4369 // Delete all empty TextHints at the Mark's position
4370 SwTextNode
* pTextNd
= rPam
.GetMark()->GetNode().GetTextNode();
4372 if( pTextNd
&& nullptr != ( pHts
= pTextNd
->GetpSwpHints()) && pHts
->Count() )
4374 const sal_Int32 nMkCntPos
= rPam
.GetMark()->GetContentIndex();
4375 for( size_t n
= pHts
->Count(); n
; )
4377 const SwTextAttr
* pAttr
= pHts
->Get( --n
);
4378 if( nMkCntPos
> pAttr
->GetStart() )
4381 const sal_Int32
*pEndIdx
;
4382 if( nMkCntPos
== pAttr
->GetStart() &&
4383 nullptr != (pEndIdx
= pAttr
->End()) &&
4384 *pEndIdx
== pAttr
->GetStart() )
4385 pTextNd
->DestroyAttr( pHts
->Cut( n
) );
4391 // Send DataChanged before deletion, so that we still know
4392 // which objects are in the range.
4393 // Afterwards they could be before/after the Position.
4394 SwDataChanged
aTmp( rPam
);
4397 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4399 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
4400 bool bMerged(false);
4401 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4403 SwUndo
*const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4404 SwUndoDelete
*const pUndoDelete(
4405 dynamic_cast<SwUndoDelete
*>(pLastUndo
) );
4408 bMerged
= pUndoDelete
->CanGrouping(m_rDoc
, rPam
);
4409 // if CanGrouping() returns true it's already merged
4414 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoDelete
>(rPam
, flags
));
4417 m_rDoc
.getIDocumentState().SetModified();
4422 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4423 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( rPam
, true, RedlineType::Any
);
4425 // Delete and move all "Flys at the paragraph", which are within the Selection
4426 if (!(flags
& SwDeleteFlags::ArtificialSelection
))
4428 DelFlyInRange(rPam
.GetMark()->GetNode(), rPam
.GetPoint()->GetNode(),
4429 rPam
.GetMark()->GetContentIndex(), rPam
.GetPoint()->GetContentIndex());
4435 pStt
->GetContentIndex(),
4436 pEnd
->GetContentIndex());
4438 SwNodeIndex
aSttIdx( pStt
->GetNode() );
4439 SwContentNode
* pCNd
= aSttIdx
.GetNode().GetContentNode();
4441 do { // middle checked loop!
4444 SwTextNode
* pStartTextNode( pCNd
->GetTextNode() );
4445 if ( pStartTextNode
)
4447 // now move the Content to the new Node
4448 bool bOneNd
= pStt
->GetNode() == pEnd
->GetNode();
4449 const sal_Int32 nLen
= ( bOneNd
? pEnd
->GetContentIndex()
4451 - pStt
->GetContentIndex();
4453 // Don't call again, if already empty
4456 pStartTextNode
->EraseText( *pStt
, nLen
);
4458 if( !pStartTextNode
->Len() )
4460 // METADATA: remove reference if empty (consider node deleted)
4461 pStartTextNode
->RemoveMetadataReference();
4465 if( bOneNd
) // that's it
4472 // So that there are no indices left registered when deleted,
4473 // we remove a SwPaM from the Content here.
4474 pStt
->nContent
.Assign( nullptr, 0 );
4478 pCNd
= pEnd
->GetNode().GetContentNode();
4481 SwTextNode
* pEndTextNode( pCNd
->GetTextNode() );
4484 // if already empty, don't call again
4485 if( pEnd
->GetContentIndex() )
4487 SwContentIndex
aIdx( pCNd
, 0 );
4488 pEndTextNode
->EraseText( aIdx
, pEnd
->GetContentIndex() );
4490 if( !pEndTextNode
->Len() )
4492 // METADATA: remove reference if empty (consider node deleted)
4493 pEndTextNode
->RemoveMetadataReference();
4499 // So that there are no indices left registered when deleted,
4500 // we remove a SwPaM from the Content here.
4501 pEnd
->nContent
.Assign( nullptr, 0 );
4505 // if the end is not a content node, delete it as well
4506 SwNodeOffset nEnd
= pEnd
->GetNodeIndex();
4507 if( pCNd
== nullptr )
4510 if( aSttIdx
!= nEnd
)
4512 // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
4514 while (pEnd
== rPam
.GetPoint()
4515 && nEnd
+ SwNodeOffset(2) < m_rDoc
.GetNodes().Count()
4516 && (pTmpNd
= m_rDoc
.GetNodes()[nEnd
+ 1])->IsEndNode()
4517 && pTmpNd
->StartOfSectionNode()->IsSectionNode()
4518 && aSttIdx
<= pTmpNd
->StartOfSectionNode()->GetIndex())
4520 SwNodeRange
range(*pTmpNd
->StartOfSectionNode(), *pTmpNd
);
4521 m_rDoc
.GetNodes().SectionUp(&range
);
4522 --nEnd
; // account for deleted start node
4525 // delete the Nodes from the NodesArray
4526 m_rDoc
.GetNodes().Delete( aSttIdx
, nEnd
- aSttIdx
.GetIndex() );
4529 // If the Node that contained the Cursor has been deleted,
4530 // the Content has to be assigned to the current Content.
4531 if (pStt
->GetNode().GetContentNode())
4532 pStt
->SetContent( pStt
->GetContentIndex() );
4534 // If we deleted across Node boundaries we have to correct the PaM,
4535 // because they are in different Nodes now.
4536 // Also, the Selection is revoked.
4542 m_rDoc
.getIDocumentState().SetModified();
4547 // It's possible to call Replace with a PaM that spans 2 paragraphs:
4548 // search with regex for "$", then replace _all_
4549 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM
& rPam
, const OUString
& rStr
,
4550 const bool bRegExReplace
)
4552 if (!rPam
.HasMark())
4555 bool bJoinText
, bJoinPrev
;
4556 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4559 // Create a copy of the Cursor in order to move all Pams from
4560 // the other views out of the deletion range.
4561 // Except for itself!
4562 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4563 ::PaMCorrAbs( aDelPam
, *aDelPam
.End() );
4565 auto [pStt
, pEnd
] = aDelPam
.StartEnd(); // SwPosition*
4566 bool bOneNode
= pStt
->GetNode() == pEnd
->GetNode();
4569 OUString
sRepl( rStr
);
4570 SwTextNode
* pTextNd
= pStt
->GetNode().GetTextNode();
4571 sal_Int32 nStt
= pStt
->GetContentIndex();
4574 SwDataChanged
aTmp( aDelPam
);
4576 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
4578 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4579 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4581 // this should no longer happen in calls from the UI but maybe via API
4582 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4583 "sw.core", "redlines will be moved in ReplaceRange");
4585 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4587 // If any Redline will change (split!) the node
4588 const ::sw::mark::IMark
* pBkmk
=
4589 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4590 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4591 ::sw::mark::InsertMode::New
);
4593 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4594 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4596 *aDelPam
.GetPoint() = pBkmk
->GetMarkPos();
4597 if(pBkmk
->IsExpanded())
4598 *aDelPam
.GetMark() = pBkmk
->GetOtherMarkPos();
4599 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4600 pStt
= aDelPam
.Start();
4601 pTextNd
= pStt
->GetNode().GetTextNode();
4602 nStt
= pStt
->GetContentIndex();
4605 if( !sRepl
.isEmpty() )
4607 // Apply the first character's attributes to the ReplaceText
4609 <RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
4610 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( m_rDoc
.GetAttrPool() );
4611 pTextNd
->GetParaAttr( aSet
, nStt
+1, nStt
+1 );
4613 aSet
.ClearItem( RES_TXTATR_REFMARK
);
4614 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
4615 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
4616 aSet
.ClearItem( RES_TXTATR_INETFMT
);
4617 aSet
.ClearItem( RES_TXTATR_META
);
4618 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
4620 if( aDelPam
.GetPoint() != aDelPam
.End() )
4624 SwNodeIndex
aPtNd( aDelPam
.GetPoint()->GetNode(), -1 );
4625 const sal_Int32 nPtCnt
= aDelPam
.GetPoint()->GetContentIndex();
4629 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4631 InsertString( aDelPam
, sIns
);
4634 SwNodeIndex
aMkNd( aDelPam
.GetMark()->GetNode(), -1 );
4635 const sal_Int32 nMkCnt
= aDelPam
.GetMark()->GetContentIndex();
4637 SplitNode( *aDelPam
.GetPoint(), false );
4640 aDelPam
.GetMark()->Assign( aMkNd
, nMkCnt
);
4644 SplitNode( *aDelPam
.GetPoint(), false );
4646 if( !sIns
.isEmpty() )
4648 InsertString( aDelPam
, sIns
);
4651 SwPaM
aTmpRange( *aDelPam
.GetPoint() );
4652 aTmpRange
.SetMark();
4655 aDelPam
.GetPoint()->Assign(aPtNd
, nPtCnt
);
4656 *aTmpRange
.GetMark() = *aDelPam
.GetPoint();
4658 m_rDoc
.RstTextAttrs( aTmpRange
);
4659 InsertItemSet( aTmpRange
, aSet
);
4662 // tdf#139982: Appending the redline may immediately delete flys
4663 // anchored in the previous text if it's inside an insert redline.
4664 // Also flys will be deleted if the redline is accepted. Move them
4665 // to the position between the previous text and the new text,
4666 // there the chance of surviving both accept and reject is best.
4668 SaveFlyInRange(aDelPam
, *aDelPam
.End(), flys
, false);
4670 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4672 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
4673 std::make_unique
<SwUndoRedlineDelete
>( aDelPam
, SwUndoId::REPLACE
));
4675 // add redline similar to DeleteAndJoinWithRedlineImpl()
4676 std::shared_ptr
<SwUnoCursor
> const pCursor(m_rDoc
.CreateUnoCursor(*aDelPam
.GetMark()));
4678 *pCursor
->GetPoint() = *aDelPam
.GetPoint();
4679 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete
, aDelPam
), true);
4680 RestFlyInRange(flys
, *aDelPam
.End(), &aDelPam
.End()->GetNode(), true);
4681 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, *pCursor
);
4683 *rPam
.GetMark() = *aDelPam
.GetMark();
4684 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4686 *aDelPam
.GetPoint() = *rPam
.GetPoint();
4687 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4689 // If any Redline will change (split!) the node
4690 const ::sw::mark::IMark
* pBkmk
=
4691 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4692 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4693 ::sw::mark::InsertMode::New
);
4695 aDelPam
.GetPoint()->Assign( SwNodeOffset(0) );
4696 aDelPam
.GetMark()->Assign( SwNodeOffset(0) );
4697 rPam
.GetPoint()->Assign( SwNodeOffset(0) );
4698 *rPam
.GetMark() = *rPam
.GetPoint();
4699 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4701 *rPam
.GetPoint() = pBkmk
->GetMarkPos();
4702 *rPam
.GetMark() = pBkmk
->IsExpanded() ? pBkmk
->GetOtherMarkPos() : pBkmk
->GetMarkPos();
4704 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4710 assert((pStt
->GetNode() == pEnd
->GetNode() ||
4711 ( pStt
->GetNodeIndex() + 1 == pEnd
->GetNodeIndex() &&
4712 !pEnd
->GetContentIndex() )) &&
4713 "invalid range: Point and Mark on different nodes" );
4715 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4716 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aDelPam
, true, RedlineType::Any
);
4718 SwUndoReplace
* pUndoRpl
= nullptr;
4719 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
4722 pUndoRpl
= new SwUndoReplace(aDelPam
, sRepl
, bRegExReplace
);
4723 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndoRpl
));
4725 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
4727 if( aDelPam
.GetPoint() != pStt
)
4730 SwNodeIndex
aPtNd( pStt
->GetNode(), -1 );
4731 const sal_Int32 nPtCnt
= pStt
->GetContentIndex();
4733 // Set the values again, if Frames or footnotes on the Text have been removed.
4735 nEnd
= bOneNode
? pEnd
->GetContentIndex()
4736 : pTextNd
->GetText().getLength();
4740 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4742 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4744 InsertString( aDelPam
, sIns
);
4746 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4748 pTextNd
->ReplaceText( *pStt
, nEnd
- nStt
, sIns
);
4750 SplitNode( *pStt
, false);
4754 if( bFirst
|| !sIns
.isEmpty() )
4756 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4758 InsertString( aDelPam
, sIns
);
4760 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4762 pTextNd
->ReplaceText( *pStt
, nEnd
- nStt
, sIns
);
4766 *rPam
.GetPoint() = *aDelPam
.GetMark();
4768 rPam
.GetMark()->Assign( aPtNd
, nPtCnt
);
4772 assert(rPam
.GetPoint() == rPam
.End());
4773 // move so that SetEnd remembers position after sw_JoinText
4774 rPam
.Move(fnMoveBackward
);
4776 else if (aDelPam
.GetPoint() == pStt
) // backward selection?
4778 assert(*rPam
.GetMark() <= *rPam
.GetPoint());
4779 rPam
.Exchange(); // swap so that rPam is backwards
4784 pUndoRpl
->SetEnd(rPam
);
4792 bRet
= ::sw_JoinText(rPam
, bJoinPrev
);
4795 m_rDoc
.getIDocumentState().SetModified();
4799 SwFlyFrameFormat
* DocumentContentOperationsManager::InsNoTextNode( const SwPosition
& rPos
, SwNoTextNode
* pNode
,
4800 const SfxItemSet
* pFlyAttrSet
,
4801 const SfxItemSet
* pGrfAttrSet
,
4802 SwFrameFormat
* pFrameFormat
)
4804 SwFlyFrameFormat
*pFormat
= nullptr;
4807 pFormat
= m_rDoc
.MakeFlySection_( rPos
, *pNode
, RndStdIds::FLY_AT_PARA
,
4808 pFlyAttrSet
, pFrameFormat
);
4810 pNode
->SetAttr( *pGrfAttrSet
);
4815 #define NUMRULE_STATE \
4816 std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
4817 std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
4819 #define PUSH_NUMRULE_STATE \
4820 lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
4822 #define POP_NUMRULE_STATE \
4823 lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
4825 static void lcl_PushNumruleState(
4826 std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4827 std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4828 const SwTextNode
*pDestTextNd
)
4830 // Safe numrule item at destination.
4831 // #i86492# - Safe also <ListId> item of destination.
4832 const SfxItemSet
* pAttrSet
= pDestTextNd
->GetpSwAttrSet();
4833 if (pAttrSet
== nullptr)
4836 if (const SwNumRuleItem
* pItem
= pAttrSet
->GetItemIfSet(RES_PARATR_NUMRULE
, false))
4838 aNumRuleItemHolderIfSet
.reset(pItem
->Clone());
4841 if (const SfxStringItem
* pItem
= pAttrSet
->GetItemIfSet(RES_PARATR_LIST_ID
, false))
4843 aListIdItemHolderIfSet
.reset(pItem
->Clone());
4847 static void lcl_PopNumruleState(
4848 const std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4849 const std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4850 SwTextNode
*pDestTextNd
, const SwPaM
& rPam
)
4852 /* If only a part of one paragraph is copied
4853 restore the numrule at the destination. */
4854 // #i86492# - restore also <ListId> item
4855 if ( lcl_MarksWholeNode(rPam
) )
4858 if (aNumRuleItemHolderIfSet
)
4860 pDestTextNd
->SetAttr(*aNumRuleItemHolderIfSet
);
4864 pDestTextNd
->ResetAttr(RES_PARATR_NUMRULE
);
4867 if (aListIdItemHolderIfSet
)
4869 pDestTextNd
->SetAttr(*aListIdItemHolderIfSet
);
4873 pDestTextNd
->ResetAttr(RES_PARATR_LIST_ID
);
4877 bool DocumentContentOperationsManager::CopyImpl(SwPaM
& rPam
, SwPosition
& rPos
,
4878 SwCopyFlags
const flags
,
4879 SwPaM
*const pCopyRange
) const
4881 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
4883 sw::CalcBreaks(Breaks
, rPam
, true);
4887 return CopyImplImpl(rPam
, rPos
, flags
, pCopyRange
);
4890 SwPosition
const & rSelectionEnd( *rPam
.End() );
4894 // iterate from end to start, ... don't think it's necessary here?
4895 auto iter( Breaks
.rbegin() );
4896 SwNodeOffset
nOffset(0);
4897 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
4898 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
4899 SwPosition
& rEnd( *aPam
.End() );
4900 SwPosition
& rStart( *aPam
.Start() );
4901 SwPaM
copyRange(rPos
, rPos
);
4903 while (iter
!= Breaks
.rend())
4905 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
4906 if (rStart
< rEnd
) // check if part is empty
4908 // pass in copyRange member as rPos; should work ...
4909 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
4910 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // fly nodes...
4915 pCopyRange
->SetMark();
4916 *pCopyRange
->GetMark() = *copyRange
.End();
4918 *pCopyRange
->GetPoint() = *copyRange
.Start();
4922 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
4926 rStart
= *rPam
.Start(); // set to original start
4927 if (rStart
< rEnd
) // check if part is empty
4929 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
4934 pCopyRange
->SetMark();
4935 *pCopyRange
->GetMark() = *copyRange
.End();
4937 *pCopyRange
->GetPoint() = *copyRange
.Start();
4944 bool DocumentContentOperationsManager::CopyImplImpl(SwPaM
& rPam
, SwPosition
& rPos
,
4945 SwCopyFlags
const flags
,
4946 SwPaM
*const pCpyRange
) const
4948 SwDoc
& rDoc
= rPos
.GetNode().GetDoc();
4949 const bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
4951 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
4953 // Catch when there's no copy to do.
4954 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
) ||
4955 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
4956 //JP 15.11.2001: don't test inclusive the end, ever exclusive
4957 ( &rDoc
== &m_rDoc
&& *pStt
<= rPos
&& rPos
< *pEnd
))
4962 const bool bEndEqualIns
= &rDoc
== &m_rDoc
&& rPos
== *pEnd
;
4964 // If Undo is enabled, create the UndoCopy object
4965 SwUndoCpyDoc
* pUndo
= nullptr;
4966 // lcl_DeleteRedlines may delete the start or end node of the cursor when
4967 // removing the redlines so use cursor that is corrected by PaMCorrAbs
4968 std::shared_ptr
<SwUnoCursor
> const pCopyPam(rDoc
.CreateUnoCursor(rPos
));
4970 SwTableNumFormatMerge
aTNFM( m_rDoc
, rDoc
);
4971 std::optional
<std::vector
<SwFrameFormat
*>> pFlys
;
4972 std::vector
<SwFrameFormat
*> const* pFlysAtInsPos
;
4974 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
4976 pUndo
= new SwUndoCpyDoc(*pCopyPam
);
4977 pFlysAtInsPos
= pUndo
->GetFlysAnchoredAt();
4981 pFlys
= sw::GetFlysAnchoredAt(rDoc
, rPos
.GetNodeIndex());
4982 pFlysAtInsPos
= pFlys
? &*pFlys
: nullptr;
4985 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4986 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
4988 // Move the PaM one node back from the insert position, so that
4989 // the position doesn't get moved
4990 pCopyPam
->SetMark();
4991 bool bCanMoveBack
= pCopyPam
->Move(fnMoveBackward
, GoInContent
);
4992 // If the position was shifted from more than one node, an end node has been skipped
4993 bool bAfterTable
= false;
4994 if ((rPos
.GetNodeIndex() - pCopyPam
->GetPoint()->GetNodeIndex()) > SwNodeOffset(1))
4996 // First go back to the original place
4997 *(pCopyPam
->GetPoint()) = rPos
;
4999 bCanMoveBack
= false;
5004 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(-1));
5005 assert(pCopyPam
->GetPoint()->GetContentIndex() == 0);
5008 SwNodeRange
aRg( pStt
->GetNode(), pEnd
->GetNode() );
5009 SwNodeIndex
aInsPos( rPos
.GetNode() );
5010 const bool bOneNode
= pStt
->GetNode() == pEnd
->GetNode();
5011 SwTextNode
* pSttTextNd
= pStt
->GetNode().GetTextNode();
5012 SwTextNode
* pEndTextNd
= pEnd
->GetNode().GetTextNode();
5013 SwTextNode
* pDestTextNd
= aInsPos
.GetNode().GetTextNode();
5014 bool bCopyCollFormat
= !rDoc
.IsInsOnlyTextGlossary() &&
5015 ( (pDestTextNd
&& !pDestTextNd
->GetText().getLength()) ||
5016 ( !bOneNode
&& !rPos
.GetContentIndex() ) );
5017 bool bCopyBookmarks
= true;
5018 bool bCopyPageSource
= false;
5019 SwNodeOffset
nDeleteTextNodes(0);
5021 // #i104585# copy outline num rule to clipboard (for ASCII filter)
5022 if (rDoc
.IsClipBoard() && m_rDoc
.GetOutlineNumRule())
5024 rDoc
.SetOutlineNumRule(*m_rDoc
.GetOutlineNumRule());
5028 // Correct the search for a previous list:
5029 // First search for non-outline numbering list. Then search for non-outline
5031 // Keep also the <ListId> value for possible propagation.
5032 OUString aListIdToPropagate
;
5033 const SwNumRule
* pNumRuleToPropagate
=
5034 rDoc
.SearchNumRule( rPos
, false, true, false, 0, aListIdToPropagate
, nullptr, true );
5035 if ( !pNumRuleToPropagate
)
5037 pNumRuleToPropagate
=
5038 rDoc
.SearchNumRule( rPos
, false, false, false, 0, aListIdToPropagate
, nullptr, true );
5041 // Do not propagate previous found list, if
5042 // - destination is an empty paragraph which is not in a list and
5043 // - source contains at least one paragraph which is not in a list
5044 if ( pNumRuleToPropagate
&&
5045 pDestTextNd
&& !pDestTextNd
->GetText().getLength() &&
5046 !pDestTextNd
->IsInList() &&
5047 !lcl_ContainsOnlyParagraphsInList( rPam
) )
5049 pNumRuleToPropagate
= nullptr;
5052 // This do/while block is only there so that we can break out of it!
5056 ++nDeleteTextNodes
; // must be joined in Undo
5057 // Don't copy the beginning completely?
5058 if( !bCopyCollFormat
|| bColumnSel
|| pStt
->GetContentIndex() )
5060 SwContentIndex
aDestIdx( rPos
.GetContentNode(), rPos
.GetContentIndex() );
5061 bool bCopyOk
= false;
5064 if( pStt
->GetContentIndex() || bOneNode
)
5065 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
.GetNode(),
5066 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
5069 pDestTextNd
= pSttTextNd
->MakeCopy(rDoc
, aInsPos
.GetNode(), true)->GetTextNode();
5072 aDestIdx
.Assign( pDestTextNd
, 0 );
5073 bCopyCollFormat
= true;
5075 else if( !bOneNode
|| bColumnSel
)
5077 const sal_Int32 nContentEnd
= pEnd
->GetContentIndex();
5079 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5080 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
5083 if (bCanMoveBack
&& rPos
== *pCopyPam
->GetPoint())
5085 // after the SplitNode, span the CpyPam correctly again
5086 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5087 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5090 pDestTextNd
= rDoc
.GetNodes()[ aInsPos
.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
5092 pDestTextNd
, pDestTextNd
->GetText().getLength());
5094 // Correct the area again
5097 bool bChg
= pEnd
!= rPam
.GetPoint();
5100 rPam
.Move( fnMoveBackward
, GoInContent
);
5104 else if( rPos
== *pEnd
)
5106 // The end was also moved
5107 pEnd
->Adjust(SwNodeOffset(-1));
5108 pEnd
->SetContent( nContentEnd
);
5110 // tdf#63022 always reset pEndTextNd after SplitNode
5111 aRg
.aEnd
= pEnd
->GetNode();
5112 pEndTextNd
= pEnd
->GetNode().GetTextNode();
5116 if( bCopyCollFormat
&& bOneNode
)
5123 const sal_Int32 nCpyLen
= ( bOneNode
5124 ? pEnd
->GetContentIndex()
5125 : pSttTextNd
->GetText().getLength())
5126 - pStt
->GetContentIndex();
5127 pSttTextNd
->CopyText( pDestTextNd
, aDestIdx
, *pStt
, nCpyLen
);
5129 pEnd
->AdjustContent( -nCpyLen
);
5136 if (bCopyCollFormat
)
5138 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5139 pSttTextNd
->CopyCollFormat(*pDestTextNd
, false);
5143 // Copy at-char flys in rPam.
5144 // Update to new (start) node for flys.
5145 // tdf#126626 prevent duplicate Undos.
5146 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5147 CopyFlyInFlyImpl(aRg
, &rPam
, *pDestTextNd
, false);
5153 else if( pDestTextNd
)
5155 // Problems with insertion of table selections into "normal" text solved.
5156 // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
5157 // the undo operation will try to merge this node after removing the table.
5158 // If we didn't split a textnode, the PaM should start at the inserted table node
5159 if( rPos
.GetContentIndex() == pDestTextNd
->Len() )
5160 { // Insertion at the last position of a textnode (empty or not)
5161 ++aInsPos
; // The table will be inserted behind the text node
5163 else if( rPos
.GetContentIndex() )
5164 { // Insertion in the middle of a text node, it has to be split
5165 // (and joined from undo)
5168 const sal_Int32 nContentEnd
= pEnd
->GetContentIndex();
5170 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5171 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
5174 if (bCanMoveBack
&& rPos
== *pCopyPam
->GetPoint())
5176 // after the SplitNode, span the CpyPam correctly again
5177 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5178 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5181 // Correct the area again
5184 // The end would also be moved
5185 else if( rPos
== *pEnd
)
5187 rPos
.Adjust(SwNodeOffset(-1));
5188 rPos
.SetContent( nContentEnd
);
5192 else if( bCanMoveBack
)
5193 { // Insertion at the first position of a text node. It will not be split, the table
5194 // will be inserted before the text node.
5195 // See below, before the SetInsertRange function of the undo object will be called,
5196 // the CpyPam would be moved to the next content position. This has to be avoided
5197 // We want to be moved to the table node itself thus we have to set bCanMoveBack
5198 // and to manipulate pCopyPam.
5199 bCanMoveBack
= false;
5200 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(-1));
5204 pDestTextNd
= aInsPos
.GetNode().GetTextNode();
5207 SwContentIndex
aDestIdx( aInsPos
.GetNode().GetContentNode(), rPos
.GetContentIndex() );
5210 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
.GetNode(),
5211 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
5212 aDestIdx
.Assign( pDestTextNd
, 0 );
5215 // if we have to insert an extra text node
5216 // at the destination, this node will be our new destination
5217 // (text) node, and thus we increment nDeleteTextNodes. This
5218 // will ensure that this node will be deleted during Undo.
5219 ++nDeleteTextNodes
; // must be deleted
5222 const bool bEmptyDestNd
= pDestTextNd
->GetText().isEmpty();
5225 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5230 pEndTextNd
->CopyText( pDestTextNd
, aDestIdx
, SwContentIndex( pEndTextNd
),
5231 pEnd
->GetContentIndex() );
5233 // Also copy all format templates
5234 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5236 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5237 pEndTextNd
->CopyCollFormat(*pDestTextNd
, false);
5245 SfxItemSet
aBrkSet( rDoc
.GetAttrPool(), aBreakSetRange
);
5246 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5248 if (pSttTextNd
&& bCopyCollFormat
&& pDestTextNd
->HasSwAttrSet())
5250 aBrkSet
.Put( *pDestTextNd
->GetpSwAttrSet() );
5251 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_BREAK
, false ) )
5252 pDestTextNd
->ResetAttr( RES_BREAK
);
5253 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_PAGEDESC
, false ) )
5254 pDestTextNd
->ResetAttr( RES_PAGEDESC
);
5259 SwPosition
startPos(pCopyPam
->GetPoint()->GetNode(), SwNodeOffset(+1));
5261 { // pCopyPam is actually 1 before the copy range so move it fwd
5262 SwPaM
temp(*pCopyPam
->GetPoint());
5263 temp
.Move(fnMoveForward
, GoInContent
);
5264 startPos
= *temp
.GetPoint();
5266 assert(startPos
.GetNode().IsContentNode());
5267 std::pair
<SwPaM
const&, SwPosition
const&> tmp(rPam
, startPos
);
5268 if( aInsPos
== pEnd
->GetNode() )
5270 SwNodeIndex
aSaveIdx( aInsPos
, -1 );
5271 assert(pStt
->GetNode() != pEnd
->GetNode());
5272 pEnd
->SetContent(0); // TODO why this?
5273 CopyWithFlyInFly(aRg
, aInsPos
.GetNode(), &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5275 pEnd
->Assign(aSaveIdx
);
5278 CopyWithFlyInFly(aRg
, aInsPos
.GetNode(), &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5280 bCopyBookmarks
= false;
5283 // at-char anchors post SplitNode are on index 0 of 2nd node and will
5284 // remain there - move them back to the start (end would also work?)
5285 // ... also for at-para anchors; here start is preferable because
5286 // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
5289 // init *again* - because CopyWithFlyInFly moved startPos
5290 SwPosition
startPos(pCopyPam
->GetPoint()->GetNode(), SwNodeOffset(+1));
5292 { // pCopyPam is actually 1 before the copy range so move it fwd
5293 SwPaM
temp(*pCopyPam
->GetPoint());
5294 temp
.Move(fnMoveForward
, GoInContent
);
5295 startPos
= *temp
.GetPoint();
5297 assert(startPos
.GetNode().IsContentNode());
5298 SwPosition
startPosAtPara(startPos
);
5299 startPosAtPara
.nContent
.Assign(nullptr, 0);
5301 for (SwFrameFormat
* pFly
: *pFlysAtInsPos
)
5303 SwFormatAnchor
const*const pAnchor
= &pFly
->GetAnchor();
5304 if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
5306 SwFormatAnchor
anchor(*pAnchor
);
5307 anchor
.SetAnchor( &startPos
);
5308 pFly
->SetFormatAttr(anchor
);
5310 else if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_PARA
)
5312 SwFormatAnchor
anchor(*pAnchor
);
5313 anchor
.SetAnchor( &startPosAtPara
);
5315 bool bSplitFly
= false;
5316 if (pFly
->GetFlySplit().GetValue())
5318 SwIterator
<SwFrame
, SwModify
> aIter(*pFly
);
5319 bSplitFly
= aIter
.First() && aIter
.Next();
5323 // This fly format has multiple frames, and we change the anchor. Remove the
5324 // old frames, which were based on the old anchor position.
5328 pFly
->SetFormatAttr(anchor
);
5332 // Re-create the frames now that the new anchor is set.
5339 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5341 // Put the breaks back into the first node
5342 if( aBrkSet
.Count() && nullptr != ( pDestTextNd
= rDoc
.GetNodes()[
5343 pCopyPam
->GetPoint()->GetNodeIndex()+1 ]->GetTextNode()))
5345 pDestTextNd
->SetAttr( aBrkSet
);
5346 bCopyPageSource
= true;
5352 // it is not possible to make this test when copy from the clipBoard to document
5353 // in this case the PageNum not exist anymore
5354 // tdf#39400 and tdf#97526
5355 // when copy from document to ClipBoard, and it is from the first page
5356 // and not the source has the page break
5357 if (rDoc
.IsClipBoard() && (rPam
.GetPageNum(pStt
== rPam
.GetPoint()) == 1) && !bCopyPageSource
)
5361 pDestTextNd
->ResetAttr(RES_BREAK
); // remove the page-break
5362 pDestTextNd
->ResetAttr(RES_PAGEDESC
);
5367 // Adjust position (in case it was moved / in another node)
5368 rPos
.nContent
.Assign( rPos
.GetNode().GetContentNode(),
5369 rPos
.GetContentIndex() );
5371 if( rPos
.GetNode() != aInsPos
.GetNode() )
5373 if (aInsPos
< rPos
.GetNode())
5374 { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
5375 pCopyPam
->GetMark()->AssignEndIndex(*aInsPos
.GetNode().GetContentNode());
5377 else // incremented in (!pSttTextNd && pDestTextNd) above
5379 pCopyPam
->GetMark()->Assign(aInsPos
);
5381 rPos
= *pCopyPam
->GetMark();
5384 *pCopyPam
->GetMark() = rPos
;
5387 pCopyPam
->Move( fnMoveForward
, bCanMoveBack
? GoInContent
: GoInNode
);
5390 // Reset the offset to 0 as it was before the insertion
5391 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(+1));
5393 // If the next node is a start node, then step back: the start node
5394 // has been copied and needs to be in the selection for the undo
5395 if (pCopyPam
->GetPoint()->GetNode().IsStartNode())
5396 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(-1));
5399 pCopyPam
->Exchange();
5401 // Also copy all bookmarks
5402 if( bCopyBookmarks
&& m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount() )
5404 sw::CopyBookmarks(rPam
, *pCopyPam
->Start());
5407 if( RedlineFlags::DeleteRedlines
& eOld
)
5409 assert(*pCopyPam
->GetPoint() == rPos
);
5410 // the Node rPos points to may be deleted so unregister ...
5411 rPos
.nContent
.Assign(nullptr, 0);
5412 lcl_DeleteRedlines(rPam
, *pCopyPam
);
5413 rPos
= *pCopyPam
->GetPoint(); // ... and restore.
5416 // If Undo is enabled, store the inserted area
5417 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
5419 // append it after styles have been copied when copying nodes
5420 rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
5421 pUndo
->SetInsertRange(*pCopyPam
, true, nDeleteTextNodes
);
5426 pCpyRange
->SetMark();
5427 *pCpyRange
->GetPoint() = *pCopyPam
->GetPoint();
5428 *pCpyRange
->GetMark() = *pCopyPam
->GetMark();
5431 if ( pNumRuleToPropagate
!= nullptr )
5433 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
5434 // Don't reset indent attributes, that would mean loss of direct
5436 rDoc
.SetNumRule( *pCopyPam
, *pNumRuleToPropagate
, false, nullptr,
5437 aListIdToPropagate
, true, /*bResetIndentAttrs=*/false );
5440 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
5441 rDoc
.getIDocumentState().SetModified();
5448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */