File: | home/maarten/src/libreoffice/core/sw/inc/pam.hxx |
Warning: | line 225, column 16 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
96 | using namespace ::com::sun::star::i18n; | |||
97 | ||||
98 | namespace | |||
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 | ||||
231 | namespace 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 | ||||
335 | namespace | |||
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 | |||
503 | namespace 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 | ||||
621 | namespace | |||
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 | ||||
1135 | namespace //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 | ||||
1852 | namespace sw | |||
1853 | { | |||
1854 | ||||
1855 | namespace 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 | ||||
1865 | DocumentContentOperationsManager::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 | */ | |||
1875 | static 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 | |||
1892 | bool | |||
1893 | DocumentContentOperationsManager::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. | |||
1973 | void 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 | ||||
1995 | void 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 | ||||
2012 | void 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 | ||||
2028 | bool 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> | |||
2186 | bool 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). | |||
2208 | bool 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 | ||||
2434 | bool 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 | ||||
2594 | bool 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. | |||
2623 | bool 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 | ||||
2726 | bool 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 | ||||
2831 | void 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 | ||||
2902 | SwFlyFrameFormat* 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 | ||||
2922 | SwFlyFrameFormat* 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 | ||||
2936 | SwFlyFrameFormat* 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 | ||||
2960 | SwFlyFrameFormat* 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 | ||||
2978 | void 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 | |||
3002 | SwDrawFrameFormat* 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(); | |||
| ||||
3016 | const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId); | |||
3017 | ||||
3018 | const SwNodeIndex* pChkIdx = nullptr; | |||
3019 | if ( pAnchor == nullptr ) | |||
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 | |||
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() ); | |||
3044 | eAnchorId = aAnch.GetAnchorId(); | |||
3045 | if ( eAnchorId == RndStdIds::FLY_AT_FLY ) | |||
3046 | { | |||
3047 | const SwStartNode* pStartNode = rRg.GetNode().FindFlyStartNode(); | |||
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 | ||||
3114 | bool 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 | ||||
3256 | bool 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 | ||||
3294 | bool 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... | |||
3375 | bool 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 | ||||
3410 | void 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 | ||||
3432 | void 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 | |||
3463 | void 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() | |||
3610 | void 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 | */ | |||
3873 | bool 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 | ||||
3911 | DocumentContentOperationsManager::~DocumentContentOperationsManager() | |||
3912 | { | |||
3913 | } | |||
3914 | //Private methods | |||
3915 | ||||
3916 | bool 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 | ||||
4057 | bool 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 | ||||
4088 | bool 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 | ||||
4104 | bool 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_ | |||
4301 | bool 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 | ||||
4546 | SwFlyFrameFormat* 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 | ||||
4574 | static 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 | ||||
4599 | static 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 | ||||
4628 | bool 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, ©Range); | |||
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, ©Range); | |||
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 | ||||
4695 | bool 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: */ |
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 | |||||
30 | class SwDoc; | ||||
31 | class SwPaM; | ||||
32 | class Point; | ||||
33 | |||||
34 | /// Marks a position in the document model. | ||||
35 | struct 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 | |||||
61 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) std::ostream &operator <<(std::ostream& s, const SwPosition& position); | ||||
62 | |||||
63 | // Result of comparing positions. | ||||
64 | enum 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 | |||||
76 | template<typename T> | ||||
77 | SwComparePosition 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 | ||||
122 | struct SwMoveFnCollection; | ||||
123 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) extern SwMoveFnCollection const & fnMoveForward; ///< SwPam::Move()/Find() default argument. | ||||
124 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) extern SwMoveFnCollection const & fnMoveBackward; | ||||
125 | |||||
126 | using SwGoInDoc = auto (*)(SwPaM& rPam, SwMoveFnCollection const & fnMove) -> bool; | ||||
127 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInDoc( SwPaM&, SwMoveFnCollection const &); | ||||
128 | bool GoInSection( SwPaM&, SwMoveFnCollection const &); | ||||
129 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInNode( SwPaM&, SwMoveFnCollection const &); | ||||
130 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) bool GoInContent( SwPaM&, SwMoveFnCollection const &); | ||||
131 | bool GoInContentCells( SwPaM&, SwMoveFnCollection const &); | ||||
132 | bool GoInContentSkipHidden( SwPaM&, SwMoveFnCollection const &); | ||||
133 | bool GoInContentCellsSkipHidden( SwPaM&, SwMoveFnCollection const &); | ||||
134 | |||||
135 | /// PaM is Point and Mark: a selection of the document model. | ||||
136 | class 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 | |||||
146 | public: | ||||
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
| ||||
| |||||
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 | |||||
278 | SW_DLLPUBLIC__attribute__ ((visibility("default"))) std::ostream &operator <<(std::ostream& s, const SwPaM& pam); | ||||
279 | |||||
280 | bool CheckNodesRange(const SwNodeIndex&, const SwNodeIndex&, bool bChkSection); | ||||
281 | |||||
282 | #endif // INCLUDED_SW_INC_PAM_HXX | ||||
283 | |||||
284 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |