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 <DocumentRedlineManager.hxx>
20 #include <frmfmt.hxx>
21 #include <rootfrm.hxx>
22 #include <txtfrm.hxx>
23 #include <doc.hxx>
24 #include <docsh.hxx>
25 #include <fmtfld.hxx>
26 #include <frmtool.hxx>
27 #include <IDocumentUndoRedo.hxx>
28 #include <IDocumentFieldsAccess.hxx>
29 #include <IDocumentLayoutAccess.hxx>
30 #include <IDocumentState.hxx>
31 #include <redline.hxx>
32 #include <UndoRedline.hxx>
33 #include <docary.hxx>
34 #include <ndtxt.hxx>
35 #include <unocrsr.hxx>
36 #include <ftnidx.hxx>
37 #include <authfld.hxx>
38 #include <strings.hrc>
39 #include <swmodule.hxx>
40 #include <osl/diagnose.h>
41 #include <editeng/prntitem.hxx>
42
43 using namespace com::sun::star;
44
45 #ifdef DBG_UTIL
46
47 #define ERROR_PREFIX "redline table corrupted: "
48
49 namespace
50 {
51 // helper function for lcl_CheckRedline
52 // 1. make sure that pPos->nContent points into pPos->nNode
53 // 2. check that position is valid and doesn't point after text
lcl_CheckPosition(const SwPosition * pPos)54 void lcl_CheckPosition( const SwPosition* pPos )
55 {
56 assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
57 == pPos->nContent.GetIdxReg());
58
59 SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
60 if( pTextNode == nullptr )
61 {
62 assert(pPos->nContent == 0);
63 }
64 else
65 {
66 assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
67 }
68 }
69
lcl_CheckPam(const SwPaM * pPam)70 void lcl_CheckPam( const SwPaM* pPam )
71 {
72 assert(pPam);
73 lcl_CheckPosition( pPam->GetPoint() );
74 lcl_CheckPosition( pPam->GetMark() );
75 }
76
77 // check validity of the redline table. Checks redline bounds, and make
78 // sure the redlines are sorted and non-overlapping.
lcl_CheckRedline(IDocumentRedlineAccess & redlineAccess)79 void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess )
80 {
81 const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
82
83 // verify valid redline positions
84 for(SwRangeRedline* i : rTable)
85 lcl_CheckPam( i );
86
87 for(SwRangeRedline* j : rTable)
88 {
89 // check for empty redlines
90 // note: these can destroy sorting in SwTextNode::Update()
91 // if there's another one without mark on the same pos.
92 OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
93 ( j->GetContentIdx() != nullptr ),
94 ERROR_PREFIX "empty redline" );
95 }
96
97 // verify proper redline sorting
98 for( size_t n = 1; n < rTable.size(); ++n )
99 {
100 const SwRangeRedline* pPrev = rTable[ n-1 ];
101 const SwRangeRedline* pCurrent = rTable[ n ];
102
103 // check redline sorting
104 SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
105 ERROR_PREFIX "not sorted correctly" );
106
107 // check for overlapping redlines
108 SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
109 ERROR_PREFIX "overlapping redlines" );
110 }
111
112 assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
113 }
114 }
115
116 #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
117
118 #else
119
120 #define CHECK_REDLINE( pDoc )
121
122 #endif
123
124 namespace sw {
125
UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)126 static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
127 {
128 auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
129 SwFieldIds::TableOfAuthorities, OUString(), false)));
130 if (pAuthType) // created on demand...
131 {
132 pAuthType->DelSequenceArray();
133 }
134 rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
135 rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
136 rIDFA.UpdateExpFields(nullptr, false);
137 rIDFA.UpdateRefFields();
138 }
139
UpdateFramesForAddDeleteRedline(SwDoc & rDoc,SwPaM const & rPam)140 void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
141 {
142 if (rDoc.IsClipBoard())
143 {
144 return;
145 }
146 // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
147 // the AppendFootnote/RemoveFootnote will do it by itself!
148 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
149 SwPosition currentStart(*rPam.Start());
150 SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
151 while (!pStartNode)
152 {
153 // note: branch only taken for redlines, not fieldmarks
154 SwStartNode *const pTableOrSectionNode(
155 currentStart.nNode.GetNode().IsTableNode()
156 ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
157 : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
158 if ( !pTableOrSectionNode )
159 {
160 SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
161 return;
162 }
163 for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
164 {
165 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
166 }
167 for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
168 {
169 if (pLayout->HasMergedParas())
170 {
171 if (pTableOrSectionNode->IsTableNode())
172 {
173 static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
174 }
175 else
176 {
177 static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
178 }
179 }
180 }
181 currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
182 currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
183 pStartNode = currentStart.nNode.GetNode().GetTextNode();
184 }
185 if (currentStart < *rPam.End())
186 {
187 SwTextNode * pNode(pStartNode);
188 do
189 {
190 std::vector<SwTextFrame*> frames;
191 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
192 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
193 {
194 if (pFrame->getRootFrame()->HasMergedParas())
195 {
196 frames.push_back(pFrame);
197 }
198 // set anchored objects as deleted
199 pFrame->SetDrawObjsAsDeleted(true);
200 }
201 if (frames.empty())
202 {
203 auto const& layouts(rDoc.GetAllLayouts());
204 assert(std::none_of(layouts.begin(), layouts.end(),
205 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
206 (void) layouts;
207 break;
208 }
209 auto eMode(sw::FrameMode::Existing);
210 SwTextNode * pLast(pNode);
211 for (SwTextFrame * pFrame : frames)
212 {
213 SwTextNode & rFirstNode(pFrame->GetMergedPara()
214 ? *pFrame->GetMergedPara()->pFirstNode
215 : *pNode);
216 assert(pNode == pStartNode
217 ? rFirstNode.GetIndex() <= pNode->GetIndex()
218 : &rFirstNode == pNode);
219 // clear old one first to avoid DelFrames confusing updates & asserts...
220 pFrame->SetMergedPara(nullptr);
221 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
222 *pFrame, rFirstNode, eMode));
223 eMode = sw::FrameMode::New; // Existing is not idempotent!
224 // the first node of the new redline is not necessarily the first
225 // node of the merged frame, there could be another redline nearby
226 sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
227 // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
228 if (pFrame->GetMergedPara())
229 {
230 pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
231 }
232 }
233 SwNodeIndex tmp(*pLast);
234 // skip over hidden sections!
235 pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
236 }
237 while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
238 }
239 // fields last - SwGetRefField::UpdateField requires up-to-date frames
240 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
241
242 // update SwPostItMgr / notes in the margin
243 rDoc.GetDocShell()->Broadcast(
244 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
245 }
246
UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc,SwPaM const & rPam)247 void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
248 {
249 if (rDoc.IsClipBoard())
250 {
251 return;
252 }
253 bool isAppendObjsCalled(false);
254 rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
255 SwPosition currentStart(*rPam.Start());
256 SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
257 while (!pStartNode)
258 {
259 // note: branch only taken for redlines, not fieldmarks
260 SwStartNode const*const pTableOrSectionNode(
261 currentStart.nNode.GetNode().IsTableNode()
262 ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
263 : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
264 assert(pTableOrSectionNode); // known pathology
265 for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
266 {
267 pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
268 }
269 if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
270 {
271 // note: this will also create frames for all currently hidden flys
272 // because it calls AppendAllObjs
273 SwNodeIndex const end(*pTableOrSectionNode->EndOfSectionNode());
274 ::MakeFrames(&rDoc, currentStart.nNode, end);
275 isAppendObjsCalled = true;
276 }
277 currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
278 currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
279 pStartNode = currentStart.nNode.GetNode().GetTextNode();
280 }
281 if (currentStart < *rPam.End())
282 {
283 SwTextNode * pNode(pStartNode);
284 do
285 {
286 std::vector<SwTextFrame*> frames;
287 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
288 for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
289 {
290 if (pFrame->getRootFrame()->HasMergedParas())
291 {
292 frames.push_back(pFrame);
293 }
294 // set anchored objects as not deleted
295 pFrame->SetDrawObjsAsDeleted(false);
296 }
297 if (frames.empty())
298 {
299 // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
300 if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
301 {
302 auto const& layouts(rDoc.GetAllLayouts());
303 assert(std::none_of(layouts.begin(), layouts.end(),
304 [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
305 (void) layouts;
306 }
307 isAppendObjsCalled = true; // skip that!
308 break;
309 }
310
311 // first, call CheckParaRedlineMerge on the first paragraph,
312 // to init flag on new merge range (if any) + 1st node post the merge
313 auto eMode(sw::FrameMode::Existing);
314 SwTextNode * pLast(pNode);
315 for (SwTextFrame * pFrame : frames)
316 {
317 if (auto const pMergedPara = pFrame->GetMergedPara())
318 {
319 pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
320 assert(pNode == pStartNode
321 ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
322 : pMergedPara->pFirstNode == pNode);
323 // clear old one first to avoid DelFrames confusing updates & asserts...
324 SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
325 pFrame->SetMergedPara(nullptr);
326 pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
327 *pFrame, rFirstNode, eMode));
328 eMode = sw::FrameMode::New; // Existing is not idempotent!
329 // update pNode so MakeFrames starts on 2nd node
330 pNode = &rFirstNode;
331 }
332 }
333 if (pLast != pNode)
334 {
335 // now start node until end of merge + 1 has proper flags; MakeFrames
336 // should pick up from the next node in need of frames by checking flags
337 SwNodeIndex const start(*pNode, +1);
338 SwNodeIndex const end(*pLast, +1); // end is exclusive
339 // note: this will also create frames for all currently hidden flys
340 // both on first and non-first nodes because it calls AppendAllObjs
341 ::MakeFrames(&rDoc, start, end);
342 isAppendObjsCalled = true;
343 // re-use this to move flys that are now on the wrong frame, with end
344 // of redline as "second" node; the nodes between start and end should
345 // be complete with MakeFrames already
346 sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
347 }
348 SwNodeIndex tmp(*pLast);
349 // skip over hidden sections!
350 pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
351 }
352 while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
353 }
354
355 if (!isAppendObjsCalled)
356 { // recreate flys in the one node the hard way...
357 for (auto const& pLayout : rDoc.GetAllLayouts())
358 {
359 if (pLayout->HasMergedParas())
360 {
361 AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
362 break;
363 }
364 }
365 }
366 // fields last - SwGetRefField::UpdateField requires up-to-date frames
367 UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
368
369 // update SwPostItMgr / notes in the margin
370 rDoc.GetDocShell()->Broadcast(
371 SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
372 }
373
374 } // namespace sw
375
376 namespace
377 {
IsPrevPos(const SwPosition & rPos1,const SwPosition & rPos2)378 bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
379 {
380 const SwContentNode* pCNd;
381 if( 0 != rPos2.nContent.GetIndex() )
382 return false;
383 if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() )
384 return false;
385 pCNd = rPos1.nNode.GetNode().GetContentNode();
386 return pCNd && rPos1.nContent.GetIndex() == pCNd->Len();
387 }
388
389 // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
lcl_CopyStyle(const SwPosition & rFrom,const SwPosition & rTo,bool bCopy=true)390 SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
391 {
392 SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
393 SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
394 if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
395 {
396 const SwPaM aPam(*pToNode);
397 SwDoc& rDoc = aPam.GetDoc();
398 // using Undo, copy paragraph style
399 SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
400 SwTextFormatColl* pToColl = pToNode->GetTextColl();
401 if (bCopy && pFromColl != pToColl)
402 rDoc.SetTextFormatColl(aPam, pFromColl);
403
404 // using Undo, remove direct paragraph formatting of the "To" paragraph,
405 // and apply here direct paragraph formatting of the "From" paragraph
406 SfxItemSet aTmp(
407 rDoc.GetAttrPool(),
408 svl::Items<
409 RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
410 RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
411 RES_CNTNT, RES_FRMATR_END - 1>{});
412 SfxItemSet aTmp2(aTmp);
413
414 pToNode->GetParaAttr(aTmp, 0, 0);
415 pFromNode->GetParaAttr(aTmp2, 0, 0);
416
417 bool bSameSet = aTmp == aTmp2;
418
419 if (!bSameSet)
420 {
421 for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
422 {
423 sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
424 if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
425 SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
426 aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
427 }
428 }
429
430 if (bCopy && !bSameSet)
431 rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
432 else if (!bCopy && (!bSameSet || pFromColl != pToColl))
433 return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
434 }
435 return nullptr;
436 }
437
438 // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
lcl_DeleteTrackedTableRow(SwPosition * pPos)439 void lcl_DeleteTrackedTableRow ( SwPosition* pPos )
440 {
441 if ( const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox() )
442 {
443 const SwTableLine* pLine = pBox->GetUpper();
444 const SvxPrintItem *pHasTextChangesOnlyProp =
445 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
446 // empty table row with property "HasTextChangesOnly" = false
447 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() &&
448 pLine->IsEmpty() )
449 {
450 SwCursor aCursor( *pPos, nullptr );
451 pPos->GetDoc().DeleteRow( aCursor );
452 }
453 }
454 }
455
456 // at rejection of a deletion in a table, remove the tracking of the table row
lcl_RemoveTrackingOfTableRow(const SwPosition * pPos)457 void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos )
458 {
459 if ( const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox() )
460 {
461 const SwTableLine* pLine = pBox->GetUpper();
462 const SvxPrintItem *pHasTextChangesOnlyProp =
463 pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
464 // table row property "HasTextChangesOnly" is set and its value is false
465 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
466 {
467 SvxPrintItem aUnsetTracking(RES_PRINT, true);
468 SwCursor aCursor( *pPos, nullptr );
469 pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
470 }
471 }
472 }
473
lcl_AcceptRedline(SwRedlineTable & rArr,SwRedlineTable::size_type & rPos,bool bCallDelete,const SwPosition * pSttRng=nullptr,const SwPosition * pEndRng=nullptr)474 bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
475 bool bCallDelete,
476 const SwPosition* pSttRng = nullptr,
477 const SwPosition* pEndRng = nullptr )
478 {
479 bool bRet = true;
480 SwRangeRedline* pRedl = rArr[ rPos ];
481 SwPosition *pRStt = nullptr, *pREnd = nullptr;
482 SwComparePosition eCmp = SwComparePosition::Outside;
483 if( pSttRng && pEndRng )
484 {
485 pRStt = pRedl->Start();
486 pREnd = pRedl->End();
487 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
488 }
489
490 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
491
492 switch( pRedl->GetType() )
493 {
494 case RedlineType::Insert:
495 case RedlineType::Format:
496 {
497 bool bCheck = false, bReplace = false;
498 switch( eCmp )
499 {
500 case SwComparePosition::Inside:
501 if( *pSttRng == *pRStt )
502 pRedl->SetStart( *pEndRng, pRStt );
503 else
504 {
505 if( *pEndRng != *pREnd )
506 {
507 // split up
508 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
509 pNew->SetStart( *pEndRng );
510 rArr.Insert( pNew ); ++rPos;
511 }
512 pRedl->SetEnd( *pSttRng, pREnd );
513 bCheck = true;
514 }
515 break;
516
517 case SwComparePosition::OverlapBefore:
518 pRedl->SetStart( *pEndRng, pRStt );
519 bReplace = true;
520 break;
521
522 case SwComparePosition::OverlapBehind:
523 pRedl->SetEnd( *pSttRng, pREnd );
524 bCheck = true;
525 break;
526
527 case SwComparePosition::Outside:
528 case SwComparePosition::Equal:
529 rArr.DeleteAndDestroy( rPos-- );
530 break;
531
532 default:
533 bRet = false;
534 }
535
536 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
537 {
538 // re-insert
539 rArr.Remove( pRedl );
540 rArr.Insert( pRedl );
541 }
542 }
543 break;
544 case RedlineType::Delete:
545 {
546 SwDoc& rDoc = pRedl->GetDoc();
547 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
548 bool bDelRedl = false;
549 switch( eCmp )
550 {
551 case SwComparePosition::Inside:
552 if( bCallDelete )
553 {
554 pDelStt = pSttRng;
555 pDelEnd = pEndRng;
556 }
557 break;
558
559 case SwComparePosition::OverlapBefore:
560 if( bCallDelete )
561 {
562 pDelStt = pRStt;
563 pDelEnd = pEndRng;
564 }
565 break;
566 case SwComparePosition::OverlapBehind:
567 if( bCallDelete )
568 {
569 pDelStt = pREnd;
570 pDelEnd = pSttRng;
571 }
572 break;
573
574 case SwComparePosition::Outside:
575 case SwComparePosition::Equal:
576 {
577 rArr.Remove( rPos-- );
578 bDelRedl = true;
579 if( bCallDelete )
580 {
581 pDelStt = pRedl->Start();
582 pDelEnd = pRedl->End();
583 }
584 }
585 break;
586 default:
587 bRet = false;
588 }
589
590 if( pDelStt && pDelEnd )
591 {
592 SwPaM aPam( *pDelStt, *pDelEnd );
593 SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
594 SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
595 pRStt = pRedl->Start();
596 pREnd = pRedl->End();
597
598 // keep style of the empty paragraph after deletion of wholly paragraphs
599 if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->nContent == 0 )
600 lcl_CopyStyle(*pREnd, *pRStt);
601
602 if( bDelRedl )
603 delete pRedl;
604
605 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
606 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
607
608 if( pCSttNd && pCEndNd )
609 {
610 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
611 lcl_DeleteTrackedTableRow( aPam.End() );
612 }
613 else if (pCSttNd && !pCEndNd)
614 {
615 aPam.GetBound().nContent.Assign( nullptr, 0 );
616 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
617 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
618 }
619 else
620 {
621 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
622 }
623 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
624 }
625 else if( bDelRedl )
626 delete pRedl;
627 }
628 break;
629
630 case RedlineType::FmtColl:
631 rArr.DeleteAndDestroy( rPos-- );
632 break;
633
634 case RedlineType::ParagraphFormat:
635 rArr.DeleteAndDestroy( rPos-- );
636 break;
637
638 default:
639 bRet = false;
640 }
641 return bRet;
642 }
643
lcl_RejectRedline(SwRedlineTable & rArr,SwRedlineTable::size_type & rPos,bool bCallDelete,const SwPosition * pSttRng=nullptr,const SwPosition * pEndRng=nullptr)644 bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
645 bool bCallDelete,
646 const SwPosition* pSttRng = nullptr,
647 const SwPosition* pEndRng = nullptr )
648 {
649 bool bRet = true;
650 SwRangeRedline* pRedl = rArr[ rPos ];
651 SwDoc& rDoc = pRedl->GetDoc();
652 SwPosition *pRStt = nullptr, *pREnd = nullptr;
653 SwComparePosition eCmp = SwComparePosition::Outside;
654 if( pSttRng && pEndRng )
655 {
656 pRStt = pRedl->Start();
657 pREnd = pRedl->End();
658 eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
659 }
660
661 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
662
663 switch( pRedl->GetType() )
664 {
665 case RedlineType::Insert:
666 {
667 const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
668 bool bDelRedl = false;
669 switch( eCmp )
670 {
671 case SwComparePosition::Inside:
672 if( bCallDelete )
673 {
674 pDelStt = pSttRng;
675 pDelEnd = pEndRng;
676 }
677 break;
678
679 case SwComparePosition::OverlapBefore:
680 if( bCallDelete )
681 {
682 pDelStt = pRStt;
683 pDelEnd = pEndRng;
684 }
685 break;
686 case SwComparePosition::OverlapBehind:
687 if( bCallDelete )
688 {
689 pDelStt = pREnd;
690 pDelEnd = pSttRng;
691 }
692 break;
693 case SwComparePosition::Outside:
694 case SwComparePosition::Equal:
695 {
696 // delete the range again
697 rArr.Remove( rPos-- );
698 bDelRedl = true;
699 if( bCallDelete )
700 {
701 pDelStt = pRedl->Start();
702 pDelEnd = pRedl->End();
703 }
704 }
705 break;
706
707 default:
708 bRet = false;
709 }
710 if( pDelStt && pDelEnd )
711 {
712 SwPaM aPam( *pDelStt, *pDelEnd );
713
714 SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
715 SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
716
717 if( bDelRedl )
718 delete pRedl;
719
720 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
721 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
722
723 if( pCSttNd && pCEndNd )
724 {
725 rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
726 lcl_DeleteTrackedTableRow( aPam.End() );
727 }
728 else if (pCSttNd && !pCEndNd)
729 {
730 aPam.GetBound().nContent.Assign( nullptr, 0 );
731 aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
732 if (aPam.End()->nNode.GetNode().IsStartNode())
733 { // end node will be deleted too! see nNodeDiff+1
734 --aPam.End()->nNode;
735 }
736 assert(!aPam.End()->nNode.GetNode().IsStartNode());
737 rDoc.getIDocumentContentOperations().DelFullPara( aPam );
738 }
739 else
740 {
741 rDoc.getIDocumentContentOperations().DeleteRange(aPam);
742 }
743 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
744 }
745 else if( bDelRedl )
746 delete pRedl;
747 }
748 break;
749 case RedlineType::Delete:
750 {
751 SwRangeRedline* pNew = nullptr;
752 bool bCheck = false, bReplace = false;
753 SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
754 pEndRng ? *pEndRng : *pRedl->End());
755
756 if( pRedl->GetExtraData() )
757 pRedl->GetExtraData()->Reject( *pRedl );
758
759 // remove tracking of the table row, if needed
760 lcl_RemoveTrackingOfTableRow( updatePaM.End() );
761
762 switch( eCmp )
763 {
764 case SwComparePosition::Inside:
765 {
766 if( 1 < pRedl->GetStackCount() )
767 {
768 pNew = new SwRangeRedline( *pRedl );
769 pNew->PopData();
770 }
771 if( *pSttRng == *pRStt )
772 {
773 pRedl->SetStart( *pEndRng, pRStt );
774 bReplace = true;
775 if( pNew )
776 pNew->SetEnd( *pEndRng );
777 }
778 else
779 {
780 if( *pEndRng != *pREnd )
781 {
782 // split up
783 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
784 pCpy->SetStart( *pEndRng );
785 rArr.Insert( pCpy ); ++rPos;
786 if( pNew )
787 pNew->SetEnd( *pEndRng );
788 }
789
790 pRedl->SetEnd( *pSttRng, pREnd );
791 bCheck = true;
792 if( pNew )
793 pNew->SetStart( *pSttRng );
794 }
795 }
796 break;
797
798 case SwComparePosition::OverlapBefore:
799 if( 1 < pRedl->GetStackCount() )
800 {
801 pNew = new SwRangeRedline( *pRedl );
802 pNew->PopData();
803 }
804 pRedl->SetStart( *pEndRng, pRStt );
805 bReplace = true;
806 if( pNew )
807 pNew->SetEnd( *pEndRng );
808 break;
809
810 case SwComparePosition::OverlapBehind:
811 if( 1 < pRedl->GetStackCount() )
812 {
813 pNew = new SwRangeRedline( *pRedl );
814 pNew->PopData();
815 }
816 pRedl->SetEnd( *pSttRng, pREnd );
817 bCheck = true;
818 if( pNew )
819 pNew->SetStart( *pSttRng );
820 break;
821
822 case SwComparePosition::Outside:
823 case SwComparePosition::Equal:
824 if( !pRedl->PopData() )
825 // deleting the RedlineObject is enough
826 rArr.DeleteAndDestroy( rPos-- );
827 break;
828
829 default:
830 bRet = false;
831 }
832
833 if( pNew )
834 {
835 rArr.Insert( pNew ); ++rPos;
836 }
837
838 if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
839 {
840 // re-insert
841 rArr.Remove( pRedl );
842 rArr.Insert( pRedl );
843 }
844
845 sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
846 }
847 break;
848
849 case RedlineType::Format:
850 case RedlineType::FmtColl:
851 case RedlineType::ParagraphFormat:
852 {
853 // tdf#52391 instead of hidden acception at the requested
854 // rejection, remove direct text formatting to get the potential
855 // original state of the text (FIXME if the original text
856 // has already contained direct text formatting: unfortunately
857 // ODF 1.2 doesn't support rejection of format-only changes)
858 if ( pRedl->GetType() == RedlineType::Format )
859 {
860 SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
861 rDoc.ResetAttrs(aPam);
862 }
863 else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
864 {
865 // handle paragraph formatting changes
866 // (range is only a full paragraph or a part of it)
867 const SwPosition* pStt = pRedl->Start();
868 SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
869 if( pTNd )
870 {
871 // expand range to the whole paragraph
872 // and reset only the paragraph attributes
873 SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
874 o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
875
876 constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
877 { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
878 { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
879 };
880
881 for (const auto& [nBegin, nEnd] : aResetableSetRange)
882 {
883 for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
884 aResetAttrsArray.insert( i );
885 }
886
887 rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
888
889 // remove numbering
890 if ( pTNd->GetNumRule() )
891 rDoc.DelNumRules(aPam);
892 }
893 }
894
895 if( pRedl->GetExtraData() )
896 pRedl->GetExtraData()->Reject( *pRedl );
897
898 rArr.DeleteAndDestroy( rPos-- );
899 }
900 break;
901
902 default:
903 bRet = false;
904 }
905 return bRet;
906 }
907
908 typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
909 bool bCallDelete,
910 const SwPosition* pSttRng,
911 const SwPosition* pEndRng);
912
913
lcl_AcceptRejectRedl(Fn_AcceptReject fn_AcceptReject,SwRedlineTable & rArr,bool bCallDelete,const SwPaM & rPam)914 int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
915 SwRedlineTable& rArr, bool bCallDelete,
916 const SwPaM& rPam)
917 {
918 SwRedlineTable::size_type n = 0;
919 int nCount = 0;
920
921 const SwPosition* pStt = rPam.Start(),
922 * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
923 : rPam.GetPoint();
924 const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
925 if( pFnd && // Is new a part of it?
926 ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
927 {
928 // Only revoke the partial selection
929 if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
930 nCount++;
931 ++n;
932 }
933
934 // tdf#119824 first we will accept only overlapping paragraph format changes
935 // in the first loop to avoid potential content changes during Redo
936 bool bHasParagraphFormatChange = false;
937 for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
938 {
939 for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
940 {
941 SwRangeRedline* pTmp = rArr[ o ];
942 if( pTmp->HasMark() && pTmp->IsVisible() )
943 {
944 if( *pTmp->End() <= *pEnd )
945 {
946 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
947 (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
948 {
949 bHasParagraphFormatChange = true;
950 nCount++;
951 }
952 }
953 else
954 {
955 if( *pTmp->Start() < *pEnd )
956 {
957 // Only revoke the partial selection
958 if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
959 (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
960 {
961 bHasParagraphFormatChange = true;
962 nCount++;
963 }
964 }
965 break;
966 }
967 }
968 }
969 }
970 return nCount;
971 }
972
lcl_AdjustRedlineRange(SwPaM & rPam)973 void lcl_AdjustRedlineRange( SwPaM& rPam )
974 {
975 // The Selection is only in the ContentSection. If there are Redlines
976 // to Non-ContentNodes before or after that, then the Selections
977 // expand to them.
978 SwPosition* pStt = rPam.Start(),
979 * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
980 : rPam.GetPoint();
981 SwDoc& rDoc = rPam.GetDoc();
982 if( !pStt->nContent.GetIndex() &&
983 !rDoc.GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
984 {
985 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
986 if( pRedl )
987 {
988 const SwPosition* pRStt = pRedl->Start();
989 if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
990 pStt->nNode.GetIndex() - 1 )
991 *pStt = *pRStt;
992 }
993 }
994 if( pEnd->nNode.GetNode().IsContentNode() &&
995 !rDoc.GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
996 pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() )
997 {
998 const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
999 if( pRedl )
1000 {
1001 const SwPosition* pREnd = pRedl->End();
1002 if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
1003 pEnd->nNode.GetIndex() + 1 )
1004 *pEnd = *pREnd;
1005 }
1006 }
1007 }
1008
1009 /// in case some text is deleted, ensure that the not-yet-inserted
1010 /// SwRangeRedline has its positions corrected not to point to deleted node
1011 class TemporaryRedlineUpdater
1012 {
1013 private:
1014 SwRangeRedline & m_rRedline;
1015 std::shared_ptr<SwUnoCursor> m_pCursor;
1016 public:
TemporaryRedlineUpdater(SwDoc & rDoc,SwRangeRedline & rRedline)1017 TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
1018 : m_rRedline(rRedline)
1019 , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
1020 {
1021 if (m_rRedline.HasMark())
1022 {
1023 m_pCursor->SetMark();
1024 *m_pCursor->GetMark() = *m_rRedline.GetMark();
1025 *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
1026 }
1027 *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
1028 }
~TemporaryRedlineUpdater()1029 ~TemporaryRedlineUpdater()
1030 {
1031 static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
1032 }
1033 };
1034 }
1035
1036 namespace sw
1037 {
1038
DocumentRedlineManager(SwDoc & i_rSwdoc)1039 DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
1040 : m_rDoc(i_rSwdoc)
1041 , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
1042 , mpRedlineTable(new SwRedlineTable)
1043 , mpExtraRedlineTable(new SwExtraRedlineTable)
1044 , mbIsRedlineMove(false)
1045 , mnAutoFormatRedlnCommentNo(0)
1046 {
1047 }
1048
GetRedlineFlags() const1049 RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
1050 {
1051 return meRedlineFlags;
1052 }
1053
SetRedlineFlags(RedlineFlags eMode)1054 void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
1055 {
1056 if( meRedlineFlags == eMode )
1057 return;
1058
1059 if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
1060 || !(RedlineFlags::ShowMask & eMode) )
1061 {
1062 bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
1063 m_rDoc.SetInXMLImport( false );
1064 // and then hide/display everything
1065 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
1066 // uninitialized ptr is possible
1067
1068 RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
1069 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1070 pFnc = &SwRangeRedline::Show;
1071 else if (eShowMode == RedlineFlags::ShowInsert)
1072 pFnc = &SwRangeRedline::Hide;
1073 else if (eShowMode == RedlineFlags::ShowDelete)
1074 pFnc = &SwRangeRedline::ShowOriginal;
1075 else
1076 {
1077 pFnc = &SwRangeRedline::Hide;
1078 eMode |= RedlineFlags::ShowInsert;
1079 }
1080
1081 CheckAnchoredFlyConsistency(m_rDoc);
1082 CHECK_REDLINE( *this )
1083
1084 o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
1085 if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
1086 {
1087 // sw_redlinehide: the problem here is that MoveFromSection
1088 // creates the frames wrongly (non-merged), because its own
1089 // SwRangeRedline has wrong positions until after the nodes
1090 // are all moved, so fix things up by force by re-creating
1091 // all merged frames from scratch.
1092 o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
1093 for (SwRootFrame *const pLayout : layouts)
1094 {
1095 if (pLayout->IsHideRedlines())
1096 {
1097 pLayout->SetHideRedlines(false);
1098 hiddenLayouts.insert(pLayout);
1099 }
1100 }
1101 }
1102
1103 for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
1104 for (size_t i = 0; i < mpRedlineTable->size(); ++i)
1105 {
1106 SwRangeRedline *const pRedline((*mpRedlineTable)[i]);
1107 (pRedline->*pFnc)(nLoop, i, false);
1108 while (mpRedlineTable->size() <= i
1109 || (*mpRedlineTable)[i] != pRedline)
1110 { // ensure current position
1111 --i; // a previous redline may have been deleted
1112 }
1113 }
1114
1115 //SwRangeRedline::MoveFromSection routinely changes
1116 //the keys that mpRedlineTable is sorted by
1117 mpRedlineTable->Resort();
1118
1119 CheckAnchoredFlyConsistency(m_rDoc);
1120 CHECK_REDLINE( *this )
1121
1122 for (SwRootFrame *const pLayout : hiddenLayouts)
1123 {
1124 pLayout->SetHideRedlines(true);
1125 }
1126
1127 m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
1128 }
1129 meRedlineFlags = eMode;
1130 m_rDoc.getIDocumentState().SetModified();
1131
1132 // #TODO - add 'SwExtraRedlineTable' also ?
1133 }
1134
IsRedlineOn() const1135 bool DocumentRedlineManager::IsRedlineOn() const
1136 {
1137 return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
1138 }
1139
IsIgnoreRedline() const1140 bool DocumentRedlineManager::IsIgnoreRedline() const
1141 {
1142 return bool(RedlineFlags::Ignore & meRedlineFlags);
1143 }
1144
SetRedlineFlags_intern(RedlineFlags eMode)1145 void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
1146 {
1147 meRedlineFlags = eMode;
1148 }
1149
GetRedlineTable() const1150 const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
1151 {
1152 return *mpRedlineTable;
1153 }
1154
GetRedlineTable()1155 SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
1156 {
1157 return *mpRedlineTable;
1158 }
1159
GetExtraRedlineTable() const1160 const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
1161 {
1162 return *mpExtraRedlineTable;
1163 }
1164
GetExtraRedlineTable()1165 SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
1166 {
1167 return *mpExtraRedlineTable;
1168 }
1169
HasExtraRedlineTable() const1170 bool DocumentRedlineManager::HasExtraRedlineTable() const
1171 {
1172 return mpExtraRedlineTable != nullptr;
1173 }
1174
IsInRedlines(const SwNode & rNode) const1175 bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
1176 {
1177 SwPosition aPos(rNode);
1178 SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
1179 SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
1180 SwPosition(rEndOfRedlines));
1181
1182 return aPam.ContainsPosition(aPos);
1183 }
1184
IsRedlineMove() const1185 bool DocumentRedlineManager::IsRedlineMove() const
1186 {
1187 return mbIsRedlineMove;
1188 }
1189
SetRedlineMove(bool bFlag)1190 void DocumentRedlineManager::SetRedlineMove(bool bFlag)
1191 {
1192 mbIsRedlineMove = bFlag;
1193 }
1194
1195 /*
1196 Text means Text not "polluted" by Redlines.
1197
1198 Behaviour of Insert-Redline:
1199 - in the Text - insert Redline Object
1200 - in InsertRedline (own) - ignore, existing is extended
1201 - in InsertRedline (others) - split up InsertRedline and
1202 insert Redline Object
1203 - in DeleteRedline - split up DeleteRedline or
1204 move at the end/beginning
1205
1206 Behaviour of Delete-Redline:
1207 - in the Text - insert Redline Object
1208 - in DeleteRedline (own/others) - ignore
1209 - in InsertRedline (own) - ignore, but delete character
1210 - in InsertRedline (others) - split up InsertRedline and
1211 insert Redline Object
1212 - Text and own Insert overlap - delete Text in the own Insert,
1213 extend in the other Text
1214 (up to the Insert!)
1215 - Text and other Insert overlap - insert Redline Object, the
1216 other Insert is overlapped by
1217 the Delete
1218 */
1219 IDocumentRedlineAccess::AppendResult
AppendRedline(SwRangeRedline * pNewRedl,bool const bCallDelete)1220 DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
1221 {
1222 bool bMerged = false;
1223 CHECK_REDLINE( *this )
1224
1225 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
1226 {
1227 pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
1228
1229 if( m_rDoc.IsAutoFormatRedline() )
1230 {
1231 pNewRedl->SetAutoFormat();
1232 if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() )
1233 {
1234 pNewRedl->SetComment( *mpAutoFormatRedlnComment );
1235 pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
1236 }
1237 }
1238
1239 SwPosition* pStt = pNewRedl->Start(),
1240 * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark()
1241 : pNewRedl->GetPoint();
1242 {
1243 SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
1244 if( pTextNode == nullptr )
1245 {
1246 if( pStt->nContent > 0 )
1247 {
1248 OSL_ENSURE( false, "Redline start: non-text-node with content" );
1249 pStt->nContent = 0;
1250 }
1251 }
1252 else
1253 {
1254 if( pStt->nContent > pTextNode->Len() )
1255 {
1256 OSL_ENSURE( false, "Redline start: index after text" );
1257 pStt->nContent = pTextNode->Len();
1258 }
1259 }
1260 pTextNode = pEnd->nNode.GetNode().GetTextNode();
1261 if( pTextNode == nullptr )
1262 {
1263 if( pEnd->nContent > 0 )
1264 {
1265 OSL_ENSURE( false, "Redline end: non-text-node with content" );
1266 pEnd->nContent = 0;
1267 }
1268 }
1269 else
1270 {
1271 if( pEnd->nContent > pTextNode->Len() )
1272 {
1273 OSL_ENSURE( false, "Redline end: index after text" );
1274 pEnd->nContent = pTextNode->Len();
1275 }
1276 }
1277 }
1278 if( ( *pStt == *pEnd ) &&
1279 ( pNewRedl->GetContentIdx() == nullptr ) )
1280 { // Do not insert empty redlines
1281 delete pNewRedl;
1282 return AppendResult::IGNORED;
1283 }
1284 bool bCompress = false;
1285 SwRedlineTable::size_type n = 0;
1286 // look up the first Redline for the starting position
1287 if( !GetRedline( *pStt, &n ) && n )
1288 --n;
1289 bool bDec = false;
1290
1291 for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n )
1292 {
1293 bDec = false;
1294
1295 SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
1296 SwPosition* pRStt = pRedl->Start(),
1297 * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
1298 : pRedl->GetPoint();
1299
1300 // #i8518# remove empty redlines while we're at it
1301 if( ( *pRStt == *pREnd ) &&
1302 ( pRedl->GetContentIdx() == nullptr ) )
1303 {
1304 mpRedlineTable->DeleteAndDestroy(n);
1305 continue;
1306 }
1307
1308 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1309
1310 switch( pNewRedl->GetType() )
1311 {
1312 case RedlineType::Insert:
1313 switch( pRedl->GetType() )
1314 {
1315 case RedlineType::Insert:
1316 if( pRedl->IsOwnRedline( *pNewRedl ) )
1317 {
1318 bool bDelete = false;
1319
1320 // Merge if applicable?
1321 if( (( SwComparePosition::Behind == eCmpPos &&
1322 IsPrevPos( *pREnd, *pStt ) ) ||
1323 ( SwComparePosition::CollideStart == eCmpPos ) ||
1324 ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
1325 pRedl->CanCombine( *pNewRedl ) &&
1326 ( n+1 >= mpRedlineTable->size() ||
1327 ( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd &&
1328 *(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) )
1329 {
1330 pRedl->SetEnd( *pEnd, pREnd );
1331 if( !pRedl->HasValidRange() )
1332 {
1333 // re-insert
1334 mpRedlineTable->Remove( n );
1335 mpRedlineTable->Insert( pRedl );
1336 }
1337
1338 bMerged = true;
1339 bDelete = true;
1340 }
1341 else if( (( SwComparePosition::Before == eCmpPos &&
1342 IsPrevPos( *pEnd, *pRStt ) ) ||
1343 ( SwComparePosition::CollideEnd == eCmpPos ) ||
1344 ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
1345 pRedl->CanCombine( *pNewRedl ) &&
1346 ( !n ||
1347 *(*mpRedlineTable)[ n-1 ]->End() != *pRStt ))
1348 {
1349 pRedl->SetStart( *pStt, pRStt );
1350 // re-insert
1351 mpRedlineTable->Remove( n );
1352 mpRedlineTable->Insert( pRedl );
1353
1354 bMerged = true;
1355 bDelete = true;
1356 }
1357 else if ( SwComparePosition::Outside == eCmpPos )
1358 {
1359 // own insert-over-insert redlines:
1360 // just scrap the inside ones
1361 mpRedlineTable->DeleteAndDestroy( n );
1362 bDec = true;
1363 }
1364 else if( SwComparePosition::OverlapBehind == eCmpPos )
1365 {
1366 *pStt = *pREnd;
1367 if( ( *pStt == *pEnd ) &&
1368 ( pNewRedl->GetContentIdx() == nullptr ) )
1369 bDelete = true;
1370 }
1371 else if( SwComparePosition::OverlapBefore == eCmpPos )
1372 {
1373 *pEnd = *pRStt;
1374 if( ( *pStt == *pEnd ) &&
1375 ( pNewRedl->GetContentIdx() == nullptr ) )
1376 bDelete = true;
1377 }
1378 else if( SwComparePosition::Inside == eCmpPos )
1379 {
1380 bDelete = true;
1381 bMerged = true;
1382 }
1383 else if( SwComparePosition::Equal == eCmpPos )
1384 bDelete = true;
1385
1386 if( bDelete )
1387 {
1388 delete pNewRedl;
1389 pNewRedl = nullptr;
1390 bCompress = true;
1391 }
1392 }
1393 else if( SwComparePosition::Inside == eCmpPos )
1394 {
1395 // split up
1396 if( *pEnd != *pREnd )
1397 {
1398 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1399 pCpy->SetStart( *pEnd );
1400 mpRedlineTable->Insert( pCpy );
1401 }
1402 pRedl->SetEnd( *pStt, pREnd );
1403 if( ( *pStt == *pRStt ) &&
1404 ( pRedl->GetContentIdx() == nullptr ) )
1405 {
1406 mpRedlineTable->DeleteAndDestroy( n );
1407 bDec = true;
1408 }
1409 else if( !pRedl->HasValidRange() )
1410 {
1411 // re-insert
1412 mpRedlineTable->Remove( n );
1413 mpRedlineTable->Insert( pRedl );
1414 }
1415 }
1416 else if ( SwComparePosition::Outside == eCmpPos )
1417 {
1418 // handle overlapping redlines in broken documents
1419
1420 // split up the new redline, since it covers the
1421 // existing redline. Insert the first part, and
1422 // progress with the remainder as usual
1423 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1424 pSplit->SetEnd( *pRStt );
1425 pNewRedl->SetStart( *pREnd );
1426 mpRedlineTable->Insert( pSplit );
1427 if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1428 {
1429 delete pNewRedl;
1430 pNewRedl = nullptr;
1431 bCompress = true;
1432 }
1433 }
1434 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1435 {
1436 // handle overlapping redlines in broken documents
1437 pNewRedl->SetStart( *pREnd );
1438 }
1439 else if ( SwComparePosition::OverlapBefore == eCmpPos )
1440 {
1441 // handle overlapping redlines in broken documents
1442 *pEnd = *pRStt;
1443 if( ( *pStt == *pEnd ) &&
1444 ( pNewRedl->GetContentIdx() == nullptr ) )
1445 {
1446 delete pNewRedl;
1447 pNewRedl = nullptr;
1448 bCompress = true;
1449 }
1450 }
1451 break;
1452 case RedlineType::Delete:
1453 if( SwComparePosition::Inside == eCmpPos )
1454 {
1455 // split up
1456 if( *pEnd != *pREnd )
1457 {
1458 SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
1459 pCpy->SetStart( *pEnd );
1460 mpRedlineTable->Insert( pCpy );
1461 }
1462 pRedl->SetEnd( *pStt, pREnd );
1463 if( ( *pStt == *pRStt ) &&
1464 ( pRedl->GetContentIdx() == nullptr ) )
1465 {
1466 mpRedlineTable->DeleteAndDestroy( n );
1467 bDec = true;
1468 }
1469 else if( !pRedl->HasValidRange() )
1470 {
1471 // re-insert
1472 mpRedlineTable->Remove( n );
1473 mpRedlineTable->Insert( pRedl, n );
1474 }
1475 }
1476 else if ( SwComparePosition::Outside == eCmpPos )
1477 {
1478 // handle overlapping redlines in broken documents
1479
1480 // split up the new redline, since it covers the
1481 // existing redline. Insert the first part, and
1482 // progress with the remainder as usual
1483 SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
1484 pSplit->SetEnd( *pRStt );
1485 pNewRedl->SetStart( *pREnd );
1486 mpRedlineTable->Insert( pSplit );
1487 if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
1488 {
1489 delete pNewRedl;
1490 pNewRedl = nullptr;
1491 bCompress = true;
1492 }
1493 }
1494 else if ( SwComparePosition::Equal == eCmpPos )
1495 {
1496 // handle identical redlines in broken documents
1497 // delete old (delete) redline
1498 mpRedlineTable->DeleteAndDestroy( n );
1499 bDec = true;
1500 }
1501 else if ( SwComparePosition::OverlapBehind == eCmpPos )
1502 { // Another workaround for broken redlines
1503 pNewRedl->SetStart( *pREnd );
1504 }
1505 break;
1506 case RedlineType::Format:
1507 switch( eCmpPos )
1508 {
1509 case SwComparePosition::OverlapBefore:
1510 pRedl->SetStart( *pEnd, pRStt );
1511 // re-insert
1512 mpRedlineTable->Remove( n );
1513 mpRedlineTable->Insert( pRedl, n );
1514 bDec = true;
1515 break;
1516
1517 case SwComparePosition::OverlapBehind:
1518 pRedl->SetEnd( *pStt, pREnd );
1519 if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1520 {
1521 mpRedlineTable->DeleteAndDestroy( n );
1522 bDec = true;
1523 }
1524 break;
1525
1526 case SwComparePosition::Equal:
1527 case SwComparePosition::Outside:
1528 // Overlaps the current one completely or has the
1529 // same dimension, delete the old one
1530 mpRedlineTable->DeleteAndDestroy( n );
1531 bDec = true;
1532 break;
1533
1534 case SwComparePosition::Inside:
1535 // Overlaps the current one completely,
1536 // split or shorten the new one
1537 if( *pEnd != *pREnd )
1538 {
1539 if( *pEnd != *pRStt )
1540 {
1541 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1542 pNew->SetStart( *pEnd );
1543 pRedl->SetEnd( *pStt, pREnd );
1544 if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
1545 mpRedlineTable->DeleteAndDestroy( n );
1546 AppendRedline( pNew, bCallDelete );
1547 n = 0; // re-initialize
1548 bDec = true;
1549 }
1550 }
1551 else
1552 pRedl->SetEnd( *pStt, pREnd );
1553 break;
1554 default:
1555 break;
1556 }
1557 break;
1558 default:
1559 break;
1560 }
1561 break;
1562
1563 case RedlineType::Delete:
1564 switch( pRedl->GetType() )
1565 {
1566 case RedlineType::Delete:
1567 switch( eCmpPos )
1568 {
1569 case SwComparePosition::Outside:
1570 {
1571 // Overlaps the current one completely,
1572 // split the new one
1573 if (*pEnd == *pREnd)
1574 {
1575 pNewRedl->SetEnd(*pRStt, pEnd);
1576 }
1577 else if (*pStt == *pRStt)
1578 {
1579 pNewRedl->SetStart(*pREnd, pStt);
1580 }
1581 else
1582 {
1583 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
1584 pNew->SetStart( *pREnd );
1585 pNewRedl->SetEnd( *pRStt, pEnd );
1586 AppendRedline( pNew, bCallDelete );
1587 n = 0; // re-initialize
1588 bDec = true;
1589 }
1590 }
1591 break;
1592
1593 case SwComparePosition::Inside:
1594 case SwComparePosition::Equal:
1595 delete pNewRedl;
1596 pNewRedl = nullptr;
1597 bCompress = true;
1598 break;
1599
1600 case SwComparePosition::OverlapBefore:
1601 case SwComparePosition::OverlapBehind:
1602 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1603 pRedl->CanCombine( *pNewRedl ))
1604 {
1605 // If that's the case we can merge it, meaning
1606 // the new one covers this well
1607 if( SwComparePosition::OverlapBehind == eCmpPos )
1608 pNewRedl->SetStart( *pRStt, pStt );
1609 else
1610 pNewRedl->SetEnd( *pREnd, pEnd );
1611 mpRedlineTable->DeleteAndDestroy( n );
1612 bDec = true;
1613 }
1614 else if( SwComparePosition::OverlapBehind == eCmpPos )
1615 pNewRedl->SetStart( *pREnd, pStt );
1616 else
1617 pNewRedl->SetEnd( *pRStt, pEnd );
1618 break;
1619
1620 case SwComparePosition::CollideStart:
1621 case SwComparePosition::CollideEnd:
1622 if( pRedl->IsOwnRedline( *pNewRedl ) &&
1623 pRedl->CanCombine( *pNewRedl ) )
1624 {
1625 if( IsHideChanges( meRedlineFlags ))
1626 {
1627 // Before we can merge, we make it visible!
1628 // We insert temporarily so that pNew is
1629 // also dealt with when moving the indices.
1630 mpRedlineTable->Insert(pNewRedl);
1631 pRedl->Show(0, mpRedlineTable->GetPos(pRedl));
1632 mpRedlineTable->Remove( pNewRedl );
1633 pRStt = pRedl->Start();
1634 pREnd = pRedl->End();
1635 }
1636
1637 // If that's the case we can merge it, meaning
1638 // the new one covers this well
1639 if( SwComparePosition::CollideStart == eCmpPos )
1640 pNewRedl->SetStart( *pRStt, pStt );
1641 else
1642 pNewRedl->SetEnd( *pREnd, pEnd );
1643
1644 // delete current (below), and restart process with
1645 // previous
1646 SwRedlineTable::size_type nToBeDeleted = n;
1647 bDec = true;
1648
1649 if( *(pNewRedl->Start()) <= *pREnd )
1650 {
1651 // Whoooah, we just extended the new 'redline'
1652 // beyond previous redlines, so better start
1653 // again. Of course this is not supposed to
1654 // happen, and in an ideal world it doesn't,
1655 // but unfortunately this code is buggy and
1656 // totally rotten so it does happen and we
1657 // better fix it.
1658 n = 0;
1659 bDec = true;
1660 }
1661
1662 mpRedlineTable->DeleteAndDestroy( nToBeDeleted );
1663 }
1664 break;
1665 default:
1666 break;
1667 }
1668 break;
1669
1670 case RedlineType::Insert:
1671 {
1672 // b62341295: Do not throw away redlines
1673 // even if they are not allowed to be combined
1674 RedlineFlags eOld = meRedlineFlags;
1675 if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
1676 pRedl->IsOwnRedline( *pNewRedl ) )
1677 {
1678
1679 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
1680 // The ShowMode needs to be retained!
1681 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
1682 switch( eCmpPos )
1683 {
1684 case SwComparePosition::Equal:
1685 bCompress = true;
1686 mpRedlineTable->DeleteAndDestroy( n );
1687 bDec = true;
1688 [[fallthrough]];
1689
1690 case SwComparePosition::Inside:
1691 if( bCallDelete )
1692 {
1693 // DeleteAndJoin does not yield the
1694 // desired result if there is no paragraph to
1695 // join with, i.e. at the end of the document.
1696 // For this case, we completely delete the
1697 // paragraphs (if, of course, we also start on
1698 // a paragraph boundary).
1699 if( (pStt->nContent == 0) &&
1700 pEnd->nNode.GetNode().IsEndNode() )
1701 {
1702 pEnd->nNode--;
1703 pEnd->nContent.Assign(
1704 pEnd->nNode.GetNode().GetTextNode(), 0);
1705 m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
1706 }
1707 else
1708 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1709
1710 bCompress = true;
1711 }
1712 if( !bCallDelete && !bDec && *pEnd == *pREnd )
1713 {
1714 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
1715 bCompress = true;
1716 }
1717 else if ( bCallDelete || !bDec )
1718 {
1719 // delete new redline, except in some cases of fallthrough from previous
1720 // case ::Equal (eg. same portion w:del in w:ins in OOXML import)
1721 delete pNewRedl;
1722 pNewRedl = nullptr;
1723 }
1724 break;
1725
1726 case SwComparePosition::Outside:
1727 {
1728 mpRedlineTable->Remove( n );
1729 bDec = true;
1730 if( bCallDelete )
1731 {
1732 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1733 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
1734 n = 0; // re-initialize
1735 }
1736 delete pRedl;
1737 }
1738 break;
1739
1740 case SwComparePosition::OverlapBefore:
1741 {
1742 SwPaM aPam( *pRStt, *pEnd );
1743
1744 if( *pEnd == *pREnd )
1745 mpRedlineTable->DeleteAndDestroy( n );
1746 else
1747 {
1748 pRedl->SetStart( *pEnd, pRStt );
1749 // re-insert
1750 mpRedlineTable->Remove( n );
1751 mpRedlineTable->Insert( pRedl, n );
1752 }
1753
1754 if( bCallDelete )
1755 {
1756 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1757 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1758 n = 0; // re-initialize
1759 }
1760 bDec = true;
1761 }
1762 break;
1763
1764 case SwComparePosition::OverlapBehind:
1765 {
1766 SwPaM aPam( *pStt, *pREnd );
1767
1768 if( *pStt == *pRStt )
1769 {
1770 mpRedlineTable->DeleteAndDestroy( n );
1771 bDec = true;
1772 }
1773 else
1774 pRedl->SetEnd( *pStt, pREnd );
1775
1776 if( bCallDelete )
1777 {
1778 TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
1779 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
1780 n = 0; // re-initialize
1781 bDec = true;
1782 }
1783 }
1784 break;
1785 default:
1786 break;
1787 }
1788
1789 meRedlineFlags = eOld;
1790 }
1791 else
1792 {
1793 // it may be necessary to split the existing redline in
1794 // two. In this case, pRedl will be changed to cover
1795 // only part of its former range, and pNew will cover
1796 // the remainder.
1797 SwRangeRedline* pNew = nullptr;
1798
1799 switch( eCmpPos )
1800 {
1801 case SwComparePosition::Equal:
1802 {
1803 pRedl->PushData( *pNewRedl );
1804 delete pNewRedl;
1805 pNewRedl = nullptr;
1806 if( IsHideChanges( meRedlineFlags ))
1807 {
1808 pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1809 }
1810 bCompress = true;
1811 }
1812 break;
1813
1814 case SwComparePosition::Inside:
1815 {
1816 if( *pRStt == *pStt )
1817 {
1818 // #i97421#
1819 // redline w/out extent loops
1820 if (*pStt != *pEnd)
1821 {
1822 pNewRedl->PushData( *pRedl, false );
1823 pRedl->SetStart( *pEnd, pRStt );
1824 // re-insert
1825 mpRedlineTable->Remove( n );
1826 mpRedlineTable->Insert( pRedl, n );
1827 bDec = true;
1828 }
1829 }
1830 else
1831 {
1832 pNewRedl->PushData( *pRedl, false );
1833 if( *pREnd != *pEnd )
1834 {
1835 pNew = new SwRangeRedline( *pRedl );
1836 pNew->SetStart( *pEnd );
1837 }
1838 pRedl->SetEnd( *pStt, pREnd );
1839 if( !pRedl->HasValidRange() )
1840 {
1841 // re-insert
1842 mpRedlineTable->Remove( n );
1843 mpRedlineTable->Insert( pRedl, n );
1844 }
1845 }
1846 }
1847 break;
1848
1849 case SwComparePosition::Outside:
1850 {
1851 pRedl->PushData( *pNewRedl );
1852 if( *pEnd == *pREnd )
1853 {
1854 pNewRedl->SetEnd( *pRStt, pEnd );
1855 }
1856 else if (*pStt == *pRStt)
1857 {
1858 pNewRedl->SetStart(*pREnd, pStt);
1859 }
1860 else
1861 {
1862 pNew = new SwRangeRedline( *pNewRedl );
1863 pNew->SetEnd( *pRStt );
1864 pNewRedl->SetStart( *pREnd, pStt );
1865 }
1866 bCompress = true;
1867 }
1868 break;
1869
1870 case SwComparePosition::OverlapBefore:
1871 {
1872 if( *pEnd == *pREnd )
1873 {
1874 pRedl->PushData( *pNewRedl );
1875 pNewRedl->SetEnd( *pRStt, pEnd );
1876 if( IsHideChanges( meRedlineFlags ))
1877 {
1878 mpRedlineTable->Insert(pNewRedl);
1879 pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1880 mpRedlineTable->Remove( pNewRedl );
1881 }
1882 }
1883 else
1884 {
1885 pNew = new SwRangeRedline( *pRedl );
1886 pNew->PushData( *pNewRedl );
1887 pNew->SetEnd( *pEnd );
1888 pNewRedl->SetEnd( *pRStt, pEnd );
1889 pRedl->SetStart( *pNew->End(), pRStt ) ;
1890 // re-insert
1891 mpRedlineTable->Remove( n );
1892 mpRedlineTable->Insert( pRedl );
1893 bDec = true;
1894 }
1895 }
1896 break;
1897
1898 case SwComparePosition::OverlapBehind:
1899 {
1900 if( *pStt == *pRStt )
1901 {
1902 pRedl->PushData( *pNewRedl );
1903 pNewRedl->SetStart( *pREnd, pStt );
1904 if( IsHideChanges( meRedlineFlags ))
1905 {
1906 mpRedlineTable->Insert( pNewRedl );
1907 pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
1908 mpRedlineTable->Remove( pNewRedl );
1909 }
1910 }
1911 else
1912 {
1913 pNew = new SwRangeRedline( *pRedl );
1914 pNew->PushData( *pNewRedl );
1915 pNew->SetStart( *pStt );
1916 pNewRedl->SetStart( *pREnd, pStt );
1917 pRedl->SetEnd( *pNew->Start(), pREnd );
1918 if( !pRedl->HasValidRange() )
1919 {
1920 // re-insert
1921 mpRedlineTable->Remove( n );
1922 mpRedlineTable->Insert( pRedl );
1923 }
1924 }
1925 }
1926 break;
1927 default:
1928 break;
1929 }
1930
1931 // insert the pNew part (if it exists)
1932 if( pNew )
1933 {
1934 mpRedlineTable->Insert( pNew );
1935
1936 // pNew must be deleted if Insert() wasn't
1937 // successful. But that can't happen, since pNew is
1938 // part of the original pRedl redline.
1939 // OSL_ENSURE( bRet, "Can't insert existing redline?" );
1940
1941 // restart (now with pRedl being split up)
1942 n = 0;
1943 bDec = true;
1944 }
1945 }
1946 }
1947 break;
1948
1949 case RedlineType::Format:
1950 switch( eCmpPos )
1951 {
1952 case SwComparePosition::OverlapBefore:
1953 pRedl->SetStart( *pEnd, pRStt );
1954 // re-insert
1955 mpRedlineTable->Remove( n );
1956 mpRedlineTable->Insert( pRedl, n );
1957 bDec = true;
1958 break;
1959
1960 case SwComparePosition::OverlapBehind:
1961 pRedl->SetEnd( *pStt, pREnd );
1962 break;
1963
1964 case SwComparePosition::Equal:
1965 case SwComparePosition::Outside:
1966 // Overlaps the current one completely or has the
1967 // same dimension, delete the old one
1968 mpRedlineTable->DeleteAndDestroy( n );
1969 bDec = true;
1970 break;
1971
1972 case SwComparePosition::Inside:
1973 // Overlaps the current one completely,
1974 // split or shorten the new one
1975 if( *pEnd != *pREnd )
1976 {
1977 if( *pEnd != *pRStt )
1978 {
1979 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
1980 pNew->SetStart( *pEnd );
1981 pRedl->SetEnd( *pStt, pREnd );
1982 if( ( *pStt == *pRStt ) &&
1983 ( pRedl->GetContentIdx() == nullptr ) )
1984 mpRedlineTable->DeleteAndDestroy( n );
1985 AppendRedline( pNew, bCallDelete );
1986 n = 0; // re-initialize
1987 bDec = true;
1988 }
1989 }
1990 else
1991 pRedl->SetEnd( *pStt, pREnd );
1992 break;
1993 default:
1994 break;
1995 }
1996 break;
1997 default:
1998 break;
1999 }
2000 break;
2001
2002 case RedlineType::Format:
2003 switch( pRedl->GetType() )
2004 {
2005 case RedlineType::Insert:
2006 case RedlineType::Delete:
2007 switch( eCmpPos )
2008 {
2009 case SwComparePosition::OverlapBefore:
2010 pNewRedl->SetEnd( *pRStt, pEnd );
2011 break;
2012
2013 case SwComparePosition::OverlapBehind:
2014 pNewRedl->SetStart( *pREnd, pStt );
2015 break;
2016
2017 case SwComparePosition::Equal:
2018 case SwComparePosition::Inside:
2019 delete pNewRedl;
2020 pNewRedl = nullptr;
2021 break;
2022
2023 case SwComparePosition::Outside:
2024 // Overlaps the current one completely,
2025 // split or shorten the new one
2026 if (*pEnd == *pREnd)
2027 {
2028 pNewRedl->SetEnd(*pRStt, pEnd);
2029 }
2030 else if (*pStt == *pRStt)
2031 {
2032 pNewRedl->SetStart(*pREnd, pStt);
2033 }
2034 else
2035 {
2036 SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
2037 pNew->SetStart( *pREnd );
2038 pNewRedl->SetEnd( *pRStt, pEnd );
2039 AppendRedline( pNew, bCallDelete );
2040 n = 0; // re-initialize
2041 bDec = true;
2042 }
2043 break;
2044 default:
2045 break;
2046 }
2047 break;
2048 case RedlineType::Format:
2049 switch( eCmpPos )
2050 {
2051 case SwComparePosition::Outside:
2052 case SwComparePosition::Equal:
2053 {
2054 // Overlaps the current one completely or has the
2055 // same dimension, delete the old one
2056 mpRedlineTable->DeleteAndDestroy( n );
2057 bDec = true;
2058 }
2059 break;
2060
2061 case SwComparePosition::Inside:
2062 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2063 pRedl->CanCombine( *pNewRedl ))
2064 {
2065 // own one can be ignored completely
2066 delete pNewRedl;
2067 pNewRedl = nullptr;
2068 }
2069 else if( *pREnd == *pEnd )
2070 // or else only shorten the current one
2071 pRedl->SetEnd( *pStt, pREnd );
2072 else if( *pRStt == *pStt )
2073 {
2074 // or else only shorten the current one
2075 pRedl->SetStart( *pEnd, pRStt );
2076 // re-insert
2077 mpRedlineTable->Remove( n );
2078 mpRedlineTable->Insert( pRedl, n );
2079 bDec = true;
2080 }
2081 else
2082 {
2083 // If it lies completely within the current one
2084 // we need to split it
2085 SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
2086 pNew->SetStart( *pEnd );
2087 pRedl->SetEnd( *pStt, pREnd );
2088 AppendRedline( pNew, bCallDelete );
2089 n = 0; // re-initialize
2090 bDec = true;
2091 }
2092 break;
2093
2094 case SwComparePosition::OverlapBefore:
2095 case SwComparePosition::OverlapBehind:
2096 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2097 pRedl->CanCombine( *pNewRedl ))
2098 {
2099 // If that's the case we can merge it, meaning
2100 // the new one covers this well
2101 if( SwComparePosition::OverlapBehind == eCmpPos )
2102 pNewRedl->SetStart( *pRStt, pStt );
2103 else
2104 pNewRedl->SetEnd( *pREnd, pEnd );
2105 mpRedlineTable->DeleteAndDestroy( n );
2106 bDec = false;
2107 }
2108 else if( SwComparePosition::OverlapBehind == eCmpPos )
2109 pNewRedl->SetStart( *pREnd, pStt );
2110 else
2111 pNewRedl->SetEnd( *pRStt, pEnd );
2112 break;
2113
2114 case SwComparePosition::CollideEnd:
2115 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2116 pRedl->CanCombine( *pNewRedl ) && n &&
2117 *(*mpRedlineTable)[ n-1 ]->End() < *pStt )
2118 {
2119 // If that's the case we can merge it, meaning
2120 // the new one covers this well
2121 pNewRedl->SetEnd( *pREnd, pEnd );
2122 mpRedlineTable->DeleteAndDestroy( n );
2123 bDec = true;
2124 }
2125 break;
2126 case SwComparePosition::CollideStart:
2127 if( pRedl->IsOwnRedline( *pNewRedl ) &&
2128 pRedl->CanCombine( *pNewRedl ) &&
2129 n+1 < mpRedlineTable->size() &&
2130 *(*mpRedlineTable)[ n+1 ]->Start() < *pEnd )
2131 {
2132 // If that's the case we can merge it, meaning
2133 // the new one covers this well
2134 pNewRedl->SetStart( *pRStt, pStt );
2135 mpRedlineTable->DeleteAndDestroy( n );
2136 bDec = true;
2137 }
2138 break;
2139 default:
2140 break;
2141 }
2142 break;
2143 default:
2144 break;
2145 }
2146 break;
2147
2148 case RedlineType::FmtColl:
2149 // How should we behave here?
2150 // insert as is
2151 break;
2152 default:
2153 break;
2154 }
2155 }
2156
2157 if( pNewRedl )
2158 {
2159 if( ( *pStt == *pEnd ) &&
2160 ( pNewRedl->GetContentIdx() == nullptr ) )
2161 { // Do not insert empty redlines
2162 delete pNewRedl;
2163 pNewRedl = nullptr;
2164 }
2165 else
2166 {
2167 if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2168 {
2169 if ( pStt->nContent != 0 )
2170 {
2171 // tdf#119571 update the style of the joined paragraph
2172 // after a partially deleted paragraph to show its correct style
2173 // in "Show changes" mode, too. All removed paragraphs
2174 // get the style of the first (partially deleted) paragraph
2175 // to avoid text insertion with bad style in the deleted
2176 // area later.
2177
2178 SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
2179 SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
2180 SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
2181 SwTextNode* pTextNode;
2182 SwNodeIndex aIdx( pEnd->nNode.GetNode() );
2183 bool bFirst = true;
2184
2185 while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
2186 {
2187 pTextNode = pTextNd->GetTextNode();
2188 if (pTextNode && pDelNode != pTextNode )
2189 {
2190 SwPosition aPos(aIdx);
2191
2192 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2193 {
2194 bCompress = true;
2195
2196 // split redline to store ExtraData per paragraphs
2197 SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
2198 pPar->SetStart( aPos );
2199 pNewRedl->SetEnd( aPos );
2200
2201 // get extradata for reset formatting of the modified paragraph
2202 SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
2203 if (pExtraData)
2204 {
2205 std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
2206 if (!bFirst)
2207 pExtraData->SetFormatAll(false);
2208 xRedlineExtraData.reset(pExtraData);
2209 pPar->SetExtraData( xRedlineExtraData.get() );
2210 }
2211
2212 // skip empty redlines without ExtraData
2213 // FIXME: maybe checking pExtraData is redundant here
2214 if ( pExtraData || *pPar->Start() != *pPar->End() )
2215 mpRedlineTable->Insert( pPar );
2216 else
2217 delete pPar;
2218 }
2219
2220 // modify paragraph formatting
2221 lcl_CopyStyle(*pStt, aPos);
2222 }
2223 pTextNd = SwNodes::GoPrevious( &aIdx );
2224
2225 if (bFirst)
2226 bFirst = false;
2227 }
2228 }
2229 }
2230 bool const ret = mpRedlineTable->Insert( pNewRedl );
2231 assert(ret || !pNewRedl);
2232 if (ret && !pNewRedl)
2233 {
2234 bMerged = true; // treat InsertWithValidRanges as "merge"
2235 }
2236 }
2237 }
2238
2239 if( bCompress )
2240 CompressRedlines();
2241 }
2242 else
2243 {
2244 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2245 {
2246 RedlineFlags eOld = meRedlineFlags;
2247 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2248 // The ShowMode needs to be retained!
2249 meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
2250 m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
2251 meRedlineFlags = eOld;
2252 }
2253 delete pNewRedl;
2254 pNewRedl = nullptr;
2255 }
2256 CHECK_REDLINE( *this )
2257
2258 return (nullptr != pNewRedl)
2259 ? AppendResult::APPENDED
2260 : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
2261 }
2262
AppendTableRowRedline(SwTableRowRedline * pNewRedl)2263 bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
2264 {
2265 // #TODO - equivalent for 'SwTableRowRedline'
2266 /*
2267 CHECK_REDLINE( this )
2268 */
2269
2270 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2271 {
2272 // #TODO - equivalent for 'SwTableRowRedline'
2273 /*
2274 pNewRedl->InvalidateRange();
2275 */
2276
2277 // Make equivalent of 'AppendRedline' checks inside here too
2278
2279 mpExtraRedlineTable->Insert( pNewRedl );
2280 }
2281 else
2282 {
2283 // TO DO - equivalent for 'SwTableRowRedline'
2284 /*
2285 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2286 {
2287 RedlineFlags eOld = meRedlineFlags;
2288 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2289 // The ShowMode needs to be retained!
2290 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2291 DeleteAndJoin( *pNewRedl );
2292 meRedlineFlags = eOld;
2293 }
2294 delete pNewRedl, pNewRedl = 0;
2295 */
2296 }
2297 // #TODO - equivalent for 'SwTableRowRedline'
2298 /*
2299 CHECK_REDLINE( this )
2300 */
2301
2302 return nullptr != pNewRedl;
2303 }
2304
AppendTableCellRedline(SwTableCellRedline * pNewRedl)2305 bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
2306 {
2307 // #TODO - equivalent for 'SwTableCellRedline'
2308 /*
2309 CHECK_REDLINE( this )
2310 */
2311
2312 if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
2313 {
2314 // #TODO - equivalent for 'SwTableCellRedline'
2315 /*
2316 pNewRedl->InvalidateRange();
2317 */
2318
2319 // Make equivalent of 'AppendRedline' checks inside here too
2320
2321 mpExtraRedlineTable->Insert( pNewRedl );
2322 }
2323 else
2324 {
2325 // TO DO - equivalent for 'SwTableCellRedline'
2326 /*
2327 if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
2328 {
2329 RedlineFlags eOld = meRedlineFlags;
2330 // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
2331 // The ShowMode needs to be retained!
2332 meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
2333 DeleteAndJoin( *pNewRedl );
2334 meRedlineFlags = eOld;
2335 }
2336 delete pNewRedl, pNewRedl = 0;
2337 */
2338 }
2339 // #TODO - equivalent for 'SwTableCellRedline'
2340 /*
2341 CHECK_REDLINE( this )
2342 */
2343
2344 return nullptr != pNewRedl;
2345 }
2346
CompressRedlines()2347 void DocumentRedlineManager::CompressRedlines()
2348 {
2349 CHECK_REDLINE( *this )
2350
2351 void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
2352 RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
2353 if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
2354 pFnc = &SwRangeRedline::Show;
2355 else if (eShow == RedlineFlags::ShowInsert)
2356 pFnc = &SwRangeRedline::Hide;
2357
2358 // Try to merge identical ones
2359 for( SwRedlineTable::size_type n = 1; n < mpRedlineTable->size(); ++n )
2360 {
2361 SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ],
2362 * pCur = (*mpRedlineTable)[ n ];
2363 const SwPosition* pPrevStt = pPrev->Start(),
2364 * pPrevEnd = pPrevStt == pPrev->GetPoint()
2365 ? pPrev->GetMark() : pPrev->GetPoint();
2366 const SwPosition* pCurStt = pCur->Start(),
2367 * pCurEnd = pCurStt == pCur->GetPoint()
2368 ? pCur->GetMark() : pCur->GetPoint();
2369 if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
2370 pPrevStt->nNode.GetNode().StartOfSectionNode() ==
2371 pCurEnd->nNode.GetNode().StartOfSectionNode() &&
2372 !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
2373 {
2374 // we then can merge them
2375 SwRedlineTable::size_type nPrevIndex = n-1;
2376 pPrev->Show(0, nPrevIndex);
2377 pCur->Show(0, n);
2378
2379 pPrev->SetEnd( *pCur->End() );
2380 mpRedlineTable->DeleteAndDestroy( n );
2381 --n;
2382 if( pFnc )
2383 (pPrev->*pFnc)(0, nPrevIndex, false);
2384 }
2385 }
2386 CHECK_REDLINE( *this )
2387
2388 // #TODO - add 'SwExtraRedlineTable' also ?
2389 }
2390
SplitRedline(const SwPaM & rRange)2391 bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
2392 {
2393 bool bChg = false;
2394 SwRedlineTable::size_type n = 0;
2395 const SwPosition* pStt = rRange.Start();
2396 const SwPosition* pEnd = rRange.End();
2397 GetRedline( *pStt, &n );
2398 for ( ; n < mpRedlineTable->size(); ++n)
2399 {
2400 SwRangeRedline * pRedline = (*mpRedlineTable)[ n ];
2401 SwPosition *const pRedlineStart = pRedline->Start();
2402 SwPosition *const pRedlineEnd = pRedline->End();
2403 if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
2404 *pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
2405 {
2406 bChg = true;
2407 int nn = 0;
2408 if (*pStt == *pRedlineStart)
2409 nn += 1;
2410 if (*pEnd == *pRedlineEnd)
2411 nn += 2;
2412
2413 SwRangeRedline* pNew = nullptr;
2414 switch( nn )
2415 {
2416 case 0:
2417 pNew = new SwRangeRedline( *pRedline );
2418 pRedline->SetEnd( *pStt, pRedlineEnd );
2419 pNew->SetStart( *pEnd );
2420 break;
2421
2422 case 1:
2423 *pRedlineStart = *pEnd;
2424 break;
2425
2426 case 2:
2427 *pRedlineEnd = *pStt;
2428 break;
2429
2430 case 3:
2431 pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2432 mpRedlineTable->DeleteAndDestroy( n-- );
2433 pRedline = nullptr;
2434 break;
2435 }
2436 if (pRedline && !pRedline->HasValidRange())
2437 {
2438 // re-insert
2439 mpRedlineTable->Remove( n );
2440 mpRedlineTable->Insert( pRedline, n );
2441 }
2442 if( pNew )
2443 mpRedlineTable->Insert( pNew, n );
2444 }
2445 else if (*pEnd < *pRedlineStart)
2446 break;
2447 }
2448 return bChg;
2449
2450 // #TODO - add 'SwExtraRedlineTable' also ?
2451 }
2452
DeleteRedline(const SwPaM & rRange,bool bSaveInUndo,RedlineType nDelType)2453 bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
2454 RedlineType nDelType )
2455 {
2456 if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
2457 return false;
2458
2459 bool bChg = false;
2460
2461 if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2462 {
2463 std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
2464 if( pUndo->GetRedlSaveCount() )
2465 {
2466 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
2467 }
2468 }
2469
2470 const SwPosition* pStt = rRange.Start(),
2471 * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark()
2472 : rRange.GetPoint();
2473 SwRedlineTable::size_type n = 0;
2474 GetRedline( *pStt, &n );
2475 for( ; n < mpRedlineTable->size() ; ++n )
2476 {
2477 SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
2478 if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
2479 continue;
2480
2481 SwPosition* pRStt = pRedl->Start(),
2482 * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
2483 : pRedl->GetPoint();
2484 switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
2485 {
2486 case SwComparePosition::Equal:
2487 case SwComparePosition::Outside:
2488 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2489 mpRedlineTable->DeleteAndDestroy( n-- );
2490 bChg = true;
2491 break;
2492
2493 case SwComparePosition::OverlapBefore:
2494 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2495 pRedl->SetStart( *pEnd, pRStt );
2496 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2497 // re-insert
2498 mpRedlineTable->Remove( n );
2499 mpRedlineTable->Insert( pRedl );
2500 --n;
2501 break;
2502
2503 case SwComparePosition::OverlapBehind:
2504 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2505 pRedl->SetEnd( *pStt, pREnd );
2506 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2507 if( !pRedl->HasValidRange() )
2508 {
2509 // re-insert
2510 mpRedlineTable->Remove( n );
2511 mpRedlineTable->Insert( pRedl );
2512 --n;
2513 }
2514 break;
2515
2516 case SwComparePosition::Inside:
2517 {
2518 // this one needs to be split
2519 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2520 if( *pRStt == *pStt )
2521 {
2522 pRedl->SetStart( *pEnd, pRStt );
2523 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2524 // re-insert
2525 mpRedlineTable->Remove( n );
2526 mpRedlineTable->Insert( pRedl );
2527 --n;
2528 }
2529 else
2530 {
2531 SwRangeRedline* pCpy;
2532 if( *pREnd != *pEnd )
2533 {
2534 pCpy = new SwRangeRedline( *pRedl );
2535 pCpy->SetStart( *pEnd );
2536 pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
2537 }
2538 else
2539 pCpy = nullptr;
2540 pRedl->SetEnd( *pStt, pREnd );
2541 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
2542 if( !pRedl->HasValidRange() )
2543 {
2544 // re-insert
2545 mpRedlineTable->Remove( n );
2546 mpRedlineTable->Insert( pRedl );
2547 --n;
2548 }
2549 if( pCpy )
2550 mpRedlineTable->Insert( pCpy );
2551 }
2552 }
2553 break;
2554
2555 case SwComparePosition::CollideEnd:
2556 // remove (not hidden) empty redlines created for fixing tdf#119571
2557 // (Note: hidden redlines are all empty, i.e. start and end are equal.)
2558 if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
2559 {
2560 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
2561 mpRedlineTable->DeleteAndDestroy( n-- );
2562 bChg = true;
2563 break;
2564 }
2565 [[fallthrough]];
2566
2567 case SwComparePosition::Before:
2568 n = mpRedlineTable->size();
2569 break;
2570 default:
2571 break;
2572 }
2573 }
2574
2575 if( bChg )
2576 m_rDoc.getIDocumentState().SetModified();
2577
2578 return bChg;
2579
2580 // #TODO - add 'SwExtraRedlineTable' also ?
2581 }
2582
DeleteRedline(const SwStartNode & rNode,bool bSaveInUndo,RedlineType nDelType)2583 bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
2584 RedlineType nDelType )
2585 {
2586 SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
2587 return DeleteRedline(aTemp, bSaveInUndo, nDelType);
2588 }
2589
GetRedlinePos(const SwNode & rNd,RedlineType nType) const2590 SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
2591 {
2592 const sal_uLong nNdIdx = rNd.GetIndex();
2593 for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
2594 {
2595 const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2596 sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
2597 nMk = pTmp->GetMark()->nNode.GetIndex();
2598 if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
2599
2600 if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
2601 nMk <= nNdIdx && nNdIdx <= nPt )
2602 return n;
2603
2604 if( nMk > nNdIdx )
2605 break;
2606 }
2607 return SwRedlineTable::npos;
2608
2609 // #TODO - add 'SwExtraRedlineTable' also ?
2610 }
2611
HasRedline(const SwPaM & rPam,RedlineType nType,bool bStartOrEndInRange) const2612 bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
2613 {
2614 SwPosition currentStart(*rPam.Start());
2615 SwPosition currentEnd(*rPam.End());
2616 SwNodeIndex pEndNodeIndex(currentEnd.nNode.GetNode());
2617
2618 for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->nNode.GetNode(), nType );
2619 n < mpRedlineTable->size(); ++n )
2620 {
2621 const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2622
2623 if ( pTmp->Start()->nNode > pEndNodeIndex )
2624 break;
2625
2626 if( RedlineType::Any != nType && nType != pTmp->GetType() )
2627 continue;
2628
2629 // redline over the range
2630 if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
2631 // starting or ending within the range
2632 ( !bStartOrEndInRange ||
2633 ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
2634 {
2635 return true;
2636 }
2637 }
2638 return false;
2639 }
2640
GetRedline(const SwPosition & rPos,SwRedlineTable::size_type * pFndPos) const2641 const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
2642 SwRedlineTable::size_type* pFndPos ) const
2643 {
2644 SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0;
2645 if( nO > 0 )
2646 {
2647 nO--;
2648 while( nU <= nO )
2649 {
2650 nM = nU + ( nO - nU ) / 2;
2651 const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ];
2652 const SwPosition* pStt = pRedl->Start();
2653 const SwPosition* pEnd = pStt == pRedl->GetPoint()
2654 ? pRedl->GetMark()
2655 : pRedl->GetPoint();
2656 if( pEnd == pStt
2657 ? *pStt == rPos
2658 : ( *pStt <= rPos && rPos < *pEnd ) )
2659 {
2660 while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() &&
2661 rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() )
2662 {
2663 --nM;
2664 pRedl = (*mpRedlineTable)[ nM ];
2665 }
2666 // if there are format and insert changes in the same position
2667 // show insert change first.
2668 // since the redlines are sorted by position, only check the redline
2669 // before and after the current redline
2670 if( RedlineType::Format == pRedl->GetType() )
2671 {
2672 if( nM && rPos >= *(*mpRedlineTable)[ nM - 1 ]->Start() &&
2673 rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() &&
2674 ( RedlineType::Insert == (*mpRedlineTable)[ nM - 1 ]->GetType() ) )
2675 {
2676 --nM;
2677 pRedl = (*mpRedlineTable)[ nM ];
2678 }
2679 else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() &&
2680 rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() &&
2681 ( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) )
2682 {
2683 ++nM;
2684 pRedl = (*mpRedlineTable)[ nM ];
2685 }
2686 }
2687
2688 if( pFndPos )
2689 *pFndPos = nM;
2690 return pRedl;
2691 }
2692 else if( *pEnd <= rPos )
2693 nU = nM + 1;
2694 else if( nM == 0 )
2695 {
2696 if( pFndPos )
2697 *pFndPos = nU;
2698 return nullptr;
2699 }
2700 else
2701 nO = nM - 1;
2702 }
2703 }
2704 if( pFndPos )
2705 *pFndPos = nU;
2706 return nullptr;
2707
2708 // #TODO - add 'SwExtraRedlineTable' also ?
2709 }
2710
AcceptRedline(SwRedlineTable::size_type nPos,bool bCallDelete)2711 bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
2712 {
2713 bool bRet = false;
2714
2715 // Switch to visible in any case
2716 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2717 (RedlineFlags::ShowMask & meRedlineFlags) )
2718 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2719
2720 SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
2721 pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2722 pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2723 if( pTmp->HasMark() && pTmp->IsVisible() )
2724 {
2725 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2726 {
2727 SwRewriter aRewriter;
2728
2729 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
2730 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
2731 }
2732
2733 int nLoopCnt = 2;
2734 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2735
2736 do {
2737
2738 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2739 {
2740 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2741 std::make_unique<SwUndoAcceptRedline>(*pTmp) );
2742 }
2743
2744 bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete );
2745
2746 if( nSeqNo )
2747 {
2748 if( SwRedlineTable::npos == nPos )
2749 nPos = 0;
2750 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2751 ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
2752 : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
2753 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
2754 SwRedlineTable::npos != ( nFndPos =
2755 mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
2756 {
2757 nPos = nFndPos;
2758 pTmp = (*mpRedlineTable)[ nPos ];
2759 }
2760 else
2761 nLoopCnt = 0;
2762 }
2763 else
2764 nLoopCnt = 0;
2765
2766 } while( nLoopCnt );
2767
2768 if( bRet )
2769 {
2770 CompressRedlines();
2771 m_rDoc.getIDocumentState().SetModified();
2772 }
2773
2774 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2775 {
2776 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2777 }
2778 }
2779 return bRet;
2780
2781 // #TODO - add 'SwExtraRedlineTable' also ?
2782 }
2783
AcceptRedline(const SwPaM & rPam,bool bCallDelete)2784 bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
2785 {
2786 // Switch to visible in any case
2787 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2788 (RedlineFlags::ShowMask & meRedlineFlags) )
2789 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2790
2791 // The Selection is only in the ContentSection. If there are Redlines
2792 // to Non-ContentNodes before or after that, then the Selections
2793 // expand to them.
2794 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2795 lcl_AdjustRedlineRange( aPam );
2796
2797 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2798 {
2799 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
2800 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam ));
2801 }
2802
2803 int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable,
2804 bCallDelete, aPam );
2805 if( nRet > 0 )
2806 {
2807 CompressRedlines();
2808 m_rDoc.getIDocumentState().SetModified();
2809 }
2810 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2811 {
2812 OUString aTmpStr;
2813
2814 {
2815 SwRewriter aRewriter;
2816 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
2817 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2818 }
2819
2820 SwRewriter aRewriter;
2821 aRewriter.AddRule(UndoArg1, aTmpStr);
2822
2823 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
2824 }
2825 return nRet != 0;
2826
2827 // #TODO - add 'SwExtraRedlineTable' also ?
2828 }
2829
AcceptRedlineParagraphFormatting(const SwPaM & rPam)2830 void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
2831 {
2832 const SwPosition* pStt = rPam.Start(),
2833 * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
2834 : rPam.GetPoint();
2835
2836 const sal_uLong nSttIdx = pStt->nNode.GetIndex();
2837 const sal_uLong nEndIdx = pEnd->nNode.GetIndex();
2838
2839 for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
2840 {
2841 const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
2842 sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
2843 nMk = pTmp->GetMark()->nNode.GetIndex();
2844 if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
2845
2846 if( RedlineType::ParagraphFormat == pTmp->GetType() &&
2847 ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
2848 AcceptRedline( n, false );
2849
2850 if( nMk > nEndIdx )
2851 break;
2852 }
2853 }
2854
RejectRedline(SwRedlineTable::size_type nPos,bool bCallDelete)2855 bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
2856 {
2857 bool bRet = false;
2858
2859 // Switch to visible in any case
2860 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2861 (RedlineFlags::ShowMask & meRedlineFlags) )
2862 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2863
2864 SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
2865 pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2866 pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
2867 if( pTmp->HasMark() && pTmp->IsVisible() )
2868 {
2869 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2870 {
2871 SwRewriter aRewriter;
2872
2873 aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
2874 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
2875 }
2876
2877 int nLoopCnt = 2;
2878 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2879
2880 do {
2881
2882 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2883 {
2884 m_rDoc.GetIDocumentUndoRedo().AppendUndo(
2885 std::make_unique<SwUndoRejectRedline>( *pTmp ) );
2886 }
2887
2888 bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete );
2889
2890 if( nSeqNo )
2891 {
2892 if( SwRedlineTable::npos == nPos )
2893 nPos = 0;
2894 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2895 ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
2896 : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
2897 if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
2898 SwRedlineTable::npos != ( nFndPos =
2899 mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
2900 {
2901 nPos = nFndPos;
2902 pTmp = (*mpRedlineTable)[ nPos ];
2903 }
2904 else
2905 nLoopCnt = 0;
2906 }
2907 else
2908 nLoopCnt = 0;
2909
2910 } while( nLoopCnt );
2911
2912 if( bRet )
2913 {
2914 CompressRedlines();
2915 m_rDoc.getIDocumentState().SetModified();
2916 }
2917
2918 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2919 {
2920 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
2921 }
2922 }
2923 return bRet;
2924
2925 // #TODO - add 'SwExtraRedlineTable' also ?
2926 }
2927
RejectRedline(const SwPaM & rPam,bool bCallDelete)2928 bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
2929 {
2930 // Switch to visible in any case
2931 if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
2932 (RedlineFlags::ShowMask & meRedlineFlags) )
2933 SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
2934
2935 // The Selection is only in the ContentSection. If there are Redlines
2936 // to Non-ContentNodes before or after that, then the Selections
2937 // expand to them.
2938 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2939 lcl_AdjustRedlineRange( aPam );
2940
2941 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2942 {
2943 m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
2944 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
2945 }
2946
2947 int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable,
2948 bCallDelete, aPam );
2949 if( nRet > 0 )
2950 {
2951 CompressRedlines();
2952 m_rDoc.getIDocumentState().SetModified();
2953 }
2954 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
2955 {
2956 OUString aTmpStr;
2957
2958 {
2959 SwRewriter aRewriter;
2960 aRewriter.AddRule(UndoArg1, OUString::number(nRet));
2961 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2962 }
2963
2964 SwRewriter aRewriter;
2965 aRewriter.AddRule(UndoArg1, aTmpStr);
2966
2967 m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
2968 }
2969
2970 return nRet != 0;
2971
2972 // #TODO - add 'SwExtraRedlineTable' also ?
2973 }
2974
AcceptAllRedline(bool bAccept)2975 void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
2976 {
2977 bool bSuccess = true;
2978 OUString sUndoStr;
2979 IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
2980
2981 if (mpRedlineTable->size() > 1)
2982 {
2983 {
2984 SwRewriter aRewriter;
2985 aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size()));
2986 sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
2987 }
2988
2989 SwRewriter aRewriter;
2990 aRewriter.AddRule(UndoArg1, sUndoStr);
2991 rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
2992 }
2993
2994 while (!mpRedlineTable->empty() && bSuccess)
2995 {
2996 if (bAccept)
2997 bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true);
2998 else
2999 bSuccess = RejectRedline(mpRedlineTable->size() - 1, true);
3000 }
3001
3002 if (!sUndoStr.isEmpty())
3003 {
3004 rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
3005 }
3006 }
3007
SelNextRedline(SwPaM & rPam) const3008 const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
3009 {
3010 rPam.DeleteMark();
3011 rPam.SetMark();
3012
3013 SwPosition& rSttPos = *rPam.GetPoint();
3014 SwPosition aSavePos( rSttPos );
3015 bool bRestart;
3016
3017 // If the starting position points to the last valid ContentNode,
3018 // we take the next Redline in any case.
3019 SwRedlineTable::size_type n = 0;
3020 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
3021 if( pFnd )
3022 {
3023 const SwPosition* pEnd = pFnd->End();
3024 if( !pEnd->nNode.GetNode().IsContentNode() )
3025 {
3026 SwNodeIndex aTmp( pEnd->nNode );
3027 SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
3028 if( !pCNd || ( aTmp == rSttPos.nNode &&
3029 pCNd->Len() == rSttPos.nContent.GetIndex() ))
3030 pFnd = nullptr;
3031 }
3032 if( pFnd )
3033 rSttPos = *pFnd->End();
3034 }
3035
3036 do {
3037 bRestart = false;
3038
3039 for( ; !pFnd && n < mpRedlineTable->size(); ++n )
3040 {
3041 pFnd = (*mpRedlineTable)[ n ];
3042 if( pFnd->HasMark() && pFnd->IsVisible() )
3043 {
3044 *rPam.GetMark() = *pFnd->Start();
3045 rSttPos = *pFnd->End();
3046 break;
3047 }
3048 else
3049 pFnd = nullptr;
3050 }
3051
3052 if( pFnd )
3053 {
3054 // Merge all of the same type and author that are
3055 // consecutive into one Selection.
3056 const SwPosition* pPrevEnd = pFnd->End();
3057 while( ++n < mpRedlineTable->size() )
3058 {
3059 const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
3060 if( pTmp->HasMark() && pTmp->IsVisible() )
3061 {
3062 const SwPosition *pRStt;
3063 if( pFnd->GetType() != pTmp->GetType() ||
3064 pFnd->GetAuthor() != pTmp->GetAuthor() )
3065 break;
3066 pRStt = pTmp->Start();
3067 if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
3068 {
3069 pPrevEnd = pTmp->End();
3070 rSttPos = *pPrevEnd;
3071 }
3072 else
3073 break;
3074 }
3075 }
3076 }
3077
3078 if( pFnd )
3079 {
3080 const SwRangeRedline* pSaveFnd = pFnd;
3081
3082 SwContentNode* pCNd;
3083 SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
3084 if( !pIdx->GetNode().IsContentNode() )
3085 {
3086 pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
3087 if( pCNd )
3088 {
3089 if( *pIdx <= rPam.GetPoint()->nNode )
3090 rPam.GetMark()->nContent.Assign( pCNd, 0 );
3091 else
3092 pFnd = nullptr;
3093 }
3094 }
3095
3096 if( pFnd )
3097 {
3098 pIdx = &rPam.GetPoint()->nNode;
3099 if( !pIdx->GetNode().IsContentNode() )
3100 {
3101 pCNd = SwNodes::GoPrevSection( pIdx );
3102 if( pCNd )
3103 {
3104 if( *pIdx >= rPam.GetMark()->nNode )
3105 rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
3106 else
3107 pFnd = nullptr;
3108 }
3109 }
3110 }
3111
3112 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3113 {
3114 if( n < mpRedlineTable->size() )
3115 {
3116 bRestart = true;
3117 *rPam.GetPoint() = *pSaveFnd->End();
3118 }
3119 else
3120 {
3121 rPam.DeleteMark();
3122 *rPam.GetPoint() = aSavePos;
3123 }
3124 pFnd = nullptr;
3125 }
3126 }
3127 } while( bRestart );
3128
3129 return pFnd;
3130
3131 // #TODO - add 'SwExtraRedlineTable' also ?
3132 }
3133
SelPrevRedline(SwPaM & rPam) const3134 const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
3135 {
3136 rPam.DeleteMark();
3137 rPam.SetMark();
3138
3139 SwPosition& rSttPos = *rPam.GetPoint();
3140 SwPosition aSavePos( rSttPos );
3141 bool bRestart;
3142
3143 // If the starting position points to the last valid ContentNode,
3144 // we take the previous Redline in any case.
3145 SwRedlineTable::size_type n = 0;
3146 const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
3147 if( pFnd )
3148 {
3149 const SwPosition* pStt = pFnd->Start();
3150 if( !pStt->nNode.GetNode().IsContentNode() )
3151 {
3152 SwNodeIndex aTmp( pStt->nNode );
3153 SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
3154 if( !pCNd || ( aTmp == rSttPos.nNode &&
3155 !rSttPos.nContent.GetIndex() ))
3156 pFnd = nullptr;
3157 }
3158 if( pFnd )
3159 rSttPos = *pFnd->Start();
3160 }
3161
3162 do {
3163 bRestart = false;
3164
3165 while( !pFnd && 0 < n )
3166 {
3167 pFnd = (*mpRedlineTable)[ --n ];
3168 if( pFnd->HasMark() && pFnd->IsVisible() )
3169 {
3170 *rPam.GetMark() = *pFnd->End();
3171 rSttPos = *pFnd->Start();
3172 }
3173 else
3174 pFnd = nullptr;
3175 }
3176
3177 if( pFnd )
3178 {
3179 // Merge all of the same type and author that are
3180 // consecutive into one Selection.
3181 const SwPosition* pNextStt = pFnd->Start();
3182 while( 0 < n )
3183 {
3184 const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ];
3185 if( pTmp->HasMark() && pTmp->IsVisible() )
3186 {
3187 const SwPosition *pREnd;
3188 if( pFnd->GetType() == pTmp->GetType() &&
3189 pFnd->GetAuthor() == pTmp->GetAuthor() &&
3190 ( *pNextStt == *( pREnd = pTmp->End() ) ||
3191 IsPrevPos( *pREnd, *pNextStt )) )
3192 {
3193 pNextStt = pTmp->Start();
3194 rSttPos = *pNextStt;
3195 }
3196 else
3197 {
3198 ++n;
3199 break;
3200 }
3201 }
3202 }
3203 }
3204
3205 if( pFnd )
3206 {
3207 const SwRangeRedline* pSaveFnd = pFnd;
3208
3209 SwContentNode* pCNd;
3210 SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
3211 if( !pIdx->GetNode().IsContentNode() )
3212 {
3213 pCNd = SwNodes::GoPrevSection( pIdx );
3214 if( pCNd )
3215 {
3216 if( *pIdx >= rPam.GetPoint()->nNode )
3217 rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
3218 else
3219 pFnd = nullptr;
3220 }
3221 }
3222
3223 if( pFnd )
3224 {
3225 pIdx = &rPam.GetPoint()->nNode;
3226 if( !pIdx->GetNode().IsContentNode() )
3227 {
3228 pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
3229 if( pCNd )
3230 {
3231 if( *pIdx <= rPam.GetMark()->nNode )
3232 rPam.GetPoint()->nContent.Assign( pCNd, 0 );
3233 else
3234 pFnd = nullptr;
3235 }
3236 }
3237 }
3238
3239 if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
3240 {
3241 if( n )
3242 {
3243 bRestart = true;
3244 *rPam.GetPoint() = *pSaveFnd->Start();
3245 }
3246 else
3247 {
3248 rPam.DeleteMark();
3249 *rPam.GetPoint() = aSavePos;
3250 }
3251 pFnd = nullptr;
3252 }
3253 }
3254 } while( bRestart );
3255
3256 return pFnd;
3257
3258 // #TODO - add 'SwExtraRedlineTable' also ?
3259 }
3260
3261 // Set comment at the Redline
SetRedlineComment(const SwPaM & rPaM,const OUString & rS)3262 bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
3263 {
3264 bool bRet = false;
3265 const SwPosition* pStt = rPaM.Start(),
3266 * pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
3267 : rPaM.GetPoint();
3268 SwRedlineTable::size_type n = 0;
3269 if( GetRedlineTable().FindAtPosition( *pStt, n ) )
3270 {
3271 for( ; n < mpRedlineTable->size(); ++n )
3272 {
3273 bRet = true;
3274 SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
3275 if( pStt != pEnd && *pTmp->Start() > *pEnd )
3276 break;
3277
3278 pTmp->SetComment( rS );
3279 if( *pTmp->End() >= *pEnd )
3280 break;
3281 }
3282 }
3283 if( bRet )
3284 m_rDoc.getIDocumentState().SetModified();
3285
3286 return bRet;
3287
3288 // #TODO - add 'SwExtraRedlineTable' also ?
3289 }
3290
3291 // Create a new author if necessary
GetRedlineAuthor()3292 std::size_t DocumentRedlineManager::GetRedlineAuthor()
3293 {
3294 return SW_MOD()->GetRedlineAuthor();
3295 }
3296
3297 /// Insert new author into the Table for the Readers etc.
InsertRedlineAuthor(const OUString & rNew)3298 std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
3299 {
3300 return SW_MOD()->InsertRedlineAuthor(rNew);
3301 }
3302
UpdateRedlineAttr()3303 void DocumentRedlineManager::UpdateRedlineAttr()
3304 {
3305 const SwRedlineTable& rTable = GetRedlineTable();
3306 for(SwRangeRedline* pRedl : rTable)
3307 {
3308 if( pRedl->IsVisible() )
3309 pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
3310 }
3311
3312 // #TODO - add 'SwExtraRedlineTable' also ?
3313 }
3314
GetRedlinePassword() const3315 const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
3316 {
3317 return maRedlinePasswd;
3318 }
3319
SetRedlinePassword(const uno::Sequence<sal_Int8> & rNewPassword)3320 void DocumentRedlineManager::SetRedlinePassword(
3321 /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
3322 {
3323 maRedlinePasswd = rNewPassword;
3324 m_rDoc.getIDocumentState().SetModified();
3325 }
3326
3327 /// Set comment text for the Redline, which is inserted later on via
3328 /// AppendRedline. Is used by Autoformat.
3329 /// A null pointer resets the mode. The pointer is not copied, so it
3330 /// needs to stay valid!
SetAutoFormatRedlineComment(const OUString * pText,sal_uInt16 nSeqNo)3331 void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
3332 {
3333 m_rDoc.SetAutoFormatRedline( nullptr != pText );
3334 if( pText )
3335 {
3336 mpAutoFormatRedlnComment.reset( new OUString( *pText ) );
3337 }
3338 else
3339 {
3340 mpAutoFormatRedlnComment.reset();
3341 }
3342
3343 mnAutoFormatRedlnCommentNo = nSeqNo;
3344 }
3345
HideAll(bool bDeletion)3346 void DocumentRedlineManager::HideAll( bool bDeletion )
3347 {
3348 const SwRedlineTable& rTable = GetRedlineTable();
3349 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3350 {
3351 SwRangeRedline* pRedline = rTable[i-1];
3352 if ( pRedline->GetType() == RedlineType::Delete )
3353 {
3354 if ( bDeletion && pRedline->IsVisible() )
3355 {
3356 pRedline->Hide(0, rTable.GetPos(pRedline), false);
3357 pRedline->Hide(1, rTable.GetPos(pRedline), false);
3358 }
3359 else if ( !bDeletion && !pRedline->IsVisible() )
3360 {
3361 pRedline->Show(0, rTable.GetPos(pRedline), true);
3362 pRedline->Show(1, rTable.GetPos(pRedline), true);
3363 }
3364 }
3365 else if ( pRedline->GetType() == RedlineType::Insert )
3366 {
3367 if ( !bDeletion && pRedline->IsVisible() )
3368 {
3369 pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
3370 pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
3371 }
3372 else if ( bDeletion && !pRedline->IsVisible() )
3373 {
3374 pRedline->Show(0, rTable.GetPos(pRedline), true);
3375 pRedline->Show(1, rTable.GetPos(pRedline), true);
3376 }
3377 }
3378 }
3379 }
3380
ShowAll()3381 void DocumentRedlineManager::ShowAll()
3382 {
3383 const SwRedlineTable& rTable = GetRedlineTable();
3384 for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
3385 {
3386 SwRangeRedline* pRedline = rTable[i-1];
3387 if ( !pRedline->IsVisible() )
3388 {
3389 pRedline->Show(0, rTable.GetPos(pRedline), true);
3390 pRedline->Show(1, rTable.GetPos(pRedline), true);
3391 }
3392 }
3393 }
3394
~DocumentRedlineManager()3395 DocumentRedlineManager::~DocumentRedlineManager()
3396 {
3397 }
3398
3399 }
3400
3401 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3402