2477 lines
89 KiB
C++
2477 lines
89 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 <stdlib.h>
|
|
#include <libxml/xmlwriter.h>
|
|
#include <osl/diagnose.h>
|
|
#include <tools/json_writer.hxx>
|
|
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
|
|
#include <sfx2/viewsh.hxx>
|
|
#include <comphelper/lok.hxx>
|
|
|
|
#include <node.hxx>
|
|
#include <doc.hxx>
|
|
#include <IDocumentUndoRedo.hxx>
|
|
#include <IDocumentFieldsAccess.hxx>
|
|
#include <IDocumentLayoutAccess.hxx>
|
|
#include <pam.hxx>
|
|
#include <txtfld.hxx>
|
|
#include <fmtfld.hxx>
|
|
#include <numrule.hxx>
|
|
#include <ndtxt.hxx>
|
|
#include <ndnotxt.hxx>
|
|
#include <swtable.hxx>
|
|
#include <section.hxx>
|
|
#include <ddefld.hxx>
|
|
#include <swddetbl.hxx>
|
|
#include <txtatr.hxx>
|
|
#include <tox.hxx>
|
|
#include <fmtrfmrk.hxx>
|
|
#include <fmtftn.hxx>
|
|
#include <docsh.hxx>
|
|
#include <rootfrm.hxx>
|
|
#include <txtfrm.hxx>
|
|
|
|
typedef std::vector<SwStartNode*> SwStartNodePointers;
|
|
|
|
// function to determine the highest level in the given range
|
|
static sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange );
|
|
|
|
/** Constructor
|
|
*
|
|
* creates the base sections (PostIts, Inserts, AutoText, RedLines, Content)
|
|
*
|
|
* @param rDocument TODO: provide documentation
|
|
*/
|
|
SwNodes::SwNodes( SwDoc& rDocument )
|
|
: m_rMyDoc( rDocument )
|
|
{
|
|
m_bInNodesDel = m_bInDelUpdOutline = false;
|
|
|
|
SwNodeOffset nPos(0);
|
|
SwStartNode* pSttNd = new SwStartNode( *this, nPos++ );
|
|
m_pEndOfPostIts = new SwEndNode( *this, nPos++, *pSttNd );
|
|
|
|
SwStartNode* pTmp = new SwStartNode( *this, nPos++ );
|
|
m_pEndOfInserts = new SwEndNode( *this, nPos++, *pTmp );
|
|
|
|
pTmp = new SwStartNode( *this, nPos++ );
|
|
pTmp->m_pStartOfSection = pSttNd;
|
|
m_pEndOfAutotext = new SwEndNode( *this, nPos++, *pTmp );
|
|
|
|
pTmp = new SwStartNode( *this, nPos++ );
|
|
pTmp->m_pStartOfSection = pSttNd;
|
|
m_pEndOfRedlines = new SwEndNode( *this, nPos++, *pTmp );
|
|
|
|
pTmp = new SwStartNode( *this, nPos++ );
|
|
pTmp->m_pStartOfSection = pSttNd;
|
|
m_pEndOfContent.reset(new SwEndNode( *this, nPos++, *pTmp ));
|
|
|
|
m_aOutlineNodes.clear();
|
|
}
|
|
|
|
/** Destructor
|
|
*
|
|
* Deletes all nodes whose pointer are in a dynamic array. This should be no
|
|
* problem as nodes cannot be created outside this array and, thus, cannot be
|
|
* part of multiple arrays.
|
|
*/
|
|
SwNodes::~SwNodes()
|
|
{
|
|
m_aOutlineNodes.clear();
|
|
|
|
{
|
|
SwNodeIndex aNdIdx( *this );
|
|
while( true )
|
|
{
|
|
SwNode *pNode = &aNdIdx.GetNode();
|
|
if( pNode == m_pEndOfContent.get() )
|
|
break;
|
|
|
|
++aNdIdx;
|
|
delete pNode;
|
|
}
|
|
}
|
|
|
|
// here, all SwNodeIndices must be unregistered
|
|
m_pEndOfContent.reset();
|
|
}
|
|
|
|
static bool IsInsertOutline(SwNodes const& rNodes, SwNodeOffset const nIndex)
|
|
{
|
|
if (!rNodes.IsDocNodes())
|
|
{
|
|
return false;
|
|
}
|
|
return nIndex < rNodes.GetEndOfRedlines().StartOfSectionNode()->GetIndex()
|
|
|| rNodes.GetEndOfRedlines().GetIndex() < nIndex;
|
|
}
|
|
|
|
void SwNodes::ChgNode( SwNodeIndex const & rDelPos, SwNodeOffset nSz,
|
|
SwNodeIndex& rInsPos, bool bNewFrames )
|
|
{
|
|
// no need for frames in the UndoArea
|
|
SwNodes& rNds = rInsPos.GetNodes();
|
|
const SwNode* pPrevInsNd = rNds[ rInsPos.GetIndex() -SwNodeOffset(1) ];
|
|
|
|
// declare all fields as invalid, updating will happen
|
|
// in the idle-handler of the doc
|
|
if( GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rDelPos.GetNode(), nSz ) &&
|
|
&rNds.GetDoc() != &GetDoc() )
|
|
rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
|
|
|
|
// NEVER include nodes from the RedLineArea
|
|
SwNodeOffset nNd = rInsPos.GetIndex();
|
|
bool const bInsOutlineIdx = IsInsertOutline(rNds, nNd);
|
|
|
|
if( &rNds == this ) // if in the same node array -> move
|
|
{
|
|
// Move order: from front to back, so that new entries are added at
|
|
// first position, thus, deletion position stays the same
|
|
const SwNodeOffset nDiff(rDelPos.GetIndex() < rInsPos.GetIndex() ? 0 : 1);
|
|
|
|
for( SwNodeOffset n = rDelPos.GetIndex(); nSz; n += nDiff, --nSz )
|
|
{
|
|
SwNodeIndex aDelIdx( *this, n );
|
|
SwNode& rNd = aDelIdx.GetNode();
|
|
|
|
// #i57920# - correction of refactoring done by cws swnumtree:
|
|
// - <SwTextNode::SetLevel( NO_NUMBERING ) is deprecated and
|
|
// set <IsCounted> state of the text node to <false>, which
|
|
// isn't correct here.
|
|
if ( rNd.IsTextNode() )
|
|
{
|
|
SwTextNode* pTextNode = rNd.GetTextNode();
|
|
|
|
pTextNode->RemoveFromList();
|
|
|
|
if (pTextNode->IsOutline())
|
|
{
|
|
SwNode* pSrch = &rNd;
|
|
m_aOutlineNodes.erase( pSrch );
|
|
}
|
|
}
|
|
|
|
BigPtrArray::Move( sal_Int32(aDelIdx.GetIndex()), sal_Int32(rInsPos.GetIndex()) );
|
|
|
|
if( rNd.IsTextNode() )
|
|
{
|
|
SwTextNode& rTextNd = static_cast<SwTextNode&>(rNd);
|
|
|
|
rTextNd.AddToList();
|
|
|
|
if (bInsOutlineIdx && rTextNd.IsOutline())
|
|
{
|
|
SwNode* pSrch = &rNd;
|
|
m_aOutlineNodes.insert( pSrch );
|
|
}
|
|
rTextNd.InvalidateNumRule();
|
|
|
|
if( RES_CONDTXTFMTCOLL == rTextNd.GetTextColl()->Which() )
|
|
rTextNd.ChkCondColl();
|
|
}
|
|
else if( rNd.IsContentNode() )
|
|
static_cast<SwContentNode&>(rNd).InvalidateNumRule();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool bSavePersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds));
|
|
bool bRestPersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this));
|
|
SwDoc* pDestDoc = &rNds.GetDoc() != &GetDoc() ? &rNds.GetDoc() : nullptr;
|
|
OSL_ENSURE(!pDestDoc, "SwNodes::ChgNode(): "
|
|
"the code to handle text fields here looks broken\n"
|
|
"if the target is in a different document.");
|
|
if( !bRestPersData && !bSavePersData && pDestDoc )
|
|
bSavePersData = bRestPersData = true;
|
|
|
|
OUString sNumRule;
|
|
for( SwNodeOffset n(0); n < nSz; n++ )
|
|
{
|
|
SwNode* pNd = &rDelPos.GetNode();
|
|
|
|
// NoTextNode keep their persistent data
|
|
if( pNd->IsNoTextNode() )
|
|
{
|
|
if( bSavePersData )
|
|
static_cast<SwNoTextNode*>(pNd)->SavePersistentData();
|
|
}
|
|
else if( pNd->IsTextNode() )
|
|
{
|
|
SwTextNode* pTextNd = static_cast<SwTextNode*>(pNd);
|
|
|
|
// remove outline index from old nodes array
|
|
if (pTextNd->IsOutline())
|
|
{
|
|
m_aOutlineNodes.erase( pNd );
|
|
}
|
|
|
|
// copy rules if needed
|
|
if( pDestDoc )
|
|
{
|
|
const SwNumRule* pNumRule = pTextNd->GetNumRule();
|
|
if( pNumRule && sNumRule != pNumRule->GetName() )
|
|
{
|
|
sNumRule = pNumRule->GetName();
|
|
SwNumRule* pDestRule = pDestDoc->FindNumRulePtr( sNumRule );
|
|
if( pDestRule )
|
|
pDestRule->Invalidate();
|
|
else
|
|
pDestDoc->MakeNumRule( sNumRule, pNumRule );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if movement into the UndoNodes-array, update numbering
|
|
if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
|
|
{
|
|
pTextNd->InvalidateNumRule();
|
|
}
|
|
}
|
|
|
|
pTextNd->RemoveFromList();
|
|
}
|
|
|
|
RemoveNode( rDelPos.GetIndex(), SwNodeOffset(1), false ); // move indices
|
|
SwContentNode * pCNd = pNd->GetContentNode();
|
|
rNds.InsertNode( pNd, rInsPos );
|
|
|
|
if( pCNd )
|
|
{
|
|
SwTextNode* pTextNd = pCNd->GetTextNode();
|
|
if( pTextNd )
|
|
{
|
|
SwpHints * const pHts = pTextNd->GetpSwpHints();
|
|
// OutlineNodes set the new nodes in the array
|
|
if (bInsOutlineIdx && pTextNd->IsOutline())
|
|
{
|
|
rNds.m_aOutlineNodes.insert( pTextNd );
|
|
}
|
|
|
|
pTextNd->AddToList();
|
|
|
|
// special treatment for fields
|
|
if( pHts && pHts->Count() )
|
|
{
|
|
bool const bToUndo = !pDestDoc &&
|
|
GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds);
|
|
for( size_t i = pHts->Count(); i; )
|
|
{
|
|
SwTextAttr * const pAttr = pHts->Get( --i );
|
|
switch ( pAttr->Which() )
|
|
{
|
|
case RES_TXTATR_FIELD:
|
|
case RES_TXTATR_ANNOTATION:
|
|
case RES_TXTATR_INPUTFIELD:
|
|
{
|
|
SwTextField* pTextField = static_txtattr_cast<SwTextField*>(pAttr);
|
|
rNds.GetDoc().getIDocumentFieldsAccess().InsDelFieldInFieldLst( !bToUndo, *pTextField );
|
|
|
|
SwFieldType* pTyp = pTextField->GetFormatField().GetField()->GetTyp();
|
|
if ( SwFieldIds::Postit == pTyp->Which() )
|
|
{
|
|
rNds.GetDoc().GetDocShell()->Broadcast(
|
|
SwFormatFieldHint(
|
|
&pTextField->GetFormatField(),
|
|
( pTextField->GetFormatField().IsFieldInDoc()
|
|
? SwFormatFieldHintWhich::INSERTED
|
|
: SwFormatFieldHintWhich::REMOVED ) ) );
|
|
}
|
|
else if( SwFieldIds::Dde == pTyp->Which() )
|
|
{
|
|
if( bToUndo )
|
|
static_cast<SwDDEFieldType*>(pTyp)->DecRefCnt();
|
|
else
|
|
static_cast<SwDDEFieldType*>(pTyp)->IncRefCnt();
|
|
}
|
|
static_cast<SwFormatField&>(pAttr->GetAttr())
|
|
.InvalidateField();
|
|
}
|
|
break;
|
|
|
|
case RES_TXTATR_FTN:
|
|
static_cast<SwFormatFootnote&>(pAttr->GetAttr())
|
|
.InvalidateFootnote();
|
|
break;
|
|
|
|
case RES_TXTATR_TOXMARK:
|
|
static_cast<SwTOXMark&>(pAttr->GetAttr())
|
|
.InvalidateTOXMark();
|
|
break;
|
|
|
|
case RES_TXTATR_REFMARK:
|
|
static_cast<SwFormatRefMark&>(pAttr->GetAttr())
|
|
.InvalidateRefMark();
|
|
break;
|
|
|
|
case RES_TXTATR_META:
|
|
case RES_TXTATR_METAFIELD:
|
|
{
|
|
SwTextMeta *const pTextMeta(
|
|
static_txtattr_cast<SwTextMeta*>(pAttr));
|
|
// force removal of UNO object
|
|
pTextMeta->ChgTextNode(nullptr);
|
|
pTextMeta->ChgTextNode(pTextNd);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( RES_CONDTXTFMTCOLL == pTextNd->GetTextColl()->Which() )
|
|
pTextNd->ChkCondColl();
|
|
}
|
|
else
|
|
{
|
|
// Moved into different Docs? Persist data again!
|
|
if( pCNd->IsNoTextNode() && bRestPersData )
|
|
static_cast<SwNoTextNode*>(pCNd)->RestorePersistentData();
|
|
}
|
|
|
|
// reset Accessibility issue state
|
|
pCNd->resetAndQueueAccessibilityCheck();
|
|
}
|
|
}
|
|
}
|
|
|
|
// declare all fields as invalid, updating will happen
|
|
// in the idle-handler of the doc
|
|
GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
|
|
if( &rNds.GetDoc() != &GetDoc() )
|
|
rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
|
|
|
|
if( bNewFrames )
|
|
bNewFrames = &GetDoc().GetNodes() == &rNds &&
|
|
GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
|
|
|
|
if( !bNewFrames )
|
|
return;
|
|
|
|
// get the frames:
|
|
SwNodeIndex aIdx( *pPrevInsNd, 1 );
|
|
SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aIdx.GetNode(),
|
|
rNds[ rInsPos.GetIndex() - 1 ] );
|
|
|
|
if( !pFrameNd )
|
|
return;
|
|
|
|
while( aIdx != rInsPos )
|
|
{
|
|
SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
|
|
if( pCNd )
|
|
{
|
|
if( pFrameNd->IsTableNode() )
|
|
static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
|
|
else if( pFrameNd->IsSectionNode() )
|
|
static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
|
|
else
|
|
static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
|
|
pFrameNd = pCNd;
|
|
}
|
|
++aIdx;
|
|
}
|
|
}
|
|
|
|
// TODO: provide documentation
|
|
/** move the node pointer
|
|
*
|
|
* Move the node pointer from "(inclusive) start position to (exclusive) end
|
|
* position" to target position.
|
|
* If the target is in front of the first or in the area between first and
|
|
* last element to move, nothing happens.
|
|
* If the area to move is empty or the end position is before the start
|
|
* position, nothing happens.
|
|
*
|
|
* @param aRange range to move (excluding end node)
|
|
* @return
|
|
*/
|
|
bool SwNodes::MoveNodes( const SwNodeRange& aRange, SwNodes & rNodes,
|
|
SwNode& rPos, bool bNewFrames )
|
|
{
|
|
SwNode * pCurrentNode;
|
|
if( rPos.GetIndex() == SwNodeOffset(0) ||
|
|
( (pCurrentNode = &rPos)->GetStartNode() &&
|
|
!pCurrentNode->StartOfSectionIndex() ))
|
|
return false;
|
|
|
|
SwNodeRange aRg( aRange );
|
|
|
|
// skip "simple" start or end nodes
|
|
while( SwNodeType::Start == (pCurrentNode = &aRg.aStart.GetNode())->GetNodeType()
|
|
|| ( pCurrentNode->IsEndNode() &&
|
|
!pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
|
|
++aRg.aStart;
|
|
--aRg.aStart;
|
|
|
|
// if aEnd-1 points to no ContentNode, search previous one
|
|
--aRg.aEnd;
|
|
while( ( (( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
|
|
!pCurrentNode->IsSectionNode() ) ||
|
|
( pCurrentNode->IsEndNode() &&
|
|
SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) ) &&
|
|
aRg.aEnd > aRg.aStart )
|
|
--aRg.aEnd;
|
|
|
|
// if in same array, check insertion position
|
|
if( aRg.aStart >= aRg.aEnd )
|
|
return false;
|
|
|
|
if( this == &rNodes )
|
|
{
|
|
if( ( rPos.GetIndex()-SwNodeOffset(1) >= aRg.aStart.GetIndex() &&
|
|
rPos.GetIndex()-SwNodeOffset(1) < aRg.aEnd.GetIndex()) ||
|
|
( rPos.GetIndex()-SwNodeOffset(1) == aRg.aEnd.GetIndex() ) )
|
|
return false;
|
|
}
|
|
|
|
SwNodeOffset nInsPos(0); // counter for tmp array
|
|
|
|
// array as a stack, storing all StartOfSelections
|
|
SwStartNodePointers aSttNdStack;
|
|
SwStartNodePointers::size_type nLevel = 0; // level counter
|
|
|
|
// set start index
|
|
SwNodeIndex aIdx( rPos );
|
|
|
|
SwStartNode* pStartNode = aIdx.GetNode().m_pStartOfSection;
|
|
aSttNdStack.insert( aSttNdStack.begin(), pStartNode );
|
|
|
|
SwNodeRange aOrigInsPos( aIdx, SwNodeOffset(-1), aIdx ); // original insertion position
|
|
|
|
// call DelFrames/MakeFrames for the upmost SectionNode
|
|
int nSectNdCnt = 0;
|
|
bool bSaveNewFrames = bNewFrames;
|
|
|
|
// continue until everything has been moved
|
|
while( aRg.aStart < aRg.aEnd )
|
|
{
|
|
pCurrentNode = &aRg.aEnd.GetNode();
|
|
switch( pCurrentNode->GetNodeType() )
|
|
{
|
|
case SwNodeType::End:
|
|
{
|
|
if( nInsPos ) // move everything until here
|
|
{
|
|
// delete and copy. CAUTION: all indices after
|
|
// "aRg.aEnd+1" will be moved as well!
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
aIdx -= nInsPos;
|
|
nInsPos = SwNodeOffset(0);
|
|
}
|
|
|
|
SwStartNode* pSttNd = pCurrentNode->m_pStartOfSection;
|
|
if( pSttNd->IsTableNode() )
|
|
{
|
|
SwTableNode* pTableNd = static_cast<SwTableNode*>(pSttNd);
|
|
|
|
// move the whole table/range
|
|
nInsPos = (aRg.aEnd.GetIndex() -
|
|
pSttNd->GetIndex() )+1;
|
|
aRg.aEnd -= nInsPos;
|
|
|
|
// NEVER include nodes from the RedLineArea
|
|
SwNodeOffset nNd = aIdx.GetIndex();
|
|
bool const bInsOutlineIdx = IsInsertOutline(rNodes, nNd);
|
|
|
|
if( bNewFrames )
|
|
// delete all frames
|
|
pTableNd->DelFrames(nullptr);
|
|
if( &rNodes == this ) // move into self?
|
|
{
|
|
// move all Start/End/ContentNodes
|
|
// ContentNodes: delete also the frames!
|
|
pTableNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
|
|
for( SwNodeOffset n(0); n < nInsPos; ++n )
|
|
{
|
|
SwNodeIndex aMvIdx( aRg.aEnd, 1 );
|
|
SwContentNode* pCNd = nullptr;
|
|
SwNode* pTmpNd = &aMvIdx.GetNode();
|
|
if( pTmpNd->IsContentNode() )
|
|
{
|
|
pCNd = static_cast<SwContentNode*>(pTmpNd);
|
|
if( pTmpNd->IsTextNode() )
|
|
static_cast<SwTextNode*>(pTmpNd)->RemoveFromList();
|
|
|
|
// remove outline index from old nodes array
|
|
if (pCNd->IsTextNode() && pCNd->GetTextNode()->IsOutline())
|
|
{
|
|
m_aOutlineNodes.erase( pCNd );
|
|
}
|
|
else
|
|
pCNd = nullptr;
|
|
}
|
|
|
|
BigPtrArray::Move( sal_Int32(aMvIdx.GetIndex()), sal_Int32(aIdx.GetIndex()) );
|
|
|
|
if( bInsOutlineIdx && pCNd )
|
|
m_aOutlineNodes.insert( pCNd );
|
|
if( pTmpNd->IsTextNode() )
|
|
static_cast<SwTextNode*>(pTmpNd)->AddToList();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get StartNode
|
|
// Even aIdx points to a startnode, we need the startnode
|
|
// of the environment of aIdx (#i80941)
|
|
SwStartNode* pSttNode = aIdx.GetNode().m_pStartOfSection;
|
|
|
|
// get all boxes with content because their indices
|
|
// pointing to the StartNodes need to be reset
|
|
// (copying the array and deleting all found ones eases
|
|
// searching)
|
|
SwNodeIndex aMvIdx( aRg.aEnd, 1 );
|
|
for( SwNodeOffset n(0); n < nInsPos; ++n )
|
|
{
|
|
SwNode* pNd = &aMvIdx.GetNode();
|
|
|
|
const bool bOutlNd = pNd->IsTextNode() && pNd->GetTextNode()->IsOutline();
|
|
// delete outline indices from old node array
|
|
if( bOutlNd )
|
|
m_aOutlineNodes.erase( pNd );
|
|
|
|
RemoveNode( aMvIdx.GetIndex(), SwNodeOffset(1), false );
|
|
pNd->m_pStartOfSection = pSttNode;
|
|
rNodes.InsertNode( pNd, aIdx );
|
|
|
|
// set correct indices in Start/EndNodes
|
|
if( bInsOutlineIdx && bOutlNd )
|
|
// and put them into the new node array
|
|
rNodes.m_aOutlineNodes.insert( pNd );
|
|
else if( pNd->IsStartNode() )
|
|
pSttNode = static_cast<SwStartNode*>(pNd);
|
|
else if( pNd->IsEndNode() )
|
|
{
|
|
pSttNode->m_pEndOfSection = static_cast<SwEndNode*>(pNd);
|
|
if( pSttNode->IsSectionNode() )
|
|
static_cast<SwSectionNode*>(pSttNode)->NodesArrChgd();
|
|
pSttNode = pSttNode->m_pStartOfSection;
|
|
}
|
|
}
|
|
|
|
if( auto pDDETable = dynamic_cast<SwDDETable*>(&pTableNd->GetTable()) )
|
|
{
|
|
SwDDEFieldType* pTyp = pDDETable->GetDDEFieldType();
|
|
if( pTyp )
|
|
{
|
|
if( rNodes.IsDocNodes() )
|
|
pTyp->IncRefCnt();
|
|
else
|
|
pTyp->DecRefCnt();
|
|
}
|
|
}
|
|
|
|
if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
|
|
{
|
|
SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
|
|
pTableFormat->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
|
|
}
|
|
}
|
|
if( bNewFrames )
|
|
{
|
|
pTableNd->MakeOwnFrames();
|
|
}
|
|
aIdx -= nInsPos;
|
|
nInsPos = SwNodeOffset(0);
|
|
}
|
|
else if( pSttNd->GetIndex() < aRg.aStart.GetIndex() )
|
|
{
|
|
// SectionNode: not the whole section will be moved, thus,
|
|
// move only the ContentNodes
|
|
// StartNode: create a new section at the given position
|
|
do { // middle check loop
|
|
if( !pSttNd->IsSectionNode() )
|
|
{
|
|
// create StartNode and EndNode at InsertPos
|
|
SwStartNode* pTmp = new SwStartNode( aIdx.GetNode(),
|
|
SwNodeType::Start,
|
|
/*?? NodeType ??*/ SwNormalStartNode );
|
|
|
|
nLevel++; // put the index to StartNode on the stack
|
|
aSttNdStack.insert( aSttNdStack.begin() + nLevel, pTmp );
|
|
|
|
// create EndNode
|
|
new SwEndNode( aIdx.GetNode(), *pTmp );
|
|
}
|
|
else if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(
|
|
rNodes))
|
|
{
|
|
// use placeholder in UndoNodes array
|
|
new SwPlaceholderNode(aIdx.GetNode());
|
|
}
|
|
else
|
|
{
|
|
// JP 18.5.2001 (Bug 70454) creating new section?
|
|
--aRg.aEnd;
|
|
break;
|
|
|
|
}
|
|
|
|
--aRg.aEnd;
|
|
--aIdx;
|
|
} while( false );
|
|
}
|
|
else
|
|
{
|
|
// move StartNode and EndNode in total
|
|
|
|
// if Start is exactly the Start of the area,
|
|
// then the Node needs to be re-visited
|
|
if( &aRg.aStart.GetNode() == pSttNd )
|
|
--aRg.aStart;
|
|
|
|
SwSectionNode* pSctNd = pSttNd->GetSectionNode();
|
|
if( bNewFrames && pSctNd )
|
|
{ // tdf#135056 skip over code in DelFrames() that moves
|
|
// SwNodeIndex around because in case of nested
|
|
// sections, m_pStartOfSection will point between
|
|
// undo nodes-array and doc nodes-array
|
|
pSctNd->DelFrames(nullptr, true);
|
|
}
|
|
|
|
RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), false ); // delete EndNode
|
|
SwNodeOffset nSttPos = pSttNd->GetIndex();
|
|
|
|
// this StartNode will be removed later
|
|
SwStartNode* pTmpSttNd = new SwStartNode( *this, nSttPos+1 );
|
|
pTmpSttNd->m_pStartOfSection = pSttNd->m_pStartOfSection;
|
|
|
|
RemoveNode( nSttPos, SwNodeOffset(1), false ); // delete SttNode
|
|
|
|
pSttNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
|
|
rNodes.InsertNode( pSttNd, aIdx );
|
|
rNodes.InsertNode( pCurrentNode, aIdx );
|
|
--aIdx;
|
|
pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
|
|
|
|
--aRg.aEnd;
|
|
|
|
nLevel++; // put the index pointing to the StartNode onto the stack
|
|
aSttNdStack.insert( aSttNdStack.begin() + nLevel, pSttNd );
|
|
|
|
// reset remaining indices if SectionNode
|
|
if( pSctNd )
|
|
{
|
|
pSctNd->NodesArrChgd();
|
|
++nSectNdCnt;
|
|
// tdf#132326 do not let frames survive in undo nodes
|
|
if (!GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
|
|
{
|
|
bNewFrames = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::Section:
|
|
if( !nLevel &&
|
|
GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
|
|
{
|
|
// here, a SectionDummyNode needs to be inserted at the current position
|
|
if( nInsPos ) // move everything until here
|
|
{
|
|
// delete and copy. CAUTION: all indices after
|
|
// "aRg.aEnd+1" will be moved as well!
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
aIdx -= nInsPos;
|
|
nInsPos = SwNodeOffset(0);
|
|
}
|
|
new SwPlaceholderNode(aIdx.GetNode());
|
|
--aRg.aEnd;
|
|
--aIdx;
|
|
break;
|
|
}
|
|
[[fallthrough]];
|
|
case SwNodeType::Table:
|
|
case SwNodeType::Start:
|
|
{
|
|
// empty section -> nothing to do
|
|
// and only if it's a top level section
|
|
if( !nInsPos && !nLevel )
|
|
{
|
|
--aRg.aEnd;
|
|
break;
|
|
}
|
|
|
|
if( !nLevel ) // level is decreasing
|
|
{
|
|
// create decrease
|
|
SwNodeIndex aTmpSIdx( aOrigInsPos.aStart, 1 );
|
|
SwStartNode* pTmpStt = new SwStartNode( aTmpSIdx.GetNode(),
|
|
SwNodeType::Start,
|
|
static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
|
|
|
|
--aTmpSIdx;
|
|
|
|
SwNodeIndex aTmpEIdx( aOrigInsPos.aEnd );
|
|
new SwEndNode( aTmpEIdx.GetNode(), *pTmpStt );
|
|
--aTmpEIdx;
|
|
++aTmpSIdx;
|
|
|
|
// set correct StartOfSection
|
|
++aRg.aEnd;
|
|
{
|
|
SwNodeIndex aCntIdx( aRg.aEnd );
|
|
for( SwNodeOffset n(0); n < nInsPos; n++, ++aCntIdx)
|
|
aCntIdx.GetNode().m_pStartOfSection = pTmpStt;
|
|
}
|
|
|
|
// also set correct StartNode for all decreased nodes
|
|
while( aTmpSIdx < aTmpEIdx )
|
|
if( nullptr != (( pCurrentNode = &aTmpEIdx.GetNode())->GetEndNode()) )
|
|
aTmpEIdx = pCurrentNode->StartOfSectionIndex();
|
|
else
|
|
{
|
|
pCurrentNode->m_pStartOfSection = pTmpStt;
|
|
--aTmpEIdx;
|
|
}
|
|
|
|
--aIdx; // after the inserted StartNode
|
|
--aRg.aEnd; // before StartNode
|
|
// copy array. CAUTION: all indices after
|
|
// "aRg.aEnd+1" will be moved as well!
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
aIdx -= nInsPos+1;
|
|
nInsPos = SwNodeOffset(0);
|
|
}
|
|
else // all nodes between StartNode and EndNode were moved
|
|
{
|
|
OSL_ENSURE( pCurrentNode == aSttNdStack[nLevel] ||
|
|
( pCurrentNode->IsStartNode() &&
|
|
aSttNdStack[nLevel]->IsSectionNode()),
|
|
"wrong StartNode" );
|
|
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
aIdx -= nInsPos+1; // before inserted StartNode
|
|
nInsPos = SwNodeOffset(0);
|
|
|
|
// remove pointer from node array
|
|
RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), true );
|
|
--aRg.aEnd;
|
|
|
|
SwSectionNode* pSectNd = aSttNdStack[ nLevel ]->GetSectionNode();
|
|
if( pSectNd && !--nSectNdCnt )
|
|
{
|
|
SwNodeIndex aTmp( *pSectNd );
|
|
pSectNd->MakeOwnFrames(&aTmp);
|
|
bNewFrames = bSaveNewFrames;
|
|
}
|
|
aSttNdStack.erase( aSttNdStack.begin() + nLevel ); // remove from stack
|
|
nLevel--;
|
|
}
|
|
|
|
// delete all resulting empty start/end node pairs
|
|
SwNode* pTmpNode = (*this)[ aRg.aEnd.GetIndex()+1 ]->GetEndNode();
|
|
if( pTmpNode && SwNodeType::Start == (pCurrentNode = &aRg.aEnd.GetNode())
|
|
->GetNodeType() && pCurrentNode->StartOfSectionIndex() &&
|
|
pTmpNode->StartOfSectionNode() == pCurrentNode )
|
|
{
|
|
DelNodes( aRg.aEnd, SwNodeOffset(2) );
|
|
--aRg.aEnd;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::Text:
|
|
//Add special function to text node.
|
|
{
|
|
if( bNewFrames && pCurrentNode->GetContentNode() )
|
|
static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
|
|
pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
|
|
nInsPos++;
|
|
--aRg.aEnd;
|
|
}
|
|
break;
|
|
case SwNodeType::Grf:
|
|
case SwNodeType::Ole:
|
|
{
|
|
if( bNewFrames && pCurrentNode->GetContentNode() )
|
|
static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
|
|
|
|
pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
|
|
nInsPos++;
|
|
--aRg.aEnd;
|
|
|
|
// reset Accessibility issue state
|
|
pCurrentNode->resetAndQueueAccessibilityCheck();
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::PlaceHolder:
|
|
if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
|
|
{
|
|
if( &rNodes == this ) // inside UndoNodesArray
|
|
{
|
|
// move everything
|
|
pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
|
|
nInsPos++;
|
|
}
|
|
else // move into "normal" node array
|
|
{
|
|
// than a SectionNode (start/end) is needed at the current
|
|
// InsPos; if so skip it, otherwise ignore current node
|
|
if( nInsPos ) // move everything until here
|
|
{
|
|
// delete and copy. CAUTION: all indices after
|
|
// "aRg.aEnd+1" will be moved as well!
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
aIdx -= nInsPos;
|
|
nInsPos = SwNodeOffset(0);
|
|
}
|
|
SwNode* pTmpNd = &aIdx.GetNode();
|
|
if( pTmpNd->IsSectionNode() ||
|
|
pTmpNd->StartOfSectionNode()->IsSectionNode() )
|
|
--aIdx; // skip
|
|
}
|
|
}
|
|
else {
|
|
assert(!"How can this node be in the node array?");
|
|
}
|
|
--aRg.aEnd;
|
|
break;
|
|
|
|
default:
|
|
assert(!"Unknown node type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( nInsPos ) // copy remaining rest
|
|
{
|
|
// rest should be ok
|
|
SwNodeIndex aSwIndex( aRg.aEnd, 1 );
|
|
ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
|
|
}
|
|
++aRg.aEnd; // again, exclusive end
|
|
|
|
// delete all resulting empty start/end node pairs
|
|
if( ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
|
|
pCurrentNode->StartOfSectionIndex() &&
|
|
aRg.aEnd.GetNode().GetEndNode() )
|
|
DelNodes( aRg.aStart, SwNodeOffset(2) );
|
|
|
|
// initialize numbering update
|
|
++aOrigInsPos.aStart;
|
|
// Moved in same node array? Then call update top down!
|
|
if( this == &rNodes &&
|
|
aRg.aEnd.GetIndex() >= aOrigInsPos.aStart.GetIndex() )
|
|
{
|
|
UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
|
|
UpdateOutlineIdx( aRg.aEnd.GetNode() );
|
|
}
|
|
else
|
|
{
|
|
UpdateOutlineIdx( aRg.aEnd.GetNode() );
|
|
rNodes.UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** create a start/end section pair
|
|
*
|
|
* Other nodes might be in between.
|
|
*
|
|
* After this method call, the start node of pRange will be pointing to the
|
|
* first node after the start section node and the end node will be the index
|
|
* of the end section node. If this method is called multiple times with the
|
|
* same input, multiple sections containing the previous ones will be created
|
|
* (no content nodes between start or end node).
|
|
*
|
|
* @note Start and end node of the range must be on the same level but MUST
|
|
* NOT be on the top level.
|
|
*
|
|
* @param [IN,OUT] pRange the range (excl. end)
|
|
* @param eSttNdTyp type of the start node
|
|
*/
|
|
void SwNodes::SectionDown(SwNodeRange *pRange, SwStartNodeType eSttNdTyp )
|
|
{
|
|
if( pRange->aStart >= pRange->aEnd ||
|
|
pRange->aEnd >= Count() ||
|
|
!::CheckNodesRange(pRange->aStart.GetNode(), pRange->aEnd.GetNode(), false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the beginning of a range is before or at a start node position, so
|
|
// delete it, otherwise empty S/E or E/S nodes would be created.
|
|
// For other nodes, create a new start node.
|
|
SwNode * pCurrentNode = &pRange->aStart.GetNode();
|
|
SwNodeIndex aTmpIdx( *pCurrentNode->StartOfSectionNode() );
|
|
|
|
if( pCurrentNode->GetEndNode() )
|
|
DelNodes( pRange->aStart ); // prevent empty section
|
|
else
|
|
{
|
|
// insert a new StartNode
|
|
SwNode* pSttNd = new SwStartNode( pRange->aStart.GetNode(), SwNodeType::Start, eSttNdTyp );
|
|
pRange->aStart = *pSttNd;
|
|
aTmpIdx = pRange->aStart;
|
|
}
|
|
|
|
// If the end of a range is before or at a StartNode, so delete it,
|
|
// otherwise empty S/E or E/S nodes would be created.
|
|
// For other nodes, insert a new end node.
|
|
--pRange->aEnd;
|
|
if( pRange->aEnd.GetNode().GetStartNode() )
|
|
DelNodes( pRange->aEnd );
|
|
else
|
|
{
|
|
++pRange->aEnd;
|
|
// insert a new EndNode
|
|
new SwEndNode( pRange->aEnd.GetNode(), *pRange->aStart.GetNode().GetStartNode() );
|
|
}
|
|
--pRange->aEnd;
|
|
|
|
SectionUpDown( aTmpIdx, pRange->aEnd );
|
|
}
|
|
|
|
/** increase level of the given range
|
|
*
|
|
* The range contained in pRange will be lifted to the next higher level.
|
|
* This is done by adding an end node at pRange.start and a start node at
|
|
* pRange.end. Furthermore all indices for this range will be updated.
|
|
*
|
|
* After this method call, the start node of pRange will be pointing to the
|
|
* first node inside the lifted range and the end node will be pointing to the
|
|
* last position inside the lifted range.
|
|
*
|
|
* @param [IN,OUT] pRange the range of nodes where the level should be increased
|
|
*/
|
|
void SwNodes::SectionUp(SwNodeRange *pRange)
|
|
{
|
|
if( pRange->aStart >= pRange->aEnd ||
|
|
pRange->aEnd >= Count() ||
|
|
!::CheckNodesRange(pRange->aStart.GetNode(), pRange->aEnd.GetNode(), false) ||
|
|
( HighestLevel( *this, *pRange ) <= 1 ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the beginning of a range is before or at a start node position, so
|
|
// delete it, otherwise empty S/E or E/S nodes would be created.
|
|
// For other nodes, create a new start node.
|
|
SwNode * pCurrentNode = &pRange->aStart.GetNode();
|
|
SwNodeIndex aIdx( *pCurrentNode->StartOfSectionNode() );
|
|
if( pCurrentNode->IsStartNode() ) // is StartNode itself
|
|
{
|
|
SwEndNode* pEndNd = pRange->aEnd.GetNode().GetEndNode();
|
|
if (pEndNd && pCurrentNode == pEndNd->m_pStartOfSection)
|
|
{
|
|
// there was a pairwise reset, adjust only those in the range
|
|
SwStartNode* pTmpSttNd = pCurrentNode->m_pStartOfSection;
|
|
RemoveNode( pRange->aStart.GetIndex(), SwNodeOffset(1), true );
|
|
RemoveNode( pRange->aEnd.GetIndex(), SwNodeOffset(1), true );
|
|
|
|
SwNodeIndex aTmpIdx( pRange->aStart );
|
|
while( aTmpIdx < pRange->aEnd )
|
|
{
|
|
pCurrentNode = &aTmpIdx.GetNode();
|
|
pCurrentNode->m_pStartOfSection = pTmpSttNd;
|
|
if( pCurrentNode->IsStartNode() )
|
|
aTmpIdx = pCurrentNode->EndOfSectionIndex() + 1;
|
|
else
|
|
++aTmpIdx;
|
|
}
|
|
return ;
|
|
}
|
|
DelNodes( pRange->aStart );
|
|
}
|
|
else if( aIdx == pRange->aStart.GetIndex()-1 ) // before StartNode
|
|
DelNodes( aIdx );
|
|
else
|
|
new SwEndNode( pRange->aStart.GetNode(), *aIdx.GetNode().GetStartNode() );
|
|
|
|
// If the end of a range is before or at a StartNode, so delete it,
|
|
// otherwise empty S/E or E/S nodes would be created.
|
|
// For other nodes, insert a new end node.
|
|
SwNodeIndex aTmpIdx( pRange->aEnd );
|
|
if( pRange->aEnd.GetNode().IsEndNode() )
|
|
DelNodes( pRange->aEnd );
|
|
else
|
|
{
|
|
new SwStartNode( pRange->aEnd.GetNode() );
|
|
/*?? which NodeType ??*/
|
|
aTmpIdx = *pRange->aEnd.GetNode().EndOfSectionNode();
|
|
--pRange->aEnd;
|
|
}
|
|
|
|
SectionUpDown( aIdx, aTmpIdx );
|
|
}
|
|
|
|
/** correct indices after movement
|
|
*
|
|
* Update all indices after movement so that the levels are consistent again.
|
|
*
|
|
* @param aStart index of the start node
|
|
* @param aEnd index of the end point
|
|
*
|
|
* @see SwNodes::SectionUp
|
|
* @see SwNodes::SectionDown
|
|
*/
|
|
void SwNodes::SectionUpDown( const SwNodeIndex & aStart, const SwNodeIndex & aEnd )
|
|
{
|
|
SwNodeIndex aTmpIdx( aStart, +1 );
|
|
// array forms a stack, holding all StartOfSelections
|
|
SwStartNodePointers aSttNdStack;
|
|
SwStartNode* pTmp = aStart.GetNode().GetStartNode();
|
|
aSttNdStack.push_back( pTmp );
|
|
|
|
// loop until the first start node that needs to be change was found
|
|
// (the indices are updated from the end node backwards to the start)
|
|
for( ;; ++aTmpIdx )
|
|
{
|
|
SwNode * pCurrentNode = &aTmpIdx.GetNode();
|
|
pCurrentNode->m_pStartOfSection = aSttNdStack[ aSttNdStack.size()-1 ];
|
|
|
|
if( pCurrentNode->GetStartNode() )
|
|
{
|
|
pTmp = static_cast<SwStartNode*>(pCurrentNode);
|
|
aSttNdStack.push_back( pTmp );
|
|
}
|
|
else if( pCurrentNode->GetEndNode() )
|
|
{
|
|
SwStartNode* pSttNd = aSttNdStack[ aSttNdStack.size() - 1 ];
|
|
pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
|
|
aSttNdStack.pop_back();
|
|
if( !aSttNdStack.empty() )
|
|
continue; // still enough EndNodes on the stack
|
|
|
|
else if( aTmpIdx < aEnd ) // too many StartNodes
|
|
// if the end is not reached, yet, get the start of the section above
|
|
{
|
|
aSttNdStack.insert( aSttNdStack.begin(), pSttNd->m_pStartOfSection );
|
|
}
|
|
else // finished, as soon as out of the range
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwNodes::Delete(const SwNodeIndex &rIndex, SwNodeOffset nNodes)
|
|
{
|
|
Delete(rIndex.GetNode(), nNodes);
|
|
}
|
|
|
|
/** delete nodes
|
|
*
|
|
* This is a specific implementation of a delete function for a variable array.
|
|
* It is necessary as there might be inconsistencies after deleting start or
|
|
* end nodes. This method can clean those up.
|
|
*
|
|
* @param rIndex position to delete at (unchanged afterwards)
|
|
* @param nNodes number of nodes to delete (default: 1)
|
|
*/
|
|
void SwNodes::Delete(const SwNode &rIndex, SwNodeOffset nNodes)
|
|
{
|
|
int nLevel = 0; // level counter
|
|
SwNode * pCurrentNode;
|
|
|
|
SwNodeOffset nCnt = Count() - rIndex.GetIndex() - 1;
|
|
if( nCnt > nNodes ) nCnt = nNodes;
|
|
|
|
if( nCnt == SwNodeOffset(0) ) // no count -> return
|
|
return;
|
|
|
|
SwNodeRange aRg( rIndex, SwNodeOffset(0), rIndex, nCnt-1 );
|
|
// check if [rIndex..rIndex + nCnt] is larger than the range
|
|
if( ( !aRg.aStart.GetNode().StartOfSectionIndex() &&
|
|
!aRg.aStart.GetIndex() ) ||
|
|
!::CheckNodesRange(aRg.aStart.GetNode(), aRg.aEnd.GetNode(), false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if aEnd is not on a ContentNode, search the previous one
|
|
while( ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() ||
|
|
( pCurrentNode->GetEndNode() &&
|
|
!pCurrentNode->m_pStartOfSection->IsTableNode() ))
|
|
--aRg.aEnd;
|
|
|
|
nCnt = SwNodeOffset(0);
|
|
//TODO: check/improve comment
|
|
// increase start so that we are able to use "<" (using "<=" might cause
|
|
// problems if aEnd == aStart and aEnd is deleted, so aEnd <= aStart)
|
|
--aRg.aStart;
|
|
|
|
bool bSaveInNodesDel = m_bInNodesDel;
|
|
m_bInNodesDel = true;
|
|
bool bUpdateOutline = false;
|
|
|
|
// loop until everything is deleted
|
|
while( aRg.aStart < aRg.aEnd )
|
|
{
|
|
pCurrentNode = &aRg.aEnd.GetNode();
|
|
|
|
if( pCurrentNode->GetEndNode() )
|
|
{
|
|
// delete the whole section?
|
|
if( pCurrentNode->StartOfSectionIndex() > aRg.aStart.GetIndex() )
|
|
{
|
|
SwTableNode* pTableNd = pCurrentNode->m_pStartOfSection->GetTableNode();
|
|
if( pTableNd )
|
|
pTableNd->DelFrames();
|
|
|
|
SwNode *pNd, *pChkNd = pCurrentNode->m_pStartOfSection;
|
|
SwOutlineNodes::size_type nIdxPos;
|
|
do {
|
|
pNd = &aRg.aEnd.GetNode();
|
|
|
|
if( pNd->IsTextNode() )
|
|
{
|
|
SwTextNode *const pTextNode(pNd->GetTextNode());
|
|
if (pTextNode->IsOutline() &&
|
|
m_aOutlineNodes.Seek_Entry( pNd, &nIdxPos ))
|
|
{
|
|
// remove outline indices
|
|
m_aOutlineNodes.erase_at(nIdxPos);
|
|
bUpdateOutline = true;
|
|
}
|
|
pTextNode->InvalidateNumRule();
|
|
}
|
|
else if( pNd->IsEndNode() &&
|
|
pNd->m_pStartOfSection->IsTableNode() )
|
|
static_cast<SwTableNode*>(pNd->m_pStartOfSection)->DelFrames();
|
|
|
|
--aRg.aEnd;
|
|
nCnt++;
|
|
|
|
} while( pNd != pChkNd );
|
|
}
|
|
else
|
|
{
|
|
RemoveNode( aRg.aEnd.GetIndex()+1, nCnt, true ); // delete
|
|
nCnt = SwNodeOffset(0);
|
|
--aRg.aEnd; // before the EndNode
|
|
nLevel++;
|
|
}
|
|
}
|
|
else if( pCurrentNode->GetStartNode() ) // found StartNode
|
|
{
|
|
if( nLevel == 0 ) // decrease one level
|
|
{
|
|
if( nCnt )
|
|
{
|
|
// now delete array
|
|
++aRg.aEnd;
|
|
RemoveNode( aRg.aEnd.GetIndex(), nCnt, true );
|
|
nCnt = SwNodeOffset(0);
|
|
}
|
|
}
|
|
else // remove all nodes between start and end node (incl. both)
|
|
{
|
|
RemoveNode( aRg.aEnd.GetIndex(), nCnt + 2, true ); // delete array
|
|
nCnt = SwNodeOffset(0);
|
|
nLevel--;
|
|
}
|
|
|
|
// after deletion, aEnd might point to an EndNode...
|
|
// delete all empty start/end node pairs
|
|
SwNode* pTmpNode = aRg.aEnd.GetNode().GetEndNode();
|
|
--aRg.aEnd;
|
|
while( pTmpNode &&
|
|
( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
|
|
pCurrentNode->StartOfSectionIndex() )
|
|
{
|
|
// remove end and start node
|
|
DelNodes( aRg.aEnd, SwNodeOffset(2) );
|
|
pTmpNode = aRg.aEnd.GetNode().GetEndNode();
|
|
--aRg.aEnd;
|
|
}
|
|
}
|
|
else // "normal" node, so insert into TmpArray
|
|
{
|
|
SwTextNode* pTextNd = pCurrentNode->GetTextNode();
|
|
if( pTextNd )
|
|
{
|
|
if( pTextNd->IsOutline())
|
|
{
|
|
// delete outline indices
|
|
m_aOutlineNodes.erase( pTextNd );
|
|
bUpdateOutline = true;
|
|
}
|
|
if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
|
|
{
|
|
pTextNd->InvalidateNumRule();
|
|
}
|
|
}
|
|
else if( pCurrentNode->IsContentNode() )
|
|
static_cast<SwContentNode*>(pCurrentNode)->InvalidateNumRule();
|
|
|
|
--aRg.aEnd;
|
|
nCnt++;
|
|
}
|
|
}
|
|
|
|
++aRg.aEnd;
|
|
if( nCnt != SwNodeOffset(0) )
|
|
RemoveNode( aRg.aEnd.GetIndex(), nCnt, true ); // delete the rest
|
|
|
|
// delete all empty start/end node pairs
|
|
while( aRg.aEnd.GetNode().GetEndNode() &&
|
|
( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
|
|
pCurrentNode->StartOfSectionIndex() )
|
|
// but none of the holy 5. (???)
|
|
{
|
|
DelNodes( aRg.aStart, SwNodeOffset(2) ); // delete start and end node
|
|
--aRg.aStart;
|
|
}
|
|
|
|
m_bInNodesDel = bSaveInNodesDel;
|
|
|
|
if( !m_bInNodesDel )
|
|
{
|
|
// update numbering
|
|
if( bUpdateOutline || m_bInDelUpdOutline )
|
|
{
|
|
UpdateOutlineIdx( aRg.aEnd.GetNode() );
|
|
m_bInDelUpdOutline = false;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if( bUpdateOutline )
|
|
m_bInDelUpdOutline = true;
|
|
}
|
|
}
|
|
|
|
/** get section level at the given position
|
|
*
|
|
* @note The first node in an array should always be a start node.
|
|
* Because of this, there is a special treatment here based on the
|
|
* assumption that this is true in this context as well.
|
|
*
|
|
* @param rIdx position of the node
|
|
* @return section level at the given position
|
|
*/
|
|
sal_uInt16 SwNodes::GetSectionLevel(const SwNode &rIdx)
|
|
{
|
|
// special treatment for 1st Node
|
|
if(rIdx.GetIndex() == SwNodeOffset(0)) return 1;
|
|
// no recursion! This calls a SwNode::GetSectionLevel (missing "s")
|
|
return rIdx.GetSectionLevel();
|
|
}
|
|
|
|
void SwNodes::GoStartOfSection(SwNodeIndex *pIdx)
|
|
{
|
|
// after the next start node
|
|
SwNodeIndex aTmp( *pIdx->GetNode().StartOfSectionNode(), +1 );
|
|
|
|
// If index points to no ContentNode, then go to one.
|
|
// If there is no further available, do not change the index' position!
|
|
while( !aTmp.GetNode().IsContentNode() )
|
|
{ // go from this StartNode (can only be one) to its end
|
|
if( *pIdx <= aTmp )
|
|
return; // ERROR: already after the section
|
|
aTmp = aTmp.GetNode().EndOfSectionIndex()+1;
|
|
if( *pIdx <= aTmp )
|
|
return; // ERROR: already after the section
|
|
}
|
|
(*pIdx) = aTmp; // is on a ContentNode
|
|
}
|
|
|
|
void SwNodes::GoEndOfSection(SwNodeIndex *pIdx)
|
|
{
|
|
if( !pIdx->GetNode().IsEndNode() )
|
|
(*pIdx) = *pIdx->GetNode().EndOfSectionNode();
|
|
}
|
|
|
|
static SwContentNode* goNext(const SwNodeIndex& rIdx)
|
|
{
|
|
const SwNodes& rNodes = rIdx.GetNodes();
|
|
const SwNodeOffset last = rNodes.Count() - 1;
|
|
for (SwNodeOffset i(rIdx.GetIndex() + 1); i < last; ++i)
|
|
if (SwContentNode* pNd = rNodes[i]->GetContentNode())
|
|
return pNd;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx)
|
|
{
|
|
SwContentNode* pNd = goNext(*pIdx);
|
|
if (pNd)
|
|
*pIdx = *pNd;
|
|
return pNd;
|
|
}
|
|
|
|
SwContentNode* SwNodes::GoNext(SwPosition *pIdx)
|
|
{
|
|
SwContentNode* pNd = goNext(pIdx->nNode);
|
|
if (pNd)
|
|
pIdx->AssignStartIndex(*pNd);
|
|
return pNd;
|
|
}
|
|
|
|
static SwNodeOffset startOfGlobalSection(const SwNode& node)
|
|
{
|
|
const SwNodes& rNodes = node.GetNodes();
|
|
const SwNodeOffset pos = node.GetIndex();
|
|
if (rNodes.GetEndOfExtras().GetIndex() < pos)
|
|
// Regular ContentSection
|
|
return rNodes.GetEndOfExtras().GetIndex() + SwNodeOffset(1);
|
|
if (rNodes.GetEndOfAutotext().GetIndex() < pos)
|
|
// Redlines
|
|
return rNodes.GetEndOfAutotext().GetIndex() + SwNodeOffset(1);
|
|
if (rNodes.GetEndOfInserts().GetIndex() < pos)
|
|
{
|
|
// Flys/Headers/Footers
|
|
if (auto* p = node.FindFlyStartNode())
|
|
return p->GetIndex();
|
|
if (auto* p = node.FindHeaderStartNode())
|
|
return p->GetIndex();
|
|
if (auto* p = node.FindFooterStartNode())
|
|
return p->GetIndex();
|
|
return rNodes.GetEndOfInserts().GetIndex() + SwNodeOffset(1);
|
|
}
|
|
if (rNodes.GetEndOfPostIts().GetIndex() < pos)
|
|
{
|
|
// Footnotes
|
|
if (auto* p = node.FindFootnoteStartNode())
|
|
return p->GetIndex();
|
|
return rNodes.GetEndOfPostIts().GetIndex() + SwNodeOffset(1);
|
|
}
|
|
return SwNodeOffset(0);
|
|
}
|
|
|
|
static SwContentNode* goPrevious(const SwNodeIndex& rIdx, bool canCrossBoundary)
|
|
{
|
|
const SwNodes& rNodes = rIdx.GetNodes();
|
|
const SwNodeOffset first(canCrossBoundary ? SwNodeOffset(0)
|
|
: startOfGlobalSection(rIdx.GetNode()));
|
|
for (SwNodeOffset i(rIdx.GetIndex() - 1); i > first; --i)
|
|
if (SwContentNode* pNd = rNodes[i]->GetContentNode())
|
|
return pNd;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SwContentNode* SwNodes::GoPrevious(SwNodeIndex* pIdx, bool canCrossBoundary)
|
|
{
|
|
SwContentNode* pNd = goPrevious(*pIdx, canCrossBoundary);
|
|
if (pNd)
|
|
*pIdx = *pNd;
|
|
return pNd;
|
|
}
|
|
|
|
SwContentNode* SwNodes::GoPrevious(SwPosition* pIdx, bool canCrossBoundary)
|
|
{
|
|
SwContentNode* pNd = goPrevious(pIdx->nNode, canCrossBoundary);
|
|
if (pNd)
|
|
pIdx->AssignStartIndex(*pNd);
|
|
return pNd;
|
|
}
|
|
|
|
/** Delete a number of nodes
|
|
*
|
|
* @param rStart starting position in this nodes array
|
|
* @param nCnt number of nodes to delete
|
|
*/
|
|
void SwNodes::DelNodes( const SwNodeIndex & rStart, SwNodeOffset nCnt )
|
|
{
|
|
SwNodeOffset nSttIdx = rStart.GetIndex();
|
|
|
|
if( !nSttIdx && nCnt == GetEndOfContent().GetIndex()+1 )
|
|
{
|
|
// The whole nodes array will be destroyed, you're in the Doc's DTOR!
|
|
// The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
|
|
SwNode* aEndNdArr[] = { m_pEndOfContent.get(),
|
|
m_pEndOfPostIts, m_pEndOfInserts,
|
|
m_pEndOfAutotext, m_pEndOfRedlines,
|
|
nullptr
|
|
};
|
|
|
|
SwNode** ppEndNdArr = aEndNdArr;
|
|
while( *ppEndNdArr )
|
|
{
|
|
nSttIdx = (*ppEndNdArr)->StartOfSectionIndex() + 1;
|
|
SwNodeOffset nEndIdx = (*ppEndNdArr)->GetIndex();
|
|
|
|
if( nSttIdx != nEndIdx )
|
|
RemoveNode( nSttIdx, nEndIdx - nSttIdx, true );
|
|
|
|
++ppEndNdArr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int bUpdateNum = 0;
|
|
for( SwNodeOffset n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n )
|
|
{
|
|
SwNode* pNd = (*this)[ n ];
|
|
|
|
if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline())
|
|
{
|
|
// remove the outline indices
|
|
if (m_aOutlineNodes.erase(pNd))
|
|
bUpdateNum = 1;
|
|
}
|
|
if( pNd->IsContentNode() )
|
|
{
|
|
static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
|
|
static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
|
|
}
|
|
}
|
|
RemoveNode( nSttIdx, nCnt, true );
|
|
|
|
// update numbering
|
|
if( bUpdateNum )
|
|
UpdateOutlineIdx( rStart.GetNode() );
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct HighLevel
|
|
{
|
|
sal_uInt16 nLevel, nTop;
|
|
explicit HighLevel( sal_uInt16 nLv ) : nLevel( nLv ), nTop( nLv ) {}
|
|
};
|
|
|
|
}
|
|
|
|
static bool lcl_HighestLevel( SwNode* pNode, void * pPara )
|
|
{
|
|
HighLevel * pHL = static_cast<HighLevel*>(pPara);
|
|
if( pNode->GetStartNode() )
|
|
{
|
|
pHL->nLevel++;
|
|
if( pHL->nTop > pHL->nLevel )
|
|
pHL->nTop = pHL->nLevel;
|
|
}
|
|
else if( pNode->GetEndNode() )
|
|
pHL->nLevel--;
|
|
return true;
|
|
|
|
}
|
|
|
|
/** Calculate the highest level in a range
|
|
*
|
|
* @param rNodes the nodes array
|
|
* @param rRange the range to inspect
|
|
* @return the highest level
|
|
*/
|
|
sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange )
|
|
{
|
|
HighLevel aPara( SwNodes::GetSectionLevel( rRange.aStart.GetNode() ));
|
|
rNodes.ForEach( rRange.aStart, rRange.aEnd, lcl_HighestLevel, &aPara );
|
|
return aPara.nTop;
|
|
|
|
}
|
|
|
|
/** move a range
|
|
*
|
|
* @param rPam the range to move
|
|
* @param rPos to destination position in the given nodes array
|
|
* @param rNodes the node array to move the range into
|
|
*/
|
|
void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes )
|
|
{
|
|
auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
|
|
|
|
if( !rPam.HasMark() || *pStt >= *pEnd )
|
|
return;
|
|
|
|
if( this == &rNodes && *pStt <= rPos && rPos < *pEnd )
|
|
return;
|
|
|
|
SwNodeIndex aEndIdx( pEnd->GetNode() );
|
|
SwNodeIndex aSttIdx( pStt->GetNode() );
|
|
SwTextNode *const pSrcNd = aSttIdx.GetNode().GetTextNode();
|
|
SwTextNode * pDestNd = rPos.GetNode().GetTextNode();
|
|
bool bSplitDestNd = true;
|
|
bool bCopyCollFormat = pDestNd && pDestNd->GetText().isEmpty();
|
|
|
|
if( pSrcNd )
|
|
{
|
|
// if the first node is a TextNode, then there must
|
|
// be also a TextNode in the NodesArray to store the content
|
|
if( !pDestNd )
|
|
{
|
|
pDestNd = rNodes.MakeTextNode( rPos.GetNode(), pSrcNd->GetTextColl() );
|
|
--rPos.nNode;
|
|
rPos.nContent.Assign( pDestNd, 0 );
|
|
bCopyCollFormat = true;
|
|
}
|
|
bSplitDestNd = pDestNd->Len() > rPos.GetContentIndex() ||
|
|
pEnd->GetNode().IsTextNode();
|
|
|
|
// move the content into the new node
|
|
bool bOneNd = pStt->GetNode() == pEnd->GetNode();
|
|
const sal_Int32 nLen =
|
|
( bOneNd ? std::min(pEnd->GetContentIndex(), pSrcNd->Len()) : pSrcNd->Len() )
|
|
- pStt->GetContentIndex();
|
|
|
|
if( !pEnd->GetNode().IsContentNode() )
|
|
{
|
|
bOneNd = true;
|
|
SwNodeOffset nSttNdIdx = pStt->GetNodeIndex() + 1;
|
|
const SwNodeOffset nEndNdIdx = pEnd->GetNodeIndex();
|
|
for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
|
|
{
|
|
if( (*this)[ nSttNdIdx ]->IsContentNode() )
|
|
{
|
|
bOneNd = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// templates must be copied/set after a split
|
|
if( !bOneNd && bSplitDestNd )
|
|
{
|
|
if( !rPos.GetContentIndex() )
|
|
{
|
|
bCopyCollFormat = true;
|
|
}
|
|
if( rNodes.IsDocNodes() )
|
|
{
|
|
SwDoc& rInsDoc = pDestNd->GetDoc();
|
|
::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
|
|
rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
|
|
}
|
|
else
|
|
{
|
|
pDestNd->SplitContentNode(rPos, nullptr);
|
|
}
|
|
|
|
if( rPos.GetNode() == aEndIdx.GetNode() )
|
|
{
|
|
--aEndIdx;
|
|
}
|
|
bSplitDestNd = true;
|
|
|
|
pDestNd = rNodes[ rPos.GetNodeIndex() - 1 ]->GetTextNode();
|
|
if( nLen )
|
|
{
|
|
pSrcNd->CutText( pDestNd, SwContentIndex( pDestNd, pDestNd->Len()),
|
|
pStt->nContent, nLen );
|
|
}
|
|
}
|
|
else if ( nLen )
|
|
{
|
|
pSrcNd->CutText( pDestNd, rPos.nContent, pStt->nContent, nLen );
|
|
}
|
|
|
|
if( bCopyCollFormat )
|
|
{
|
|
SwDoc& rInsDoc = pDestNd->GetDoc();
|
|
::sw::UndoGuard const undoGuard(rInsDoc.GetIDocumentUndoRedo());
|
|
pSrcNd->CopyCollFormat( *pDestNd );
|
|
}
|
|
|
|
if( bOneNd )
|
|
{
|
|
// Correct the PaM, because it might have happened that the move
|
|
// went over the node borders (so the data might be in different nodes).
|
|
// Also, a selection is invalidated.
|
|
pEnd->nContent = pStt->nContent;
|
|
rPam.DeleteMark();
|
|
GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
|
|
rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
|
|
return;
|
|
}
|
|
|
|
++aSttIdx;
|
|
}
|
|
else if( pDestNd )
|
|
{
|
|
if( rPos.GetContentIndex() )
|
|
{
|
|
if( rPos.GetContentIndex() == pDestNd->Len() )
|
|
{
|
|
++rPos.nNode;
|
|
}
|
|
else if( rPos.GetContentIndex() )
|
|
{
|
|
// if the EndNode is split than correct the EndIdx
|
|
const bool bCorrEnd = aEndIdx == rPos.nNode;
|
|
|
|
// if no text is attached to the TextNode, split it
|
|
if( rNodes.IsDocNodes() )
|
|
{
|
|
SwDoc& rInsDoc = pDestNd->GetDoc();
|
|
::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
|
|
rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
|
|
}
|
|
else
|
|
{
|
|
pDestNd->SplitContentNode(rPos, nullptr);
|
|
}
|
|
|
|
if ( bCorrEnd )
|
|
{
|
|
--aEndIdx;
|
|
}
|
|
}
|
|
}
|
|
// at the end only an empty TextNode is left over
|
|
bSplitDestNd = true;
|
|
}
|
|
|
|
SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
|
|
if ( pEndSrcNd )
|
|
{
|
|
// at the end of this range a new TextNode will be created
|
|
if( !bSplitDestNd )
|
|
{
|
|
if( rPos.GetNode() < rNodes.GetEndOfContent() )
|
|
{
|
|
++rPos.nNode;
|
|
}
|
|
|
|
pDestNd =
|
|
rNodes.MakeTextNode( rPos.GetNode(), pEndSrcNd->GetTextColl() );
|
|
--rPos.nNode;
|
|
rPos.nContent.Assign( pDestNd, 0 );
|
|
}
|
|
else
|
|
{
|
|
pDestNd = rPos.GetNode().GetTextNode();
|
|
}
|
|
|
|
if (pDestNd && pEnd->GetContentIndex())
|
|
{
|
|
// move the content into the new node
|
|
SwContentIndex aIdx( pEndSrcNd, 0 );
|
|
pEndSrcNd->CutText( pDestNd, rPos.nContent, aIdx,
|
|
pEnd->GetContentIndex());
|
|
}
|
|
|
|
if (pDestNd && bCopyCollFormat)
|
|
{
|
|
SwDoc& rInsDoc = pDestNd->GetDoc();
|
|
::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
|
|
pEndSrcNd->CopyCollFormat( *pDestNd );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
|
|
{
|
|
++aEndIdx;
|
|
}
|
|
if( !bSplitDestNd )
|
|
{
|
|
rPos.Adjust(SwNodeOffset(1));
|
|
}
|
|
}
|
|
|
|
if( aEndIdx != aSttIdx )
|
|
{
|
|
// move the nodes into the NodesArray
|
|
const SwNodeOffset nSttDiff = aSttIdx.GetIndex() - pStt->GetNodeIndex();
|
|
SwNodeRange aRg( aSttIdx, aEndIdx );
|
|
MoveNodes( aRg, rNodes, rPos.GetNode() );
|
|
|
|
// if in the same node array, all indices are now at new positions (so correct them)
|
|
if( &rNodes == this )
|
|
{
|
|
pStt->nNode = aRg.aEnd.GetIndex() - nSttDiff;
|
|
}
|
|
}
|
|
|
|
// if the StartNode was moved to whom the cursor pointed, so
|
|
// the content must be registered in the current content!
|
|
if ( pStt->GetNode() == GetEndOfContent() )
|
|
{
|
|
const bool bSuccess = GoPrevious( &pStt->nNode );
|
|
OSL_ENSURE( bSuccess, "Move() - no ContentNode here" );
|
|
}
|
|
pStt->nContent.Assign( pStt->GetNode().GetContentNode(),
|
|
pStt->GetContentIndex() );
|
|
// Correct the PaM, because it might have happened that the move
|
|
// went over the node borders (so the data might be in different nodes).
|
|
// Also, a selection is invalidated.
|
|
*pEnd = *pStt;
|
|
rPam.DeleteMark();
|
|
GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
|
|
rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
|
|
}
|
|
|
|
///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
|
|
void SwNodes::CopyNodes( const SwNodeRange& rRange,
|
|
SwNode& rPos, bool bNewFrames, bool bTableInsDummyNode ) const
|
|
{
|
|
SwDoc& rDoc = rPos.GetDoc();
|
|
|
|
SwNode * pCurrentNode;
|
|
if( rPos.GetIndex() == SwNodeOffset(0) ||
|
|
( (pCurrentNode = &rPos)->GetStartNode() &&
|
|
!pCurrentNode->StartOfSectionIndex() ))
|
|
return;
|
|
|
|
SwNodeRange aRg( rRange );
|
|
|
|
// skip "simple" StartNodes or EndNodes
|
|
while( SwNodeType::Start == (pCurrentNode = & aRg.aStart.GetNode())->GetNodeType()
|
|
|| ( pCurrentNode->IsEndNode() &&
|
|
!pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
|
|
++aRg.aStart;
|
|
|
|
const SwNode *aEndNode = &aRg.aEnd.GetNode();
|
|
SwNodeOffset nIsEndOfContent((aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0);
|
|
|
|
if (SwNodeOffset(0) == nIsEndOfContent)
|
|
{
|
|
// if aEnd-1 points to no ContentNode, search previous one
|
|
--aRg.aEnd;
|
|
// #i107142#: if aEnd is start node of a special section, do nothing.
|
|
// Otherwise this could lead to crash: going through all previous
|
|
// special section nodes and then one before the first.
|
|
if (aRg.aEnd.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
|
|
{
|
|
while( ((pCurrentNode = & aRg.aEnd.GetNode())->GetStartNode() &&
|
|
!pCurrentNode->IsSectionNode() ) ||
|
|
( pCurrentNode->IsEndNode() &&
|
|
SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) )
|
|
{
|
|
--aRg.aEnd;
|
|
}
|
|
}
|
|
++aRg.aEnd;
|
|
}
|
|
|
|
// is there anything left to copy?
|
|
if( aRg.aStart >= aRg.aEnd )
|
|
return;
|
|
|
|
// when inserting into the source range, nothing need to be done
|
|
assert(&aRg.aStart.GetNodes() == this);
|
|
assert(&aRg.aStart.GetNodes() == &aRg.aEnd.GetNodes());
|
|
if( &rPos.GetNodes() == &aRg.aStart.GetNodes() &&
|
|
rPos.GetIndex() >= aRg.aStart.GetIndex() &&
|
|
rPos.GetIndex() < aRg.aEnd.GetIndex() )
|
|
return;
|
|
|
|
SwNodeIndex aInsPos( rPos );
|
|
SwNodeIndex aOrigInsPos( rPos, -1 ); // original insertion position
|
|
int nLevel = 0; // level counter
|
|
|
|
for( SwNodeOffset nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
|
|
nNodeCnt > SwNodeOffset(0); --nNodeCnt )
|
|
{
|
|
pCurrentNode = &aRg.aStart.GetNode();
|
|
switch( pCurrentNode->GetNodeType() )
|
|
{
|
|
case SwNodeType::Table:
|
|
// Does it copy a table in(to) a footnote?
|
|
if( aInsPos < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
|
|
rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex()
|
|
< aInsPos.GetIndex() )
|
|
{
|
|
const SwNodeOffset nDistance =
|
|
pCurrentNode->EndOfSectionIndex() -
|
|
aRg.aStart.GetIndex();
|
|
if (nDistance < nNodeCnt)
|
|
nNodeCnt -= nDistance;
|
|
else
|
|
nNodeCnt = SwNodeOffset(1);
|
|
|
|
// insert a DummyNode for a TableNode
|
|
if( bTableInsDummyNode )
|
|
new SwPlaceholderNode(aInsPos.GetNode());
|
|
|
|
// copy all of the table's nodes into the current cell
|
|
for( ++aRg.aStart; aRg.aStart.GetIndex() <
|
|
pCurrentNode->EndOfSectionIndex();
|
|
++aRg.aStart )
|
|
{
|
|
// insert a DummyNode for the box-StartNode?
|
|
if( bTableInsDummyNode )
|
|
new SwPlaceholderNode(aInsPos.GetNode());
|
|
|
|
SwStartNode* pSttNd = aRg.aStart.GetNode().GetStartNode();
|
|
CopyNodes( SwNodeRange( *pSttNd, SwNodeOffset(+ 1),
|
|
*pSttNd->EndOfSectionNode() ),
|
|
aInsPos.GetNode(), bNewFrames );
|
|
|
|
// insert a DummyNode for the box-EndNode?
|
|
if( bTableInsDummyNode )
|
|
new SwPlaceholderNode(aInsPos.GetNode());
|
|
aRg.aStart = *pSttNd->EndOfSectionNode();
|
|
}
|
|
// insert a DummyNode for the table-EndNode
|
|
if( bTableInsDummyNode )
|
|
new SwPlaceholderNode(aInsPos.GetNode());
|
|
aRg.aStart = *pCurrentNode->EndOfSectionNode();
|
|
}
|
|
else
|
|
{
|
|
SwNodeIndex nStt( aInsPos, -1 );
|
|
SwTableNode* pTableNd = static_cast<SwTableNode*>(pCurrentNode)->
|
|
MakeCopy( rDoc, aInsPos );
|
|
const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
|
|
if (nDistance < nNodeCnt)
|
|
nNodeCnt -= nDistance;
|
|
else
|
|
nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
|
|
|
|
aRg.aStart = pCurrentNode->EndOfSectionIndex();
|
|
|
|
if( bNewFrames && pTableNd )
|
|
pTableNd->MakeOwnFrames();
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::Section:
|
|
// If the end of the section is outside the copy range,
|
|
// the section node will skipped, not copied!
|
|
// If someone want to change this behaviour, he has to adjust the function
|
|
// lcl_NonCopyCount() which relies on it.
|
|
if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() )
|
|
{
|
|
// copy of the whole section, so create a new SectionNode
|
|
SwNodeIndex nStt( aInsPos, -1 );
|
|
SwSectionNode* pSectNd = static_cast<SwSectionNode*>(pCurrentNode)->
|
|
MakeCopy( rDoc, aInsPos );
|
|
|
|
const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
|
|
if (nDistance < nNodeCnt)
|
|
nNodeCnt -= nDistance;
|
|
else
|
|
nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
|
|
aRg.aStart = pCurrentNode->EndOfSectionIndex();
|
|
|
|
if( bNewFrames && pSectNd &&
|
|
!pSectNd->GetSection().IsHidden() )
|
|
pSectNd->MakeOwnFrames(&nStt);
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::Start:
|
|
{
|
|
SwStartNode* pTmp = new SwStartNode( aInsPos.GetNode(), SwNodeType::Start,
|
|
static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
|
|
new SwEndNode( aInsPos.GetNode(), *pTmp );
|
|
--aInsPos;
|
|
nLevel++;
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::End:
|
|
if( nLevel ) // complete section
|
|
{
|
|
--nLevel;
|
|
++aInsPos; // EndNode already exists
|
|
}
|
|
else if( SwNodeOffset(1) == nNodeCnt && SwNodeOffset(1) == nIsEndOfContent )
|
|
// we have reached the EndOfContent node - nothing to do!
|
|
continue;
|
|
else if( !pCurrentNode->m_pStartOfSection->IsSectionNode() )
|
|
{
|
|
// create a section at the original InsertPosition
|
|
SwNodeRange aTmpRg( aOrigInsPos, SwNodeOffset(1), aInsPos );
|
|
rDoc.GetNodes().SectionDown( &aTmpRg,
|
|
pCurrentNode->m_pStartOfSection->GetStartNodeType() );
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::Text:
|
|
case SwNodeType::Grf:
|
|
case SwNodeType::Ole:
|
|
{
|
|
static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
|
|
rDoc, aInsPos.GetNode(), bNewFrames);
|
|
}
|
|
break;
|
|
|
|
case SwNodeType::PlaceHolder:
|
|
if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
|
|
{
|
|
// than a SectionNode (start/end) is needed at the current
|
|
// InsPos; if so skip it, otherwise ignore current node
|
|
SwNode *const pTmpNd = & aInsPos.GetNode();
|
|
if( pTmpNd->IsSectionNode() ||
|
|
pTmpNd->StartOfSectionNode()->IsSectionNode() )
|
|
++aInsPos; // skip
|
|
}
|
|
else {
|
|
assert(!"How can this node be in the node array?");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
++aRg.aStart;
|
|
}
|
|
}
|
|
|
|
void SwNodes::DelDummyNodes( const SwNodeRange& rRg )
|
|
{
|
|
SwNodeIndex aIdx( rRg.aStart );
|
|
while( aIdx.GetIndex() < rRg.aEnd.GetIndex() )
|
|
{
|
|
if (SwNodeType::PlaceHolder == aIdx.GetNode().GetNodeType())
|
|
RemoveNode( aIdx.GetIndex(), SwNodeOffset(1), true );
|
|
else
|
|
++aIdx;
|
|
}
|
|
}
|
|
|
|
SwStartNode* SwNodes::MakeEmptySection( SwNode& rWhere,
|
|
SwStartNodeType eSttNdTyp )
|
|
{
|
|
SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
|
|
new SwEndNode( rWhere, *pSttNd );
|
|
return pSttNd;
|
|
}
|
|
|
|
SwStartNode* SwNodes::MakeTextSection( const SwNode & rWhere,
|
|
SwStartNodeType eSttNdTyp,
|
|
SwTextFormatColl *pColl )
|
|
{
|
|
SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
|
|
new SwEndNode( rWhere, *pSttNd );
|
|
MakeTextNode( SwNodeIndex( rWhere, - 1 ).GetNode(), pColl );
|
|
return pSttNd;
|
|
}
|
|
|
|
static bool shouldSkipSection(const SwSectionNode& rSectNode, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
const SwSection& rSect = rSectNode.GetSection();
|
|
return (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag());
|
|
}
|
|
|
|
static SwContentNode* goNextSection(const SwNode& rNode, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
const SwNodes& rNodes = rNode.GetNodes();
|
|
const SwNodeOffset last = rNodes.Count() - 1;
|
|
for (SwNodeOffset i(rNode.GetIndex()); i < last; ++i)
|
|
{
|
|
SwNode* pNd = rNodes[i];
|
|
if (SwSectionNode* pSectNd = pNd->GetSectionNode())
|
|
{
|
|
if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
|
|
// than skip the section
|
|
i = pSectNd->EndOfSectionIndex();
|
|
}
|
|
else if (i == rNode.GetIndex()) // The first iteration
|
|
{
|
|
if ((pSectNd = pNd->StartOfSectionNode()->GetSectionNode()))
|
|
if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
|
|
// than skip the section
|
|
i = pSectNd->EndOfSectionIndex();
|
|
}
|
|
else if (SwContentNode* pContentNode = pNd->GetContentNode())
|
|
{
|
|
if (bSkipHidden || bSkipProtect)
|
|
if ((pSectNd = pNd->FindSectionNode()))
|
|
if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
|
|
{
|
|
i = pSectNd->EndOfSectionIndex();
|
|
continue;
|
|
}
|
|
return pContentNode;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//TODO: provide better documentation
|
|
/** go to next section that is not protected nor hidden
|
|
*
|
|
* @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
|
|
*
|
|
* @param pIdx
|
|
* @param bSkipHidden
|
|
* @param bSkipProtect
|
|
* @return
|
|
* @see SwNodes::GoNext
|
|
* @see SwNodes::GoPrevious
|
|
* @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
|
|
*/
|
|
SwContentNode* SwNodes::GoNextSection(SwNodeIndex* pIdx, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
SwContentNode* pNd = goNextSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
|
|
if (pNd)
|
|
*pIdx = *pNd;
|
|
return pNd;
|
|
}
|
|
|
|
//TODO: provide better documentation
|
|
/** go to next section that is not protected nor hidden
|
|
*
|
|
* @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
|
|
*
|
|
* @param pIdx
|
|
* @param bSkipHidden
|
|
* @param bSkipProtect
|
|
* @return
|
|
* @see SwNodes::GoNext
|
|
* @see SwNodes::GoPrevious
|
|
* @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
|
|
*/
|
|
SwContentNode* SwNodes::GoNextSection(SwPosition* pIdx, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
SwContentNode* pNd = goNextSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
|
|
if (pNd)
|
|
pIdx->AssignStartIndex(*pNd);
|
|
return pNd;
|
|
}
|
|
|
|
static SwContentNode* goPrevSection(const SwNode& rNode, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
const SwNodes& rNodes = rNode.GetNodes();
|
|
SwNodeOffset first(startOfGlobalSection(rNode));
|
|
for (SwNodeOffset i(rNode.GetIndex()); i > first; --i)
|
|
{
|
|
SwNode* pNd = rNodes[i];
|
|
if (pNd->IsEndNode() || i == rNode.GetIndex() /* the first iteration */)
|
|
{
|
|
if (SwSectionNode* pSectNd = pNd->StartOfSectionNode()->GetSectionNode())
|
|
{
|
|
if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
|
|
// then skip section
|
|
i = pSectNd->GetIndex();
|
|
}
|
|
}
|
|
else if (SwContentNode* pContentNode = pNd->GetContentNode())
|
|
{
|
|
if (bSkipHidden || bSkipProtect)
|
|
if (const SwSectionNode* pSectNd = pNd->FindSectionNode())
|
|
if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
|
|
{
|
|
i = pSectNd->GetIndex();
|
|
continue;
|
|
}
|
|
return pContentNode;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
///@see SwNodes::GoNextSection
|
|
SwContentNode* SwNodes::GoPrevSection(SwNodeIndex* pIdx, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
SwContentNode* pNd = goPrevSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
|
|
if (pNd)
|
|
*pIdx = *pNd;
|
|
return pNd;
|
|
}
|
|
|
|
///@see SwNodes::GoNextSection
|
|
SwContentNode* SwNodes::GoPrevSection(SwPosition* pIdx, bool bSkipHidden, bool bSkipProtect)
|
|
{
|
|
SwContentNode* pNd = goPrevSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
|
|
if (pNd)
|
|
pIdx->AssignStartIndex(*pNd);
|
|
return pNd;
|
|
}
|
|
|
|
//TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
|
|
/** find the next/previous ContentNode or table node that should have layout
|
|
* frames that are siblings to the ones of the node at rFrameNd.
|
|
*
|
|
* Search is started backward with the one before rFrameNd and
|
|
* forward after pEnd.
|
|
*
|
|
* @param rFrameNd node with frames to search in
|
|
* @param pEnd last node after rFrameNd that should be excluded from search
|
|
* @return result node; nullptr if not found
|
|
*/
|
|
SwNode* SwNodes::FindPrvNxtFrameNode( const SwNode& rFrameNd,
|
|
SwNode const*const pEnd,
|
|
SwRootFrame const*const pLayout) const
|
|
{
|
|
assert(pEnd != nullptr); // every caller currently
|
|
|
|
// no layout -> skip
|
|
if (!GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell())
|
|
return nullptr;
|
|
|
|
const SwNode *const pSttNd = &rFrameNd;
|
|
|
|
// inside a hidden section?
|
|
const SwSectionNode *const pSectNd = pSttNd->IsSectionNode()
|
|
? pSttNd->StartOfSectionNode()->FindSectionNode()
|
|
: pSttNd->FindSectionNode();
|
|
if (pSectNd && pSectNd->GetSection().CalcHiddenFlag())
|
|
return nullptr;
|
|
|
|
// in a table in table situation we have to assure that we don't leave the
|
|
// outer table cell when the inner table is looking for a PrvNxt...
|
|
const SwTableNode *const pTableNd = pSttNd->IsTableNode()
|
|
? pSttNd->StartOfSectionNode()->FindTableNode()
|
|
: pSttNd->FindTableNode();
|
|
SwNodeIndex aIdx( rFrameNd );
|
|
|
|
// search backward for a content or table node
|
|
|
|
--aIdx;
|
|
SwNode* pFrameNd = &aIdx.GetNode();
|
|
|
|
do
|
|
{
|
|
if (pFrameNd->IsContentNode())
|
|
{
|
|
// TODO why does this not check for nested tables like forward direction
|
|
return pFrameNd;
|
|
}
|
|
else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode())
|
|
{
|
|
if (pLayout == nullptr
|
|
|| !pLayout->HasMergedParas()
|
|
|| pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
|
|
{
|
|
pFrameNd = pFrameNd->StartOfSectionNode();
|
|
return pFrameNd;
|
|
}
|
|
else
|
|
{
|
|
aIdx = *pFrameNd->StartOfSectionNode();
|
|
--aIdx;
|
|
pFrameNd = &aIdx.GetNode();
|
|
}
|
|
}
|
|
else if (pFrameNd->IsSectionNode()
|
|
|| (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsSectionNode()))
|
|
{
|
|
pFrameNd = GoPrevSection( &aIdx, true, false );
|
|
// did we move *into* a table?
|
|
if (pFrameNd && ::CheckNodesRange(aIdx.GetNode(), rFrameNd, true))
|
|
{
|
|
for (SwTableNode * pTable = pFrameNd->FindTableNode();
|
|
pTable && pTable->EndOfSectionIndex() < rFrameNd.GetIndex();
|
|
pTable = pTable->StartOfSectionNode()->FindTableNode())
|
|
{
|
|
pFrameNd = pTable->EndOfSectionNode();
|
|
}
|
|
if (pFrameNd->IsEndNode())
|
|
{ // GoPrevSection() checks that text node isn't section-hidden,
|
|
// so table node between can't be section-hidden either
|
|
assert(pFrameNd->StartOfSectionNode()->IsTableNode());
|
|
continue; // check other hidden conditions on next iteration
|
|
}
|
|
}
|
|
if ( nullptr != pFrameNd && !(
|
|
::CheckNodesRange( aIdx.GetNode(), rFrameNd, true ) &&
|
|
// Never out of the table at the start
|
|
pFrameNd->FindTableNode() == pTableNd &&
|
|
// Bug 37652: Never out of the table at the end
|
|
(!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
|
|
== pSttNd->FindTableBoxStartNode() ) &&
|
|
(!pSectNd || pSttNd->IsSectionNode() ||
|
|
pSectNd->GetIndex() < pFrameNd->GetIndex())
|
|
))
|
|
{
|
|
pFrameNd = nullptr; // no preceding content node, stop search
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFrameNd = nullptr; // no preceding content node, stop search
|
|
}
|
|
}
|
|
while (pFrameNd != nullptr);
|
|
|
|
// search forward for a content or table node
|
|
|
|
aIdx = pEnd->GetIndex() + 1;
|
|
pFrameNd = &aIdx.GetNode();
|
|
|
|
do
|
|
{
|
|
if (pFrameNd->IsContentNode())
|
|
{
|
|
// Undo when merging a table with one before, if there is also one after it.
|
|
// However, if the node is in a table, it needs to be returned if the
|
|
// SttNode is a section or a table!
|
|
SwTableNode *const pTableNode = pFrameNd->FindTableNode();
|
|
if (pSttNd->IsTableNode() &&
|
|
nullptr != pTableNode &&
|
|
// TABLE IN TABLE:
|
|
pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
|
|
{
|
|
pFrameNd = pTableNode;
|
|
}
|
|
return pFrameNd;
|
|
}
|
|
else if (pFrameNd->IsTableNode())
|
|
{
|
|
if (pLayout == nullptr
|
|
|| !pLayout->HasMergedParas()
|
|
|| pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
|
|
{
|
|
return pFrameNd;
|
|
}
|
|
else
|
|
{
|
|
aIdx = *pFrameNd->EndOfSectionNode();
|
|
++aIdx;
|
|
pFrameNd = &aIdx.GetNode();
|
|
}
|
|
}
|
|
else if (pFrameNd->IsSectionNode()
|
|
|| (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsSectionNode()))
|
|
{
|
|
pFrameNd = GoNextSection( &aIdx, true, false );
|
|
// did we move *into* a table?
|
|
if (pFrameNd && ::CheckNodesRange(aIdx.GetNode(), rFrameNd, true))
|
|
{
|
|
for (SwTableNode * pTable = pFrameNd->FindTableNode();
|
|
pTable && pEnd->GetIndex() < pTable->GetIndex();
|
|
pTable = pTable->StartOfSectionNode()->FindTableNode())
|
|
{
|
|
pFrameNd = pTable;
|
|
}
|
|
if (pFrameNd->IsTableNode())
|
|
{ // GoNextSection() checks that text node isn't section-hidden,
|
|
// so table node between can't be section-hidden either
|
|
continue; // check other hidden conditions on next iteration
|
|
}
|
|
}
|
|
// NEVER leave the section when doing this!
|
|
if (pFrameNd
|
|
&& !(::CheckNodesRange(aIdx.GetNode(), rFrameNd, true)
|
|
&& (pFrameNd->FindTableNode() == pTableNd &&
|
|
// NEVER go out of the table cell at the end
|
|
(!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
|
|
== pSttNd->FindTableBoxStartNode()))
|
|
&& (!pSectNd || pSttNd->IsSectionNode() ||
|
|
pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
|
|
)
|
|
{
|
|
pFrameNd = nullptr; // no following content node, stop search
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFrameNd = nullptr; // no preceding content node, stop search
|
|
}
|
|
}
|
|
while (pFrameNd != nullptr);
|
|
|
|
return pFrameNd;
|
|
}
|
|
|
|
void SwNodes::ForEach( SwNodeOffset nStart, SwNodeOffset nEnd,
|
|
FnForEach_SwNodes fn, void* pArgs )
|
|
{
|
|
if( nEnd > SwNodeOffset(m_nSize) )
|
|
nEnd = SwNodeOffset(m_nSize);
|
|
|
|
if( nStart >= nEnd )
|
|
return;
|
|
|
|
sal_uInt16 cur = Index2Block( sal_Int32(nStart) );
|
|
BlockInfo** pp = m_ppInf.get() + cur;
|
|
BlockInfo* p = *pp;
|
|
sal_uInt16 nElem = sal_uInt16( sal_Int32(nStart) - p->nStart );
|
|
auto pElem = p->mvData.begin() + nElem;
|
|
nElem = p->nElem - nElem;
|
|
for(;;)
|
|
{
|
|
if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
|
|
break;
|
|
|
|
// next element
|
|
if( !--nElem )
|
|
{
|
|
// new block
|
|
p = *++pp;
|
|
pElem = p->mvData.begin();
|
|
nElem = p->nElem;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwNodes::ForEach( const SwNodeIndex& rStart, const SwNodeIndex& rEnd,
|
|
FnForEach_SwNodes fnForEach, void* pArgs )
|
|
{
|
|
ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
|
|
}
|
|
|
|
void SwNodes::ForEach( SwNode& rStart, SwNode& rEnd,
|
|
FnForEach_SwNodes fnForEach, void* pArgs )
|
|
{
|
|
ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
|
|
}
|
|
|
|
void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel )
|
|
{
|
|
#ifndef NDEBUG
|
|
SwNode *const pFirst((*this)[nDelPos]);
|
|
#endif
|
|
std::vector<SwTextAttr*> flys;
|
|
for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
|
|
{
|
|
SwNode* pNode = (*this)[ nDelPos + nCnt ];
|
|
SwTextNode * pTextNd = pNode->GetTextNode();
|
|
|
|
if (pTextNd)
|
|
{
|
|
pTextNd->RemoveFromList();
|
|
// remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
|
|
// so their anchor still points to correct node when deleted!
|
|
// NOTE: this will call RemoveNode() recursively!
|
|
// so adjust our indexes to account for removed nodes
|
|
SwpHints *const pHints(pTextNd->GetpSwpHints());
|
|
if (pHints)
|
|
{
|
|
SwNodeOffset const nPos = pTextNd->GetIndex();
|
|
flys.clear();
|
|
for (size_t i = 0; i < pHints->Count(); ++i)
|
|
{
|
|
SwTextAttr *const pHint(pHints->Get(i));
|
|
if (RES_TXTATR_FLYCNT == pHint->Which())
|
|
{
|
|
flys.push_back(pHint);
|
|
}
|
|
}
|
|
for (SwTextAttr * pHint : flys)
|
|
{
|
|
pTextNd->DeleteAttribute(pHint);
|
|
} // pHints may be dead now
|
|
SwNodeOffset const nDiff = nPos - pTextNd->GetIndex();
|
|
if (nDiff)
|
|
{
|
|
nDelPos -= nDiff;
|
|
}
|
|
assert(pTextNd == (*this)[nDelPos + nCnt]);
|
|
assert(pFirst == (*this)[nDelPos]);
|
|
}
|
|
}
|
|
SwTableNode* pTableNode = pNode->GetTableNode();
|
|
if (pTableNode)
|
|
{
|
|
// The node that is deleted is a table node.
|
|
// Need to make sure that all the redlines that are
|
|
// related to this table are removed from the
|
|
// 'Extra Redlines' array
|
|
pTableNode->RemoveRedlines();
|
|
}
|
|
|
|
SwSectionNode* pSectionNode = pNode->GetSectionNode();
|
|
SfxViewShell* pKitClipSh = (comphelper::LibreOfficeKit::isActive() && pSectionNode && !GetDoc().IsClipBoard())
|
|
? SfxViewShell::Current() : nullptr;
|
|
if (pKitClipSh)
|
|
{
|
|
OUString fieldCommand = pSectionNode->GetSection().GetSectionName();
|
|
tools::JsonWriter aJson;
|
|
aJson.put("commandName", ".uno:DeleteSection");
|
|
aJson.put("success", true);
|
|
{
|
|
auto result = aJson.startNode("result");
|
|
aJson.put("DeleteSection", fieldCommand);
|
|
}
|
|
|
|
pKitClipSh->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
|
|
}
|
|
}
|
|
|
|
SwNodeOffset nEnd = nDelPos + nSz;
|
|
SwNode* pNew = (*this)[ nEnd ];
|
|
|
|
for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
|
|
{
|
|
SwNode* pNode = (*this)[ nDelPos + nCnt ];
|
|
// the assignment will de-link the entry from the ring
|
|
while (pNode->m_vIndices)
|
|
(*pNode->m_vIndices) = *pNew;
|
|
}
|
|
|
|
std::vector<BigPtrEntry> aTempEntries;
|
|
if( bDel )
|
|
{
|
|
SwNodeOffset nCnt = nSz;
|
|
BigPtrEntry *pDel = (*this)[ nDelPos+nCnt-1 ], *pPrev = (*this)[ nDelPos+nCnt-2 ];
|
|
|
|
// set temporary object
|
|
// JP 24.08.98: this should actually be removed because one could
|
|
// call Remove recursively, e.g. for character bound frames. However,
|
|
// since there happens way too much here, this temporary object was
|
|
// inserted that will be deleted in Remove again (see Bug 55406)
|
|
aTempEntries.resize(sal_Int32(nCnt));
|
|
|
|
while( nCnt-- )
|
|
{
|
|
delete pDel;
|
|
// coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
|
|
pDel = pPrev;
|
|
BigPtrEntry* pTempEntry = &aTempEntries[sal_Int32(nCnt)];
|
|
pPrev = ReplaceTheOneAfter(pPrev, pTempEntry);
|
|
}
|
|
nDelPos = SwNodeOffset(pDel->GetPos() + 1);
|
|
}
|
|
|
|
BigPtrArray::Remove( sal_Int32(nDelPos), sal_Int32(nSz) );
|
|
}
|
|
|
|
void SwNodes::InsertNode( SwNode* pNode, const SwNodeIndex& rPos )
|
|
{
|
|
BigPtrEntry* pIns = pNode;
|
|
BigPtrArray::Insert( pIns, sal_Int32(rPos.GetIndex()) );
|
|
}
|
|
|
|
void SwNodes::InsertNode( SwNode* pNode, SwNodeOffset nPos )
|
|
{
|
|
BigPtrEntry* pIns = pNode;
|
|
BigPtrArray::Insert( pIns, sal_Int32(nPos) );
|
|
}
|
|
|
|
// ->#112139#
|
|
SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const
|
|
{
|
|
if (nullptr != pNode)
|
|
{
|
|
SwNodeIndex aIdx(*pNode);
|
|
|
|
if (aIdx <= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
|
|
pNode = (*this)[SwNodeOffset(0)];
|
|
else
|
|
{
|
|
while ((*this)[SwNodeOffset(0)] != pNode->StartOfSectionNode())
|
|
pNode = pNode->StartOfSectionNode();
|
|
}
|
|
}
|
|
|
|
return pNode;
|
|
}
|
|
|
|
SwNode * SwNodes::DocumentSectionEndNode(SwNode * pNode) const
|
|
{
|
|
return DocumentSectionStartNode(pNode)->EndOfSectionNode();
|
|
}
|
|
|
|
bool SwNodes::IsDocNodes() const
|
|
{
|
|
return this == &m_rMyDoc.GetNodes();
|
|
}
|
|
|
|
void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const
|
|
{
|
|
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes"));
|
|
for (SwNodeOffset i(0); i < Count(); ++i)
|
|
(*this)[i]->dumpAsXml(pWriter);
|
|
(void)xmlTextWriterEndElement(pWriter);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|