Files
odfstudio/sw/source/core/doc/DocumentRedlineManager.cxx
T
László Németh 6aeeef8807 tdf#116757 sw ChangesInMargin: add "Show Insertions in Margin"
layout mode to allow to show the original text inline,
i.e. only deletions, while insertions are showed in margin
(opposite of the Show Deletions in Margin mode).

Add a menu to the Show Changes icon on the
Changes toolbar with menu items for the 3 layout modes
"All Changes Inline", "Deletions in Margin" and
"Insertions in Margin" with the following behavior:

– Selecting a menu item turns on also the Show Changes mode
  to show the result immediately;

– Disabling Show Changes mode always show the final text, i.e.
  only insertions. Enabling Show Changes mode again shows the
  changes according to the last Show Changes mode selected in
  the menu.

– The new "Insertions in Margin" mode is intended to be only a
  run-time option, i.e. it's not possible to set it as a default
  Show Changes mode in Options, unlike "Deletions in Margin".

Change-Id: Ie4dc9fe3b4b201fa9976ef04877af649e242ab46
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109649
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
2021-01-20 19:08:05 +01:00

3348 lines
128 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <DocumentRedlineManager.hxx>
#include <frmfmt.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>
#include <doc.hxx>
#include <docsh.hxx>
#include <fmtfld.hxx>
#include <frmtool.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentState.hxx>
#include <redline.hxx>
#include <UndoRedline.hxx>
#include <docary.hxx>
#include <ndtxt.hxx>
#include <unocrsr.hxx>
#include <ftnidx.hxx>
#include <authfld.hxx>
#include <strings.hrc>
#include <swmodule.hxx>
#include <osl/diagnose.h>
using namespace com::sun::star;
#ifdef DBG_UTIL
#define ERROR_PREFIX "redline table corrupted: "
namespace
{
// helper function for lcl_CheckRedline
// 1. make sure that pPos->nContent points into pPos->nNode
// 2. check that position is valid and doesn't point after text
void lcl_CheckPosition( const SwPosition* pPos )
{
assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
== pPos->nContent.GetIdxReg());
SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
if( pTextNode == nullptr )
{
assert(pPos->nContent == 0);
}
else
{
assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
}
}
void lcl_CheckPam( const SwPaM* pPam )
{
assert(pPam);
lcl_CheckPosition( pPam->GetPoint() );
lcl_CheckPosition( pPam->GetMark() );
}
// check validity of the redline table. Checks redline bounds, and make
// sure the redlines are sorted and non-overlapping.
void lcl_CheckRedline( IDocumentRedlineAccess& redlineAccess )
{
const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
// verify valid redline positions
for(SwRangeRedline* i : rTable)
lcl_CheckPam( i );
for(SwRangeRedline* j : rTable)
{
// check for empty redlines
// note: these can destroy sorting in SwTextNode::Update()
// if there's another one without mark on the same pos.
OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
( j->GetContentIdx() != nullptr ),
ERROR_PREFIX "empty redline" );
}
// verify proper redline sorting
for( size_t n = 1; n < rTable.size(); ++n )
{
const SwRangeRedline* pPrev = rTable[ n-1 ];
const SwRangeRedline* pCurrent = rTable[ n ];
// check redline sorting
SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
ERROR_PREFIX "not sorted correctly" );
// check for overlapping redlines
SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
ERROR_PREFIX "overlapping redlines" );
}
assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
}
}
#define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
#else
#define CHECK_REDLINE( pDoc )
#endif
namespace sw {
static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
{
auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
SwFieldIds::TableOfAuthorities, OUString(), false)));
if (pAuthType) // created on demand...
{
pAuthType->DelSequenceArray();
}
rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
rIDFA.UpdateExpFields(nullptr, false);
rIDFA.UpdateRefFields();
}
void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
{
if (rDoc.IsClipBoard())
{
return;
}
// no need to call UpdateFootnoteNums for FTNNUM_PAGE:
// the AppendFootnote/RemoveFootnote will do it by itself!
rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
SwPosition currentStart(*rPam.Start());
SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
while (!pStartNode)
{
// note: branch only taken for redlines, not fieldmarks
SwStartNode *const pTableOrSectionNode(
currentStart.nNode.GetNode().IsTableNode()
? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
: static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
assert(pTableOrSectionNode); // known pathology
for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
{
pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
}
for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
{
if (pLayout->HasMergedParas())
{
if (pTableOrSectionNode->IsTableNode())
{
static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
}
else
{
static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
}
}
}
currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
pStartNode = currentStart.nNode.GetNode().GetTextNode();
}
if (currentStart < *rPam.End())
{
SwTextNode * pNode(pStartNode);
do
{
std::vector<SwTextFrame*> frames;
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{
if (pFrame->getRootFrame()->HasMergedParas())
{
frames.push_back(pFrame);
}
}
if (frames.empty())
{
auto const& layouts(rDoc.GetAllLayouts());
assert(std::none_of(layouts.begin(), layouts.end(),
[](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
(void) layouts;
break;
}
auto eMode(sw::FrameMode::Existing);
SwTextNode * pLast(pNode);
for (SwTextFrame * pFrame : frames)
{
SwTextNode & rFirstNode(pFrame->GetMergedPara()
? *pFrame->GetMergedPara()->pFirstNode
: *pNode);
assert(pNode == pStartNode
? rFirstNode.GetIndex() <= pNode->GetIndex()
: &rFirstNode == pNode);
// clear old one first to avoid DelFrames confusing updates & asserts...
pFrame->SetMergedPara(nullptr);
pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
*pFrame, rFirstNode, eMode));
eMode = sw::FrameMode::New; // Existing is not idempotent!
// the first node of the new redline is not necessarily the first
// node of the merged frame, there could be another redline nearby
sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
// if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
if (pFrame->GetMergedPara())
{
pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
}
}
SwNodeIndex tmp(*pLast);
// skip over hidden sections!
pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
}
while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
}
// fields last - SwGetRefField::UpdateField requires up-to-date frames
UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
// update SwPostItMgr / notes in the margin
rDoc.GetDocShell()->Broadcast(
SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
}
void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
{
if (rDoc.IsClipBoard())
{
return;
}
bool isAppendObjsCalled(false);
rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
SwPosition currentStart(*rPam.Start());
SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
while (!pStartNode)
{
// note: branch only taken for redlines, not fieldmarks
SwStartNode const*const pTableOrSectionNode(
currentStart.nNode.GetNode().IsTableNode()
? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
: static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
assert(pTableOrSectionNode); // known pathology
for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
{
pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
}
if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
{
// note: this will also create frames for all currently hidden flys
// because it calls AppendAllObjs
SwNodeIndex const end(*pTableOrSectionNode->EndOfSectionNode());
::MakeFrames(&rDoc, currentStart.nNode, end);
isAppendObjsCalled = true;
}
currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
pStartNode = currentStart.nNode.GetNode().GetTextNode();
}
if (currentStart < *rPam.End())
{
SwTextNode * pNode(pStartNode);
do
{
std::vector<SwTextFrame*> frames;
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
{
if (pFrame->getRootFrame()->HasMergedParas())
{
frames.push_back(pFrame);
}
}
if (frames.empty())
{
// in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
{
auto const& layouts(rDoc.GetAllLayouts());
assert(std::none_of(layouts.begin(), layouts.end(),
[](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
(void) layouts;
}
isAppendObjsCalled = true; // skip that!
break;
}
// first, call CheckParaRedlineMerge on the first paragraph,
// to init flag on new merge range (if any) + 1st node post the merge
auto eMode(sw::FrameMode::Existing);
SwTextNode * pLast(pNode);
for (SwTextFrame * pFrame : frames)
{
if (auto const pMergedPara = pFrame->GetMergedPara())
{
pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
assert(pNode == pStartNode
? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
: pMergedPara->pFirstNode == pNode);
// clear old one first to avoid DelFrames confusing updates & asserts...
SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
pFrame->SetMergedPara(nullptr);
pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
*pFrame, rFirstNode, eMode));
eMode = sw::FrameMode::New; // Existing is not idempotent!
// update pNode so MakeFrames starts on 2nd node
pNode = &rFirstNode;
}
}
if (pLast != pNode)
{
// now start node until end of merge + 1 has proper flags; MakeFrames
// should pick up from the next node in need of frames by checking flags
SwNodeIndex const start(*pNode, +1);
SwNodeIndex const end(*pLast, +1); // end is exclusive
// note: this will also create frames for all currently hidden flys
// both on first and non-first nodes because it calls AppendAllObjs
::MakeFrames(&rDoc, start, end);
isAppendObjsCalled = true;
// re-use this to move flys that are now on the wrong frame, with end
// of redline as "second" node; the nodes between start and end should
// be complete with MakeFrames already
sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
}
SwNodeIndex tmp(*pLast);
// skip over hidden sections!
pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
}
while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
}
if (!isAppendObjsCalled)
{ // recreate flys in the one node the hard way...
for (auto const& pLayout : rDoc.GetAllLayouts())
{
if (pLayout->HasMergedParas())
{
AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
break;
}
}
}
// fields last - SwGetRefField::UpdateField requires up-to-date frames
UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
// update SwPostItMgr / notes in the margin
rDoc.GetDocShell()->Broadcast(
SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
}
} // namespace sw
namespace
{
bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
{
const SwContentNode* pCNd;
if( 0 != rPos2.nContent.GetIndex() )
return false;
if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() )
return false;
pCNd = rPos1.nNode.GetNode().GetContentNode();
return pCNd && rPos1.nContent.GetIndex() == pCNd->Len();
}
// copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
{
SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
{
const SwPaM aPam(*pToNode);
SwDoc& rDoc = aPam.GetDoc();
// using Undo, copy paragraph style
SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
SwTextFormatColl* pToColl = pToNode->GetTextColl();
if (bCopy && pFromColl != pToColl)
rDoc.SetTextFormatColl(aPam, pFromColl);
// using Undo, remove direct paragraph formatting of the "To" paragraph,
// and apply here direct paragraph formatting of the "From" paragraph
SfxItemSet aTmp(
rDoc.GetAttrPool(),
svl::Items<
RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
RES_CNTNT, RES_FRMATR_END - 1>{});
SfxItemSet aTmp2(aTmp);
pToNode->GetParaAttr(aTmp, 0, 0);
pFromNode->GetParaAttr(aTmp2, 0, 0);
bool bSameSet = aTmp == aTmp2;
if (!bSameSet)
{
for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
{
sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
}
}
if (bCopy && !bSameSet)
rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
else if (!bCopy && (!bSameSet || pFromColl != pToColl))
return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
}
return nullptr;
}
bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
bool bCallDelete,
const SwPosition* pSttRng = nullptr,
const SwPosition* pEndRng = nullptr )
{
bool bRet = true;
SwRangeRedline* pRedl = rArr[ rPos ];
SwPosition *pRStt = nullptr, *pREnd = nullptr;
SwComparePosition eCmp = SwComparePosition::Outside;
if( pSttRng && pEndRng )
{
pRStt = pRedl->Start();
pREnd = pRedl->End();
eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
}
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
switch( pRedl->GetType() )
{
case RedlineType::Insert:
case RedlineType::Format:
{
bool bCheck = false, bReplace = false;
switch( eCmp )
{
case SwComparePosition::Inside:
if( *pSttRng == *pRStt )
pRedl->SetStart( *pEndRng, pRStt );
else
{
if( *pEndRng != *pREnd )
{
// split up
SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
pNew->SetStart( *pEndRng );
rArr.Insert( pNew ); ++rPos;
}
pRedl->SetEnd( *pSttRng, pREnd );
bCheck = true;
}
break;
case SwComparePosition::OverlapBefore:
pRedl->SetStart( *pEndRng, pRStt );
bReplace = true;
break;
case SwComparePosition::OverlapBehind:
pRedl->SetEnd( *pSttRng, pREnd );
bCheck = true;
break;
case SwComparePosition::Outside:
case SwComparePosition::Equal:
rArr.DeleteAndDestroy( rPos-- );
break;
default:
bRet = false;
}
if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
{
// re-insert
rArr.Remove( pRedl );
rArr.Insert( pRedl );
}
}
break;
case RedlineType::Delete:
{
SwDoc& rDoc = pRedl->GetDoc();
const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
bool bDelRedl = false;
switch( eCmp )
{
case SwComparePosition::Inside:
if( bCallDelete )
{
pDelStt = pSttRng;
pDelEnd = pEndRng;
}
break;
case SwComparePosition::OverlapBefore:
if( bCallDelete )
{
pDelStt = pRStt;
pDelEnd = pEndRng;
}
break;
case SwComparePosition::OverlapBehind:
if( bCallDelete )
{
pDelStt = pREnd;
pDelEnd = pSttRng;
}
break;
case SwComparePosition::Outside:
case SwComparePosition::Equal:
{
rArr.Remove( rPos-- );
bDelRedl = true;
if( bCallDelete )
{
pDelStt = pRedl->Start();
pDelEnd = pRedl->End();
}
}
break;
default:
bRet = false;
}
if( pDelStt && pDelEnd )
{
SwPaM aPam( *pDelStt, *pDelEnd );
SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
pRStt = pRedl->Start();
pREnd = pRedl->End();
// keep style of the empty paragraph after deletion of wholly paragraphs
if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->nContent == 0 )
lcl_CopyStyle(*pREnd, *pRStt);
if( bDelRedl )
delete pRedl;
RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
if( pCSttNd && pCEndNd )
rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
else if (pCSttNd && !pCEndNd)
{
aPam.GetBound().nContent.Assign( nullptr, 0 );
aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
rDoc.getIDocumentContentOperations().DelFullPara( aPam );
}
else
{
rDoc.getIDocumentContentOperations().DeleteRange(aPam);
}
rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
else if( bDelRedl )
delete pRedl;
}
break;
case RedlineType::FmtColl:
rArr.DeleteAndDestroy( rPos-- );
break;
case RedlineType::ParagraphFormat:
rArr.DeleteAndDestroy( rPos-- );
break;
default:
bRet = false;
}
return bRet;
}
bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
bool bCallDelete,
const SwPosition* pSttRng = nullptr,
const SwPosition* pEndRng = nullptr )
{
bool bRet = true;
SwRangeRedline* pRedl = rArr[ rPos ];
SwDoc& rDoc = pRedl->GetDoc();
SwPosition *pRStt = nullptr, *pREnd = nullptr;
SwComparePosition eCmp = SwComparePosition::Outside;
if( pSttRng && pEndRng )
{
pRStt = pRedl->Start();
pREnd = pRedl->End();
eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
}
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
switch( pRedl->GetType() )
{
case RedlineType::Insert:
{
const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
bool bDelRedl = false;
switch( eCmp )
{
case SwComparePosition::Inside:
if( bCallDelete )
{
pDelStt = pSttRng;
pDelEnd = pEndRng;
}
break;
case SwComparePosition::OverlapBefore:
if( bCallDelete )
{
pDelStt = pRStt;
pDelEnd = pEndRng;
}
break;
case SwComparePosition::OverlapBehind:
if( bCallDelete )
{
pDelStt = pREnd;
pDelEnd = pSttRng;
}
break;
case SwComparePosition::Outside:
case SwComparePosition::Equal:
{
// delete the range again
rArr.Remove( rPos-- );
bDelRedl = true;
if( bCallDelete )
{
pDelStt = pRedl->Start();
pDelEnd = pRedl->End();
}
}
break;
default:
bRet = false;
}
if( pDelStt && pDelEnd )
{
SwPaM aPam( *pDelStt, *pDelEnd );
SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
if( bDelRedl )
delete pRedl;
RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
if( pCSttNd && pCEndNd )
rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
else if (pCSttNd && !pCEndNd)
{
aPam.GetBound().nContent.Assign( nullptr, 0 );
aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
if (aPam.End()->nNode.GetNode().IsStartNode())
{ // end node will be deleted too! see nNodeDiff+1
--aPam.End()->nNode;
}
assert(!aPam.End()->nNode.GetNode().IsStartNode());
rDoc.getIDocumentContentOperations().DelFullPara( aPam );
}
else
{
rDoc.getIDocumentContentOperations().DeleteRange(aPam);
}
rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
else if( bDelRedl )
delete pRedl;
}
break;
case RedlineType::Delete:
{
SwRangeRedline* pNew = nullptr;
bool bCheck = false, bReplace = false;
SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
pEndRng ? *pEndRng : *pRedl->End());
if( pRedl->GetExtraData() )
pRedl->GetExtraData()->Reject( *pRedl );
switch( eCmp )
{
case SwComparePosition::Inside:
{
if( 1 < pRedl->GetStackCount() )
{
pNew = new SwRangeRedline( *pRedl );
pNew->PopData();
}
if( *pSttRng == *pRStt )
{
pRedl->SetStart( *pEndRng, pRStt );
bReplace = true;
if( pNew )
pNew->SetEnd( *pEndRng );
}
else
{
if( *pEndRng != *pREnd )
{
// split up
SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
pCpy->SetStart( *pEndRng );
rArr.Insert( pCpy ); ++rPos;
if( pNew )
pNew->SetEnd( *pEndRng );
}
pRedl->SetEnd( *pSttRng, pREnd );
bCheck = true;
if( pNew )
pNew->SetStart( *pSttRng );
}
}
break;
case SwComparePosition::OverlapBefore:
if( 1 < pRedl->GetStackCount() )
{
pNew = new SwRangeRedline( *pRedl );
pNew->PopData();
}
pRedl->SetStart( *pEndRng, pRStt );
bReplace = true;
if( pNew )
pNew->SetEnd( *pEndRng );
break;
case SwComparePosition::OverlapBehind:
if( 1 < pRedl->GetStackCount() )
{
pNew = new SwRangeRedline( *pRedl );
pNew->PopData();
}
pRedl->SetEnd( *pSttRng, pREnd );
bCheck = true;
if( pNew )
pNew->SetStart( *pSttRng );
break;
case SwComparePosition::Outside:
case SwComparePosition::Equal:
if( !pRedl->PopData() )
// deleting the RedlineObject is enough
rArr.DeleteAndDestroy( rPos-- );
break;
default:
bRet = false;
}
if( pNew )
{
rArr.Insert( pNew ); ++rPos;
}
if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
{
// re-insert
rArr.Remove( pRedl );
rArr.Insert( pRedl );
}
sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
}
break;
case RedlineType::Format:
case RedlineType::FmtColl:
case RedlineType::ParagraphFormat:
{
// tdf#52391 instead of hidden acception at the requested
// rejection, remove direct text formatting to get the potential
// original state of the text (FIXME if the original text
// has already contained direct text formatting: unfortunately
// ODF 1.2 doesn't support rejection of format-only changes)
if ( pRedl->GetType() == RedlineType::Format )
{
SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
rDoc.ResetAttrs(aPam);
}
else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
{
// handle paragraph formatting changes
// (range is only a full paragraph or a part of it)
const SwPosition* pStt = pRedl->Start();
SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
if( pTNd )
{
// expand range to the whole paragraph
// and reset only the paragraph attributes
SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
{ RES_PARATR_BEGIN, RES_PARATR_END - 1 },
{ RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
};
for (const auto& [nBegin, nEnd] : aResetableSetRange)
{
for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
aResetAttrsArray.insert( i );
}
rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
// remove numbering
if ( pTNd->GetNumRule() )
rDoc.DelNumRules(aPam);
}
}
if( pRedl->GetExtraData() )
pRedl->GetExtraData()->Reject( *pRedl );
rArr.DeleteAndDestroy( rPos-- );
}
break;
default:
bRet = false;
}
return bRet;
}
typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
bool bCallDelete,
const SwPosition* pSttRng,
const SwPosition* pEndRng);
int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
SwRedlineTable& rArr, bool bCallDelete,
const SwPaM& rPam)
{
SwRedlineTable::size_type n = 0;
int nCount = 0;
const SwPosition* pStt = rPam.Start(),
* pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
: rPam.GetPoint();
const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
if( pFnd && // Is new a part of it?
( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
{
// Only revoke the partial selection
if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
nCount++;
++n;
}
// tdf#119824 first we will accept only overlapping paragraph format changes
// in the first loop to avoid potential content changes during Redo
bool bHasParagraphFormatChange = false;
for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
{
for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
{
SwRangeRedline* pTmp = rArr[ o ];
if( pTmp->HasMark() && pTmp->IsVisible() )
{
if( *pTmp->End() <= *pEnd )
{
if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
(*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
{
bHasParagraphFormatChange = true;
nCount++;
}
}
else
{
if( *pTmp->Start() < *pEnd )
{
// Only revoke the partial selection
if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
(*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
{
bHasParagraphFormatChange = true;
nCount++;
}
}
break;
}
}
}
}
return nCount;
}
void lcl_AdjustRedlineRange( SwPaM& rPam )
{
// The Selection is only in the ContentSection. If there are Redlines
// to Non-ContentNodes before or after that, then the Selections
// expand to them.
SwPosition* pStt = rPam.Start(),
* pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
: rPam.GetPoint();
SwDoc& rDoc = rPam.GetDoc();
if( !pStt->nContent.GetIndex() &&
!rDoc.GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
{
const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
if( pRedl )
{
const SwPosition* pRStt = pRedl->Start();
if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
pStt->nNode.GetIndex() - 1 )
*pStt = *pRStt;
}
}
if( pEnd->nNode.GetNode().IsContentNode() &&
!rDoc.GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() )
{
const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
if( pRedl )
{
const SwPosition* pREnd = pRedl->End();
if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
pEnd->nNode.GetIndex() + 1 )
*pEnd = *pREnd;
}
}
}
/// in case some text is deleted, ensure that the not-yet-inserted
/// SwRangeRedline has its positions corrected not to point to deleted node
class TemporaryRedlineUpdater
{
private:
SwRangeRedline & m_rRedline;
std::shared_ptr<SwUnoCursor> m_pCursor;
public:
TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
: m_rRedline(rRedline)
, m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
{
if (m_rRedline.HasMark())
{
m_pCursor->SetMark();
*m_pCursor->GetMark() = *m_rRedline.GetMark();
*m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
}
*m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
}
~TemporaryRedlineUpdater()
{
static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
}
};
}
namespace sw
{
DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
: m_rDoc(i_rSwdoc)
, meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
, mpRedlineTable(new SwRedlineTable)
, mpExtraRedlineTable(new SwExtraRedlineTable)
, mbIsRedlineMove(false)
, mnAutoFormatRedlnCommentNo(0)
{
}
RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
{
return meRedlineFlags;
}
void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
{
if( meRedlineFlags == eMode )
return;
if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
|| !(RedlineFlags::ShowMask & eMode) )
{
bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
m_rDoc.SetInXMLImport( false );
// and then hide/display everything
void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
// uninitialized ptr is possible
RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
pFnc = &SwRangeRedline::Show;
else if (eShowMode == RedlineFlags::ShowInsert)
pFnc = &SwRangeRedline::Hide;
else if (eShowMode == RedlineFlags::ShowDelete)
pFnc = &SwRangeRedline::ShowOriginal;
else
{
pFnc = &SwRangeRedline::Hide;
eMode |= RedlineFlags::ShowInsert;
}
CheckAnchoredFlyConsistency(m_rDoc);
CHECK_REDLINE( *this )
o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
{
// sw_redlinehide: the problem here is that MoveFromSection
// creates the frames wrongly (non-merged), because its own
// SwRangeRedline has wrong positions until after the nodes
// are all moved, so fix things up by force by re-creating
// all merged frames from scratch.
o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
for (SwRootFrame *const pLayout : layouts)
{
if (pLayout->IsHideRedlines())
{
pLayout->SetHideRedlines(false);
hiddenLayouts.insert(pLayout);
}
}
}
for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
for (size_t i = 0; i < mpRedlineTable->size(); ++i)
{
SwRangeRedline *const pRedline((*mpRedlineTable)[i]);
(pRedline->*pFnc)(nLoop, i, false);
while (mpRedlineTable->size() <= i
|| (*mpRedlineTable)[i] != pRedline)
{ // ensure current position
--i; // a previous redline may have been deleted
}
}
//SwRangeRedline::MoveFromSection routinely changes
//the keys that mpRedlineTable is sorted by
mpRedlineTable->Resort();
CheckAnchoredFlyConsistency(m_rDoc);
CHECK_REDLINE( *this )
for (SwRootFrame *const pLayout : hiddenLayouts)
{
pLayout->SetHideRedlines(true);
}
m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
}
meRedlineFlags = eMode;
m_rDoc.getIDocumentState().SetModified();
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::IsRedlineOn() const
{
return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
}
bool DocumentRedlineManager::IsIgnoreRedline() const
{
return bool(RedlineFlags::Ignore & meRedlineFlags);
}
void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
{
meRedlineFlags = eMode;
}
const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
{
return *mpRedlineTable;
}
SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
{
return *mpRedlineTable;
}
const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
{
return *mpExtraRedlineTable;
}
SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
{
return *mpExtraRedlineTable;
}
bool DocumentRedlineManager::HasExtraRedlineTable() const
{
return mpExtraRedlineTable != nullptr;
}
bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
{
SwPosition aPos(rNode);
SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
SwPosition(rEndOfRedlines));
return aPam.ContainsPosition(aPos);
}
bool DocumentRedlineManager::IsRedlineMove() const
{
return mbIsRedlineMove;
}
void DocumentRedlineManager::SetRedlineMove(bool bFlag)
{
mbIsRedlineMove = bFlag;
}
/*
Text means Text not "polluted" by Redlines.
Behaviour of Insert-Redline:
- in the Text - insert Redline Object
- in InsertRedline (own) - ignore, existing is extended
- in InsertRedline (others) - split up InsertRedline and
insert Redline Object
- in DeleteRedline - split up DeleteRedline or
move at the end/beginning
Behaviour of Delete-Redline:
- in the Text - insert Redline Object
- in DeleteRedline (own/others) - ignore
- in InsertRedline (own) - ignore, but delete character
- in InsertRedline (others) - split up InsertRedline and
insert Redline Object
- Text and own Insert overlap - delete Text in the own Insert,
extend in the other Text
(up to the Insert!)
- Text and other Insert overlap - insert Redline Object, the
other Insert is overlapped by
the Delete
*/
IDocumentRedlineAccess::AppendResult
DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
{
bool bMerged = false;
CHECK_REDLINE( *this )
if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
{
pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
if( m_rDoc.IsAutoFormatRedline() )
{
pNewRedl->SetAutoFormat();
if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() )
{
pNewRedl->SetComment( *mpAutoFormatRedlnComment );
pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
}
}
SwPosition* pStt = pNewRedl->Start(),
* pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark()
: pNewRedl->GetPoint();
{
SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
if( pTextNode == nullptr )
{
if( pStt->nContent > 0 )
{
OSL_ENSURE( false, "Redline start: non-text-node with content" );
pStt->nContent = 0;
}
}
else
{
if( pStt->nContent > pTextNode->Len() )
{
OSL_ENSURE( false, "Redline start: index after text" );
pStt->nContent = pTextNode->Len();
}
}
pTextNode = pEnd->nNode.GetNode().GetTextNode();
if( pTextNode == nullptr )
{
if( pEnd->nContent > 0 )
{
OSL_ENSURE( false, "Redline end: non-text-node with content" );
pEnd->nContent = 0;
}
}
else
{
if( pEnd->nContent > pTextNode->Len() )
{
OSL_ENSURE( false, "Redline end: index after text" );
pEnd->nContent = pTextNode->Len();
}
}
}
if( ( *pStt == *pEnd ) &&
( pNewRedl->GetContentIdx() == nullptr ) )
{ // Do not insert empty redlines
delete pNewRedl;
return AppendResult::IGNORED;
}
bool bCompress = false;
SwRedlineTable::size_type n = 0;
// look up the first Redline for the starting position
if( !GetRedline( *pStt, &n ) && n )
--n;
bool bDec = false;
for( ; pNewRedl && n < mpRedlineTable->size(); bDec ? n : ++n )
{
bDec = false;
SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
SwPosition* pRStt = pRedl->Start(),
* pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
: pRedl->GetPoint();
// #i8518# remove empty redlines while we're at it
if( ( *pRStt == *pREnd ) &&
( pRedl->GetContentIdx() == nullptr ) )
{
mpRedlineTable->DeleteAndDestroy(n);
continue;
}
SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
switch( pNewRedl->GetType() )
{
case RedlineType::Insert:
switch( pRedl->GetType() )
{
case RedlineType::Insert:
if( pRedl->IsOwnRedline( *pNewRedl ) )
{
bool bDelete = false;
// Merge if applicable?
if( (( SwComparePosition::Behind == eCmpPos &&
IsPrevPos( *pREnd, *pStt ) ) ||
( SwComparePosition::CollideStart == eCmpPos ) ||
( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
pRedl->CanCombine( *pNewRedl ) &&
( n+1 >= mpRedlineTable->size() ||
( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd &&
*(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) )
{
pRedl->SetEnd( *pEnd, pREnd );
if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
}
bMerged = true;
bDelete = true;
}
else if( (( SwComparePosition::Before == eCmpPos &&
IsPrevPos( *pEnd, *pRStt ) ) ||
( SwComparePosition::CollideEnd == eCmpPos ) ||
( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
pRedl->CanCombine( *pNewRedl ) &&
( !n ||
*(*mpRedlineTable)[ n-1 ]->End() != *pRStt ))
{
pRedl->SetStart( *pStt, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
bMerged = true;
bDelete = true;
}
else if ( SwComparePosition::Outside == eCmpPos )
{
// own insert-over-insert redlines:
// just scrap the inside ones
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else if( SwComparePosition::OverlapBehind == eCmpPos )
{
*pStt = *pREnd;
if( ( *pStt == *pEnd ) &&
( pNewRedl->GetContentIdx() == nullptr ) )
bDelete = true;
}
else if( SwComparePosition::OverlapBefore == eCmpPos )
{
*pEnd = *pRStt;
if( ( *pStt == *pEnd ) &&
( pNewRedl->GetContentIdx() == nullptr ) )
bDelete = true;
}
else if( SwComparePosition::Inside == eCmpPos )
{
bDelete = true;
bMerged = true;
}
else if( SwComparePosition::Equal == eCmpPos )
bDelete = true;
if( bDelete )
{
delete pNewRedl;
pNewRedl = nullptr;
bCompress = true;
}
}
else if( SwComparePosition::Inside == eCmpPos )
{
// split up
if( *pEnd != *pREnd )
{
SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
pCpy->SetStart( *pEnd );
mpRedlineTable->Insert( pCpy );
}
pRedl->SetEnd( *pStt, pREnd );
if( ( *pStt == *pRStt ) &&
( pRedl->GetContentIdx() == nullptr ) )
{
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
}
}
else if ( SwComparePosition::Outside == eCmpPos )
{
// handle overlapping redlines in broken documents
// split up the new redline, since it covers the
// existing redline. Insert the first part, and
// progress with the remainder as usual
SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
pSplit->SetEnd( *pRStt );
pNewRedl->SetStart( *pREnd );
mpRedlineTable->Insert( pSplit );
if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
{
delete pNewRedl;
pNewRedl = nullptr;
bCompress = true;
}
}
else if ( SwComparePosition::OverlapBehind == eCmpPos )
{
// handle overlapping redlines in broken documents
pNewRedl->SetStart( *pREnd );
}
else if ( SwComparePosition::OverlapBefore == eCmpPos )
{
// handle overlapping redlines in broken documents
*pEnd = *pRStt;
if( ( *pStt == *pEnd ) &&
( pNewRedl->GetContentIdx() == nullptr ) )
{
delete pNewRedl;
pNewRedl = nullptr;
bCompress = true;
}
}
break;
case RedlineType::Delete:
if( SwComparePosition::Inside == eCmpPos )
{
// split up
if( *pEnd != *pREnd )
{
SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
pCpy->SetStart( *pEnd );
mpRedlineTable->Insert( pCpy );
}
pRedl->SetEnd( *pStt, pREnd );
if( ( *pStt == *pRStt ) &&
( pRedl->GetContentIdx() == nullptr ) )
{
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
}
}
else if ( SwComparePosition::Outside == eCmpPos )
{
// handle overlapping redlines in broken documents
// split up the new redline, since it covers the
// existing redline. Insert the first part, and
// progress with the remainder as usual
SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
pSplit->SetEnd( *pRStt );
pNewRedl->SetStart( *pREnd );
mpRedlineTable->Insert( pSplit );
if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
{
delete pNewRedl;
pNewRedl = nullptr;
bCompress = true;
}
}
else if ( SwComparePosition::Equal == eCmpPos )
{
// handle identical redlines in broken documents
// delete old (delete) redline
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else if ( SwComparePosition::OverlapBehind == eCmpPos )
{ // Another workaround for broken redlines
pNewRedl->SetStart( *pREnd );
}
break;
case RedlineType::Format:
switch( eCmpPos )
{
case SwComparePosition::OverlapBefore:
pRedl->SetStart( *pEnd, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
bDec = true;
break;
case SwComparePosition::OverlapBehind:
pRedl->SetEnd( *pStt, pREnd );
if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
{
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
break;
case SwComparePosition::Equal:
case SwComparePosition::Outside:
// Overlaps the current one completely or has the
// same dimension, delete the old one
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
break;
case SwComparePosition::Inside:
// Overlaps the current one completely,
// split or shorten the new one
if( *pEnd != *pREnd )
{
if( *pEnd != *pRStt )
{
SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
pNew->SetStart( *pEnd );
pRedl->SetEnd( *pStt, pREnd );
if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
mpRedlineTable->DeleteAndDestroy( n );
AppendRedline( pNew, bCallDelete );
n = 0; // re-initialize
bDec = true;
}
}
else
pRedl->SetEnd( *pStt, pREnd );
break;
default:
break;
}
break;
default:
break;
}
break;
case RedlineType::Delete:
switch( pRedl->GetType() )
{
case RedlineType::Delete:
switch( eCmpPos )
{
case SwComparePosition::Outside:
{
// Overlaps the current one completely,
// split the new one
if (*pEnd == *pREnd)
{
pNewRedl->SetEnd(*pRStt, pEnd);
}
else if (*pStt == *pRStt)
{
pNewRedl->SetStart(*pREnd, pStt);
}
else
{
SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
pNew->SetStart( *pREnd );
pNewRedl->SetEnd( *pRStt, pEnd );
AppendRedline( pNew, bCallDelete );
n = 0; // re-initialize
bDec = true;
}
}
break;
case SwComparePosition::Inside:
case SwComparePosition::Equal:
delete pNewRedl;
pNewRedl = nullptr;
bCompress = true;
break;
case SwComparePosition::OverlapBefore:
case SwComparePosition::OverlapBehind:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ))
{
// If that's the case we can merge it, meaning
// the new one covers this well
if( SwComparePosition::OverlapBehind == eCmpPos )
pNewRedl->SetStart( *pRStt, pStt );
else
pNewRedl->SetEnd( *pREnd, pEnd );
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else if( SwComparePosition::OverlapBehind == eCmpPos )
pNewRedl->SetStart( *pREnd, pStt );
else
pNewRedl->SetEnd( *pRStt, pEnd );
break;
case SwComparePosition::CollideStart:
case SwComparePosition::CollideEnd:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ) )
{
if( IsHideChanges( meRedlineFlags ))
{
// Before we can merge, we make it visible!
// We insert temporarily so that pNew is
// also dealt with when moving the indices.
mpRedlineTable->Insert(pNewRedl);
pRedl->Show(0, mpRedlineTable->GetPos(pRedl));
mpRedlineTable->Remove( pNewRedl );
pRStt = pRedl->Start();
pREnd = pRedl->End();
}
// If that's the case we can merge it, meaning
// the new one covers this well
if( SwComparePosition::CollideStart == eCmpPos )
pNewRedl->SetStart( *pRStt, pStt );
else
pNewRedl->SetEnd( *pREnd, pEnd );
// delete current (below), and restart process with
// previous
SwRedlineTable::size_type nToBeDeleted = n;
bDec = true;
if( *(pNewRedl->Start()) <= *pREnd )
{
// Whoooah, we just extended the new 'redline'
// beyond previous redlines, so better start
// again. Of course this is not supposed to
// happen, and in an ideal world it doesn't,
// but unfortunately this code is buggy and
// totally rotten so it does happen and we
// better fix it.
n = 0;
bDec = true;
}
mpRedlineTable->DeleteAndDestroy( nToBeDeleted );
}
break;
default:
break;
}
break;
case RedlineType::Insert:
{
// b62341295: Do not throw away redlines
// even if they are not allowed to be combined
RedlineFlags eOld = meRedlineFlags;
if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
pRedl->IsOwnRedline( *pNewRedl ) )
{
// Set to NONE, so that the Delete::Redo merges the Redline data correctly!
// The ShowMode needs to be retained!
meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
switch( eCmpPos )
{
case SwComparePosition::Equal:
bCompress = true;
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
[[fallthrough]];
case SwComparePosition::Inside:
if( bCallDelete )
{
// DeleteAndJoin does not yield the
// desired result if there is no paragraph to
// join with, i.e. at the end of the document.
// For this case, we completely delete the
// paragraphs (if, of course, we also start on
// a paragraph boundary).
if( (pStt->nContent == 0) &&
pEnd->nNode.GetNode().IsEndNode() )
{
pEnd->nNode--;
pEnd->nContent.Assign(
pEnd->nNode.GetNode().GetTextNode(), 0);
m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
}
else
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
bCompress = true;
}
if( !bCallDelete && !bDec && *pEnd == *pREnd )
{
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
bCompress = true;
}
else if ( bCallDelete || !bDec )
{
// delete new redline, except in some cases of fallthrough from previous
// case ::Equal (eg. same portion w:del in w:ins in OOXML import)
delete pNewRedl;
pNewRedl = nullptr;
}
break;
case SwComparePosition::Outside:
{
mpRedlineTable->Remove( n );
bDec = true;
if( bCallDelete )
{
TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
n = 0; // re-initialize
}
delete pRedl;
}
break;
case SwComparePosition::OverlapBefore:
{
SwPaM aPam( *pRStt, *pEnd );
if( *pEnd == *pREnd )
mpRedlineTable->DeleteAndDestroy( n );
else
{
pRedl->SetStart( *pEnd, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
}
if( bCallDelete )
{
TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
n = 0; // re-initialize
}
bDec = true;
}
break;
case SwComparePosition::OverlapBehind:
{
SwPaM aPam( *pStt, *pREnd );
if( *pStt == *pRStt )
{
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
else
pRedl->SetEnd( *pStt, pREnd );
if( bCallDelete )
{
TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
n = 0; // re-initialize
bDec = true;
}
}
break;
default:
break;
}
meRedlineFlags = eOld;
}
else
{
// it may be necessary to split the existing redline in
// two. In this case, pRedl will be changed to cover
// only part of its former range, and pNew will cover
// the remainder.
SwRangeRedline* pNew = nullptr;
switch( eCmpPos )
{
case SwComparePosition::Equal:
{
pRedl->PushData( *pNewRedl );
delete pNewRedl;
pNewRedl = nullptr;
if( IsHideChanges( meRedlineFlags ))
{
pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
}
bCompress = true;
}
break;
case SwComparePosition::Inside:
{
if( *pRStt == *pStt )
{
// #i97421#
// redline w/out extent loops
if (*pStt != *pEnd)
{
pNewRedl->PushData( *pRedl, false );
pRedl->SetStart( *pEnd, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
bDec = true;
}
}
else
{
pNewRedl->PushData( *pRedl, false );
if( *pREnd != *pEnd )
{
pNew = new SwRangeRedline( *pRedl );
pNew->SetStart( *pEnd );
}
pRedl->SetEnd( *pStt, pREnd );
if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
}
}
}
break;
case SwComparePosition::Outside:
{
pRedl->PushData( *pNewRedl );
if( *pEnd == *pREnd )
{
pNewRedl->SetEnd( *pRStt, pEnd );
}
else if (*pStt == *pRStt)
{
pNewRedl->SetStart(*pREnd, pStt);
}
else
{
pNew = new SwRangeRedline( *pNewRedl );
pNew->SetEnd( *pRStt );
pNewRedl->SetStart( *pREnd, pStt );
}
bCompress = true;
}
break;
case SwComparePosition::OverlapBefore:
{
if( *pEnd == *pREnd )
{
pRedl->PushData( *pNewRedl );
pNewRedl->SetEnd( *pRStt, pEnd );
if( IsHideChanges( meRedlineFlags ))
{
mpRedlineTable->Insert(pNewRedl);
pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
mpRedlineTable->Remove( pNewRedl );
}
}
else
{
pNew = new SwRangeRedline( *pRedl );
pNew->PushData( *pNewRedl );
pNew->SetEnd( *pEnd );
pNewRedl->SetEnd( *pRStt, pEnd );
pRedl->SetStart( *pNew->End(), pRStt ) ;
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
bDec = true;
}
}
break;
case SwComparePosition::OverlapBehind:
{
if( *pStt == *pRStt )
{
pRedl->PushData( *pNewRedl );
pNewRedl->SetStart( *pREnd, pStt );
if( IsHideChanges( meRedlineFlags ))
{
mpRedlineTable->Insert( pNewRedl );
pRedl->Hide(0, mpRedlineTable->GetPos(pRedl));
mpRedlineTable->Remove( pNewRedl );
}
}
else
{
pNew = new SwRangeRedline( *pRedl );
pNew->PushData( *pNewRedl );
pNew->SetStart( *pStt );
pNewRedl->SetStart( *pREnd, pStt );
pRedl->SetEnd( *pNew->Start(), pREnd );
if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
}
}
}
break;
default:
break;
}
// insert the pNew part (if it exists)
if( pNew )
{
mpRedlineTable->Insert( pNew );
// pNew must be deleted if Insert() wasn't
// successful. But that can't happen, since pNew is
// part of the original pRedl redline.
// OSL_ENSURE( bRet, "Can't insert existing redline?" );
// restart (now with pRedl being split up)
n = 0;
bDec = true;
}
}
}
break;
case RedlineType::Format:
switch( eCmpPos )
{
case SwComparePosition::OverlapBefore:
pRedl->SetStart( *pEnd, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
bDec = true;
break;
case SwComparePosition::OverlapBehind:
pRedl->SetEnd( *pStt, pREnd );
break;
case SwComparePosition::Equal:
case SwComparePosition::Outside:
// Overlaps the current one completely or has the
// same dimension, delete the old one
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
break;
case SwComparePosition::Inside:
// Overlaps the current one completely,
// split or shorten the new one
if( *pEnd != *pREnd )
{
if( *pEnd != *pRStt )
{
SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
pNew->SetStart( *pEnd );
pRedl->SetEnd( *pStt, pREnd );
if( ( *pStt == *pRStt ) &&
( pRedl->GetContentIdx() == nullptr ) )
mpRedlineTable->DeleteAndDestroy( n );
AppendRedline( pNew, bCallDelete );
n = 0; // re-initialize
bDec = true;
}
}
else
pRedl->SetEnd( *pStt, pREnd );
break;
default:
break;
}
break;
default:
break;
}
break;
case RedlineType::Format:
switch( pRedl->GetType() )
{
case RedlineType::Insert:
case RedlineType::Delete:
switch( eCmpPos )
{
case SwComparePosition::OverlapBefore:
pNewRedl->SetEnd( *pRStt, pEnd );
break;
case SwComparePosition::OverlapBehind:
pNewRedl->SetStart( *pREnd, pStt );
break;
case SwComparePosition::Equal:
case SwComparePosition::Inside:
delete pNewRedl;
pNewRedl = nullptr;
break;
case SwComparePosition::Outside:
// Overlaps the current one completely,
// split or shorten the new one
if (*pEnd == *pREnd)
{
pNewRedl->SetEnd(*pRStt, pEnd);
}
else if (*pStt == *pRStt)
{
pNewRedl->SetStart(*pREnd, pStt);
}
else
{
SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
pNew->SetStart( *pREnd );
pNewRedl->SetEnd( *pRStt, pEnd );
AppendRedline( pNew, bCallDelete );
n = 0; // re-initialize
bDec = true;
}
break;
default:
break;
}
break;
case RedlineType::Format:
switch( eCmpPos )
{
case SwComparePosition::Outside:
case SwComparePosition::Equal:
{
// Overlaps the current one completely or has the
// same dimension, delete the old one
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
break;
case SwComparePosition::Inside:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ))
{
// own one can be ignored completely
delete pNewRedl;
pNewRedl = nullptr;
}
else if( *pREnd == *pEnd )
// or else only shorten the current one
pRedl->SetEnd( *pStt, pREnd );
else if( *pRStt == *pStt )
{
// or else only shorten the current one
pRedl->SetStart( *pEnd, pRStt );
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl, n );
bDec = true;
}
else
{
// If it lies completely within the current one
// we need to split it
SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
pNew->SetStart( *pEnd );
pRedl->SetEnd( *pStt, pREnd );
AppendRedline( pNew, bCallDelete );
n = 0; // re-initialize
bDec = true;
}
break;
case SwComparePosition::OverlapBefore:
case SwComparePosition::OverlapBehind:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ))
{
// If that's the case we can merge it, meaning
// the new one covers this well
if( SwComparePosition::OverlapBehind == eCmpPos )
pNewRedl->SetStart( *pRStt, pStt );
else
pNewRedl->SetEnd( *pREnd, pEnd );
mpRedlineTable->DeleteAndDestroy( n );
bDec = false;
}
else if( SwComparePosition::OverlapBehind == eCmpPos )
pNewRedl->SetStart( *pREnd, pStt );
else
pNewRedl->SetEnd( *pRStt, pEnd );
break;
case SwComparePosition::CollideEnd:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ) && n &&
*(*mpRedlineTable)[ n-1 ]->End() < *pStt )
{
// If that's the case we can merge it, meaning
// the new one covers this well
pNewRedl->SetEnd( *pREnd, pEnd );
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
break;
case SwComparePosition::CollideStart:
if( pRedl->IsOwnRedline( *pNewRedl ) &&
pRedl->CanCombine( *pNewRedl ) &&
n+1 < mpRedlineTable->size() &&
*(*mpRedlineTable)[ n+1 ]->Start() < *pEnd )
{
// If that's the case we can merge it, meaning
// the new one covers this well
pNewRedl->SetStart( *pRStt, pStt );
mpRedlineTable->DeleteAndDestroy( n );
bDec = true;
}
break;
default:
break;
}
break;
default:
break;
}
break;
case RedlineType::FmtColl:
// How should we behave here?
// insert as is
break;
default:
break;
}
}
if( pNewRedl )
{
if( ( *pStt == *pEnd ) &&
( pNewRedl->GetContentIdx() == nullptr ) )
{ // Do not insert empty redlines
delete pNewRedl;
pNewRedl = nullptr;
}
else
{
if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
{
if ( pStt->nContent != 0 )
{
// tdf#119571 update the style of the joined paragraph
// after a partially deleted paragraph to show its correct style
// in "Show changes" mode, too. All removed paragraphs
// get the style of the first (partially deleted) paragraph
// to avoid text insertion with bad style in the deleted
// area later.
SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode();
SwTextNode* pTextNode;
SwNodeIndex aIdx( pEnd->nNode.GetNode() );
bool bFirst = true;
while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
{
pTextNode = pTextNd->GetTextNode();
if (pTextNode && pDelNode != pTextNode )
{
SwPosition aPos(aIdx);
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
bCompress = true;
// split redline to store ExtraData per paragraphs
SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
pPar->SetStart( aPos );
pNewRedl->SetEnd( aPos );
// get extradata for reset formatting of the modified paragraph
SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
if (pExtraData)
{
std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
if (!bFirst)
pExtraData->SetFormatAll(false);
xRedlineExtraData.reset(pExtraData);
pPar->SetExtraData( xRedlineExtraData.get() );
}
// skip empty redlines without ExtraData
// FIXME: maybe checking pExtraData is redundant here
if ( pExtraData || *pPar->Start() != *pPar->End() )
mpRedlineTable->Insert( pPar );
else
delete pPar;
}
// modify paragraph formatting
lcl_CopyStyle(*pStt, aPos);
}
pTextNd = SwNodes::GoPrevious( &aIdx );
if (bFirst)
bFirst = false;
}
}
}
bool const ret = mpRedlineTable->Insert( pNewRedl );
assert(ret || !pNewRedl);
if (ret && !pNewRedl)
{
bMerged = true; // treat InsertWithValidRanges as "merge"
}
}
}
if( bCompress )
CompressRedlines();
}
else
{
if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
{
RedlineFlags eOld = meRedlineFlags;
// Set to NONE, so that the Delete::Redo merges the Redline data correctly!
// The ShowMode needs to be retained!
meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
meRedlineFlags = eOld;
}
delete pNewRedl;
pNewRedl = nullptr;
}
CHECK_REDLINE( *this )
return (nullptr != pNewRedl)
? AppendResult::APPENDED
: (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
}
bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
{
// #TODO - equivalent for 'SwTableRowRedline'
/*
CHECK_REDLINE( this )
*/
if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
{
// #TODO - equivalent for 'SwTableRowRedline'
/*
pNewRedl->InvalidateRange();
*/
// Make equivalent of 'AppendRedline' checks inside here too
mpExtraRedlineTable->Insert( pNewRedl );
}
else
{
// TO DO - equivalent for 'SwTableRowRedline'
/*
if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
{
RedlineFlags eOld = meRedlineFlags;
// Set to NONE, so that the Delete::Redo merges the Redline data correctly!
// The ShowMode needs to be retained!
meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
DeleteAndJoin( *pNewRedl );
meRedlineFlags = eOld;
}
delete pNewRedl, pNewRedl = 0;
*/
}
// #TODO - equivalent for 'SwTableRowRedline'
/*
CHECK_REDLINE( this )
*/
return nullptr != pNewRedl;
}
bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
{
// #TODO - equivalent for 'SwTableCellRedline'
/*
CHECK_REDLINE( this )
*/
if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
{
// #TODO - equivalent for 'SwTableCellRedline'
/*
pNewRedl->InvalidateRange();
*/
// Make equivalent of 'AppendRedline' checks inside here too
mpExtraRedlineTable->Insert( pNewRedl );
}
else
{
// TO DO - equivalent for 'SwTableCellRedline'
/*
if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
{
RedlineFlags eOld = meRedlineFlags;
// Set to NONE, so that the Delete::Redo merges the Redline data correctly!
// The ShowMode needs to be retained!
meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
DeleteAndJoin( *pNewRedl );
meRedlineFlags = eOld;
}
delete pNewRedl, pNewRedl = 0;
*/
}
// #TODO - equivalent for 'SwTableCellRedline'
/*
CHECK_REDLINE( this )
*/
return nullptr != pNewRedl;
}
void DocumentRedlineManager::CompressRedlines()
{
CHECK_REDLINE( *this )
void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
pFnc = &SwRangeRedline::Show;
else if (eShow == RedlineFlags::ShowInsert)
pFnc = &SwRangeRedline::Hide;
// Try to merge identical ones
for( SwRedlineTable::size_type n = 1; n < mpRedlineTable->size(); ++n )
{
SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ],
* pCur = (*mpRedlineTable)[ n ];
const SwPosition* pPrevStt = pPrev->Start(),
* pPrevEnd = pPrevStt == pPrev->GetPoint()
? pPrev->GetMark() : pPrev->GetPoint();
const SwPosition* pCurStt = pCur->Start(),
* pCurEnd = pCurStt == pCur->GetPoint()
? pCur->GetMark() : pCur->GetPoint();
if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
pPrevStt->nNode.GetNode().StartOfSectionNode() ==
pCurEnd->nNode.GetNode().StartOfSectionNode() &&
!pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
{
// we then can merge them
SwRedlineTable::size_type nPrevIndex = n-1;
pPrev->Show(0, nPrevIndex);
pCur->Show(0, n);
pPrev->SetEnd( *pCur->End() );
mpRedlineTable->DeleteAndDestroy( n );
--n;
if( pFnc )
(pPrev->*pFnc)(0, nPrevIndex, false);
}
}
CHECK_REDLINE( *this )
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
{
bool bChg = false;
SwRedlineTable::size_type n = 0;
const SwPosition* pStt = rRange.Start();
const SwPosition* pEnd = rRange.End();
GetRedline( *pStt, &n );
for ( ; n < mpRedlineTable->size(); ++n)
{
SwRangeRedline * pRedline = (*mpRedlineTable)[ n ];
SwPosition *const pRedlineStart = pRedline->Start();
SwPosition *const pRedlineEnd = pRedline->End();
if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
*pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
{
bChg = true;
int nn = 0;
if (*pStt == *pRedlineStart)
nn += 1;
if (*pEnd == *pRedlineEnd)
nn += 2;
SwRangeRedline* pNew = nullptr;
switch( nn )
{
case 0:
pNew = new SwRangeRedline( *pRedline );
pRedline->SetEnd( *pStt, pRedlineEnd );
pNew->SetStart( *pEnd );
break;
case 1:
*pRedlineStart = *pEnd;
break;
case 2:
*pRedlineEnd = *pStt;
break;
case 3:
pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
mpRedlineTable->DeleteAndDestroy( n-- );
pRedline = nullptr;
break;
}
if (pRedline && !pRedline->HasValidRange())
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedline, n );
}
if( pNew )
mpRedlineTable->Insert( pNew, n );
}
else if (*pEnd < *pRedlineStart)
break;
}
return bChg;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
RedlineType nDelType )
{
if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
return false;
bool bChg = false;
if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
if( pUndo->GetRedlSaveCount() )
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
}
}
const SwPosition* pStt = rRange.Start(),
* pEnd = pStt == rRange.GetPoint() ? rRange.GetMark()
: rRange.GetPoint();
SwRedlineTable::size_type n = 0;
GetRedline( *pStt, &n );
for( ; n < mpRedlineTable->size() ; ++n )
{
SwRangeRedline* pRedl = (*mpRedlineTable)[ n ];
if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
continue;
SwPosition* pRStt = pRedl->Start(),
* pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark()
: pRedl->GetPoint();
switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
{
case SwComparePosition::Equal:
case SwComparePosition::Outside:
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
mpRedlineTable->DeleteAndDestroy( n-- );
bChg = true;
break;
case SwComparePosition::OverlapBefore:
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
pRedl->SetStart( *pEnd, pRStt );
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
--n;
break;
case SwComparePosition::OverlapBehind:
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
pRedl->SetEnd( *pStt, pREnd );
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
--n;
}
break;
case SwComparePosition::Inside:
{
// this one needs to be split
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
if( *pRStt == *pStt )
{
pRedl->SetStart( *pEnd, pRStt );
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
--n;
}
else
{
SwRangeRedline* pCpy;
if( *pREnd != *pEnd )
{
pCpy = new SwRangeRedline( *pRedl );
pCpy->SetStart( *pEnd );
pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
}
else
pCpy = nullptr;
pRedl->SetEnd( *pStt, pREnd );
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
if( !pRedl->HasValidRange() )
{
// re-insert
mpRedlineTable->Remove( n );
mpRedlineTable->Insert( pRedl );
--n;
}
if( pCpy )
mpRedlineTable->Insert( pCpy );
}
}
break;
case SwComparePosition::CollideEnd:
// remove (not hidden) empty redlines created for fixing tdf#119571
// (Note: hidden redlines are all empty, i.e. start and end are equal.)
if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
{
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
mpRedlineTable->DeleteAndDestroy( n-- );
bChg = true;
break;
}
[[fallthrough]];
case SwComparePosition::Before:
n = mpRedlineTable->size();
break;
default:
break;
}
}
if( bChg )
m_rDoc.getIDocumentState().SetModified();
return bChg;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
RedlineType nDelType )
{
SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
return DeleteRedline(aTemp, bSaveInUndo, nDelType);
}
SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
{
const sal_uLong nNdIdx = rNd.GetIndex();
for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
{
const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
nMk = pTmp->GetMark()->nNode.GetIndex();
if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
nMk <= nNdIdx && nNdIdx <= nPt )
return n;
if( nMk > nNdIdx )
break;
}
return SwRedlineTable::npos;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const // xxx
{
SwPosition currentStart(*rPam.Start());
SwPosition currentEnd(*rPam.End());
SwNodeIndex pEndNodeIndex(currentEnd.nNode.GetNode());
for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->nNode.GetNode(), nType );
n < mpRedlineTable->size(); ++n )
{
const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
if ( pTmp->Start()->nNode > pEndNodeIndex )
break;
if( RedlineType::Any != nType && nType != pTmp->GetType() )
continue;
// redline over the range
if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
// starting or ending within the range
( !bStartOrEndInRange ||
( currentStart <= *pTmp->Start() || *pTmp->End() <= currentEnd ) ) )
{
return true;
}
}
return false;
}
const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
SwRedlineTable::size_type* pFndPos ) const
{
SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0;
if( nO > 0 )
{
nO--;
while( nU <= nO )
{
nM = nU + ( nO - nU ) / 2;
const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ];
const SwPosition* pStt = pRedl->Start();
const SwPosition* pEnd = pStt == pRedl->GetPoint()
? pRedl->GetMark()
: pRedl->GetPoint();
if( pEnd == pStt
? *pStt == rPos
: ( *pStt <= rPos && rPos < *pEnd ) )
{
while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() &&
rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() )
{
--nM;
pRedl = (*mpRedlineTable)[ nM ];
}
// if there are format and insert changes in the same position
// show insert change first.
// since the redlines are sorted by position, only check the redline
// before and after the current redline
if( RedlineType::Format == pRedl->GetType() )
{
if( nM && rPos >= *(*mpRedlineTable)[ nM - 1 ]->Start() &&
rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() &&
( RedlineType::Insert == (*mpRedlineTable)[ nM - 1 ]->GetType() ) )
{
--nM;
pRedl = (*mpRedlineTable)[ nM ];
}
else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() &&
rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() &&
( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) )
{
++nM;
pRedl = (*mpRedlineTable)[ nM ];
}
}
if( pFndPos )
*pFndPos = nM;
return pRedl;
}
else if( *pEnd <= rPos )
nU = nM + 1;
else if( nM == 0 )
{
if( pFndPos )
*pFndPos = nU;
return nullptr;
}
else
nO = nM - 1;
}
}
if( pFndPos )
*pFndPos = nU;
return nullptr;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
{
bool bRet = false;
// Switch to visible in any case
if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
(RedlineFlags::ShowMask & meRedlineFlags) )
SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
if( pTmp->HasMark() && pTmp->IsVisible() )
{
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
}
int nLoopCnt = 2;
sal_uInt16 nSeqNo = pTmp->GetSeqNo();
do {
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(
std::make_unique<SwUndoAcceptRedline>(*pTmp) );
}
bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete );
if( nSeqNo )
{
if( SwRedlineTable::npos == nPos )
nPos = 0;
SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
: mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
SwRedlineTable::npos != ( nFndPos =
mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
{
nPos = nFndPos;
pTmp = (*mpRedlineTable)[ nPos ];
}
else
nLoopCnt = 0;
}
else
nLoopCnt = 0;
} while( nLoopCnt );
if( bRet )
{
CompressRedlines();
m_rDoc.getIDocumentState().SetModified();
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
}
}
return bRet;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
{
// Switch to visible in any case
if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
(RedlineFlags::ShowMask & meRedlineFlags) )
SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
// The Selection is only in the ContentSection. If there are Redlines
// to Non-ContentNodes before or after that, then the Selections
// expand to them.
SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
lcl_AdjustRedlineRange( aPam );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam ));
}
int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable,
bCallDelete, aPam );
if( nRet > 0 )
{
CompressRedlines();
m_rDoc.getIDocumentState().SetModified();
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
OUString aTmpStr;
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, OUString::number(nRet));
aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
}
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, aTmpStr);
m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
}
return nRet != 0;
// #TODO - add 'SwExtraRedlineTable' also ?
}
void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
{
const SwPosition* pStt = rPam.Start(),
* pEnd = pStt == rPam.GetPoint() ? rPam.GetMark()
: rPam.GetPoint();
const sal_uLong nSttIdx = pStt->nNode.GetIndex();
const sal_uLong nEndIdx = pEnd->nNode.GetIndex();
for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n )
{
const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
nMk = pTmp->GetMark()->nNode.GetIndex();
if( nPt < nMk ) { tools::Long nTmp = nMk; nMk = nPt; nPt = nTmp; }
if( RedlineType::ParagraphFormat == pTmp->GetType() &&
( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
AcceptRedline( n, false );
if( nMk > nEndIdx )
break;
}
}
bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
{
bool bRet = false;
// Switch to visible in any case
if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
(RedlineFlags::ShowMask & meRedlineFlags) )
SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
SwRangeRedline* pTmp = (*mpRedlineTable)[ nPos ];
pTmp->Show(0, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
pTmp->Show(1, mpRedlineTable->GetPos(pTmp), /*bForced=*/true);
if( pTmp->HasMark() && pTmp->IsVisible() )
{
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
}
int nLoopCnt = 2;
sal_uInt16 nSeqNo = pTmp->GetSeqNo();
do {
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().AppendUndo(
std::make_unique<SwUndoRejectRedline>( *pTmp ) );
}
bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete );
if( nSeqNo )
{
if( SwRedlineTable::npos == nPos )
nPos = 0;
SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos )
: mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos );
if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
SwRedlineTable::npos != ( nFndPos =
mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) )
{
nPos = nFndPos;
pTmp = (*mpRedlineTable)[ nPos ];
}
else
nLoopCnt = 0;
}
else
nLoopCnt = 0;
} while( nLoopCnt );
if( bRet )
{
CompressRedlines();
m_rDoc.getIDocumentState().SetModified();
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
}
}
return bRet;
// #TODO - add 'SwExtraRedlineTable' also ?
}
bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
{
// Switch to visible in any case
if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
(RedlineFlags::ShowMask & meRedlineFlags) )
SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
// The Selection is only in the ContentSection. If there are Redlines
// to Non-ContentNodes before or after that, then the Selections
// expand to them.
SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
lcl_AdjustRedlineRange( aPam );
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
}
int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable,
bCallDelete, aPam );
if( nRet > 0 )
{
CompressRedlines();
m_rDoc.getIDocumentState().SetModified();
}
if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
{
OUString aTmpStr;
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, OUString::number(nRet));
aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
}
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, aTmpStr);
m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
}
return nRet != 0;
// #TODO - add 'SwExtraRedlineTable' also ?
}
void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
{
bool bSuccess = true;
OUString sUndoStr;
IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
if (mpRedlineTable->size() > 1)
{
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->size()));
sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
}
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, sUndoStr);
rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
}
while (!mpRedlineTable->empty() && bSuccess)
{
if (bAccept)
bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true);
else
bSuccess = RejectRedline(mpRedlineTable->size() - 1, true);
}
if (!sUndoStr.isEmpty())
{
rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
}
}
const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
{
rPam.DeleteMark();
rPam.SetMark();
SwPosition& rSttPos = *rPam.GetPoint();
SwPosition aSavePos( rSttPos );
bool bRestart;
// If the starting position points to the last valid ContentNode,
// we take the next Redline in any case.
SwRedlineTable::size_type n = 0;
const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
if( pFnd )
{
const SwPosition* pEnd = pFnd->End();
if( !pEnd->nNode.GetNode().IsContentNode() )
{
SwNodeIndex aTmp( pEnd->nNode );
SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
if( !pCNd || ( aTmp == rSttPos.nNode &&
pCNd->Len() == rSttPos.nContent.GetIndex() ))
pFnd = nullptr;
}
if( pFnd )
rSttPos = *pFnd->End();
}
do {
bRestart = false;
for( ; !pFnd && n < mpRedlineTable->size(); ++n )
{
pFnd = (*mpRedlineTable)[ n ];
if( pFnd->HasMark() && pFnd->IsVisible() )
{
*rPam.GetMark() = *pFnd->Start();
rSttPos = *pFnd->End();
break;
}
else
pFnd = nullptr;
}
if( pFnd )
{
// Merge all of the same type and author that are
// consecutive into one Selection.
const SwPosition* pPrevEnd = pFnd->End();
while( ++n < mpRedlineTable->size() )
{
const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
if( pTmp->HasMark() && pTmp->IsVisible() )
{
const SwPosition *pRStt;
if( pFnd->GetType() != pTmp->GetType() ||
pFnd->GetAuthor() != pTmp->GetAuthor() )
break;
pRStt = pTmp->Start();
if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
{
pPrevEnd = pTmp->End();
rSttPos = *pPrevEnd;
}
else
break;
}
}
}
if( pFnd )
{
const SwRangeRedline* pSaveFnd = pFnd;
SwContentNode* pCNd;
SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
if( !pIdx->GetNode().IsContentNode() )
{
pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
if( pCNd )
{
if( *pIdx <= rPam.GetPoint()->nNode )
rPam.GetMark()->nContent.Assign( pCNd, 0 );
else
pFnd = nullptr;
}
}
if( pFnd )
{
pIdx = &rPam.GetPoint()->nNode;
if( !pIdx->GetNode().IsContentNode() )
{
pCNd = SwNodes::GoPrevSection( pIdx );
if( pCNd )
{
if( *pIdx >= rPam.GetMark()->nNode )
rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
else
pFnd = nullptr;
}
}
}
if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
{
if( n < mpRedlineTable->size() )
{
bRestart = true;
*rPam.GetPoint() = *pSaveFnd->End();
}
else
{
rPam.DeleteMark();
*rPam.GetPoint() = aSavePos;
}
pFnd = nullptr;
}
}
} while( bRestart );
return pFnd;
// #TODO - add 'SwExtraRedlineTable' also ?
}
const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
{
rPam.DeleteMark();
rPam.SetMark();
SwPosition& rSttPos = *rPam.GetPoint();
SwPosition aSavePos( rSttPos );
bool bRestart;
// If the starting position points to the last valid ContentNode,
// we take the previous Redline in any case.
SwRedlineTable::size_type n = 0;
const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
if( pFnd )
{
const SwPosition* pStt = pFnd->Start();
if( !pStt->nNode.GetNode().IsContentNode() )
{
SwNodeIndex aTmp( pStt->nNode );
SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
if( !pCNd || ( aTmp == rSttPos.nNode &&
!rSttPos.nContent.GetIndex() ))
pFnd = nullptr;
}
if( pFnd )
rSttPos = *pFnd->Start();
}
do {
bRestart = false;
while( !pFnd && 0 < n )
{
pFnd = (*mpRedlineTable)[ --n ];
if( pFnd->HasMark() && pFnd->IsVisible() )
{
*rPam.GetMark() = *pFnd->End();
rSttPos = *pFnd->Start();
}
else
pFnd = nullptr;
}
if( pFnd )
{
// Merge all of the same type and author that are
// consecutive into one Selection.
const SwPosition* pNextStt = pFnd->Start();
while( 0 < n )
{
const SwRangeRedline* pTmp = (*mpRedlineTable)[ --n ];
if( pTmp->HasMark() && pTmp->IsVisible() )
{
const SwPosition *pREnd;
if( pFnd->GetType() == pTmp->GetType() &&
pFnd->GetAuthor() == pTmp->GetAuthor() &&
( *pNextStt == *( pREnd = pTmp->End() ) ||
IsPrevPos( *pREnd, *pNextStt )) )
{
pNextStt = pTmp->Start();
rSttPos = *pNextStt;
}
else
{
++n;
break;
}
}
}
}
if( pFnd )
{
const SwRangeRedline* pSaveFnd = pFnd;
SwContentNode* pCNd;
SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
if( !pIdx->GetNode().IsContentNode() )
{
pCNd = SwNodes::GoPrevSection( pIdx );
if( pCNd )
{
if( *pIdx >= rPam.GetPoint()->nNode )
rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
else
pFnd = nullptr;
}
}
if( pFnd )
{
pIdx = &rPam.GetPoint()->nNode;
if( !pIdx->GetNode().IsContentNode() )
{
pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
if( pCNd )
{
if( *pIdx <= rPam.GetMark()->nNode )
rPam.GetPoint()->nContent.Assign( pCNd, 0 );
else
pFnd = nullptr;
}
}
}
if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
{
if( n )
{
bRestart = true;
*rPam.GetPoint() = *pSaveFnd->Start();
}
else
{
rPam.DeleteMark();
*rPam.GetPoint() = aSavePos;
}
pFnd = nullptr;
}
}
} while( bRestart );
return pFnd;
// #TODO - add 'SwExtraRedlineTable' also ?
}
// Set comment at the Redline
bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
{
bool bRet = false;
const SwPosition* pStt = rPaM.Start(),
* pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
: rPaM.GetPoint();
SwRedlineTable::size_type n = 0;
if( GetRedlineTable().FindAtPosition( *pStt, n ) )
{
for( ; n < mpRedlineTable->size(); ++n )
{
bRet = true;
SwRangeRedline* pTmp = (*mpRedlineTable)[ n ];
if( pStt != pEnd && *pTmp->Start() > *pEnd )
break;
pTmp->SetComment( rS );
if( *pTmp->End() >= *pEnd )
break;
}
}
if( bRet )
m_rDoc.getIDocumentState().SetModified();
return bRet;
// #TODO - add 'SwExtraRedlineTable' also ?
}
// Create a new author if necessary
std::size_t DocumentRedlineManager::GetRedlineAuthor()
{
return SW_MOD()->GetRedlineAuthor();
}
/// Insert new author into the Table for the Readers etc.
std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
{
return SW_MOD()->InsertRedlineAuthor(rNew);
}
void DocumentRedlineManager::UpdateRedlineAttr()
{
const SwRedlineTable& rTable = GetRedlineTable();
for(SwRangeRedline* pRedl : rTable)
{
if( pRedl->IsVisible() )
pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
}
// #TODO - add 'SwExtraRedlineTable' also ?
}
const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
{
return maRedlinePasswd;
}
void DocumentRedlineManager::SetRedlinePassword(
/*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
{
maRedlinePasswd = rNewPassword;
m_rDoc.getIDocumentState().SetModified();
}
/// Set comment text for the Redline, which is inserted later on via
/// AppendRedline. Is used by Autoformat.
/// A null pointer resets the mode. The pointer is not copied, so it
/// needs to stay valid!
void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
{
m_rDoc.SetAutoFormatRedline( nullptr != pText );
if( pText )
{
mpAutoFormatRedlnComment.reset( new OUString( *pText ) );
}
else
{
mpAutoFormatRedlnComment.reset();
}
mnAutoFormatRedlnCommentNo = nSeqNo;
}
void DocumentRedlineManager::HideAll( bool bDeletion )
{
const SwRedlineTable& rTable = GetRedlineTable();
for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
{
SwRangeRedline* pRedline = rTable[i-1];
if ( pRedline->GetType() == RedlineType::Delete )
{
if ( bDeletion && pRedline->IsVisible() )
{
pRedline->Hide(0, rTable.GetPos(pRedline), false);
pRedline->Hide(1, rTable.GetPos(pRedline), false);
}
else if ( !bDeletion && !pRedline->IsVisible() )
{
pRedline->Show(0, rTable.GetPos(pRedline), true);
pRedline->Show(1, rTable.GetPos(pRedline), true);
}
}
else if ( pRedline->GetType() == RedlineType::Insert )
{
if ( !bDeletion && pRedline->IsVisible() )
{
pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
}
else if ( bDeletion && !pRedline->IsVisible() )
{
pRedline->Show(0, rTable.GetPos(pRedline), true);
pRedline->Show(1, rTable.GetPos(pRedline), true);
}
}
}
}
void DocumentRedlineManager::ShowAll()
{
const SwRedlineTable& rTable = GetRedlineTable();
for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
{
SwRangeRedline* pRedline = rTable[i-1];
if ( !pRedline->IsVisible() )
{
pRedline->Show(0, rTable.GetPos(pRedline), true);
pRedline->Show(1, rTable.GetPos(pRedline), true);
}
}
}
DocumentRedlineManager::~DocumentRedlineManager()
{
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */