Bug Summary

File:home/maarten/src/libreoffice/core/sw/inc/pam.hxx
Warning:line 225, column 16
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name DocumentContentOperationsManager.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SW_DLLIMPLEMENTATION -D SWUI_DLL_NAME="libswuilo.so" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sw/source/core/inc -I /home/maarten/src/libreoffice/core/sw/source/filter/inc -I /home/maarten/src/libreoffice/core/sw/source/uibase/inc -I /home/maarten/src/libreoffice/core/sw/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sw/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/sw/generated -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sw/source/core/doc/DocumentContentOperationsManager.cxx

/home/maarten/src/libreoffice/core/sw/source/core/doc/DocumentContentOperationsManager.cxx

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

/home/maarten/src/libreoffice/core/sw/inc/pam.hxx

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 */
19#ifndef INCLUDED_SW_INC_PAM_HXX
20#define INCLUDED_SW_INC_PAM_HXX
21
22#include <sal/types.h>
23#include "ring.hxx"
24#include "index.hxx"
25#include "ndindex.hxx"
26#include "swdllapi.h"
27
28#include <iostream>
29
30class SwDoc;
31class SwPaM;
32class Point;
33
34/// Marks a position in the document model.
35struct SAL_WARN_UNUSED__attribute__((warn_unused)) SW_DLLPUBLIC__attribute__ ((visibility("default"))) SwPosition
36{
37 SwNodeIndex nNode;
38 SwIndex nContent;
39
40 SwPosition( const SwNodeIndex &rNode, const SwIndex &rContent );
41 explicit SwPosition( const SwNodeIndex &rNode );
42 explicit SwPosition( const SwNode& rNode );
43 explicit SwPosition( SwContentNode& rNode, const sal_Int32 nOffset = 0 );
44
45 /**
46 Returns the document this position is in.
47
48 @return the document this position is in.
49 */
50 SwDoc& GetDoc() const;
51
52 bool operator < (const SwPosition &) const;
53 bool operator > (const SwPosition &) const;
54 bool operator <=(const SwPosition &) const;
55 bool operator >=(const SwPosition &) const;
56 bool operator ==(const SwPosition &) const;
57 bool operator !=(const SwPosition &) const;
58 void dumpAsXml(xmlTextWriterPtr pWriter) const;
59};
60
61SW_DLLPUBLIC__attribute__ ((visibility("default"))) std::ostream &operator <<(std::ostream& s, const SwPosition& position);
62
63// Result of comparing positions.
64enum class SwComparePosition {
65 Before, ///< Pos1 before Pos2.
66 Behind, ///< Pos1 behind Pos2.
67 Inside, ///< Pos1 completely contained in Pos2.
68 Outside, ///< Pos2 completely contained in Pos1.
69 Equal, ///< Pos1 is as large as Pos2.
70 OverlapBefore, ///< Pos1 overlaps Pos2 at the beginning.
71 OverlapBehind, ///< Pos1 overlaps Pos2 at the end.
72 CollideStart, ///< Pos1 start touches at Pos2 end.
73 CollideEnd ///< Pos1 end touches at Pos2 start.
74};
75
76template<typename T>
77SwComparePosition ComparePosition(
78 const T& rStt1, const T& rEnd1,
79 const T& rStt2, const T& rEnd2 )
80{
81 SwComparePosition nRet;
82 if( rStt1 < rStt2 )
83 {
84 if( rEnd1 > rStt2 )
85 {
86 if( rEnd1 >= rEnd2 )
87 nRet = SwComparePosition::Outside;
88 else
89 nRet = SwComparePosition::OverlapBefore;
90
91 }
92 else if( rEnd1 == rStt2 )
93 nRet = SwComparePosition::CollideEnd;
94 else
95 nRet = SwComparePosition::Before;
96 }
97 else if( rEnd2 > rStt1 )
98 {
99 if( rEnd2 >= rEnd1 )
100 {
101 if( rEnd2 == rEnd1 && rStt2 == rStt1 )
102 nRet = SwComparePosition::Equal;
103 else
104 nRet = SwComparePosition::Inside;
105 }
106 else
107 {
108 if (rStt1 == rStt2)
109 nRet = SwComparePosition::Outside;
110 else
111 nRet = SwComparePosition::OverlapBehind;
112 }
113 }
114 else if( rEnd2 == rStt1 )
115 nRet = SwComparePosition::CollideStart;
116 else
117 nRet = SwComparePosition::Behind;
118 return nRet;
119}
120
121/// SwPointAndMark / SwPaM
122struct SwMoveFnCollection;
123SW_DLLPUBLIC__attribute__ ((visibility("default"))) extern SwMoveFnCollection const & fnMoveForward; ///< SwPam::Move()/Find() default argument.
124SW_DLLPUBLIC__attribute__ ((visibility("default"))) extern SwMoveFnCollection const & fnMoveBackward;
125
126using SwGoInDoc = auto (*)(SwPaM& rPam, SwMoveFnCollection const & fnMove) -> bool;
127SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInDoc( SwPaM&, SwMoveFnCollection const &);
128bool GoInSection( SwPaM&, SwMoveFnCollection const &);
129SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInNode( SwPaM&, SwMoveFnCollection const &);
130SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInContent( SwPaM&, SwMoveFnCollection const &);
131bool GoInContentCells( SwPaM&, SwMoveFnCollection const &);
132bool GoInContentSkipHidden( SwPaM&, SwMoveFnCollection const &);
133bool GoInContentCellsSkipHidden( SwPaM&, SwMoveFnCollection const &);
134
135/// PaM is Point and Mark: a selection of the document model.
136class SAL_WARN_UNUSED__attribute__((warn_unused)) SW_DLLPUBLIC__attribute__ ((visibility("default"))) SwPaM : public sw::Ring<SwPaM>
137{
138 SwPosition m_Bound1;
139 SwPosition m_Bound2;
140 SwPosition * m_pPoint; ///< points at either m_Bound1 or m_Bound2
141 SwPosition * m_pMark; ///< points at either m_Bound1 or m_Bound2
142 bool m_bIsInFrontOfLabel;
143
144 SwPaM(SwPaM const& rPaM) = delete;
145
146public:
147 explicit SwPaM( const SwPosition& rPos, SwPaM* pRing = nullptr );
148 SwPaM( const SwPosition& rMk, const SwPosition& rPt, SwPaM* pRing = nullptr );
149 SwPaM( const SwNodeIndex& rMk, const SwNodeIndex& rPt,
150 long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = nullptr );
151 SwPaM( const SwNode& rMk, const SwNode& rPt,
152 long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = nullptr );
153 SwPaM( const SwNodeIndex& rMk, sal_Int32 nMkContent,
154 const SwNodeIndex& rPt, sal_Int32 nPtContent, SwPaM* pRing = nullptr );
155 SwPaM( const SwNode& rMk, sal_Int32 nMkContent,
156 const SwNode& rPt, sal_Int32 nPtContent, SwPaM* pRing = nullptr );
157 SwPaM( const SwNode& rNd, sal_Int32 nContent = 0, SwPaM* pRing = nullptr );
158 SwPaM( const SwNodeIndex& rNd, sal_Int32 nContent = 0, SwPaM* pRing = nullptr );
159 virtual ~SwPaM() override;
160
161 /// this takes a second parameter, which indicates the Ring that
162 /// the new PaM should be part of (may be null)
163 SwPaM(SwPaM const& rPaM, SwPaM * pRing);
164 /// @@@ semantic: no copy assignment for super class Ring.
165 SwPaM& operator=( const SwPaM & );
166
167 /// Movement of cursor.
168 bool Move( SwMoveFnCollection const & fnMove = fnMoveForward,
169 SwGoInDoc fnGo = GoInContent );
170
171 bool IsInFrontOfLabel() const { return m_bIsInFrontOfLabel; }
172 void SetInFrontOfLabel_( bool bNew ) { m_bIsInFrontOfLabel = bNew; }
173
174 /// Unless this is called, the getter method of Mark will return Point.
175 virtual void SetMark();
176
177 void DeleteMark()
178 {
179 if (m_pMark != m_pPoint)
180 {
181 /** clear the mark position; this helps if mark's SwIndex is
182 registered at some node, and that node is then deleted */
183 *m_pMark = SwPosition( SwNodeIndex( GetNode().GetNodes() ) );
184 m_pMark = m_pPoint;
185 }
186 }
187#ifdef DBG_UTIL
188 void Exchange();
189
190#else
191 void Exchange()
192 {
193 if (m_pPoint != m_pMark)
194 {
195 SwPosition *pTmp = m_pPoint;
196 m_pPoint = m_pMark;
197 m_pMark = pTmp;
198 }
199 }
200#endif
201
202 /** A PaM marks a selection if Point and Mark are distinct positions.
203 @return true if the PaM spans a selection
204 */
205 bool HasMark() const { return m_pPoint != m_pMark; }
206
207 const SwPosition *GetPoint() const { return m_pPoint; }
208 SwPosition *GetPoint() { return m_pPoint; }
209 const SwPosition *GetMark() const { return m_pMark; }
210 SwPosition *GetMark() { return m_pMark; }
211
212 const SwPosition *Start() const
213 { return (*m_pPoint) <= (*m_pMark) ? m_pPoint : m_pMark; }
214 SwPosition *Start()
215 { return (*m_pPoint) <= (*m_pMark) ? m_pPoint : m_pMark; }
216
217 const SwPosition *End() const
218 { return (*m_pPoint) > (*m_pMark) ? m_pPoint : m_pMark; }
219 SwPosition *End()
220 { return (*m_pPoint) > (*m_pMark) ? m_pPoint : m_pMark; }
221
222 /// @return current Node at Point/Mark
223 SwNode & GetNode ( bool bPoint = true ) const
224 {
225 return ( bPoint
9.1
'bPoint' is true
9.1
'bPoint' is true
? m_pPoint->nNode : m_pMark->nNode )
.GetNode();
10
'?' condition is true
11
Called C++ object pointer is null
226 }
227
228 /// @return current ContentNode at Point/Mark
229 SwContentNode* GetContentNode( bool bPoint = true ) const
230 {
231 return GetNode(bPoint).GetContentNode();
232 }
233
234 /**
235 Normalizes PaM, i.e. sort point and mark.
236
237 @param bPointFirst true: If the point is behind the mark then swap.
238 false: If the mark is behind the point then swap.
239 */
240 void Normalize(bool bPointFirst = true);
241
242 /// @return the document (SwDoc) at which the PaM is registered
243 SwDoc& GetDoc() const { return m_pPoint->nNode.GetNode().GetDoc(); }
244
245 SwPosition& GetBound( bool bOne = true )
246 { return bOne ? m_Bound1 : m_Bound2; }
247 const SwPosition& GetBound( bool bOne = true ) const
248 { return bOne ? m_Bound1 : m_Bound2; }
249
250 /// Get number of page which contains cursor.
251 sal_uInt16 GetPageNum( bool bAtPoint = true, const Point* pLayPos = nullptr );
252
253 /** Is in something protected (readonly) or selection contains
254 something protected. */
255 bool HasReadonlySel( bool bFormView ) const;
256
257 bool ContainsPosition(const SwPosition & rPos) const
258 {
259 return *Start() <= rPos && rPos <= *End();
260 }
261
262 OUString GetText() const;
263 void InvalidatePaM();
264 SwPaM* GetNext()
265 { return GetNextInRing(); }
266 const SwPaM* GetNext() const
267 { return GetNextInRing(); }
268 SwPaM* GetPrev()
269 { return GetPrevInRing(); }
270 const SwPaM* GetPrev() const
271 { return GetPrevInRing(); }
272 bool IsMultiSelection() const
273 { return !unique(); }
274
275 void dumpAsXml(xmlTextWriterPtr pWriter) const;
276};
277
278SW_DLLPUBLIC__attribute__ ((visibility("default"))) std::ostream &operator <<(std::ostream& s, const SwPaM& pam);
279
280bool CheckNodesRange(const SwNodeIndex&, const SwNodeIndex&, bool bChkSection);
281
282#endif // INCLUDED_SW_INC_PAM_HXX
283
284/* vim:set shiftwidth=4 softtabstop=4 expandtab: */