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>
33 #include <textboxhelper.hxx>
34 #include <dcontact.hxx>
36 #include <numrule.hxx>
37 #include <charfmt.hxx>
39 #include <ndnotxt.hxx>
41 #include <breakit.hxx>
43 #include <fmtanchr.hxx>
44 #include <fmtcntnt.hxx>
45 #include <fmtinfmt.hxx>
46 #include <fmtpdsc.hxx>
47 #include <fmtcnct.hxx>
48 #include <SwStyleNameMapper.hxx>
49 #include <redline.hxx>
51 #include <rootfrm.hxx>
52 #include <frmtool.hxx>
53 #include <unocrsr.hxx>
56 #include <poolfmt.hxx>
58 #include <txatbase.hxx>
59 #include <UndoRedline.hxx>
61 #include <UndoBookmark.hxx>
62 #include <UndoDelete.hxx>
63 #include <UndoSplitMove.hxx>
64 #include <UndoOverwrite.hxx>
65 #include <UndoInsert.hxx>
66 #include <UndoAttribute.hxx>
68 #include <acorrect.hxx>
69 #include <bookmark.hxx>
73 #include <fmtflcnt.hxx>
75 #include <frameformats.hxx>
76 #include <formatflysplit.hxx>
77 #include <o3tl/safeint.hxx>
78 #include <sal/log.hxx>
79 #include <unotools/charclass.hxx>
80 #include <unotools/configmgr.hxx>
81 #include <unotools/transliterationwrapper.hxx>
82 #include <i18nutil/transliteration.hxx>
83 #include <sfx2/Metadatable.hxx>
84 #include <sot/exchange.hxx>
85 #include <svl/stritem.hxx>
86 #include <svl/itemiter.hxx>
87 #include <svx/svdobj.hxx>
88 #include <svx/svdouno.hxx>
89 #include <tools/globname.hxx>
90 #include <editeng/formatbreakitem.hxx>
91 #include <com/sun/star/i18n/Boundary.hpp>
92 #include <com/sun/star/i18n/WordType.hpp>
93 #include <com/sun/star/i18n/XBreakIterator.hpp>
94 #include <com/sun/star/embed/XEmbeddedObject.hpp>
100 using namespace ::com::sun::star::i18n
;
104 // Copy method from SwDoc
105 // Prevent copying into Flys that are anchored in the range
106 bool lcl_ChkFlyFly( SwDoc
& rDoc
, SwNodeOffset nSttNd
, SwNodeOffset nEndNd
,
107 SwNodeOffset nInsNd
)
110 for(sw::SpzFrameFormat
* pFormat
: *rDoc
.GetSpzFrameFormats())
112 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
113 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
115 ((RndStdIds::FLY_AS_CHAR
== pAnchor
->GetAnchorId()) ||
116 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()) ||
117 (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId()) ||
118 (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId())) &&
119 nSttNd
<= pAnchorNode
->GetIndex() &&
120 pAnchorNode
->GetIndex() < nEndNd
)
122 const SwFormatContent
& rContent
= pFormat
->GetContent();
124 if( !rContent
.GetContentIdx() ||
125 nullptr == ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ))
128 if( pSNd
->GetIndex() < nInsNd
&&
129 nInsNd
< pSNd
->EndOfSectionIndex() )
133 if( lcl_ChkFlyFly( rDoc
, pSNd
->GetIndex(),
134 pSNd
->EndOfSectionIndex(), nInsNd
) )
143 SwNodeIndex
InitDelCount(SwPaM
const& rSourcePaM
, SwNodeOffset
& rDelCount
)
145 SwPosition
const& rStart(*rSourcePaM
.Start());
146 // Special handling for SwDoc::AppendDoc
147 if (rSourcePaM
.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
148 == rStart
.GetNodeIndex())
150 rDelCount
= SwNodeOffset(1);
151 return SwNodeIndex(rStart
.GetNode(), +1);
155 rDelCount
= SwNodeOffset(0);
156 return SwNodeIndex(rStart
.GetNode());
161 The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
162 array. It is called after a call of the CopyNodes(..) function. But this function does not copy
163 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
164 if the corresponding end/start node is outside the copied pam.
165 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
166 index inside the pam.
167 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
168 of "non-copy" nodes between rPam.Start() and rLastIdx.
169 nNewIdx is the new position of interest.
171 void lcl_NonCopyCount( const SwPaM
& rPam
, SwNodeIndex
& rLastIdx
, const SwNodeOffset nNewIdx
, SwNodeOffset
& rDelCount
)
173 SwNodeOffset nStart
= rPam
.Start()->GetNodeIndex();
174 SwNodeOffset nEnd
= rPam
.End()->GetNodeIndex();
175 if( rLastIdx
.GetIndex() < nNewIdx
) // Moving forward?
177 // We never copy the StartOfContent node
178 do // count "non-copy" nodes
180 SwNode
& rNode
= rLastIdx
.GetNode();
181 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
182 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
188 while( rLastIdx
.GetIndex() < nNewIdx
);
190 else if( rDelCount
) // optimization: if there are no "non-copy" nodes until now,
191 // no move backward needed
193 while( rLastIdx
.GetIndex() > nNewIdx
)
195 SwNode
& rNode
= rLastIdx
.GetNode();
196 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
197 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
206 void lcl_SetCpyPos( const SwPosition
& rOrigPos
,
207 const SwPosition
& rOrigStt
,
208 const SwPosition
& rCpyStt
,
210 SwNodeOffset nDelCount
)
212 SwNodeOffset nNdOff
= rOrigPos
.GetNodeIndex();
213 nNdOff
-= rOrigStt
.GetNodeIndex();
215 sal_Int32 nContentPos
= rOrigPos
.GetContentIndex();
217 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
218 rChgPos
.Assign( nNdOff
+ rCpyStt
.GetNodeIndex() );
219 if (!rChgPos
.GetNode().GetContentNode())
223 // just adapt the content index
224 if( nContentPos
> rOrigStt
.GetContentIndex() )
225 nContentPos
-= rOrigStt
.GetContentIndex();
228 nContentPos
+= rCpyStt
.GetContentIndex();
230 rChgPos
.SetContent( nContentPos
);
237 // TODO: use SaveBookmark (from DelBookmarks)
238 void CopyBookmarks(const SwPaM
& rPam
, const SwPosition
& rCpyPam
, SwCopyFlags flags
)
240 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
241 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
242 const IDocumentMarkAccess
* const pSrcMarkAccess
= rSrcDoc
.getIDocumentMarkAccess();
243 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
245 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
246 SwPosition
const*const pCpyStt
= &rCpyPam
;
248 std::vector
< const ::sw::mark::IMark
* > vMarksToCopy
;
249 for ( IDocumentMarkAccess::const_iterator_t ppMark
= pSrcMarkAccess
->getAllMarksBegin();
250 ppMark
!= pSrcMarkAccess
->getAllMarksEnd();
253 const ::sw::mark::IMark
* const pMark
= *ppMark
;
255 const SwPosition
& rMarkStart
= pMark
->GetMarkStart();
256 const SwPosition
& rMarkEnd
= pMark
->GetMarkEnd();
257 // only include marks that are in the range and not touching both start and end
258 // - not for annotation or checkbox marks.
259 bool const isIncludeStart(
260 (rStt
.GetContentIndex() == 0 // paragraph start selected?
261 // also: only if inserting at the start - cross reference
262 // marks require index to be 0, and there could be one
263 // on the target node already
264 && rCpyPam
.GetContentIndex() == 0)
265 || rMarkStart
!= rStt
);
266 bool const isIncludeEnd(
267 (rEnd
.GetNode().IsTextNode() // paragraph end selected?
268 && rEnd
.GetContentIndex() == rEnd
.GetNode().GetTextNode()->Len())
269 || rMarkEnd
!= rEnd
);
270 const bool bIsNotOnBoundary
=
272 ? (isIncludeStart
|| isIncludeEnd
) // rMarkStart != rMarkEnd
273 : (isIncludeStart
&& isIncludeEnd
); // rMarkStart == rMarkEnd
274 const IDocumentMarkAccess::MarkType aMarkType
= IDocumentMarkAccess::GetType(*pMark
);
275 if ( rMarkStart
>= rStt
&& rMarkEnd
<= rEnd
276 && ( bIsNotOnBoundary
277 || aMarkType
== IDocumentMarkAccess::MarkType::ANNOTATIONMARK
278 || aMarkType
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
279 || aMarkType
== IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
280 || aMarkType
== IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
281 || aMarkType
== IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
283 vMarksToCopy
.push_back(pMark
);
286 // We have to count the "non-copied" nodes...
287 SwNodeOffset nDelCount
;
288 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
289 for(const sw::mark::IMark
* const pMark
: vMarksToCopy
)
291 SwPaM
aTmpPam(*pCpyStt
);
292 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetMarkPos().GetNodeIndex(), nDelCount
);
293 lcl_SetCpyPos( pMark
->GetMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetPoint(), nDelCount
);
294 if(pMark
->IsExpanded())
297 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetOtherMarkPos().GetNodeIndex(), nDelCount
);
298 lcl_SetCpyPos(pMark
->GetOtherMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetMark(), nDelCount
);
301 OUString sRequestedName
= pMark
->GetName();
302 if (flags
& SwCopyFlags::IsMoveToFly
)
304 assert(&rSrcDoc
== &rDestDoc
);
305 // Ensure the name can be given to NewMark, since this is ultimately a move
306 auto pSoonToBeDeletedMark
= const_cast<sw::mark::IMark
*>(pMark
);
307 rDestDoc
.getIDocumentMarkAccess()->renameMark(pSoonToBeDeletedMark
,
308 sRequestedName
+ "COPY_IS_MOVE");
311 ::sw::mark::IMark
* const pNewMark
= rDestDoc
.getIDocumentMarkAccess()->makeMark(
314 IDocumentMarkAccess::GetType(*pMark
),
315 ::sw::mark::InsertMode::CopyText
);
316 // Explicitly try to get exactly the same name as in the source
317 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
318 if (pNewMark
== nullptr)
320 assert(IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
321 || IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
);
322 continue; // can't insert duplicate cross reference mark
324 rDestDoc
.getIDocumentMarkAccess()->renameMark(pNewMark
, sRequestedName
);
326 // copying additional attributes for bookmarks or fieldmarks
327 ::sw::mark::IBookmark
* const pNewBookmark
=
328 dynamic_cast< ::sw::mark::IBookmark
* const >(pNewMark
);
329 const ::sw::mark::IBookmark
* const pOldBookmark
=
330 dynamic_cast< const ::sw::mark::IBookmark
* >(pMark
);
331 if (pNewBookmark
&& pOldBookmark
)
333 pNewBookmark
->SetKeyCode(pOldBookmark
->GetKeyCode());
334 pNewBookmark
->SetShortName(pOldBookmark
->GetShortName());
335 pNewBookmark
->Hide(pOldBookmark
->IsHidden());
336 pNewBookmark
->SetHideCondition(pOldBookmark
->GetHideCondition());
338 ::sw::mark::IFieldmark
* const pNewFieldmark
=
339 dynamic_cast< ::sw::mark::IFieldmark
* const >(pNewMark
);
340 const ::sw::mark::IFieldmark
* const pOldFieldmark
=
341 dynamic_cast< const ::sw::mark::IFieldmark
* >(pMark
);
342 if (pNewFieldmark
&& pOldFieldmark
)
344 pNewFieldmark
->SetFieldname(pOldFieldmark
->GetFieldname());
345 pNewFieldmark
->SetFieldHelptext(pOldFieldmark
->GetFieldHelptext());
346 ::sw::mark::IFieldmark::parameter_map_t
* pNewParams
= pNewFieldmark
->GetParameters();
347 const ::sw::mark::IFieldmark::parameter_map_t
* pOldParams
= pOldFieldmark
->GetParameters();
348 for (const auto& rEntry
: *pOldParams
)
350 pNewParams
->insert( rEntry
);
354 ::sfx2::Metadatable
const*const pMetadatable(
355 dynamic_cast< ::sfx2::Metadatable
const* >(pMark
));
356 ::sfx2::Metadatable
*const pNewMetadatable(
357 dynamic_cast< ::sfx2::Metadatable
* >(pNewMark
));
358 if (pMetadatable
&& pNewMetadatable
)
360 pNewMetadatable
->RegisterAsCopyOf(*pMetadatable
);
368 void lcl_DeleteRedlines( const SwPaM
& rPam
, SwPaM
& rCpyPam
)
370 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
371 const SwRedlineTable
& rTable
= rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable();
375 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
376 SwPosition
* pCpyStt
= rCpyPam
.Start(), *pCpyEnd
= rCpyPam
.End();
377 std::unique_ptr
<SwPaM
> pDelPam
;
378 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
379 // We have to count the "non-copied" nodes
380 SwNodeOffset nDelCount
;
381 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
383 SwRedlineTable::size_type n
= 0;
384 rSrcDoc
.getIDocumentRedlineAccess().GetRedline( *pStt
, &n
);
385 for( ; n
< rTable
.size(); ++n
)
387 const SwRangeRedline
* pRedl
= rTable
[ n
];
388 if( RedlineType::Delete
== pRedl
->GetType() && pRedl
->IsVisible() )
390 auto [pRStt
, pREnd
] = pRedl
->StartEnd(); // SwPosition*
392 SwComparePosition eCmpPos
= ComparePosition( *pStt
, *pEnd
, *pRStt
, *pREnd
);
395 case SwComparePosition::CollideEnd
:
396 case SwComparePosition::Before
:
397 // Pos1 is before Pos2
400 case SwComparePosition::CollideStart
:
401 case SwComparePosition::Behind
:
402 // Pos1 is after Pos2
408 pDelPam
.reset(new SwPaM( *pCpyStt
, pDelPam
.release() ));
411 lcl_NonCopyCount( rPam
, aCorrIdx
, pRStt
->GetNodeIndex(), nDelCount
);
412 lcl_SetCpyPos( *pRStt
, *pStt
, *pCpyStt
,
413 *pDelPam
->GetPoint(), nDelCount
);
418 *pDelPam
->GetPoint() = *pCpyEnd
;
421 lcl_NonCopyCount( rPam
, aCorrIdx
, pREnd
->GetNodeIndex(), nDelCount
);
422 lcl_SetCpyPos( *pREnd
, *pStt
, *pCpyStt
,
423 *pDelPam
->GetPoint(), nDelCount
);
426 if (pDelPam
->GetNext() != pDelPam
.get()
427 && *pDelPam
->GetNext()->End() == *pDelPam
->Start())
429 *pDelPam
->GetNext()->End() = *pDelPam
->End();
430 pDelPam
.reset(pDelPam
->GetNext());
440 RedlineFlags eOld
= rDestDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
441 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
| RedlineFlags::Ignore
);
443 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
445 // At this point, pDelPam points to the last of maybe several disjoint selections, organized
446 // in reverse order in document (so every GetNext() returns a PaM closer to document start,
447 // until wrap to pDelPam). Removal of the selections must be from last in document to first,
448 // to avoid situations when another PaM in chain points into the node that will be destroyed
449 // (joined to previous) by removal of the currently processed PaM.
451 rDestDoc
.getIDocumentContentOperations().DeleteAndJoin(*pDelPam
);
452 if( !pDelPam
->IsMultiSelection() )
454 pDelPam
.reset(pDelPam
->GetNext());
457 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
460 void lcl_DeleteRedlines( const SwNodeRange
& rRg
, SwNodeRange
const & rCpyRg
)
462 SwDoc
& rSrcDoc
= rRg
.aStart
.GetNode().GetDoc();
463 if( !rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
465 SwPaM
aRgTmp( rRg
.aStart
, rRg
.aEnd
);
466 SwPaM
aCpyTmp( rCpyRg
.aStart
, rCpyRg
.aEnd
);
467 lcl_DeleteRedlines( aRgTmp
, aCpyTmp
);
471 void lcl_ChainFormats( SwFlyFrameFormat
*pSrc
, SwFlyFrameFormat
*pDest
)
473 SwFormatChain
aSrc( pSrc
->GetChain() );
474 if ( !aSrc
.GetNext() )
476 aSrc
.SetNext( pDest
);
477 pSrc
->SetFormatAttr( aSrc
);
479 SwFormatChain
aDest( pDest
->GetChain() );
480 if ( !aDest
.GetPrev() )
482 aDest
.SetPrev( pSrc
);
483 pDest
->SetFormatAttr( aDest
);
488 bool lcl_ContainsOnlyParagraphsInList( const SwPaM
& rPam
)
492 const SwTextNode
* pTextNd
= rPam
.Start()->GetNode().GetTextNode();
493 const SwTextNode
* pEndTextNd
= rPam
.End()->GetNode().GetTextNode();
494 if ( pTextNd
&& pTextNd
->IsInList() &&
495 pEndTextNd
&& pEndTextNd
->IsInList() )
498 SwNodeIndex
aIdx(rPam
.Start()->GetNode());
503 pTextNd
= aIdx
.GetNode().GetTextNode();
505 if ( !pTextNd
|| !pTextNd
->IsInList() )
510 } while (pTextNd
!= pEndTextNd
);
516 bool lcl_MarksWholeNode(const SwPaM
& rPam
)
518 bool bResult
= false;
519 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
521 if (nullptr != pStt
&& nullptr != pEnd
)
523 const SwTextNode
* pSttNd
= pStt
->GetNode().GetTextNode();
524 const SwTextNode
* pEndNd
= pEnd
->GetNode().GetTextNode();
526 if (nullptr != pSttNd
&& nullptr != pEndNd
&&
527 pStt
->GetContentIndex() == 0 &&
528 pEnd
->GetContentIndex() == pEndNd
->Len())
538 //local functions originally from sw/source/core/doc/docedt.cxx
541 void CalcBreaks(std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> & rBreaks
,
542 SwPaM
const & rPam
, bool const isOnlyFieldmarks
)
544 SwNodeOffset
const nStartNode(rPam
.Start()->GetNodeIndex());
545 SwNodeOffset
const nEndNode(rPam
.End()->GetNodeIndex());
546 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
547 IDocumentMarkAccess
const& rIDMA(*rPam
.GetDoc().getIDocumentMarkAccess());
549 std::stack
<std::tuple
<sw::mark::IFieldmark
const*, bool, SwNodeOffset
, sal_Int32
>> startedFields
;
551 for (SwNodeOffset n
= nStartNode
; n
<= nEndNode
; ++n
)
553 SwNode
*const pNode(rNodes
[n
]);
554 if (pNode
->IsTextNode())
556 SwTextNode
& rTextNode(*pNode
->GetTextNode());
557 sal_Int32
const nStart(n
== nStartNode
558 ? rPam
.Start()->GetContentIndex()
560 sal_Int32
const nEnd(n
== nEndNode
561 ? rPam
.End()->GetContentIndex()
563 for (sal_Int32 i
= nStart
; i
< nEnd
; ++i
)
565 const sal_Unicode
c(rTextNode
.GetText()[i
]);
568 // note: CH_TXT_ATR_FORMELEMENT does not need handling
569 // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
570 case CH_TXTATR_INWORD
:
571 case CH_TXTATR_BREAKWORD
:
573 // META hints only have dummy char at the start, not
574 // at the end, so no need to check in nStartNode
575 if (n
== nEndNode
&& !isOnlyFieldmarks
)
577 SwTextAttr
const* pAttr(rTextNode
.GetTextAttrForCharAt(i
));
578 if (pAttr
&& pAttr
->End() && (nEnd
< *pAttr
->End()))
580 assert(pAttr
->HasDummyChar());
581 rBreaks
.emplace_back(n
, i
);
586 // See if this is an end dummy character for a content control.
587 pAttr
= rTextNode
.GetTextAttrForEndCharAt(i
, RES_TXTATR_CONTENTCONTROL
);
588 if (pAttr
&& (nStart
> pAttr
->GetStart()))
590 rBreaks
.emplace_back(n
, i
);
596 case CH_TXT_ATR_FIELDSTART
:
598 auto const pFieldMark(rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
599 startedFields
.emplace(pFieldMark
, false, 0, 0);
602 case CH_TXT_ATR_FIELDSEP
:
604 if (startedFields
.empty())
606 rBreaks
.emplace_back(n
, i
);
609 { // no way to find the field via MarkManager...
610 assert(std::get
<0>(startedFields
.top())->IsCoveringPosition(SwPosition(rTextNode
, i
)));
611 std::get
<1>(startedFields
.top()) = true;
612 std::get
<2>(startedFields
.top()) = n
;
613 std::get
<3>(startedFields
.top()) = i
;
617 case CH_TXT_ATR_FIELDEND
:
619 if (startedFields
.empty())
621 rBreaks
.emplace_back(n
, i
);
624 { // fieldmarks must not overlap => stack
625 assert(std::get
<0>(startedFields
.top()) == rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
633 else if (pNode
->IsStartNode())
635 if (pNode
->EndOfSectionIndex() <= nEndNode
)
636 { // fieldmark cannot overlap node section
637 n
= pNode
->EndOfSectionIndex();
641 { // EndNode can actually happen with sections :(
642 assert(pNode
->IsEndNode() || pNode
->IsNoTextNode());
645 while (!startedFields
.empty())
647 if (const sw::mark::IFieldmark
* pMark
= std::get
<0>(startedFields
.top()))
649 SwPosition
const& rStart(pMark
->GetMarkStart());
650 std::pair
<SwNodeOffset
, sal_Int32
> const pos(
651 rStart
.GetNodeIndex(), rStart
.GetContentIndex());
652 auto it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), pos
);
653 assert(it
== rBreaks
.end() || *it
!= pos
);
654 rBreaks
.insert(it
, pos
);
656 if (std::get
<1>(startedFields
.top()))
658 std::pair
<SwNodeOffset
, sal_Int32
> const posSep(
659 std::get
<2>(startedFields
.top()),
660 std::get
<3>(startedFields
.top()));
661 auto it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), posSep
);
662 assert(it
== rBreaks
.end() || *it
!= posSep
);
663 rBreaks
.insert(it
, posSep
);
673 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager
& rDocumentContentOperations
,
674 SwPaM
& rPam
, SwDeleteFlags
const flags
,
675 bool (::sw::DocumentContentOperationsManager::*pFunc
)(SwPaM
&, SwDeleteFlags
))
677 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
679 sw::CalcBreaks(Breaks
, rPam
);
683 return (rDocumentContentOperations
.*pFunc
)(rPam
, flags
);
686 // Deletion must be split into several parts if the text node
687 // contains a text attribute with end and with dummy character
688 // and the selection does not contain the text attribute completely,
689 // but overlaps its start (left), where the dummy character is.
691 SwPosition
const & rSelectionEnd( *rPam
.End() );
694 // iterate from end to start, to avoid invalidating the offsets!
695 auto iter( Breaks
.rbegin() );
696 SwNodeOffset
nOffset(0);
697 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
698 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
699 SwPosition
& rEnd( *aPam
.End() );
700 SwPosition
& rStart( *aPam
.Start() );
702 while (iter
!= Breaks
.rend())
704 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
705 if (rStart
< rEnd
) // check if part is empty
707 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, flags
);
708 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // deleted fly nodes...
710 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
714 rStart
= *rPam
.Start(); // set to original start
715 if (rStart
< rEnd
) // check if part is empty
717 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, flags
);
723 bool lcl_StrLenOverflow( const SwPaM
& rPam
)
725 // If we try to merge two paragraphs we have to test if afterwards
726 // the string doesn't exceed the allowed string length
727 if( rPam
.GetPoint()->GetNode() != rPam
.GetMark()->GetNode() )
729 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
730 const SwTextNode
* pEndNd
= pEnd
->GetNode().GetTextNode();
731 if( (nullptr != pEndNd
) && pStt
->GetNode().IsTextNode() )
733 const sal_uInt64 nSum
= pStt
->GetContentIndex() +
734 pEndNd
->GetText().getLength() - pEnd
->GetContentIndex();
735 return nSum
> o3tl::make_unsigned(SAL_MAX_INT32
);
743 SwRangeRedline
* pRedl
;
744 SwNodeOffset nStt
, nEnd
;
748 SaveRedline( SwRangeRedline
* pR
, const SwNodeIndex
& rSttIdx
)
753 auto [pStt
, pEnd
] = pR
->StartEnd(); // SwPosition*
754 SwNodeOffset nSttIdx
= rSttIdx
.GetIndex();
755 nStt
= pStt
->GetNodeIndex() - nSttIdx
;
756 nSttCnt
= pStt
->GetContentIndex();
759 nEnd
= pEnd
->GetNodeIndex() - nSttIdx
;
760 nEndCnt
= pEnd
->GetContentIndex();
763 pRedl
->GetPoint()->Assign( SwNodeOffset(0) );
764 pRedl
->GetMark()->Assign( SwNodeOffset(0) );
767 SaveRedline( SwRangeRedline
* pR
, const SwPosition
& rPos
)
772 auto [pStt
, pEnd
] = pR
->StartEnd(); // SwPosition*
773 SwNodeOffset nSttIdx
= rPos
.GetNodeIndex();
774 nStt
= pStt
->GetNodeIndex() - nSttIdx
;
775 nSttCnt
= pStt
->GetContentIndex();
776 if( nStt
== SwNodeOffset(0) )
777 nSttCnt
= nSttCnt
- rPos
.GetContentIndex();
780 nEnd
= pEnd
->GetNodeIndex() - nSttIdx
;
781 nEndCnt
= pEnd
->GetContentIndex();
782 if( nEnd
== SwNodeOffset(0) )
783 nEndCnt
= nEndCnt
- rPos
.GetContentIndex();
786 pRedl
->GetPoint()->Assign( SwNodeOffset(0) );
787 pRedl
->GetMark()->Assign( SwNodeOffset(0) );
790 void SetPos( SwNodeOffset nInsPos
)
792 pRedl
->GetPoint()->Assign( nInsPos
+ nStt
, nSttCnt
);
793 if( pRedl
->HasMark() )
795 pRedl
->GetMark()->Assign( nInsPos
+ nEnd
, nEndCnt
);
799 void SetPos( const SwPosition
& aPos
)
801 pRedl
->GetPoint()->Assign( aPos
.GetNodeIndex() + nStt
,
802 nSttCnt
+ ( nStt
== SwNodeOffset(0) ? aPos
.GetContentIndex() : 0 ) );
803 if( pRedl
->HasMark() )
805 pRedl
->GetMark()->Assign( aPos
.GetNodeIndex() + nEnd
,
806 nEndCnt
+ ( nEnd
== SwNodeOffset(0) ? aPos
.GetContentIndex() : 0 ) );
811 typedef std::vector
< SaveRedline
> SaveRedlines_t
;
813 void lcl_SaveRedlines(const SwPaM
& aPam
, SaveRedlines_t
& rArr
)
815 SwDoc
& rDoc
= aPam
.GetPointNode().GetDoc();
817 auto [pStart
, pEnd
] = aPam
.StartEnd(); // SwPosition*
819 // get first relevant redline
820 SwRedlineTable::size_type nCurrentRedline
;
821 rDoc
.getIDocumentRedlineAccess().GetRedline( *pStart
, &nCurrentRedline
);
822 if( nCurrentRedline
> 0)
825 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
826 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
827 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
829 // iterate over relevant redlines and decide for each whether it should
830 // be saved, or split + saved
831 SwRedlineTable
& rRedlineTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
832 for( ; nCurrentRedline
< rRedlineTable
.size(); nCurrentRedline
++ )
834 SwRangeRedline
* pCurrent
= rRedlineTable
[ nCurrentRedline
];
835 SwComparePosition eCompare
=
836 ComparePosition( *pCurrent
->Start(), *pCurrent
->End(),
839 // we must save this redline if it overlaps aPam
840 // (we may have to split it, too)
841 if( eCompare
== SwComparePosition::OverlapBehind
||
842 eCompare
== SwComparePosition::OverlapBefore
||
843 eCompare
== SwComparePosition::Outside
||
844 eCompare
== SwComparePosition::Inside
||
845 eCompare
== SwComparePosition::Equal
)
847 rRedlineTable
.Remove( nCurrentRedline
-- );
849 // split beginning, if necessary
850 if( eCompare
== SwComparePosition::OverlapBefore
||
851 eCompare
== SwComparePosition::Outside
)
853 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
854 *pNewRedline
->End() = *pStart
;
855 *pCurrent
->Start() = *pStart
;
856 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
859 // split end, if necessary
860 if( eCompare
== SwComparePosition::OverlapBehind
||
861 eCompare
== SwComparePosition::Outside
)
863 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
864 *pNewRedline
->Start() = *pEnd
;
865 *pCurrent
->End() = *pEnd
;
866 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
869 // save the current redline
870 rArr
.emplace_back( pCurrent
, *pStart
);
874 // restore old redline mode
875 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
878 void lcl_RestoreRedlines(SwDoc
& rDoc
, const SwPosition
& rPos
, SaveRedlines_t
& rArr
)
880 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
881 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
883 for(SaveRedline
& rSvRedLine
: rArr
)
885 rSvRedLine
.SetPos( rPos
);
886 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true );
889 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
892 void lcl_SaveRedlines(const SwNodeRange
& rRg
, SaveRedlines_t
& rArr
)
894 SwDoc
& rDoc
= rRg
.aStart
.GetNode().GetDoc();
895 SwRedlineTable::size_type nRedlPos
;
896 SwPosition
aSrchPos( rRg
.aStart
);
897 aSrchPos
.Adjust(SwNodeOffset(-1));
898 if( rDoc
.getIDocumentRedlineAccess().GetRedline( aSrchPos
, &nRedlPos
) && nRedlPos
)
900 else if( nRedlPos
>= rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() )
903 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
904 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
905 SwRedlineTable
& rRedlTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
908 SwRangeRedline
* pTmp
= rRedlTable
[ nRedlPos
];
910 auto [pRStt
, pREnd
] = pTmp
->StartEnd(); // SwPosition*
912 if( pRStt
->GetNode() < rRg
.aStart
.GetNode() )
914 if( pREnd
->GetNode() > rRg
.aStart
.GetNode() && pREnd
->GetNode() < rRg
.aEnd
.GetNode() )
916 // Create a copy and set the end of the original to the end of the MoveArea.
917 // The copy is moved too.
918 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
919 SwPosition
* pTmpPos
= pNewRedl
->Start();
920 pTmpPos
->Assign(rRg
.aStart
);
922 rArr
.emplace_back(pNewRedl
, rRg
.aStart
);
924 pTmpPos
= pTmp
->End();
925 pTmpPos
->Assign(rRg
.aEnd
);
927 else if( pREnd
->GetNode() == rRg
.aStart
.GetNode() )
929 SwPosition
* pTmpPos
= pTmp
->End();
930 pTmpPos
->Assign(rRg
.aEnd
);
933 else if( pRStt
->GetNode() < rRg
.aEnd
.GetNode() )
935 rRedlTable
.Remove( nRedlPos
-- );
936 if( pREnd
->GetNode() < rRg
.aEnd
.GetNode() ||
937 ( pREnd
->GetNode() == rRg
.aEnd
.GetNode() && !pREnd
->GetContentIndex()) )
940 rArr
.emplace_back( pTmp
, rRg
.aStart
);
945 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
946 SwPosition
* pTmpPos
= pNewRedl
->End();
947 pTmpPos
->Assign(rRg
.aEnd
);
949 rArr
.emplace_back( pNewRedl
, rRg
.aStart
);
951 pTmpPos
= pTmp
->Start();
952 pTmpPos
->Assign(rRg
.aEnd
);
953 rDoc
.getIDocumentRedlineAccess().AppendRedline( pTmp
, true );
959 } while( ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() );
960 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
963 void lcl_RestoreRedlines(SwDoc
& rDoc
, SwNodeOffset
const nInsPos
, SaveRedlines_t
& rArr
)
965 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
966 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
968 for(SaveRedline
& rSvRedLine
: rArr
)
970 rSvRedLine
.SetPos( nInsPos
);
971 IDocumentRedlineAccess::AppendResult
const result(
972 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true ));
973 if ( IDocumentRedlineAccess::AppendResult::APPENDED
== result
&&
974 rSvRedLine
.pRedl
->GetType() == RedlineType::Delete
)
976 UpdateFramesForAddDeleteRedline(rDoc
, *rSvRedLine
.pRedl
);
980 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
983 bool lcl_SaveFootnote( const SwNode
& rSttNd
, const SwNode
& rEndNd
,
984 const SwNode
& rInsPos
,
985 SwFootnoteIdxs
& rFootnoteArr
, SwFootnoteIdxs
& rSaveArr
,
986 std::optional
<sal_Int32
> oSttCnt
= std::nullopt
, std::optional
<sal_Int32
> oEndCnt
= std::nullopt
)
988 bool bUpdateFootnote
= false;
989 const SwNodes
& rNds
= rInsPos
.GetNodes();
990 const bool bDelFootnote
= rInsPos
.GetIndex() < rNds
.GetEndOfAutotext().GetIndex() &&
991 rSttNd
.GetIndex() >= rNds
.GetEndOfAutotext().GetIndex();
992 const bool bSaveFootnote
= !bDelFootnote
&&
993 rInsPos
.GetIndex() >= rNds
.GetEndOfExtras().GetIndex();
994 if( !rFootnoteArr
.empty() )
998 rFootnoteArr
.SeekEntry( rSttNd
, &nPos
);
999 SwTextFootnote
* pSrch
;
1000 const SwNode
* pFootnoteNd
;
1002 // Delete/save all that come after it
1003 while( nPos
< rFootnoteArr
.size() && ( pFootnoteNd
=
1004 &( pSrch
= rFootnoteArr
[ nPos
] )->GetTextNode())->GetIndex()
1005 <= rEndNd
.GetIndex() )
1007 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
1008 if( ( oEndCnt
&& oSttCnt
)
1009 ? (( &rSttNd
== pFootnoteNd
&&
1010 *oSttCnt
> nFootnoteSttIdx
) ||
1011 ( &rEndNd
== pFootnoteNd
&&
1012 nFootnoteSttIdx
>= *oEndCnt
))
1013 : ( &rEndNd
== pFootnoteNd
))
1015 ++nPos
; // continue searching
1022 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1023 SwContentIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1024 rTextNd
.EraseText( aIdx
, 1 );
1028 pSrch
->DelFrames(nullptr);
1029 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1031 rSaveArr
.insert( pSrch
);
1033 bUpdateFootnote
= true;
1037 while( nPos
-- && ( pFootnoteNd
= &( pSrch
= rFootnoteArr
[ nPos
] )->
1038 GetTextNode())->GetIndex() >= rSttNd
.GetIndex() )
1040 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
1041 if( !oEndCnt
|| !oSttCnt
||
1042 ! (( &rSttNd
== pFootnoteNd
&&
1043 *oSttCnt
> nFootnoteSttIdx
) ||
1044 ( &rEndNd
== pFootnoteNd
&&
1045 nFootnoteSttIdx
>= *oEndCnt
)) )
1050 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1051 SwContentIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1052 rTextNd
.EraseText( aIdx
, 1 );
1056 pSrch
->DelFrames(nullptr);
1057 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1059 rSaveArr
.insert( pSrch
);
1061 bUpdateFootnote
= true;
1065 // When moving from redline section into document content section, e.g.
1066 // after loading a document with (delete-)redlines, the footnote array
1067 // has to be adjusted... (#i70572)
1070 SwNodeIndex
aIdx( rSttNd
);
1071 while( aIdx
< rEndNd
) // Check the moved section
1073 SwNode
* pNode
= &aIdx
.GetNode();
1074 if( pNode
->IsTextNode() ) // Looking for text nodes...
1076 SwpHints
*pHints
= pNode
->GetTextNode()->GetpSwpHints();
1077 if( pHints
&& pHints
->HasFootnote() ) //...with footnotes
1079 bUpdateFootnote
= true; // Heureka
1080 const size_t nCount
= pHints
->Count();
1081 for( size_t i
= 0; i
< nCount
; ++i
)
1083 SwTextAttr
*pAttr
= pHints
->Get( i
);
1084 if ( pAttr
->Which() == RES_TXTATR_FTN
)
1086 rSaveArr
.insert( static_cast<SwTextFootnote
*>(pAttr
) );
1094 return bUpdateFootnote
;
1097 bool lcl_MayOverwrite( const SwTextNode
*pNode
, const sal_Int32 nPos
)
1099 sal_Unicode
const cChr
= pNode
->GetText()[nPos
];
1102 case CH_TXTATR_BREAKWORD
:
1103 case CH_TXTATR_INWORD
:
1104 return !pNode
->GetTextAttrForCharAt(nPos
);// how could there be none?
1105 case CH_TXT_ATR_INPUTFIELDSTART
:
1106 case CH_TXT_ATR_INPUTFIELDEND
:
1107 case CH_TXT_ATR_FIELDSTART
:
1108 case CH_TXT_ATR_FIELDSEP
:
1109 case CH_TXT_ATR_FIELDEND
:
1110 case CH_TXT_ATR_FORMELEMENT
:
1117 void lcl_SkipAttr( const SwTextNode
*pNode
, SwPosition
&rIdx
, sal_Int32
&rStart
)
1119 if( !lcl_MayOverwrite( pNode
, rStart
) )
1121 // skip all special attributes
1123 rIdx
.AdjustContent(+1);
1124 rStart
= rIdx
.GetContentIndex();
1125 } while (rStart
< pNode
->GetText().getLength()
1126 && !lcl_MayOverwrite(pNode
, rStart
) );
1130 bool lcl_GetTokenToParaBreak( OUString
& rStr
, OUString
& rRet
, bool bRegExpRplc
)
1135 static constexpr OUString
sPara(u
"\\n"_ustr
);
1138 nPos
= rStr
.indexOf( sPara
, nPos
);
1143 // Has this been escaped?
1144 if( nPos
&& '\\' == rStr
[nPos
-1])
1147 if( nPos
>= rStr
.getLength() )
1154 rRet
= rStr
.copy( 0, nPos
);
1155 rStr
= rStr
.copy( nPos
+ sPara
.getLength() );
1166 namespace //local functions originally from docfmt.cxx
1169 bool lcl_ApplyOtherSet(
1170 SwContentNode
& rNode
,
1171 SwHistory
*const pHistory
,
1172 SfxItemSet
const& rOtherSet
,
1173 SfxItemSet
const& rFirstSet
,
1174 SfxItemSet
const& rPropsSet
,
1175 SwRootFrame
const*const pLayout
,
1176 SwNodeIndex
*const o_pIndex
= nullptr)
1178 assert(rOtherSet
.Count());
1181 SwTextNode
*const pTNd
= rNode
.GetTextNode();
1182 sw::MergedPara
const* pMerged(nullptr);
1183 if (pLayout
&& pLayout
->HasMergedParas() && pTNd
)
1185 SwTextFrame
const*const pTextFrame(static_cast<SwTextFrame
const*>(
1186 pTNd
->getLayoutFrame(pLayout
)));
1189 pMerged
= pTextFrame
->GetMergedPara();
1193 if (rFirstSet
.Count())
1197 SwRegHistory
aRegH(pMerged
->pFirstNode
, *pMerged
->pFirstNode
, pHistory
);
1198 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1202 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1205 if (rPropsSet
.Count())
1209 SwRegHistory
aRegH(pMerged
->pParaPropsNode
, *pMerged
->pParaPropsNode
, pHistory
);
1210 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1214 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1219 *o_pIndex
= *pMerged
->pLastNode
; // skip hidden
1224 // input cursor can't be on hidden node, and iteration skips them
1225 assert(!pLayout
|| !pLayout
->HasMergedParas()
1226 || rNode
.GetRedlineMergeFlag() != SwNode::Merge::Hidden
);
1232 SwRegHistory
aRegH(&rNode
, rNode
, pHistory
);
1233 ret
= rNode
.SetAttr( rOtherSet
);
1237 ret
= rNode
.SetAttr( rOtherSet
);
1243 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1245 // set format redline with extra data for lcl_InsAttr()
1246 void lcl_SetRedline(
1250 std::unique_ptr
<SwRedlineExtraData_FormatColl
> xExtra
;
1252 // check existing redline on the same range, and use its extra data, if it exists
1253 SwRedlineTable::size_type nRedlPos
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos(
1254 rRg
.Start()->GetNode(), RedlineType::Format
);
1255 if( SwRedlineTable::npos
!= nRedlPos
)
1257 const SwPosition
*pRStt
, *pREnd
;
1259 SwRangeRedline
* pTmp
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
1260 pRStt
= pTmp
->Start();
1261 pREnd
= pTmp
->End();
1262 SwComparePosition eCompare
= ComparePosition( *rRg
.Start(), *rRg
.End(), *pRStt
, *pREnd
);
1263 if ( eCompare
== SwComparePosition::Inside
|| eCompare
== SwComparePosition::Equal
)
1265 if (pTmp
->GetExtraData())
1267 const SwRedlineExtraData
* pExtraData
= pTmp
->GetExtraData();
1268 const SwRedlineExtraData_FormatColl
* pFormattingChanges
=
1269 dynamic_cast<const SwRedlineExtraData_FormatColl
*>(pExtraData
);
1270 // Check if the extra data is of type 'formatting changes'
1271 if (pFormattingChanges
)
1273 // Get the item set that holds all the changes properties
1274 const SfxItemSet
*pChangesSet
= pFormattingChanges
->GetItemSet();
1275 xExtra
.reset(new SwRedlineExtraData_FormatColl(u
""_ustr
, USHRT_MAX
, pChangesSet
));
1280 } while( pRStt
<= rRg
.Start() && ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
1283 SwRangeRedline
* pRedline
= new SwRangeRedline( RedlineType::Format
, rRg
);
1284 auto const result(rDoc
.getIDocumentRedlineAccess().AppendRedline( pRedline
, true));
1285 // store original text attributes to reject formatting change
1286 if (IDocumentRedlineAccess::AppendResult::IGNORED
== result
)
1289 // no existing format redline in the range
1292 // Apply the first character's attributes to the ReplaceText
1293 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
1294 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( rDoc
.GetAttrPool() );
1295 SwTextNode
* pNode
= rRg
.Start()->GetNode().GetTextNode();
1296 pNode
->GetParaAttr( aSet
, rRg
.Start()->GetContentIndex() + 1, rRg
.End()->GetContentIndex() );
1298 aSet
.ClearItem( RES_TXTATR_REFMARK
);
1299 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
1300 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
1301 aSet
.ClearItem( RES_TXTATR_INETFMT
);
1302 aSet
.ClearItem( RES_TXTATR_META
);
1303 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
1305 // After GetParaAttr aSet can contain invalid/dontcare items (true == IsInvalidItem,
1306 // DONTCARE == SfxItemState), e.g. RES_TXTATR_CHARFMT and (a copy of) this
1307 // SfxItemSet can be passed to MSWordExportBase::OutputItemSet
1308 // which doesn't handle invalid/dontcare items so clear them here
1309 aSet
.ClearInvalidItems();
1311 xExtra
.reset(new SwRedlineExtraData_FormatColl(u
""_ustr
, USHRT_MAX
, &aSet
));
1316 pRedline
->SetExtraData(xExtra
.get() );
1320 // create format redline(s) for the given range:
1321 // to track the original formatting stored in the
1322 // hints, create redlines for all parts of the
1323 // range partitioned by boundaries of the hints.
1324 void lcl_SetRedlines(
1328 SwNodeIndex
aIdx( rRg
.Start()->GetNode() );
1329 const SwNodeIndex
aEndNd( rRg
.End()->GetNode() );
1330 while( aIdx
<= aEndNd
)
1332 SwTextNode
*pNode
= aIdx
.GetNode().GetTextNode();
1335 const sal_Int32 nStart
= aIdx
== rRg
.Start()->GetNode()
1336 ? rRg
.Start()->GetContentIndex()
1338 const sal_Int32 nEnd
= aIdx
< aEndNd
1339 ? pNode
->GetText().getLength()
1340 : rRg
.End()->GetContentIndex();
1342 if( SwpHints
*pHints
= pNode
->GetpSwpHints() )
1344 const size_t nCount
= pHints
->Count();
1345 sal_Int32 nRedEnd
= nStart
;
1346 for( size_t i
= 0; i
< nCount
; ++i
)
1348 SwTextAttr
*pAttr
= pHints
->Get( i
);
1350 if ( pAttr
->GetStart() > nEnd
)
1352 break; // after the range
1355 if ( !pAttr
->GetEnd() || *pAttr
->GetEnd() < nStart
)
1357 continue; // before the range
1360 // range part before the hint
1361 if ( nRedEnd
< pAttr
->GetStart() )
1363 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, pAttr
->GetStart() );
1364 lcl_SetRedline(rDoc
, aPam
);
1367 // range part at the hint
1368 sal_Int32 nRedStart
= std::max(pAttr
->GetStart(), nStart
);
1369 nRedEnd
= std::min(*pAttr
->GetEnd(), nEnd
);
1370 SwPaM
aPam2( *pNode
, nRedStart
, *pNode
, nRedEnd
);
1371 lcl_SetRedline(rDoc
, aPam2
);
1374 // range part after the last hint
1375 if ( nRedEnd
< nEnd
)
1377 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, nEnd
);
1378 lcl_SetRedline(rDoc
, aPam
);
1383 SwPaM
aPam( *pNode
, nStart
, *pNode
, nEnd
);
1384 lcl_SetRedline(rDoc
, aPam
);
1391 /// Insert Hints according to content types;
1392 // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1397 const SfxItemSet
& rChgSet
,
1398 const SetAttrMode nFlags
,
1399 SwUndoAttr
*const pUndo
,
1400 SwRootFrame
const*const pLayout
,
1401 SwTextAttr
**ppNewTextAttr
)
1403 // Divide the Sets (for selections in Nodes)
1404 const SfxItemSet
* pCharSet
= nullptr;
1405 const SfxItemSet
* pOtherSet
= nullptr;
1406 bool bDelete
= false;
1407 bool bCharAttr
= false;
1408 bool bOtherAttr
= false;
1410 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1411 if ( 1 == rChgSet
.Count() )
1413 SfxItemIter
aIter( rChgSet
);
1414 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
1415 if (pItem
&& !IsInvalidItem(pItem
))
1417 const sal_uInt16 nWhich
= pItem
->Which();
1419 if ( isCHRATR(nWhich
) ||
1420 (RES_TXTATR_CHARFMT
== nWhich
) ||
1421 (RES_TXTATR_INETFMT
== nWhich
) ||
1422 (RES_TXTATR_AUTOFMT
== nWhich
) ||
1423 (RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
) )
1425 pCharSet
= &rChgSet
;
1429 if ( isPARATR(nWhich
)
1430 || isPARATR_LIST(nWhich
)
1433 || isUNKNOWNATR(nWhich
)
1434 || isDrawingLayerAttribute(nWhich
) )
1436 pOtherSet
= &rChgSet
;
1442 // Build new itemset if either
1443 // - rChgSet.Count() > 1 or
1444 // - The attribute in rChgSet does not belong to one of the above categories
1445 if ( !bCharAttr
&& !bOtherAttr
)
1447 SfxItemSet
* pTmpCharItemSet
= new SfxItemSetFixed
<
1448 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
1449 RES_TXTATR_AUTOFMT
, RES_TXTATR_CHARFMT
,
1450 RES_TXTATR_UNKNOWN_CONTAINER
,
1451 RES_TXTATR_UNKNOWN_CONTAINER
>( rDoc
.GetAttrPool() );
1453 SfxItemSet
* pTmpOtherItemSet
= new SfxItemSetFixed
<
1454 RES_PARATR_BEGIN
, RES_GRFATR_END
- 1,
1455 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1,
1456 // FillAttribute support:
1457 XATTR_FILL_FIRST
, XATTR_FILL_LAST
>( rDoc
.GetAttrPool() );
1459 pTmpCharItemSet
->Put( rChgSet
);
1460 pTmpOtherItemSet
->Put( rChgSet
);
1462 pCharSet
= pTmpCharItemSet
;
1463 pOtherSet
= pTmpOtherItemSet
;
1468 SwHistory
* pHistory
= pUndo
? &pUndo
->GetHistory() : nullptr;
1470 const SwPosition
*pStt
= rRg
.Start(), *pEnd
= rRg
.End();
1471 SwContentNode
* pNode
= pStt
->GetNode().GetContentNode();
1473 if( pNode
&& pNode
->IsTextNode() )
1475 // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
1476 if (pLayout
&& pNode
->GetTextNode()->getIDocumentSettingAccess()->
1477 get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING
))
1479 SwContentNode
* pEndNode
= pEnd
->GetNode().GetContentNode();
1480 SwContentNode
* pCurrentNode
= pEndNode
;
1481 auto nStartIndex
= pNode
->GetIndex();
1482 auto nEndIndex
= pEndNode
->GetIndex();
1483 SwNodeIndex
aIdx( pEnd
->GetNode() );
1484 while ( pCurrentNode
!= nullptr && nStartIndex
<= pCurrentNode
->GetIndex() )
1486 if (pCurrentNode
->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT
) &&
1487 // remove character formatting only on wholly selected paragraphs
1488 (nStartIndex
< pCurrentNode
->GetIndex() || pStt
->GetContentIndex() == 0) &&
1489 (pCurrentNode
->GetIndex() < nEndIndex
|| pEnd
->GetContentIndex() == pEndNode
->Len()))
1491 pCurrentNode
->ResetAttr(RES_PARATR_LIST_AUTOFMT
);
1492 // reset also paragraph marker
1493 pCurrentNode
->GetTextNode()->RstTextAttr(pCurrentNode
->Len(), 1);
1495 pCurrentNode
= SwNodes::GoPrevious( &aIdx
);
1499 if (rRg
.IsInFrontOfLabel())
1501 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1504 pTextNd
= sw::GetParaPropsNode(*pLayout
, *pTextNd
);
1506 SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
1510 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1515 int nLevel
= pTextNd
->GetActualListLevel();
1520 if (nLevel
>= MAXLEVEL
)
1521 nLevel
= MAXLEVEL
- 1;
1523 SwNumFormat aNumFormat
= pNumRule
->Get(o3tl::narrowing
<sal_uInt16
>(nLevel
));
1524 SwCharFormat
* pCharFormat
=
1525 rDoc
.FindCharFormatByName(aNumFormat
.GetCharFormatName());
1530 pHistory
->AddCharFormat(pCharFormat
->GetAttrSet(), *pCharFormat
);
1533 pCharFormat
->SetFormatAttr(*pCharSet
);
1540 // Attributes without an end do not have a range
1541 if ( !bCharAttr
&& !bOtherAttr
)
1543 SfxItemSetFixed
<RES_TXTATR_NOEND_BEGIN
, RES_TXTATR_NOEND_END
-1>
1544 aTextSet( rDoc
.GetAttrPool() );
1545 aTextSet
.Put( rChgSet
);
1546 if( aTextSet
.Count() )
1548 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1549 bRet
= history
.InsertItems(
1550 aTextSet
, pStt
->GetContentIndex(), pStt
->GetContentIndex(), nFlags
, /*ppNewTextAttr*/nullptr ) || bRet
;
1552 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1553 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1555 SwPaM
aPam( pStt
->GetNode(), pStt
->GetContentIndex()-1,
1556 pStt
->GetNode(), pStt
->GetContentIndex() );
1559 pUndo
->SaveRedlineData( aPam
, true );
1561 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1562 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
1564 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1569 // TextAttributes with an end never expand their range
1570 if ( !bCharAttr
&& !bOtherAttr
)
1572 // CharFormat and URL attributes are treated separately!
1573 // TEST_TEMP ToDo: AutoFormat!
1575 RES_TXTATR_REFMARK
, RES_TXTATR_METAFIELD
,
1576 RES_TXTATR_CJK_RUBY
, RES_TXTATR_CJK_RUBY
,
1577 RES_TXTATR_INPUTFIELD
, RES_TXTATR_CONTENTCONTROL
>
1578 aTextSet(rDoc
.GetAttrPool());
1580 aTextSet
.Put( rChgSet
);
1581 if( aTextSet
.Count() )
1583 const sal_Int32 nInsCnt
= pStt
->GetContentIndex();
1584 const sal_Int32 nEnd
= pStt
->GetNode() == pEnd
->GetNode()
1585 ? pEnd
->GetContentIndex()
1587 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1588 bRet
= history
.InsertItems( aTextSet
, nInsCnt
, nEnd
, nFlags
, ppNewTextAttr
)
1591 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1592 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1594 // Was text content inserted? (RefMark/TOXMarks without an end)
1595 bool bTextIns
= nInsCnt
!= pStt
->GetContentIndex();
1596 // Was content inserted or set over the selection?
1597 SwPaM
aPam( pStt
->GetNode(), bTextIns
? nInsCnt
+ 1 : nEnd
,
1598 pStt
->GetNode(), nInsCnt
);
1600 pUndo
->SaveRedlineData( aPam
, bTextIns
);
1602 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1603 rDoc
.getIDocumentRedlineAccess().AppendRedline(
1605 bTextIns
? RedlineType::Insert
: RedlineType::Format
, aPam
),
1608 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1614 // We always have to set the auto flag for PageDescs that are set at the Node!
1615 if( pOtherSet
&& pOtherSet
->Count() )
1617 SwTableNode
* pTableNd
;
1618 const SwFormatPageDesc
* pDesc
= pOtherSet
->GetItemIfSet( RES_PAGEDESC
, false );
1623 // Set auto flag. Only in the template it's without auto!
1624 SwFormatPageDesc
aNew( *pDesc
);
1626 // Tables now also know line breaks
1627 if( !(nFlags
& SetAttrMode::APICALL
) &&
1628 nullptr != ( pTableNd
= pNode
->FindTableNode() ) )
1630 SwTableNode
* pCurTableNd
= pTableNd
;
1631 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1632 pTableNd
= pCurTableNd
;
1634 // set the table format
1635 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1636 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1637 pFormat
->SetFormatAttr( aNew
);
1642 SwContentNode
* pFirstNode(pNode
);
1643 if (pLayout
&& pLayout
->HasMergedParas())
1645 pFirstNode
= sw::GetFirstAndLastNode(*pLayout
, pStt
->GetNode()).first
;
1647 SwRegHistory
aRegH( pFirstNode
, *pFirstNode
, pHistory
);
1648 bRet
= pFirstNode
->SetAttr( aNew
) || bRet
;
1652 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1653 // we know, that there is only one attribute in pOtherSet. We cannot
1654 // perform the following operations, instead we return:
1658 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_PAGEDESC
);
1659 if( !pOtherSet
->Count() )
1666 // Tables now also know line breaks
1667 const SvxFormatBreakItem
* pBreak
;
1668 if( pNode
&& !(nFlags
& SetAttrMode::APICALL
) &&
1669 nullptr != (pTableNd
= pNode
->FindTableNode() ) &&
1670 (pBreak
= pOtherSet
->GetItemIfSet( RES_BREAK
, false )) )
1672 SwTableNode
* pCurTableNd
= pTableNd
;
1673 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1674 pTableNd
= pCurTableNd
;
1676 // set the table format
1677 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1678 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1679 pFormat
->SetFormatAttr( *pBreak
);
1682 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1683 // we know, that there is only one attribute in pOtherSet. We cannot
1684 // perform the following operations, instead we return:
1688 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_BREAK
);
1689 if( !pOtherSet
->Count() )
1697 // If we have a PoolNumRule, create it if needed
1698 sal_uInt16 nPoolId
=0;
1699 const SwNumRuleItem
* pRule
= pOtherSet
->GetItemIfSet( RES_PARATR_NUMRULE
, false );
1701 !rDoc
.FindNumRulePtr( pRule
->GetValue() ) &&
1702 USHRT_MAX
!= (nPoolId
= SwStyleNameMapper::GetPoolIdFromUIName ( pRule
->GetValue(),
1703 SwGetPoolIdFromName::NumRule
)) )
1704 rDoc
.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId
);
1708 SfxItemSetFixed
<RES_PAGEDESC
, RES_BREAK
> firstSet(rDoc
.GetAttrPool());
1709 if (pOtherSet
&& pOtherSet
->Count())
1710 { // actually only RES_BREAK is possible here...
1711 firstSet
.Put(*pOtherSet
);
1714 <RES_PARATR_BEGIN
, RES_PAGEDESC
,
1715 RES_BREAK
+1, RES_FRMATR_END
,
1716 XATTR_FILL_FIRST
, XATTR_FILL_LAST
+1> propsSet(rDoc
.GetAttrPool());
1717 if (pOtherSet
&& pOtherSet
->Count())
1719 propsSet
.Put(*pOtherSet
);
1722 if( !rRg
.HasMark() ) // no range
1730 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1732 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1733 sal_Int32 nMkPos
, nPtPos
= pStt
->GetContentIndex();
1734 const OUString
& rStr
= pTextNd
->GetText();
1736 // Special case: if the Cursor is located within a URL attribute, we take over it's area
1737 SwTextAttr
const*const pURLAttr(
1738 pTextNd
->GetTextAttrAt(pStt
->GetContentIndex(), RES_TXTATR_INETFMT
));
1739 if (pURLAttr
&& !pURLAttr
->GetINetFormat().GetValue().isEmpty())
1741 nMkPos
= pURLAttr
->GetStart();
1742 nPtPos
= *pURLAttr
->End();
1746 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
1747 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1748 pTextNd
->GetText(), nPtPos
,
1749 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1750 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
1753 if( aBndry
.startPos
< nPtPos
&& nPtPos
< aBndry
.endPos
)
1755 nMkPos
= aBndry
.startPos
;
1756 nPtPos
= aBndry
.endPos
;
1759 nPtPos
= nMkPos
= pStt
->GetContentIndex();
1762 // Remove the overriding attributes from the SwpHintsArray,
1763 // if the selection spans across the whole paragraph.
1764 // These attributes are inserted as FormatAttributes and
1765 // never override the TextAttributes!
1766 if( !(nFlags
& SetAttrMode::DONTREPLACE
) &&
1767 pTextNd
->HasHints() && !nMkPos
&& nPtPos
== rStr
.getLength())
1771 // Save all attributes for the Undo.
1772 SwRegHistory
aRHst( *pTextNd
, pHistory
);
1773 pTextNd
->GetpSwpHints()->Register( &aRHst
);
1774 pTextNd
->RstTextAttr( 0, nPtPos
, 0, pCharSet
);
1775 if( pTextNd
->GetpSwpHints() )
1776 pTextNd
->GetpSwpHints()->DeRegister();
1779 pTextNd
->RstTextAttr( 0, nPtPos
, 0, pCharSet
);
1782 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1784 SwPaM
aPam( *pNode
, nMkPos
, *pNode
, nPtPos
);
1787 pUndo
->SaveRedlineData( aPam
, false );
1789 lcl_SetRedlines(rDoc
, aPam
);
1792 // the SwRegHistory inserts the attribute into the TextNode!
1793 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1795 bRet
= history
.InsertItems( *pCharSet
, nMkPos
, nPtPos
, nFlags
, /*ppNewTextAttr*/nullptr )
1799 if( pOtherSet
&& pOtherSet
->Count() )
1801 // Need to check for unique item for DrawingLayer items of type NameOrIndex
1802 // and evtl. correct that item to ensure unique names for that type. This call may
1803 // modify/correct entries inside of the given SfxItemSet
1804 SfxItemSet
aTempLocalCopy(*pOtherSet
);
1806 rDoc
.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy
);
1807 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, aTempLocalCopy
, firstSet
, propsSet
, pLayout
) || bRet
;
1814 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet
&& pCharSet
->Count() )
1817 pUndo
->SaveRedlineData( rRg
, false );
1819 lcl_SetRedlines(rDoc
, rRg
);
1823 sal_uLong nNodes
= 0;
1825 SwNodeIndex
aSt( rDoc
.GetNodes() );
1826 SwNodeIndex
aEnd( rDoc
.GetNodes() );
1827 SwContentIndex
aCntEnd( pEnd
->GetContentNode(), pEnd
->GetContentIndex() );
1831 const sal_Int32 nLen
= pNode
->Len();
1832 if( pStt
->GetNode() != pEnd
->GetNode() )
1833 aCntEnd
.Assign( pNode
, nLen
);
1835 if( pStt
->GetContentIndex() != 0 || aCntEnd
.GetIndex() != nLen
)
1837 // the SwRegHistory inserts the attribute into the TextNode!
1838 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1840 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1841 bRet
= history
.InsertItems(*pCharSet
,
1842 pStt
->GetContentIndex(), aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr)
1846 if( pOtherSet
&& pOtherSet
->Count() )
1848 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
) || bRet
;
1851 // Only selection in a Node.
1852 if( pStt
->GetNode() == pEnd
->GetNode() )
1858 aSt
.Assign( pStt
->GetNode(), +1 );
1861 aSt
= pStt
->GetNode();
1862 aCntEnd
.Assign(pEnd
->GetContentNode(), pEnd
->GetContentIndex()); // aEnd was changed!
1865 aSt
.Assign( pStt
->GetNode(), +1 );
1867 // aSt points to the first full Node now
1870 * The selection spans more than one Node.
1872 if( pStt
->GetNode() < pEnd
->GetNode() )
1874 pNode
= pEnd
->GetNode().GetContentNode();
1877 if( aCntEnd
.GetIndex() != pNode
->Len() )
1879 // the SwRegHistory inserts the attribute into the TextNode!
1880 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1882 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1883 (void)history
.InsertItems(*pCharSet
,
1884 0, aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr);
1887 if( pOtherSet
&& pOtherSet
->Count() )
1889 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
);
1893 aEnd
= pEnd
->GetNode();
1896 aEnd
.Assign( pEnd
->GetNode(), +1 );
1899 aEnd
= pEnd
->GetNode();
1902 aEnd
.Assign( pEnd
->GetNode(), +1 );
1904 // aEnd points BEHIND the last full node now
1906 /* Edit the fully selected Nodes. */
1907 // Reset all attributes from the set!
1908 if( pCharSet
&& pCharSet
->Count() && !( SetAttrMode::DONTREPLACE
& nFlags
) )
1910 ::sw::DocumentContentOperationsManager::ParaRstFormat
aPara(
1911 pStt
, pEnd
, pHistory
, pCharSet
, pLayout
);
1912 rDoc
.GetNodes().ForEach( aSt
, aEnd
, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr
, &aPara
);
1915 bool bCreateSwpHints
= pCharSet
&& (
1916 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_CHARFMT
, false ) ||
1917 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_INETFMT
, false ) );
1919 for (SwNodeIndex current
= aSt
; current
< aEnd
; ++current
)
1921 SwTextNode
*const pTNd
= current
.GetNode().GetTextNode();
1925 if (pLayout
&& pLayout
->HasMergedParas()
1926 && pTNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
1927 { // not really sure what to do here, but applying to hidden
1928 continue; // nodes doesn't make sense...
1933 SwRegHistory
aRegH( pTNd
, *pTNd
, pHistory
);
1935 if (pCharSet
&& pCharSet
->Count())
1937 if (SwpHints
*pSwpHints
= bCreateSwpHints
? &pTNd
->GetOrCreateSwpHints()
1938 : pTNd
->GetpSwpHints())
1940 pSwpHints
->Register( &aRegH
);
1943 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1945 // re-fetch as it may be deleted by SetAttr
1946 if (SwpHints
*pSwpHints
= pTNd
->GetpSwpHints())
1947 pSwpHints
->DeRegister();
1952 if (pCharSet
&& pCharSet
->Count())
1953 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1958 if (pOtherSet
&& pOtherSet
->Count())
1960 for (; aSt
< aEnd
; ++aSt
)
1962 pNode
= aSt
.GetNode().GetContentNode();
1966 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
, &aSt
);
1972 return (nNodes
!= 0) || bRet
;
1981 bool IsFieldmarkOverlap(SwPaM
const& rPaM
)
1983 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
1984 sw::CalcBreaks(Breaks
, rPaM
);
1985 return !Breaks
.empty();
1989 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc
& i_rSwdoc
) : m_rDoc( i_rSwdoc
)
1994 * Checks if rStart..rEnd mark a range that makes sense to copy.
1996 * IsMoveToFly means the copy is a move to create a fly
1997 * and so existing flys at the edge must not be copied.
1999 static bool IsEmptyRange(const SwPosition
& rStart
, const SwPosition
& rEnd
,
2000 SwCopyFlags
const flags
)
2003 { // check if a fly anchored there would be copied - then copy...
2004 return !IsDestroyFrameAnchoredAtChar(rStart
, rStart
, rEnd
,
2005 (flags
& SwCopyFlags::IsMoveToFly
)
2006 ? DelContentType::WriterfilterHack
|DelContentType::AllMask
2007 : DelContentType::AllMask
);
2011 return rEnd
< rStart
;
2015 // Copy an area into this document or into another document
2016 bool DocumentContentOperationsManager::CopyRange(SwPaM
& rPam
, SwPosition
& rPos
,
2017 SwCopyFlags
const flags
,
2018 sal_uInt32 nMovedID
) const
2020 const SwPosition
*pStt
= rPam
.Start(), *pEnd
= rPam
.End();
2022 SwDoc
& rDoc
= rPos
.GetNode().GetDoc();
2023 bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
2025 // Catch if there's no copy to do
2026 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
))
2029 // Prevent copying into Flys that are anchored in the source range
2030 if (&rDoc
== &m_rDoc
&& (flags
& SwCopyFlags::CheckPosInFly
))
2032 // Correct the Start-/EndNode
2033 SwNodeOffset nStt
= pStt
->GetNodeIndex(),
2034 nEnd
= pEnd
->GetNodeIndex(),
2035 nDiff
= nEnd
- nStt
+1;
2036 SwNode
* pNd
= m_rDoc
.GetNodes()[ nStt
];
2037 if( pNd
->IsContentNode() && pStt
->GetContentIndex() )
2042 if( (pNd
= m_rDoc
.GetNodes()[ nEnd
])->IsContentNode() &&
2043 static_cast<SwContentNode
*>(pNd
)->Len() != pEnd
->GetContentIndex() )
2049 lcl_ChkFlyFly( rDoc
, nStt
, nEnd
, rPos
.GetNodeIndex() ) )
2055 std::optional
<SwPaM
> pRedlineRange
;
2056 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2057 (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
2058 pRedlineRange
.emplace( rPos
);
2060 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2064 if( &rDoc
!= &m_rDoc
)
2066 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
? &*pRedlineRange
: nullptr);
2068 else if( ! ( *pStt
<= rPos
&& rPos
< *pEnd
&&
2069 ( pStt
->GetNode() != pEnd
->GetNode() ||
2070 !pStt
->GetNode().IsTextNode() )) )
2072 // Copy to a position outside of the area, or copy a single TextNode
2073 // Do an ordinary copy
2074 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
? &*pRedlineRange
: nullptr);
2078 // Copy the range in itself
2079 assert(!"mst: this is assumed to be dead code");
2082 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
2085 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2086 rDoc
.getIDocumentRedlineAccess().AppendRedline(
2087 new SwRangeRedline(RedlineType::Insert
, *pRedlineRange
, nMovedID
), true);
2089 rDoc
.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange
);
2090 pRedlineRange
.reset();
2096 static auto GetCorrPosition(SwPaM
const& rPam
) -> SwPosition
2098 // tdf#152710 target position must be on node that survives deletion
2099 // so that PaMCorrAbs can invalidate SwUnoCursors properly
2100 return rPam
.GetPoint()->GetNode().IsContentNode()
2102 : rPam
.GetMark()->GetNode().IsContentNode()
2104 // this would be the result in SwNodes::RemoveNode()
2105 : SwPosition(rPam
.End()->GetNode(), SwNodeOffset(+1));
2108 /// Delete a full Section of the NodeArray.
2109 /// The passed Node is located somewhere in the designated Section.
2110 void DocumentContentOperationsManager::DeleteSection( SwNode
*pNode
)
2112 assert(pNode
&& "Didn't pass a Node.");
2114 SwStartNode
* pSttNd
= pNode
->IsStartNode() ? static_cast<SwStartNode
*>(pNode
)
2115 : pNode
->StartOfSectionNode();
2116 SwNodeIndex
aSttIdx( *pSttNd
), aEndIdx( *pNode
->EndOfSectionNode() );
2118 // delete all Flys, Bookmarks, ...
2119 DelFlyInRange( aSttIdx
.GetNode(), aEndIdx
.GetNode() );
2120 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( *pSttNd
, true, RedlineType::Any
);
2121 DelBookmarks(aSttIdx
.GetNode(), aEndIdx
.GetNode());
2124 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
2125 SwPaM
const range(aSttIdx
, aEndIdx
);
2126 SwPosition
const pos(GetCorrPosition(range
));
2127 ::PaMCorrAbs(range
, pos
);
2130 m_rDoc
.GetNodes().DelNodes( aSttIdx
, aEndIdx
.GetIndex() - aSttIdx
.GetIndex() + 1 );
2133 void DocumentContentOperationsManager::DeleteDummyChar(
2134 SwPosition
const& rPos
, sal_Unicode
const cDummy
)
2136 SwPaM
aPam(rPos
, rPos
);
2137 aPam
.GetPoint()->AdjustContent(+1);
2138 assert(aPam
.GetText().getLength() == 1 && aPam
.GetText()[0] == cDummy
);
2141 DeleteRangeImpl(aPam
, SwDeleteFlags::Default
);
2143 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2144 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2146 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2150 void DocumentContentOperationsManager::DeleteRange( SwPaM
& rPam
)
2152 // Seek all redlines that are in that PaM to be deleted..
2153 SwRedlineTable::size_type nRedlStart
= m_rDoc
.getIDocumentRedlineAccess().GetRedlinePos(
2154 rPam
.Start()->GetNode(), RedlineType::Any
);
2155 SwRedlineTable::size_type nRedlEnd
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineEndPos(
2156 nRedlStart
, rPam
.End()->GetNode(), RedlineType::Any
);
2158 lcl_DoWithBreaks(*this, rPam
, SwDeleteFlags::Default
, &DocumentContentOperationsManager::DeleteRangeImpl
);
2160 // update all redlines was in the Pam that is
2161 m_rDoc
.getIDocumentRedlineAccess().UpdateRedlineContentNode(nRedlStart
, nRedlEnd
);
2163 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2164 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2166 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2170 bool DocumentContentOperationsManager::DelFullPara( SwPaM
& rPam
)
2172 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
2173 const SwNode
* pNd
= &rStt
.GetNode();
2174 SwNodeOffset nSectDiff
= pNd
->StartOfSectionNode()->EndOfSectionIndex() -
2175 pNd
->StartOfSectionIndex();
2176 SwNodeOffset nNodeDiff
= rEnd
.GetNodeIndex() - rStt
.GetNodeIndex();
2178 if ( nSectDiff
-SwNodeOffset(2) <= nNodeDiff
|| m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2179 /* #i9185# Prevent getting the node after the end node (see below) */
2180 rEnd
.GetNodeIndex() + 1 == m_rDoc
.GetNodes().Count() )
2186 SwPaM
temp(rPam
, nullptr);
2187 if (!temp
.HasMark())
2191 if (SwTextNode
*const pNode
= temp
.Start()->GetNode().GetTextNode())
2192 { // rPam may not have nContent set but IsFieldmarkOverlap requires it
2193 temp
.Start()->AssignStartIndex(*pNode
);
2195 if (SwTextNode
*const pNode
= temp
.End()->GetNode().GetTextNode())
2197 temp
.End()->AssignEndIndex(*pNode
);
2199 if (sw::mark::IsFieldmarkOverlap(temp
))
2200 { // a bit of a problem: we want to completely remove the nodes
2201 // but then how can the CH_TXT_ATR survive?
2206 // Move hard page breaks to the following Node.
2207 bool bSavePageBreak
= false, bSavePageDesc
= false;
2209 /* #i9185# This would lead to a segmentation fault if not caught above. */
2210 SwNodeOffset nNextNd
= rEnd
.GetNodeIndex() + 1;
2211 SwTableNode
*const pTableNd
= m_rDoc
.GetNodes()[ nNextNd
]->GetTableNode();
2213 if( pTableNd
&& pNd
->IsContentNode() )
2215 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
2218 const SfxPoolItem
*pItem
;
2219 const SfxItemSet
* pSet
= static_cast<const SwContentNode
*>(pNd
)->GetpSwAttrSet();
2220 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_PAGEDESC
,
2223 pTableFormat
->SetFormatAttr( *pItem
);
2224 bSavePageDesc
= true;
2227 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_BREAK
,
2230 pTableFormat
->SetFormatAttr( *pItem
);
2231 bSavePageBreak
= true;
2236 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2239 if( !rPam
.HasMark() )
2241 else if( rPam
.GetPoint() == &rStt
)
2243 rPam
.GetPoint()->Adjust(SwNodeOffset(1));
2245 SwContentNode
*pTmpNode
= rPam
.GetPoint()->GetNode().GetContentNode();
2246 bool bGoNext
= (nullptr == pTmpNode
);
2248 if (rPam
.GetMark()->GetContentNode())
2249 rPam
.GetMark()->SetContent( 0 );
2251 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2253 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
2255 SwPosition
aTmpPos( *aDelPam
.GetPoint() );
2258 SwNodes::GoNext(&aTmpPos
);
2260 ::PaMCorrAbs( aDelPam
, aTmpPos
);
2263 std::unique_ptr
<SwUndoDelete
> pUndo(new SwUndoDelete(aDelPam
, SwDeleteFlags::Default
, true));
2265 *rPam
.GetPoint() = *aDelPam
.GetPoint();
2266 pUndo
->SetPgBrkFlags( bSavePageBreak
, bSavePageDesc
);
2267 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2272 SwNodeRange
aRg( rStt
.GetNode(), rEnd
.GetNode() );
2273 rPam
.Normalize(false);
2275 // Try to move past the End
2276 if( !rPam
.Move( fnMoveForward
, GoInNode
) )
2278 // Fair enough, at the Beginning then
2280 if( !rPam
.Move( fnMoveBackward
, GoInNode
))
2282 SAL_WARN("sw.core", "DelFullPara: no more Nodes");
2287 // must delete all fieldmarks before CorrAbs(), or they'll remain
2288 // moved to wrong node without their CH_TXT_ATR_FIELD*
2289 // (note: deleteMarks() doesn't help here, in case of partially
2290 // selected fieldmarks; let's delete these as re-inserting their chars
2291 // elsewhere looks difficult)
2292 ::std::set
<::sw::mark::IFieldmark
*> fieldmarks
;
2293 for (SwNodeIndex i
= aRg
.aStart
; i
<= aRg
.aEnd
; ++i
)
2295 if (SwTextNode
*const pTextNode
= i
.GetNode().GetTextNode())
2297 for (sal_Int32 j
= 0; j
< pTextNode
->GetText().getLength(); ++j
)
2299 switch (pTextNode
->GetText()[j
])
2301 case CH_TXT_ATR_FIELDSTART
:
2302 case CH_TXT_ATR_FIELDEND
:
2303 fieldmarks
.insert(m_rDoc
.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode
, j
)));
2305 case CH_TXT_ATR_FIELDSEP
:
2306 fieldmarks
.insert(m_rDoc
.getIDocumentMarkAccess()->getInnerFieldmarkFor(SwPosition(*pTextNode
, j
)));
2312 for (auto const pFieldMark
: fieldmarks
)
2314 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pFieldMark
);
2317 // move bookmarks, redlines etc.
2318 if (aRg
.aStart
== aRg
.aEnd
) // only first CorrAbs variant handles this
2320 m_rDoc
.CorrAbs( aRg
.aStart
.GetNode(), *rPam
.GetPoint(), 0, true );
2324 SwDoc::CorrAbs( aRg
.aStart
, aRg
.aEnd
, *rPam
.GetPoint(), true );
2327 // What's with Flys?
2329 // If there are FlyFrames left, delete these too
2330 for( size_t n
= 0; n
< m_rDoc
.GetSpzFrameFormats()->size(); ++n
)
2332 sw::SpzFrameFormat
* pFly
= (*m_rDoc
.GetSpzFrameFormats())[n
];
2333 const SwFormatAnchor
* pAnchor
= &pFly
->GetAnchor();
2334 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
2336 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
2337 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
2338 // note: here use <= not < like in
2339 // IsDestroyFrameAnchoredAtChar() because of the increment
2340 // of rPam in the bDoesUndo path above!
2341 aRg
.aStart
<= *pAnchorNode
&& *pAnchorNode
<= aRg
.aEnd
.GetNode() )
2343 m_rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFly
);
2350 m_rDoc
.GetNodes().Delete( aRg
.aStart
, nNodeDiff
+1 );
2353 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2354 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2356 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2359 m_rDoc
.getIDocumentState().SetModified();
2364 bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM
& rPam
, SwDeleteFlags
const flags
)
2366 if ( lcl_StrLenOverflow( rPam
) )
2369 bool const ret
= lcl_DoWithBreaks( *this, rPam
, flags
, (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
2370 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
2371 : &DocumentContentOperationsManager::DeleteAndJoinImpl
);
2376 // It seems that this is mostly used by SwDoc internals; the only
2377 // way to call this from the outside seems to be the special case in
2378 // SwDoc::CopyRange (but I have not managed to actually hit that case).
2379 bool DocumentContentOperationsManager::MoveRange( SwPaM
& rPaM
, SwPosition
& rPos
, SwMoveFlags eMvFlags
)
2381 // nothing moved: return
2382 const SwPosition
*pStt
= rPaM
.Start(), *pEnd
= rPaM
.End();
2383 if( !rPaM
.HasMark() || *pStt
>= *pEnd
|| (*pStt
<= rPos
&& rPos
< *pEnd
))
2386 assert(!sw::mark::IsFieldmarkOverlap(rPaM
)); // probably an invalid redline was created?
2388 // Save the paragraph anchored Flys, so that they can be moved.
2389 SaveFlyArr aSaveFlyArr
;
2390 SaveFlyInRange( rPaM
, rPos
, aSaveFlyArr
, bool( SwMoveFlags::ALLFLYS
& eMvFlags
) );
2392 // save redlines (if DOC_MOVEREDLINES is used)
2393 SaveRedlines_t aSaveRedl
;
2394 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2396 lcl_SaveRedlines( rPaM
, aSaveRedl
);
2398 // #i17764# unfortunately, code below relies on undos being
2399 // in a particular order, and presence of bookmarks
2400 // will change this order. Hence, we delete bookmarks
2401 // here without undo.
2402 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
2407 pStt
->GetContentIndex(),
2408 pEnd
->GetContentIndex());
2411 bool bUpdateFootnote
= false;
2412 SwFootnoteIdxs aTmpFntIdx
;
2414 std::unique_ptr
<SwUndoMove
> pUndoMove
;
2415 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2417 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2418 pUndoMove
.reset(new SwUndoMove( rPaM
, rPos
));
2419 pUndoMove
->SetMoveRedlines( eMvFlags
== SwMoveFlags::REDLINES
);
2423 bUpdateFootnote
= lcl_SaveFootnote( pStt
->GetNode(), pEnd
->GetNode(), rPos
.GetNode(),
2424 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
,
2425 pStt
->GetContentIndex(), pEnd
->GetContentIndex() );
2428 bool bSplit
= false;
2429 SwPaM
aSavePam( rPos
, rPos
);
2431 // Move the SPoint to the beginning of the range
2432 if( rPaM
.GetPoint() == pEnd
)
2435 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
2436 SwTextNode
* pSrcNd
= rPaM
.GetPoint()->GetNode().GetTextNode();
2437 bool bCorrSavePam
= pSrcNd
&& pStt
->GetNode() != pEnd
->GetNode();
2439 // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
2440 // However, this does not update the cursor. So we create a TextNode to keep
2441 // updating the indices. After the Move the Node is optionally deleted.
2442 SwTextNode
* pTNd
= rPos
.GetNode().GetTextNode();
2443 if( pTNd
&& rPaM
.GetPoint()->GetNode() != rPaM
.GetMark()->GetNode() &&
2444 ( rPos
.GetContentIndex() || ( pTNd
->Len() && bCorrSavePam
)) )
2447 const sal_Int32 nMkContent
= rPaM
.GetMark()->GetContentIndex();
2449 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
2450 pContentStore
->Save( m_rDoc
, rPos
.GetNodeIndex(), rPos
.GetContentIndex(), true );
2452 SwTextNode
* pOrigNode
= pTNd
;
2453 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2454 *aSavePam
.GetPoint() == rPos
);
2455 assert(aSavePam
.GetPoint()->GetContentNode() == pOrigNode
);
2456 assert(aSavePam
.GetPoint()->GetNode() == rPos
.GetNode());
2457 assert(rPos
.GetNodeIndex() == pOrigNode
->GetIndex());
2459 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool)> restoreFunc(
2460 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool)
2462 if (!pContentStore
->Empty())
2464 pContentStore
->Restore(m_rDoc
, pOrigNode
->GetIndex()-SwNodeOffset(1), 0, true, false, eMode
);
2467 pTNd
->SplitContentNode(rPos
, &restoreFunc
);
2469 //A new node was inserted before the orig pTNd and the content up to
2470 //rPos moved into it. The old node is returned with the remainder
2471 //of the content in it.
2473 //aSavePam was created with rPos, it continues to point to the
2474 //old node, but with the *original* content index into the node.
2475 //Seeing as all the orignode content before that index has
2476 //been removed, the new index into the original node should now be set
2477 //to 0 and the content index of rPos should also be adapted to the
2479 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2480 *aSavePam
.GetPoint() == rPos
);
2481 assert(aSavePam
.GetPoint()->GetContentNode() == pOrigNode
);
2482 assert(aSavePam
.GetPoint()->GetNode() == rPos
.GetNode());
2483 assert(rPos
.GetNodeIndex() == pOrigNode
->GetIndex());
2484 aSavePam
.GetPoint()->SetContent(0);
2485 rPos
= *aSavePam
.GetMark() = *aSavePam
.GetPoint();
2488 if( rPos
.GetNode() == rPaM
.GetMark()->GetNode() )
2490 rPaM
.GetMark()->Assign( rPos
.GetNodeIndex() - SwNodeOffset(1) );
2491 rPaM
.GetMark()->SetContent( nMkContent
);
2495 // Put back the Pam by one "content"; so that it's always outside of
2496 // the manipulated range.
2497 // tdf#99692 don't Move() back if that would end up in another node
2498 // because moving backward is not necessarily the inverse of forward then.
2499 // (but do Move() back if we have split the node)
2500 const bool bNullContent
= !bSplit
&& aSavePam
.GetPoint()->GetContentIndex() == 0;
2503 aSavePam
.GetPoint()->Adjust(SwNodeOffset(-1));
2507 bool const success(aSavePam
.Move(fnMoveBackward
, GoInContent
));
2512 // Copy all Bookmarks that are within the Move range into an array,
2513 // that saves the position as an offset.
2514 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2519 pStt
->GetContentIndex(),
2520 pEnd
->GetContentIndex());
2522 // If there is no range anymore due to the above deletions (e.g. the
2523 // footnotes got deleted), it's still a valid Move!
2524 if( *rPaM
.GetPoint() != *rPaM
.GetMark() )
2526 // now do the actual move
2527 m_rDoc
.GetNodes().MoveRange( rPaM
, rPos
, m_rDoc
.GetNodes() );
2529 // after a MoveRange() the Mark is deleted
2530 if ( rPaM
.HasMark() ) // => no Move occurred!
2538 OSL_ENSURE( *aSavePam
.GetMark() == rPos
||
2539 ( aSavePam
.GetMark()->GetNode().GetContentNode() == nullptr ),
2540 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2541 *aSavePam
.GetMark() = rPos
;
2543 rPaM
.SetMark(); // create a Sel. around the new range
2544 pTNd
= aSavePam
.GetPointNode().GetTextNode();
2545 assert(!m_rDoc
.GetIDocumentUndoRedo().DoesUndo());
2546 bool bRemove
= true;
2547 // Do two Nodes have to be joined at the SavePam?
2550 if (pTNd
->CanJoinNext())
2552 // Always join next, because <pTNd> has to stay as it is.
2553 // A join previous from its next would more or less delete <pTNd>
2560 aSavePam
.GetPoint()->Adjust(SwNodeOffset(1));
2562 else if (bRemove
) // No move forward after joining with next paragraph
2564 aSavePam
.Move( fnMoveForward
, GoInContent
);
2567 // Insert the Bookmarks back into the Document.
2568 *rPaM
.GetMark() = *aSavePam
.Start();
2569 for(auto& rBkmk
: aSaveBkmks
)
2572 rPaM
.GetMark()->GetNode(),
2573 rPaM
.GetMark()->GetContentIndex());
2574 *rPaM
.GetPoint() = *aSavePam
.End();
2576 // Move the Flys to the new position.
2577 // note: rPos is at the end here; can't really tell flys that used to be
2578 // at the start of rPam from flys that used to be at the end of rPam
2579 // unfortunately, so some of them are going to end up with wrong anchor...
2580 RestFlyInRange( aSaveFlyArr
, *rPaM
.Start(), &rPos
.GetNode() );
2582 // restore redlines (if DOC_MOVEREDLINES is used)
2583 if( !aSaveRedl
.empty() )
2585 lcl_RestoreRedlines( m_rDoc
, *aSavePam
.Start(), aSaveRedl
);
2588 if( bUpdateFootnote
)
2590 if( !aTmpFntIdx
.empty() )
2592 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2596 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2599 m_rDoc
.getIDocumentState().SetModified();
2603 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange
& rRange
, SwNode
& rDestNd
,
2604 SwMoveFlags eMvFlags
)
2606 // Moves all Nodes to the new position.
2607 // Bookmarks are moved too (currently without Undo support).
2609 // If footnotes are being moved to the special section, remove them now.
2611 // Or else delete the Frames for all footnotes that are being moved
2612 // and have it rebuild after the Move (footnotes can change pages).
2613 // Additionally we have to correct the FootnoteIdx array's sorting.
2614 bool bUpdateFootnote
= false;
2615 SwFootnoteIdxs aTmpFntIdx
;
2617 std::unique_ptr
<SwUndoMove
> pUndo
;
2618 if ((SwMoveFlags::CREATEUNDOOBJ
& eMvFlags
) && m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2620 pUndo
.reset(new SwUndoMove( m_rDoc
, rRange
, rDestNd
));
2624 bUpdateFootnote
= lcl_SaveFootnote( rRange
.aStart
.GetNode(), rRange
.aEnd
.GetNode(), rDestNd
,
2625 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
);
2628 SaveRedlines_t aSaveRedl
;
2629 std::vector
<SwRangeRedline
*> aSavRedlInsPosArr
;
2630 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2632 lcl_SaveRedlines( rRange
, aSaveRedl
);
2634 // Find all RedLines that end at the InsPos.
2635 // These have to be moved back to the "old" position after the Move.
2636 SwRedlineTable::size_type nRedlPos
= m_rDoc
.getIDocumentRedlineAccess().GetRedlinePos( rDestNd
, RedlineType::Any
);
2637 if( SwRedlineTable::npos
!= nRedlPos
)
2639 const SwPosition
*pRStt
, *pREnd
;
2641 SwRangeRedline
* pTmp
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
2642 pRStt
= pTmp
->Start();
2643 pREnd
= pTmp
->End();
2644 if( pREnd
->GetNode() == rDestNd
&& pRStt
->GetNode() < rDestNd
)
2646 aSavRedlInsPosArr
.push_back( pTmp
);
2648 } while( pRStt
->GetNode() < rDestNd
&& ++nRedlPos
< m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
2652 // Copy all Bookmarks that are within the Move range into an array
2653 // that stores all references to positions as an offset.
2654 // The final mapping happens after the Move.
2655 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2656 DelBookmarks(rRange
.aStart
.GetNode(), rRange
.aEnd
.GetNode(), &aSaveBkmks
);
2658 // Save the paragraph-bound Flys, so that they can be moved.
2659 SaveFlyArr aSaveFlyArr
;
2660 if( !m_rDoc
.GetSpzFrameFormats()->empty() )
2661 SaveFlyInRange( rRange
, aSaveFlyArr
);
2663 // Set it to before the Position, so that it cannot be moved further.
2664 SwNodeIndex
aIdx( rDestNd
, -1 );
2666 std::optional
<SwNodeIndex
> oSaveInsPos
;
2668 oSaveInsPos
.emplace(rRange
.aStart
, -1 );
2671 bool bNoDelFrames
= bool(SwMoveFlags::NO_DELFRMS
& eMvFlags
);
2672 if( m_rDoc
.GetNodes().MoveNodes( rRange
, m_rDoc
.GetNodes(), rDestNd
, !bNoDelFrames
) )
2674 ++aIdx
; // again back to old position
2680 aIdx
= rRange
.aStart
;
2684 // move the Flys to the new position
2685 if( !aSaveFlyArr
.empty() )
2687 SwPosition
const tmp(aIdx
);
2688 RestFlyInRange(aSaveFlyArr
, tmp
, nullptr);
2691 // Add the Bookmarks back to the Document
2692 for(auto& rBkmk
: aSaveBkmks
)
2693 rBkmk
.SetInDoc(&m_rDoc
, aIdx
.GetNode());
2695 if( !aSavRedlInsPosArr
.empty() )
2697 for(SwRangeRedline
* pTmp
: aSavRedlInsPosArr
)
2699 if( m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp
) )
2701 SwPosition
* pEnd
= pTmp
->End();
2707 if( !aSaveRedl
.empty() )
2708 lcl_RestoreRedlines( m_rDoc
, aIdx
.GetIndex(), aSaveRedl
);
2712 pUndo
->SetDestRange( aIdx
.GetNode(), rDestNd
, *oSaveInsPos
);
2713 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2716 oSaveInsPos
.reset();
2718 if( bUpdateFootnote
)
2720 if( !aTmpFntIdx
.empty() )
2722 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2726 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2729 m_rDoc
.getIDocumentState().SetModified();
2733 void DocumentContentOperationsManager::MoveAndJoin( SwPaM
& rPaM
, SwPosition
& rPos
)
2735 SwNodeIndex
aIdx( rPaM
.Start()->GetNode() );
2736 bool bJoinText
= aIdx
.GetNode().IsTextNode();
2737 bool bOneNode
= rPaM
.GetPoint()->GetNode() == rPaM
.GetMark()->GetNode();
2738 --aIdx
; // in front of the move area!
2740 bool bRet
= MoveRange( rPaM
, rPos
, SwMoveFlags::DEFAULT
);
2741 if( !bRet
|| bOneNode
)
2746 SwTextNode
* pTextNd
= aIdx
.GetNode().GetTextNode();
2747 SwNodeIndex
aNxtIdx( aIdx
);
2748 if( pTextNd
&& pTextNd
->CanJoinNext( &aNxtIdx
) )
2750 { // Block so SwContentIndex into node is deleted before Join
2751 m_rDoc
.CorrRel( aNxtIdx
.GetNode(),
2752 SwPosition( *pTextNd
, pTextNd
->GetText().getLength() ),
2755 pTextNd
->JoinNext();
2759 // Overwrite only uses the point of the PaM, the mark is ignored; characters
2760 // are replaced from point until the end of the node; at the end of the node,
2761 // characters are inserted.
2762 bool DocumentContentOperationsManager::Overwrite( const SwPaM
&rRg
, const OUString
&rStr
)
2764 assert(rStr
.getLength());
2765 SwPosition
& rPt
= *const_cast<SwPosition
*>(rRg
.GetPoint());
2766 if( m_rDoc
.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2768 if( 1 == rStr
.getLength() )
2769 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPt
, rStr
[ 0 ] );
2770 m_rDoc
.DeleteAutoCorrExceptWord();
2773 SwTextNode
*pNode
= rPt
.GetNode().GetTextNode();
2774 if (!pNode
|| rStr
.getLength() > pNode
->GetSpaceLeft()) // worst case: no erase
2779 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2781 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2784 const size_t nOldAttrCnt
= pNode
->GetpSwpHints()
2785 ? pNode
->GetpSwpHints()->Count() : 0;
2786 SwDataChanged
aTmp( rRg
);
2787 sal_Int32
const nActualStart(rPt
.GetContentIndex());
2788 sal_Int32 nStart
= 0;
2790 bool bOldExpFlg
= pNode
->IsIgnoreDontExpand();
2791 pNode
->SetIgnoreDontExpand( true );
2793 for( sal_Int32 nCnt
= 0; nCnt
< rStr
.getLength(); ++nCnt
)
2795 // start behind the characters (to fix the attributes!)
2796 nStart
= rPt
.GetContentIndex();
2797 if (nStart
< pNode
->GetText().getLength())
2799 lcl_SkipAttr( pNode
, rPt
, nStart
);
2801 sal_Unicode c
= rStr
[ nCnt
];
2802 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2804 bool bMerged(false);
2805 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2807 SwUndo
*const pUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2808 SwUndoOverwrite
*const pUndoOW(
2809 dynamic_cast<SwUndoOverwrite
*>(pUndo
) );
2812 // if CanGrouping() returns true it's already merged
2813 bMerged
= pUndoOW
->CanGrouping(m_rDoc
, rPt
, c
);
2818 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2819 std::make_unique
<SwUndoOverwrite
>(m_rDoc
, rPt
, c
) );
2824 // start behind the characters (to fix the attributes!)
2825 if (nStart
< pNode
->GetText().getLength())
2826 rPt
.AdjustContent(+1);
2827 pNode
->InsertText( OUString(c
), rPt
, SwInsertFlags::EMPTYEXPAND
);
2828 if( nStart
+1 < rPt
.GetContentIndex() )
2830 rPt
.SetContent(nStart
);
2831 pNode
->EraseText( rPt
, 1 );
2832 rPt
.AdjustContent(+1);
2836 pNode
->SetIgnoreDontExpand( bOldExpFlg
);
2838 const size_t nNewAttrCnt
= pNode
->GetpSwpHints()
2839 ? pNode
->GetpSwpHints()->Count() : 0;
2840 if( nOldAttrCnt
!= nNewAttrCnt
)
2842 const SwUpdateAttr
aHint(0,0,0);
2843 pNode
->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint
, &aHint
));
2846 if (!m_rDoc
.GetIDocumentUndoRedo().DoesUndo() &&
2847 !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2849 SwPaM
aPam(rPt
.GetNode(), nActualStart
, rPt
.GetNode(), rPt
.GetContentIndex());
2850 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aPam
, true, RedlineType::Any
);
2852 else if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2854 // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2855 // characters are also included in aPam
2856 SwPaM
aPam(rPt
.GetNode(), nActualStart
, rPt
.GetNode(), rPt
.GetContentIndex());
2857 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2860 m_rDoc
.getIDocumentState().SetModified();
2864 bool DocumentContentOperationsManager::InsertString( const SwPaM
&rRg
, const OUString
&rStr
,
2865 const SwInsertFlags nInsertMode
)
2867 // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
2868 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2870 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2871 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg
);
2872 if (eOld
!= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags())
2873 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
2876 // fetching DoesUndo is surprisingly expensive
2877 bool bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2879 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2881 const SwPosition
& rPos
= *rRg
.GetPoint();
2883 if( m_rDoc
.GetAutoCorrExceptWord() ) // add to auto correction
2885 if( 1 == rStr
.getLength() && m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() )
2887 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPos
, rStr
[ 0 ] );
2889 m_rDoc
.DeleteAutoCorrExceptWord();
2892 SwTextNode
*const pNode
= rPos
.GetNode().GetTextNode();
2896 SwDataChanged
aTmp( rRg
);
2898 if (!bDoesUndo
|| !m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2900 OUString
const ins(pNode
->InsertText(rStr
, rPos
, nInsertMode
));
2903 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2904 std::make_unique
<SwUndoInsert
>(rPos
.GetNode(),
2905 rPos
.GetContentIndex(), ins
.getLength(), nInsertMode
));
2909 { // if Undo and grouping is enabled, everything changes!
2910 SwUndoInsert
* pUndo
= nullptr;
2912 // don't group the start if hints at the start should be expanded
2913 if (!(nInsertMode
& SwInsertFlags::FORCEHINTEXPAND
))
2915 SwUndo
*const pLastUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2916 SwUndoInsert
*const pUndoInsert(
2917 dynamic_cast<SwUndoInsert
*>(pLastUndo
) );
2918 if (pUndoInsert
&& pUndoInsert
->CanGrouping(rPos
))
2920 pUndo
= pUndoInsert
;
2924 CharClass
const& rCC
= GetAppCharClass();
2925 sal_Int32 nInsPos
= rPos
.GetContentIndex();
2929 pUndo
= new SwUndoInsert( rPos
.GetNode(), nInsPos
, 0, nInsertMode
,
2930 !rCC
.isLetterNumeric( rStr
, 0 ) );
2931 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2934 OUString
const ins(pNode
->InsertText(rStr
, rPos
, nInsertMode
));
2936 for (sal_Int32 i
= 0; i
< ins
.getLength(); ++i
)
2939 // if CanGrouping() returns true, everything has already been done
2940 if (!pUndo
->CanGrouping(ins
[i
]))
2942 pUndo
= new SwUndoInsert(rPos
.GetNode(), nInsPos
, 1, nInsertMode
,
2943 !rCC
.isLetterNumeric(ins
, i
));
2944 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2949 // To-Do - add 'SwExtraRedlineTable' also ?
2950 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2952 SwPaM
aPam( rPos
.GetNode(), aTmp
.GetContent(),
2953 rPos
.GetNode(), rPos
.GetContentIndex());
2954 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2956 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
2957 new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2961 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
2965 m_rDoc
.getIDocumentState().SetModified();
2969 void DocumentContentOperationsManager::SetIME(bool bIME
)
2974 bool DocumentContentOperationsManager::GetIME() const
2979 void DocumentContentOperationsManager::TransliterateText(
2981 utl::TransliterationWrapper
& rTrans
)
2983 std::unique_ptr
<SwUndoTransliterate
> pUndo
;
2984 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2985 pUndo
.reset(new SwUndoTransliterate( rPaM
, rTrans
));
2987 auto [pStt
, pEnd
] = rPaM
.StartEnd(); // SwPosition*
2988 SwNodeOffset nSttNd
= pStt
->GetNodeIndex(),
2989 nEndNd
= pEnd
->GetNodeIndex();
2990 sal_Int32 nSttCnt
= pStt
->GetContentIndex();
2991 sal_Int32 nEndCnt
= pEnd
->GetContentIndex();
2993 SwTextNode
* pTNd
= pStt
->GetNode().GetTextNode();
2994 bool bNoSelection
= (pStt
== pEnd
) && pTNd
; // no selection?
2997 /* Check if cursor is inside of a word */
2998 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
2999 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
3000 pTNd
->GetText(), nSttCnt
,
3001 g_pBreakIt
->GetLocale( pTNd
->GetLang( nSttCnt
) ),
3002 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
3005 if( aBndry
.startPos
< nSttCnt
&& nSttCnt
< aBndry
.endPos
)
3007 /* Cursor is inside of a word */
3008 if (rTrans
.getType() == TransliterationFlags::SENTENCE_CASE
) {
3009 /* set current sentence as 'area of effect' */
3010 nSttCnt
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
3011 pTNd
->GetText(), nSttCnt
,
3012 g_pBreakIt
->GetLocale( pTNd
->GetLang( nSttCnt
) ) );
3013 nEndCnt
= g_pBreakIt
->GetBreakIter()->endOfSentence(
3014 pTNd
->GetText(), nEndCnt
,
3015 g_pBreakIt
->GetLocale( pTNd
->GetLang( nEndCnt
) ) );
3017 /* Set current word as 'area of effect' */
3018 nSttCnt
= aBndry
.startPos
;
3019 nEndCnt
= aBndry
.endPos
;
3022 /* Cursor is not inside of a word. Nothing should happen. */
3023 /* Except in the case of change tracking, when the cursor is at the end of the change */
3024 /* Recognize and reject the previous deleted and inserted words to allow to cycle */
3025 IDocumentRedlineAccess
& rIDRA
= m_rDoc
.getIDocumentRedlineAccess();
3026 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA
.GetRedlineFlags() ) &&
3027 pStt
->GetContentIndex() > 0 )
3029 SwPosition
aPos(*pStt
->GetContentNode(), pStt
->GetContentIndex() - 1);
3030 SwRedlineTable::size_type n
= 0;
3032 const SwRangeRedline
* pFnd
=
3033 rIDRA
.GetRedlineTable().FindAtPosition( aPos
, n
);
3034 if ( pFnd
&& RedlineType::Insert
== pFnd
->GetType() && n
> 0 )
3036 const SwRangeRedline
* pFnd2
= rIDRA
.GetRedlineTable()[n
-1];
3037 if ( RedlineType::Delete
== pFnd2
->GetType() &&
3038 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() &&
3039 *pFnd2
->End() == *pFnd
->Start() &&
3040 pFnd
->GetAuthor() == pFnd2
->GetAuthor() )
3042 SwPosition
aPos2(*pFnd2
->End());
3043 rIDRA
.RejectRedline(*pFnd
, true);
3044 rIDRA
.RejectRedline(*pFnd2
, true);
3045 // positionate the text cursor inside the changed word to allow to cycle
3046 if ( SwWrtShell
*pWrtShell
= dynamic_cast<SwWrtShell
*>(
3047 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell()) )
3049 pWrtShell
->GetCursor()->GetPoint()->
3050 Assign(*aPos2
.GetContentNode(), aPos2
.GetContentIndex() - 1);
3060 bool bHasTrackedChange
= false;
3061 IDocumentRedlineAccess
& rIDRA
= m_rDoc
.getIDocumentRedlineAccess();
3062 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA
.GetRedlineFlags() ) &&
3063 pEnd
->GetContentIndex() > 0 )
3065 // search all own redlines within the selected area
3066 SwRedlineTable::size_type n
= SwRedlineTable::npos
;
3067 const SwRedlineTable
& aRedlineTable
= rIDRA
.GetRedlineTable();
3068 for( SwRedlineTable::size_type m
= 0; m
< aRedlineTable
.size(); ++m
)
3070 const SwRangeRedline
* pRedline
= aRedlineTable
[ m
];
3072 if ( *pRedline
->Start() > *pEnd
)
3075 if ( *pRedline
->Start() >= *pStt
)
3079 if ( n
!= SwRedlineTable::npos
&& n
> 0 )
3081 SwWrtShell
*pWrtShell
= dynamic_cast<SwWrtShell
*>(
3082 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell());
3084 sal_Int32 nRejectedCharacters
= 0;
3085 SwRangeRedline
* pFnd
= rIDRA
.GetRedlineTable()[n
];
3086 SwRangeRedline
* pFnd2
= rIDRA
.GetRedlineTable()[--n
];
3087 // loop on all redlines of a case changing, and reject them
3088 while ( ( ( RedlineType::Insert
== pFnd
->GetType() &&
3089 RedlineType::Delete
== pFnd2
->GetType() ) ||
3090 ( RedlineType::Delete
== pFnd
->GetType() &&
3091 RedlineType::Insert
== pFnd2
->GetType() ) ) &&
3093 // use time stamp to recognize the multiple selections in the text,
3094 // not only the changes from the same author within the (sometimes
3095 // incomplete) selection
3096 ( pFnd2
->GetTimeStamp() == pFnd
->GetTimeStamp() ||
3097 ( pStt
->GetContentNode() < pFnd2
->Start()->GetContentNode() ||
3098 ( pStt
->GetContentNode() == pFnd2
->Start()->GetContentNode() &&
3099 nSttCnt
<= pFnd2
->Start()->GetContentIndex() ) ) ) &&
3100 pFnd
->GetAuthor() == pFnd2
->GetAuthor() )
3102 bHasTrackedChange
= true;
3104 if ( RedlineType::Insert
== pFnd
->GetType() )
3105 nRejectedCharacters
+= pFnd
->GetText().getLength();
3107 rIDRA
.RejectRedline(*pFnd
, true);
3112 pFnd2
= rIDRA
.GetRedlineTable()[--n
];
3115 // remove the last item and restore the original selection within the node
3116 if ( bHasTrackedChange
)
3118 if ( nSttNd
== nEndNd
)
3120 pWrtShell
->GetCursor()->GetPoint()->
3121 Assign(*rPaM
.Start()->GetContentNode(), nSttCnt
);
3122 if ( nEndCnt
>= nRejectedCharacters
)
3123 pWrtShell
->GetCursor()->GetMark()->
3124 Assign(*rPaM
.End()->GetContentNode(), nEndCnt
- nRejectedCharacters
);
3126 rIDRA
.RejectRedline(*pFnd
, true);
3131 // TODO handle title case to lowercase
3132 if ( bHasTrackedChange
)
3136 bool bUseRedlining
= m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn();
3137 // as a workaround for a known performance problem, switch off redlining
3138 // to avoid freezing, if transliteration could result too many redlines
3139 if ( bUseRedlining
)
3141 const sal_uLong nMaxRedlines
= 500;
3142 const bool bIsTitleCase
= rTrans
.getType() == TransliterationFlags::TITLE_CASE
;
3143 sal_uLong nAffectedNodes
= 0;
3144 sal_uLong nAffectedChars
= nEndCnt
;
3145 SwNodeIndex
aIdx( pStt
->GetNode() );
3146 for( ; aIdx
.GetIndex() <= nEndNd
; ++aIdx
)
3148 SwTextNode
* pAffectedNode
= aIdx
.GetNode().GetTextNode();
3150 // don't count not text nodes or empty text nodes
3151 if( !pAffectedNode
|| pAffectedNode
->GetText().isEmpty() )
3156 // count characters of the node (the last - maybe partially
3157 // selected - node was counted at initialization of nAffectedChars)
3158 if( aIdx
.GetIndex() < nEndNd
)
3159 nAffectedChars
+= pAffectedNode
->GetText().getLength();
3161 // transliteration creates n redlines for n nodes, except in the
3162 // case of title case, where it creates n redlines for n words
3163 if( nAffectedNodes
> nMaxRedlines
||
3164 // estimate word count based on the character count, where
3165 // 6 = average English word length is ~5 letters + space
3166 ( bIsTitleCase
&& (nAffectedChars
- nSttCnt
)/6 > nMaxRedlines
) )
3168 bUseRedlining
= false;
3174 if( nSttNd
!= nEndNd
) // is more than one text node involved?
3176 // iterate over all affected text nodes, the first and the last one
3177 // may be incomplete because the selection starts and/or ends there
3179 SwNodeIndex
aIdx( pStt
->GetNode() );
3185 pTNd
->TransliterateText(
3186 rTrans
, nSttCnt
, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3190 for( ; aIdx
.GetIndex() < nEndNd
; ++aIdx
)
3192 pTNd
= aIdx
.GetNode().GetTextNode();
3195 pTNd
->TransliterateText(
3196 rTrans
, 0, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3200 if( nEndCnt
&& nullptr != ( pTNd
= pEnd
->GetNode().GetTextNode() ))
3202 pTNd
->TransliterateText( rTrans
, 0, nEndCnt
, pUndo
.get(), bUseRedlining
);
3205 else if( pTNd
&& nSttCnt
< nEndCnt
)
3207 pTNd
->TransliterateText( rTrans
, nSttCnt
, nEndCnt
, pUndo
.get(), bUseRedlining
);
3209 if( pUndo
&& pUndo
->HasData() )
3211 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
3214 // restore selection after tracked changes
3215 if ( !bNoSelection
&& bUseRedlining
&& nSttNd
== nEndNd
)
3217 if ( SwWrtShell
*pWrtShell
= dynamic_cast<SwWrtShell
*>(
3218 m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell()) )
3220 *pWrtShell
->GetCursor()->GetMark() = *pWrtShell
->GetCursor()->End();
3221 pWrtShell
->GetCursor()->GetPoint()->Assign(*pStt
->GetContentNode(), nSttCnt
);
3225 m_rDoc
.getIDocumentState().SetModified();
3228 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertGraphic(
3230 const OUString
& rGrfName
,
3231 const OUString
& rFltName
,
3232 const Graphic
* pGraphic
,
3233 const SfxItemSet
* pFlyAttrSet
,
3234 const SfxItemSet
* pGrfAttrSet
,
3235 SwFrameFormat
* pFrameFormat
)
3238 pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC
);
3239 SwGrfNode
* pSwGrfNode
= SwNodes::MakeGrfNode(
3240 m_rDoc
.GetNodes().GetEndOfAutotext(),
3241 rGrfName
, rFltName
, pGraphic
,
3242 m_rDoc
.GetDfltGrfFormatColl() );
3243 SwFlyFrameFormat
* pSwFlyFrameFormat
= InsNoTextNode( *rRg
.GetPoint(), pSwGrfNode
,
3244 pFlyAttrSet
, pGrfAttrSet
, pFrameFormat
);
3245 return pSwFlyFrameFormat
;
3248 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertEmbObject(
3249 const SwPaM
&rRg
, const svt::EmbeddedObjectRef
& xObj
,
3250 SfxItemSet
* pFlyAttrSet
)
3252 sal_uInt16 nId
= RES_POOLFRM_OLE
;
3255 SvGlobalName
aClassName( xObj
->getClassID() );
3256 if (SotExchange::IsMath(aClassName
))
3258 nId
= RES_POOLFRM_FORMEL
;
3262 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId
);
3264 return InsNoTextNode( *rRg
.GetPoint(), m_rDoc
.GetNodes().MakeOLENode(
3265 m_rDoc
.GetNodes().GetEndOfAutotext(),
3267 m_rDoc
.GetDfltGrfFormatColl() ),
3268 pFlyAttrSet
, nullptr,
3272 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertOLE(const SwPaM
&rRg
, const OUString
& rObjName
,
3274 const SfxItemSet
* pFlyAttrSet
,
3275 const SfxItemSet
* pGrfAttrSet
)
3277 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE
);
3279 return InsNoTextNode( *rRg
.GetPoint(),
3280 m_rDoc
.GetNodes().MakeOLENode(
3281 m_rDoc
.GetNodes().GetEndOfAutotext(),
3284 m_rDoc
.GetDfltGrfFormatColl(),
3286 pFlyAttrSet
, pGrfAttrSet
,
3290 void DocumentContentOperationsManager::ReRead( SwPaM
& rPam
, const OUString
& rGrfName
,
3291 const OUString
& rFltName
, const Graphic
* pGraphic
)
3294 if( !(( !rPam
.HasMark()
3295 || rPam
.GetPoint()->GetNodeIndex() == rPam
.GetMark()->GetNodeIndex() )
3296 && nullptr != ( pGrfNd
= rPam
.GetPoint()->GetNode().GetGrfNode() )) )
3299 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3301 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoReRead
>(rPam
, *pGrfNd
));
3304 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
3305 if( MirrorGraph::Dont
!= pGrfNd
->GetSwAttrSet().
3306 GetMirrorGrf().GetValue() )
3307 pGrfNd
->SetAttr( SwMirrorGrf() );
3309 pGrfNd
->ReRead( rGrfName
, rFltName
, pGraphic
);
3310 m_rDoc
.getIDocumentState().SetModified();
3313 // Insert drawing object, which has to be already inserted in the DrawModel
3314 SwDrawFrameFormat
* DocumentContentOperationsManager::InsertDrawObj(
3316 SdrObject
& rDrawObj
,
3317 const SfxItemSet
& rFlyAttrSet
)
3319 SwDrawFrameFormat
* pFormat
= m_rDoc
.MakeDrawFrameFormat( OUString(), m_rDoc
.GetDfltFrameFormat() );
3321 const SwFormatAnchor
* pAnchor
= rFlyAttrSet
.GetItemIfSet( RES_ANCHOR
, false );
3322 pFormat
->SetFormatAttr( rFlyAttrSet
);
3324 // Didn't set the Anchor yet?
3325 // DrawObjecte must never end up in the Header/Footer!
3326 RndStdIds eAnchorId
= pAnchor
!= nullptr ? pAnchor
->GetAnchorId() : pFormat
->GetAnchor().GetAnchorId();
3327 const bool bIsAtContent
= (RndStdIds::FLY_AT_PAGE
!= eAnchorId
);
3329 const SwPosition
* pChkPos
= nullptr;
3330 if ( pAnchor
== nullptr )
3332 pChkPos
= rRg
.GetPoint();
3334 else if ( bIsAtContent
)
3337 pAnchor
->GetContentAnchor() ? pAnchor
->GetContentAnchor() : rRg
.GetPoint();
3340 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
3341 if( pChkPos
!= nullptr
3342 && ::CheckControlLayer( &rDrawObj
)
3343 && m_rDoc
.IsInHeaderFooter( pChkPos
->GetNode() ) )
3345 // apply at-page anchor format
3346 eAnchorId
= RndStdIds::FLY_AT_PAGE
;
3347 pFormat
->SetFormatAttr( SwFormatAnchor( eAnchorId
) );
3349 else if( pAnchor
== nullptr
3351 && pAnchor
->GetAnchorNode() == nullptr ) )
3353 // apply anchor format
3354 SwFormatAnchor
aAnch( pAnchor
!= nullptr ? *pAnchor
: pFormat
->GetAnchor() );
3355 eAnchorId
= aAnch
.GetAnchorId();
3356 if ( eAnchorId
== RndStdIds::FLY_AT_FLY
)
3358 const SwStartNode
* pStartNode
= rRg
.GetPointNode().FindFlyStartNode();
3360 SwPosition
aPos(*pStartNode
);
3361 aAnch
.SetAnchor( &aPos
);
3365 aAnch
.SetAnchor( rRg
.GetPoint() );
3366 if ( eAnchorId
== RndStdIds::FLY_AT_PAGE
)
3368 eAnchorId
= dynamic_cast<const SdrUnoObj
*>( &rDrawObj
) != nullptr ? RndStdIds::FLY_AS_CHAR
: RndStdIds::FLY_AT_PARA
;
3369 aAnch
.SetType( eAnchorId
);
3372 pFormat
->SetFormatAttr( aAnch
);
3375 // insert text attribute for as-character anchored drawing object
3376 if ( eAnchorId
== RndStdIds::FLY_AS_CHAR
)
3378 bool bAnchorAtPageAsFallback
= true;
3379 const SwFormatAnchor
& rDrawObjAnchorFormat
= pFormat
->GetAnchor();
3380 if ( rDrawObjAnchorFormat
.GetAnchorNode() != nullptr )
3382 SwTextNode
* pAnchorTextNode
=
3383 rDrawObjAnchorFormat
.GetAnchorNode()->GetTextNode();
3384 if ( pAnchorTextNode
!= nullptr )
3386 const sal_Int32 nStt
= rDrawObjAnchorFormat
.GetContentAnchor()->GetContentIndex();
3387 SwFormatFlyCnt
aFormat( pFormat
);
3388 pAnchorTextNode
->InsertItem( aFormat
, nStt
, nStt
);
3389 bAnchorAtPageAsFallback
= false;
3393 if ( bAnchorAtPageAsFallback
)
3395 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
3396 pFormat
->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE
) );
3400 SwDrawContact
* pContact
= new SwDrawContact( pFormat
, &rDrawObj
);
3402 // Create Frames if necessary
3403 if( m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() )
3405 // create layout representation
3406 pFormat
->MakeFrames();
3407 // #i42319# - follow-up of #i35635#
3408 // move object to visible layer
3410 if ( pContact
->GetAnchorFrame() )
3412 pContact
->MoveObjToVisibleLayer( &rDrawObj
);
3416 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3418 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsLayFormat
>(pFormat
, SwNodeOffset(0), 0) );
3421 m_rDoc
.getIDocumentState().SetModified();
3425 bool DocumentContentOperationsManager::SplitNode( const SwPosition
&rPos
, bool bChkTableStart
)
3427 SwContentNode
*pNode
= rPos
.GetNode().GetContentNode();
3428 if(nullptr == pNode
)
3432 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
3433 // After that they can be before/after the position.
3434 SwDataChanged
aTmp( m_rDoc
, rPos
);
3437 SwUndoSplitNode
* pUndo
= nullptr;
3438 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3440 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3441 // insert the Undo object (currently only for TextNode)
3442 if( pNode
->IsTextNode() )
3444 pUndo
= new SwUndoSplitNode( m_rDoc
, rPos
, bChkTableStart
);
3445 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3449 // Update the rsid of the old and the new node unless
3450 // the old node is split at the beginning or at the end
3451 SwTextNode
*pTextNode
= rPos
.GetNode().GetTextNode();
3452 const sal_Int32 nPos
= rPos
.GetContentIndex();
3453 if( pTextNode
&& nPos
&& nPos
!= pTextNode
->Len() )
3455 m_rDoc
.UpdateParRsid( pTextNode
);
3458 //JP 28.01.97: Special case for SplitNode at table start:
3459 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
3460 // then insert a paragraph before it.
3461 if( bChkTableStart
&& !rPos
.GetContentIndex() && pNode
->IsTextNode() )
3463 SwNodeOffset nPrevPos
= rPos
.GetNodeIndex() - 1;
3464 SwTableNode
* pTableNd
;
3465 const SwNode
* pNd
= m_rDoc
.GetNodes()[ nPrevPos
];
3466 if( pNd
->IsStartNode() &&
3467 SwTableBoxStartNode
== static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() &&
3468 nullptr != ( pTableNd
= m_rDoc
.GetNodes()[ --nPrevPos
]->GetTableNode() ) &&
3469 ((( pNd
= m_rDoc
.GetNodes()[ --nPrevPos
])->IsStartNode() &&
3470 SwTableBoxStartNode
!= static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() )
3471 || ( pNd
->IsEndNode() && pNd
->StartOfSectionNode()->IsTableNode() )
3472 || pNd
->IsContentNode() ))
3474 if( pNd
->IsContentNode() )
3476 //JP 30.04.99 Bug 65660:
3477 // There are no page breaks outside of the normal body area,
3478 // so this is not a valid condition to insert a paragraph.
3479 if( nPrevPos
< m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3483 // Only if the table has page breaks!
3484 const SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3485 if( SfxItemState::SET
!= pFrameFormat
->GetItemState(RES_PAGEDESC
, false) &&
3486 SfxItemState::SET
!= pFrameFormat
->GetItemState( RES_BREAK
, false ) )
3493 SwTextNode
* pTextNd
= m_rDoc
.GetNodes().MakeTextNode(
3495 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT
));
3498 const_cast<SwPosition
&>(rPos
).Assign( pTableNd
->GetIndex() - SwNodeOffset(1) );
3500 // only add page breaks/styles to the body area
3501 if( nPrevPos
> m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3503 SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3504 const SfxPoolItem
*pItem
;
3505 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_PAGEDESC
,
3508 pTextNd
->SetAttr( *pItem
);
3509 pFrameFormat
->ResetFormatAttr( RES_PAGEDESC
);
3511 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_BREAK
,
3514 pTextNd
->SetAttr( *pItem
);
3515 pFrameFormat
->ResetFormatAttr( RES_BREAK
);
3520 pUndo
->SetTableFlag();
3521 m_rDoc
.getIDocumentState().SetModified();
3528 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
3529 pContentStore
->Save( m_rDoc
, rPos
.GetNodeIndex(), rPos
.GetContentIndex(), true );
3530 assert(pNode
->IsTextNode());
3531 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool bAtStart
)> restoreFunc(
3532 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool const bAtStart
)
3534 if (!pContentStore
->Empty())
3535 { // move all bookmarks, TOXMarks, FlyAtCnt
3536 pContentStore
->Restore(m_rDoc
, rPos
.GetNodeIndex()-SwNodeOffset(1), 0, true, bAtStart
&& (eMode
& sw::mark::RestoreMode::Flys
), eMode
);
3538 if (eMode
& sw::mark::RestoreMode::NonFlys
)
3540 // To-Do - add 'SwExtraRedlineTable' also ?
3541 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
3542 (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() &&
3543 !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty()))
3547 aPam
.Move( fnMoveBackward
);
3548 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3550 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
3551 new SwRangeRedline(RedlineType::Insert
, aPam
), true);
3555 m_rDoc
.getIDocumentRedlineAccess().SplitRedline(aPam
);
3560 pNode
->GetTextNode()->SplitContentNode(rPos
, &restoreFunc
);
3562 m_rDoc
.getIDocumentState().SetModified();
3566 bool DocumentContentOperationsManager::AppendTextNode( SwPosition
& rPos
)
3568 // create new node before EndOfContent
3569 SwTextNode
* pCurNode
= rPos
.GetNode().GetTextNode();
3572 // so then one can be created!
3573 SwNodeIndex
aIdx( rPos
.GetNode(), 1 );
3574 pCurNode
= m_rDoc
.GetNodes().MakeTextNode( aIdx
.GetNode(),
3575 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
));
3578 pCurNode
= pCurNode
->AppendNode( rPos
)->GetTextNode();
3580 rPos
.Adjust(SwNodeOffset(1));
3582 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3584 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsert
>( rPos
.GetNode() ) );
3587 // To-Do - add 'SwExtraRedlineTable' also ?
3588 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
3592 aPam
.Move( fnMoveBackward
);
3593 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
3594 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
3596 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
3599 m_rDoc
.getIDocumentState().SetModified();
3603 bool DocumentContentOperationsManager::ReplaceRange( SwPaM
& rPam
, const OUString
& rStr
,
3604 const bool bRegExReplace
)
3606 // unfortunately replace works slightly differently from delete,
3607 // so we cannot use lcl_DoWithBreaks here...
3609 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
3611 SwPaM
aPam( *rPam
.GetMark(), *rPam
.GetPoint() );
3612 aPam
.Normalize(false);
3613 if (aPam
.GetPoint()->GetNode() != aPam
.GetMark()->GetNode())
3615 aPam
.Move(fnMoveBackward
);
3617 OSL_ENSURE((aPam
.GetPoint()->GetNode() == aPam
.GetMark()->GetNode()), "invalid pam?");
3619 sw::CalcBreaks(Breaks
, aPam
);
3621 while (!Breaks
.empty() // skip over prefix of dummy chars
3622 && (aPam
.GetMark()->GetNodeIndex() == Breaks
.begin()->first
)
3623 && (aPam
.GetMark()->GetContentIndex() == Breaks
.begin()->second
))
3626 aPam
.GetMark()->AdjustContent(+1); // always in bounds if Breaks valid
3627 Breaks
.erase(Breaks
.begin());
3629 *rPam
.Start() = *aPam
.GetMark(); // update start of original pam w/ prefix
3633 // park aPam somewhere so it does not point to node that is deleted
3635 aPam
.GetPoint()->Assign(m_rDoc
.GetNodes().GetEndOfContent());
3636 return ReplaceRangeImpl(rPam
, rStr
, bRegExReplace
); // original pam!
3639 // Deletion must be split into several parts if the text node
3640 // contains a text attribute with end and with dummy character
3641 // and the selection does not contain the text attribute completely,
3642 // but overlaps its start (left), where the dummy character is.
3645 // iterate from end to start, to avoid invalidating the offsets!
3646 auto iter( Breaks
.rbegin() );
3647 SwNodeOffset
nOffset(0);
3648 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
3649 OSL_ENSURE(aPam
.GetPoint() == aPam
.End(), "wrong!");
3650 SwPosition
& rEnd( *aPam
.End() );
3651 SwPosition
& rStart( *aPam
.Start() );
3653 // set end of temp pam to original end (undo Move backward above)
3655 // after first deletion, rEnd will point into the original text node again!
3657 while (iter
!= Breaks
.rend())
3659 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
3660 if (rStart
< rEnd
) // check if part is empty
3662 bRet
&= (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3663 ? DeleteAndJoinWithRedlineImpl(aPam
, SwDeleteFlags::Default
)
3664 : DeleteAndJoinImpl(aPam
, SwDeleteFlags::Default
);
3665 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // deleted fly nodes...
3667 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
3671 rStart
= *rPam
.Start(); // set to original start
3672 assert(rStart
< rEnd
&& "replace part empty!");
3673 if (rStart
< rEnd
) // check if part is empty
3675 bRet
&= ReplaceRangeImpl(aPam
, rStr
, bRegExReplace
);
3678 rPam
= aPam
; // update original pam (is this required?)
3683 bool DocumentContentOperationsManager::InsertPoolItem(
3685 const SfxPoolItem
&rHt
,
3686 const SetAttrMode nFlags
,
3687 SwRootFrame
const*const pLayout
,
3688 SwTextAttr
**ppNewTextAttr
)
3690 SwDataChanged
aTmp( rRg
);
3691 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3692 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3694 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3695 pUndoAttr
.reset(new SwUndoAttr( rRg
, rHt
, nFlags
));
3698 SfxItemSet
aSet( m_rDoc
.GetAttrPool(), rHt
.Which(), rHt
.Which() );
3700 const bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, aSet
, nFlags
, pUndoAttr
.get(), pLayout
, ppNewTextAttr
);
3702 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3704 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3709 m_rDoc
.getIDocumentState().SetModified();
3714 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM
&rRg
, const SfxItemSet
&rSet
,
3715 const SetAttrMode nFlags
, SwRootFrame
const*const pLayout
)
3717 SwDataChanged
aTmp( rRg
);
3718 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3719 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3721 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3722 pUndoAttr
.reset(new SwUndoAttr( rRg
, rSet
, nFlags
));
3725 bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, rSet
, nFlags
, pUndoAttr
.get(), pLayout
, /*ppNewTextAttr*/nullptr );
3727 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3729 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3733 m_rDoc
.getIDocumentState().SetModified();
3736 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition
& rPos
)
3738 const SwTextNode
* pTNd
= rPos
.GetNode().GetTextNode();
3742 const OUString
& rText
= pTNd
->GetText();
3744 while (nIdx
< rText
.getLength())
3746 sal_Unicode
const cCh
= rText
[nIdx
];
3747 if (('\t' != cCh
) && (' ' != cCh
))
3757 aPam
.GetPoint()->SetContent(0);
3759 aPam
.GetMark()->SetContent(nIdx
);
3760 DeleteRange( aPam
);
3764 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(SwPaM
& rPaM
)
3766 for (SwPaM
& rSel
:rPaM
.GetRingContainer())
3768 SwNodeOffset nStt
= rSel
.Start()->GetNodeIndex();
3769 SwNodeOffset nEnd
= rSel
.End()->GetNodeIndex();
3770 for (SwNodeOffset nPos
= nStt
; nPos
<=nEnd
; nPos
++)
3771 RemoveLeadingWhiteSpace(SwPosition(rSel
.GetBound().GetNodes(), nPos
));
3775 // Copy method from SwDoc - "copy Flys in Flys"
3776 /// note: rRg/rInsPos *exclude* a partially selected start text node;
3777 /// pCopiedPaM *includes* a partially selected start text node
3778 void DocumentContentOperationsManager::CopyWithFlyInFly(
3779 const SwNodeRange
& rRg
,
3781 const std::pair
<const SwPaM
&, const SwPosition
&>* pCopiedPaM
/*and real insert pos*/,
3782 const bool bMakeNewFrames
,
3783 const bool bDelRedlines
,
3784 const bool bCopyFlyAtFly
,
3785 SwCopyFlags
const flags
) const
3787 assert(!pCopiedPaM
|| pCopiedPaM
->first
.End()->GetNode() == rRg
.aEnd
.GetNode());
3788 assert(!pCopiedPaM
|| pCopiedPaM
->second
.GetNode() <= rInsPos
);
3790 SwDoc
& rDest
= rInsPos
.GetDoc();
3791 SwNodeIndex
aSavePos( rInsPos
);
3793 SwPaM
aCopiedPaM(rRg
.aStart
, rRg
.aEnd
);
3795 aCopiedPaM
= pCopiedPaM
->first
;
3797 if (rRg
.aStart
!= rRg
.aEnd
)
3799 bool bEndIsEqualEndPos
= rInsPos
== rRg
.aEnd
.GetNode();
3801 SaveRedlEndPosForRestore
aRedlRest( rInsPos
, 0 );
3802 auto savedEndContentIndex
= aCopiedPaM
.End()->GetContentIndex();
3804 // insert behind the already copied start node
3805 m_rDoc
.GetNodes().CopyNodes( rRg
, rInsPos
, false, true );
3806 aRedlRest
.Restore();
3808 if (bEndIsEqualEndPos
)
3810 const_cast<SwNodeIndex
&>(rRg
.aEnd
).Assign(aSavePos
.GetNode(), +1);
3811 // pCopiedPaM->first now spans a range from the start of the original selection
3812 // to the end of newly added text, and the insertion point is in the middle of
3813 // that range. Adjust the local copy to cover the original copied PaM.
3814 aCopiedPaM
.End()->Assign(rRg
.aEnd
, savedEndContentIndex
);
3818 // Also copy all bookmarks
3819 // guess this must be done before the DelDummyNodes below as that
3820 // deletes nodes so would mess up the index arithmetic
3821 // sw_fieldmarkhide: also needs to be done before making frames
3822 if (m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount())
3824 SwPosition
targetPos(aSavePos
, SwNodeOffset(rRg
.aStart
!= rRg
.aEnd
? +1 : 0));
3825 if (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->GetNode())
3827 // there is 1 (partially selected, maybe) paragraph before
3828 assert(SwNodeIndex(rRg
.aStart
, -1) == pCopiedPaM
->first
.Start()->GetNode());
3829 // only use the passed in target SwPosition if the source PaM point
3830 // is on a different node; if it was the same node then the target
3831 // position was likely moved along by the copy operation and now
3832 // points to the end of the range!
3833 targetPos
= pCopiedPaM
->second
;
3836 sw::CopyBookmarks(aCopiedPaM
, targetPos
, flags
);
3839 if (rRg
.aStart
!= rRg
.aEnd
)
3844 #if OSL_DEBUG_LEVEL > 0
3846 //JP 17.06.99: Bug 66973 - check count only if the selection is in
3847 // the same section or there's no section, because sections that are
3848 // not fully selected are not copied.
3849 const SwSectionNode
* pSSectNd
= rRg
.aStart
.GetNode().FindSectionNode();
3850 SwNodeIndex
aTmpI( rRg
.aEnd
, -1 );
3851 const SwSectionNode
* pESectNd
= aTmpI
.GetNode().FindSectionNode();
3852 if( pSSectNd
== pESectNd
&&
3853 !rRg
.aStart
.GetNode().IsSectionNode() &&
3854 !aTmpI
.GetNode().IsEndNode() )
3856 // If the range starts with a SwStartNode, it isn't copied
3857 SwNodeOffset
offset( (rRg
.aStart
.GetNode().GetNodeType() != SwNodeType::Start
) ? 1 : 0 );
3858 OSL_ENSURE( rInsPos
.GetIndex() - aSavePos
.GetIndex() ==
3859 rRg
.aEnd
.GetIndex() - rRg
.aStart
.GetIndex() - 1 + offset
,
3860 "An insufficient number of nodes were copied!" );
3866 ::sw::UndoGuard
const undoGuard(rDest
.GetIDocumentUndoRedo());
3867 // this must happen before lcl_DeleteRedlines() because it counts nodes
3868 CopyFlyInFlyImpl(rRg
, pCopiedPaM
? &aCopiedPaM
: nullptr,
3869 // see comment below regarding use of pCopiedPaM->second
3870 (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->GetNode())
3871 ? pCopiedPaM
->second
.GetNode()
3872 : aSavePos
.GetNode(),
3878 SwNodeRange
aCpyRange( aSavePos
.GetNode(), rInsPos
);
3880 if( bDelRedlines
&& ( RedlineFlags::DeleteRedlines
& rDest
.getIDocumentRedlineAccess().GetRedlineFlags() ))
3881 lcl_DeleteRedlines( rRg
, aCpyRange
);
3883 rDest
.GetNodes().DelDummyNodes( aCpyRange
);
3885 // tdf#159023 create layout frames after DelDummyNodes():
3886 // InsertCnt_() does early return on the first SwPlaceholderNode
3887 if (rRg
.aStart
!= rRg
.aEnd
)
3889 --aSavePos
; // restore temporarily...
3890 bool isRecreateEndNode(false);
3891 if (bMakeNewFrames
) // tdf#130685 only after aRedlRest
3892 { // recreate from previous node (could be merged now)
3893 o3tl::sorted_vector
<SwTextFrame
*> frames
;
3894 SwTextNode
* pNode(aSavePos
.GetNode().GetTextNode());
3895 SwTextNode
*const pEndNode(rInsPos
.GetTextNode());
3898 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3899 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3901 if (pFrame
->getRootFrame()->HasMergedParas())
3903 frames
.insert(pFrame
);
3904 // tdf#135061 check if end node is merged to a preceding node
3905 if (pNode
== nullptr && pFrame
->GetMergedPara()
3906 && pFrame
->GetMergedPara()->pFirstNode
->GetIndex() < aSavePos
.GetIndex())
3908 pNode
= pFrame
->GetMergedPara()->pFirstNode
;
3913 if (pNode
!= nullptr)
3915 sw::RecreateStartTextFrames(*pNode
);
3916 if (!frames
.empty())
3917 { // tdf#132187 check if the end node needs new frames
3919 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3920 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3922 if (pFrame
->getRootFrame()->HasMergedParas())
3924 auto const it(frames
.find(pFrame
));
3925 if (it
!= frames
.end())
3931 if (!frames
.empty()) // existing frame was deleted
3932 { // all layouts because MakeFrames recreates all layouts
3933 pEndNode
->DelFrames(nullptr);
3934 isRecreateEndNode
= true;
3939 bool const isAtStartOfSection(aSavePos
.GetNode().IsStartNode());
3943 // it's possible that CheckParaRedlineMerge() deleted frames
3944 // on rInsPos so have to include it, but it must not be included
3945 // if it was the first node in the document so that MakeFrames()
3946 // will find the existing (wasn't deleted) frame on it
3947 SwNodeIndex
const end(rInsPos
,
3948 SwNodeOffset((!isRecreateEndNode
|| isAtStartOfSection
)
3950 ::MakeFrames(&rDest
, aSavePos
.GetNode(), end
.GetNode());
3955 // note: for the redline Show/Hide this must be in sync with
3956 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
3957 void DocumentContentOperationsManager::CopyFlyInFlyImpl(
3958 const SwNodeRange
& rRg
,
3959 SwPaM
const*const pCopiedPaM
,
3961 const bool bCopyFlyAtFly
,
3962 SwCopyFlags
const flags
,
3963 bool const bMakeNewFrames
) const
3965 assert(!pCopiedPaM
|| pCopiedPaM
->End()->GetNode() == rRg
.aEnd
.GetNode());
3967 // First collect all Flys, sort them according to their ordering number,
3968 // and then only copy them. This maintains the ordering numbers (which are only
3969 // managed in the DrawModel).
3970 SwDoc
& rDest
= rStartIdx
.GetDoc();
3971 std::set
< ZSortFly
> aSet
;
3972 const size_t nArrLen
= m_rDoc
.GetSpzFrameFormats()->size();
3974 SwTextBoxHelper::SavedLink aOldTextBoxes
;
3975 SwTextBoxHelper::saveLinks(*m_rDoc
.GetSpzFrameFormats(), aOldTextBoxes
);
3977 for ( size_t n
= 0; n
< nArrLen
; ++n
)
3979 SwFrameFormat
* pFormat
= (*m_rDoc
.GetSpzFrameFormats())[n
];
3980 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
3981 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
3985 SwNodeOffset nSkipAfter
= pAnchorNode
->GetIndex();
3986 SwNodeOffset nStart
= rRg
.aStart
.GetIndex();
3987 switch ( pAnchor
->GetAnchorId() )
3989 case RndStdIds::FLY_AT_FLY
:
3992 else if(m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove())
3995 case RndStdIds::FLY_AT_PARA
:
3997 bAdd
= IsSelectFrameAnchoredAtPara(*pAnchor
->GetContentAnchor(),
3998 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
3999 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
4000 (flags
& SwCopyFlags::IsMoveToFly
)
4001 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
4002 : DelContentType::AllMask
);
4005 case RndStdIds::FLY_AT_CHAR
:
4007 bAdd
= IsDestroyFrameAnchoredAtChar(*pAnchor
->GetContentAnchor(),
4008 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
4009 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
4010 (flags
& SwCopyFlags::IsMoveToFly
)
4011 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
4012 : DelContentType::AllMask
);
4018 if (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId())
4020 if (nStart
> nSkipAfter
)
4022 if (*pAnchorNode
> rRg
.aEnd
.GetNode())
4024 //frames at the last source node are not always copied:
4025 //- if the node is empty and is the last node of the document or a table cell
4026 // or a text frame then they have to be copied
4027 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
4028 //- to-character bound objects are copied if their index is <= nEndContentIndex
4029 if (*pAnchorNode
< rRg
.aEnd
.GetNode())
4031 if (!bAdd
&& !m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
4035 // technically old code checked nContent of AT_FLY which is pointless
4036 bAdd
= pCopiedPaM
&& 0 < pCopiedPaM
->End()->GetContentIndex();
4042 aSet
.insert( ZSortFly( pFormat
, pAnchor
, nArrLen
+ aSet
.size() ));
4046 // Store all copied (and also the newly created) frames in another array.
4047 // They are stored as matching the originals, so that we will be later
4048 // able to build the chains accordingly.
4049 std::vector
< SwFrameFormat
* > aVecSwFrameFormat
;
4050 std::set
< ZSortFly
>::const_iterator it
=aSet
.begin();
4052 while (it
!= aSet
.end())
4055 // correct determination of new anchor position
4056 SwFormatAnchor
aAnchor( *(*it
).GetAnchor() );
4057 assert( aAnchor
.GetContentAnchor() != nullptr );
4058 SwPosition newPos
= *aAnchor
.GetContentAnchor();
4059 // for at-paragraph and at-character anchored objects the new anchor
4060 // position can *not* be determined by the difference of the current
4061 // anchor position to the start of the copied range, because not
4062 // complete selected sections in the copied range aren't copied - see
4063 // method <SwNodes::CopyNodes(..)>.
4064 // Thus, the new anchor position in the destination document is found
4065 // by counting the text nodes.
4066 if ((aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PARA
) ||
4067 (aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
) )
4069 // First, determine number of anchor text node in the copied range.
4070 // Note: The anchor text node *have* to be inside the copied range.
4071 sal_uLong
nAnchorTextNdNumInRange( 0 );
4072 bool bAnchorTextNdFound( false );
4073 // start at the first node for which flys are copied
4074 SwNodeIndex
aIdx(pCopiedPaM
? pCopiedPaM
->Start()->GetNode() : rRg
.aStart
.GetNode());
4075 while ( !bAnchorTextNdFound
&& aIdx
<= rRg
.aEnd
)
4077 if ( aIdx
.GetNode().IsTextNode() )
4079 ++nAnchorTextNdNumInRange
;
4080 bAnchorTextNdFound
= *aAnchor
.GetAnchorNode() == aIdx
.GetNode();
4086 if ( !bAnchorTextNdFound
)
4088 // This case can *not* happen, but to be robust take the first
4089 // text node in the destination document.
4090 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
4091 nAnchorTextNdNumInRange
= 1;
4093 // Second, search corresponding text node in destination document
4094 // by counting forward from start insert position <rStartIdx> the
4095 // determined number of text nodes.
4097 SwNodeIndex
aAnchorNdIdx( rStartIdx
);
4098 const SwNode
& aEndOfContentNd
=
4099 aIdx
.GetNode().GetNodes().GetEndOfContent();
4100 while ( nAnchorTextNdNumInRange
> 0 &&
4101 aIdx
.GetNode() != aEndOfContentNd
)
4103 if ( aIdx
.GetNode().IsTextNode() )
4105 --nAnchorTextNdNumInRange
;
4106 aAnchorNdIdx
= aIdx
;
4111 if ( !aAnchorNdIdx
.GetNode().IsTextNode() )
4113 // This case can *not* happen, but to be robust take the first
4114 // text node in the destination document.
4115 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
4116 aAnchorNdIdx
= rStartIdx
;
4117 while ( !aAnchorNdIdx
.GetNode().IsTextNode() )
4122 // apply found anchor text node as new anchor position
4123 newPos
.Assign( aAnchorNdIdx
);
4127 SwNodeOffset nOffset
= newPos
.GetNodeIndex() - rRg
.aStart
.GetIndex();
4128 newPos
.Assign( rStartIdx
, nOffset
);
4130 // Set the character bound Flys back at the original character
4131 if ((RndStdIds::FLY_AT_CHAR
== aAnchor
.GetAnchorId()) &&
4132 newPos
.GetNode().IsTextNode() )
4134 // only if pCopiedPaM: care about partially selected start node
4135 sal_Int32
const nContent
= pCopiedPaM
&& pCopiedPaM
->Start()->GetNode() == *aAnchor
.GetAnchorNode()
4136 ? newPos
.GetContentIndex() - pCopiedPaM
->Start()->GetContentIndex()
4137 : newPos
.GetContentIndex();
4138 newPos
.SetContent(nContent
);
4140 aAnchor
.SetAnchor( &newPos
);
4142 // Check recursion: if copying content inside the same frame, then don't copy the format.
4143 if( &rDest
== &m_rDoc
)
4145 const SwFormatContent
& rContent
= (*it
).GetFormat()->GetContent();
4146 const SwStartNode
* pSNd
;
4147 if( rContent
.GetContentIdx() &&
4148 nullptr != ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ) &&
4149 pSNd
->GetIndex() < rStartIdx
.GetIndex() &&
4150 rStartIdx
.GetIndex() < pSNd
->EndOfSectionIndex() )
4152 it
= aSet
.erase(it
);
4157 // Ignore TextBoxes, they are already handled in
4158 // sw::DocumentLayoutManager::CopyLayoutFormat().
4159 if (SwTextBoxHelper::isTextBox(it
->GetFormat(), RES_FLYFRMFMT
))
4161 it
= aSet
.erase(it
);
4165 // Copy the format and set the new anchor
4166 aVecSwFrameFormat
.push_back( rDest
.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it
).GetFormat(),
4167 aAnchor
, false, bMakeNewFrames
) );
4171 // Rebuild as much as possible of all chains that are available in the original,
4172 OSL_ENSURE( aSet
.size() == aVecSwFrameFormat
.size(), "Missing new Flys" );
4173 if ( aSet
.size() != aVecSwFrameFormat
.size() )
4177 for (const auto& rFlyN
: aSet
)
4179 const SwFrameFormat
*pFormatN
= rFlyN
.GetFormat();
4180 const SwFormatChain
&rChain
= pFormatN
->GetChain();
4182 for (const auto& rFlyK
: aSet
)
4184 const SwFrameFormat
*pFormatK
= rFlyK
.GetFormat();
4185 if ( rChain
.GetPrev() == pFormatK
)
4187 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]),
4188 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]) );
4190 else if ( rChain
.GetNext() == pFormatK
)
4192 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]),
4193 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]) );
4200 // Re-create content property of draw formats, knowing how old shapes
4201 // were paired with old fly formats (aOldTextBoxes) and that aSet is
4202 // parallel with aVecSwFrameFormat.
4203 SwTextBoxHelper::restoreLinks(aSet
, aVecSwFrameFormat
, aOldTextBoxes
);
4207 * Reset the text's hard formatting
4209 /** @params pArgs contains the document's ChrFormatTable
4210 * Is need for selections at the beginning/end and with no SSelection.
4212 bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode
* pNd
, void* pArgs
)
4214 ParaRstFormat
* pPara
= static_cast<ParaRstFormat
*>(pArgs
);
4215 if (pPara
->pLayout
&& pPara
->pLayout
->HasMergedParas()
4216 && pNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
4218 return true; // skip hidden, since new items aren't applied
4220 SwTextNode
* pTextNode
= pNd
->GetTextNode();
4221 if( pTextNode
&& pTextNode
->GetpSwpHints() )
4223 SwContentIndex
aSt( pTextNode
, 0 );
4224 sal_Int32 nEnd
= pTextNode
->Len();
4226 if( &pPara
->pSttNd
->GetNode() == pTextNode
&&
4227 pPara
->pSttNd
->GetContentIndex() )
4228 aSt
= pPara
->pSttNd
->GetContentIndex();
4230 if( &pPara
->pEndNd
->GetNode() == pNd
)
4231 nEnd
= pPara
->pEndNd
->GetContentIndex();
4233 if( pPara
->pHistory
)
4235 // Save all attributes for the Undo.
4236 SwRegHistory
aRHst( *pTextNode
, pPara
->pHistory
);
4237 pTextNode
->GetpSwpHints()->Register( &aRHst
);
4238 pTextNode
->RstTextAttr( aSt
.GetIndex(), nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4239 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4240 if( pTextNode
->GetpSwpHints() )
4241 pTextNode
->GetpSwpHints()->DeRegister();
4244 pTextNode
->RstTextAttr( aSt
.GetIndex(), nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4245 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4250 DocumentContentOperationsManager::~DocumentContentOperationsManager()
4255 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4257 assert(m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn());
4259 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4261 if (*rPam
.GetPoint() == *rPam
.GetMark())
4263 return false; // do not add empty redlines
4266 std::vector
<std::unique_ptr
<SwRangeRedline
>> redlines
;
4268 auto pRedline(std::make_unique
<SwRangeRedline
>(RedlineType::Delete
, rPam
));
4269 if (pRedline
->HasValidRange())
4271 redlines
.push_back(std::move(pRedline
));
4273 else // sigh ... why is such a selection even possible...
4274 { // split it up so we get one SwUndoRedlineDelete per inserted RL
4275 redlines
= GetAllValidRanges(std::move(pRedline
));
4279 if (redlines
.empty())
4284 // tdf#54819 current redlining needs also modification of paragraph style and
4285 // attributes added to the same grouped Undo
4286 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4287 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4289 auto & rDMA(*m_rDoc
.getIDocumentMarkAccess());
4290 std::vector
<std::unique_ptr
<SwUndo
>> MarkUndos
;
4291 for (auto iter
= rDMA
.getAnnotationMarksBegin();
4292 iter
!= rDMA
.getAnnotationMarksEnd(); )
4294 // tdf#111524 remove annotation marks that have their field
4295 // characters deleted
4296 SwPosition
const& rEndPos((**iter
).GetMarkEnd());
4297 if (*rPam
.Start() < rEndPos
&& rEndPos
<= *rPam
.End())
4299 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4301 MarkUndos
.emplace_back(std::make_unique
<SwUndoDeleteBookmark
>(**iter
));
4303 // iter is into annotation mark vector so must be dereferenced!
4304 rDMA
.deleteMark(&**iter
);
4305 // this invalidates iter, have to start over...
4306 iter
= rDMA
.getAnnotationMarksBegin();
4309 { // marks are sorted by start
4310 if (*rPam
.End() < (**iter
).GetMarkStart())
4318 // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
4319 if (*rPam
.GetPoint() != *rPam
.GetMark())
4320 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam
);
4322 std::vector
<std::unique_ptr
<SwUndoRedlineDelete
>> undos
;
4323 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4325 // this should no longer happen in calls from the UI but maybe via API
4326 // (randomTest and testTdf54819 triggers it)
4327 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4328 "sw.core", "redlines will be moved in DeleteAndJoin");
4329 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4330 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4332 for (std::unique_ptr
<SwRangeRedline
> & pRedline
: redlines
)
4334 assert(pRedline
->HasValidRange());
4335 undos
.emplace_back(std::make_unique
<SwUndoRedlineDelete
>(
4336 *pRedline
, SwUndoId::DELETE
, flags
));
4338 const SwRewriter aRewriter
= undos
.front()->GetRewriter();
4339 // can only group a single undo action
4340 if (MarkUndos
.empty() && undos
.size() == 1
4341 && m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4343 SwUndo
* const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4344 SwUndoRedlineDelete
*const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete
*>(pLastUndo
));
4345 bool const bMerged
= pUndoRedlineDel
4346 && pUndoRedlineDel
->CanGrouping(*undos
.front());
4349 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(undos
.front()));
4351 undos
.clear(); // prevent unmatched EndUndo
4355 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE
, &aRewriter
);
4356 for (auto& it
: MarkUndos
)
4358 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4360 for (auto & it
: undos
)
4362 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4367 for (std::unique_ptr
<SwRangeRedline
> & pRedline
: redlines
)
4369 // note: 1. the pRedline can still be merged & deleted
4370 // 2. the impl. can even DeleteAndJoin the range => no plain PaM
4371 std::shared_ptr
<SwUnoCursor
> const pCursor(m_rDoc
.CreateUnoCursor(*pRedline
->GetMark()));
4373 *pCursor
->GetPoint() = *pRedline
->GetPoint();
4374 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(pRedline
.release(), true);
4375 // sw_redlinehide: 2 reasons why this is needed:
4376 // 1. it's the first redline in node => RedlineDelText was sent but ignored
4377 // 2. redline spans multiple nodes => must merge text frames
4378 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, *pCursor
);
4380 m_rDoc
.getIDocumentState().SetModified();
4382 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4386 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4388 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4391 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4392 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4397 bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4399 bool bJoinText
, bJoinPrev
;
4400 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4402 bool const bSuccess( DeleteRangeImpl(rPam
, flags
) );
4408 ::sw_JoinText( rPam
, bJoinPrev
);
4411 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
4412 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty()
4413 && !(flags
& SwDeleteFlags::DontCompressRedlines
))
4415 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
4421 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4423 // Move all cursors out of the deleted range, but first copy the
4424 // passed PaM, because it could be a cursor that would be moved!
4425 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4427 SwPosition
const pos(GetCorrPosition(aDelPam
));
4428 ::PaMCorrAbs(aDelPam
, pos
);
4431 bool const bSuccess( DeleteRangeImplImpl(aDelPam
, flags
) );
4433 { // now copy position from temp copy to given PaM
4434 *rPam
.GetPoint() = *aDelPam
.GetPoint();
4440 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM
& rPam
, SwDeleteFlags
const flags
)
4442 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
4445 || (*pStt
== *pEnd
&& !IsFlySelectedByCursor(m_rDoc
, *pStt
, *pEnd
)))
4450 if( m_rDoc
.GetAutoCorrExceptWord() )
4452 // if necessary the saved Word for the exception
4453 if( m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() || pStt
->GetNode() != pEnd
->GetNode() ||
4454 pStt
->GetContentIndex() + 1 != pEnd
->GetContentIndex() ||
4455 !m_rDoc
.GetAutoCorrExceptWord()->CheckDelChar( *pStt
))
4456 { m_rDoc
.DeleteAutoCorrExceptWord(); }
4460 // Delete all empty TextHints at the Mark's position
4461 SwTextNode
* pTextNd
= rPam
.GetMark()->GetNode().GetTextNode();
4463 if( pTextNd
&& nullptr != ( pHts
= pTextNd
->GetpSwpHints()) && pHts
->Count() )
4465 const sal_Int32 nMkCntPos
= rPam
.GetMark()->GetContentIndex();
4466 for( size_t n
= pHts
->Count(); n
; )
4468 const SwTextAttr
* pAttr
= pHts
->Get( --n
);
4469 if( nMkCntPos
> pAttr
->GetStart() )
4472 const sal_Int32
*pEndIdx
;
4473 if( nMkCntPos
== pAttr
->GetStart() &&
4474 nullptr != (pEndIdx
= pAttr
->End()) &&
4475 *pEndIdx
== pAttr
->GetStart() )
4476 pTextNd
->DestroyAttr( pHts
->Cut( n
) );
4482 // Send DataChanged before deletion, so that we still know
4483 // which objects are in the range.
4484 // Afterwards they could be before/after the Position.
4485 SwDataChanged
aTmp( rPam
);
4488 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4490 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
4491 bool bMerged(false);
4492 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4494 SwUndo
*const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4495 SwUndoDelete
*const pUndoDelete(
4496 dynamic_cast<SwUndoDelete
*>(pLastUndo
) );
4499 bMerged
= pUndoDelete
->CanGrouping(m_rDoc
, rPam
);
4500 // if CanGrouping() returns true it's already merged
4505 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoDelete
>(rPam
, flags
));
4508 m_rDoc
.getIDocumentState().SetModified();
4513 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4514 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( rPam
, true, RedlineType::Any
);
4516 // Delete and move all "Flys at the paragraph", which are within the Selection
4517 if (!(flags
& SwDeleteFlags::ArtificialSelection
))
4519 DelFlyInRange(rPam
.GetMark()->GetNode(), rPam
.GetPoint()->GetNode(),
4520 rPam
.GetMark()->GetContentIndex(), rPam
.GetPoint()->GetContentIndex());
4526 pStt
->GetContentIndex(),
4527 pEnd
->GetContentIndex(),
4528 bool(flags
& SwDeleteFlags::ArtificialSelection
));
4530 SwNodeIndex
aSttIdx( pStt
->GetNode() );
4531 SwContentNode
* pCNd
= aSttIdx
.GetNode().GetContentNode();
4533 do { // middle checked loop!
4536 SwTextNode
* pStartTextNode( pCNd
->GetTextNode() );
4537 if ( pStartTextNode
)
4539 // now move the Content to the new Node
4540 bool bOneNd
= pStt
->GetNode() == pEnd
->GetNode();
4541 const sal_Int32 nLen
= ( bOneNd
? pEnd
->GetContentIndex()
4543 - pStt
->GetContentIndex();
4545 // Don't call again, if already empty
4548 pStartTextNode
->EraseText( *pStt
, nLen
);
4550 if( !pStartTextNode
->Len() )
4552 // METADATA: remove reference if empty (consider node deleted)
4553 pStartTextNode
->RemoveMetadataReference();
4557 if( bOneNd
) // that's it
4564 // So that there are no indices left registered when deleted,
4565 // we remove a SwPaM from the Content here.
4566 pStt
->nContent
.Assign( nullptr, 0 );
4570 pCNd
= pEnd
->GetNode().GetContentNode();
4573 SwTextNode
* pEndTextNode( pCNd
->GetTextNode() );
4576 // if already empty, don't call again
4577 if( pEnd
->GetContentIndex() )
4579 SwContentIndex
aIdx( pCNd
, 0 );
4580 pEndTextNode
->EraseText( aIdx
, pEnd
->GetContentIndex() );
4582 if( !pEndTextNode
->Len() )
4584 // METADATA: remove reference if empty (consider node deleted)
4585 pEndTextNode
->RemoveMetadataReference();
4591 // So that there are no indices left registered when deleted,
4592 // we remove a SwPaM from the Content here.
4593 pEnd
->nContent
.Assign( nullptr, 0 );
4597 // if the end is not a content node, delete it as well
4598 SwNodeOffset nEnd
= pEnd
->GetNodeIndex();
4599 if( pCNd
== nullptr )
4602 if( aSttIdx
!= nEnd
)
4604 // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
4606 while (pEnd
== rPam
.GetPoint()
4607 && nEnd
+ SwNodeOffset(2) < m_rDoc
.GetNodes().Count()
4608 && (pTmpNd
= m_rDoc
.GetNodes()[nEnd
+ 1])->IsEndNode()
4609 && pTmpNd
->StartOfSectionNode()->IsSectionNode()
4610 && aSttIdx
<= pTmpNd
->StartOfSectionNode()->GetIndex())
4612 SwNodeRange
range(*pTmpNd
->StartOfSectionNode(), *pTmpNd
);
4613 m_rDoc
.GetNodes().SectionUp(&range
);
4614 --nEnd
; // account for deleted start node
4617 // delete the Nodes from the NodesArray
4618 m_rDoc
.GetNodes().Delete( aSttIdx
, nEnd
- aSttIdx
.GetIndex() );
4621 // If the Node that contained the Cursor has been deleted,
4622 // the Content has to be assigned to the current Content.
4623 if (pStt
->GetNode().GetContentNode())
4624 pStt
->SetContent( pStt
->GetContentIndex() );
4626 // If we deleted across Node boundaries we have to correct the PaM,
4627 // because they are in different Nodes now.
4628 // Also, the Selection is revoked.
4634 m_rDoc
.getIDocumentState().SetModified();
4639 // It's possible to call Replace with a PaM that spans 2 paragraphs:
4640 // search with regex for "$", then replace _all_
4641 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM
& rPam
, const OUString
& rStr
,
4642 const bool bRegExReplace
)
4644 if (!rPam
.HasMark())
4647 bool bJoinText
, bJoinPrev
;
4648 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4651 // Create a copy of the Cursor in order to move all Pams from
4652 // the other views out of the deletion range.
4653 // Except for itself!
4654 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4655 ::PaMCorrAbs( aDelPam
, *aDelPam
.End() );
4657 auto [pStt
, pEnd
] = aDelPam
.StartEnd(); // SwPosition*
4658 bool bOneNode
= pStt
->GetNode() == pEnd
->GetNode();
4661 OUString
sRepl( rStr
);
4662 SwTextNode
* pTextNd
= pStt
->GetNode().GetTextNode();
4663 sal_Int32 nStt
= pStt
->GetContentIndex();
4666 SwDataChanged
aTmp( aDelPam
);
4668 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
4670 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4671 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4673 // this should no longer happen in calls from the UI but maybe via API
4674 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4675 "sw.core", "redlines will be moved in ReplaceRange");
4677 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4679 // If any Redline will change (split!) the node
4680 const ::sw::mark::IMark
* pBkmk
=
4681 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4682 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4683 ::sw::mark::InsertMode::New
);
4685 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4686 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4688 *aDelPam
.GetPoint() = pBkmk
->GetMarkPos();
4689 if(pBkmk
->IsExpanded())
4690 *aDelPam
.GetMark() = pBkmk
->GetOtherMarkPos();
4691 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4692 pStt
= aDelPam
.Start();
4693 pTextNd
= pStt
->GetNode().GetTextNode();
4694 nStt
= pStt
->GetContentIndex();
4697 if( !sRepl
.isEmpty() )
4699 // Apply the first character's attributes to the ReplaceText
4701 <RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
4702 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( m_rDoc
.GetAttrPool() );
4703 pTextNd
->GetParaAttr( aSet
, nStt
+1, nStt
+1 );
4705 aSet
.ClearItem( RES_TXTATR_REFMARK
);
4706 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
4707 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
4708 aSet
.ClearItem( RES_TXTATR_INETFMT
);
4709 aSet
.ClearItem( RES_TXTATR_META
);
4710 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
4712 if( aDelPam
.GetPoint() != aDelPam
.End() )
4716 SwNodeIndex
aPtNd( aDelPam
.GetPoint()->GetNode(), -1 );
4717 const sal_Int32 nPtCnt
= aDelPam
.GetPoint()->GetContentIndex();
4721 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4723 InsertString( aDelPam
, sIns
);
4726 SwNodeIndex
aMkNd( aDelPam
.GetMark()->GetNode(), -1 );
4727 const sal_Int32 nMkCnt
= aDelPam
.GetMark()->GetContentIndex();
4729 SplitNode( *aDelPam
.GetPoint(), false );
4732 aDelPam
.GetMark()->Assign( aMkNd
, nMkCnt
);
4736 SplitNode( *aDelPam
.GetPoint(), false );
4738 if( !sIns
.isEmpty() )
4740 InsertString( aDelPam
, sIns
);
4743 SwPaM
aTmpRange( *aDelPam
.GetPoint() );
4744 aTmpRange
.SetMark();
4747 aDelPam
.GetPoint()->Assign(aPtNd
, nPtCnt
);
4748 *aTmpRange
.GetMark() = *aDelPam
.GetPoint();
4750 m_rDoc
.RstTextAttrs( aTmpRange
);
4751 InsertItemSet( aTmpRange
, aSet
);
4754 // tdf#139982: Appending the redline may immediately delete flys
4755 // anchored in the previous text if it's inside an insert redline.
4756 // Also flys will be deleted if the redline is accepted. Move them
4757 // to the position between the previous text and the new text,
4758 // there the chance of surviving both accept and reject is best.
4760 SaveFlyInRange(aDelPam
, *aDelPam
.End(), flys
, false);
4762 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4764 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
4765 std::make_unique
<SwUndoRedlineDelete
>( aDelPam
, SwUndoId::REPLACE
));
4767 // add redline similar to DeleteAndJoinWithRedlineImpl()
4768 std::shared_ptr
<SwUnoCursor
> const pCursor(m_rDoc
.CreateUnoCursor(*aDelPam
.GetMark()));
4770 *pCursor
->GetPoint() = *aDelPam
.GetPoint();
4771 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete
, aDelPam
), true);
4772 RestFlyInRange(flys
, *aDelPam
.End(), &aDelPam
.End()->GetNode(), true);
4773 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, *pCursor
);
4775 *rPam
.GetMark() = *aDelPam
.GetMark();
4776 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4778 *aDelPam
.GetPoint() = *rPam
.GetPoint();
4779 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4781 // If any Redline will change (split!) the node
4782 const ::sw::mark::IMark
* pBkmk
=
4783 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4784 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4785 ::sw::mark::InsertMode::New
);
4787 aDelPam
.GetPoint()->Assign( SwNodeOffset(0) );
4788 aDelPam
.GetMark()->Assign( SwNodeOffset(0) );
4789 rPam
.GetPoint()->Assign( SwNodeOffset(0) );
4790 *rPam
.GetMark() = *rPam
.GetPoint();
4791 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4793 *rPam
.GetPoint() = pBkmk
->GetMarkPos();
4794 *rPam
.GetMark() = pBkmk
->IsExpanded() ? pBkmk
->GetOtherMarkPos() : pBkmk
->GetMarkPos();
4796 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4802 assert((pStt
->GetNode() == pEnd
->GetNode() ||
4803 ( pStt
->GetNodeIndex() + 1 == pEnd
->GetNodeIndex() &&
4804 !pEnd
->GetContentIndex() )) &&
4805 "invalid range: Point and Mark on different nodes" );
4807 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4808 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aDelPam
, true, RedlineType::Any
);
4810 SwUndoReplace
* pUndoRpl
= nullptr;
4811 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
4814 pUndoRpl
= new SwUndoReplace(aDelPam
, sRepl
, bRegExReplace
);
4815 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndoRpl
));
4817 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
4819 if( aDelPam
.GetPoint() != pStt
)
4822 SwNodeIndex
aPtNd( pStt
->GetNode(), -1 );
4823 const sal_Int32 nPtCnt
= pStt
->GetContentIndex();
4825 // Set the values again, if Frames or footnotes on the Text have been removed.
4827 nEnd
= bOneNode
? pEnd
->GetContentIndex()
4828 : pTextNd
->GetText().getLength();
4832 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4834 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4836 InsertString( aDelPam
, sIns
);
4838 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4840 pTextNd
->ReplaceText( *pStt
, nEnd
- nStt
, sIns
);
4842 SplitNode( *pStt
, false);
4846 if( bFirst
|| !sIns
.isEmpty() )
4848 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4850 InsertString( aDelPam
, sIns
);
4852 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4854 pTextNd
->ReplaceText( *pStt
, nEnd
- nStt
, sIns
);
4858 *rPam
.GetPoint() = *aDelPam
.GetMark();
4860 rPam
.GetMark()->Assign( aPtNd
, nPtCnt
);
4864 assert(rPam
.GetPoint() == rPam
.End());
4865 // move so that SetEnd remembers position after sw_JoinText
4866 rPam
.Move(fnMoveBackward
);
4868 else if (aDelPam
.GetPoint() == pStt
) // backward selection?
4870 assert(*rPam
.GetMark() <= *rPam
.GetPoint());
4871 rPam
.Exchange(); // swap so that rPam is backwards
4876 pUndoRpl
->SetEnd(rPam
);
4884 bRet
= ::sw_JoinText(rPam
, bJoinPrev
);
4887 m_rDoc
.getIDocumentState().SetModified();
4891 SwFlyFrameFormat
* DocumentContentOperationsManager::InsNoTextNode( const SwPosition
& rPos
, SwNoTextNode
* pNode
,
4892 const SfxItemSet
* pFlyAttrSet
,
4893 const SfxItemSet
* pGrfAttrSet
,
4894 SwFrameFormat
* pFrameFormat
)
4896 SwFlyFrameFormat
*pFormat
= nullptr;
4899 pFormat
= m_rDoc
.MakeFlySection_( rPos
, *pNode
, RndStdIds::FLY_AT_PARA
,
4900 pFlyAttrSet
, pFrameFormat
);
4902 pNode
->SetAttr( *pGrfAttrSet
);
4907 #define NUMRULE_STATE \
4908 std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
4909 std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
4911 #define PUSH_NUMRULE_STATE \
4912 lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
4914 #define POP_NUMRULE_STATE \
4915 lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
4917 static void lcl_PushNumruleState(
4918 std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4919 std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4920 const SwTextNode
*pDestTextNd
)
4922 // Safe numrule item at destination.
4923 // #i86492# - Safe also <ListId> item of destination.
4924 const SfxItemSet
* pAttrSet
= pDestTextNd
->GetpSwAttrSet();
4925 if (pAttrSet
== nullptr)
4928 if (const SwNumRuleItem
* pItem
= pAttrSet
->GetItemIfSet(RES_PARATR_NUMRULE
, false))
4930 aNumRuleItemHolderIfSet
.reset(pItem
->Clone());
4933 if (const SfxStringItem
* pItem
= pAttrSet
->GetItemIfSet(RES_PARATR_LIST_ID
, false))
4935 aListIdItemHolderIfSet
.reset(pItem
->Clone());
4939 static void lcl_PopNumruleState(
4940 const std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4941 const std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4942 SwTextNode
*pDestTextNd
, const SwPaM
& rPam
)
4944 /* If only a part of one paragraph is copied
4945 restore the numrule at the destination. */
4946 // #i86492# - restore also <ListId> item
4947 if ( lcl_MarksWholeNode(rPam
) )
4950 if (aNumRuleItemHolderIfSet
)
4952 pDestTextNd
->SetAttr(*aNumRuleItemHolderIfSet
);
4956 pDestTextNd
->ResetAttr(RES_PARATR_NUMRULE
);
4959 if (aListIdItemHolderIfSet
)
4961 pDestTextNd
->SetAttr(*aListIdItemHolderIfSet
);
4965 pDestTextNd
->ResetAttr(RES_PARATR_LIST_ID
);
4969 bool DocumentContentOperationsManager::CopyImpl(SwPaM
& rPam
, SwPosition
& rPos
,
4970 SwCopyFlags
const flags
,
4971 SwPaM
*const pCopyRange
) const
4973 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
4975 sw::CalcBreaks(Breaks
, rPam
, true);
4979 return CopyImplImpl(rPam
, rPos
, flags
, pCopyRange
);
4982 SwPosition
const & rSelectionEnd( *rPam
.End() );
4986 // iterate from end to start, ... don't think it's necessary here?
4987 auto iter( Breaks
.rbegin() );
4988 SwNodeOffset
nOffset(0);
4989 SwNodes
const& rNodes(rPam
.GetPoint()->GetNodes());
4990 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
4991 SwPosition
& rEnd( *aPam
.End() );
4992 SwPosition
& rStart( *aPam
.Start() );
4993 SwPaM
copyRange(rPos
, rPos
);
4995 while (iter
!= Breaks
.rend())
4997 rStart
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
4998 if (rStart
< rEnd
) // check if part is empty
5000 // pass in copyRange member as rPos; should work ...
5001 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
5002 nOffset
= iter
->first
- rStart
.GetNodeIndex(); // fly nodes...
5007 pCopyRange
->SetMark();
5008 *pCopyRange
->GetMark() = *copyRange
.End();
5010 *pCopyRange
->GetPoint() = *copyRange
.Start();
5014 rEnd
.Assign(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
5018 rStart
= *rPam
.Start(); // set to original start
5019 if (rStart
< rEnd
) // check if part is empty
5021 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
5026 pCopyRange
->SetMark();
5027 *pCopyRange
->GetMark() = *copyRange
.End();
5029 *pCopyRange
->GetPoint() = *copyRange
.Start();
5036 bool DocumentContentOperationsManager::CopyImplImpl(SwPaM
& rPam
, SwPosition
& rPos
,
5037 SwCopyFlags
const flags
,
5038 SwPaM
*const pCpyRange
) const
5040 SwDoc
& rDoc
= rPos
.GetNode().GetDoc();
5041 const bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
5043 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
5045 // Catch when there's no copy to do.
5046 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
) ||
5047 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
5048 //JP 15.11.2001: don't test inclusive the end, ever exclusive
5049 ( &rDoc
== &m_rDoc
&& *pStt
<= rPos
&& rPos
< *pEnd
))
5054 const bool bEndEqualIns
= &rDoc
== &m_rDoc
&& rPos
== *pEnd
;
5056 // If Undo is enabled, create the UndoCopy object
5057 SwUndoCpyDoc
* pUndo
= nullptr;
5058 // lcl_DeleteRedlines may delete the start or end node of the cursor when
5059 // removing the redlines so use cursor that is corrected by PaMCorrAbs
5060 std::shared_ptr
<SwUnoCursor
> const pCopyPam(rDoc
.CreateUnoCursor(rPos
));
5062 SwTableNumFormatMerge
aTNFM( m_rDoc
, rDoc
);
5063 std::optional
<std::vector
<SwFrameFormat
*>> pFlys
;
5064 std::vector
<SwFrameFormat
*> const* pFlysAtInsPos
;
5066 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
5068 pUndo
= new SwUndoCpyDoc(*pCopyPam
);
5069 pFlysAtInsPos
= pUndo
->GetFlysAnchoredAt();
5073 pFlys
= sw::GetFlysAnchoredAt(rDoc
, rPos
.GetNodeIndex(), false);
5074 pFlysAtInsPos
= pFlys
? &*pFlys
: nullptr;
5077 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
5078 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
5080 // Move the PaM one node back from the insert position, so that
5081 // the position doesn't get moved
5082 pCopyPam
->SetMark();
5083 bool bCanMoveBack
= false;
5084 // First check if it will be able to move *to* first copied node.
5085 // Note this doesn't just check IsStartNode() because SwDoc::AppendDoc()
5086 // intentionally sets it to the body start node, perhaps it should just
5087 // call SplitNode instead?
5088 if (!pStt
->GetNode().IsSectionNode() && !pStt
->GetNode().IsTableNode())
5090 bCanMoveBack
= pCopyPam
->Move(fnMoveBackward
, GoInContent
);
5094 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(-1));
5095 assert(pCopyPam
->GetPoint()->GetContentIndex() == 0);
5098 SwNodeRange
aRg( pStt
->GetNode(), pEnd
->GetNode() );
5099 SwNodeIndex
aInsPos( rPos
.GetNode() );
5100 ::std::optional
<SwContentIndex
> oInsContentIndex
;
5101 const bool bOneNode
= pStt
->GetNode() == pEnd
->GetNode();
5102 SwTextNode
* pSttTextNd
= pStt
->GetNode().GetTextNode();
5103 SwTextNode
* pEndTextNd
= pEnd
->GetNode().GetTextNode();
5104 SwTextNode
* pDestTextNd
= aInsPos
.GetNode().GetTextNode();
5105 bool bCopyCollFormat
= !rDoc
.IsInsOnlyTextGlossary() &&
5106 ( (pDestTextNd
&& !pDestTextNd
->GetText().getLength()) ||
5107 ( !bOneNode
&& !rPos
.GetContentIndex() ) );
5108 bool bCopyBookmarks
= true;
5109 bool bCopyPageSource
= false;
5110 SwNodeOffset
nDeleteTextNodes(0);
5112 // #i104585# copy outline num rule to clipboard (for ASCII filter)
5113 if (rDoc
.IsClipBoard() && m_rDoc
.GetOutlineNumRule())
5115 rDoc
.SetOutlineNumRule(*m_rDoc
.GetOutlineNumRule());
5119 // Correct the search for a previous list:
5120 // First search for non-outline numbering list. Then search for non-outline
5122 // Keep also the <ListId> value for possible propagation.
5123 OUString aListIdToPropagate
;
5124 SvxTextLeftMarginItem
const* pTextLeftMarginToPropagate
{nullptr};
5125 SvxFirstLineIndentItem
const* pFirstLineIndentToPropagate
{nullptr};
5126 const SwNumRule
* pNumRuleToPropagate
=
5127 rDoc
.SearchNumRule(rPos
, false, true, false, 0, aListIdToPropagate
, nullptr,
5128 true, &pTextLeftMarginToPropagate
, &pFirstLineIndentToPropagate
);
5129 if ( !pNumRuleToPropagate
)
5131 pNumRuleToPropagate
=
5132 rDoc
.SearchNumRule(rPos
, false, false, false, 0, aListIdToPropagate
, nullptr,
5133 true, &pTextLeftMarginToPropagate
, &pFirstLineIndentToPropagate
);
5136 // Do not propagate previous found list, if
5137 // - destination is an empty paragraph which is not in a list and
5138 // - source contains at least one paragraph which is not in a list
5140 // - source is a table
5141 if ( pNumRuleToPropagate
&&
5142 ((pDestTextNd
&& !pDestTextNd
->GetText().getLength() &&
5143 !pDestTextNd
->IsInList() &&
5144 !lcl_ContainsOnlyParagraphsInList(rPam
)) ||
5145 rPam
.GetBound().nNode
.GetNode().GetNodeType() == SwNodeType::Table
) )
5147 pNumRuleToPropagate
= nullptr;
5150 // This do/while block is only there so that we can break out of it!
5154 ++nDeleteTextNodes
; // must be joined in Undo
5155 // Don't copy the beginning completely?
5156 if( !bCopyCollFormat
|| bColumnSel
|| pStt
->GetContentIndex() )
5158 SwContentIndex
aDestIdx( rPos
.GetContentNode(), rPos
.GetContentIndex() );
5159 bool bCopyOk
= false;
5162 if( pStt
->GetContentIndex() || bOneNode
)
5163 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
.GetNode(),
5164 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
5167 pDestTextNd
= pSttTextNd
->MakeCopy(rDoc
, aInsPos
.GetNode(), true)->GetTextNode();
5170 aDestIdx
.Assign( pDestTextNd
, 0 );
5171 bCopyCollFormat
= true;
5173 else if( !bOneNode
|| bColumnSel
)
5175 const sal_Int32 nContentEnd
= pEnd
->GetContentIndex();
5177 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5178 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
5181 assert(rPos
!= *pCopyPam
->GetPoint()); // code removed
5183 pDestTextNd
= rDoc
.GetNodes()[ aInsPos
.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
5185 pDestTextNd
, pDestTextNd
->GetText().getLength());
5187 // Correct the area again
5190 bool bChg
= pEnd
!= rPam
.GetPoint();
5193 rPam
.Move( fnMoveBackward
, GoInContent
);
5197 else if( rPos
== *pEnd
)
5199 // The end was also moved
5200 pEnd
->Adjust(SwNodeOffset(-1));
5201 pEnd
->SetContent( nContentEnd
);
5203 // tdf#63022 always reset pEndTextNd after SplitNode
5204 aRg
.aEnd
= pEnd
->GetNode();
5205 pEndTextNd
= pEnd
->GetNode().GetTextNode();
5209 if( bCopyCollFormat
&& bOneNode
)
5216 const sal_Int32 nCpyLen
= ( bOneNode
5217 ? pEnd
->GetContentIndex()
5218 : pSttTextNd
->GetText().getLength())
5219 - pStt
->GetContentIndex();
5220 pSttTextNd
->CopyText( pDestTextNd
, aDestIdx
, *pStt
, nCpyLen
);
5222 pEnd
->AdjustContent( -nCpyLen
);
5229 if (bCopyCollFormat
)
5231 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5232 pSttTextNd
->CopyCollFormat(*pDestTextNd
, false);
5236 // Copy at-char flys in rPam.
5237 // Update to new (start) node for flys.
5238 // tdf#126626 prevent duplicate Undos.
5239 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5240 CopyFlyInFlyImpl(aRg
, &rPam
, *pDestTextNd
, false);
5246 else if( pDestTextNd
)
5248 // Problems with insertion of table selections into "normal" text solved.
5249 // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
5250 // the undo operation will try to merge this node after removing the table.
5251 // If we didn't split a textnode, the PaM should start at the inserted table node
5252 if (pDestTextNd
->Len() && rPos
.GetContentIndex() == pDestTextNd
->Len())
5253 { // Insertion at the last position of a textnode
5254 ++aInsPos
; // The table will be inserted behind the text node
5256 else if( rPos
.GetContentIndex() )
5257 { // Insertion in the middle of a text node, it has to be split
5258 // (and joined from undo)
5261 const sal_Int32 nContentEnd
= pEnd
->GetContentIndex();
5263 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5264 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
5267 assert(rPos
!= *pCopyPam
->GetPoint()); // code removed
5269 // Correct the area again
5272 // The end would also be moved
5273 else if( rPos
== *pEnd
)
5275 rPos
.Adjust(SwNodeOffset(-1));
5276 rPos
.SetContent( nContentEnd
);
5280 assert(!bCanMoveBack
);
5283 pDestTextNd
= aInsPos
.GetNode().GetTextNode();
5286 oInsContentIndex
.emplace(aInsPos
.GetNode().GetContentNode(), rPos
.GetContentIndex());
5289 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
.GetNode(),
5290 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
5291 oInsContentIndex
->Assign(pDestTextNd
, 0);
5294 // if we have to insert an extra text node
5295 // at the destination, this node will be our new destination
5296 // (text) node, and thus we increment nDeleteTextNodes. This
5297 // will ensure that this node will be deleted during Undo.
5298 ++nDeleteTextNodes
; // must be deleted
5301 const bool bEmptyDestNd
= pDestTextNd
->GetText().isEmpty();
5304 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5309 pEndTextNd
->CopyText(pDestTextNd
, *oInsContentIndex
,
5310 SwContentIndex(pEndTextNd
), pEnd
->GetContentIndex());
5312 // Also copy all format templates
5313 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5315 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5316 pEndTextNd
->CopyCollFormat(*pDestTextNd
, false);
5324 SfxItemSet
aBrkSet( rDoc
.GetAttrPool(), aBreakSetRange
);
5325 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5327 if (pSttTextNd
&& bCopyCollFormat
&& pDestTextNd
->HasSwAttrSet())
5329 aBrkSet
.Put( *pDestTextNd
->GetpSwAttrSet() );
5330 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_BREAK
, false ) )
5331 pDestTextNd
->ResetAttr( RES_BREAK
);
5332 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_PAGEDESC
, false ) )
5333 pDestTextNd
->ResetAttr( RES_PAGEDESC
);
5338 SwPosition
startPos(pCopyPam
->GetPoint()->GetNode(), SwNodeOffset(+1));
5340 { // pCopyPam is actually 1 before the copy range so move it fwd
5341 SwPaM
temp(*pCopyPam
->GetPoint());
5342 temp
.Move(fnMoveForward
, GoInContent
);
5343 startPos
= *temp
.GetPoint();
5345 assert(startPos
.GetNode().IsContentNode());
5346 std::pair
<SwPaM
const&, SwPosition
const&> tmp(rPam
, startPos
);
5347 if( aInsPos
== pEnd
->GetNode() )
5349 SwNodeIndex
aSaveIdx( aInsPos
, -1 );
5350 assert(pStt
->GetNode() != pEnd
->GetNode());
5351 pEnd
->SetContent(0); // TODO why this?
5352 CopyWithFlyInFly(aRg
, aInsPos
.GetNode(), &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5354 pEnd
->Assign(aSaveIdx
);
5357 CopyWithFlyInFly(aRg
, aInsPos
.GetNode(), &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5359 bCopyBookmarks
= false;
5363 // init *again* - because CopyWithFlyInFly moved startPos
5364 SwPosition
startPos(pCopyPam
->GetPoint()->GetNode(), SwNodeOffset(+1));
5365 // at-char anchors post SplitNode are on index 0 of 2nd node and will
5366 // remain there - move them back to the start (end would also work?)
5367 // ... also for at-para anchors; here start is preferable because
5368 // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
5371 || startPos
.GetNode().IsTextNode()
5372 || (pCopyPam
->GetPoint()->GetNode().IsStartNode()
5373 && startPos
.GetNode().IsSectionNode()))) // not into table
5376 { // pCopyPam is actually 1 before the copy range so move it fwd
5377 SwPaM
temp(*pCopyPam
->GetPoint());
5378 temp
.Move(fnMoveForward
, GoInContent
);
5379 startPos
= *temp
.GetPoint();
5381 else if (startPos
.GetNode().IsSectionNode())
5382 { // probably on top-level start node, so no CheckNodesRange here;
5383 GoNextPos(&startPos
, false); // SwFEShell::Paste() deletes node
5385 assert(startPos
.GetNode().IsContentNode());
5386 SwPosition
startPosAtPara(startPos
);
5387 startPosAtPara
.nContent
.Assign(nullptr, 0);
5389 for (SwFrameFormat
* pFly
: *pFlysAtInsPos
)
5391 SwFormatAnchor
const*const pAnchor
= &pFly
->GetAnchor();
5392 if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
5394 SwFormatAnchor
anchor(*pAnchor
);
5395 anchor
.SetAnchor( &startPos
);
5396 pFly
->SetFormatAttr(anchor
);
5398 else if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_PARA
)
5400 SwFormatAnchor
anchor(*pAnchor
);
5401 anchor
.SetAnchor( &startPosAtPara
);
5403 bool bSplitFly
= false;
5404 if (pFly
->GetFlySplit().GetValue())
5406 SwIterator
<SwFrame
, SwModify
> aIter(*pFly
);
5407 bSplitFly
= aIter
.First() && aIter
.Next();
5411 // This fly format has multiple frames, and we change the anchor. Remove the
5412 // old frames, which were based on the old anchor position.
5416 pFly
->SetFormatAttr(anchor
);
5420 // Re-create the frames now that the new anchor is set.
5427 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5429 // Put the breaks back into the first node
5430 if( aBrkSet
.Count() && nullptr != ( pDestTextNd
= rDoc
.GetNodes()[
5431 pCopyPam
->GetPoint()->GetNodeIndex()+1 ]->GetTextNode()))
5433 pDestTextNd
->SetAttr( aBrkSet
);
5434 bCopyPageSource
= true;
5440 // it is not possible to make this test when copy from the clipBoard to document
5441 // in this case the PageNum not exist anymore
5442 // tdf#39400 and tdf#97526
5443 // when copy from document to ClipBoard, and it is from the first page
5444 // and not the source has the page break
5445 if (rDoc
.IsClipBoard() && (rPam
.GetPageNum(pStt
== rPam
.GetPoint()) == 1) && !bCopyPageSource
)
5449 pDestTextNd
->ResetAttr(RES_BREAK
); // remove the page-break
5450 pDestTextNd
->ResetAttr(RES_PAGEDESC
);
5455 // Adjust position (in case it was moved / in another node)
5456 rPos
.nContent
.Assign( rPos
.GetNode().GetContentNode(),
5457 rPos
.GetContentIndex() );
5459 if( rPos
.GetNode() != aInsPos
.GetNode() )
5461 if (aInsPos
< rPos
.GetNode())
5462 { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
5463 pCopyPam
->GetMark()->AssignEndIndex(*aInsPos
.GetNode().GetContentNode());
5465 else // incremented in (!pSttTextNd && pDestTextNd) above
5467 // assign also content index in this case, see testSectionAnchorCopyTableAtStart
5468 assert(oInsContentIndex
);
5469 assert(oInsContentIndex
->GetContentNode() == &aInsPos
.GetNode());
5470 pCopyPam
->GetMark()->Assign(aInsPos
, oInsContentIndex
->GetIndex());
5472 rPos
= *pCopyPam
->GetMark();
5475 *pCopyPam
->GetMark() = rPos
;
5479 pCopyPam
->Move(fnMoveForward
, GoInContent
);
5483 // Reset the offset to 0 as it was before the insertion
5484 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(+1));
5486 // If the next node is a start node, then step back: SetInsertRange()
5487 // will add 1 in this case, but that is too much...
5488 if (pCopyPam
->GetPoint()->GetNode().IsStartNode())
5489 pCopyPam
->GetPoint()->Adjust(SwNodeOffset(-1));
5491 oInsContentIndex
.reset();
5492 pCopyPam
->Exchange();
5494 // Also copy all bookmarks
5495 if( bCopyBookmarks
&& m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount() )
5497 sw::CopyBookmarks(rPam
, *pCopyPam
->Start());
5500 if( RedlineFlags::DeleteRedlines
& eOld
)
5502 assert(*pCopyPam
->GetPoint() == rPos
);
5503 // the Node rPos points to may be deleted so unregister ...
5504 rPos
.nContent
.Assign(nullptr, 0);
5505 lcl_DeleteRedlines(rPam
, *pCopyPam
);
5506 rPos
= *pCopyPam
->GetPoint(); // ... and restore.
5509 // If Undo is enabled, store the inserted area
5510 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
5512 // append it after styles have been copied when copying nodes
5513 rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
5514 pUndo
->SetInsertRange(*pCopyPam
, true, nDeleteTextNodes
);
5519 pCpyRange
->SetMark();
5520 *pCpyRange
->GetPoint() = *pCopyPam
->GetPoint();
5521 *pCpyRange
->GetMark() = *pCopyPam
->GetMark();
5524 if ( pNumRuleToPropagate
!= nullptr )
5526 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
5527 // Don't reset indent attributes, that would mean loss of direct
5529 // It could be that pNumRuleToPropagate is already applied via
5530 // the paragraph style, in that case applying it again in mpAttrSet could
5531 // override indents, so avoid that.
5532 rDoc
.SetNumRule(*pCopyPam
, *pNumRuleToPropagate
,
5533 SwDoc::SetNumRuleMode::DontSetIfAlreadyApplied
, nullptr, aListIdToPropagate
,
5534 pTextLeftMarginToPropagate
, pFirstLineIndentToPropagate
);
5537 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
5538 rDoc
.getIDocumentState().SetModified();
5545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */