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>
21 #include <IDocumentUndoRedo.hxx>
22 #include <IDocumentMarkAccess.hxx>
23 #include <IDocumentState.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <IDocumentRedlineAccess.hxx>
26 #include <IDocumentStylePoolAccess.hxx>
27 #include <IDocumentSettingAccess.hxx>
28 #include <UndoManager.hxx>
30 #include <textboxhelper.hxx>
31 #include <dcontact.hxx>
33 #include <numrule.hxx>
34 #include <charfmt.hxx>
36 #include <ndnotxt.hxx>
38 #include <breakit.hxx>
40 #include <fmtanchr.hxx>
41 #include <fmtcntnt.hxx>
42 #include <fmtinfmt.hxx>
43 #include <fmtpdsc.hxx>
44 #include <fmtcnct.hxx>
45 #include <SwStyleNameMapper.hxx>
46 #include <redline.hxx>
48 #include <rootfrm.hxx>
49 #include <frmtool.hxx>
50 #include <unocrsr.hxx>
53 #include <poolfmt.hxx>
55 #include <txatbase.hxx>
56 #include <UndoRedline.hxx>
58 #include <UndoBookmark.hxx>
59 #include <UndoDelete.hxx>
60 #include <UndoSplitMove.hxx>
61 #include <UndoOverwrite.hxx>
62 #include <UndoInsert.hxx>
63 #include <UndoAttribute.hxx>
65 #include <acorrect.hxx>
66 #include <bookmark.hxx>
70 #include <fmtflcnt.hxx>
72 #include <frameformats.hxx>
73 #include <o3tl/safeint.hxx>
74 #include <sal/log.hxx>
75 #include <unotools/charclass.hxx>
76 #include <unotools/configmgr.hxx>
77 #include <unotools/transliterationwrapper.hxx>
78 #include <i18nutil/transliteration.hxx>
79 #include <sfx2/Metadatable.hxx>
80 #include <sot/exchange.hxx>
81 #include <svl/stritem.hxx>
82 #include <svl/itemiter.hxx>
83 #include <svx/svdobj.hxx>
84 #include <svx/svdouno.hxx>
85 #include <tools/globname.hxx>
86 #include <editeng/formatbreakitem.hxx>
87 #include <com/sun/star/i18n/Boundary.hpp>
88 #include <com/sun/star/i18n/WordType.hpp>
89 #include <com/sun/star/i18n/XBreakIterator.hpp>
90 #include <com/sun/star/embed/XEmbeddedObject.hpp>
95 using namespace ::com::sun::star::i18n
;
99 // Copy method from SwDoc
100 // Prevent copying into Flys that are anchored in the range
101 bool lcl_ChkFlyFly( SwDoc
& rDoc
, SwNodeOffset nSttNd
, SwNodeOffset nEndNd
,
102 SwNodeOffset nInsNd
)
104 const SwFrameFormats
& rFrameFormatTable
= *rDoc
.GetSpzFrameFormats();
106 for( size_t n
= 0; n
< rFrameFormatTable
.size(); ++n
)
108 SwFrameFormat
const*const pFormat
= rFrameFormatTable
[n
];
109 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
110 SwPosition
const*const pAPos
= pAnchor
->GetContentAnchor();
112 ((RndStdIds::FLY_AS_CHAR
== pAnchor
->GetAnchorId()) ||
113 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId()) ||
114 (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId()) ||
115 (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId())) &&
116 nSttNd
<= pAPos
->nNode
.GetIndex() &&
117 pAPos
->nNode
.GetIndex() < nEndNd
)
119 const SwFormatContent
& rContent
= pFormat
->GetContent();
121 if( !rContent
.GetContentIdx() ||
122 nullptr == ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ))
125 if( pSNd
->GetIndex() < nInsNd
&&
126 nInsNd
< pSNd
->EndOfSectionIndex() )
130 if( lcl_ChkFlyFly( rDoc
, pSNd
->GetIndex(),
131 pSNd
->EndOfSectionIndex(), nInsNd
) )
140 SwNodeIndex
InitDelCount(SwPaM
const& rSourcePaM
, SwNodeOffset
& rDelCount
)
142 SwNodeIndex
const& rStart(rSourcePaM
.Start()->nNode
);
143 // Special handling for SwDoc::AppendDoc
144 if (rSourcePaM
.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
145 == rStart
.GetIndex())
147 rDelCount
= SwNodeOffset(1);
148 return SwNodeIndex(rStart
, +1);
152 rDelCount
= SwNodeOffset(0);
158 The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
159 array. It is called after a call of the CopyNodes(..) function. But this function does not copy
160 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
161 if the corresponding end/start node is outside the copied pam.
162 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
163 index inside the pam.
164 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
165 of "non-copy" nodes between rPam.Start() and rLastIdx.
166 nNewIdx is the new position of interest.
168 void lcl_NonCopyCount( const SwPaM
& rPam
, SwNodeIndex
& rLastIdx
, const SwNodeOffset nNewIdx
, SwNodeOffset
& rDelCount
)
170 SwNodeOffset nStart
= rPam
.Start()->nNode
.GetIndex();
171 SwNodeOffset nEnd
= rPam
.End()->nNode
.GetIndex();
172 if( rLastIdx
.GetIndex() < nNewIdx
) // Moving forward?
174 // We never copy the StartOfContent node
175 do // count "non-copy" nodes
177 SwNode
& rNode
= rLastIdx
.GetNode();
178 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
179 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
185 while( rLastIdx
.GetIndex() < nNewIdx
);
187 else if( rDelCount
) // optimization: if there are no "non-copy" nodes until now,
188 // no move backward needed
190 while( rLastIdx
.GetIndex() > nNewIdx
)
192 SwNode
& rNode
= rLastIdx
.GetNode();
193 if( ( rNode
.IsSectionNode() && rNode
.EndOfSectionIndex() >= nEnd
)
194 || ( rNode
.IsEndNode() && rNode
.StartOfSectionNode()->GetIndex() < nStart
) )
203 void lcl_SetCpyPos( const SwPosition
& rOrigPos
,
204 const SwPosition
& rOrigStt
,
205 const SwPosition
& rCpyStt
,
207 SwNodeOffset nDelCount
)
209 SwNodeOffset nNdOff
= rOrigPos
.nNode
.GetIndex();
210 nNdOff
-= rOrigStt
.nNode
.GetIndex();
212 sal_Int32 nContentPos
= rOrigPos
.nContent
.GetIndex();
214 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
215 rChgPos
.nNode
= nNdOff
+ rCpyStt
.nNode
.GetIndex();
218 // just adapt the content index
219 if( nContentPos
> rOrigStt
.nContent
.GetIndex() )
220 nContentPos
-= rOrigStt
.nContent
.GetIndex();
223 nContentPos
+= rCpyStt
.nContent
.GetIndex();
225 rChgPos
.nContent
.Assign( rChgPos
.nNode
.GetNode().GetContentNode(), nContentPos
);
232 // TODO: use SaveBookmark (from DelBookmarks)
233 void CopyBookmarks(const SwPaM
& rPam
, const SwPosition
& rCpyPam
)
235 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
236 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
237 const IDocumentMarkAccess
* const pSrcMarkAccess
= rSrcDoc
.getIDocumentMarkAccess();
238 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
240 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
241 SwPosition
const*const pCpyStt
= &rCpyPam
;
243 std::vector
< const ::sw::mark::IMark
* > vMarksToCopy
;
244 for ( IDocumentMarkAccess::const_iterator_t ppMark
= pSrcMarkAccess
->getAllMarksBegin();
245 ppMark
!= pSrcMarkAccess
->getAllMarksEnd();
248 const ::sw::mark::IMark
* const pMark
= *ppMark
;
250 const SwPosition
& rMarkStart
= pMark
->GetMarkStart();
251 const SwPosition
& rMarkEnd
= pMark
->GetMarkEnd();
252 // only include marks that are in the range and not touching both start and end
253 // - not for annotation or checkbox marks.
254 bool const isIncludeStart(
255 (rStt
.nContent
.GetIndex() == 0 // paragraph start selected?
256 // also: only if inserting at the start - cross reference
257 // marks require index to be 0, and there could be one
258 // on the target node already
259 && rCpyPam
.nContent
.GetIndex() == 0)
260 || rMarkStart
!= rStt
);
261 bool const isIncludeEnd(
262 (rEnd
.nNode
.GetNode().IsTextNode() // paragraph end selected?
263 && rEnd
.nContent
.GetIndex() == rEnd
.nNode
.GetNode().GetTextNode()->Len())
264 || rMarkEnd
!= rEnd
);
265 const bool bIsNotOnBoundary
=
267 ? (isIncludeStart
|| isIncludeEnd
) // rMarkStart != rMarkEnd
268 : (isIncludeStart
&& isIncludeEnd
); // rMarkStart == rMarkEnd
269 const IDocumentMarkAccess::MarkType aMarkType
= IDocumentMarkAccess::GetType(*pMark
);
270 if ( rMarkStart
>= rStt
&& rMarkEnd
<= rEnd
271 && ( bIsNotOnBoundary
272 || aMarkType
== IDocumentMarkAccess::MarkType::ANNOTATIONMARK
273 || aMarkType
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
274 || aMarkType
== IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
275 || aMarkType
== IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
276 || aMarkType
== IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
278 vMarksToCopy
.push_back(pMark
);
281 // We have to count the "non-copied" nodes...
282 SwNodeOffset nDelCount
;
283 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
284 for(const sw::mark::IMark
* const pMark
: vMarksToCopy
)
286 SwPaM
aTmpPam(*pCpyStt
);
287 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetMarkPos().nNode
.GetIndex(), nDelCount
);
288 lcl_SetCpyPos( pMark
->GetMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetPoint(), nDelCount
);
289 if(pMark
->IsExpanded())
292 lcl_NonCopyCount(rPam
, aCorrIdx
, pMark
->GetOtherMarkPos().nNode
.GetIndex(), nDelCount
);
293 lcl_SetCpyPos(pMark
->GetOtherMarkPos(), rStt
, *pCpyStt
, *aTmpPam
.GetMark(), nDelCount
);
296 ::sw::mark::IMark
* const pNewMark
= rDestDoc
.getIDocumentMarkAccess()->makeMark(
299 IDocumentMarkAccess::GetType(*pMark
),
300 ::sw::mark::InsertMode::CopyText
);
301 // Explicitly try to get exactly the same name as in the source
302 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
303 rDestDoc
.getIDocumentMarkAccess()->renameMark(pNewMark
, pMark
->GetName());
305 // copying additional attributes for bookmarks or fieldmarks
306 ::sw::mark::IBookmark
* const pNewBookmark
=
307 dynamic_cast< ::sw::mark::IBookmark
* const >(pNewMark
);
308 const ::sw::mark::IBookmark
* const pOldBookmark
=
309 dynamic_cast< const ::sw::mark::IBookmark
* >(pMark
);
310 if (pNewBookmark
&& pOldBookmark
)
312 pNewBookmark
->SetKeyCode(pOldBookmark
->GetKeyCode());
313 pNewBookmark
->SetShortName(pOldBookmark
->GetShortName());
314 pNewBookmark
->Hide(pOldBookmark
->IsHidden());
315 pNewBookmark
->SetHideCondition(pOldBookmark
->GetHideCondition());
317 ::sw::mark::IFieldmark
* const pNewFieldmark
=
318 dynamic_cast< ::sw::mark::IFieldmark
* const >(pNewMark
);
319 const ::sw::mark::IFieldmark
* const pOldFieldmark
=
320 dynamic_cast< const ::sw::mark::IFieldmark
* >(pMark
);
321 if (pNewFieldmark
&& pOldFieldmark
)
323 pNewFieldmark
->SetFieldname(pOldFieldmark
->GetFieldname());
324 pNewFieldmark
->SetFieldHelptext(pOldFieldmark
->GetFieldHelptext());
325 ::sw::mark::IFieldmark::parameter_map_t
* pNewParams
= pNewFieldmark
->GetParameters();
326 const ::sw::mark::IFieldmark::parameter_map_t
* pOldParams
= pOldFieldmark
->GetParameters();
327 for (const auto& rEntry
: *pOldParams
)
329 pNewParams
->insert( rEntry
);
333 ::sfx2::Metadatable
const*const pMetadatable(
334 dynamic_cast< ::sfx2::Metadatable
const* >(pMark
));
335 ::sfx2::Metadatable
*const pNewMetadatable(
336 dynamic_cast< ::sfx2::Metadatable
* >(pNewMark
));
337 if (pMetadatable
&& pNewMetadatable
)
339 pNewMetadatable
->RegisterAsCopyOf(*pMetadatable
);
347 void lcl_DeleteRedlines( const SwPaM
& rPam
, SwPaM
& rCpyPam
)
349 const SwDoc
& rSrcDoc
= rPam
.GetDoc();
350 const SwRedlineTable
& rTable
= rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable();
354 SwDoc
& rDestDoc
= rCpyPam
.GetDoc();
355 SwPosition
* pCpyStt
= rCpyPam
.Start(), *pCpyEnd
= rCpyPam
.End();
356 std::unique_ptr
<SwPaM
> pDelPam
;
357 const SwPosition
*pStt
= rPam
.Start(), *pEnd
= rPam
.End();
358 // We have to count the "non-copied" nodes
359 SwNodeOffset nDelCount
;
360 SwNodeIndex
aCorrIdx(InitDelCount(rPam
, nDelCount
));
362 SwRedlineTable::size_type n
= 0;
363 rSrcDoc
.getIDocumentRedlineAccess().GetRedline( *pStt
, &n
);
364 for( ; n
< rTable
.size(); ++n
)
366 const SwRangeRedline
* pRedl
= rTable
[ n
];
367 if( RedlineType::Delete
== pRedl
->GetType() && pRedl
->IsVisible() )
369 const SwPosition
*pRStt
= pRedl
->Start(), *pREnd
= pRedl
->End();
371 SwComparePosition eCmpPos
= ComparePosition( *pStt
, *pEnd
, *pRStt
, *pREnd
);
374 case SwComparePosition::CollideEnd
:
375 case SwComparePosition::Before
:
376 // Pos1 is before Pos2
379 case SwComparePosition::CollideStart
:
380 case SwComparePosition::Behind
:
381 // Pos1 is after Pos2
387 pDelPam
.reset(new SwPaM( *pCpyStt
, pDelPam
.release() ));
390 lcl_NonCopyCount( rPam
, aCorrIdx
, pRStt
->nNode
.GetIndex(), nDelCount
);
391 lcl_SetCpyPos( *pRStt
, *pStt
, *pCpyStt
,
392 *pDelPam
->GetPoint(), nDelCount
);
397 *pDelPam
->GetPoint() = *pCpyEnd
;
400 lcl_NonCopyCount( rPam
, aCorrIdx
, pREnd
->nNode
.GetIndex(), nDelCount
);
401 lcl_SetCpyPos( *pREnd
, *pStt
, *pCpyStt
,
402 *pDelPam
->GetPoint(), nDelCount
);
405 if (pDelPam
->GetNext() && *pDelPam
->GetNext()->End() == *pDelPam
->Start())
407 *pDelPam
->GetNext()->End() = *pDelPam
->End();
408 pDelPam
.reset(pDelPam
->GetNext());
418 RedlineFlags eOld
= rDestDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
419 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
| RedlineFlags::Ignore
);
421 ::sw::UndoGuard
const undoGuard(rDestDoc
.GetIDocumentUndoRedo());
424 rDestDoc
.getIDocumentContentOperations().DeleteAndJoin( *pDelPam
->GetNext() );
425 if( !pDelPam
->IsMultiSelection() )
427 delete pDelPam
->GetNext();
430 rDestDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
433 void lcl_DeleteRedlines( const SwNodeRange
& rRg
, SwNodeRange
const & rCpyRg
)
435 SwDoc
& rSrcDoc
= rRg
.aStart
.GetNode().GetDoc();
436 if( !rSrcDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
438 SwPaM
aRgTmp( rRg
.aStart
, rRg
.aEnd
);
439 SwPaM
aCpyTmp( rCpyRg
.aStart
, rCpyRg
.aEnd
);
440 lcl_DeleteRedlines( aRgTmp
, aCpyTmp
);
444 void lcl_ChainFormats( SwFlyFrameFormat
*pSrc
, SwFlyFrameFormat
*pDest
)
446 SwFormatChain
aSrc( pSrc
->GetChain() );
447 if ( !aSrc
.GetNext() )
449 aSrc
.SetNext( pDest
);
450 pSrc
->SetFormatAttr( aSrc
);
452 SwFormatChain
aDest( pDest
->GetChain() );
453 if ( !aDest
.GetPrev() )
455 aDest
.SetPrev( pSrc
);
456 pDest
->SetFormatAttr( aDest
);
461 bool lcl_ContainsOnlyParagraphsInList( const SwPaM
& rPam
)
465 const SwTextNode
* pTextNd
= rPam
.Start()->nNode
.GetNode().GetTextNode();
466 const SwTextNode
* pEndTextNd
= rPam
.End()->nNode
.GetNode().GetTextNode();
467 if ( pTextNd
&& pTextNd
->IsInList() &&
468 pEndTextNd
&& pEndTextNd
->IsInList() )
471 SwNodeIndex
aIdx(rPam
.Start()->nNode
);
476 pTextNd
= aIdx
.GetNode().GetTextNode();
478 if ( !pTextNd
|| !pTextNd
->IsInList() )
483 } while (pTextNd
!= pEndTextNd
);
489 bool lcl_MarksWholeNode(const SwPaM
& rPam
)
491 bool bResult
= false;
492 const SwPosition
* pStt
= rPam
.Start();
493 const SwPosition
* pEnd
= rPam
.End();
495 if (nullptr != pStt
&& nullptr != pEnd
)
497 const SwTextNode
* pSttNd
= pStt
->nNode
.GetNode().GetTextNode();
498 const SwTextNode
* pEndNd
= pEnd
->nNode
.GetNode().GetTextNode();
500 if (nullptr != pSttNd
&& nullptr != pEndNd
&&
501 pStt
->nContent
.GetIndex() == 0 &&
502 pEnd
->nContent
.GetIndex() == pEndNd
->Len())
512 //local functions originally from sw/source/core/doc/docedt.cxx
515 void CalcBreaks(std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> & rBreaks
,
516 SwPaM
const & rPam
, bool const isOnlyFieldmarks
)
518 SwNodeOffset
const nStartNode(rPam
.Start()->nNode
.GetIndex());
519 SwNodeOffset
const nEndNode(rPam
.End()->nNode
.GetIndex());
520 SwNodes
const& rNodes(rPam
.GetPoint()->nNode
.GetNodes());
521 IDocumentMarkAccess
const& rIDMA(*rPam
.GetDoc().getIDocumentMarkAccess());
523 std::stack
<std::tuple
<sw::mark::IFieldmark
const*, bool, SwNodeOffset
, sal_Int32
>> startedFields
;
525 for (SwNodeOffset n
= nStartNode
; n
<= nEndNode
; ++n
)
527 SwNode
*const pNode(rNodes
[n
]);
528 if (pNode
->IsTextNode())
530 SwTextNode
& rTextNode(*pNode
->GetTextNode());
531 sal_Int32
const nStart(n
== nStartNode
532 ? rPam
.Start()->nContent
.GetIndex()
534 sal_Int32
const nEnd(n
== nEndNode
535 ? rPam
.End()->nContent
.GetIndex()
537 for (sal_Int32 i
= nStart
; i
< nEnd
; ++i
)
539 const sal_Unicode
c(rTextNode
.GetText()[i
]);
542 // note: CH_TXT_ATR_FORMELEMENT does not need handling
543 // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
544 case CH_TXTATR_INWORD
:
545 case CH_TXTATR_BREAKWORD
:
547 // META hints only have dummy char at the start, not
548 // at the end, so no need to check in nStartNode
549 if (n
== nEndNode
&& !isOnlyFieldmarks
)
551 SwTextAttr
const*const pAttr(rTextNode
.GetTextAttrForCharAt(i
));
552 if (pAttr
&& pAttr
->End() && (nEnd
< *pAttr
->End()))
554 assert(pAttr
->HasDummyChar());
555 rBreaks
.emplace_back(n
, i
);
560 case CH_TXT_ATR_FIELDSTART
:
562 auto const pFieldMark(rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
563 startedFields
.emplace(pFieldMark
, false, 0, 0);
566 case CH_TXT_ATR_FIELDSEP
:
568 if (startedFields
.empty())
570 rBreaks
.emplace_back(n
, i
);
573 { // no way to find the field via MarkManager...
574 assert(std::get
<0>(startedFields
.top())->IsCoveringPosition(SwPosition(rTextNode
, i
)));
575 std::get
<1>(startedFields
.top()) = true;
576 std::get
<2>(startedFields
.top()) = n
;
577 std::get
<3>(startedFields
.top()) = i
;
581 case CH_TXT_ATR_FIELDEND
:
583 if (startedFields
.empty())
585 rBreaks
.emplace_back(n
, i
);
588 { // fieldmarks must not overlap => stack
589 assert(std::get
<0>(startedFields
.top()) == rIDMA
.getFieldmarkAt(SwPosition(rTextNode
, i
)));
597 else if (pNode
->IsStartNode())
599 if (pNode
->EndOfSectionIndex() <= nEndNode
)
600 { // fieldmark cannot overlap node section
601 n
= pNode
->EndOfSectionIndex();
605 { // EndNode can actually happen with sections :(
606 assert(pNode
->IsEndNode() || pNode
->IsNoTextNode());
609 while (!startedFields
.empty())
611 SwPosition
const& rStart(std::get
<0>(startedFields
.top())->GetMarkStart());
612 std::pair
<SwNodeOffset
, sal_Int32
> const pos(
613 rStart
.nNode
.GetIndex(), rStart
.nContent
.GetIndex());
614 auto it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), pos
);
615 assert(it
== rBreaks
.end() || *it
!= pos
);
616 rBreaks
.insert(it
, pos
);
617 if (std::get
<1>(startedFields
.top()))
619 std::pair
<SwNodeOffset
, sal_Int32
> const posSep(
620 std::get
<2>(startedFields
.top()),
621 std::get
<3>(startedFields
.top()));
622 it
= std::lower_bound(rBreaks
.begin(), rBreaks
.end(), posSep
);
623 assert(it
== rBreaks
.end() || *it
!= posSep
);
624 rBreaks
.insert(it
, posSep
);
634 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager
& rDocumentContentOperations
, SwPaM
& rPam
,
635 bool (::sw::DocumentContentOperationsManager::*pFunc
)(SwPaM
&, bool), const bool bForceJoinNext
= false)
637 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
639 sw::CalcBreaks(Breaks
, rPam
);
643 return (rDocumentContentOperations
.*pFunc
)(rPam
, bForceJoinNext
);
646 // Deletion must be split into several parts if the text node
647 // contains a text attribute with end and with dummy character
648 // and the selection does not contain the text attribute completely,
649 // but overlaps its start (left), where the dummy character is.
651 SwPosition
const & rSelectionEnd( *rPam
.End() );
654 // iterate from end to start, to avoid invalidating the offsets!
655 auto iter( Breaks
.rbegin() );
656 SwNodeOffset
nOffset(0);
657 SwNodes
const& rNodes(rPam
.GetPoint()->nNode
.GetNodes());
658 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
659 SwPosition
& rEnd( *aPam
.End() );
660 SwPosition
& rStart( *aPam
.Start() );
662 while (iter
!= Breaks
.rend())
664 rStart
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
665 if (rStart
< rEnd
) // check if part is empty
667 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, bForceJoinNext
);
668 nOffset
= iter
->first
- rStart
.nNode
.GetIndex(); // deleted fly nodes...
670 rEnd
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
674 rStart
= *rPam
.Start(); // set to original start
675 if (rStart
< rEnd
) // check if part is empty
677 bRet
&= (rDocumentContentOperations
.*pFunc
)(aPam
, bForceJoinNext
);
683 bool lcl_StrLenOverflow( const SwPaM
& rPam
)
685 // If we try to merge two paragraphs we have to test if afterwards
686 // the string doesn't exceed the allowed string length
687 if( rPam
.GetPoint()->nNode
!= rPam
.GetMark()->nNode
)
689 const SwPosition
* pStt
= rPam
.Start(), *pEnd
= rPam
.End();
690 const SwTextNode
* pEndNd
= pEnd
->nNode
.GetNode().GetTextNode();
691 if( (nullptr != pEndNd
) && pStt
->nNode
.GetNode().IsTextNode() )
693 const sal_uInt64 nSum
= pStt
->nContent
.GetIndex() +
694 pEndNd
->GetText().getLength() - pEnd
->nContent
.GetIndex();
695 return nSum
> o3tl::make_unsigned(SAL_MAX_INT32
);
703 SwRangeRedline
* pRedl
;
704 SwNodeOffset nStt
, nEnd
;
708 SaveRedline( SwRangeRedline
* pR
, const SwNodeIndex
& rSttIdx
)
713 const SwPosition
* pStt
= pR
->Start(),
715 SwNodeOffset nSttIdx
= rSttIdx
.GetIndex();
716 nStt
= pStt
->nNode
.GetIndex() - nSttIdx
;
717 nSttCnt
= pStt
->nContent
.GetIndex();
720 nEnd
= pEnd
->nNode
.GetIndex() - nSttIdx
;
721 nEndCnt
= pEnd
->nContent
.GetIndex();
724 pRedl
->GetPoint()->nNode
= SwNodeOffset(0);
725 pRedl
->GetPoint()->nContent
.Assign( nullptr, 0 );
726 pRedl
->GetMark()->nNode
= SwNodeOffset(0);
727 pRedl
->GetMark()->nContent
.Assign( nullptr, 0 );
730 SaveRedline( SwRangeRedline
* pR
, const SwPosition
& rPos
)
735 const SwPosition
* pStt
= pR
->Start(),
737 SwNodeOffset nSttIdx
= rPos
.nNode
.GetIndex();
738 nStt
= pStt
->nNode
.GetIndex() - nSttIdx
;
739 nSttCnt
= pStt
->nContent
.GetIndex();
740 if( nStt
== SwNodeOffset(0) )
741 nSttCnt
= nSttCnt
- rPos
.nContent
.GetIndex();
744 nEnd
= pEnd
->nNode
.GetIndex() - nSttIdx
;
745 nEndCnt
= pEnd
->nContent
.GetIndex();
746 if( nEnd
== SwNodeOffset(0) )
747 nEndCnt
= nEndCnt
- rPos
.nContent
.GetIndex();
750 pRedl
->GetPoint()->nNode
= SwNodeOffset(0);
751 pRedl
->GetPoint()->nContent
.Assign( nullptr, 0 );
752 pRedl
->GetMark()->nNode
= SwNodeOffset(0);
753 pRedl
->GetMark()->nContent
.Assign( nullptr, 0 );
756 void SetPos( SwNodeOffset nInsPos
)
758 pRedl
->GetPoint()->nNode
= nInsPos
+ nStt
;
759 pRedl
->GetPoint()->nContent
.Assign( pRedl
->GetContentNode(), nSttCnt
);
760 if( pRedl
->HasMark() )
762 pRedl
->GetMark()->nNode
= nInsPos
+ nEnd
;
763 pRedl
->GetMark()->nContent
.Assign( pRedl
->GetContentNode(false), nEndCnt
);
767 void SetPos( const SwPosition
& aPos
)
769 pRedl
->GetPoint()->nNode
= aPos
.nNode
.GetIndex() + nStt
;
770 pRedl
->GetPoint()->nContent
.Assign( pRedl
->GetContentNode(), nSttCnt
+ ( nStt
== SwNodeOffset(0) ? aPos
.nContent
.GetIndex() : 0 ) );
771 if( pRedl
->HasMark() )
773 pRedl
->GetMark()->nNode
= aPos
.nNode
.GetIndex() + nEnd
;
774 pRedl
->GetMark()->nContent
.Assign( pRedl
->GetContentNode(false), nEndCnt
+ ( nEnd
== SwNodeOffset(0) ? aPos
.nContent
.GetIndex() : 0 ) );
779 typedef std::vector
< SaveRedline
> SaveRedlines_t
;
781 void lcl_SaveRedlines(const SwPaM
& aPam
, SaveRedlines_t
& rArr
)
783 SwDoc
& rDoc
= aPam
.GetNode().GetDoc();
785 const SwPosition
* pStart
= aPam
.Start();
786 const SwPosition
* pEnd
= aPam
.End();
788 // get first relevant redline
789 SwRedlineTable::size_type nCurrentRedline
;
790 rDoc
.getIDocumentRedlineAccess().GetRedline( *pStart
, &nCurrentRedline
);
791 if( nCurrentRedline
> 0)
794 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
795 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
796 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
798 // iterate over relevant redlines and decide for each whether it should
799 // be saved, or split + saved
800 SwRedlineTable
& rRedlineTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
801 for( ; nCurrentRedline
< rRedlineTable
.size(); nCurrentRedline
++ )
803 SwRangeRedline
* pCurrent
= rRedlineTable
[ nCurrentRedline
];
804 SwComparePosition eCompare
=
805 ComparePosition( *pCurrent
->Start(), *pCurrent
->End(),
808 // we must save this redline if it overlaps aPam
809 // (we may have to split it, too)
810 if( eCompare
== SwComparePosition::OverlapBehind
||
811 eCompare
== SwComparePosition::OverlapBefore
||
812 eCompare
== SwComparePosition::Outside
||
813 eCompare
== SwComparePosition::Inside
||
814 eCompare
== SwComparePosition::Equal
)
816 rRedlineTable
.Remove( nCurrentRedline
-- );
818 // split beginning, if necessary
819 if( eCompare
== SwComparePosition::OverlapBefore
||
820 eCompare
== SwComparePosition::Outside
)
822 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
823 *pNewRedline
->End() = *pStart
;
824 *pCurrent
->Start() = *pStart
;
825 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
828 // split end, if necessary
829 if( eCompare
== SwComparePosition::OverlapBehind
||
830 eCompare
== SwComparePosition::Outside
)
832 SwRangeRedline
* pNewRedline
= new SwRangeRedline( *pCurrent
);
833 *pNewRedline
->Start() = *pEnd
;
834 *pCurrent
->End() = *pEnd
;
835 rDoc
.getIDocumentRedlineAccess().AppendRedline( pNewRedline
, true );
838 // save the current redline
839 rArr
.emplace_back( pCurrent
, *pStart
);
843 // restore old redline mode
844 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
847 void lcl_RestoreRedlines(SwDoc
& rDoc
, const SwPosition
& rPos
, SaveRedlines_t
& rArr
)
849 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
850 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
852 for(SaveRedline
& rSvRedLine
: rArr
)
854 rSvRedLine
.SetPos( rPos
);
855 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true );
858 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
861 void lcl_SaveRedlines(const SwNodeRange
& rRg
, SaveRedlines_t
& rArr
)
863 SwDoc
& rDoc
= rRg
.aStart
.GetNode().GetDoc();
864 SwRedlineTable::size_type nRedlPos
;
865 SwPosition
aSrchPos( rRg
.aStart
); aSrchPos
.nNode
--;
866 aSrchPos
.nContent
.Assign( aSrchPos
.nNode
.GetNode().GetContentNode(), 0 );
867 if( rDoc
.getIDocumentRedlineAccess().GetRedline( aSrchPos
, &nRedlPos
) && nRedlPos
)
869 else if( nRedlPos
>= rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() )
872 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
873 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
874 SwRedlineTable
& rRedlTable
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable();
877 SwRangeRedline
* pTmp
= rRedlTable
[ nRedlPos
];
879 const SwPosition
* pRStt
= pTmp
->Start(),
880 * pREnd
= pTmp
->End();
882 if( pRStt
->nNode
< rRg
.aStart
)
884 if( pREnd
->nNode
> rRg
.aStart
&& pREnd
->nNode
< rRg
.aEnd
)
886 // Create a copy and set the end of the original to the end of the MoveArea.
887 // The copy is moved too.
888 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
889 SwPosition
* pTmpPos
= pNewRedl
->Start();
890 pTmpPos
->nNode
= rRg
.aStart
;
891 pTmpPos
->nContent
.Assign(
892 pTmpPos
->nNode
.GetNode().GetContentNode(), 0 );
894 rArr
.emplace_back(pNewRedl
, rRg
.aStart
);
896 pTmpPos
= pTmp
->End();
897 pTmpPos
->nNode
= rRg
.aEnd
;
898 pTmpPos
->nContent
.Assign(
899 pTmpPos
->nNode
.GetNode().GetContentNode(), 0 );
901 else if( pREnd
->nNode
== rRg
.aStart
)
903 SwPosition
* pTmpPos
= pTmp
->End();
904 pTmpPos
->nNode
= rRg
.aEnd
;
905 pTmpPos
->nContent
.Assign(
906 pTmpPos
->nNode
.GetNode().GetContentNode(), 0 );
909 else if( pRStt
->nNode
< rRg
.aEnd
)
911 rRedlTable
.Remove( nRedlPos
-- );
912 if( pREnd
->nNode
< rRg
.aEnd
||
913 ( pREnd
->nNode
== rRg
.aEnd
&& !pREnd
->nContent
.GetIndex()) )
916 rArr
.emplace_back( pTmp
, rRg
.aStart
);
921 SwRangeRedline
* pNewRedl
= new SwRangeRedline( *pTmp
);
922 SwPosition
* pTmpPos
= pNewRedl
->End();
923 pTmpPos
->nNode
= rRg
.aEnd
;
924 pTmpPos
->nContent
.Assign(
925 pTmpPos
->nNode
.GetNode().GetContentNode(), 0 );
927 rArr
.emplace_back( pNewRedl
, rRg
.aStart
);
929 pTmpPos
= pTmp
->Start();
930 pTmpPos
->nNode
= rRg
.aEnd
;
931 pTmpPos
->nContent
.Assign(
932 pTmpPos
->nNode
.GetNode().GetContentNode(), 0 );
933 rDoc
.getIDocumentRedlineAccess().AppendRedline( pTmp
, true );
939 } while( ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size() );
940 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
943 void lcl_RestoreRedlines(SwDoc
& rDoc
, SwNodeOffset
const nInsPos
, SaveRedlines_t
& rArr
)
945 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
946 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
948 for(SaveRedline
& rSvRedLine
: rArr
)
950 rSvRedLine
.SetPos( nInsPos
);
951 IDocumentRedlineAccess::AppendResult
const result(
952 rDoc
.getIDocumentRedlineAccess().AppendRedline( rSvRedLine
.pRedl
, true ));
953 if ( IDocumentRedlineAccess::AppendResult::APPENDED
== result
&&
954 rSvRedLine
.pRedl
->GetType() == RedlineType::Delete
)
956 UpdateFramesForAddDeleteRedline(rDoc
, *rSvRedLine
.pRedl
);
960 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
963 bool lcl_SaveFootnote( const SwNodeIndex
& rSttNd
, const SwNodeIndex
& rEndNd
,
964 const SwNodeIndex
& rInsPos
,
965 SwFootnoteIdxs
& rFootnoteArr
, SwFootnoteIdxs
& rSaveArr
,
966 const SwIndex
* pSttCnt
= nullptr, const SwIndex
* pEndCnt
= nullptr )
968 bool bUpdateFootnote
= false;
969 const SwNodes
& rNds
= rInsPos
.GetNodes();
970 const bool bDelFootnote
= rInsPos
.GetIndex() < rNds
.GetEndOfAutotext().GetIndex() &&
971 rSttNd
.GetIndex() >= rNds
.GetEndOfAutotext().GetIndex();
972 const bool bSaveFootnote
= !bDelFootnote
&&
973 rInsPos
.GetIndex() >= rNds
.GetEndOfExtras().GetIndex();
974 if( !rFootnoteArr
.empty() )
978 rFootnoteArr
.SeekEntry( rSttNd
, &nPos
);
979 SwTextFootnote
* pSrch
;
980 const SwNode
* pFootnoteNd
;
982 // Delete/save all that come after it
983 while( nPos
< rFootnoteArr
.size() && ( pFootnoteNd
=
984 &( pSrch
= rFootnoteArr
[ nPos
] )->GetTextNode())->GetIndex()
985 <= rEndNd
.GetIndex() )
987 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
988 if( ( pEndCnt
&& pSttCnt
)
989 ? (( &rSttNd
.GetNode() == pFootnoteNd
&&
990 pSttCnt
->GetIndex() > nFootnoteSttIdx
) ||
991 ( &rEndNd
.GetNode() == pFootnoteNd
&&
992 nFootnoteSttIdx
>= pEndCnt
->GetIndex() ))
993 : ( &rEndNd
.GetNode() == pFootnoteNd
))
995 ++nPos
; // continue searching
1002 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1003 SwIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1004 rTextNd
.EraseText( aIdx
, 1 );
1008 pSrch
->DelFrames(nullptr);
1009 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1011 rSaveArr
.insert( pSrch
);
1013 bUpdateFootnote
= true;
1017 while( nPos
-- && ( pFootnoteNd
= &( pSrch
= rFootnoteArr
[ nPos
] )->
1018 GetTextNode())->GetIndex() >= rSttNd
.GetIndex() )
1020 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
1021 if( !pEndCnt
|| !pSttCnt
||
1022 ! (( &rSttNd
.GetNode() == pFootnoteNd
&&
1023 pSttCnt
->GetIndex() > nFootnoteSttIdx
) ||
1024 ( &rEndNd
.GetNode() == pFootnoteNd
&&
1025 nFootnoteSttIdx
>= pEndCnt
->GetIndex() )) )
1030 SwTextNode
& rTextNd
= const_cast<SwTextNode
&>(pSrch
->GetTextNode());
1031 SwIndex
aIdx( &rTextNd
, nFootnoteSttIdx
);
1032 rTextNd
.EraseText( aIdx
, 1 );
1036 pSrch
->DelFrames(nullptr);
1037 rFootnoteArr
.erase( rFootnoteArr
.begin() + nPos
);
1039 rSaveArr
.insert( pSrch
);
1041 bUpdateFootnote
= true;
1045 // When moving from redline section into document content section, e.g.
1046 // after loading a document with (delete-)redlines, the footnote array
1047 // has to be adjusted... (#i70572)
1050 SwNodeIndex
aIdx( rSttNd
);
1051 while( aIdx
< rEndNd
) // Check the moved section
1053 SwNode
* pNode
= &aIdx
.GetNode();
1054 if( pNode
->IsTextNode() ) // Looking for text nodes...
1056 SwpHints
*pHints
= pNode
->GetTextNode()->GetpSwpHints();
1057 if( pHints
&& pHints
->HasFootnote() ) //...with footnotes
1059 bUpdateFootnote
= true; // Heureka
1060 const size_t nCount
= pHints
->Count();
1061 for( size_t i
= 0; i
< nCount
; ++i
)
1063 SwTextAttr
*pAttr
= pHints
->Get( i
);
1064 if ( pAttr
->Which() == RES_TXTATR_FTN
)
1066 rSaveArr
.insert( static_cast<SwTextFootnote
*>(pAttr
) );
1074 return bUpdateFootnote
;
1077 bool lcl_MayOverwrite( const SwTextNode
*pNode
, const sal_Int32 nPos
)
1079 sal_Unicode
const cChr
= pNode
->GetText()[nPos
];
1082 case CH_TXTATR_BREAKWORD
:
1083 case CH_TXTATR_INWORD
:
1084 return !pNode
->GetTextAttrForCharAt(nPos
);// how could there be none?
1085 case CH_TXT_ATR_INPUTFIELDSTART
:
1086 case CH_TXT_ATR_INPUTFIELDEND
:
1087 case CH_TXT_ATR_FIELDSTART
:
1088 case CH_TXT_ATR_FIELDSEP
:
1089 case CH_TXT_ATR_FIELDEND
:
1090 case CH_TXT_ATR_FORMELEMENT
:
1097 void lcl_SkipAttr( const SwTextNode
*pNode
, SwIndex
&rIdx
, sal_Int32
&rStart
)
1099 if( !lcl_MayOverwrite( pNode
, rStart
) )
1101 // skip all special attributes
1104 rStart
= rIdx
.GetIndex();
1105 } while (rStart
< pNode
->GetText().getLength()
1106 && !lcl_MayOverwrite(pNode
, rStart
) );
1110 bool lcl_GetTokenToParaBreak( OUString
& rStr
, OUString
& rRet
, bool bRegExpRplc
)
1115 static const OUStringLiteral
sPara(u
"\\n");
1118 nPos
= rStr
.indexOf( sPara
, nPos
);
1123 // Has this been escaped?
1124 if( nPos
&& '\\' == rStr
[nPos
-1])
1127 if( nPos
>= rStr
.getLength() )
1134 rRet
= rStr
.copy( 0, nPos
);
1135 rStr
= rStr
.copy( nPos
+ sPara
.getLength() );
1146 namespace //local functions originally from docfmt.cxx
1149 bool lcl_ApplyOtherSet(
1150 SwContentNode
& rNode
,
1151 SwHistory
*const pHistory
,
1152 SfxItemSet
const& rOtherSet
,
1153 SfxItemSet
const& rFirstSet
,
1154 SfxItemSet
const& rPropsSet
,
1155 SwRootFrame
const*const pLayout
,
1156 SwNodeIndex
*const o_pIndex
= nullptr)
1158 assert(rOtherSet
.Count());
1161 SwTextNode
*const pTNd
= rNode
.GetTextNode();
1162 sw::MergedPara
const* pMerged(nullptr);
1163 if (pLayout
&& pLayout
->HasMergedParas() && pTNd
)
1165 SwTextFrame
const*const pTextFrame(static_cast<SwTextFrame
const*>(
1166 pTNd
->getLayoutFrame(pLayout
)));
1169 pMerged
= pTextFrame
->GetMergedPara();
1173 if (rFirstSet
.Count())
1177 SwRegHistory
aRegH(pMerged
->pFirstNode
, *pMerged
->pFirstNode
, pHistory
);
1178 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1182 ret
= pMerged
->pFirstNode
->SetAttr(rFirstSet
);
1185 if (rPropsSet
.Count())
1189 SwRegHistory
aRegH(pMerged
->pParaPropsNode
, *pMerged
->pParaPropsNode
, pHistory
);
1190 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1194 ret
= pMerged
->pParaPropsNode
->SetAttr(rPropsSet
) || ret
;
1199 *o_pIndex
= *pMerged
->pLastNode
; // skip hidden
1204 // input cursor can't be on hidden node, and iteration skips them
1205 assert(!pLayout
|| !pLayout
->HasMergedParas()
1206 || rNode
.GetRedlineMergeFlag() != SwNode::Merge::Hidden
);
1212 SwRegHistory
aRegH(&rNode
, rNode
, pHistory
);
1213 ret
= rNode
.SetAttr( rOtherSet
);
1217 ret
= rNode
.SetAttr( rOtherSet
);
1223 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
1225 // set format redline with extra data for lcl_InsAttr()
1226 void lcl_SetRedline(
1230 std::unique_ptr
<SwRedlineExtraData_FormatColl
> xExtra
;
1232 // check existing redline on the same range, and use its extra data, if it exists
1233 SwRedlineTable::size_type nRedlPos
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos(
1234 rRg
.Start()->nNode
.GetNode(), RedlineType::Format
);
1235 if( SwRedlineTable::npos
!= nRedlPos
)
1237 const SwPosition
*pRStt
, *pREnd
;
1239 SwRangeRedline
* pTmp
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
1240 pRStt
= pTmp
->Start();
1241 pREnd
= pTmp
->End();
1242 SwComparePosition eCompare
= ComparePosition( *rRg
.Start(), *rRg
.End(), *pRStt
, *pREnd
);
1243 if ( eCompare
== SwComparePosition::Inside
|| eCompare
== SwComparePosition::Equal
)
1245 if (pTmp
->GetExtraData())
1247 const SwRedlineExtraData
* pExtraData
= pTmp
->GetExtraData();
1248 const SwRedlineExtraData_FormatColl
* pFormattingChanges
=
1249 dynamic_cast<const SwRedlineExtraData_FormatColl
*>(pExtraData
);
1250 // Check if the extra data is of type 'formatting changes'
1251 if (pFormattingChanges
)
1253 // Get the item set that holds all the changes properties
1254 const SfxItemSet
*pChangesSet
= pFormattingChanges
->GetItemSet();
1255 xExtra
.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX
, pChangesSet
));
1260 } while( pRStt
<= rRg
.Start() && ++nRedlPos
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
1263 SwRangeRedline
* pRedline
= new SwRangeRedline( RedlineType::Format
, rRg
);
1264 auto const result(rDoc
.getIDocumentRedlineAccess().AppendRedline( pRedline
, true));
1265 // store original text attributes to reject formatting change
1266 if (IDocumentRedlineAccess::AppendResult::IGNORED
== result
)
1269 // no existing format redline in the range
1272 // Apply the first character's attributes to the ReplaceText
1273 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
1274 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( rDoc
.GetAttrPool() );
1275 SwTextNode
* pNode
= rRg
.Start()->nNode
.GetNode().GetTextNode();
1276 pNode
->GetParaAttr( aSet
, rRg
.Start()->nContent
.GetIndex() + 1, rRg
.End()->nContent
.GetIndex() );
1278 aSet
.ClearItem( RES_TXTATR_REFMARK
);
1279 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
1280 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
1281 aSet
.ClearItem( RES_TXTATR_INETFMT
);
1282 aSet
.ClearItem( RES_TXTATR_META
);
1283 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
1285 // After GetParaAttr aSet can contain INVALID_POOL_ITEM items, e.g. RES_TXTATR_CHARFMT
1286 // and (a copy of) this SfxItemSet can be passed to MSWordExportBase::OutputItemSet
1287 // which doesn't handle INVALID_POOL_ITEM items so clear InvalidItems here
1288 aSet
.ClearInvalidItems();
1290 xExtra
.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX
, &aSet
));
1295 pRedline
->SetExtraData(xExtra
.get() );
1299 // create format redline(s) for the given range:
1300 // to track the original formatting stored in the
1301 // hints, create redlines for all parts of the
1302 // range partitioned by boundaries of the hints.
1303 void lcl_SetRedlines(
1307 SwNodeIndex
aIdx( rRg
.Start()->nNode
);
1308 const SwNodeIndex
aEndNd( rRg
.End()->nNode
);
1309 while( aIdx
<= aEndNd
)
1311 SwTextNode
*pNode
= aIdx
.GetNode().GetTextNode();
1314 const sal_Int32 nStart
= aIdx
== rRg
.Start()->nNode
1315 ? rRg
.Start()->nContent
.GetIndex()
1317 const sal_Int32 nEnd
= aIdx
< aEndNd
1318 ? pNode
->GetText().getLength()
1319 : rRg
.End()->nContent
.GetIndex();
1321 if( SwpHints
*pHints
= pNode
->GetpSwpHints() )
1323 const size_t nCount
= pHints
->Count();
1324 sal_Int32 nRedEnd
= nStart
;
1325 for( size_t i
= 0; i
< nCount
; ++i
)
1327 SwTextAttr
*pAttr
= pHints
->Get( i
);
1329 if ( pAttr
->GetStart() > nEnd
)
1331 break; // after the range
1334 if ( !pAttr
->GetEnd() || *pAttr
->GetEnd() < nStart
)
1336 continue; // before the range
1339 // range part before the hint
1340 if ( nRedEnd
< pAttr
->GetStart() )
1342 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, pAttr
->GetStart() );
1343 lcl_SetRedline(rDoc
, aPam
);
1346 // range part at the hint
1347 sal_Int32 nRedStart
= std::max(pAttr
->GetStart(), nStart
);
1348 nRedEnd
= std::min(*pAttr
->GetEnd(), nEnd
);
1349 SwPaM
aPam2( *pNode
, nRedStart
, *pNode
, nRedEnd
);
1350 lcl_SetRedline(rDoc
, aPam2
);
1353 // range part after the last hint
1354 if ( nRedEnd
< nEnd
)
1356 SwPaM
aPam( *pNode
, nRedEnd
, *pNode
, nEnd
);
1357 lcl_SetRedline(rDoc
, aPam
);
1362 SwPaM
aPam( *pNode
, nStart
, *pNode
, nEnd
);
1363 lcl_SetRedline(rDoc
, aPam
);
1370 /// Insert Hints according to content types;
1371 // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
1376 const SfxItemSet
& rChgSet
,
1377 const SetAttrMode nFlags
,
1378 SwUndoAttr
*const pUndo
,
1379 SwRootFrame
const*const pLayout
,
1380 SwTextAttr
**ppNewTextAttr
)
1382 // Divide the Sets (for selections in Nodes)
1383 const SfxItemSet
* pCharSet
= nullptr;
1384 const SfxItemSet
* pOtherSet
= nullptr;
1385 bool bDelete
= false;
1386 bool bCharAttr
= false;
1387 bool bOtherAttr
= false;
1389 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
1390 if ( 1 == rChgSet
.Count() )
1392 SfxItemIter
aIter( rChgSet
);
1393 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
1394 if (pItem
&& !IsInvalidItem(pItem
))
1396 const sal_uInt16 nWhich
= pItem
->Which();
1398 if ( isCHRATR(nWhich
) ||
1399 (RES_TXTATR_CHARFMT
== nWhich
) ||
1400 (RES_TXTATR_INETFMT
== nWhich
) ||
1401 (RES_TXTATR_AUTOFMT
== nWhich
) ||
1402 (RES_TXTATR_UNKNOWN_CONTAINER
== nWhich
) )
1404 pCharSet
= &rChgSet
;
1408 if ( isPARATR(nWhich
)
1409 || isPARATR_LIST(nWhich
)
1412 || isUNKNOWNATR(nWhich
)
1413 || isDrawingLayerAttribute(nWhich
) )
1415 pOtherSet
= &rChgSet
;
1421 // Build new itemset if either
1422 // - rChgSet.Count() > 1 or
1423 // - The attribute in rChgSet does not belong to one of the above categories
1424 if ( !bCharAttr
&& !bOtherAttr
)
1426 SfxItemSet
* pTmpCharItemSet
= new SfxItemSetFixed
<
1427 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
1428 RES_TXTATR_AUTOFMT
, RES_TXTATR_CHARFMT
,
1429 RES_TXTATR_UNKNOWN_CONTAINER
,
1430 RES_TXTATR_UNKNOWN_CONTAINER
>( rDoc
.GetAttrPool() );
1432 SfxItemSet
* pTmpOtherItemSet
= new SfxItemSetFixed
<
1433 RES_PARATR_BEGIN
, RES_GRFATR_END
- 1,
1434 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
- 1,
1435 // FillAttribute support:
1436 XATTR_FILL_FIRST
, XATTR_FILL_LAST
>( rDoc
.GetAttrPool() );
1438 pTmpCharItemSet
->Put( rChgSet
);
1439 pTmpOtherItemSet
->Put( rChgSet
);
1441 pCharSet
= pTmpCharItemSet
;
1442 pOtherSet
= pTmpOtherItemSet
;
1447 SwHistory
* pHistory
= pUndo
? &pUndo
->GetHistory() : nullptr;
1449 const SwPosition
*pStt
= rRg
.Start(), *pEnd
= rRg
.End();
1450 SwContentNode
* pNode
= pStt
->nNode
.GetNode().GetContentNode();
1452 if( pNode
&& pNode
->IsTextNode() )
1454 // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
1455 if (pLayout
&& pNode
->GetTextNode()->getIDocumentSettingAccess()->
1456 get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING
))
1458 SwContentNode
* pEndNode
= pEnd
->nNode
.GetNode().GetContentNode();
1459 SwContentNode
* pCurrentNode
= pEndNode
;
1460 auto nStartIndex
= pNode
->GetIndex();
1461 auto nEndIndex
= pEndNode
->GetIndex();
1462 SwNodeIndex
aIdx( pEnd
->nNode
.GetNode() );
1463 while ( pCurrentNode
!= nullptr && nStartIndex
<= pCurrentNode
->GetIndex() )
1465 if (pCurrentNode
->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT
) &&
1466 // remove character formatting only on wholly selected paragraphs
1467 (nStartIndex
< pCurrentNode
->GetIndex() || pStt
->nContent
.GetIndex() == 0) &&
1468 (pCurrentNode
->GetIndex() < nEndIndex
|| pEnd
->nContent
.GetIndex() == pEndNode
->Len()))
1470 pCurrentNode
->ResetAttr(RES_PARATR_LIST_AUTOFMT
);
1471 // reset also paragraph marker
1472 SwIndex
nIdx( pCurrentNode
, pCurrentNode
->Len() );
1473 pCurrentNode
->GetTextNode()->RstTextAttr(nIdx
, 1);
1475 pCurrentNode
= SwNodes::GoPrevious( &aIdx
);
1479 if (rRg
.IsInFrontOfLabel())
1481 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1484 pTextNd
= sw::GetParaPropsNode(*pLayout
, *pTextNd
);
1486 SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
1490 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
1495 int nLevel
= pTextNd
->GetActualListLevel();
1500 if (nLevel
>= MAXLEVEL
)
1501 nLevel
= MAXLEVEL
- 1;
1503 SwNumFormat aNumFormat
= pNumRule
->Get(o3tl::narrowing
<sal_uInt16
>(nLevel
));
1504 SwCharFormat
* pCharFormat
=
1505 rDoc
.FindCharFormatByName(aNumFormat
.GetCharFormatName());
1510 pHistory
->Add(pCharFormat
->GetAttrSet(), *pCharFormat
);
1513 pCharFormat
->SetFormatAttr(*pCharSet
);
1520 const SwIndex
& rSt
= pStt
->nContent
;
1522 // Attributes without an end do not have a range
1523 if ( !bCharAttr
&& !bOtherAttr
)
1525 SfxItemSetFixed
<RES_TXTATR_NOEND_BEGIN
, RES_TXTATR_NOEND_END
-1>
1526 aTextSet( rDoc
.GetAttrPool() );
1527 aTextSet
.Put( rChgSet
);
1528 if( aTextSet
.Count() )
1530 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1531 bRet
= history
.InsertItems(
1532 aTextSet
, rSt
.GetIndex(), rSt
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr ) || bRet
;
1534 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1535 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1537 SwPaM
aPam( pStt
->nNode
, pStt
->nContent
.GetIndex()-1,
1538 pStt
->nNode
, pStt
->nContent
.GetIndex() );
1541 pUndo
->SaveRedlineData( aPam
, true );
1543 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1544 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
1546 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1551 // TextAttributes with an end never expand their range
1552 if ( !bCharAttr
&& !bOtherAttr
)
1554 // CharFormat and URL attributes are treated separately!
1555 // TEST_TEMP ToDo: AutoFormat!
1557 RES_TXTATR_REFMARK
, RES_TXTATR_METAFIELD
,
1558 RES_TXTATR_CJK_RUBY
, RES_TXTATR_CJK_RUBY
,
1559 RES_TXTATR_INPUTFIELD
, RES_TXTATR_INPUTFIELD
>
1560 aTextSet(rDoc
.GetAttrPool());
1562 aTextSet
.Put( rChgSet
);
1563 if( aTextSet
.Count() )
1565 const sal_Int32 nInsCnt
= rSt
.GetIndex();
1566 const sal_Int32 nEnd
= pStt
->nNode
== pEnd
->nNode
1567 ? pEnd
->nContent
.GetIndex()
1569 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1570 bRet
= history
.InsertItems( aTextSet
, nInsCnt
, nEnd
, nFlags
, ppNewTextAttr
)
1573 if (bRet
&& (rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
1574 && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())))
1576 // Was text content inserted? (RefMark/TOXMarks without an end)
1577 bool bTextIns
= nInsCnt
!= rSt
.GetIndex();
1578 // Was content inserted or set over the selection?
1579 SwPaM
aPam( pStt
->nNode
, bTextIns
? nInsCnt
+ 1 : nEnd
,
1580 pStt
->nNode
, nInsCnt
);
1582 pUndo
->SaveRedlineData( aPam
, bTextIns
);
1584 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1585 rDoc
.getIDocumentRedlineAccess().AppendRedline(
1587 bTextIns
? RedlineType::Insert
: RedlineType::Format
, aPam
),
1590 rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
1596 // We always have to set the auto flag for PageDescs that are set at the Node!
1597 if( pOtherSet
&& pOtherSet
->Count() )
1599 SwTableNode
* pTableNd
;
1600 const SwFormatPageDesc
* pDesc
;
1601 if( SfxItemState::SET
== pOtherSet
->GetItemState( RES_PAGEDESC
,
1602 false, reinterpret_cast<const SfxPoolItem
**>(&pDesc
) ))
1606 // Set auto flag. Only in the template it's without auto!
1607 SwFormatPageDesc
aNew( *pDesc
);
1609 // Tables now also know line breaks
1610 if( !(nFlags
& SetAttrMode::APICALL
) &&
1611 nullptr != ( pTableNd
= pNode
->FindTableNode() ) )
1613 SwTableNode
* pCurTableNd
= pTableNd
;
1614 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1615 pTableNd
= pCurTableNd
;
1617 // set the table format
1618 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1619 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1620 pFormat
->SetFormatAttr( aNew
);
1625 SwContentNode
* pFirstNode(pNode
);
1626 if (pLayout
&& pLayout
->HasMergedParas())
1628 pFirstNode
= sw::GetFirstAndLastNode(*pLayout
, pStt
->nNode
).first
;
1630 SwRegHistory
aRegH( pFirstNode
, *pFirstNode
, pHistory
);
1631 bRet
= pFirstNode
->SetAttr( aNew
) || bRet
;
1635 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1636 // we know, that there is only one attribute in pOtherSet. We cannot
1637 // perform the following operations, instead we return:
1641 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_PAGEDESC
);
1642 if( !pOtherSet
->Count() )
1649 // Tables now also know line breaks
1650 const SvxFormatBreakItem
* pBreak
;
1651 if( pNode
&& !(nFlags
& SetAttrMode::APICALL
) &&
1652 nullptr != (pTableNd
= pNode
->FindTableNode() ) &&
1653 SfxItemState::SET
== pOtherSet
->GetItemState( RES_BREAK
,
1654 false, reinterpret_cast<const SfxPoolItem
**>(&pBreak
) ) )
1656 SwTableNode
* pCurTableNd
= pTableNd
;
1657 while ( nullptr != ( pCurTableNd
= pCurTableNd
->StartOfSectionNode()->FindTableNode() ) )
1658 pTableNd
= pCurTableNd
;
1660 // set the table format
1661 SwFrameFormat
* pFormat
= pTableNd
->GetTable().GetFrameFormat();
1662 SwRegHistory
aRegH( pFormat
, *pTableNd
, pHistory
);
1663 pFormat
->SetFormatAttr( *pBreak
);
1666 // bOtherAttr = true means that pOtherSet == rChgSet. In this case
1667 // we know, that there is only one attribute in pOtherSet. We cannot
1668 // perform the following operations, instead we return:
1672 const_cast<SfxItemSet
*>(pOtherSet
)->ClearItem( RES_BREAK
);
1673 if( !pOtherSet
->Count() )
1681 // If we have a PoolNumRule, create it if needed
1682 const SwNumRuleItem
* pRule
;
1683 sal_uInt16 nPoolId
=0;
1684 if( SfxItemState::SET
== pOtherSet
->GetItemState( RES_PARATR_NUMRULE
,
1685 false, reinterpret_cast<const SfxPoolItem
**>(&pRule
) ) &&
1686 !rDoc
.FindNumRulePtr( pRule
->GetValue() ) &&
1687 USHRT_MAX
!= (nPoolId
= SwStyleNameMapper::GetPoolIdFromUIName ( pRule
->GetValue(),
1688 SwGetPoolIdFromName::NumRule
)) )
1689 rDoc
.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId
);
1693 SfxItemSetFixed
<RES_PAGEDESC
, RES_BREAK
> firstSet(rDoc
.GetAttrPool());
1694 if (pOtherSet
&& pOtherSet
->Count())
1695 { // actually only RES_BREAK is possible here...
1696 firstSet
.Put(*pOtherSet
);
1699 <RES_PARATR_BEGIN
, RES_PAGEDESC
,
1700 RES_BREAK
+1, RES_FRMATR_END
,
1701 XATTR_FILL_FIRST
, XATTR_FILL_LAST
+1> propsSet(rDoc
.GetAttrPool());
1702 if (pOtherSet
&& pOtherSet
->Count())
1704 propsSet
.Put(*pOtherSet
);
1707 if( !rRg
.HasMark() ) // no range
1715 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1717 SwTextNode
* pTextNd
= pNode
->GetTextNode();
1718 const SwIndex
& rSt
= pStt
->nContent
;
1719 sal_Int32 nMkPos
, nPtPos
= rSt
.GetIndex();
1720 const OUString
& rStr
= pTextNd
->GetText();
1722 // Special case: if the Cursor is located within a URL attribute, we take over it's area
1723 SwTextAttr
const*const pURLAttr(
1724 pTextNd
->GetTextAttrAt(rSt
.GetIndex(), RES_TXTATR_INETFMT
));
1725 if (pURLAttr
&& !pURLAttr
->GetINetFormat().GetValue().isEmpty())
1727 nMkPos
= pURLAttr
->GetStart();
1728 nPtPos
= *pURLAttr
->End();
1732 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
1733 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1734 pTextNd
->GetText(), nPtPos
,
1735 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1736 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
1739 if( aBndry
.startPos
< nPtPos
&& nPtPos
< aBndry
.endPos
)
1741 nMkPos
= aBndry
.startPos
;
1742 nPtPos
= aBndry
.endPos
;
1745 nPtPos
= nMkPos
= rSt
.GetIndex();
1748 // Remove the overriding attributes from the SwpHintsArray,
1749 // if the selection spans across the whole paragraph.
1750 // These attributes are inserted as FormatAttributes and
1751 // never override the TextAttributes!
1752 if( !(nFlags
& SetAttrMode::DONTREPLACE
) &&
1753 pTextNd
->HasHints() && !nMkPos
&& nPtPos
== rStr
.getLength())
1755 SwIndex
aSt( pTextNd
);
1758 // Save all attributes for the Undo.
1759 SwRegHistory
aRHst( *pTextNd
, pHistory
);
1760 pTextNd
->GetpSwpHints()->Register( &aRHst
);
1761 pTextNd
->RstTextAttr( aSt
, nPtPos
, 0, pCharSet
);
1762 if( pTextNd
->GetpSwpHints() )
1763 pTextNd
->GetpSwpHints()->DeRegister();
1766 pTextNd
->RstTextAttr( aSt
, nPtPos
, 0, pCharSet
);
1769 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
1771 SwPaM
aPam( *pNode
, nMkPos
, *pNode
, nPtPos
);
1774 pUndo
->SaveRedlineData( aPam
, false );
1776 lcl_SetRedlines(rDoc
, aPam
);
1779 // the SwRegHistory inserts the attribute into the TextNode!
1780 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1782 bRet
= history
.InsertItems( *pCharSet
, nMkPos
, nPtPos
, nFlags
, /*ppNewTextAttr*/nullptr )
1786 if( pOtherSet
&& pOtherSet
->Count() )
1788 // Need to check for unique item for DrawingLayer items of type NameOrIndex
1789 // and evtl. correct that item to ensure unique names for that type. This call may
1790 // modify/correct entries inside of the given SfxItemSet
1791 SfxItemSet
aTempLocalCopy(*pOtherSet
);
1793 rDoc
.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy
);
1794 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, aTempLocalCopy
, firstSet
, propsSet
, pLayout
) || bRet
;
1801 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet
&& pCharSet
->Count() )
1804 pUndo
->SaveRedlineData( rRg
, false );
1806 lcl_SetRedlines(rDoc
, rRg
);
1810 sal_uLong nNodes
= 0;
1812 SwNodeIndex
aSt( rDoc
.GetNodes() );
1813 SwNodeIndex
aEnd( rDoc
.GetNodes() );
1814 SwIndex
aCntEnd( pEnd
->nContent
);
1818 const sal_Int32 nLen
= pNode
->Len();
1819 if( pStt
->nNode
!= pEnd
->nNode
)
1820 aCntEnd
.Assign( pNode
, nLen
);
1822 if( pStt
->nContent
.GetIndex() != 0 || aCntEnd
.GetIndex() != nLen
)
1824 // the SwRegHistory inserts the attribute into the TextNode!
1825 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1827 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1828 bRet
= history
.InsertItems(*pCharSet
,
1829 pStt
->nContent
.GetIndex(), aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr)
1833 if( pOtherSet
&& pOtherSet
->Count() )
1835 bRet
= lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
) || bRet
;
1838 // Only selection in a Node.
1839 if( pStt
->nNode
== pEnd
->nNode
)
1845 aSt
.Assign( pStt
->nNode
.GetNode(), +1 );
1849 aCntEnd
= pEnd
->nContent
; // aEnd was changed!
1852 aSt
.Assign( pStt
->nNode
.GetNode(), +1 );
1854 // aSt points to the first full Node now
1857 * The selection spans more than one Node.
1859 if( pStt
->nNode
< pEnd
->nNode
)
1861 pNode
= pEnd
->nNode
.GetNode().GetContentNode();
1864 if( aCntEnd
.GetIndex() != pNode
->Len() )
1866 // the SwRegHistory inserts the attribute into the TextNode!
1867 if( pNode
->IsTextNode() && pCharSet
&& pCharSet
->Count() )
1869 SwRegHistory
history( pNode
, *pNode
, pHistory
);
1870 (void)history
.InsertItems(*pCharSet
,
1871 0, aCntEnd
.GetIndex(), nFlags
, /*ppNewTextAttr*/nullptr);
1874 if( pOtherSet
&& pOtherSet
->Count() )
1876 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
);
1883 aEnd
.Assign( pEnd
->nNode
.GetNode(), +1 );
1889 aEnd
.Assign( pEnd
->nNode
.GetNode(), +1 );
1891 // aEnd points BEHIND the last full node now
1893 /* Edit the fully selected Nodes. */
1894 // Reset all attributes from the set!
1895 if( pCharSet
&& pCharSet
->Count() && !( SetAttrMode::DONTREPLACE
& nFlags
) )
1897 ::sw::DocumentContentOperationsManager::ParaRstFormat
aPara(
1898 pStt
, pEnd
, pHistory
, pCharSet
, pLayout
);
1899 rDoc
.GetNodes().ForEach( aSt
, aEnd
, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr
, &aPara
);
1902 bool bCreateSwpHints
= pCharSet
&& (
1903 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_CHARFMT
, false ) ||
1904 SfxItemState::SET
== pCharSet
->GetItemState( RES_TXTATR_INETFMT
, false ) );
1906 for (SwNodeIndex current
= aSt
; current
< aEnd
; ++current
)
1908 SwTextNode
*const pTNd
= current
.GetNode().GetTextNode();
1912 if (pLayout
&& pLayout
->HasMergedParas()
1913 && pTNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
1914 { // not really sure what to do here, but applying to hidden
1915 continue; // nodes doesn't make sense...
1920 SwRegHistory
aRegH( pTNd
, *pTNd
, pHistory
);
1922 if (pCharSet
&& pCharSet
->Count())
1924 if (SwpHints
*pSwpHints
= bCreateSwpHints
? &pTNd
->GetOrCreateSwpHints()
1925 : pTNd
->GetpSwpHints())
1927 pSwpHints
->Register( &aRegH
);
1930 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1932 // re-fetch as it may be deleted by SetAttr
1933 if (SwpHints
*pSwpHints
= pTNd
->GetpSwpHints())
1934 pSwpHints
->DeRegister();
1939 if (pCharSet
&& pCharSet
->Count())
1940 pTNd
->SetAttr(*pCharSet
, 0, pTNd
->GetText().getLength(), nFlags
);
1945 if (pOtherSet
&& pOtherSet
->Count())
1947 for (; aSt
< aEnd
; ++aSt
)
1949 pNode
= aSt
.GetNode().GetContentNode();
1953 lcl_ApplyOtherSet(*pNode
, pHistory
, *pOtherSet
, firstSet
, propsSet
, pLayout
, &aSt
);
1959 return (nNodes
!= 0) || bRet
;
1968 bool IsFieldmarkOverlap(SwPaM
const& rPaM
)
1970 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
1971 sw::CalcBreaks(Breaks
, rPaM
);
1972 return !Breaks
.empty();
1976 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc
& i_rSwdoc
) : m_rDoc( i_rSwdoc
)
1981 * Checks if rStart..rEnd mark a range that makes sense to copy.
1983 * IsMoveToFly means the copy is a move to create a fly
1984 * and so existing flys at the edge must not be copied.
1986 static bool IsEmptyRange(const SwPosition
& rStart
, const SwPosition
& rEnd
,
1987 SwCopyFlags
const flags
)
1990 { // check if a fly anchored there would be copied - then copy...
1991 return !IsDestroyFrameAnchoredAtChar(rStart
, rStart
, rEnd
,
1992 (flags
& SwCopyFlags::IsMoveToFly
)
1993 ? DelContentType::WriterfilterHack
|DelContentType::AllMask
1994 : DelContentType::AllMask
);
1998 return rEnd
< rStart
;
2002 // Copy an area into this document or into another document
2004 DocumentContentOperationsManager::CopyRange( SwPaM
& rPam
, SwPosition
& rPos
,
2005 SwCopyFlags
const flags
) const
2007 const SwPosition
*pStt
= rPam
.Start(), *pEnd
= rPam
.End();
2009 SwDoc
& rDoc
= rPos
.nNode
.GetNode().GetDoc();
2010 bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
2012 // Catch if there's no copy to do
2013 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
))
2016 // Prevent copying into Flys that are anchored in the source range
2017 if (&rDoc
== &m_rDoc
&& (flags
& SwCopyFlags::CheckPosInFly
))
2019 // Correct the Start-/EndNode
2020 SwNodeOffset nStt
= pStt
->nNode
.GetIndex(),
2021 nEnd
= pEnd
->nNode
.GetIndex(),
2022 nDiff
= nEnd
- nStt
+1;
2023 SwNode
* pNd
= m_rDoc
.GetNodes()[ nStt
];
2024 if( pNd
->IsContentNode() && pStt
->nContent
.GetIndex() )
2029 if( (pNd
= m_rDoc
.GetNodes()[ nEnd
])->IsContentNode() &&
2030 static_cast<SwContentNode
*>(pNd
)->Len() != pEnd
->nContent
.GetIndex() )
2036 lcl_ChkFlyFly( rDoc
, nStt
, nEnd
, rPos
.nNode
.GetIndex() ) )
2042 SwPaM
* pRedlineRange
= nullptr;
2043 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2044 (!rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
2045 pRedlineRange
= new SwPaM( rPos
);
2047 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2051 if( &rDoc
!= &m_rDoc
)
2053 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
);
2055 else if( ! ( *pStt
<= rPos
&& rPos
< *pEnd
&&
2056 ( pStt
->nNode
!= pEnd
->nNode
||
2057 !pStt
->nNode
.GetNode().IsTextNode() )) )
2059 // Copy to a position outside of the area, or copy a single TextNode
2060 // Do an ordinary copy
2061 bRet
= CopyImpl(rPam
, rPos
, flags
& ~SwCopyFlags::CheckPosInFly
, pRedlineRange
);
2065 // Copy the range in itself
2066 assert(!"mst: this is assumed to be dead code");
2069 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
2072 if( rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2073 rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, *pRedlineRange
), true);
2075 rDoc
.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange
);
2076 delete pRedlineRange
;
2082 /// Delete a full Section of the NodeArray.
2083 /// The passed Node is located somewhere in the designated Section.
2084 void DocumentContentOperationsManager::DeleteSection( SwNode
*pNode
)
2086 assert(pNode
&& "Didn't pass a Node.");
2088 SwStartNode
* pSttNd
= pNode
->IsStartNode() ? static_cast<SwStartNode
*>(pNode
)
2089 : pNode
->StartOfSectionNode();
2090 SwNodeIndex
aSttIdx( *pSttNd
), aEndIdx( *pNode
->EndOfSectionNode() );
2092 // delete all Flys, Bookmarks, ...
2093 DelFlyInRange( aSttIdx
, aEndIdx
);
2094 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( *pSttNd
, true, RedlineType::Any
);
2095 DelBookmarks(aSttIdx
, aEndIdx
);
2098 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
2099 SwNodeIndex
aMvStt( aSttIdx
, 1 );
2100 SwDoc::CorrAbs( aMvStt
, aEndIdx
, SwPosition( aSttIdx
), true );
2103 m_rDoc
.GetNodes().DelNodes( aSttIdx
, aEndIdx
.GetIndex() - aSttIdx
.GetIndex() + 1 );
2106 void DocumentContentOperationsManager::DeleteDummyChar(
2107 SwPosition
const& rPos
, sal_Unicode
const cDummy
)
2109 SwPaM
aPam(rPos
, rPos
);
2110 ++aPam
.GetPoint()->nContent
;
2111 assert(aPam
.GetText().getLength() == 1 && aPam
.GetText()[0] == cDummy
);
2114 DeleteRangeImpl(aPam
);
2116 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2117 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2119 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2123 void DocumentContentOperationsManager::DeleteRange( SwPaM
& rPam
)
2125 lcl_DoWithBreaks( *this, rPam
, &DocumentContentOperationsManager::DeleteRangeImpl
);
2127 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
2128 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2130 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
2134 bool DocumentContentOperationsManager::DelFullPara( SwPaM
& rPam
)
2136 const SwPosition
&rStt
= *rPam
.Start(), &rEnd
= *rPam
.End();
2137 const SwNode
* pNd
= &rStt
.nNode
.GetNode();
2138 SwNodeOffset nSectDiff
= pNd
->StartOfSectionNode()->EndOfSectionIndex() -
2139 pNd
->StartOfSectionIndex();
2140 SwNodeOffset nNodeDiff
= rEnd
.nNode
.GetIndex() - rStt
.nNode
.GetIndex();
2142 if ( nSectDiff
-SwNodeOffset(2) <= nNodeDiff
|| m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
2143 /* #i9185# Prevent getting the node after the end node (see below) */
2144 rEnd
.nNode
.GetIndex() + 1 == m_rDoc
.GetNodes().Count() )
2150 SwPaM
temp(rPam
, nullptr);
2151 if (!temp
.HasMark())
2155 if (SwTextNode
*const pNode
= temp
.Start()->nNode
.GetNode().GetTextNode())
2156 { // rPam may not have nContent set but IsFieldmarkOverlap requires it
2157 pNode
->MakeStartIndex(&temp
.Start()->nContent
);
2159 if (SwTextNode
*const pNode
= temp
.End()->nNode
.GetNode().GetTextNode())
2161 pNode
->MakeEndIndex(&temp
.End()->nContent
);
2163 if (sw::mark::IsFieldmarkOverlap(temp
))
2164 { // a bit of a problem: we want to completely remove the nodes
2165 // but then how can the CH_TXT_ATR survive?
2170 // Move hard page breaks to the following Node.
2171 bool bSavePageBreak
= false, bSavePageDesc
= false;
2173 /* #i9185# This would lead to a segmentation fault if not caught above. */
2174 SwNodeOffset nNextNd
= rEnd
.nNode
.GetIndex() + 1;
2175 SwTableNode
*const pTableNd
= m_rDoc
.GetNodes()[ nNextNd
]->GetTableNode();
2177 if( pTableNd
&& pNd
->IsContentNode() )
2179 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
2182 const SfxPoolItem
*pItem
;
2183 const SfxItemSet
* pSet
= static_cast<const SwContentNode
*>(pNd
)->GetpSwAttrSet();
2184 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_PAGEDESC
,
2187 pTableFormat
->SetFormatAttr( *pItem
);
2188 bSavePageDesc
= true;
2191 if( pSet
&& SfxItemState::SET
== pSet
->GetItemState( RES_BREAK
,
2194 pTableFormat
->SetFormatAttr( *pItem
);
2195 bSavePageBreak
= true;
2200 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2203 if( !rPam
.HasMark() )
2205 else if( rPam
.GetPoint() == &rStt
)
2207 rPam
.GetPoint()->nNode
++;
2209 SwContentNode
*pTmpNode
= rPam
.GetPoint()->nNode
.GetNode().GetContentNode();
2210 rPam
.GetPoint()->nContent
.Assign( pTmpNode
, 0 );
2211 bool bGoNext
= (nullptr == pTmpNode
);
2212 pTmpNode
= rPam
.GetMark()->nNode
.GetNode().GetContentNode();
2213 rPam
.GetMark()->nContent
.Assign( pTmpNode
, 0 );
2215 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2217 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
2219 SwPosition
aTmpPos( *aDelPam
.GetPoint() );
2222 pTmpNode
= m_rDoc
.GetNodes().GoNext( &aTmpPos
.nNode
);
2223 aTmpPos
.nContent
.Assign( pTmpNode
, 0 );
2225 ::PaMCorrAbs( aDelPam
, aTmpPos
);
2228 std::unique_ptr
<SwUndoDelete
> pUndo(new SwUndoDelete( aDelPam
, true ));
2230 *rPam
.GetPoint() = *aDelPam
.GetPoint();
2231 pUndo
->SetPgBrkFlags( bSavePageBreak
, bSavePageDesc
);
2232 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2237 SwNodeRange
aRg( rStt
.nNode
, rEnd
.nNode
);
2238 rPam
.Normalize(false);
2240 // Try to move past the End
2241 if( !rPam
.Move( fnMoveForward
, GoInNode
) )
2243 // Fair enough, at the Beginning then
2245 if( !rPam
.Move( fnMoveBackward
, GoInNode
))
2247 SAL_WARN("sw.core", "DelFullPara: no more Nodes");
2251 // move bookmarks, redlines etc.
2252 if (aRg
.aStart
== aRg
.aEnd
) // only first CorrAbs variant handles this
2254 m_rDoc
.CorrAbs( aRg
.aStart
, *rPam
.GetPoint(), 0, true );
2258 SwDoc::CorrAbs( aRg
.aStart
, aRg
.aEnd
, *rPam
.GetPoint(), true );
2261 // What's with Flys?
2263 // If there are FlyFrames left, delete these too
2264 for( size_t n
= 0; n
< m_rDoc
.GetSpzFrameFormats()->size(); ++n
)
2266 SwFrameFormat
* pFly
= (*m_rDoc
.GetSpzFrameFormats())[n
];
2267 const SwFormatAnchor
* pAnchor
= &pFly
->GetAnchor();
2268 SwPosition
const*const pAPos
= pAnchor
->GetContentAnchor();
2270 ((RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) ||
2271 (RndStdIds::FLY_AT_CHAR
== pAnchor
->GetAnchorId())) &&
2272 // note: here use <= not < like in
2273 // IsDestroyFrameAnchoredAtChar() because of the increment
2274 // of rPam in the bDoesUndo path above!
2275 aRg
.aStart
<= pAPos
->nNode
&& pAPos
->nNode
<= aRg
.aEnd
)
2277 m_rDoc
.getIDocumentLayoutAccess().DelLayoutFormat( pFly
);
2284 m_rDoc
.GetNodes().Delete( aRg
.aStart
, nNodeDiff
+1 );
2286 m_rDoc
.getIDocumentState().SetModified();
2291 // #i100466# Add handling of new optional parameter <bForceJoinNext>
2292 bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM
& rPam
,
2293 const bool bForceJoinNext
)
2295 if ( lcl_StrLenOverflow( rPam
) )
2298 bool const ret
= lcl_DoWithBreaks( *this, rPam
, (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
2299 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
2300 : &DocumentContentOperationsManager::DeleteAndJoinImpl
,
2306 // It seems that this is mostly used by SwDoc internals; the only
2307 // way to call this from the outside seems to be the special case in
2308 // SwDoc::CopyRange (but I have not managed to actually hit that case).
2309 bool DocumentContentOperationsManager::MoveRange( SwPaM
& rPaM
, SwPosition
& rPos
, SwMoveFlags eMvFlags
)
2311 // nothing moved: return
2312 const SwPosition
*pStt
= rPaM
.Start(), *pEnd
= rPaM
.End();
2313 if( !rPaM
.HasMark() || *pStt
>= *pEnd
|| (*pStt
<= rPos
&& rPos
< *pEnd
))
2316 assert(!sw::mark::IsFieldmarkOverlap(rPaM
)); // probably an invalid redline was created?
2318 // Save the paragraph anchored Flys, so that they can be moved.
2319 SaveFlyArr aSaveFlyArr
;
2320 SaveFlyInRange( rPaM
, rPos
, aSaveFlyArr
, bool( SwMoveFlags::ALLFLYS
& eMvFlags
) );
2322 // save redlines (if DOC_MOVEREDLINES is used)
2323 SaveRedlines_t aSaveRedl
;
2324 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2326 lcl_SaveRedlines( rPaM
, aSaveRedl
);
2328 // #i17764# unfortunately, code below relies on undos being
2329 // in a particular order, and presence of bookmarks
2330 // will change this order. Hence, we delete bookmarks
2331 // here without undo.
2332 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
2341 bool bUpdateFootnote
= false;
2342 SwFootnoteIdxs aTmpFntIdx
;
2344 std::unique_ptr
<SwUndoMove
> pUndoMove
;
2345 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2347 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
2348 pUndoMove
.reset(new SwUndoMove( rPaM
, rPos
));
2349 pUndoMove
->SetMoveRedlines( eMvFlags
== SwMoveFlags::REDLINES
);
2353 bUpdateFootnote
= lcl_SaveFootnote( pStt
->nNode
, pEnd
->nNode
, rPos
.nNode
,
2354 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
,
2355 &pStt
->nContent
, &pEnd
->nContent
);
2358 bool bSplit
= false;
2359 SwPaM
aSavePam( rPos
, rPos
);
2361 // Move the SPoint to the beginning of the range
2362 if( rPaM
.GetPoint() == pEnd
)
2365 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
2366 SwTextNode
* pSrcNd
= rPaM
.GetPoint()->nNode
.GetNode().GetTextNode();
2367 bool bCorrSavePam
= pSrcNd
&& pStt
->nNode
!= pEnd
->nNode
;
2369 // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
2370 // However, this does not update the cursor. So we create a TextNode to keep
2371 // updating the indices. After the Move the Node is optionally deleted.
2372 SwTextNode
* pTNd
= rPos
.nNode
.GetNode().GetTextNode();
2373 if( pTNd
&& rPaM
.GetPoint()->nNode
!= rPaM
.GetMark()->nNode
&&
2374 ( rPos
.nContent
.GetIndex() || ( pTNd
->Len() && bCorrSavePam
)) )
2377 const sal_Int32 nMkContent
= rPaM
.GetMark()->nContent
.GetIndex();
2379 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
2380 pContentStore
->Save( m_rDoc
, rPos
.nNode
.GetIndex(), rPos
.nContent
.GetIndex(), true );
2382 SwTextNode
* pOrigNode
= pTNd
;
2383 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2384 *aSavePam
.GetPoint() == rPos
);
2385 assert(aSavePam
.GetPoint()->nContent
.GetIdxReg() == pOrigNode
);
2386 assert(aSavePam
.GetPoint()->nNode
== rPos
.nNode
.GetIndex());
2387 assert(rPos
.nNode
.GetIndex() == pOrigNode
->GetIndex());
2389 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool)> restoreFunc(
2390 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool)
2392 if (!pContentStore
->Empty())
2394 pContentStore
->Restore(m_rDoc
, pOrigNode
->GetIndex()-SwNodeOffset(1), 0, true, false, eMode
);
2397 pTNd
= pTNd
->SplitContentNode(rPos
, &restoreFunc
)->GetTextNode();
2399 //A new node was inserted before the orig pTNd and the content up to
2400 //rPos moved into it. The old node is returned with the remainder
2401 //of the content in it.
2403 //aSavePam was created with rPos, it continues to point to the
2404 //old node, but with the *original* content index into the node.
2405 //Seeing as all the orignode content before that index has
2406 //been removed, the new index into the original node should now be set
2407 //to 0 and the content index of rPos should also be adapted to the
2409 assert(*aSavePam
.GetPoint() == *aSavePam
.GetMark() &&
2410 *aSavePam
.GetPoint() == rPos
);
2411 assert(aSavePam
.GetPoint()->nContent
.GetIdxReg() == pOrigNode
);
2412 assert(aSavePam
.GetPoint()->nNode
== rPos
.nNode
.GetIndex());
2413 assert(rPos
.nNode
.GetIndex() == pOrigNode
->GetIndex());
2414 aSavePam
.GetPoint()->nContent
.Assign(pOrigNode
, 0);
2415 rPos
= *aSavePam
.GetMark() = *aSavePam
.GetPoint();
2418 if( rPos
.nNode
== rPaM
.GetMark()->nNode
)
2420 rPaM
.GetMark()->nNode
= rPos
.nNode
.GetIndex()-SwNodeOffset(1);
2421 rPaM
.GetMark()->nContent
.Assign( pTNd
, nMkContent
);
2425 // Put back the Pam by one "content"; so that it's always outside of
2426 // the manipulated range.
2427 // tdf#99692 don't Move() back if that would end up in another node
2428 // because moving backward is not necessarily the inverse of forward then.
2429 // (but do Move() back if we have split the node)
2430 const bool bNullContent
= !bSplit
&& aSavePam
.GetPoint()->nContent
== 0;
2433 aSavePam
.GetPoint()->nNode
--;
2434 aSavePam
.GetPoint()->nContent
.Assign(aSavePam
.GetContentNode(), 0);
2438 bool const success(aSavePam
.Move(fnMoveBackward
, GoInContent
));
2443 // Copy all Bookmarks that are within the Move range into an array,
2444 // that saves the position as an offset.
2445 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2453 // If there is no range anymore due to the above deletions (e.g. the
2454 // footnotes got deleted), it's still a valid Move!
2455 if( *rPaM
.GetPoint() != *rPaM
.GetMark() )
2457 // now do the actual move
2458 m_rDoc
.GetNodes().MoveRange( rPaM
, rPos
, m_rDoc
.GetNodes() );
2460 // after a MoveRange() the Mark is deleted
2461 if ( rPaM
.HasMark() ) // => no Move occurred!
2469 OSL_ENSURE( *aSavePam
.GetMark() == rPos
||
2470 ( aSavePam
.GetMark()->nNode
.GetNode().GetContentNode() == nullptr ),
2471 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
2472 *aSavePam
.GetMark() = rPos
;
2474 rPaM
.SetMark(); // create a Sel. around the new range
2475 pTNd
= aSavePam
.GetNode().GetTextNode();
2476 assert(!m_rDoc
.GetIDocumentUndoRedo().DoesUndo());
2477 bool bRemove
= true;
2478 // Do two Nodes have to be joined at the SavePam?
2481 if (pTNd
->CanJoinNext())
2483 // Always join next, because <pTNd> has to stay as it is.
2484 // A join previous from its next would more or less delete <pTNd>
2491 aSavePam
.GetPoint()->nNode
++;
2492 aSavePam
.GetPoint()->nContent
.Assign( aSavePam
.GetContentNode(), 0 );
2494 else if (bRemove
) // No move forward after joining with next paragraph
2496 aSavePam
.Move( fnMoveForward
, GoInContent
);
2499 // Insert the Bookmarks back into the Document.
2500 *rPaM
.GetMark() = *aSavePam
.Start();
2501 for(auto& rBkmk
: aSaveBkmks
)
2504 rPaM
.GetMark()->nNode
,
2505 &rPaM
.GetMark()->nContent
);
2506 *rPaM
.GetPoint() = *aSavePam
.End();
2508 // Move the Flys to the new position.
2509 // note: rPos is at the end here; can't really tell flys that used to be
2510 // at the start of rPam from flys that used to be at the end of rPam
2511 // unfortunately, so some of them are going to end up with wrong anchor...
2512 RestFlyInRange( aSaveFlyArr
, *rPaM
.Start(), &(rPos
.nNode
) );
2514 // restore redlines (if DOC_MOVEREDLINES is used)
2515 if( !aSaveRedl
.empty() )
2517 lcl_RestoreRedlines( m_rDoc
, *aSavePam
.Start(), aSaveRedl
);
2520 if( bUpdateFootnote
)
2522 if( !aTmpFntIdx
.empty() )
2524 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2528 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2531 m_rDoc
.getIDocumentState().SetModified();
2535 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange
& rRange
, SwNodeIndex
& rPos
,
2536 SwMoveFlags eMvFlags
)
2538 // Moves all Nodes to the new position.
2539 // Bookmarks are moved too (currently without Undo support).
2541 // If footnotes are being moved to the special section, remove them now.
2543 // Or else delete the Frames for all footnotes that are being moved
2544 // and have it rebuild after the Move (footnotes can change pages).
2545 // Additionally we have to correct the FootnoteIdx array's sorting.
2546 bool bUpdateFootnote
= false;
2547 SwFootnoteIdxs aTmpFntIdx
;
2549 std::unique_ptr
<SwUndoMove
> pUndo
;
2550 if ((SwMoveFlags::CREATEUNDOOBJ
& eMvFlags
) && m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2552 pUndo
.reset(new SwUndoMove( m_rDoc
, rRange
, rPos
));
2556 bUpdateFootnote
= lcl_SaveFootnote( rRange
.aStart
, rRange
.aEnd
, rPos
,
2557 m_rDoc
.GetFootnoteIdxs(), aTmpFntIdx
);
2560 SaveRedlines_t aSaveRedl
;
2561 std::vector
<SwRangeRedline
*> aSavRedlInsPosArr
;
2562 if( SwMoveFlags::REDLINES
& eMvFlags
&& !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
2564 lcl_SaveRedlines( rRange
, aSaveRedl
);
2566 // Find all RedLines that end at the InsPos.
2567 // These have to be moved back to the "old" position after the Move.
2568 SwRedlineTable::size_type nRedlPos
= m_rDoc
.getIDocumentRedlineAccess().GetRedlinePos( rPos
.GetNode(), RedlineType::Any
);
2569 if( SwRedlineTable::npos
!= nRedlPos
)
2571 const SwPosition
*pRStt
, *pREnd
;
2573 SwRangeRedline
* pTmp
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos
];
2574 pRStt
= pTmp
->Start();
2575 pREnd
= pTmp
->End();
2576 if( pREnd
->nNode
== rPos
&& pRStt
->nNode
< rPos
)
2578 aSavRedlInsPosArr
.push_back( pTmp
);
2580 } while( pRStt
->nNode
< rPos
&& ++nRedlPos
< m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size());
2584 // Copy all Bookmarks that are within the Move range into an array
2585 // that stores all references to positions as an offset.
2586 // The final mapping happens after the Move.
2587 std::vector
< ::sw::mark::SaveBookmark
> aSaveBkmks
;
2588 DelBookmarks(rRange
.aStart
, rRange
.aEnd
, &aSaveBkmks
);
2590 // Save the paragraph-bound Flys, so that they can be moved.
2591 SaveFlyArr aSaveFlyArr
;
2592 if( !m_rDoc
.GetSpzFrameFormats()->empty() )
2593 SaveFlyInRange( rRange
, aSaveFlyArr
);
2595 // Set it to before the Position, so that it cannot be moved further.
2596 SwNodeIndex
aIdx( rPos
, -1 );
2598 std::unique_ptr
<SwNodeIndex
> pSaveInsPos
;
2600 pSaveInsPos
.reset(new SwNodeIndex( rRange
.aStart
, -1 ));
2603 bool bNoDelFrames
= bool(SwMoveFlags::NO_DELFRMS
& eMvFlags
);
2604 if( m_rDoc
.GetNodes().MoveNodes( rRange
, m_rDoc
.GetNodes(), rPos
, !bNoDelFrames
) )
2606 ++aIdx
; // again back to old position
2612 aIdx
= rRange
.aStart
;
2616 // move the Flys to the new position
2617 if( !aSaveFlyArr
.empty() )
2619 SwPosition
const tmp(aIdx
);
2620 RestFlyInRange(aSaveFlyArr
, tmp
, nullptr);
2623 // Add the Bookmarks back to the Document
2624 for(auto& rBkmk
: aSaveBkmks
)
2625 rBkmk
.SetInDoc(&m_rDoc
, aIdx
);
2627 if( !aSavRedlInsPosArr
.empty() )
2629 SwNode
* pNewNd
= &aIdx
.GetNode();
2630 for(SwRangeRedline
* pTmp
: aSavRedlInsPosArr
)
2632 if( m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp
) )
2634 SwPosition
* pEnd
= pTmp
->End();
2636 pEnd
->nContent
.Assign( pNewNd
->GetContentNode(), 0 );
2641 if( !aSaveRedl
.empty() )
2642 lcl_RestoreRedlines( m_rDoc
, aIdx
.GetIndex(), aSaveRedl
);
2646 pUndo
->SetDestRange( aIdx
, rPos
, *pSaveInsPos
);
2647 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
2650 pSaveInsPos
.reset();
2652 if( bUpdateFootnote
)
2654 if( !aTmpFntIdx
.empty() )
2656 m_rDoc
.GetFootnoteIdxs().insert( aTmpFntIdx
);
2660 m_rDoc
.GetFootnoteIdxs().UpdateAllFootnote();
2663 m_rDoc
.getIDocumentState().SetModified();
2667 void DocumentContentOperationsManager::MoveAndJoin( SwPaM
& rPaM
, SwPosition
& rPos
)
2669 SwNodeIndex
aIdx( rPaM
.Start()->nNode
);
2670 bool bJoinText
= aIdx
.GetNode().IsTextNode();
2671 bool bOneNode
= rPaM
.GetPoint()->nNode
== rPaM
.GetMark()->nNode
;
2672 aIdx
--; // in front of the move area!
2674 bool bRet
= MoveRange( rPaM
, rPos
, SwMoveFlags::DEFAULT
);
2675 if( !bRet
|| bOneNode
)
2680 SwTextNode
* pTextNd
= aIdx
.GetNode().GetTextNode();
2681 SwNodeIndex
aNxtIdx( aIdx
);
2682 if( pTextNd
&& pTextNd
->CanJoinNext( &aNxtIdx
) )
2684 { // Block so SwIndex into node is deleted before Join
2685 m_rDoc
.CorrRel( aNxtIdx
, SwPosition( aIdx
, SwIndex(pTextNd
,
2686 pTextNd
->GetText().getLength()) ), 0, true );
2688 pTextNd
->JoinNext();
2692 // Overwrite only uses the point of the PaM, the mark is ignored; characters
2693 // are replaced from point until the end of the node; at the end of the node,
2694 // characters are inserted.
2695 bool DocumentContentOperationsManager::Overwrite( const SwPaM
&rRg
, const OUString
&rStr
)
2697 assert(rStr
.getLength());
2698 SwPosition
& rPt
= *const_cast<SwPosition
*>(rRg
.GetPoint());
2699 if( m_rDoc
.GetAutoCorrExceptWord() ) // Add to AutoCorrect
2701 if( 1 == rStr
.getLength() )
2702 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPt
, rStr
[ 0 ] );
2703 m_rDoc
.DeleteAutoCorrExceptWord();
2706 SwTextNode
*pNode
= rPt
.nNode
.GetNode().GetTextNode();
2707 if (!pNode
|| rStr
.getLength() > pNode
->GetSpaceLeft()) // worst case: no erase
2712 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2714 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
2717 const size_t nOldAttrCnt
= pNode
->GetpSwpHints()
2718 ? pNode
->GetpSwpHints()->Count() : 0;
2719 SwDataChanged
aTmp( rRg
);
2720 SwIndex
& rIdx
= rPt
.nContent
;
2721 sal_Int32
const nActualStart(rIdx
.GetIndex());
2722 sal_Int32 nStart
= 0;
2724 bool bOldExpFlg
= pNode
->IsIgnoreDontExpand();
2725 pNode
->SetIgnoreDontExpand( true );
2727 for( sal_Int32 nCnt
= 0; nCnt
< rStr
.getLength(); ++nCnt
)
2729 // start behind the characters (to fix the attributes!)
2730 nStart
= rIdx
.GetIndex();
2731 if (nStart
< pNode
->GetText().getLength())
2733 lcl_SkipAttr( pNode
, rIdx
, nStart
);
2735 sal_Unicode c
= rStr
[ nCnt
];
2736 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2738 bool bMerged(false);
2739 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2741 SwUndo
*const pUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2742 SwUndoOverwrite
*const pUndoOW(
2743 dynamic_cast<SwUndoOverwrite
*>(pUndo
) );
2746 // if CanGrouping() returns true it's already merged
2747 bMerged
= pUndoOW
->CanGrouping(m_rDoc
, rPt
, c
);
2752 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2753 std::make_unique
<SwUndoOverwrite
>(m_rDoc
, rPt
, c
) );
2758 // start behind the characters (to fix the attributes!)
2759 if (nStart
< pNode
->GetText().getLength())
2761 pNode
->InsertText( OUString(c
), rIdx
, SwInsertFlags::EMPTYEXPAND
);
2762 if( nStart
+1 < rIdx
.GetIndex() )
2765 pNode
->EraseText( rIdx
, 1 );
2770 pNode
->SetIgnoreDontExpand( bOldExpFlg
);
2772 const size_t nNewAttrCnt
= pNode
->GetpSwpHints()
2773 ? pNode
->GetpSwpHints()->Count() : 0;
2774 if( nOldAttrCnt
!= nNewAttrCnt
)
2776 const SwUpdateAttr
aHint(0,0,0);
2777 pNode
->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint
, &aHint
));
2780 if (!m_rDoc
.GetIDocumentUndoRedo().DoesUndo() &&
2781 !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
2783 SwPaM
aPam(rPt
.nNode
, nActualStart
, rPt
.nNode
, rPt
.nContent
.GetIndex());
2784 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aPam
, true, RedlineType::Any
);
2786 else if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2788 // FIXME: this redline is WRONG: there is no DELETE, and the skipped
2789 // characters are also included in aPam
2790 SwPaM
aPam(rPt
.nNode
, nActualStart
, rPt
.nNode
, rPt
.nContent
.GetIndex());
2791 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2794 m_rDoc
.getIDocumentState().SetModified();
2798 bool DocumentContentOperationsManager::InsertString( const SwPaM
&rRg
, const OUString
&rStr
,
2799 const SwInsertFlags nInsertMode
)
2801 // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
2802 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2804 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
2805 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg
);
2806 if (eOld
!= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags())
2807 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
2810 // fetching DoesUndo is surprisingly expensive
2811 bool bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
2813 m_rDoc
.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
2815 const SwPosition
& rPos
= *rRg
.GetPoint();
2817 if( m_rDoc
.GetAutoCorrExceptWord() ) // add to auto correction
2819 if( 1 == rStr
.getLength() && m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() )
2821 m_rDoc
.GetAutoCorrExceptWord()->CheckChar( rPos
, rStr
[ 0 ] );
2823 m_rDoc
.DeleteAutoCorrExceptWord();
2826 SwTextNode
*const pNode
= rPos
.nNode
.GetNode().GetTextNode();
2830 SwDataChanged
aTmp( rRg
);
2832 if (!bDoesUndo
|| !m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
2834 OUString
const ins(pNode
->InsertText(rStr
, rPos
.nContent
, nInsertMode
));
2837 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
2838 std::make_unique
<SwUndoInsert
>(rPos
.nNode
,
2839 rPos
.nContent
.GetIndex(), ins
.getLength(), nInsertMode
));
2843 { // if Undo and grouping is enabled, everything changes!
2844 SwUndoInsert
* pUndo
= nullptr;
2846 // don't group the start if hints at the start should be expanded
2847 if (!(nInsertMode
& SwInsertFlags::FORCEHINTEXPAND
))
2849 SwUndo
*const pLastUndo
= m_rDoc
.GetUndoManager().GetLastUndo();
2850 SwUndoInsert
*const pUndoInsert(
2851 dynamic_cast<SwUndoInsert
*>(pLastUndo
) );
2852 if (pUndoInsert
&& pUndoInsert
->CanGrouping(rPos
))
2854 pUndo
= pUndoInsert
;
2858 CharClass
const& rCC
= GetAppCharClass();
2859 sal_Int32 nInsPos
= rPos
.nContent
.GetIndex();
2863 pUndo
= new SwUndoInsert( rPos
.nNode
, nInsPos
, 0, nInsertMode
,
2864 !rCC
.isLetterNumeric( rStr
, 0 ) );
2865 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2868 OUString
const ins(pNode
->InsertText(rStr
, rPos
.nContent
, nInsertMode
));
2870 for (sal_Int32 i
= 0; i
< ins
.getLength(); ++i
)
2873 // if CanGrouping() returns true, everything has already been done
2874 if (!pUndo
->CanGrouping(ins
[i
]))
2876 pUndo
= new SwUndoInsert(rPos
.nNode
, nInsPos
, 1, nInsertMode
,
2877 !rCC
.isLetterNumeric(ins
, i
));
2878 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
2883 // To-Do - add 'SwExtraRedlineTable' also ?
2884 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
2886 SwPaM
aPam( rPos
.nNode
, aTmp
.GetContent(),
2887 rPos
.nNode
, rPos
.nContent
.GetIndex());
2888 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
2890 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
2891 new SwRangeRedline( RedlineType::Insert
, aPam
), true);
2895 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
2899 m_rDoc
.getIDocumentState().SetModified();
2903 void DocumentContentOperationsManager::SetIME(bool bIME
)
2908 bool DocumentContentOperationsManager::GetIME() const
2913 void DocumentContentOperationsManager::TransliterateText(
2915 utl::TransliterationWrapper
& rTrans
)
2917 std::unique_ptr
<SwUndoTransliterate
> pUndo
;
2918 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
2919 pUndo
.reset(new SwUndoTransliterate( rPaM
, rTrans
));
2921 const SwPosition
* pStt
= rPaM
.Start(),
2922 * pEnd
= rPaM
.End();
2923 SwNodeOffset nSttNd
= pStt
->nNode
.GetIndex(),
2924 nEndNd
= pEnd
->nNode
.GetIndex();
2925 sal_Int32 nSttCnt
= pStt
->nContent
.GetIndex();
2926 sal_Int32 nEndCnt
= pEnd
->nContent
.GetIndex();
2928 SwTextNode
* pTNd
= pStt
->nNode
.GetNode().GetTextNode();
2929 if( pStt
== pEnd
&& pTNd
) // no selection?
2931 // set current word as 'area of effect'
2933 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
2934 Boundary aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
2935 pTNd
->GetText(), nSttCnt
,
2936 g_pBreakIt
->GetLocale( pTNd
->GetLang( nSttCnt
) ),
2937 WordType::ANY_WORD
/*ANYWORD_IGNOREWHITESPACES*/,
2940 if( aBndry
.startPos
< nSttCnt
&& nSttCnt
< aBndry
.endPos
)
2942 nSttCnt
= aBndry
.startPos
;
2943 nEndCnt
= aBndry
.endPos
;
2947 bool bUseRedlining
= m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn();
2948 // as a workaround for a known performance problem, switch off redlining
2949 // to avoid freezing, if transliteration could result too many redlines
2950 if ( bUseRedlining
)
2952 const sal_uLong nMaxRedlines
= 500;
2953 const bool bIsTitleCase
= rTrans
.getType() == TransliterationFlags::TITLE_CASE
;
2954 sal_uLong nAffectedNodes
= 0;
2955 sal_uLong nAffectedChars
= nEndCnt
;
2956 SwNodeIndex
aIdx( pStt
->nNode
);
2957 for( ; aIdx
.GetIndex() <= nEndNd
; ++aIdx
)
2959 SwTextNode
* pAffectedNode
= aIdx
.GetNode().GetTextNode();
2961 // don't count not text nodes or empty text nodes
2962 if( !pAffectedNode
|| pAffectedNode
->GetText().isEmpty() )
2967 // count characters of the node (the last - maybe partially
2968 // selected - node was counted at initialization of nAffectedChars)
2969 if( aIdx
.GetIndex() < nEndNd
)
2970 nAffectedChars
+= pAffectedNode
->GetText().getLength();
2972 // transliteration creates n redlines for n nodes, except in the
2973 // case of title case, where it creates n redlines for n words
2974 if( nAffectedNodes
> nMaxRedlines
||
2975 // estimate word count based on the character count, where
2976 // 6 = average English word length is ~5 letters + space
2977 ( bIsTitleCase
&& (nAffectedChars
- nSttCnt
)/6 > nMaxRedlines
) )
2979 bUseRedlining
= false;
2985 if( nSttNd
!= nEndNd
) // is more than one text node involved?
2987 // iterate over all effected text nodes, the first and the last one
2988 // may be incomplete because the selection starts and/or ends there
2990 SwNodeIndex
aIdx( pStt
->nNode
);
2996 pTNd
->TransliterateText(
2997 rTrans
, nSttCnt
, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3001 for( ; aIdx
.GetIndex() < nEndNd
; ++aIdx
)
3003 pTNd
= aIdx
.GetNode().GetTextNode();
3006 pTNd
->TransliterateText(
3007 rTrans
, 0, pTNd
->GetText().getLength(), pUndo
.get(), bUseRedlining
);
3011 if( nEndCnt
&& nullptr != ( pTNd
= pEnd
->nNode
.GetNode().GetTextNode() ))
3013 pTNd
->TransliterateText( rTrans
, 0, nEndCnt
, pUndo
.get(), bUseRedlining
);
3016 else if( pTNd
&& nSttCnt
< nEndCnt
)
3018 pTNd
->TransliterateText( rTrans
, nSttCnt
, nEndCnt
, pUndo
.get(), bUseRedlining
);
3020 if( pUndo
&& pUndo
->HasData() )
3022 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
3024 m_rDoc
.getIDocumentState().SetModified();
3027 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertGraphic(
3029 const OUString
& rGrfName
,
3030 const OUString
& rFltName
,
3031 const Graphic
* pGraphic
,
3032 const SfxItemSet
* pFlyAttrSet
,
3033 const SfxItemSet
* pGrfAttrSet
,
3034 SwFrameFormat
* pFrameFormat
)
3037 pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC
);
3038 SwGrfNode
* pSwGrfNode
= SwNodes::MakeGrfNode(
3039 SwNodeIndex( m_rDoc
.GetNodes().GetEndOfAutotext() ),
3040 rGrfName
, rFltName
, pGraphic
,
3041 m_rDoc
.GetDfltGrfFormatColl() );
3042 SwFlyFrameFormat
* pSwFlyFrameFormat
= InsNoTextNode( *rRg
.GetPoint(), pSwGrfNode
,
3043 pFlyAttrSet
, pGrfAttrSet
, pFrameFormat
);
3044 return pSwFlyFrameFormat
;
3047 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertGraphicObject(
3048 const SwPaM
&rRg
, const GraphicObject
& rGrfObj
,
3049 const SfxItemSet
* pFlyAttrSet
,
3050 const SfxItemSet
* pGrfAttrSet
)
3052 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC
);
3053 SwGrfNode
* pSwGrfNode
= SwNodes::MakeGrfNode(
3054 SwNodeIndex( m_rDoc
.GetNodes().GetEndOfAutotext() ),
3055 rGrfObj
, m_rDoc
.GetDfltGrfFormatColl() );
3056 SwFlyFrameFormat
* pSwFlyFrameFormat
= InsNoTextNode( *rRg
.GetPoint(), pSwGrfNode
,
3057 pFlyAttrSet
, pGrfAttrSet
, pFrameFormat
);
3058 return pSwFlyFrameFormat
;
3061 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertEmbObject(
3062 const SwPaM
&rRg
, const svt::EmbeddedObjectRef
& xObj
,
3063 SfxItemSet
* pFlyAttrSet
)
3065 sal_uInt16 nId
= RES_POOLFRM_OLE
;
3068 SvGlobalName
aClassName( xObj
->getClassID() );
3069 if (SotExchange::IsMath(aClassName
))
3071 nId
= RES_POOLFRM_FORMEL
;
3075 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId
);
3077 return InsNoTextNode( *rRg
.GetPoint(), m_rDoc
.GetNodes().MakeOLENode(
3078 SwNodeIndex( m_rDoc
.GetNodes().GetEndOfAutotext() ),
3080 m_rDoc
.GetDfltGrfFormatColl() ),
3081 pFlyAttrSet
, nullptr,
3085 SwFlyFrameFormat
* DocumentContentOperationsManager::InsertOLE(const SwPaM
&rRg
, const OUString
& rObjName
,
3087 const SfxItemSet
* pFlyAttrSet
,
3088 const SfxItemSet
* pGrfAttrSet
)
3090 SwFrameFormat
* pFrameFormat
= m_rDoc
.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE
);
3092 return InsNoTextNode( *rRg
.GetPoint(),
3093 m_rDoc
.GetNodes().MakeOLENode(
3094 SwNodeIndex( m_rDoc
.GetNodes().GetEndOfAutotext() ),
3097 m_rDoc
.GetDfltGrfFormatColl(),
3099 pFlyAttrSet
, pGrfAttrSet
,
3103 void DocumentContentOperationsManager::ReRead( SwPaM
& rPam
, const OUString
& rGrfName
,
3104 const OUString
& rFltName
, const Graphic
* pGraphic
)
3107 if( !(( !rPam
.HasMark()
3108 || rPam
.GetPoint()->nNode
.GetIndex() == rPam
.GetMark()->nNode
.GetIndex() )
3109 && nullptr != ( pGrfNd
= rPam
.GetPoint()->nNode
.GetNode().GetGrfNode() )) )
3112 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3114 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoReRead
>(rPam
, *pGrfNd
));
3117 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
3118 if( MirrorGraph::Dont
!= pGrfNd
->GetSwAttrSet().
3119 GetMirrorGrf().GetValue() )
3120 pGrfNd
->SetAttr( SwMirrorGrf() );
3122 pGrfNd
->ReRead( rGrfName
, rFltName
, pGraphic
);
3123 m_rDoc
.getIDocumentState().SetModified();
3126 // Insert drawing object, which has to be already inserted in the DrawModel
3127 SwDrawFrameFormat
* DocumentContentOperationsManager::InsertDrawObj(
3129 SdrObject
& rDrawObj
,
3130 const SfxItemSet
& rFlyAttrSet
)
3132 SwDrawFrameFormat
* pFormat
= m_rDoc
.MakeDrawFrameFormat( OUString(), m_rDoc
.GetDfltFrameFormat() );
3134 const SwFormatAnchor
* pAnchor
= nullptr;
3135 rFlyAttrSet
.GetItemState( RES_ANCHOR
, false, reinterpret_cast<const SfxPoolItem
**>(&pAnchor
) );
3136 pFormat
->SetFormatAttr( rFlyAttrSet
);
3138 // Didn't set the Anchor yet?
3139 // DrawObjecte must never end up in the Header/Footer!
3140 RndStdIds eAnchorId
= pAnchor
!= nullptr ? pAnchor
->GetAnchorId() : pFormat
->GetAnchor().GetAnchorId();
3141 const bool bIsAtContent
= (RndStdIds::FLY_AT_PAGE
!= eAnchorId
);
3143 const SwNodeIndex
* pChkIdx
= nullptr;
3144 if ( pAnchor
== nullptr )
3146 pChkIdx
= &rRg
.GetPoint()->nNode
;
3148 else if ( bIsAtContent
)
3151 pAnchor
->GetContentAnchor() ? &pAnchor
->GetContentAnchor()->nNode
: &rRg
.GetPoint()->nNode
;
3154 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
3155 if( pChkIdx
!= nullptr
3156 && ::CheckControlLayer( &rDrawObj
)
3157 && m_rDoc
.IsInHeaderFooter( *pChkIdx
) )
3159 // apply at-page anchor format
3160 eAnchorId
= RndStdIds::FLY_AT_PAGE
;
3161 pFormat
->SetFormatAttr( SwFormatAnchor( eAnchorId
) );
3163 else if( pAnchor
== nullptr
3165 && pAnchor
->GetContentAnchor() == nullptr ) )
3167 // apply anchor format
3168 SwFormatAnchor
aAnch( pAnchor
!= nullptr ? *pAnchor
: pFormat
->GetAnchor() );
3169 eAnchorId
= aAnch
.GetAnchorId();
3170 if ( eAnchorId
== RndStdIds::FLY_AT_FLY
)
3172 const SwStartNode
* pStartNode
= rRg
.GetNode().FindFlyStartNode();
3174 SwPosition
aPos(*pStartNode
);
3175 aAnch
.SetAnchor( &aPos
);
3179 aAnch
.SetAnchor( rRg
.GetPoint() );
3180 if ( eAnchorId
== RndStdIds::FLY_AT_PAGE
)
3182 eAnchorId
= dynamic_cast<const SdrUnoObj
*>( &rDrawObj
) != nullptr ? RndStdIds::FLY_AS_CHAR
: RndStdIds::FLY_AT_PARA
;
3183 aAnch
.SetType( eAnchorId
);
3186 pFormat
->SetFormatAttr( aAnch
);
3189 // insert text attribute for as-character anchored drawing object
3190 if ( eAnchorId
== RndStdIds::FLY_AS_CHAR
)
3192 bool bAnchorAtPageAsFallback
= true;
3193 const SwFormatAnchor
& rDrawObjAnchorFormat
= pFormat
->GetAnchor();
3194 if ( rDrawObjAnchorFormat
.GetContentAnchor() != nullptr )
3196 SwTextNode
* pAnchorTextNode
=
3197 rDrawObjAnchorFormat
.GetContentAnchor()->nNode
.GetNode().GetTextNode();
3198 if ( pAnchorTextNode
!= nullptr )
3200 const sal_Int32 nStt
= rDrawObjAnchorFormat
.GetContentAnchor()->nContent
.GetIndex();
3201 SwFormatFlyCnt
aFormat( pFormat
);
3202 pAnchorTextNode
->InsertItem( aFormat
, nStt
, nStt
);
3203 bAnchorAtPageAsFallback
= false;
3207 if ( bAnchorAtPageAsFallback
)
3209 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
3210 pFormat
->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE
) );
3214 SwDrawContact
* pContact
= new SwDrawContact( pFormat
, &rDrawObj
);
3216 // Create Frames if necessary
3217 if( m_rDoc
.getIDocumentLayoutAccess().GetCurrentViewShell() )
3219 // create layout representation
3220 pFormat
->MakeFrames();
3221 // #i42319# - follow-up of #i35635#
3222 // move object to visible layer
3224 if ( pContact
->GetAnchorFrame() )
3226 pContact
->MoveObjToVisibleLayer( &rDrawObj
);
3230 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3232 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsLayFormat
>(pFormat
, SwNodeOffset(0), 0) );
3235 m_rDoc
.getIDocumentState().SetModified();
3239 bool DocumentContentOperationsManager::SplitNode( const SwPosition
&rPos
, bool bChkTableStart
)
3241 SwContentNode
*pNode
= rPos
.nNode
.GetNode().GetContentNode();
3242 if(nullptr == pNode
)
3246 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
3247 // After that they can be before/after the position.
3248 SwDataChanged
aTmp( m_rDoc
, rPos
);
3251 SwUndoSplitNode
* pUndo
= nullptr;
3252 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3254 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3255 // insert the Undo object (currently only for TextNode)
3256 if( pNode
->IsTextNode() )
3258 pUndo
= new SwUndoSplitNode( m_rDoc
, rPos
, bChkTableStart
);
3259 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3263 // Update the rsid of the old and the new node unless
3264 // the old node is split at the beginning or at the end
3265 SwTextNode
*pTextNode
= rPos
.nNode
.GetNode().GetTextNode();
3266 const sal_Int32 nPos
= rPos
.nContent
.GetIndex();
3267 if( pTextNode
&& nPos
&& nPos
!= pTextNode
->Len() )
3269 m_rDoc
.UpdateParRsid( pTextNode
);
3272 //JP 28.01.97: Special case for SplitNode at table start:
3273 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
3274 // then insert a paragraph before it.
3275 if( bChkTableStart
&& !rPos
.nContent
.GetIndex() && pNode
->IsTextNode() )
3277 SwNodeOffset nPrevPos
= rPos
.nNode
.GetIndex() - 1;
3278 const SwTableNode
* pTableNd
;
3279 const SwNode
* pNd
= m_rDoc
.GetNodes()[ nPrevPos
];
3280 if( pNd
->IsStartNode() &&
3281 SwTableBoxStartNode
== static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() &&
3282 nullptr != ( pTableNd
= m_rDoc
.GetNodes()[ --nPrevPos
]->GetTableNode() ) &&
3283 ((( pNd
= m_rDoc
.GetNodes()[ --nPrevPos
])->IsStartNode() &&
3284 SwTableBoxStartNode
!= static_cast<const SwStartNode
*>(pNd
)->GetStartNodeType() )
3285 || ( pNd
->IsEndNode() && pNd
->StartOfSectionNode()->IsTableNode() )
3286 || pNd
->IsContentNode() ))
3288 if( pNd
->IsContentNode() )
3290 //JP 30.04.99 Bug 65660:
3291 // There are no page breaks outside of the normal body area,
3292 // so this is not a valid condition to insert a paragraph.
3293 if( nPrevPos
< m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3297 // Only if the table has page breaks!
3298 const SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3299 if( SfxItemState::SET
!= pFrameFormat
->GetItemState(RES_PAGEDESC
, false) &&
3300 SfxItemState::SET
!= pFrameFormat
->GetItemState( RES_BREAK
, false ) )
3307 SwTextNode
* pTextNd
= m_rDoc
.GetNodes().MakeTextNode(
3308 SwNodeIndex( *pTableNd
),
3309 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT
));
3312 const_cast<SwPosition
&>(rPos
).nNode
= pTableNd
->GetIndex()-SwNodeOffset(1);
3313 const_cast<SwPosition
&>(rPos
).nContent
.Assign( pTextNd
, 0 );
3315 // only add page breaks/styles to the body area
3316 if( nPrevPos
> m_rDoc
.GetNodes().GetEndOfExtras().GetIndex() )
3318 SwFrameFormat
* pFrameFormat
= pTableNd
->GetTable().GetFrameFormat();
3319 const SfxPoolItem
*pItem
;
3320 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_PAGEDESC
,
3323 pTextNd
->SetAttr( *pItem
);
3324 pFrameFormat
->ResetFormatAttr( RES_PAGEDESC
);
3326 if( SfxItemState::SET
== pFrameFormat
->GetItemState( RES_BREAK
,
3329 pTextNd
->SetAttr( *pItem
);
3330 pFrameFormat
->ResetFormatAttr( RES_BREAK
);
3335 pUndo
->SetTableFlag();
3336 m_rDoc
.getIDocumentState().SetModified();
3343 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
3344 pContentStore
->Save( m_rDoc
, rPos
.nNode
.GetIndex(), rPos
.nContent
.GetIndex(), true );
3345 assert(pNode
->IsTextNode());
3346 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool bAtStart
)> restoreFunc(
3347 [&](SwTextNode
*const, sw::mark::RestoreMode
const eMode
, bool const bAtStart
)
3349 if (!pContentStore
->Empty())
3350 { // move all bookmarks, TOXMarks, FlyAtCnt
3351 pContentStore
->Restore(m_rDoc
, rPos
.nNode
.GetIndex()-SwNodeOffset(1), 0, true, bAtStart
&& (eMode
& sw::mark::RestoreMode::Flys
), eMode
);
3353 if (eMode
& sw::mark::RestoreMode::NonFlys
)
3355 // To-Do - add 'SwExtraRedlineTable' also ?
3356 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() ||
3357 (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() &&
3358 !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty()))
3362 aPam
.Move( fnMoveBackward
);
3363 if (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3365 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(
3366 new SwRangeRedline(RedlineType::Insert
, aPam
), true);
3370 m_rDoc
.getIDocumentRedlineAccess().SplitRedline(aPam
);
3375 pNode
->GetTextNode()->SplitContentNode(rPos
, &restoreFunc
);
3377 m_rDoc
.getIDocumentState().SetModified();
3381 bool DocumentContentOperationsManager::AppendTextNode( SwPosition
& rPos
)
3383 // create new node before EndOfContent
3384 SwTextNode
* pCurNode
= rPos
.nNode
.GetNode().GetTextNode();
3387 // so then one can be created!
3388 SwNodeIndex
aIdx( rPos
.nNode
, 1 );
3389 pCurNode
= m_rDoc
.GetNodes().MakeTextNode( aIdx
,
3390 m_rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
));
3393 pCurNode
= pCurNode
->AppendNode( rPos
)->GetTextNode();
3396 rPos
.nContent
.Assign( pCurNode
, 0 );
3398 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3400 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoInsert
>( rPos
.nNode
) );
3403 // To-Do - add 'SwExtraRedlineTable' also ?
3404 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
3408 aPam
.Move( fnMoveBackward
);
3409 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
3410 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
3412 m_rDoc
.getIDocumentRedlineAccess().SplitRedline( aPam
);
3415 m_rDoc
.getIDocumentState().SetModified();
3419 bool DocumentContentOperationsManager::ReplaceRange( SwPaM
& rPam
, const OUString
& rStr
,
3420 const bool bRegExReplace
)
3422 // unfortunately replace works slightly differently from delete,
3423 // so we cannot use lcl_DoWithBreaks here...
3425 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
3427 SwPaM
aPam( *rPam
.GetMark(), *rPam
.GetPoint() );
3428 aPam
.Normalize(false);
3429 if (aPam
.GetPoint()->nNode
!= aPam
.GetMark()->nNode
)
3431 aPam
.Move(fnMoveBackward
);
3433 OSL_ENSURE((aPam
.GetPoint()->nNode
== aPam
.GetMark()->nNode
), "invalid pam?");
3435 sw::CalcBreaks(Breaks
, aPam
);
3437 while (!Breaks
.empty() // skip over prefix of dummy chars
3438 && (aPam
.GetMark()->nNode
.GetIndex() == Breaks
.begin()->first
)
3439 && (aPam
.GetMark()->nContent
.GetIndex() == Breaks
.begin()->second
))
3442 ++aPam
.GetMark()->nContent
; // always in bounds if Breaks valid
3443 Breaks
.erase(Breaks
.begin());
3445 *rPam
.Start() = *aPam
.GetMark(); // update start of original pam w/ prefix
3449 // park aPam somewhere so it does not point to node that is deleted
3451 *aPam
.GetPoint() = SwPosition(m_rDoc
.GetNodes().GetEndOfContent());
3452 return ReplaceRangeImpl(rPam
, rStr
, bRegExReplace
); // original pam!
3455 // Deletion must be split into several parts if the text node
3456 // contains a text attribute with end and with dummy character
3457 // and the selection does not contain the text attribute completely,
3458 // but overlaps its start (left), where the dummy character is.
3461 // iterate from end to start, to avoid invalidating the offsets!
3462 auto iter( Breaks
.rbegin() );
3463 SwNodeOffset
nOffset(0);
3464 SwNodes
const& rNodes(rPam
.GetPoint()->nNode
.GetNodes());
3465 OSL_ENSURE(aPam
.GetPoint() == aPam
.End(), "wrong!");
3466 SwPosition
& rEnd( *aPam
.End() );
3467 SwPosition
& rStart( *aPam
.Start() );
3469 // set end of temp pam to original end (undo Move backward above)
3471 // after first deletion, rEnd will point into the original text node again!
3473 while (iter
!= Breaks
.rend())
3475 rStart
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
3476 if (rStart
< rEnd
) // check if part is empty
3478 bRet
&= (m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn())
3479 ? DeleteAndJoinWithRedlineImpl(aPam
)
3480 : DeleteAndJoinImpl(aPam
, false);
3481 nOffset
= iter
->first
- rStart
.nNode
.GetIndex(); // deleted fly nodes...
3483 rEnd
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
3487 rStart
= *rPam
.Start(); // set to original start
3488 assert(rStart
< rEnd
&& "replace part empty!");
3489 if (rStart
< rEnd
) // check if part is empty
3491 bRet
&= ReplaceRangeImpl(aPam
, rStr
, bRegExReplace
);
3494 rPam
= aPam
; // update original pam (is this required?)
3499 bool DocumentContentOperationsManager::InsertPoolItem(
3501 const SfxPoolItem
&rHt
,
3502 const SetAttrMode nFlags
,
3503 SwRootFrame
const*const pLayout
,
3504 SwTextAttr
**ppNewTextAttr
)
3506 if (utl::ConfigManager::IsFuzzing())
3509 SwDataChanged
aTmp( rRg
);
3510 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3511 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3513 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3514 pUndoAttr
.reset(new SwUndoAttr( rRg
, rHt
, nFlags
));
3517 SfxItemSet
aSet( m_rDoc
.GetAttrPool(), rHt
.Which(), rHt
.Which() );
3519 const bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, aSet
, nFlags
, pUndoAttr
.get(), pLayout
, ppNewTextAttr
);
3521 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3523 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3528 m_rDoc
.getIDocumentState().SetModified();
3533 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM
&rRg
, const SfxItemSet
&rSet
,
3534 const SetAttrMode nFlags
, SwRootFrame
const*const pLayout
)
3536 SwDataChanged
aTmp( rRg
);
3537 std::unique_ptr
<SwUndoAttr
> pUndoAttr
;
3538 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3540 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
3541 pUndoAttr
.reset(new SwUndoAttr( rRg
, rSet
, nFlags
));
3544 bool bRet
= lcl_InsAttr(m_rDoc
, rRg
, rSet
, nFlags
, pUndoAttr
.get(), pLayout
, /*ppNewTextAttr*/nullptr );
3546 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
3548 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr
) );
3552 m_rDoc
.getIDocumentState().SetModified();
3555 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition
& rPos
)
3557 const SwTextNode
* pTNd
= rPos
.nNode
.GetNode().GetTextNode();
3561 const OUString
& rText
= pTNd
->GetText();
3563 while (nIdx
< rText
.getLength())
3565 sal_Unicode
const cCh
= rText
[nIdx
];
3566 if (('\t' != cCh
) && (' ' != cCh
))
3576 aPam
.GetPoint()->nContent
= 0;
3578 aPam
.GetMark()->nContent
= nIdx
;
3579 DeleteRange( aPam
);
3583 // Copy method from SwDoc - "copy Flys in Flys"
3584 /// note: rRg/rInsPos *exclude* a partially selected start text node;
3585 /// pCopiedPaM *includes* a partially selected start text node
3586 void DocumentContentOperationsManager::CopyWithFlyInFly(
3587 const SwNodeRange
& rRg
,
3588 const SwNodeIndex
& rInsPos
,
3589 const std::pair
<const SwPaM
&, const SwPosition
&>* pCopiedPaM
/*and real insert pos*/,
3590 const bool bMakeNewFrames
,
3591 const bool bDelRedlines
,
3592 const bool bCopyFlyAtFly
,
3593 SwCopyFlags
const flags
) const
3595 assert(!pCopiedPaM
|| pCopiedPaM
->first
.End()->nNode
== rRg
.aEnd
);
3596 assert(!pCopiedPaM
|| pCopiedPaM
->second
.nNode
<= rInsPos
);
3598 SwDoc
& rDest
= rInsPos
.GetNode().GetDoc();
3599 SwNodeIndex
aSavePos( rInsPos
);
3601 if (rRg
.aStart
!= rRg
.aEnd
)
3603 bool bEndIsEqualEndPos
= rInsPos
== rRg
.aEnd
;
3605 SaveRedlEndPosForRestore
aRedlRest( rInsPos
, 0 );
3607 // insert behind the already copied start node
3608 m_rDoc
.GetNodes().CopyNodes( rRg
, rInsPos
, false, true );
3609 aRedlRest
.Restore();
3611 if (bEndIsEqualEndPos
)
3613 const_cast<SwNodeIndex
&>(rRg
.aEnd
).Assign(aSavePos
.GetNode(), +1);
3617 // Also copy all bookmarks
3618 // guess this must be done before the DelDummyNodes below as that
3619 // deletes nodes so would mess up the index arithmetic
3620 // sw_fieldmarkhide: also needs to be done before making frames
3621 if (m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount())
3623 SwPaM
aRgTmp( rRg
.aStart
, rRg
.aEnd
);
3624 SwPosition
targetPos(SwNodeIndex(aSavePos
,
3625 SwNodeOffset(rRg
.aStart
!= rRg
.aEnd
? +1 : 0)));
3626 if (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->nNode
)
3628 // there is 1 (partially selected, maybe) paragraph before
3629 assert(SwNodeIndex(rRg
.aStart
, -1) == pCopiedPaM
->first
.Start()->nNode
);
3630 // only use the passed in target SwPosition if the source PaM point
3631 // is on a different node; if it was the same node then the target
3632 // position was likely moved along by the copy operation and now
3633 // points to the end of the range!
3634 targetPos
= pCopiedPaM
->second
;
3637 sw::CopyBookmarks(pCopiedPaM
? pCopiedPaM
->first
: aRgTmp
, targetPos
);
3640 if (rRg
.aStart
!= rRg
.aEnd
)
3642 bool isRecreateEndNode(false);
3643 if (bMakeNewFrames
) // tdf#130685 only after aRedlRest
3644 { // recreate from previous node (could be merged now)
3645 if (SwTextNode
*const pNode
= aSavePos
.GetNode().GetTextNode())
3647 o3tl::sorted_vector
<SwTextFrame
*> frames
;
3648 SwTextNode
*const pEndNode
= rInsPos
.GetNode().GetTextNode();
3651 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3652 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3654 if (pFrame
->getRootFrame()->HasMergedParas())
3656 frames
.insert(pFrame
);
3660 sw::RecreateStartTextFrames(*pNode
);
3661 if (!frames
.empty())
3662 { // tdf#132187 check if the end node needs new frames
3663 SwIterator
<SwTextFrame
, SwTextNode
, sw::IteratorMode::UnwrapMulti
> aIter(*pEndNode
);
3664 for (SwTextFrame
* pFrame
= aIter
.First(); pFrame
; pFrame
= aIter
.Next())
3666 if (pFrame
->getRootFrame()->HasMergedParas())
3668 auto const it
= frames
.find(pFrame
);
3669 if (it
!= frames
.end())
3675 if (!frames
.empty()) // existing frame was deleted
3676 { // all layouts because MakeFrames recreates all layouts
3677 pEndNode
->DelFrames(nullptr);
3678 isRecreateEndNode
= true;
3683 bool const isAtStartOfSection(aSavePos
.GetNode().IsStartNode());
3687 // it's possible that CheckParaRedlineMerge() deleted frames
3688 // on rInsPos so have to include it, but it must not be included
3689 // if it was the first node in the document so that MakeFrames()
3690 // will find the existing (wasn't deleted) frame on it
3691 SwNodeIndex
const end(rInsPos
,
3692 SwNodeOffset((!isRecreateEndNode
|| isAtStartOfSection
)
3694 ::MakeFrames(&rDest
, aSavePos
, end
);
3698 #if OSL_DEBUG_LEVEL > 0
3700 //JP 17.06.99: Bug 66973 - check count only if the selection is in
3701 // the same section or there's no section, because sections that are
3702 // not fully selected are not copied.
3703 const SwSectionNode
* pSSectNd
= rRg
.aStart
.GetNode().FindSectionNode();
3704 SwNodeIndex
aTmpI( rRg
.aEnd
, -1 );
3705 const SwSectionNode
* pESectNd
= aTmpI
.GetNode().FindSectionNode();
3706 if( pSSectNd
== pESectNd
&&
3707 !rRg
.aStart
.GetNode().IsSectionNode() &&
3708 !aTmpI
.GetNode().IsEndNode() )
3710 // If the range starts with a SwStartNode, it isn't copied
3711 SwNodeOffset
offset( (rRg
.aStart
.GetNode().GetNodeType() != SwNodeType::Start
) ? 1 : 0 );
3712 OSL_ENSURE( rInsPos
.GetIndex() - aSavePos
.GetIndex() ==
3713 rRg
.aEnd
.GetIndex() - rRg
.aStart
.GetIndex() - 1 + offset
,
3714 "An insufficient number of nodes were copied!" );
3720 ::sw::UndoGuard
const undoGuard(rDest
.GetIDocumentUndoRedo());
3721 CopyFlyInFlyImpl(rRg
, pCopiedPaM
? &pCopiedPaM
->first
: nullptr,
3722 // see comment below regarding use of pCopiedPaM->second
3723 (pCopiedPaM
&& rRg
.aStart
!= pCopiedPaM
->first
.Start()->nNode
)
3724 ? pCopiedPaM
->second
.nNode
3730 SwNodeRange
aCpyRange( aSavePos
, rInsPos
);
3732 if( bDelRedlines
&& ( RedlineFlags::DeleteRedlines
& rDest
.getIDocumentRedlineAccess().GetRedlineFlags() ))
3733 lcl_DeleteRedlines( rRg
, aCpyRange
);
3735 rDest
.GetNodes().DelDummyNodes( aCpyRange
);
3738 // note: for the redline Show/Hide this must be in sync with
3739 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
3740 void DocumentContentOperationsManager::CopyFlyInFlyImpl(
3741 const SwNodeRange
& rRg
,
3742 SwPaM
const*const pCopiedPaM
,
3743 const SwNodeIndex
& rStartIdx
,
3744 const bool bCopyFlyAtFly
,
3745 SwCopyFlags
const flags
) const
3747 assert(!pCopiedPaM
|| pCopiedPaM
->End()->nNode
== rRg
.aEnd
);
3749 // First collect all Flys, sort them according to their ordering number,
3750 // and then only copy them. This maintains the ordering numbers (which are only
3751 // managed in the DrawModel).
3752 SwDoc
& rDest
= rStartIdx
.GetNode().GetDoc();
3753 std::set
< ZSortFly
> aSet
;
3754 const size_t nArrLen
= m_rDoc
.GetSpzFrameFormats()->size();
3756 SwTextBoxHelper::SavedLink aOldTextBoxes
;
3757 SwTextBoxHelper::saveLinks(*m_rDoc
.GetSpzFrameFormats(), aOldTextBoxes
);
3759 for ( size_t n
= 0; n
< nArrLen
; ++n
)
3761 SwFrameFormat
* pFormat
= (*m_rDoc
.GetSpzFrameFormats())[n
];
3762 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
3763 SwPosition
const*const pAPos
= pAnchor
->GetContentAnchor();
3767 SwNodeOffset nSkipAfter
= pAPos
->nNode
.GetIndex();
3768 SwNodeOffset nStart
= rRg
.aStart
.GetIndex();
3769 switch ( pAnchor
->GetAnchorId() )
3771 case RndStdIds::FLY_AT_FLY
:
3774 else if(m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove())
3777 case RndStdIds::FLY_AT_PARA
:
3779 bAdd
= IsSelectFrameAnchoredAtPara(*pAPos
,
3780 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
3781 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
3782 (flags
& SwCopyFlags::IsMoveToFly
)
3783 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
3784 : DelContentType::AllMask
);
3787 case RndStdIds::FLY_AT_CHAR
:
3789 bAdd
= IsDestroyFrameAnchoredAtChar(*pAPos
,
3790 pCopiedPaM
? *pCopiedPaM
->Start() : SwPosition(rRg
.aStart
),
3791 pCopiedPaM
? *pCopiedPaM
->End() : SwPosition(rRg
.aEnd
),
3792 (flags
& SwCopyFlags::IsMoveToFly
)
3793 ? DelContentType::AllMask
|DelContentType::WriterfilterHack
3794 : DelContentType::AllMask
);
3800 if (RndStdIds::FLY_AT_FLY
== pAnchor
->GetAnchorId())
3802 if (nStart
> nSkipAfter
)
3804 if (pAPos
->nNode
> rRg
.aEnd
)
3806 //frames at the last source node are not always copied:
3807 //- if the node is empty and is the last node of the document or a table cell
3808 // or a text frame then they have to be copied
3809 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
3810 //- to-character bound objects are copied if their index is <= nEndContentIndex
3811 if (pAPos
->nNode
< rRg
.aEnd
)
3813 if (!bAdd
&& !m_rDoc
.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
3817 // technically old code checked nContent of AT_FLY which is pointless
3818 bAdd
= pCopiedPaM
&& 0 < pCopiedPaM
->End()->nContent
.GetIndex();
3824 aSet
.insert( ZSortFly( pFormat
, pAnchor
, nArrLen
+ aSet
.size() ));
3828 // Store all copied (and also the newly created) frames in another array.
3829 // They are stored as matching the originals, so that we will be later
3830 // able to build the chains accordingly.
3831 std::vector
< SwFrameFormat
* > aVecSwFrameFormat
;
3832 std::set
< ZSortFly
>::const_iterator it
=aSet
.begin();
3834 while (it
!= aSet
.end())
3837 // correct determination of new anchor position
3838 SwFormatAnchor
aAnchor( *(*it
).GetAnchor() );
3839 assert( aAnchor
.GetContentAnchor() != nullptr );
3840 SwPosition newPos
= *aAnchor
.GetContentAnchor();
3841 // for at-paragraph and at-character anchored objects the new anchor
3842 // position can *not* be determined by the difference of the current
3843 // anchor position to the start of the copied range, because not
3844 // complete selected sections in the copied range aren't copied - see
3845 // method <SwNodes::CopyNodes(..)>.
3846 // Thus, the new anchor position in the destination document is found
3847 // by counting the text nodes.
3848 if ((aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_PARA
) ||
3849 (aAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
) )
3851 // First, determine number of anchor text node in the copied range.
3852 // Note: The anchor text node *have* to be inside the copied range.
3853 sal_uLong
nAnchorTextNdNumInRange( 0 );
3854 bool bAnchorTextNdFound( false );
3855 // start at the first node for which flys are copied
3856 SwNodeIndex
aIdx(pCopiedPaM
? pCopiedPaM
->Start()->nNode
: rRg
.aStart
);
3857 while ( !bAnchorTextNdFound
&& aIdx
<= rRg
.aEnd
)
3859 if ( aIdx
.GetNode().IsTextNode() )
3861 ++nAnchorTextNdNumInRange
;
3862 bAnchorTextNdFound
= aAnchor
.GetContentAnchor()->nNode
== aIdx
;
3868 if ( !bAnchorTextNdFound
)
3870 // This case can *not* happen, but to be robust take the first
3871 // text node in the destination document.
3872 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
3873 nAnchorTextNdNumInRange
= 1;
3875 // Second, search corresponding text node in destination document
3876 // by counting forward from start insert position <rStartIdx> the
3877 // determined number of text nodes.
3879 SwNodeIndex
aAnchorNdIdx( rStartIdx
);
3880 const SwNode
& aEndOfContentNd
=
3881 aIdx
.GetNode().GetNodes().GetEndOfContent();
3882 while ( nAnchorTextNdNumInRange
> 0 &&
3883 &(aIdx
.GetNode()) != &aEndOfContentNd
)
3885 if ( aIdx
.GetNode().IsTextNode() )
3887 --nAnchorTextNdNumInRange
;
3888 aAnchorNdIdx
= aIdx
;
3893 if ( !aAnchorNdIdx
.GetNode().IsTextNode() )
3895 // This case can *not* happen, but to be robust take the first
3896 // text node in the destination document.
3897 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
3898 aAnchorNdIdx
= rStartIdx
;
3899 while ( !aAnchorNdIdx
.GetNode().IsTextNode() )
3904 // apply found anchor text node as new anchor position
3905 newPos
.nNode
= aAnchorNdIdx
;
3909 SwNodeOffset nOffset
= newPos
.nNode
.GetIndex() - rRg
.aStart
.GetIndex();
3910 SwNodeIndex
aIdx( rStartIdx
, nOffset
);
3911 newPos
.nNode
= aIdx
;
3913 // Set the character bound Flys back at the original character
3914 if ((RndStdIds::FLY_AT_CHAR
== aAnchor
.GetAnchorId()) &&
3915 newPos
.nNode
.GetNode().IsTextNode() )
3917 // only if pCopiedPaM: care about partially selected start node
3918 sal_Int32
const nContent
= pCopiedPaM
&& pCopiedPaM
->Start()->nNode
== aAnchor
.GetContentAnchor()->nNode
3919 ? newPos
.nContent
.GetIndex() - pCopiedPaM
->Start()->nContent
.GetIndex()
3920 : newPos
.nContent
.GetIndex();
3921 newPos
.nContent
.Assign(newPos
.nNode
.GetNode().GetTextNode(), nContent
);
3925 newPos
.nContent
.Assign( nullptr, 0 );
3927 aAnchor
.SetAnchor( &newPos
);
3929 // Check recursion: if copying content inside the same frame, then don't copy the format.
3930 if( &rDest
== &m_rDoc
)
3932 const SwFormatContent
& rContent
= (*it
).GetFormat()->GetContent();
3933 const SwStartNode
* pSNd
;
3934 if( rContent
.GetContentIdx() &&
3935 nullptr != ( pSNd
= rContent
.GetContentIdx()->GetNode().GetStartNode() ) &&
3936 pSNd
->GetIndex() < rStartIdx
.GetIndex() &&
3937 rStartIdx
.GetIndex() < pSNd
->EndOfSectionIndex() )
3939 it
= aSet
.erase(it
);
3944 // Ignore TextBoxes, they are already handled in
3945 // sw::DocumentLayoutManager::CopyLayoutFormat().
3946 if (SwTextBoxHelper::isTextBox(it
->GetFormat(), RES_FLYFRMFMT
))
3948 it
= aSet
.erase(it
);
3952 // Copy the format and set the new anchor
3953 aVecSwFrameFormat
.push_back( rDest
.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it
).GetFormat(),
3954 aAnchor
, false, true ) );
3958 // Rebuild as much as possible of all chains that are available in the original,
3959 OSL_ENSURE( aSet
.size() == aVecSwFrameFormat
.size(), "Missing new Flys" );
3960 if ( aSet
.size() != aVecSwFrameFormat
.size() )
3964 for (const auto& rFlyN
: aSet
)
3966 const SwFrameFormat
*pFormatN
= rFlyN
.GetFormat();
3967 const SwFormatChain
&rChain
= pFormatN
->GetChain();
3968 int nCnt
= int(nullptr != rChain
.GetPrev());
3969 nCnt
+= rChain
.GetNext() ? 1: 0;
3971 for (const auto& rFlyK
: aSet
)
3973 const SwFrameFormat
*pFormatK
= rFlyK
.GetFormat();
3974 if ( rChain
.GetPrev() == pFormatK
)
3976 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]),
3977 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]) );
3980 else if ( rChain
.GetNext() == pFormatK
)
3982 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[n
]),
3983 static_cast< SwFlyFrameFormat
* >(aVecSwFrameFormat
[k
]) );
3991 // Re-create content property of draw formats, knowing how old shapes
3992 // were paired with old fly formats (aOldTextBoxes) and that aSet is
3993 // parallel with aVecSwFrameFormat.
3994 SwTextBoxHelper::restoreLinks(aSet
, aVecSwFrameFormat
, aOldTextBoxes
);
3998 * Reset the text's hard formatting
4000 /** @params pArgs contains the document's ChrFormatTable
4001 * Is need for selections at the beginning/end and with no SSelection.
4003 bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode
* pNd
, void* pArgs
)
4005 ParaRstFormat
* pPara
= static_cast<ParaRstFormat
*>(pArgs
);
4006 if (pPara
->pLayout
&& pPara
->pLayout
->HasMergedParas()
4007 && pNd
->GetRedlineMergeFlag() == SwNode::Merge::Hidden
)
4009 return true; // skip hidden, since new items aren't applied
4011 SwTextNode
* pTextNode
= pNd
->GetTextNode();
4012 if( pTextNode
&& pTextNode
->GetpSwpHints() )
4014 SwIndex
aSt( pTextNode
, 0 );
4015 sal_Int32 nEnd
= pTextNode
->Len();
4017 if( &pPara
->pSttNd
->nNode
.GetNode() == pTextNode
&&
4018 pPara
->pSttNd
->nContent
.GetIndex() )
4019 aSt
= pPara
->pSttNd
->nContent
.GetIndex();
4021 if( &pPara
->pEndNd
->nNode
.GetNode() == pNd
)
4022 nEnd
= pPara
->pEndNd
->nContent
.GetIndex();
4024 if( pPara
->pHistory
)
4026 // Save all attributes for the Undo.
4027 SwRegHistory
aRHst( *pTextNode
, pPara
->pHistory
);
4028 pTextNode
->GetpSwpHints()->Register( &aRHst
);
4029 pTextNode
->RstTextAttr( aSt
, nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4030 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4031 if( pTextNode
->GetpSwpHints() )
4032 pTextNode
->GetpSwpHints()->DeRegister();
4035 pTextNode
->RstTextAttr( aSt
, nEnd
- aSt
.GetIndex(), pPara
->nWhich
,
4036 pPara
->pDelSet
, pPara
->bInclRefToxMark
, pPara
->bExactRange
);
4041 DocumentContentOperationsManager::~DocumentContentOperationsManager()
4046 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl( SwPaM
& rPam
, const bool )
4048 assert(m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn());
4050 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4052 if (*rPam
.GetPoint() == *rPam
.GetMark())
4054 return false; // do not add empty redlines
4057 std::vector
<SwRangeRedline
*> redlines
;
4059 auto pRedline(std::make_unique
<SwRangeRedline
>(RedlineType::Delete
, rPam
));
4060 if (pRedline
->HasValidRange())
4062 redlines
.push_back(pRedline
.release());
4064 else // sigh ... why is such a selection even possible...
4065 { // split it up so we get one SwUndoRedlineDelete per inserted RL
4066 redlines
= GetAllValidRanges(std::move(pRedline
));
4070 if (redlines
.empty())
4075 // tdf#54819 current redlining needs also modification of paragraph style and
4076 // attributes added to the same grouped Undo
4077 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4078 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4080 auto & rDMA(*m_rDoc
.getIDocumentMarkAccess());
4081 std::vector
<std::unique_ptr
<SwUndo
>> MarkUndos
;
4082 for (auto iter
= rDMA
.getAnnotationMarksBegin();
4083 iter
!= rDMA
.getAnnotationMarksEnd(); )
4085 // tdf#111524 remove annotation marks that have their field
4086 // characters deleted
4087 SwPosition
const& rEndPos((**iter
).GetMarkEnd());
4088 if (*rPam
.Start() < rEndPos
&& rEndPos
<= *rPam
.End())
4090 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4092 MarkUndos
.emplace_back(std::make_unique
<SwUndoDeleteBookmark
>(**iter
));
4094 // iter is into annotation mark vector so must be dereferenced!
4095 rDMA
.deleteMark(&**iter
);
4096 // this invalidates iter, have to start over...
4097 iter
= rDMA
.getAnnotationMarksBegin();
4100 { // marks are sorted by start
4101 if (*rPam
.End() < (**iter
).GetMarkStart())
4109 // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
4110 if (*rPam
.GetPoint() != *rPam
.GetMark())
4111 m_rDoc
.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam
);
4113 std::vector
<std::unique_ptr
<SwUndoRedlineDelete
>> undos
;
4114 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4116 // this should no longer happen in calls from the UI but maybe via API
4117 // (randomTest and testTdf54819 triggers it)
4118 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4119 "sw.core", "redlines will be moved in DeleteAndJoin");
4120 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4121 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4123 for (SwRangeRedline
* pRedline
: redlines
)
4125 assert(pRedline
->HasValidRange());
4126 undos
.emplace_back(std::make_unique
<SwUndoRedlineDelete
>(
4127 *pRedline
, SwUndoId::DELETE
));
4129 const SwRewriter aRewriter
= undos
.front()->GetRewriter();
4130 // can only group a single undo action
4131 if (MarkUndos
.empty() && undos
.size() == 1
4132 && m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4134 SwUndo
* const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4135 SwUndoRedlineDelete
*const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete
*>(pLastUndo
));
4136 bool const bMerged
= pUndoRedlineDel
4137 && pUndoRedlineDel
->CanGrouping(*undos
.front());
4140 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(undos
.front()));
4142 undos
.clear(); // prevent unmatched EndUndo
4146 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE
, &aRewriter
);
4147 for (auto& it
: MarkUndos
)
4149 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4151 for (auto & it
: undos
)
4153 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::move(it
));
4158 for (SwRangeRedline
*const pRedline
: redlines
)
4160 // note: 1. the pRedline can still be merged & deleted
4161 // 2. the impl. can even DeleteAndJoin the range => no plain PaM
4162 std::shared_ptr
<SwUnoCursor
> const pCursor(m_rDoc
.CreateUnoCursor(*pRedline
->GetMark()));
4164 *pCursor
->GetPoint() = *pRedline
->GetPoint();
4165 m_rDoc
.getIDocumentRedlineAccess().AppendRedline(pRedline
, true);
4166 // sw_redlinehide: 2 reasons why this is needed:
4167 // 1. it's the first redline in node => RedlineDelText was sent but ignored
4168 // 2. redline spans multiple nodes => must merge text frames
4169 sw::UpdateFramesForAddDeleteRedline(m_rDoc
, *pCursor
);
4171 m_rDoc
.getIDocumentState().SetModified();
4173 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4177 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4179 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4182 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4183 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4188 bool DocumentContentOperationsManager::DeleteAndJoinImpl( SwPaM
& rPam
,
4189 const bool bForceJoinNext
)
4191 bool bJoinText
, bJoinPrev
;
4192 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4194 if ( bForceJoinNext
)
4200 bool const bSuccess( DeleteRangeImpl( rPam
) );
4207 ::sw_JoinText( rPam
, bJoinPrev
);
4210 if (!m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline()
4211 && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty())
4213 m_rDoc
.getIDocumentRedlineAccess().CompressRedlines();
4219 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM
& rPam
, const bool)
4221 // Move all cursors out of the deleted range, but first copy the
4222 // passed PaM, because it could be a cursor that would be moved!
4223 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4224 ::PaMCorrAbs( aDelPam
, *aDelPam
.GetPoint() );
4226 bool const bSuccess( DeleteRangeImplImpl( aDelPam
) );
4228 { // now copy position from temp copy to given PaM
4229 *rPam
.GetPoint() = *aDelPam
.GetPoint();
4235 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM
& rPam
)
4237 SwPosition
*pStt
= rPam
.Start(), *pEnd
= rPam
.End();
4240 || (*pStt
== *pEnd
&& !IsFlySelectedByCursor(m_rDoc
, *pStt
, *pEnd
)))
4245 if( m_rDoc
.GetAutoCorrExceptWord() )
4247 // if necessary the saved Word for the exception
4248 if( m_rDoc
.GetAutoCorrExceptWord()->IsDeleted() || pStt
->nNode
!= pEnd
->nNode
||
4249 pStt
->nContent
.GetIndex() + 1 != pEnd
->nContent
.GetIndex() ||
4250 !m_rDoc
.GetAutoCorrExceptWord()->CheckDelChar( *pStt
))
4251 { m_rDoc
.DeleteAutoCorrExceptWord(); }
4255 // Delete all empty TextHints at the Mark's position
4256 SwTextNode
* pTextNd
= rPam
.GetMark()->nNode
.GetNode().GetTextNode();
4258 if( pTextNd
&& nullptr != ( pHts
= pTextNd
->GetpSwpHints()) && pHts
->Count() )
4260 const sal_Int32 nMkCntPos
= rPam
.GetMark()->nContent
.GetIndex();
4261 for( size_t n
= pHts
->Count(); n
; )
4263 const SwTextAttr
* pAttr
= pHts
->Get( --n
);
4264 if( nMkCntPos
> pAttr
->GetStart() )
4267 const sal_Int32
*pEndIdx
;
4268 if( nMkCntPos
== pAttr
->GetStart() &&
4269 nullptr != (pEndIdx
= pAttr
->End()) &&
4270 *pEndIdx
== pAttr
->GetStart() )
4271 pTextNd
->DestroyAttr( pHts
->Cut( n
) );
4277 // Send DataChanged before deletion, so that we still know
4278 // which objects are in the range.
4279 // Afterwards they could be before/after the Position.
4280 SwDataChanged
aTmp( rPam
);
4283 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4285 m_rDoc
.GetIDocumentUndoRedo().ClearRedo();
4286 bool bMerged(false);
4287 if (m_rDoc
.GetIDocumentUndoRedo().DoesGroupUndo())
4289 SwUndo
*const pLastUndo( m_rDoc
.GetUndoManager().GetLastUndo() );
4290 SwUndoDelete
*const pUndoDelete(
4291 dynamic_cast<SwUndoDelete
*>(pLastUndo
) );
4294 bMerged
= pUndoDelete
->CanGrouping(m_rDoc
, rPam
);
4295 // if CanGrouping() returns true it's already merged
4300 m_rDoc
.GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoDelete
>( rPam
) );
4303 m_rDoc
.getIDocumentState().SetModified();
4308 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4309 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( rPam
, true, RedlineType::Any
);
4311 // Delete and move all "Flys at the paragraph", which are within the Selection
4312 DelFlyInRange(rPam
.GetMark()->nNode
, rPam
.GetPoint()->nNode
,
4313 &rPam
.GetMark()->nContent
, &rPam
.GetPoint()->nContent
);
4321 SwNodeIndex
aSttIdx( pStt
->nNode
);
4322 SwContentNode
* pCNd
= aSttIdx
.GetNode().GetContentNode();
4324 do { // middle checked loop!
4327 SwTextNode
* pStartTextNode( pCNd
->GetTextNode() );
4328 if ( pStartTextNode
)
4330 // now move the Content to the new Node
4331 bool bOneNd
= pStt
->nNode
== pEnd
->nNode
;
4332 const sal_Int32 nLen
= ( bOneNd
? pEnd
->nContent
.GetIndex()
4334 - pStt
->nContent
.GetIndex();
4336 // Don't call again, if already empty
4339 pStartTextNode
->EraseText( pStt
->nContent
, nLen
);
4341 if( !pStartTextNode
->Len() )
4343 // METADATA: remove reference if empty (consider node deleted)
4344 pStartTextNode
->RemoveMetadataReference();
4348 if( bOneNd
) // that's it
4355 // So that there are no indices left registered when deleted,
4356 // we remove a SwPaM from the Content here.
4357 pStt
->nContent
.Assign( nullptr, 0 );
4361 pCNd
= pEnd
->nNode
.GetNode().GetContentNode();
4364 SwTextNode
* pEndTextNode( pCNd
->GetTextNode() );
4367 // if already empty, don't call again
4368 if( pEnd
->nContent
.GetIndex() )
4370 SwIndex
aIdx( pCNd
, 0 );
4371 pEndTextNode
->EraseText( aIdx
, pEnd
->nContent
.GetIndex() );
4373 if( !pEndTextNode
->Len() )
4375 // METADATA: remove reference if empty (consider node deleted)
4376 pEndTextNode
->RemoveMetadataReference();
4382 // So that there are no indices left registered when deleted,
4383 // we remove a SwPaM from the Content here.
4384 pEnd
->nContent
.Assign( nullptr, 0 );
4388 // if the end is not a content node, delete it as well
4389 SwNodeOffset nEnd
= pEnd
->nNode
.GetIndex();
4390 if( pCNd
== nullptr )
4393 if( aSttIdx
!= nEnd
)
4395 // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
4397 while (pEnd
== rPam
.GetPoint()
4398 && nEnd
+ SwNodeOffset(2) < m_rDoc
.GetNodes().Count()
4399 && (pTmpNd
= m_rDoc
.GetNodes()[nEnd
+ 1])->IsEndNode()
4400 && pTmpNd
->StartOfSectionNode()->IsSectionNode()
4401 && aSttIdx
<= pTmpNd
->StartOfSectionNode()->GetIndex())
4403 SwNodeRange
range(*pTmpNd
->StartOfSectionNode(), *pTmpNd
);
4404 m_rDoc
.GetNodes().SectionUp(&range
);
4405 --nEnd
; // account for deleted start node
4408 // delete the Nodes from the NodesArray
4409 m_rDoc
.GetNodes().Delete( aSttIdx
, nEnd
- aSttIdx
.GetIndex() );
4412 // If the Node that contained the Cursor has been deleted,
4413 // the Content has to be assigned to the current Content.
4414 pStt
->nContent
.Assign( pStt
->nNode
.GetNode().GetContentNode(),
4415 pStt
->nContent
.GetIndex() );
4417 // If we deleted across Node boundaries we have to correct the PaM,
4418 // because they are in different Nodes now.
4419 // Also, the Selection is revoked.
4425 m_rDoc
.getIDocumentState().SetModified();
4430 // It's possible to call Replace with a PaM that spans 2 paragraphs:
4431 // search with regex for "$", then replace _all_
4432 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM
& rPam
, const OUString
& rStr
,
4433 const bool bRegExReplace
)
4435 if (!rPam
.HasMark())
4438 bool bJoinText
, bJoinPrev
;
4439 ::sw_GetJoinFlags( rPam
, bJoinText
, bJoinPrev
);
4442 // Create a copy of the Cursor in order to move all Pams from
4443 // the other views out of the deletion range.
4444 // Except for itself!
4445 SwPaM
aDelPam( *rPam
.GetMark(), *rPam
.GetPoint() );
4446 ::PaMCorrAbs( aDelPam
, *aDelPam
.GetPoint() );
4448 SwPosition
*pStt
= aDelPam
.Start(),
4449 *pEnd
= aDelPam
.End();
4450 bool bOneNode
= pStt
->nNode
== pEnd
->nNode
;
4453 OUString
sRepl( rStr
);
4454 SwTextNode
* pTextNd
= pStt
->nNode
.GetNode().GetTextNode();
4455 sal_Int32 nStt
= pStt
->nContent
.GetIndex();
4458 SwDataChanged
aTmp( aDelPam
);
4460 if( m_rDoc
.getIDocumentRedlineAccess().IsRedlineOn() )
4462 RedlineFlags eOld
= m_rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4463 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4465 // this should no longer happen in calls from the UI but maybe via API
4466 SAL_WARN_IF((eOld
& RedlineFlags::ShowMask
) != RedlineFlags::ShowMask
,
4467 "sw.core", "redlines will be moved in ReplaceRange");
4469 m_rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4471 // If any Redline will change (split!) the node
4472 const ::sw::mark::IMark
* pBkmk
=
4473 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4474 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4475 ::sw::mark::InsertMode::New
);
4477 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags(
4478 RedlineFlags::On
| RedlineFlags::ShowInsert
| RedlineFlags::ShowDelete
);
4480 *aDelPam
.GetPoint() = pBkmk
->GetMarkPos();
4481 if(pBkmk
->IsExpanded())
4482 *aDelPam
.GetMark() = pBkmk
->GetOtherMarkPos();
4483 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4484 pStt
= aDelPam
.Start();
4485 pTextNd
= pStt
->nNode
.GetNode().GetTextNode();
4486 nStt
= pStt
->nContent
.GetIndex();
4489 if( !sRepl
.isEmpty() )
4491 // Apply the first character's attributes to the ReplaceText
4493 <RES_CHRATR_BEGIN
, RES_TXTATR_WITHEND_END
- 1,
4494 RES_UNKNOWNATR_BEGIN
, RES_UNKNOWNATR_END
-1> aSet( m_rDoc
.GetAttrPool() );
4495 pTextNd
->GetParaAttr( aSet
, nStt
+1, nStt
+1 );
4497 aSet
.ClearItem( RES_TXTATR_REFMARK
);
4498 aSet
.ClearItem( RES_TXTATR_TOXMARK
);
4499 aSet
.ClearItem( RES_TXTATR_CJK_RUBY
);
4500 aSet
.ClearItem( RES_TXTATR_INETFMT
);
4501 aSet
.ClearItem( RES_TXTATR_META
);
4502 aSet
.ClearItem( RES_TXTATR_METAFIELD
);
4504 if( aDelPam
.GetPoint() != aDelPam
.End() )
4508 SwNodeIndex
aPtNd( aDelPam
.GetPoint()->nNode
, -1 );
4509 const sal_Int32 nPtCnt
= aDelPam
.GetPoint()->nContent
.GetIndex();
4513 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4515 InsertString( aDelPam
, sIns
);
4518 SwNodeIndex
aMkNd( aDelPam
.GetMark()->nNode
, -1 );
4519 const sal_Int32 nMkCnt
= aDelPam
.GetMark()->nContent
.GetIndex();
4521 SplitNode( *aDelPam
.GetPoint(), false );
4524 aDelPam
.GetMark()->nNode
= aMkNd
;
4525 aDelPam
.GetMark()->nContent
.Assign(
4526 aMkNd
.GetNode().GetContentNode(), nMkCnt
);
4530 SplitNode( *aDelPam
.GetPoint(), false );
4532 if( !sIns
.isEmpty() )
4534 InsertString( aDelPam
, sIns
);
4537 SwPaM
aTmpRange( *aDelPam
.GetPoint() );
4538 aTmpRange
.SetMark();
4541 aDelPam
.GetPoint()->nNode
= aPtNd
;
4542 aDelPam
.GetPoint()->nContent
.Assign( aPtNd
.GetNode().GetContentNode(),
4544 *aTmpRange
.GetMark() = *aDelPam
.GetPoint();
4546 m_rDoc
.RstTextAttrs( aTmpRange
);
4547 InsertItemSet( aTmpRange
, aSet
);
4550 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4552 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(
4553 std::make_unique
<SwUndoRedlineDelete
>( aDelPam
, SwUndoId::REPLACE
));
4555 m_rDoc
.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete
, aDelPam
), true);
4557 *rPam
.GetMark() = *aDelPam
.GetMark();
4558 if (m_rDoc
.GetIDocumentUndoRedo().DoesUndo())
4560 *aDelPam
.GetPoint() = *rPam
.GetPoint();
4561 m_rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4563 // If any Redline will change (split!) the node
4564 const ::sw::mark::IMark
* pBkmk
=
4565 m_rDoc
.getIDocumentMarkAccess()->makeMark( aDelPam
,
4566 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
4567 ::sw::mark::InsertMode::New
);
4569 SwIndex
& rIdx
= aDelPam
.GetPoint()->nContent
;
4570 rIdx
.Assign( nullptr, 0 );
4571 aDelPam
.GetMark()->nContent
= rIdx
;
4572 rPam
.GetPoint()->nNode
= SwNodeOffset(0);
4573 rPam
.GetPoint()->nContent
= rIdx
;
4574 *rPam
.GetMark() = *rPam
.GetPoint();
4575 m_rDoc
.getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4577 *rPam
.GetPoint() = pBkmk
->GetMarkPos();
4578 if(pBkmk
->IsExpanded())
4579 *rPam
.GetMark() = pBkmk
->GetOtherMarkPos();
4580 m_rDoc
.getIDocumentMarkAccess()->deleteMark(pBkmk
);
4586 assert((pStt
->nNode
== pEnd
->nNode
||
4587 ( pStt
->nNode
.GetIndex() + 1 == pEnd
->nNode
.GetIndex() &&
4588 !pEnd
->nContent
.GetIndex() )) &&
4589 "invalid range: Point and Mark on different nodes" );
4591 if( !m_rDoc
.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc
.getIDocumentRedlineAccess().GetRedlineTable().empty() )
4592 m_rDoc
.getIDocumentRedlineAccess().DeleteRedline( aDelPam
, true, RedlineType::Any
);
4594 SwUndoReplace
* pUndoRpl
= nullptr;
4595 bool const bDoesUndo
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
4598 pUndoRpl
= new SwUndoReplace(aDelPam
, sRepl
, bRegExReplace
);
4599 m_rDoc
.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndoRpl
));
4601 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
4603 if( aDelPam
.GetPoint() != pStt
)
4606 SwNodeIndex
aPtNd( pStt
->nNode
, -1 );
4607 const sal_Int32 nPtCnt
= pStt
->nContent
.GetIndex();
4609 // Set the values again, if Frames or footnotes on the Text have been removed.
4611 nEnd
= bOneNode
? pEnd
->nContent
.GetIndex()
4612 : pTextNd
->GetText().getLength();
4616 while ( lcl_GetTokenToParaBreak( sRepl
, sIns
, bRegExReplace
) )
4618 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4620 InsertString( aDelPam
, sIns
);
4622 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4624 pTextNd
->ReplaceText( pStt
->nContent
, nEnd
- nStt
, sIns
);
4626 SplitNode( *pStt
, false);
4630 if( bFirst
|| !sIns
.isEmpty() )
4632 if (!bFirst
|| nStt
== pTextNd
->GetText().getLength())
4634 InsertString( aDelPam
, sIns
);
4636 else if( nStt
< nEnd
|| !sIns
.isEmpty() )
4638 pTextNd
->ReplaceText( pStt
->nContent
, nEnd
- nStt
, sIns
);
4642 *rPam
.GetPoint() = *aDelPam
.GetMark();
4644 rPam
.GetMark()->nNode
= aPtNd
;
4645 rPam
.GetMark()->nContent
.Assign( aPtNd
.GetNode().GetContentNode(),
4650 assert(rPam
.GetPoint() == rPam
.End());
4651 // move so that SetEnd remembers position after sw_JoinText
4652 rPam
.Move(fnMoveBackward
);
4654 else if (aDelPam
.GetPoint() == pStt
) // backward selection?
4656 assert(*rPam
.GetMark() <= *rPam
.GetPoint());
4657 rPam
.Exchange(); // swap so that rPam is backwards
4662 pUndoRpl
->SetEnd(rPam
);
4670 bRet
= ::sw_JoinText(rPam
, bJoinPrev
);
4673 m_rDoc
.getIDocumentState().SetModified();
4677 SwFlyFrameFormat
* DocumentContentOperationsManager::InsNoTextNode( const SwPosition
& rPos
, SwNoTextNode
* pNode
,
4678 const SfxItemSet
* pFlyAttrSet
,
4679 const SfxItemSet
* pGrfAttrSet
,
4680 SwFrameFormat
* pFrameFormat
)
4682 SwFlyFrameFormat
*pFormat
= nullptr;
4685 pFormat
= m_rDoc
.MakeFlySection_( rPos
, *pNode
, RndStdIds::FLY_AT_PARA
,
4686 pFlyAttrSet
, pFrameFormat
);
4688 pNode
->SetAttr( *pGrfAttrSet
);
4693 #define NUMRULE_STATE \
4694 std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
4695 std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
4697 #define PUSH_NUMRULE_STATE \
4698 lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
4700 #define POP_NUMRULE_STATE \
4701 lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
4703 static void lcl_PushNumruleState(
4704 std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4705 std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4706 const SwTextNode
*pDestTextNd
)
4708 // Safe numrule item at destination.
4709 // #i86492# - Safe also <ListId> item of destination.
4710 const SfxItemSet
* pAttrSet
= pDestTextNd
->GetpSwAttrSet();
4711 if (pAttrSet
== nullptr)
4714 const SfxPoolItem
* pItem(nullptr);
4715 if (SfxItemState::SET
== pAttrSet
->GetItemState(RES_PARATR_NUMRULE
, false, &pItem
))
4717 aNumRuleItemHolderIfSet
.reset(&pItem
->Clone()->StaticWhichCast(RES_PARATR_NUMRULE
));
4720 if (SfxItemState::SET
== pAttrSet
->GetItemState(RES_PARATR_LIST_ID
, false, &pItem
))
4722 aListIdItemHolderIfSet
.reset(&pItem
->Clone()->StaticWhichCast(RES_PARATR_LIST_ID
));
4726 static void lcl_PopNumruleState(
4727 const std::shared_ptr
<SwNumRuleItem
>& aNumRuleItemHolderIfSet
,
4728 const std::shared_ptr
<SfxStringItem
>& aListIdItemHolderIfSet
,
4729 SwTextNode
*pDestTextNd
, const SwPaM
& rPam
)
4731 /* If only a part of one paragraph is copied
4732 restore the numrule at the destination. */
4733 // #i86492# - restore also <ListId> item
4734 if ( lcl_MarksWholeNode(rPam
) )
4737 if (aNumRuleItemHolderIfSet
)
4739 pDestTextNd
->SetAttr(*aNumRuleItemHolderIfSet
);
4743 pDestTextNd
->ResetAttr(RES_PARATR_NUMRULE
);
4746 if (aListIdItemHolderIfSet
)
4748 pDestTextNd
->SetAttr(*aListIdItemHolderIfSet
);
4752 pDestTextNd
->ResetAttr(RES_PARATR_LIST_ID
);
4756 bool DocumentContentOperationsManager::CopyImpl(SwPaM
& rPam
, SwPosition
& rPos
,
4757 SwCopyFlags
const flags
,
4758 SwPaM
*const pCopyRange
) const
4760 std::vector
<std::pair
<SwNodeOffset
, sal_Int32
>> Breaks
;
4762 sw::CalcBreaks(Breaks
, rPam
, true);
4766 return CopyImplImpl(rPam
, rPos
, flags
, pCopyRange
);
4769 SwPosition
const & rSelectionEnd( *rPam
.End() );
4773 // iterate from end to start, ... don't think it's necessary here?
4774 auto iter( Breaks
.rbegin() );
4775 SwNodeOffset
nOffset(0);
4776 SwNodes
const& rNodes(rPam
.GetPoint()->nNode
.GetNodes());
4777 SwPaM
aPam( rSelectionEnd
, rSelectionEnd
); // end node!
4778 SwPosition
& rEnd( *aPam
.End() );
4779 SwPosition
& rStart( *aPam
.Start() );
4780 SwPaM
copyRange(rPos
, rPos
);
4782 while (iter
!= Breaks
.rend())
4784 rStart
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
+ 1);
4785 if (rStart
< rEnd
) // check if part is empty
4787 // pass in copyRange member as rPos; should work ...
4788 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
4789 nOffset
= iter
->first
- rStart
.nNode
.GetIndex(); // fly nodes...
4794 pCopyRange
->SetMark();
4795 *pCopyRange
->GetMark() = *copyRange
.End();
4797 *pCopyRange
->GetPoint() = *copyRange
.Start();
4801 rEnd
= SwPosition(*rNodes
[iter
->first
- nOffset
]->GetTextNode(), iter
->second
);
4805 rStart
= *rPam
.Start(); // set to original start
4806 if (rStart
< rEnd
) // check if part is empty
4808 bRet
&= CopyImplImpl(aPam
, *copyRange
.Start(), flags
& ~SwCopyFlags::IsMoveToFly
, ©Range
);
4813 pCopyRange
->SetMark();
4814 *pCopyRange
->GetMark() = *copyRange
.End();
4816 *pCopyRange
->GetPoint() = *copyRange
.Start();
4823 bool DocumentContentOperationsManager::CopyImplImpl(SwPaM
& rPam
, SwPosition
& rPos
,
4824 SwCopyFlags
const flags
,
4825 SwPaM
*const pCpyRange
) const
4827 SwDoc
& rDoc
= rPos
.nNode
.GetNode().GetDoc();
4828 const bool bColumnSel
= rDoc
.IsClipBoard() && rDoc
.IsColumnSelection();
4830 SwPosition
const*const pStt
= rPam
.Start();
4831 SwPosition
*const pEnd
= rPam
.End();
4833 // Catch when there's no copy to do.
4834 if (!rPam
.HasMark() || (IsEmptyRange(*pStt
, *pEnd
, flags
) && !bColumnSel
) ||
4835 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
4836 //JP 15.11.2001: don't test inclusive the end, ever exclusive
4837 ( &rDoc
== &m_rDoc
&& *pStt
<= rPos
&& rPos
< *pEnd
))
4842 const bool bEndEqualIns
= &rDoc
== &m_rDoc
&& rPos
== *pEnd
;
4844 // If Undo is enabled, create the UndoCopy object
4845 SwUndoCpyDoc
* pUndo
= nullptr;
4846 // lcl_DeleteRedlines may delete the start or end node of the cursor when
4847 // removing the redlines so use cursor that is corrected by PaMCorrAbs
4848 std::shared_ptr
<SwUnoCursor
> const pCopyPam(rDoc
.CreateUnoCursor(rPos
));
4850 SwTableNumFormatMerge
aTNFM( m_rDoc
, rDoc
);
4851 std::optional
<std::vector
<SwFrameFormat
*>> pFlys
;
4852 std::vector
<SwFrameFormat
*> const* pFlysAtInsPos
;
4854 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
4856 pUndo
= new SwUndoCpyDoc(*pCopyPam
);
4857 pFlysAtInsPos
= pUndo
->GetFlysAnchoredAt();
4861 pFlys
= sw::GetFlysAnchoredAt(rDoc
, rPos
.nNode
.GetIndex());
4862 pFlysAtInsPos
= pFlys
? &*pFlys
: nullptr;
4865 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
4866 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
4868 // Move the PaM one node back from the insert position, so that
4869 // the position doesn't get moved
4870 pCopyPam
->SetMark();
4871 bool bCanMoveBack
= pCopyPam
->Move(fnMoveBackward
, GoInContent
);
4872 // If the position was shifted from more than one node, an end node has been skipped
4873 bool bAfterTable
= false;
4874 if ((rPos
.nNode
.GetIndex() - pCopyPam
->GetPoint()->nNode
.GetIndex()) > SwNodeOffset(1))
4876 // First go back to the original place
4877 pCopyPam
->GetPoint()->nNode
= rPos
.nNode
;
4878 pCopyPam
->GetPoint()->nContent
= rPos
.nContent
;
4880 bCanMoveBack
= false;
4885 pCopyPam
->GetPoint()->nNode
--;
4886 assert(pCopyPam
->GetPoint()->nContent
.GetIndex() == 0);
4889 SwNodeRange
aRg( pStt
->nNode
, pEnd
->nNode
);
4890 SwNodeIndex
aInsPos( rPos
.nNode
);
4891 const bool bOneNode
= pStt
->nNode
== pEnd
->nNode
;
4892 SwTextNode
* pSttTextNd
= pStt
->nNode
.GetNode().GetTextNode();
4893 SwTextNode
* pEndTextNd
= pEnd
->nNode
.GetNode().GetTextNode();
4894 SwTextNode
* pDestTextNd
= aInsPos
.GetNode().GetTextNode();
4895 bool bCopyCollFormat
= !rDoc
.IsInsOnlyTextGlossary() &&
4896 ( (pDestTextNd
&& !pDestTextNd
->GetText().getLength()) ||
4897 ( !bOneNode
&& !rPos
.nContent
.GetIndex() ) );
4898 bool bCopyBookmarks
= true;
4899 bool bCopyPageSource
= false;
4900 SwNodeOffset
nDeleteTextNodes(0);
4902 // #i104585# copy outline num rule to clipboard (for ASCII filter)
4903 if (rDoc
.IsClipBoard() && m_rDoc
.GetOutlineNumRule())
4905 rDoc
.SetOutlineNumRule(*m_rDoc
.GetOutlineNumRule());
4909 // Correct the search for a previous list:
4910 // First search for non-outline numbering list. Then search for non-outline
4912 // Keep also the <ListId> value for possible propagation.
4913 OUString aListIdToPropagate
;
4914 const SwNumRule
* pNumRuleToPropagate
=
4915 rDoc
.SearchNumRule( rPos
, false, true, false, 0, aListIdToPropagate
, nullptr, true );
4916 if ( !pNumRuleToPropagate
)
4918 pNumRuleToPropagate
=
4919 rDoc
.SearchNumRule( rPos
, false, false, false, 0, aListIdToPropagate
, nullptr, true );
4922 // Do not propagate previous found list, if
4923 // - destination is an empty paragraph which is not in a list and
4924 // - source contains at least one paragraph which is not in a list
4925 if ( pNumRuleToPropagate
&&
4926 pDestTextNd
&& !pDestTextNd
->GetText().getLength() &&
4927 !pDestTextNd
->IsInList() &&
4928 !lcl_ContainsOnlyParagraphsInList( rPam
) )
4930 pNumRuleToPropagate
= nullptr;
4933 // This do/while block is only there so that we can break out of it!
4937 ++nDeleteTextNodes
; // must be joined in Undo
4938 // Don't copy the beginning completely?
4939 if( !bCopyCollFormat
|| bColumnSel
|| pStt
->nContent
.GetIndex() )
4941 SwIndex
aDestIdx( rPos
.nContent
);
4942 bool bCopyOk
= false;
4945 if( pStt
->nContent
.GetIndex() || bOneNode
)
4946 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
,
4947 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
4950 pDestTextNd
= pSttTextNd
->MakeCopy(rDoc
, aInsPos
, true)->GetTextNode();
4953 aDestIdx
.Assign( pDestTextNd
, 0 );
4954 bCopyCollFormat
= true;
4956 else if( !bOneNode
|| bColumnSel
)
4958 const sal_Int32 nContentEnd
= pEnd
->nContent
.GetIndex();
4960 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
4961 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
4964 if (bCanMoveBack
&& rPos
== *pCopyPam
->GetPoint())
4966 // after the SplitNode, span the CpyPam correctly again
4967 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
4968 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
4971 pDestTextNd
= rDoc
.GetNodes()[ aInsPos
.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
4973 pDestTextNd
, pDestTextNd
->GetText().getLength());
4975 // Correct the area again
4978 bool bChg
= pEnd
!= rPam
.GetPoint();
4981 rPam
.Move( fnMoveBackward
, GoInContent
);
4985 else if( rPos
== *pEnd
)
4987 // The end was also moved
4989 pEnd
->nContent
.Assign( pDestTextNd
, nContentEnd
);
4991 // tdf#63022 always reset pEndTextNd after SplitNode
4992 aRg
.aEnd
= pEnd
->nNode
;
4993 pEndTextNd
= pEnd
->nNode
.GetNode().GetTextNode();
4997 if( bCopyCollFormat
&& bOneNode
)
5004 const sal_Int32 nCpyLen
= ( bOneNode
5005 ? pEnd
->nContent
.GetIndex()
5006 : pSttTextNd
->GetText().getLength())
5007 - pStt
->nContent
.GetIndex();
5008 pSttTextNd
->CopyText( pDestTextNd
, aDestIdx
,
5009 pStt
->nContent
, nCpyLen
);
5011 pEnd
->nContent
-= nCpyLen
;
5018 if (bCopyCollFormat
)
5020 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5021 pSttTextNd
->CopyCollFormat(*pDestTextNd
, false);
5025 // copy at-char flys in rPam
5026 SwNodeIndex
temp(*pDestTextNd
); // update to new (start) node for flys
5027 // tdf#126626 prevent duplicate Undos
5028 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5029 CopyFlyInFlyImpl(aRg
, &rPam
, temp
, false);
5035 else if( pDestTextNd
)
5037 // Problems with insertion of table selections into "normal" text solved.
5038 // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
5039 // the undo operation will try to merge this node after removing the table.
5040 // If we didn't split a textnode, the PaM should start at the inserted table node
5041 if( rPos
.nContent
.GetIndex() == pDestTextNd
->Len() )
5042 { // Insertion at the last position of a textnode (empty or not)
5043 ++aInsPos
; // The table will be inserted behind the text node
5045 else if( rPos
.nContent
.GetIndex() )
5046 { // Insertion in the middle of a text node, it has to be split
5047 // (and joined from undo)
5050 const sal_Int32 nContentEnd
= pEnd
->nContent
.GetIndex();
5052 ::sw::UndoGuard
const ug(rDoc
.GetIDocumentUndoRedo());
5053 rDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
5056 if (bCanMoveBack
&& rPos
== *pCopyPam
->GetPoint())
5058 // after the SplitNode, span the CpyPam correctly again
5059 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5060 pCopyPam
->Move( fnMoveBackward
, GoInContent
);
5063 // Correct the area again
5066 // The end would also be moved
5067 else if( rPos
== *pEnd
)
5069 rPos
.nNode
-=SwNodeOffset(2);
5070 rPos
.nContent
.Assign( rPos
.nNode
.GetNode().GetContentNode(),
5076 else if( bCanMoveBack
)
5077 { // Insertion at the first position of a text node. It will not be split, the table
5078 // will be inserted before the text node.
5079 // See below, before the SetInsertRange function of the undo object will be called,
5080 // the CpyPam would be moved to the next content position. This has to be avoided
5081 // We want to be moved to the table node itself thus we have to set bCanMoveBack
5082 // and to manipulate pCopyPam.
5083 bCanMoveBack
= false;
5084 pCopyPam
->GetPoint()->nNode
--;
5088 pDestTextNd
= aInsPos
.GetNode().GetTextNode();
5091 SwIndex
aDestIdx( rPos
.nContent
);
5094 pDestTextNd
= rDoc
.GetNodes().MakeTextNode( aInsPos
,
5095 rDoc
.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD
));
5096 aDestIdx
.Assign( pDestTextNd
, 0 );
5099 // if we have to insert an extra text node
5100 // at the destination, this node will be our new destination
5101 // (text) node, and thus we increment nDeleteTextNodes. This
5102 // will ensure that this node will be deleted during Undo.
5103 ++nDeleteTextNodes
; // must be deleted
5106 const bool bEmptyDestNd
= pDestTextNd
->GetText().isEmpty();
5109 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5114 pEndTextNd
->CopyText( pDestTextNd
, aDestIdx
, SwIndex( pEndTextNd
),
5115 pEnd
->nContent
.GetIndex() );
5117 // Also copy all format templates
5118 if( bCopyCollFormat
&& ( bOneNode
|| bEmptyDestNd
))
5120 // tdf#138897 no Undo for applying style, SwUndoInserts does it
5121 pEndTextNd
->CopyCollFormat(*pDestTextNd
, false);
5129 SfxItemSet
aBrkSet( rDoc
.GetAttrPool(), aBreakSetRange
);
5130 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5132 if (pSttTextNd
&& bCopyCollFormat
&& pDestTextNd
->HasSwAttrSet())
5134 aBrkSet
.Put( *pDestTextNd
->GetpSwAttrSet() );
5135 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_BREAK
, false ) )
5136 pDestTextNd
->ResetAttr( RES_BREAK
);
5137 if( SfxItemState::SET
== aBrkSet
.GetItemState( RES_PAGEDESC
, false ) )
5138 pDestTextNd
->ResetAttr( RES_PAGEDESC
);
5143 SwPosition
startPos(SwNodeIndex(pCopyPam
->GetPoint()->nNode
, +1),
5144 SwIndex(SwNodeIndex(pCopyPam
->GetPoint()->nNode
, +1).GetNode().GetContentNode()));
5146 { // pCopyPam is actually 1 before the copy range so move it fwd
5147 SwPaM
temp(*pCopyPam
->GetPoint());
5148 temp
.Move(fnMoveForward
, GoInContent
);
5149 startPos
= *temp
.GetPoint();
5151 assert(startPos
.nNode
.GetNode().IsContentNode());
5152 std::pair
<SwPaM
const&, SwPosition
const&> tmp(rPam
, startPos
);
5153 if( aInsPos
== pEnd
->nNode
)
5155 SwNodeIndex
aSaveIdx( aInsPos
, -1 );
5156 assert(pStt
->nNode
!= pEnd
->nNode
);
5157 pEnd
->nContent
= 0; // TODO why this?
5158 CopyWithFlyInFly(aRg
, aInsPos
, &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5160 pEnd
->nNode
= aSaveIdx
;
5161 pEnd
->nContent
.Assign( aSaveIdx
.GetNode().GetTextNode(), 0 );
5164 CopyWithFlyInFly(aRg
, aInsPos
, &tmp
, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags
);
5166 bCopyBookmarks
= false;
5169 // at-char anchors post SplitNode are on index 0 of 2nd node and will
5170 // remain there - move them back to the start (end would also work?)
5171 // ... also for at-para anchors; here start is preferable because
5172 // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
5175 // init *again* - because CopyWithFlyInFly moved startPos
5176 SwPosition
startPos(SwNodeIndex(pCopyPam
->GetPoint()->nNode
, +1),
5177 SwIndex(SwNodeIndex(pCopyPam
->GetPoint()->nNode
, +1).GetNode().GetContentNode()));
5179 { // pCopyPam is actually 1 before the copy range so move it fwd
5180 SwPaM
temp(*pCopyPam
->GetPoint());
5181 temp
.Move(fnMoveForward
, GoInContent
);
5182 startPos
= *temp
.GetPoint();
5184 assert(startPos
.nNode
.GetNode().IsContentNode());
5185 SwPosition
startPosAtPara(startPos
);
5186 startPosAtPara
.nContent
.Assign(nullptr, 0);
5188 for (SwFrameFormat
* pFly
: *pFlysAtInsPos
)
5190 SwFormatAnchor
const*const pAnchor
= &pFly
->GetAnchor();
5191 if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
5193 SwFormatAnchor
anchor(*pAnchor
);
5194 anchor
.SetAnchor( &startPos
);
5195 pFly
->SetFormatAttr(anchor
);
5197 else if (pAnchor
->GetAnchorId() == RndStdIds::FLY_AT_PARA
)
5199 SwFormatAnchor
anchor(*pAnchor
);
5200 anchor
.SetAnchor( &startPosAtPara
);
5201 pFly
->SetFormatAttr(anchor
);
5206 if ((flags
& SwCopyFlags::CopyAll
) || aRg
.aStart
!= aRg
.aEnd
)
5208 // Put the breaks back into the first node
5209 if( aBrkSet
.Count() && nullptr != ( pDestTextNd
= rDoc
.GetNodes()[
5210 pCopyPam
->GetPoint()->nNode
.GetIndex()+1 ]->GetTextNode()))
5212 pDestTextNd
->SetAttr( aBrkSet
);
5213 bCopyPageSource
= true;
5219 // it is not possible to make this test when copy from the clipBoard to document
5220 // in this case the PageNum not exist anymore
5221 // tdf#39400 and tdf#97526
5222 // when copy from document to ClipBoard, and it is from the first page
5223 // and not the source has the page break
5224 if (rDoc
.IsClipBoard() && (rPam
.GetPageNum(pStt
== rPam
.GetPoint()) == 1) && !bCopyPageSource
)
5228 pDestTextNd
->ResetAttr(RES_BREAK
); // remove the page-break
5229 pDestTextNd
->ResetAttr(RES_PAGEDESC
);
5234 // Adjust position (in case it was moved / in another node)
5235 rPos
.nContent
.Assign( rPos
.nNode
.GetNode().GetContentNode(),
5236 rPos
.nContent
.GetIndex() );
5238 if( rPos
.nNode
!= aInsPos
)
5240 pCopyPam
->GetMark()->nNode
= aInsPos
;
5241 if (aInsPos
< rPos
.nNode
)
5242 { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
5243 pCopyPam
->GetContentNode(false)->MakeEndIndex(&pCopyPam
->GetMark()->nContent
);
5245 else // incremented in (!pSttTextNd && pDestTextNd) above
5247 pCopyPam
->GetMark()->nContent
.Assign(pCopyPam
->GetContentNode(false), 0);
5249 rPos
= *pCopyPam
->GetMark();
5252 *pCopyPam
->GetMark() = rPos
;
5255 pCopyPam
->Move( fnMoveForward
, bCanMoveBack
? GoInContent
: GoInNode
);
5258 pCopyPam
->GetPoint()->nNode
++;
5260 // Reset the offset to 0 as it was before the insertion
5261 pCopyPam
->GetPoint()->nContent
.Assign(pCopyPam
->GetPoint()->nNode
.GetNode().GetContentNode(), 0);
5262 // If the next node is a start node, then step back: the start node
5263 // has been copied and needs to be in the selection for the undo
5264 if (pCopyPam
->GetPoint()->nNode
.GetNode().IsStartNode())
5265 pCopyPam
->GetPoint()->nNode
--;
5268 pCopyPam
->Exchange();
5270 // Also copy all bookmarks
5271 if( bCopyBookmarks
&& m_rDoc
.getIDocumentMarkAccess()->getAllMarksCount() )
5273 sw::CopyBookmarks(rPam
, *pCopyPam
->Start());
5276 if( RedlineFlags::DeleteRedlines
& eOld
)
5278 assert(*pCopyPam
->GetPoint() == rPos
);
5279 // the Node rPos points to may be deleted so unregister ...
5280 rPos
.nContent
.Assign(nullptr, 0);
5281 lcl_DeleteRedlines(rPam
, *pCopyPam
);
5282 rPos
= *pCopyPam
->GetPoint(); // ... and restore.
5285 // If Undo is enabled, store the inserted area
5286 if (rDoc
.GetIDocumentUndoRedo().DoesUndo())
5288 // append it after styles have been copied when copying nodes
5289 rDoc
.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
5290 pUndo
->SetInsertRange(*pCopyPam
, true, nDeleteTextNodes
);
5295 pCpyRange
->SetMark();
5296 *pCpyRange
->GetPoint() = *pCopyPam
->GetPoint();
5297 *pCpyRange
->GetMark() = *pCopyPam
->GetMark();
5300 if ( pNumRuleToPropagate
!= nullptr )
5302 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
5303 // Don't reset indent attributes, that would mean loss of direct
5305 rDoc
.SetNumRule( *pCopyPam
, *pNumRuleToPropagate
, false, nullptr,
5306 aListIdToPropagate
, true, /*bResetIndentAttrs=*/false );
5309 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
5310 rDoc
.getIDocumentState().SetModified();
5317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */