/* -*- 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 .
*/
// This class saves the Pam as integers and can recompose those into a PaM
SwUndRng::SwUndRng()
: m_nSttNode( 0 ), m_nEndNode( 0 ), m_nSttContent( 0 ), m_nEndContent( 0 )
{
}
if (m_pHistory)
{
m_pHistory->dumpAsXml(pWriter);
}
(void)xmlTextWriterEndElement(pWriter);
}
// This is needed when deleting content. For REDO all contents will be moved // into the UndoNodesArray. These methods always create a new node to insert // content. As a result, the attributes will not be expanded. // - MoveTo moves from NodesArray into UndoNodesArray // - MoveFrom moves from UndoNodesArray into NodesArray
// If pEndNdIdx is given, Undo/Redo calls -Ins/DelFly. In that case the whole // section should be moved. void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
SwNodeOffset* pEndNdIdx )
{
SwDoc& rDoc = rPaM.GetDoc();
::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
// here comes the actual delete (move)
SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts()
: rNds.GetEndOfExtras() );
// delete the last Node as well bool bDeleteLastNode = false; if( !aPaM.GetPoint()->GetContentIndex() )
bDeleteLastNode = true; else
{ // still empty Nodes at the end?
aPaM.GetPoint()->Adjust(SwNodeOffset(1)); if ( &rNds.GetEndOfExtras() != &aPaM.GetPoint()->GetNode() )
bDeleteLastNode = true;
} if( bDeleteLastNode )
{
SwNode& rDelNode = aPaM.GetPoint()->GetNode();
SwNodeOffset nDelOffset = rNds.GetEndOfExtras().GetIndex() -
aPaM.GetPoint()->GetNodeIndex(); //move it so we don't have SwContentIndex pointing at a node when it is deleted.
aPaM.GetPoint()->Adjust(SwNodeOffset(-1));
aPaM.SetMark();
rNds.Delete( rDelNode, nDelOffset );
}
// These two methods save and restore the Point of PaM. // If the point cannot be moved, a "backup" is created on the previous node. // Either way, returned, inserting at its original position will not move it.
::std::optional<SwNodeIndex> SwUndoSaveContent::MovePtBackward(SwPaM & rPam)
{
rPam.SetMark(); if( rPam.Move( fnMoveBackward )) return {};
void SwUndoSaveContent::MovePtForward(SwPaM& rPam, ::std::optional<SwNodeIndex> && oMvBkwrd)
{ // Was there content before this position? if (!oMvBkwrd)
rPam.Move( fnMoveForward ); else
{
*rPam.GetPoint() = SwPosition(*oMvBkwrd);
rPam.GetPoint()->Adjust(SwNodeOffset(1));
SwContentNode* pCNd = rPam.GetPointContentNode(); if( !pCNd )
rPam.Move( fnMoveForward );
}
}
// Delete all objects that have ContentIndices to the given area. // Currently (1994) these exist: // - Footnotes // - Flys // - Bookmarks
// #i81002# - extending method // delete certain (not all) cross-reference bookmarks at text node of <rMark> // and at text node of <rPoint>, if these text nodes aren't the same. void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, const SwPosition& rPoint,
DelContentType nDelContentType )
{ const SwPosition *pStart = rMark < rPoint ? &rMark : &rPoint,
*pEnd = &rMark == pStart ? &rPoint : &rMark;
SwDoc& rDoc = rMark.GetNode().GetDoc();
// if it's not in the doc array, probably missing some invalidation somewhere
assert(&rPoint.GetNodes() == &rDoc.GetNodes());
assert(&rMark.GetNodes() == &rDoc.GetNodes());
// FIXME: duplicated code here and below -> refactor? // Unfortunately an index needs to be created. Otherwise there // will be problems with TextNode because the index will be // deleted in the DTOR of SwFootnote!
SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd)); if( !m_pHistory )
m_pHistory.reset( new SwHistory );
SwTextAttr* const pFootnoteHint =
pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
assert(pFootnoteHint);
SwContentIndex aIdx( pTextNd, nFootnoteSttIdx );
m_pHistory->AddTextAttr(pFootnoteHint, pTextNd->GetIndex(), false);
pTextNd->EraseText( aIdx, 1 );
}
// Unfortunately an index needs to be created. Otherwise there // will be problems with TextNode because the index will be // deleted in the DTOR of SwFootnote!
SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd)); if( !m_pHistory )
m_pHistory.reset( new SwHistory );
SwTextAttr* const pFootnoteHint =
pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
assert(pFootnoteHint);
SwContentIndex aIdx( pTextNd, nFootnoteSttIdx );
m_pHistory->AddTextAttr(pFootnoteHint, pTextNd->GetIndex(), false);
pTextNd->EraseText( aIdx, 1 );
}
}
}
while( n && !rSpzArr.empty() )
{
pFormat = rSpzArr[--n];
pAnchor = &pFormat->GetAnchor(); switch( pAnchor->GetAnchorId() )
{ case RndStdIds::FLY_AS_CHAR: if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
(( DelContentType::CheckNoCntnt & nDelContentType )
? ( pStart->GetNode() <= pAPos->GetNode() &&
pAPos->GetNode() < pEnd->GetNode() )
: ( *pStart <= *pAPos && *pAPos < *pEnd )) )
{ if( !m_pHistory )
m_pHistory.reset( new SwHistory );
SwTextNode *const pTextNd =
pAPos->GetNode().GetTextNode();
SwTextAttr* const pFlyHint = pTextNd->GetTextAttrForCharAt(
pAPos->GetContentIndex());
assert(pFlyHint);
m_pHistory->AddTextAttr(pFlyHint, SwNodeOffset(0), false); // reset n so that no Format is skipped
n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
} break; case RndStdIds::FLY_AT_PARA:
{
pAPos = pAnchor->GetContentAnchor(); if (pAPos &&
pStart->GetNode() <= pAPos->GetNode() && pAPos->GetNode() <= pEnd->GetNode())
{ if (!m_pHistory)
m_pHistory.reset( new SwHistory );
if (IsSelectFrameAnchoredAtPara(*pAPos, *pStart, *pEnd, nDelContentType))
{
m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); // reset n so that no Format is skipped
n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
} // Moving the anchor? elseif (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd)
& nDelContentType) && // for SwUndoDelete: rPoint is the node that // will be Joined - so anchor should be moved // off it - but UndoImpl() split will insert // new node *before* existing one so a no-op // may need to be done here to add it to // history for Undo.
(rPoint.GetNodeIndex() == pAPos->GetNodeIndex()
|| pStart->GetNodeIndex() == pAPos->GetNodeIndex()) // Do not try to move the anchor to a table!
&& rMark.GetNode().IsTextNode())
{
m_pHistory->AddChangeFlyAnchor(*pFormat);
SwFormatAnchor aAnch( *pAnchor );
SwPosition aPos( rMark.GetNode() );
aAnch.SetAnchor( &aPos );
pFormat->SetFormatAttr( aAnch );
}
}
} break; case RndStdIds::FLY_AT_CHAR: if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
( pStart->GetNode() <= pAPos->GetNode() && pAPos->GetNode() <= pEnd->GetNode() ) )
{ if( !m_pHistory )
m_pHistory.reset( new SwHistory ); if (IsDestroyFrameAnchoredAtChar(*pAPos, *pStart, *pEnd, nDelContentType))
{
m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
} elseif (!((DelContentType::CheckNoCntnt |
DelContentType::ExcludeFlyAtStartEnd)
& nDelContentType))
{ if( *pStart <= *pAPos && *pAPos < *pEnd )
{ // These are the objects anchored // between section start and end position // Do not try to move the anchor to a table! if( rMark.GetNode().GetTextNode() )
{
m_pHistory->AddChangeFlyAnchor(*pFormat);
SwFormatAnchor aAnch( *pAnchor );
aAnch.SetAnchor( &rMark );
pFormat->SetFormatAttr( aAnch );
}
}
}
} break; case RndStdIds::FLY_AT_FLY:
if ( !bSavePos && !bSaveOtherPos
&& dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) )
{ // certain special handling for cross-reference bookmarks constbool bDifferentTextNodesAtMarkAndPoint =
rMark.GetNode() != rPoint.GetNode()
&& rMark.GetNode().GetTextNode()
&& rPoint.GetNode().GetTextNode(); if ( bDifferentTextNodesAtMarkAndPoint )
{ // delete cross-reference bookmark at <pStart>, if only part of // <pEnd> text node content is deleted. if( pStart->GetNode() == pBkmk->GetMarkPos().GetNode()
&& pEnd->GetContentIndex() != pEnd->GetNode().GetTextNode()->Len() )
{
bSavePos = true;
bSaveOtherPos = false; // cross-reference bookmarks are not expanded
} // delete cross-reference bookmark at <pEnd>, if only part of // <pStart> text node content is deleted. elseif( pEnd->GetNode() == pBkmk->GetMarkPos().GetNode() &&
pStart->GetContentIndex() != 0 )
{
bSavePos = true;
bSaveOtherPos = false; // cross-reference bookmarks are not expanded
}
}
} elseif (type == IDocumentMarkAccess::MarkType::ANNOTATIONMARK)
{ // delete annotation marks, if its end position is covered by the deletion const SwPosition& rAnnotationEndPos = pBkmk->GetMarkEnd(); if ( *pStart < rAnnotationEndPos && rAnnotationEndPos <= *pEnd )
{
bSavePos = true;
bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only save the other pos if there is one
bDelete = true;
}
}
}
if ( bSavePos || bSaveOtherPos )
{ if (type != IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
{ if( !m_pHistory )
m_pHistory.reset( new SwHistory );
m_pHistory->AddIMark(*pBkmk, bSavePos, bSaveOtherPos);
} if ( bSavePos
&& (bDelete || !pBkmk->IsExpanded()))
{
pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, false);
n--;
}
}
}
}
// save a complete section into UndoNodes array
SwUndoSaveSection::SwUndoSaveSection()
: m_nMoveLen( 0 ), m_nStartPos( NODE_OFFSET_MAX )
{
}
SwUndoSaveSection::~SwUndoSaveSection()
{ if (m_oMovedStart) // delete also the section from UndoNodes array
{ // SaveSection saves the content in the PostIt section.
SwNodes& rUNds = m_oMovedStart->GetNode().GetNodes(); // cid#1486004 Uncaught exception
suppress_fun_call_w_exception(rUNds.Delete(*m_oMovedStart, m_nMoveLen));
// redlines *before* CorrAbs, because DelBookmarks will make them 0-length // but *after* DelContentIndex because that also may use FillSaveData (in // flys) and that will be restored *after* this one...
m_pRedlineSaveData.reset( new SwRedlineSaveDatas ); if (!SwUndo::FillSaveData( aPam, *m_pRedlineSaveData ))
{
m_pRedlineSaveData.reset();
}
{ // move certain indexes out of deleted range
SwNodeIndex aSttIdx( aPam.Start()->GetNode() );
SwNodeIndex aEndIdx( aPam.End()->GetNode() );
SwNodeIndex aMvStt( aEndIdx, 1 );
SwDoc::CorrAbs( aSttIdx, aEndIdx, SwPosition( aMvStt ), true );
}
m_nStartPos = rRange.aStart.GetIndex();
if (bExpandNodes)
{
aPam.GetPoint()->Adjust(SwNodeOffset(-1));
aPam.GetMark()->Adjust(SwNodeOffset(+1));
}
// Keep positions as SwContentIndex so that this section can be deleted in DTOR
SwNodeOffset nEnd;
m_oMovedStart = rRange.aStart;
MoveToUndoNds(aPam, &*m_oMovedStart, &nEnd);
m_nMoveLen = nEnd - m_oMovedStart->GetIndex() + 1;
}
void SwUndoSaveSection::RestoreSection( SwDoc& rDoc, SwNodeIndex* pIdx,
sal_uInt16 nSectType )
{ if( NODE_OFFSET_MAX == m_nStartPos ) // was there any content? return;
// check if the content is at the old position
SwNodeIndex aSttIdx( rDoc.GetNodes(), m_nStartPos );
// move the content from UndoNodes array into Fly
SwStartNode* pSttNd = SwNodes::MakeEmptySection( aSttIdx.GetNode(), static_cast<SwStartNodeType>(nSectType) );
// save and set the RedlineData
SwRedlineSaveData::SwRedlineSaveData(
SwComparePosition eCmpPos, const SwPosition& rSttPos, const SwPosition& rEndPos,
SwRangeRedline& rRedl, bool bCopyNext )
: SwUndRng( rRedl )
, SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
{
assert( SwComparePosition::Outside == eCmpPos ||
!rRedl.GetContentIdx() ); // "Redline with Content"
switch (eCmpPos)
{ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at the beginning
m_nEndNode = rEndPos.GetNodeIndex();
m_nEndContent = rEndPos.GetContentIndex(); break;
case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
m_nSttNode = rSttPos.GetNodeIndex();
m_nSttContent = rSttPos.GetContentIndex(); break;
case SwComparePosition::Outside: // Pos2 lays completely in Pos1 if ( rRedl.GetContentIdx() )
{ // than move section into UndoArray and memorize it
SaveSection( *rRedl.GetContentIdx() );
rRedl.ClearContentIdx();
} break;
case SwComparePosition::Equal: // Pos1 is exactly as big as Pos2 break;
if( GetMvSttIdx() )
{
SwNodeIndex aIdx( rDoc.GetNodes() );
RestoreSection( rDoc, &aIdx, SwNormalStartNode ); if( GetHistory() )
GetHistory()->Rollback( rDoc );
pRedl->SetContentIdx( aIdx );
}
SetPaM( *pRedl ); // First, delete the "old" so that in an Append no unexpected things will // happen, e.g. a delete in an insert. In the latter case the just restored // content will be deleted and not the one you originally wanted.
rDoc.getIDocumentRedlineAccess().DeleteRedline( *pRedl, false, RedlineType::Any );
#if OSL_DEBUG_LEVEL > 0 // check redline count against count saved in RedlineSaveData object // except in the case of moved redlines
assert(
rSData.empty() || rSData[0].m_bRedlineMoved || rSData[0].m_bRedlineCountDontCheck ||
(rSData[0].m_nRedlineCount == rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())); // "redline count not restored properly" #endif
/// passed start / end position could be on section start / end node staticbool IsAtEndOfSection2(SwPosition const& rPos)
{ return rPos.GetNode().IsEndNode()
|| IsAtEndOfSection(rPos);
}
// CheckNoCntnt means DelFullPara which is obvious to handle if (DelContentType::CheckNoCntnt & nDelContentType)
{ // exclude selection end node because it won't be deleted return (rAnchorPos.GetNode() < rEnd.GetNode())
&& (rStart.GetNode() <= rAnchorPos.GetNode());
}
if ((nDelContentType & DelContentType::WriterfilterHack)
&& rAnchorPos.GetDoc().IsInWriterfilterImport())
{ // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific? return (rStart < rAnchorPos) && (rAnchorPos < rEnd);
}
if (nDelContentType & (DelContentType::ExcludeFlyAtStartEnd|DelContentType::Replace))
{ // exclude selection start and end node return (rAnchorPos.GetNode() < rEnd.GetNode())
&& (rStart.GetNode() < rAnchorPos.GetNode());
}
// in general, exclude the start and end position return ((rStart < rAnchorPos)
|| (rStart == rAnchorPos // special case: fully deleted node
&& ((rStart.GetNode() != rEnd.GetNode() && rStart.GetContentIndex() == 0 // but not if the selection is backspace/delete!
&& IsNotBackspaceHeuristic(rStart, rEnd))
|| (IsAtStartOfSection(rAnchorPos) && IsAtEndOfSection2(rEnd)))))
&& ((rAnchorPos < rEnd)
|| (rAnchorPos == rEnd // special case: fully deleted node
&& ((rEnd.GetNode() != rStart.GetNode() && rEnd.GetContentIndex() == rEnd.GetNode().GetTextNode()->Len()
&& IsNotBackspaceHeuristic(rStart, rEnd))
|| (IsAtEndOfSection(rAnchorPos) && IsAtStartOfSection2(rStart)))));
}
// CheckNoCntnt means DelFullPara which is obvious to handle if (DelContentType::CheckNoCntnt & nDelContentType)
{ // exclude selection end node because it won't be deleted return (rAnchorPos.GetNode() < rEnd.GetNode())
&& (rStart.GetNode() <= rAnchorPos.GetNode());
}
if ((nDelContentType & DelContentType::WriterfilterHack)
&& rAnchorPos.GetDoc().IsInWriterfilterImport())
{ // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific? // but it MUST NOT be done during the SetRedlineFlags at the end of ODF // import, where the IsInXMLImport() cannot be checked because the // stupid code temp. overrides it - instead rely on setting the ALLFLYS // flag in MoveFromSection() and converting that to CheckNoCntnt with // adjusted cursor! return (rStart.GetNode() < rAnchorPos.GetNode()) && (rAnchorPos.GetNode() < rEnd.GetNode());
}
// in general, exclude the start and end position return ((rStart.GetNode() < rAnchorPos.GetNode())
|| (rStart.GetNode() == rAnchorPos.GetNode()
&& !(nDelContentType & (DelContentType::ExcludeFlyAtStartEnd|DelContentType::Replace)) // special case: fully deleted node
&& ((rStart.GetNode() != rEnd.GetNode() && rStart.GetContentIndex() == 0 // but not if the selection is backspace/delete!
&& IsNotBackspaceHeuristic(rStart, rEnd))
|| (IsAtStartOfSection2(rStart) && IsAtEndOfSection2(rEnd)))))
&& ((rAnchorPos.GetNode() < rEnd.GetNode())
|| (rAnchorPos.GetNode() == rEnd.GetNode()
&& !(nDelContentType & (DelContentType::ExcludeFlyAtStartEnd|DelContentType::Replace)) // special case: fully deleted node
&& ((rEnd.GetNode() != rStart.GetNode() && rEnd.GetContentIndex() == rEnd.GetNode().GetTextNode()->Len()
&& IsNotBackspaceHeuristic(rStart, rEnd))
|| (IsAtEndOfSection2(rEnd) && IsAtStartOfSection2(rStart)))));
}
bool IsFlySelectedByCursor(SwDoc const & rDoc,
SwPosition const & rStart, SwPosition const & rEnd)
{ for (SwFrameFormat const*const pFly : *rDoc.GetSpzFrameFormats())
{
SwFormatAnchor const& rAnchor(pFly->GetAnchor()); switch (rAnchor.GetAnchorId())
{ case RndStdIds::FLY_AT_CHAR: case RndStdIds::FLY_AT_PARA:
{
SwPosition const*const pAnchorPos(rAnchor.GetContentAnchor()); // can this really be null? if (pAnchorPos != nullptr
&& ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
? IsDestroyFrameAnchoredAtChar(*pAnchorPos, rStart, rEnd)
: IsSelectFrameAnchoredAtPara(*pAnchorPos, rStart, rEnd)))
{ returntrue;
}
} break; default: // other types not relevant break;
}
} returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.