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