1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <libxml/xmlwriter.h>
22 #include <osl/diagnose.h>
23 #include <tools/json_writer.hxx>
24 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
25 #include <sfx2/viewsh.hxx>
26 #include <comphelper/lok.hxx>
30 #include <IDocumentUndoRedo.hxx>
31 #include <IDocumentFieldsAccess.hxx>
32 #include <IDocumentLayoutAccess.hxx>
36 #include <numrule.hxx>
38 #include <ndnotxt.hxx>
39 #include <swtable.hxx>
40 #include <section.hxx>
42 #include <swddetbl.hxx>
45 #include <fmtrfmrk.hxx>
48 #include <rootfrm.hxx>
51 typedef std::vector
<SwStartNode
*> SwStartNodePointers
;
53 // function to determine the highest level in the given range
54 static sal_uInt16
HighestLevel( SwNodes
& rNodes
, const SwNodeRange
& rRange
);
58 * creates the base sections (PostIts, Inserts, AutoText, RedLines, Content)
60 * @param rDocument TODO: provide documentation
62 SwNodes::SwNodes( SwDoc
& rDocument
)
63 : m_rMyDoc( rDocument
)
65 m_bInNodesDel
= m_bInDelUpdOutline
= false;
68 SwStartNode
* pSttNd
= new SwStartNode( *this, nPos
++ );
69 m_pEndOfPostIts
= new SwEndNode( *this, nPos
++, *pSttNd
);
71 SwStartNode
* pTmp
= new SwStartNode( *this, nPos
++ );
72 m_pEndOfInserts
= new SwEndNode( *this, nPos
++, *pTmp
);
74 pTmp
= new SwStartNode( *this, nPos
++ );
75 pTmp
->m_pStartOfSection
= pSttNd
;
76 m_pEndOfAutotext
= new SwEndNode( *this, nPos
++, *pTmp
);
78 pTmp
= new SwStartNode( *this, nPos
++ );
79 pTmp
->m_pStartOfSection
= pSttNd
;
80 m_pEndOfRedlines
= new SwEndNode( *this, nPos
++, *pTmp
);
82 pTmp
= new SwStartNode( *this, nPos
++ );
83 pTmp
->m_pStartOfSection
= pSttNd
;
84 m_pEndOfContent
.reset(new SwEndNode( *this, nPos
++, *pTmp
));
86 m_aOutlineNodes
.clear();
91 * Deletes all nodes whose pointer are in a dynamic array. This should be no
92 * problem as nodes cannot be created outside this array and, thus, cannot be
93 * part of multiple arrays.
97 m_aOutlineNodes
.clear();
100 SwNodeIndex
aNdIdx( *this );
103 SwNode
*pNode
= &aNdIdx
.GetNode();
104 if( pNode
== m_pEndOfContent
.get() )
112 // here, all SwNodeIndices must be unregistered
113 m_pEndOfContent
.reset();
116 static bool IsInsertOutline(SwNodes
const& rNodes
, SwNodeOffset
const nIndex
)
118 if (!rNodes
.IsDocNodes())
122 return nIndex
< rNodes
.GetEndOfRedlines().StartOfSectionNode()->GetIndex()
123 || rNodes
.GetEndOfRedlines().GetIndex() < nIndex
;
126 void SwNodes::ChgNode( SwNodeIndex
const & rDelPos
, SwNodeOffset nSz
,
127 SwNodeIndex
& rInsPos
, bool bNewFrames
)
129 // no need for frames in the UndoArea
130 SwNodes
& rNds
= rInsPos
.GetNodes();
131 const SwNode
* pPrevInsNd
= rNds
[ rInsPos
.GetIndex() -SwNodeOffset(1) ];
133 // declare all fields as invalid, updating will happen
134 // in the idle-handler of the doc
135 if( GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rDelPos
.GetNode(), nSz
) &&
136 &rNds
.GetDoc() != &GetDoc() )
137 rNds
.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
139 // NEVER include nodes from the RedLineArea
140 SwNodeOffset nNd
= rInsPos
.GetIndex();
141 bool const bInsOutlineIdx
= IsInsertOutline(rNds
, nNd
);
143 if( &rNds
== this ) // if in the same node array -> move
145 // Move order: from front to back, so that new entries are added at
146 // first position, thus, deletion position stays the same
147 const SwNodeOffset
nDiff(rDelPos
.GetIndex() < rInsPos
.GetIndex() ? 0 : 1);
149 for( SwNodeOffset n
= rDelPos
.GetIndex(); nSz
; n
+= nDiff
, --nSz
)
151 SwNodeIndex
aDelIdx( *this, n
);
152 SwNode
& rNd
= aDelIdx
.GetNode();
154 // #i57920# - correction of refactoring done by cws swnumtree:
155 // - <SwTextNode::SetLevel( NO_NUMBERING ) is deprecated and
156 // set <IsCounted> state of the text node to <false>, which
157 // isn't correct here.
158 if ( rNd
.IsTextNode() )
160 SwTextNode
* pTextNode
= rNd
.GetTextNode();
162 pTextNode
->RemoveFromList();
164 if (pTextNode
->IsOutline())
166 SwNode
* pSrch
= &rNd
;
167 m_aOutlineNodes
.erase( pSrch
);
171 BigPtrArray::Move( sal_Int32(aDelIdx
.GetIndex()), sal_Int32(rInsPos
.GetIndex()) );
173 if( rNd
.IsTextNode() )
175 SwTextNode
& rTextNd
= static_cast<SwTextNode
&>(rNd
);
179 if (bInsOutlineIdx
&& rTextNd
.IsOutline())
181 SwNode
* pSrch
= &rNd
;
182 m_aOutlineNodes
.insert( pSrch
);
184 rTextNd
.InvalidateNumRule();
186 if( RES_CONDTXTFMTCOLL
== rTextNd
.GetTextColl()->Which() )
187 rTextNd
.ChkCondColl();
189 else if( rNd
.IsContentNode() )
190 static_cast<SwContentNode
&>(rNd
).InvalidateNumRule();
195 bool bSavePersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds
));
196 bool bRestPersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this));
197 SwDoc
* pDestDoc
= &rNds
.GetDoc() != &GetDoc() ? &rNds
.GetDoc() : nullptr;
198 OSL_ENSURE(!pDestDoc
, "SwNodes::ChgNode(): "
199 "the code to handle text fields here looks broken\n"
200 "if the target is in a different document.");
201 if( !bRestPersData
&& !bSavePersData
&& pDestDoc
)
202 bSavePersData
= bRestPersData
= true;
205 for( SwNodeOffset
n(0); n
< nSz
; n
++ )
207 SwNode
* pNd
= &rDelPos
.GetNode();
209 // NoTextNode keep their persistent data
210 if( pNd
->IsNoTextNode() )
213 static_cast<SwNoTextNode
*>(pNd
)->SavePersistentData();
215 else if( pNd
->IsTextNode() )
217 SwTextNode
* pTextNd
= static_cast<SwTextNode
*>(pNd
);
219 // remove outline index from old nodes array
220 if (pTextNd
->IsOutline())
222 m_aOutlineNodes
.erase( pNd
);
225 // copy rules if needed
228 const SwNumRule
* pNumRule
= pTextNd
->GetNumRule();
229 if( pNumRule
&& sNumRule
!= pNumRule
->GetName() )
231 sNumRule
= pNumRule
->GetName();
232 SwNumRule
* pDestRule
= pDestDoc
->FindNumRulePtr( sNumRule
);
234 pDestRule
->Invalidate();
236 pDestDoc
->MakeNumRule( sNumRule
, pNumRule
);
241 // if movement into the UndoNodes-array, update numbering
242 if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd
))
244 pTextNd
->InvalidateNumRule();
248 pTextNd
->RemoveFromList();
251 RemoveNode( rDelPos
.GetIndex(), SwNodeOffset(1), false ); // move indices
252 SwContentNode
* pCNd
= pNd
->GetContentNode();
253 rNds
.InsertNode( pNd
, rInsPos
);
257 SwTextNode
* pTextNd
= pCNd
->GetTextNode();
260 SwpHints
* const pHts
= pTextNd
->GetpSwpHints();
261 // OutlineNodes set the new nodes in the array
262 if (bInsOutlineIdx
&& pTextNd
->IsOutline())
264 rNds
.m_aOutlineNodes
.insert( pTextNd
);
267 pTextNd
->AddToList();
269 // special treatment for fields
270 if( pHts
&& pHts
->Count() )
272 bool const bToUndo
= !pDestDoc
&&
273 GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds
);
274 for( size_t i
= pHts
->Count(); i
; )
276 SwTextAttr
* const pAttr
= pHts
->Get( --i
);
277 switch ( pAttr
->Which() )
279 case RES_TXTATR_FIELD
:
280 case RES_TXTATR_ANNOTATION
:
281 case RES_TXTATR_INPUTFIELD
:
283 SwTextField
* pTextField
= static_txtattr_cast
<SwTextField
*>(pAttr
);
284 rNds
.GetDoc().getIDocumentFieldsAccess().InsDelFieldInFieldLst( !bToUndo
, *pTextField
);
286 SwFieldType
* pTyp
= pTextField
->GetFormatField().GetField()->GetTyp();
287 if ( SwFieldIds::Postit
== pTyp
->Which() )
289 rNds
.GetDoc().GetDocShell()->Broadcast(
291 &pTextField
->GetFormatField(),
292 ( pTextField
->GetFormatField().IsFieldInDoc()
293 ? SwFormatFieldHintWhich::INSERTED
294 : SwFormatFieldHintWhich::REMOVED
) ) );
296 else if( SwFieldIds::Dde
== pTyp
->Which() )
299 static_cast<SwDDEFieldType
*>(pTyp
)->DecRefCnt();
301 static_cast<SwDDEFieldType
*>(pTyp
)->IncRefCnt();
303 static_cast<SwFormatField
&>(pAttr
->GetAttr())
309 static_cast<SwFormatFootnote
&>(pAttr
->GetAttr())
310 .InvalidateFootnote();
313 case RES_TXTATR_TOXMARK
:
314 static_cast<SwTOXMark
&>(pAttr
->GetAttr())
315 .InvalidateTOXMark();
318 case RES_TXTATR_REFMARK
:
319 static_cast<SwFormatRefMark
&>(pAttr
->GetAttr())
320 .InvalidateRefMark();
323 case RES_TXTATR_META
:
324 case RES_TXTATR_METAFIELD
:
326 SwTextMeta
*const pTextMeta(
327 static_txtattr_cast
<SwTextMeta
*>(pAttr
));
328 // force removal of UNO object
329 pTextMeta
->ChgTextNode(nullptr);
330 pTextMeta
->ChgTextNode(pTextNd
);
340 if( RES_CONDTXTFMTCOLL
== pTextNd
->GetTextColl()->Which() )
341 pTextNd
->ChkCondColl();
345 // Moved into different Docs? Persist data again!
346 if( pCNd
->IsNoTextNode() && bRestPersData
)
347 static_cast<SwNoTextNode
*>(pCNd
)->RestorePersistentData();
350 // reset Accessibility issue state
351 pCNd
->resetAndQueueAccessibilityCheck();
356 // declare all fields as invalid, updating will happen
357 // in the idle-handler of the doc
358 GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
359 if( &rNds
.GetDoc() != &GetDoc() )
360 rNds
.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
363 bNewFrames
= &GetDoc().GetNodes() == &rNds
&&
364 GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
370 SwNodeIndex
aIdx( *pPrevInsNd
, 1 );
371 SwNode
* pFrameNd
= rNds
.FindPrvNxtFrameNode( aIdx
.GetNode(),
372 rNds
[ rInsPos
.GetIndex() - 1 ] );
377 while( aIdx
!= rInsPos
)
379 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
382 if( pFrameNd
->IsTableNode() )
383 static_cast<SwTableNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(aIdx
);
384 else if( pFrameNd
->IsSectionNode() )
385 static_cast<SwSectionNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(aIdx
);
387 static_cast<SwContentNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(*pCNd
);
394 // TODO: provide documentation
395 /** move the node pointer
397 * Move the node pointer from "(inclusive) start position to (exclusive) end
398 * position" to target position.
399 * If the target is in front of the first or in the area between first and
400 * last element to move, nothing happens.
401 * If the area to move is empty or the end position is before the start
402 * position, nothing happens.
404 * @param aRange range to move (excluding end node)
407 bool SwNodes::MoveNodes( const SwNodeRange
& aRange
, SwNodes
& rNodes
,
408 SwNode
& rPos
, bool bNewFrames
)
410 SwNode
* pCurrentNode
;
411 if( rPos
.GetIndex() == SwNodeOffset(0) ||
412 ( (pCurrentNode
= &rPos
)->GetStartNode() &&
413 !pCurrentNode
->StartOfSectionIndex() ))
416 SwNodeRange
aRg( aRange
);
418 // skip "simple" start or end nodes
419 while( SwNodeType::Start
== (pCurrentNode
= &aRg
.aStart
.GetNode())->GetNodeType()
420 || ( pCurrentNode
->IsEndNode() &&
421 !pCurrentNode
->m_pStartOfSection
->IsSectionNode() ) )
425 // if aEnd-1 points to no ContentNode, search previous one
427 while( ( (( pCurrentNode
= &aRg
.aEnd
.GetNode())->GetStartNode() &&
428 !pCurrentNode
->IsSectionNode() ) ||
429 ( pCurrentNode
->IsEndNode() &&
430 SwNodeType::Start
== pCurrentNode
->m_pStartOfSection
->GetNodeType()) ) &&
431 aRg
.aEnd
> aRg
.aStart
)
434 // if in same array, check insertion position
435 if( aRg
.aStart
>= aRg
.aEnd
)
438 if( this == &rNodes
)
440 if( ( rPos
.GetIndex()-SwNodeOffset(1) >= aRg
.aStart
.GetIndex() &&
441 rPos
.GetIndex()-SwNodeOffset(1) < aRg
.aEnd
.GetIndex()) ||
442 ( rPos
.GetIndex()-SwNodeOffset(1) == aRg
.aEnd
.GetIndex() ) )
446 SwNodeOffset
nInsPos(0); // counter for tmp array
448 // array as a stack, storing all StartOfSelections
449 SwStartNodePointers aSttNdStack
;
450 SwStartNodePointers::size_type nLevel
= 0; // level counter
453 SwNodeIndex
aIdx( rPos
);
455 SwStartNode
* pStartNode
= aIdx
.GetNode().m_pStartOfSection
;
456 aSttNdStack
.insert( aSttNdStack
.begin(), pStartNode
);
458 SwNodeRange
aOrigInsPos( aIdx
, SwNodeOffset(-1), aIdx
); // original insertion position
460 // call DelFrames/MakeFrames for the upmost SectionNode
462 bool bSaveNewFrames
= bNewFrames
;
464 // continue until everything has been moved
465 while( aRg
.aStart
< aRg
.aEnd
)
467 pCurrentNode
= &aRg
.aEnd
.GetNode();
468 switch( pCurrentNode
->GetNodeType() )
470 case SwNodeType::End
:
472 if( nInsPos
) // move everything until here
474 // delete and copy. CAUTION: all indices after
475 // "aRg.aEnd+1" will be moved as well!
476 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
477 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
479 nInsPos
= SwNodeOffset(0);
482 SwStartNode
* pSttNd
= pCurrentNode
->m_pStartOfSection
;
483 if( pSttNd
->IsTableNode() )
485 SwTableNode
* pTableNd
= static_cast<SwTableNode
*>(pSttNd
);
487 // move the whole table/range
488 nInsPos
= (aRg
.aEnd
.GetIndex() -
489 pSttNd
->GetIndex() )+1;
492 // NEVER include nodes from the RedLineArea
493 SwNodeOffset nNd
= aIdx
.GetIndex();
494 bool const bInsOutlineIdx
= IsInsertOutline(rNodes
, nNd
);
498 pTableNd
->DelFrames(nullptr);
499 if( &rNodes
== this ) // move into self?
501 // move all Start/End/ContentNodes
502 // ContentNodes: delete also the frames!
503 pTableNd
->m_pStartOfSection
= aIdx
.GetNode().m_pStartOfSection
;
504 for( SwNodeOffset
n(0); n
< nInsPos
; ++n
)
506 SwNodeIndex
aMvIdx( aRg
.aEnd
, 1 );
507 SwContentNode
* pCNd
= nullptr;
508 SwNode
* pTmpNd
= &aMvIdx
.GetNode();
509 if( pTmpNd
->IsContentNode() )
511 pCNd
= static_cast<SwContentNode
*>(pTmpNd
);
512 if( pTmpNd
->IsTextNode() )
513 static_cast<SwTextNode
*>(pTmpNd
)->RemoveFromList();
515 // remove outline index from old nodes array
516 if (pCNd
->IsTextNode() && pCNd
->GetTextNode()->IsOutline())
518 m_aOutlineNodes
.erase( pCNd
);
524 BigPtrArray::Move( sal_Int32(aMvIdx
.GetIndex()), sal_Int32(aIdx
.GetIndex()) );
526 if( bInsOutlineIdx
&& pCNd
)
527 m_aOutlineNodes
.insert( pCNd
);
528 if( pTmpNd
->IsTextNode() )
529 static_cast<SwTextNode
*>(pTmpNd
)->AddToList();
535 // Even aIdx points to a startnode, we need the startnode
536 // of the environment of aIdx (#i80941)
537 SwStartNode
* pSttNode
= aIdx
.GetNode().m_pStartOfSection
;
539 // get all boxes with content because their indices
540 // pointing to the StartNodes need to be reset
541 // (copying the array and deleting all found ones eases
543 SwNodeIndex
aMvIdx( aRg
.aEnd
, 1 );
544 for( SwNodeOffset
n(0); n
< nInsPos
; ++n
)
546 SwNode
* pNd
= &aMvIdx
.GetNode();
548 const bool bOutlNd
= pNd
->IsTextNode() && pNd
->GetTextNode()->IsOutline();
549 // delete outline indices from old node array
551 m_aOutlineNodes
.erase( pNd
);
553 RemoveNode( aMvIdx
.GetIndex(), SwNodeOffset(1), false );
554 pNd
->m_pStartOfSection
= pSttNode
;
555 rNodes
.InsertNode( pNd
, aIdx
);
557 // set correct indices in Start/EndNodes
558 if( bInsOutlineIdx
&& bOutlNd
)
559 // and put them into the new node array
560 rNodes
.m_aOutlineNodes
.insert( pNd
);
561 else if( pNd
->IsStartNode() )
562 pSttNode
= static_cast<SwStartNode
*>(pNd
);
563 else if( pNd
->IsEndNode() )
565 pSttNode
->m_pEndOfSection
= static_cast<SwEndNode
*>(pNd
);
566 if( pSttNode
->IsSectionNode() )
567 static_cast<SwSectionNode
*>(pSttNode
)->NodesArrChgd();
568 pSttNode
= pSttNode
->m_pStartOfSection
;
572 if( auto pDDETable
= dynamic_cast<SwDDETable
*>(&pTableNd
->GetTable()) )
574 SwDDEFieldType
* pTyp
= pDDETable
->GetDDEFieldType();
577 if( rNodes
.IsDocNodes() )
584 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes
))
586 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
587 pTableFormat
->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying
));
592 pTableNd
->MakeOwnFrames();
595 nInsPos
= SwNodeOffset(0);
597 else if( pSttNd
->GetIndex() < aRg
.aStart
.GetIndex() )
599 // SectionNode: not the whole section will be moved, thus,
600 // move only the ContentNodes
601 // StartNode: create a new section at the given position
602 do { // middle check loop
603 if( !pSttNd
->IsSectionNode() )
605 // create StartNode and EndNode at InsertPos
606 SwStartNode
* pTmp
= new SwStartNode( aIdx
.GetNode(),
608 /*?? NodeType ??*/ SwNormalStartNode
);
610 nLevel
++; // put the index to StartNode on the stack
611 aSttNdStack
.insert( aSttNdStack
.begin() + nLevel
, pTmp
);
614 new SwEndNode( aIdx
.GetNode(), *pTmp
);
616 else if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(
619 // use placeholder in UndoNodes array
620 new SwPlaceholderNode(aIdx
.GetNode());
624 // JP 18.5.2001 (Bug 70454) creating new section?
636 // move StartNode and EndNode in total
638 // if Start is exactly the Start of the area,
639 // then the Node needs to be re-visited
640 if( &aRg
.aStart
.GetNode() == pSttNd
)
643 SwSectionNode
* pSctNd
= pSttNd
->GetSectionNode();
644 if( bNewFrames
&& pSctNd
)
645 { // tdf#135056 skip over code in DelFrames() that moves
646 // SwNodeIndex around because in case of nested
647 // sections, m_pStartOfSection will point between
648 // undo nodes-array and doc nodes-array
649 pSctNd
->DelFrames(nullptr, true);
652 RemoveNode( aRg
.aEnd
.GetIndex(), SwNodeOffset(1), false ); // delete EndNode
653 SwNodeOffset nSttPos
= pSttNd
->GetIndex();
655 // this StartNode will be removed later
656 SwStartNode
* pTmpSttNd
= new SwStartNode( *this, nSttPos
+1 );
657 pTmpSttNd
->m_pStartOfSection
= pSttNd
->m_pStartOfSection
;
659 RemoveNode( nSttPos
, SwNodeOffset(1), false ); // delete SttNode
661 pSttNd
->m_pStartOfSection
= aIdx
.GetNode().m_pStartOfSection
;
662 rNodes
.InsertNode( pSttNd
, aIdx
);
663 rNodes
.InsertNode( pCurrentNode
, aIdx
);
665 pSttNd
->m_pEndOfSection
= static_cast<SwEndNode
*>(pCurrentNode
);
669 nLevel
++; // put the index pointing to the StartNode onto the stack
670 aSttNdStack
.insert( aSttNdStack
.begin() + nLevel
, pSttNd
);
672 // reset remaining indices if SectionNode
675 pSctNd
->NodesArrChgd();
677 // tdf#132326 do not let frames survive in undo nodes
678 if (!GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes
))
687 case SwNodeType::Section
:
689 GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes
))
691 // here, a SectionDummyNode needs to be inserted at the current position
692 if( nInsPos
) // move everything until here
694 // delete and copy. CAUTION: all indices after
695 // "aRg.aEnd+1" will be moved as well!
696 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
697 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
699 nInsPos
= SwNodeOffset(0);
701 new SwPlaceholderNode(aIdx
.GetNode());
707 case SwNodeType::Table
:
708 case SwNodeType::Start
:
710 // empty section -> nothing to do
711 // and only if it's a top level section
712 if( !nInsPos
&& !nLevel
)
718 if( !nLevel
) // level is decreasing
721 SwNodeIndex
aTmpSIdx( aOrigInsPos
.aStart
, 1 );
722 SwStartNode
* pTmpStt
= new SwStartNode( aTmpSIdx
.GetNode(),
724 static_cast<SwStartNode
*>(pCurrentNode
)->GetStartNodeType() );
728 SwNodeIndex
aTmpEIdx( aOrigInsPos
.aEnd
);
729 new SwEndNode( aTmpEIdx
.GetNode(), *pTmpStt
);
733 // set correct StartOfSection
736 SwNodeIndex
aCntIdx( aRg
.aEnd
);
737 for( SwNodeOffset
n(0); n
< nInsPos
; n
++, ++aCntIdx
)
738 aCntIdx
.GetNode().m_pStartOfSection
= pTmpStt
;
741 // also set correct StartNode for all decreased nodes
742 while( aTmpSIdx
< aTmpEIdx
)
743 if( nullptr != (( pCurrentNode
= &aTmpEIdx
.GetNode())->GetEndNode()) )
744 aTmpEIdx
= pCurrentNode
->StartOfSectionIndex();
747 pCurrentNode
->m_pStartOfSection
= pTmpStt
;
751 --aIdx
; // after the inserted StartNode
752 --aRg
.aEnd
; // before StartNode
753 // copy array. CAUTION: all indices after
754 // "aRg.aEnd+1" will be moved as well!
755 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
756 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
758 nInsPos
= SwNodeOffset(0);
760 else // all nodes between StartNode and EndNode were moved
762 OSL_ENSURE( pCurrentNode
== aSttNdStack
[nLevel
] ||
763 ( pCurrentNode
->IsStartNode() &&
764 aSttNdStack
[nLevel
]->IsSectionNode()),
767 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
768 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
769 aIdx
-= nInsPos
+1; // before inserted StartNode
770 nInsPos
= SwNodeOffset(0);
772 // remove pointer from node array
773 RemoveNode( aRg
.aEnd
.GetIndex(), SwNodeOffset(1), true );
776 SwSectionNode
* pSectNd
= aSttNdStack
[ nLevel
]->GetSectionNode();
777 if( pSectNd
&& !--nSectNdCnt
)
779 SwNodeIndex
aTmp( *pSectNd
);
780 pSectNd
->MakeOwnFrames(&aTmp
);
781 bNewFrames
= bSaveNewFrames
;
783 aSttNdStack
.erase( aSttNdStack
.begin() + nLevel
); // remove from stack
787 // delete all resulting empty start/end node pairs
788 SwNode
* pTmpNode
= (*this)[ aRg
.aEnd
.GetIndex()+1 ]->GetEndNode();
789 if( pTmpNode
&& SwNodeType::Start
== (pCurrentNode
= &aRg
.aEnd
.GetNode())
790 ->GetNodeType() && pCurrentNode
->StartOfSectionIndex() &&
791 pTmpNode
->StartOfSectionNode() == pCurrentNode
)
793 DelNodes( aRg
.aEnd
, SwNodeOffset(2) );
799 case SwNodeType::Text
:
800 //Add special function to text node.
802 if( bNewFrames
&& pCurrentNode
->GetContentNode() )
803 static_cast<SwContentNode
*>(pCurrentNode
)->DelFrames(nullptr);
804 pCurrentNode
->m_pStartOfSection
= aSttNdStack
[ nLevel
];
809 case SwNodeType::Grf
:
810 case SwNodeType::Ole
:
812 if( bNewFrames
&& pCurrentNode
->GetContentNode() )
813 static_cast<SwContentNode
*>(pCurrentNode
)->DelFrames(nullptr);
815 pCurrentNode
->m_pStartOfSection
= aSttNdStack
[ nLevel
];
819 // reset Accessibility issue state
820 pCurrentNode
->resetAndQueueAccessibilityCheck();
824 case SwNodeType::PlaceHolder
:
825 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
827 if( &rNodes
== this ) // inside UndoNodesArray
830 pCurrentNode
->m_pStartOfSection
= aSttNdStack
[ nLevel
];
833 else // move into "normal" node array
835 // than a SectionNode (start/end) is needed at the current
836 // InsPos; if so skip it, otherwise ignore current node
837 if( nInsPos
) // move everything until here
839 // delete and copy. CAUTION: all indices after
840 // "aRg.aEnd+1" will be moved as well!
841 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
842 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
844 nInsPos
= SwNodeOffset(0);
846 SwNode
* pTmpNd
= &aIdx
.GetNode();
847 if( pTmpNd
->IsSectionNode() ||
848 pTmpNd
->StartOfSectionNode()->IsSectionNode() )
853 assert(!"How can this node be in the node array?");
859 assert(!"Unknown node type");
864 if( nInsPos
) // copy remaining rest
867 SwNodeIndex
aSwIndex( aRg
.aEnd
, 1 );
868 ChgNode( aSwIndex
, nInsPos
, aIdx
, bNewFrames
);
870 ++aRg
.aEnd
; // again, exclusive end
872 // delete all resulting empty start/end node pairs
873 if( ( pCurrentNode
= &aRg
.aStart
.GetNode())->GetStartNode() &&
874 pCurrentNode
->StartOfSectionIndex() &&
875 aRg
.aEnd
.GetNode().GetEndNode() )
876 DelNodes( aRg
.aStart
, SwNodeOffset(2) );
878 // initialize numbering update
879 ++aOrigInsPos
.aStart
;
880 // Moved in same node array? Then call update top down!
881 if( this == &rNodes
&&
882 aRg
.aEnd
.GetIndex() >= aOrigInsPos
.aStart
.GetIndex() )
884 UpdateOutlineIdx( aOrigInsPos
.aStart
.GetNode() );
885 UpdateOutlineIdx( aRg
.aEnd
.GetNode() );
889 UpdateOutlineIdx( aRg
.aEnd
.GetNode() );
890 rNodes
.UpdateOutlineIdx( aOrigInsPos
.aStart
.GetNode() );
896 /** create a start/end section pair
898 * Other nodes might be in between.
900 * After this method call, the start node of pRange will be pointing to the
901 * first node after the start section node and the end node will be the index
902 * of the end section node. If this method is called multiple times with the
903 * same input, multiple sections containing the previous ones will be created
904 * (no content nodes between start or end node).
906 * @note Start and end node of the range must be on the same level but MUST
907 * NOT be on the top level.
909 * @param [IN,OUT] pRange the range (excl. end)
910 * @param eSttNdTyp type of the start node
912 void SwNodes::SectionDown(SwNodeRange
*pRange
, SwStartNodeType eSttNdTyp
)
914 if( pRange
->aStart
>= pRange
->aEnd
||
915 pRange
->aEnd
>= Count() ||
916 !::CheckNodesRange(pRange
->aStart
.GetNode(), pRange
->aEnd
.GetNode(), false))
921 // If the beginning of a range is before or at a start node position, so
922 // delete it, otherwise empty S/E or E/S nodes would be created.
923 // For other nodes, create a new start node.
924 SwNode
* pCurrentNode
= &pRange
->aStart
.GetNode();
925 SwNodeIndex
aTmpIdx( *pCurrentNode
->StartOfSectionNode() );
927 if( pCurrentNode
->GetEndNode() )
928 DelNodes( pRange
->aStart
); // prevent empty section
931 // insert a new StartNode
932 SwNode
* pSttNd
= new SwStartNode( pRange
->aStart
.GetNode(), SwNodeType::Start
, eSttNdTyp
);
933 pRange
->aStart
= *pSttNd
;
934 aTmpIdx
= pRange
->aStart
;
937 // If the end of a range is before or at a StartNode, so delete it,
938 // otherwise empty S/E or E/S nodes would be created.
939 // For other nodes, insert a new end node.
941 if( pRange
->aEnd
.GetNode().GetStartNode() )
942 DelNodes( pRange
->aEnd
);
946 // insert a new EndNode
947 new SwEndNode( pRange
->aEnd
.GetNode(), *pRange
->aStart
.GetNode().GetStartNode() );
951 SectionUpDown( aTmpIdx
, pRange
->aEnd
);
954 /** increase level of the given range
956 * The range contained in pRange will be lifted to the next higher level.
957 * This is done by adding an end node at pRange.start and a start node at
958 * pRange.end. Furthermore all indices for this range will be updated.
960 * After this method call, the start node of pRange will be pointing to the
961 * first node inside the lifted range and the end node will be pointing to the
962 * last position inside the lifted range.
964 * @param [IN,OUT] pRange the range of nodes where the level should be increased
966 void SwNodes::SectionUp(SwNodeRange
*pRange
)
968 if( pRange
->aStart
>= pRange
->aEnd
||
969 pRange
->aEnd
>= Count() ||
970 !::CheckNodesRange(pRange
->aStart
.GetNode(), pRange
->aEnd
.GetNode(), false) ||
971 ( HighestLevel( *this, *pRange
) <= 1 ))
976 // If the beginning of a range is before or at a start node position, so
977 // delete it, otherwise empty S/E or E/S nodes would be created.
978 // For other nodes, create a new start node.
979 SwNode
* pCurrentNode
= &pRange
->aStart
.GetNode();
980 SwNodeIndex
aIdx( *pCurrentNode
->StartOfSectionNode() );
981 if( pCurrentNode
->IsStartNode() ) // is StartNode itself
983 SwEndNode
* pEndNd
= pRange
->aEnd
.GetNode().GetEndNode();
984 if (pEndNd
&& pCurrentNode
== pEndNd
->m_pStartOfSection
)
986 // there was a pairwise reset, adjust only those in the range
987 SwStartNode
* pTmpSttNd
= pCurrentNode
->m_pStartOfSection
;
988 RemoveNode( pRange
->aStart
.GetIndex(), SwNodeOffset(1), true );
989 RemoveNode( pRange
->aEnd
.GetIndex(), SwNodeOffset(1), true );
991 SwNodeIndex
aTmpIdx( pRange
->aStart
);
992 while( aTmpIdx
< pRange
->aEnd
)
994 pCurrentNode
= &aTmpIdx
.GetNode();
995 pCurrentNode
->m_pStartOfSection
= pTmpSttNd
;
996 if( pCurrentNode
->IsStartNode() )
997 aTmpIdx
= pCurrentNode
->EndOfSectionIndex() + 1;
1003 DelNodes( pRange
->aStart
);
1005 else if( aIdx
== pRange
->aStart
.GetIndex()-1 ) // before StartNode
1008 new SwEndNode( pRange
->aStart
.GetNode(), *aIdx
.GetNode().GetStartNode() );
1010 // If the end of a range is before or at a StartNode, so delete it,
1011 // otherwise empty S/E or E/S nodes would be created.
1012 // For other nodes, insert a new end node.
1013 SwNodeIndex
aTmpIdx( pRange
->aEnd
);
1014 if( pRange
->aEnd
.GetNode().IsEndNode() )
1015 DelNodes( pRange
->aEnd
);
1018 new SwStartNode( pRange
->aEnd
.GetNode() );
1019 /*?? which NodeType ??*/
1020 aTmpIdx
= *pRange
->aEnd
.GetNode().EndOfSectionNode();
1024 SectionUpDown( aIdx
, aTmpIdx
);
1027 /** correct indices after movement
1029 * Update all indices after movement so that the levels are consistent again.
1031 * @param aStart index of the start node
1032 * @param aEnd index of the end point
1034 * @see SwNodes::SectionUp
1035 * @see SwNodes::SectionDown
1037 void SwNodes::SectionUpDown( const SwNodeIndex
& aStart
, const SwNodeIndex
& aEnd
)
1039 SwNodeIndex
aTmpIdx( aStart
, +1 );
1040 // array forms a stack, holding all StartOfSelections
1041 SwStartNodePointers aSttNdStack
;
1042 SwStartNode
* pTmp
= aStart
.GetNode().GetStartNode();
1043 aSttNdStack
.push_back( pTmp
);
1045 // loop until the first start node that needs to be change was found
1046 // (the indices are updated from the end node backwards to the start)
1049 SwNode
* pCurrentNode
= &aTmpIdx
.GetNode();
1050 pCurrentNode
->m_pStartOfSection
= aSttNdStack
[ aSttNdStack
.size()-1 ];
1052 if( pCurrentNode
->GetStartNode() )
1054 pTmp
= static_cast<SwStartNode
*>(pCurrentNode
);
1055 aSttNdStack
.push_back( pTmp
);
1057 else if( pCurrentNode
->GetEndNode() )
1059 SwStartNode
* pSttNd
= aSttNdStack
[ aSttNdStack
.size() - 1 ];
1060 pSttNd
->m_pEndOfSection
= static_cast<SwEndNode
*>(pCurrentNode
);
1061 aSttNdStack
.pop_back();
1062 if( !aSttNdStack
.empty() )
1063 continue; // still enough EndNodes on the stack
1065 else if( aTmpIdx
< aEnd
) // too many StartNodes
1066 // if the end is not reached, yet, get the start of the section above
1068 aSttNdStack
.insert( aSttNdStack
.begin(), pSttNd
->m_pStartOfSection
);
1070 else // finished, as soon as out of the range
1076 void SwNodes::Delete(const SwNodeIndex
&rIndex
, SwNodeOffset nNodes
)
1078 Delete(rIndex
.GetNode(), nNodes
);
1083 * This is a specific implementation of a delete function for a variable array.
1084 * It is necessary as there might be inconsistencies after deleting start or
1085 * end nodes. This method can clean those up.
1087 * @param rIndex position to delete at (unchanged afterwards)
1088 * @param nNodes number of nodes to delete (default: 1)
1090 void SwNodes::Delete(const SwNode
&rIndex
, SwNodeOffset nNodes
)
1092 int nLevel
= 0; // level counter
1093 SwNode
* pCurrentNode
;
1095 SwNodeOffset nCnt
= Count() - rIndex
.GetIndex() - 1;
1096 if( nCnt
> nNodes
) nCnt
= nNodes
;
1098 if( nCnt
== SwNodeOffset(0) ) // no count -> return
1101 SwNodeRange
aRg( rIndex
, SwNodeOffset(0), rIndex
, nCnt
-1 );
1102 // check if [rIndex..rIndex + nCnt] is larger than the range
1103 if( ( !aRg
.aStart
.GetNode().StartOfSectionIndex() &&
1104 !aRg
.aStart
.GetIndex() ) ||
1105 !::CheckNodesRange(aRg
.aStart
.GetNode(), aRg
.aEnd
.GetNode(), false))
1110 // if aEnd is not on a ContentNode, search the previous one
1111 while( ( pCurrentNode
= &aRg
.aEnd
.GetNode())->GetStartNode() ||
1112 ( pCurrentNode
->GetEndNode() &&
1113 !pCurrentNode
->m_pStartOfSection
->IsTableNode() ))
1116 nCnt
= SwNodeOffset(0);
1117 //TODO: check/improve comment
1118 // increase start so that we are able to use "<" (using "<=" might cause
1119 // problems if aEnd == aStart and aEnd is deleted, so aEnd <= aStart)
1122 bool bSaveInNodesDel
= m_bInNodesDel
;
1123 m_bInNodesDel
= true;
1124 bool bUpdateOutline
= false;
1126 // loop until everything is deleted
1127 while( aRg
.aStart
< aRg
.aEnd
)
1129 pCurrentNode
= &aRg
.aEnd
.GetNode();
1131 if( pCurrentNode
->GetEndNode() )
1133 // delete the whole section?
1134 if( pCurrentNode
->StartOfSectionIndex() > aRg
.aStart
.GetIndex() )
1136 SwTableNode
* pTableNd
= pCurrentNode
->m_pStartOfSection
->GetTableNode();
1138 pTableNd
->DelFrames();
1140 SwNode
*pNd
, *pChkNd
= pCurrentNode
->m_pStartOfSection
;
1141 SwOutlineNodes::size_type nIdxPos
;
1143 pNd
= &aRg
.aEnd
.GetNode();
1145 if( pNd
->IsTextNode() )
1147 SwTextNode
*const pTextNode(pNd
->GetTextNode());
1148 if (pTextNode
->IsOutline() &&
1149 m_aOutlineNodes
.Seek_Entry( pNd
, &nIdxPos
))
1151 // remove outline indices
1152 m_aOutlineNodes
.erase_at(nIdxPos
);
1153 bUpdateOutline
= true;
1155 pTextNode
->InvalidateNumRule();
1157 else if( pNd
->IsEndNode() &&
1158 pNd
->m_pStartOfSection
->IsTableNode() )
1159 static_cast<SwTableNode
*>(pNd
->m_pStartOfSection
)->DelFrames();
1164 } while( pNd
!= pChkNd
);
1168 RemoveNode( aRg
.aEnd
.GetIndex()+1, nCnt
, true ); // delete
1169 nCnt
= SwNodeOffset(0);
1170 --aRg
.aEnd
; // before the EndNode
1174 else if( pCurrentNode
->GetStartNode() ) // found StartNode
1176 if( nLevel
== 0 ) // decrease one level
1182 RemoveNode( aRg
.aEnd
.GetIndex(), nCnt
, true );
1183 nCnt
= SwNodeOffset(0);
1186 else // remove all nodes between start and end node (incl. both)
1188 RemoveNode( aRg
.aEnd
.GetIndex(), nCnt
+ 2, true ); // delete array
1189 nCnt
= SwNodeOffset(0);
1193 // after deletion, aEnd might point to an EndNode...
1194 // delete all empty start/end node pairs
1195 SwNode
* pTmpNode
= aRg
.aEnd
.GetNode().GetEndNode();
1198 ( pCurrentNode
= &aRg
.aEnd
.GetNode())->GetStartNode() &&
1199 pCurrentNode
->StartOfSectionIndex() )
1201 // remove end and start node
1202 DelNodes( aRg
.aEnd
, SwNodeOffset(2) );
1203 pTmpNode
= aRg
.aEnd
.GetNode().GetEndNode();
1207 else // "normal" node, so insert into TmpArray
1209 SwTextNode
* pTextNd
= pCurrentNode
->GetTextNode();
1212 if( pTextNd
->IsOutline())
1214 // delete outline indices
1215 m_aOutlineNodes
.erase( pTextNd
);
1216 bUpdateOutline
= true;
1218 if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd
))
1220 pTextNd
->InvalidateNumRule();
1223 else if( pCurrentNode
->IsContentNode() )
1224 static_cast<SwContentNode
*>(pCurrentNode
)->InvalidateNumRule();
1232 if( nCnt
!= SwNodeOffset(0) )
1233 RemoveNode( aRg
.aEnd
.GetIndex(), nCnt
, true ); // delete the rest
1235 // delete all empty start/end node pairs
1236 while( aRg
.aEnd
.GetNode().GetEndNode() &&
1237 ( pCurrentNode
= &aRg
.aStart
.GetNode())->GetStartNode() &&
1238 pCurrentNode
->StartOfSectionIndex() )
1239 // but none of the holy 5. (???)
1241 DelNodes( aRg
.aStart
, SwNodeOffset(2) ); // delete start and end node
1245 m_bInNodesDel
= bSaveInNodesDel
;
1247 if( !m_bInNodesDel
)
1250 if( bUpdateOutline
|| m_bInDelUpdOutline
)
1252 UpdateOutlineIdx( aRg
.aEnd
.GetNode() );
1253 m_bInDelUpdOutline
= false;
1259 if( bUpdateOutline
)
1260 m_bInDelUpdOutline
= true;
1264 /** get section level at the given position
1266 * @note The first node in an array should always be a start node.
1267 * Because of this, there is a special treatment here based on the
1268 * assumption that this is true in this context as well.
1270 * @param rIdx position of the node
1271 * @return section level at the given position
1273 sal_uInt16
SwNodes::GetSectionLevel(const SwNode
&rIdx
)
1275 // special treatment for 1st Node
1276 if(rIdx
.GetIndex() == SwNodeOffset(0)) return 1;
1277 // no recursion! This calls a SwNode::GetSectionLevel (missing "s")
1278 return rIdx
.GetSectionLevel();
1281 void SwNodes::GoStartOfSection(SwNodeIndex
*pIdx
)
1283 // after the next start node
1284 SwNodeIndex
aTmp( *pIdx
->GetNode().StartOfSectionNode(), +1 );
1286 // If index points to no ContentNode, then go to one.
1287 // If there is no further available, do not change the index' position!
1288 while( !aTmp
.GetNode().IsContentNode() )
1289 { // go from this StartNode (can only be one) to its end
1291 return; // ERROR: already after the section
1292 aTmp
= aTmp
.GetNode().EndOfSectionIndex()+1;
1294 return; // ERROR: already after the section
1296 (*pIdx
) = aTmp
; // is on a ContentNode
1299 void SwNodes::GoEndOfSection(SwNodeIndex
*pIdx
)
1301 if( !pIdx
->GetNode().IsEndNode() )
1302 (*pIdx
) = *pIdx
->GetNode().EndOfSectionNode();
1305 static SwContentNode
* goNext(const SwNodeIndex
& rIdx
)
1307 const SwNodes
& rNodes
= rIdx
.GetNodes();
1308 const SwNodeOffset last
= rNodes
.Count() - 1;
1309 for (SwNodeOffset
i(rIdx
.GetIndex() + 1); i
< last
; ++i
)
1310 if (SwContentNode
* pNd
= rNodes
[i
]->GetContentNode())
1316 SwContentNode
* SwNodes::GoNext(SwNodeIndex
*pIdx
)
1318 SwContentNode
* pNd
= goNext(*pIdx
);
1324 SwContentNode
* SwNodes::GoNext(SwPosition
*pIdx
)
1326 SwContentNode
* pNd
= goNext(pIdx
->nNode
);
1328 pIdx
->AssignStartIndex(*pNd
);
1332 static SwNodeOffset
startOfGlobalSection(const SwNode
& node
)
1334 const SwNodes
& rNodes
= node
.GetNodes();
1335 const SwNodeOffset pos
= node
.GetIndex();
1336 if (rNodes
.GetEndOfExtras().GetIndex() < pos
)
1337 // Regular ContentSection
1338 return rNodes
.GetEndOfExtras().GetIndex() + SwNodeOffset(1);
1339 if (rNodes
.GetEndOfAutotext().GetIndex() < pos
)
1341 return rNodes
.GetEndOfAutotext().GetIndex() + SwNodeOffset(1);
1342 if (rNodes
.GetEndOfInserts().GetIndex() < pos
)
1344 // Flys/Headers/Footers
1345 if (auto* p
= node
.FindFlyStartNode())
1346 return p
->GetIndex();
1347 if (auto* p
= node
.FindHeaderStartNode())
1348 return p
->GetIndex();
1349 if (auto* p
= node
.FindFooterStartNode())
1350 return p
->GetIndex();
1351 return rNodes
.GetEndOfInserts().GetIndex() + SwNodeOffset(1);
1353 if (rNodes
.GetEndOfPostIts().GetIndex() < pos
)
1356 if (auto* p
= node
.FindFootnoteStartNode())
1357 return p
->GetIndex();
1358 return rNodes
.GetEndOfPostIts().GetIndex() + SwNodeOffset(1);
1360 return SwNodeOffset(0);
1363 static SwContentNode
* goPrevious(const SwNodeIndex
& rIdx
, bool canCrossBoundary
)
1365 const SwNodes
& rNodes
= rIdx
.GetNodes();
1366 const SwNodeOffset
first(canCrossBoundary
? SwNodeOffset(0)
1367 : startOfGlobalSection(rIdx
.GetNode()));
1368 for (SwNodeOffset
i(rIdx
.GetIndex() - 1); i
> first
; --i
)
1369 if (SwContentNode
* pNd
= rNodes
[i
]->GetContentNode())
1375 SwContentNode
* SwNodes::GoPrevious(SwNodeIndex
* pIdx
, bool canCrossBoundary
)
1377 SwContentNode
* pNd
= goPrevious(*pIdx
, canCrossBoundary
);
1383 SwContentNode
* SwNodes::GoPrevious(SwPosition
* pIdx
, bool canCrossBoundary
)
1385 SwContentNode
* pNd
= goPrevious(pIdx
->nNode
, canCrossBoundary
);
1387 pIdx
->AssignStartIndex(*pNd
);
1391 /** Delete a number of nodes
1393 * @param rStart starting position in this nodes array
1394 * @param nCnt number of nodes to delete
1396 void SwNodes::DelNodes( const SwNodeIndex
& rStart
, SwNodeOffset nCnt
)
1398 SwNodeOffset nSttIdx
= rStart
.GetIndex();
1400 if( !nSttIdx
&& nCnt
== GetEndOfContent().GetIndex()+1 )
1402 // The whole nodes array will be destroyed, you're in the Doc's DTOR!
1403 // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
1404 SwNode
* aEndNdArr
[] = { m_pEndOfContent
.get(),
1405 m_pEndOfPostIts
, m_pEndOfInserts
,
1406 m_pEndOfAutotext
, m_pEndOfRedlines
,
1410 SwNode
** ppEndNdArr
= aEndNdArr
;
1411 while( *ppEndNdArr
)
1413 nSttIdx
= (*ppEndNdArr
)->StartOfSectionIndex() + 1;
1414 SwNodeOffset nEndIdx
= (*ppEndNdArr
)->GetIndex();
1416 if( nSttIdx
!= nEndIdx
)
1417 RemoveNode( nSttIdx
, nEndIdx
- nSttIdx
, true );
1425 for( SwNodeOffset n
= nSttIdx
, nEnd
= nSttIdx
+ nCnt
; n
< nEnd
; ++n
)
1427 SwNode
* pNd
= (*this)[ n
];
1429 if (pNd
->IsTextNode() && pNd
->GetTextNode()->IsOutline())
1431 // remove the outline indices
1432 if (m_aOutlineNodes
.erase(pNd
))
1435 if( pNd
->IsContentNode() )
1437 static_cast<SwContentNode
*>(pNd
)->InvalidateNumRule();
1438 static_cast<SwContentNode
*>(pNd
)->DelFrames(nullptr);
1441 RemoveNode( nSttIdx
, nCnt
, true );
1445 UpdateOutlineIdx( rStart
.GetNode() );
1453 sal_uInt16 nLevel
, nTop
;
1454 explicit HighLevel( sal_uInt16 nLv
) : nLevel( nLv
), nTop( nLv
) {}
1459 static bool lcl_HighestLevel( SwNode
* pNode
, void * pPara
)
1461 HighLevel
* pHL
= static_cast<HighLevel
*>(pPara
);
1462 if( pNode
->GetStartNode() )
1465 if( pHL
->nTop
> pHL
->nLevel
)
1466 pHL
->nTop
= pHL
->nLevel
;
1468 else if( pNode
->GetEndNode() )
1474 /** Calculate the highest level in a range
1476 * @param rNodes the nodes array
1477 * @param rRange the range to inspect
1478 * @return the highest level
1480 sal_uInt16
HighestLevel( SwNodes
& rNodes
, const SwNodeRange
& rRange
)
1482 HighLevel
aPara( SwNodes::GetSectionLevel( rRange
.aStart
.GetNode() ));
1483 rNodes
.ForEach( rRange
.aStart
, rRange
.aEnd
, lcl_HighestLevel
, &aPara
);
1490 * @param rPam the range to move
1491 * @param rPos to destination position in the given nodes array
1492 * @param rNodes the node array to move the range into
1494 void SwNodes::MoveRange( SwPaM
& rPam
, SwPosition
& rPos
, SwNodes
& rNodes
)
1496 auto [pStart
, pEnd
] = rPam
.StartEnd(); // SwPosition*
1498 if( !rPam
.HasMark() || *pStart
>= *pEnd
)
1501 if( this == &rNodes
&& *pStart
<= rPos
&& rPos
< *pEnd
)
1504 SwNodeIndex
aEndIdx( pEnd
->GetNode() );
1505 SwNodeIndex
aSttIdx( pStart
->GetNode() );
1506 SwTextNode
*const pSrcNd
= aSttIdx
.GetNode().GetTextNode();
1507 SwTextNode
* pDestNd
= rPos
.GetNode().GetTextNode();
1508 bool bSplitDestNd
= true;
1509 bool bCopyCollFormat
= pDestNd
&& pDestNd
->GetText().isEmpty();
1513 // if the first node is a TextNode, then there must
1514 // be also a TextNode in the NodesArray to store the content
1517 pDestNd
= rNodes
.MakeTextNode( rPos
.GetNode(), pSrcNd
->GetTextColl() );
1519 rPos
.nContent
.Assign( pDestNd
, 0 );
1520 bCopyCollFormat
= true;
1522 bSplitDestNd
= pDestNd
->Len() > rPos
.GetContentIndex() ||
1523 pEnd
->GetNode().IsTextNode();
1525 // move the content into the new node
1526 bool bOneNd
= pStart
->GetNode() == pEnd
->GetNode();
1527 const sal_Int32 nLen
=
1528 ( bOneNd
? std::min(pEnd
->GetContentIndex(), pSrcNd
->Len()) : pSrcNd
->Len() )
1529 - pStart
->GetContentIndex();
1531 if( !pEnd
->GetNode().IsContentNode() )
1534 SwNodeOffset nSttNdIdx
= pStart
->GetNodeIndex() + 1;
1535 const SwNodeOffset nEndNdIdx
= pEnd
->GetNodeIndex();
1536 for( ; nSttNdIdx
< nEndNdIdx
; ++nSttNdIdx
)
1538 if( (*this)[ nSttNdIdx
]->IsContentNode() )
1546 // templates must be copied/set after a split
1547 if( !bOneNd
&& bSplitDestNd
)
1549 if( !rPos
.GetContentIndex() )
1551 bCopyCollFormat
= true;
1553 if( rNodes
.IsDocNodes() )
1555 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1556 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1557 rInsDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
1561 pDestNd
->SplitContentNode(rPos
, nullptr);
1564 if( rPos
.GetNode() == aEndIdx
.GetNode() )
1568 bSplitDestNd
= true;
1570 pDestNd
= rNodes
[ rPos
.GetNodeIndex() - 1 ]->GetTextNode();
1573 pSrcNd
->CutText( pDestNd
, SwContentIndex( pDestNd
, pDestNd
->Len()),
1574 pStart
->nContent
, nLen
);
1579 pSrcNd
->CutText( pDestNd
, rPos
.nContent
, pStart
->nContent
, nLen
);
1582 if( bCopyCollFormat
)
1584 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1585 ::sw::UndoGuard
const undoGuard(rInsDoc
.GetIDocumentUndoRedo());
1586 pSrcNd
->CopyCollFormat( *pDestNd
);
1591 // Correct the PaM, because it might have happened that the move
1592 // went over the node borders (so the data might be in different nodes).
1593 // Also, a selection is invalidated.
1594 pEnd
->nContent
= pStart
->nContent
;
1596 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1597 rNodes
.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED
: SwFormatFieldHintWhich::REMOVED
) );
1605 if( rPos
.GetContentIndex() )
1607 if( rPos
.GetContentIndex() == pDestNd
->Len() )
1611 else if( rPos
.GetContentIndex() )
1613 // if the EndNode is split than correct the EndIdx
1614 const bool bCorrEnd
= aEndIdx
== rPos
.nNode
;
1616 // if no text is attached to the TextNode, split it
1617 if( rNodes
.IsDocNodes() )
1619 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1620 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1621 rInsDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
1625 pDestNd
->SplitContentNode(rPos
, nullptr);
1634 // at the end only an empty TextNode is left over
1635 bSplitDestNd
= true;
1638 SwTextNode
* const pEndSrcNd
= aEndIdx
.GetNode().GetTextNode();
1641 // at the end of this range a new TextNode will be created
1644 if( rPos
.GetNode() < rNodes
.GetEndOfContent() )
1650 rNodes
.MakeTextNode( rPos
.GetNode(), pEndSrcNd
->GetTextColl() );
1652 rPos
.nContent
.Assign( pDestNd
, 0 );
1656 pDestNd
= rPos
.GetNode().GetTextNode();
1659 if (pDestNd
&& pEnd
->GetContentIndex())
1661 // move the content into the new node
1662 SwContentIndex
aIdx( pEndSrcNd
, 0 );
1663 pEndSrcNd
->CutText( pDestNd
, rPos
.nContent
, aIdx
,
1664 pEnd
->GetContentIndex());
1667 if (pDestNd
&& bCopyCollFormat
)
1669 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1670 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1671 pEndSrcNd
->CopyCollFormat( *pDestNd
);
1676 if ( pSrcNd
&& aEndIdx
.GetNode().IsContentNode() )
1682 rPos
.Adjust(SwNodeOffset(1));
1686 if( aEndIdx
!= aSttIdx
)
1688 // move the nodes into the NodesArray
1689 const SwNodeOffset nSttDiff
= aSttIdx
.GetIndex() - pStart
->GetNodeIndex();
1690 SwNodeRange
aRg( aSttIdx
, aEndIdx
);
1691 MoveNodes( aRg
, rNodes
, rPos
.GetNode() );
1693 // if in the same node array, all indices are now at new positions (so correct them)
1694 if( &rNodes
== this )
1696 pStart
->nNode
= aRg
.aEnd
.GetIndex() - nSttDiff
;
1700 // if the StartNode was moved to whom the cursor pointed, so
1701 // the content must be registered in the current content!
1702 if ( pStart
->GetNode() == GetEndOfContent() )
1704 const bool bSuccess
= GoPrevious( &pStart
->nNode
);
1705 OSL_ENSURE( bSuccess
, "Move() - no ContentNode here" );
1707 pStart
->nContent
.Assign( pStart
->GetNode().GetContentNode(),
1708 pStart
->GetContentIndex() );
1709 // Correct the PaM, because it might have happened that the move
1710 // went over the node borders (so the data might be in different nodes).
1711 // Also, a selection is invalidated.
1714 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1715 rNodes
.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED
: SwFormatFieldHintWhich::REMOVED
) );
1718 ///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
1719 void SwNodes::CopyNodes( const SwNodeRange
& rRange
,
1720 SwNode
& rPos
, bool bNewFrames
, bool bTableInsDummyNode
) const
1722 SwDoc
& rDoc
= rPos
.GetDoc();
1724 SwNode
* pCurrentNode
;
1725 if( rPos
.GetIndex() == SwNodeOffset(0) ||
1726 ( (pCurrentNode
= &rPos
)->GetStartNode() &&
1727 !pCurrentNode
->StartOfSectionIndex() ))
1730 SwNodeRange
aRg( rRange
);
1732 // skip "simple" StartNodes or EndNodes
1733 while( SwNodeType::Start
== (pCurrentNode
= & aRg
.aStart
.GetNode())->GetNodeType()
1734 || ( pCurrentNode
->IsEndNode() &&
1735 !pCurrentNode
->m_pStartOfSection
->IsSectionNode() ) )
1738 const SwNode
*aEndNode
= &aRg
.aEnd
.GetNode();
1739 SwNodeOffset
nIsEndOfContent((aEndNode
== &aEndNode
->GetNodes().GetEndOfContent()) ? 1 : 0);
1741 if (SwNodeOffset(0) == nIsEndOfContent
)
1743 // if aEnd-1 points to no ContentNode, search previous one
1745 // #i107142#: if aEnd is start node of a special section, do nothing.
1746 // Otherwise this could lead to crash: going through all previous
1747 // special section nodes and then one before the first.
1748 if (aRg
.aEnd
.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
1750 while( ((pCurrentNode
= & aRg
.aEnd
.GetNode())->GetStartNode() &&
1751 !pCurrentNode
->IsSectionNode() ) ||
1752 ( pCurrentNode
->IsEndNode() &&
1753 SwNodeType::Start
== pCurrentNode
->m_pStartOfSection
->GetNodeType()) )
1761 // is there anything left to copy?
1762 if( aRg
.aStart
>= aRg
.aEnd
)
1765 // when inserting into the source range, nothing need to be done
1766 assert(&aRg
.aStart
.GetNodes() == this);
1767 assert(&aRg
.aStart
.GetNodes() == &aRg
.aEnd
.GetNodes());
1768 if( &rPos
.GetNodes() == &aRg
.aStart
.GetNodes() &&
1769 rPos
.GetIndex() >= aRg
.aStart
.GetIndex() &&
1770 rPos
.GetIndex() < aRg
.aEnd
.GetIndex() )
1773 SwNodeIndex
aInsPos( rPos
);
1774 SwNodeIndex
aOrigInsPos( rPos
, -1 ); // original insertion position
1775 int nLevel
= 0; // level counter
1777 for( SwNodeOffset nNodeCnt
= aRg
.aEnd
.GetIndex() - aRg
.aStart
.GetIndex();
1778 nNodeCnt
> SwNodeOffset(0); --nNodeCnt
)
1780 pCurrentNode
= &aRg
.aStart
.GetNode();
1781 switch( pCurrentNode
->GetNodeType() )
1783 case SwNodeType::Table
:
1784 // Does it copy a table in(to) a footnote?
1785 if( aInsPos
< rDoc
.GetNodes().GetEndOfInserts().GetIndex() &&
1786 rDoc
.GetNodes().GetEndOfInserts().StartOfSectionIndex()
1787 < aInsPos
.GetIndex() )
1789 const SwNodeOffset nDistance
=
1790 pCurrentNode
->EndOfSectionIndex() -
1791 aRg
.aStart
.GetIndex();
1792 if (nDistance
< nNodeCnt
)
1793 nNodeCnt
-= nDistance
;
1795 nNodeCnt
= SwNodeOffset(1);
1797 // insert a DummyNode for a TableNode
1798 if( bTableInsDummyNode
)
1799 new SwPlaceholderNode(aInsPos
.GetNode());
1801 // copy all of the table's nodes into the current cell
1802 for( ++aRg
.aStart
; aRg
.aStart
.GetIndex() <
1803 pCurrentNode
->EndOfSectionIndex();
1806 // insert a DummyNode for the box-StartNode?
1807 if( bTableInsDummyNode
)
1808 new SwPlaceholderNode(aInsPos
.GetNode());
1810 SwStartNode
* pSttNd
= aRg
.aStart
.GetNode().GetStartNode();
1811 CopyNodes( SwNodeRange( *pSttNd
, SwNodeOffset(+ 1),
1812 *pSttNd
->EndOfSectionNode() ),
1813 aInsPos
.GetNode(), bNewFrames
);
1815 // insert a DummyNode for the box-EndNode?
1816 if( bTableInsDummyNode
)
1817 new SwPlaceholderNode(aInsPos
.GetNode());
1818 aRg
.aStart
= *pSttNd
->EndOfSectionNode();
1820 // insert a DummyNode for the table-EndNode
1821 if( bTableInsDummyNode
)
1822 new SwPlaceholderNode(aInsPos
.GetNode());
1823 aRg
.aStart
= *pCurrentNode
->EndOfSectionNode();
1827 SwNodeIndex
nStt( aInsPos
, -1 );
1828 SwTableNode
* pTableNd
= static_cast<SwTableNode
*>(pCurrentNode
)->
1829 MakeCopy( rDoc
, aInsPos
);
1830 const SwNodeOffset nDistance
= aInsPos
.GetIndex() - nStt
.GetIndex() - 2;
1831 if (nDistance
< nNodeCnt
)
1832 nNodeCnt
-= nDistance
;
1834 nNodeCnt
= SwNodeOffset(1) - nIsEndOfContent
;
1836 aRg
.aStart
= pCurrentNode
->EndOfSectionIndex();
1838 if( bNewFrames
&& pTableNd
)
1839 pTableNd
->MakeOwnFrames();
1843 case SwNodeType::Section
:
1844 // If the end of the section is outside the copy range,
1845 // the section node will skipped, not copied!
1846 // If someone want to change this behaviour, he has to adjust the function
1847 // lcl_NonCopyCount() which relies on it.
1848 if( pCurrentNode
->EndOfSectionIndex() < aRg
.aEnd
.GetIndex() )
1850 // copy of the whole section, so create a new SectionNode
1851 SwNodeIndex
nStt( aInsPos
, -1 );
1852 SwSectionNode
* pSectNd
= static_cast<SwSectionNode
*>(pCurrentNode
)->
1853 MakeCopy( rDoc
, aInsPos
);
1855 const SwNodeOffset nDistance
= aInsPos
.GetIndex() - nStt
.GetIndex() - 2;
1856 if (nDistance
< nNodeCnt
)
1857 nNodeCnt
-= nDistance
;
1859 nNodeCnt
= SwNodeOffset(1) - nIsEndOfContent
;
1860 aRg
.aStart
= pCurrentNode
->EndOfSectionIndex();
1862 if( bNewFrames
&& pSectNd
&&
1863 !pSectNd
->GetSection().IsHidden() )
1864 pSectNd
->MakeOwnFrames(&nStt
);
1868 case SwNodeType::Start
:
1870 SwStartNode
* pTmp
= new SwStartNode( aInsPos
.GetNode(), SwNodeType::Start
,
1871 static_cast<SwStartNode
*>(pCurrentNode
)->GetStartNodeType() );
1872 new SwEndNode( aInsPos
.GetNode(), *pTmp
);
1878 case SwNodeType::End
:
1879 if( nLevel
) // complete section
1882 ++aInsPos
; // EndNode already exists
1884 else if( SwNodeOffset(1) == nNodeCnt
&& SwNodeOffset(1) == nIsEndOfContent
)
1885 // we have reached the EndOfContent node - nothing to do!
1887 else if( !pCurrentNode
->m_pStartOfSection
->IsSectionNode() )
1889 // create a section at the original InsertPosition
1890 SwNodeRange
aTmpRg( aOrigInsPos
, SwNodeOffset(1), aInsPos
);
1891 rDoc
.GetNodes().SectionDown( &aTmpRg
,
1892 pCurrentNode
->m_pStartOfSection
->GetStartNodeType() );
1896 case SwNodeType::Text
:
1897 case SwNodeType::Grf
:
1898 case SwNodeType::Ole
:
1900 static_cast<SwContentNode
*>(pCurrentNode
)->MakeCopy(
1901 rDoc
, aInsPos
.GetNode(), bNewFrames
);
1905 case SwNodeType::PlaceHolder
:
1906 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
1908 // than a SectionNode (start/end) is needed at the current
1909 // InsPos; if so skip it, otherwise ignore current node
1910 SwNode
*const pTmpNd
= & aInsPos
.GetNode();
1911 if( pTmpNd
->IsSectionNode() ||
1912 pTmpNd
->StartOfSectionNode()->IsSectionNode() )
1916 assert(!"How can this node be in the node array?");
1927 void SwNodes::DelDummyNodes( const SwNodeRange
& rRg
)
1929 SwNodeIndex
aIdx( rRg
.aStart
);
1930 while( aIdx
.GetIndex() < rRg
.aEnd
.GetIndex() )
1932 if (SwNodeType::PlaceHolder
== aIdx
.GetNode().GetNodeType())
1933 RemoveNode( aIdx
.GetIndex(), SwNodeOffset(1), true );
1939 SwStartNode
* SwNodes::MakeEmptySection( SwNode
& rWhere
,
1940 SwStartNodeType eSttNdTyp
)
1942 SwStartNode
* pSttNd
= new SwStartNode( rWhere
, SwNodeType::Start
, eSttNdTyp
);
1943 new SwEndNode( rWhere
, *pSttNd
);
1947 SwStartNode
* SwNodes::MakeTextSection( const SwNode
& rWhere
,
1948 SwStartNodeType eSttNdTyp
,
1949 SwTextFormatColl
*pColl
)
1951 SwStartNode
* pSttNd
= new SwStartNode( rWhere
, SwNodeType::Start
, eSttNdTyp
);
1952 new SwEndNode( rWhere
, *pSttNd
);
1953 MakeTextNode( SwNodeIndex( rWhere
, - 1 ).GetNode(), pColl
);
1957 static bool shouldSkipSection(const SwSectionNode
& rSectNode
, bool bSkipHidden
, bool bSkipProtect
)
1959 const SwSection
& rSect
= rSectNode
.GetSection();
1960 return (bSkipHidden
&& rSect
.CalcHiddenFlag()) || (bSkipProtect
&& rSect
.IsProtectFlag());
1963 static SwContentNode
* goNextSection(const SwNode
& rNode
, bool bSkipHidden
, bool bSkipProtect
)
1965 const SwNodes
& rNodes
= rNode
.GetNodes();
1966 const SwNodeOffset last
= rNodes
.Count() - 1;
1967 for (SwNodeOffset
i(rNode
.GetIndex()); i
< last
; ++i
)
1969 SwNode
* pNd
= rNodes
[i
];
1970 if (SwSectionNode
* pSectNd
= pNd
->GetSectionNode())
1972 if (shouldSkipSection(*pSectNd
, bSkipHidden
, bSkipProtect
))
1973 // than skip the section
1974 i
= pSectNd
->EndOfSectionIndex();
1976 else if (i
== rNode
.GetIndex()) // The first iteration
1978 if ((pSectNd
= pNd
->StartOfSectionNode()->GetSectionNode()))
1979 if (shouldSkipSection(*pSectNd
, bSkipHidden
, bSkipProtect
))
1980 // than skip the section
1981 i
= pSectNd
->EndOfSectionIndex();
1983 else if (SwContentNode
* pContentNode
= pNd
->GetContentNode())
1985 if (bSkipHidden
|| bSkipProtect
)
1986 if ((pSectNd
= pNd
->FindSectionNode()))
1987 if (shouldSkipSection(*pSectNd
, bSkipHidden
, bSkipProtect
))
1989 i
= pSectNd
->EndOfSectionIndex();
1992 return pContentNode
;
1998 //TODO: provide better documentation
1999 /** go to next section that is not protected nor hidden
2001 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
2004 * @param bSkipHidden
2005 * @param bSkipProtect
2007 * @see SwNodes::GoNext
2008 * @see SwNodes::GoPrevious
2009 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2011 SwContentNode
* SwNodes::GoNextSection(SwNodeIndex
* pIdx
, bool bSkipHidden
, bool bSkipProtect
)
2013 SwContentNode
* pNd
= goNextSection(pIdx
->GetNode(), bSkipHidden
, bSkipProtect
);
2019 //TODO: provide better documentation
2020 /** go to next section that is not protected nor hidden
2022 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
2025 * @param bSkipHidden
2026 * @param bSkipProtect
2028 * @see SwNodes::GoNext
2029 * @see SwNodes::GoPrevious
2030 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2032 SwContentNode
* SwNodes::GoNextSection(SwPosition
* pIdx
, bool bSkipHidden
, bool bSkipProtect
)
2034 SwContentNode
* pNd
= goNextSection(pIdx
->GetNode(), bSkipHidden
, bSkipProtect
);
2036 pIdx
->AssignStartIndex(*pNd
);
2040 static SwContentNode
* goPrevSection(const SwNode
& rNode
, bool bSkipHidden
, bool bSkipProtect
)
2042 const SwNodes
& rNodes
= rNode
.GetNodes();
2043 SwNodeOffset
first(startOfGlobalSection(rNode
));
2044 for (SwNodeOffset
i(rNode
.GetIndex()); i
> first
; --i
)
2046 SwNode
* pNd
= rNodes
[i
];
2047 if (pNd
->IsEndNode() || i
== rNode
.GetIndex() /* the first iteration */)
2049 if (SwSectionNode
* pSectNd
= pNd
->StartOfSectionNode()->GetSectionNode())
2051 if (shouldSkipSection(*pSectNd
, bSkipHidden
, bSkipProtect
))
2052 // then skip section
2053 i
= pSectNd
->GetIndex();
2056 else if (SwContentNode
* pContentNode
= pNd
->GetContentNode())
2058 if (bSkipHidden
|| bSkipProtect
)
2059 if (const SwSectionNode
* pSectNd
= pNd
->FindSectionNode())
2060 if (shouldSkipSection(*pSectNd
, bSkipHidden
, bSkipProtect
))
2062 i
= pSectNd
->GetIndex();
2065 return pContentNode
;
2071 ///@see SwNodes::GoNextSection
2072 SwContentNode
* SwNodes::GoPrevSection(SwNodeIndex
* pIdx
, bool bSkipHidden
, bool bSkipProtect
)
2074 SwContentNode
* pNd
= goPrevSection(pIdx
->GetNode(), bSkipHidden
, bSkipProtect
);
2080 ///@see SwNodes::GoNextSection
2081 SwContentNode
* SwNodes::GoPrevSection(SwPosition
* pIdx
, bool bSkipHidden
, bool bSkipProtect
)
2083 SwContentNode
* pNd
= goPrevSection(pIdx
->GetNode(), bSkipHidden
, bSkipProtect
);
2085 pIdx
->AssignStartIndex(*pNd
);
2089 //TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
2090 /** find the next/previous ContentNode or table node that should have layout
2091 * frames that are siblings to the ones of the node at rFrameNd.
2093 * Search is started backward with the one before rFrameNd and
2094 * forward after pEnd.
2096 * @param rFrameNd node with frames to search in
2097 * @param pEnd last node after rFrameNd that should be excluded from search
2098 * @return result node; nullptr if not found
2100 SwNode
* SwNodes::FindPrvNxtFrameNode( const SwNode
& rFrameNd
,
2101 SwNode
const*const pEnd
,
2102 SwRootFrame
const*const pLayout
) const
2104 assert(pEnd
!= nullptr); // every caller currently
2106 // no layout -> skip
2107 if (!GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell())
2110 const SwNode
*const pSttNd
= &rFrameNd
;
2112 // inside a hidden section?
2113 const SwSectionNode
*const pSectNd
= pSttNd
->IsSectionNode()
2114 ? pSttNd
->StartOfSectionNode()->FindSectionNode()
2115 : pSttNd
->FindSectionNode();
2116 if (pSectNd
&& pSectNd
->GetSection().CalcHiddenFlag())
2119 // in a table in table situation we have to assure that we don't leave the
2120 // outer table cell when the inner table is looking for a PrvNxt...
2121 const SwTableNode
*const pTableNd
= pSttNd
->IsTableNode()
2122 ? pSttNd
->StartOfSectionNode()->FindTableNode()
2123 : pSttNd
->FindTableNode();
2124 SwNodeIndex
aIdx( rFrameNd
);
2126 // search backward for a content or table node
2129 SwNode
* pFrameNd
= &aIdx
.GetNode();
2133 if (pFrameNd
->IsContentNode())
2135 // TODO why does this not check for nested tables like forward direction
2138 else if (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsTableNode())
2140 if (pLayout
== nullptr
2141 || !pLayout
->HasMergedParas()
2142 || pFrameNd
->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden
)
2144 pFrameNd
= pFrameNd
->StartOfSectionNode();
2149 aIdx
= *pFrameNd
->StartOfSectionNode();
2151 pFrameNd
= &aIdx
.GetNode();
2154 else if (pFrameNd
->IsSectionNode()
2155 || (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsSectionNode()))
2157 pFrameNd
= GoPrevSection( &aIdx
, true, false );
2158 // did we move *into* a table?
2159 if (pFrameNd
&& ::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true))
2161 for (SwTableNode
* pTable
= pFrameNd
->FindTableNode();
2162 pTable
&& pTable
->EndOfSectionIndex() < rFrameNd
.GetIndex();
2163 pTable
= pTable
->StartOfSectionNode()->FindTableNode())
2165 pFrameNd
= pTable
->EndOfSectionNode();
2167 if (pFrameNd
->IsEndNode())
2168 { // GoPrevSection() checks that text node isn't section-hidden,
2169 // so table node between can't be section-hidden either
2170 assert(pFrameNd
->StartOfSectionNode()->IsTableNode());
2171 continue; // check other hidden conditions on next iteration
2174 if ( nullptr != pFrameNd
&& !(
2175 ::CheckNodesRange( aIdx
.GetNode(), rFrameNd
, true ) &&
2176 // Never out of the table at the start
2177 pFrameNd
->FindTableNode() == pTableNd
&&
2178 // Bug 37652: Never out of the table at the end
2179 (!pFrameNd
->FindTableNode() || pFrameNd
->FindTableBoxStartNode()
2180 == pSttNd
->FindTableBoxStartNode() ) &&
2181 (!pSectNd
|| pSttNd
->IsSectionNode() ||
2182 pSectNd
->GetIndex() < pFrameNd
->GetIndex())
2185 pFrameNd
= nullptr; // no preceding content node, stop search
2190 pFrameNd
= nullptr; // no preceding content node, stop search
2193 while (pFrameNd
!= nullptr);
2195 // search forward for a content or table node
2197 aIdx
= pEnd
->GetIndex() + 1;
2198 pFrameNd
= &aIdx
.GetNode();
2202 if (pFrameNd
->IsContentNode())
2204 // Undo when merging a table with one before, if there is also one after it.
2205 // However, if the node is in a table, it needs to be returned if the
2206 // SttNode is a section or a table!
2207 SwTableNode
*const pTableNode
= pFrameNd
->FindTableNode();
2208 if (pSttNd
->IsTableNode() &&
2209 nullptr != pTableNode
&&
2211 pTableNode
!= pSttNd
->StartOfSectionNode()->FindTableNode())
2213 pFrameNd
= pTableNode
;
2217 else if (pFrameNd
->IsTableNode())
2219 if (pLayout
== nullptr
2220 || !pLayout
->HasMergedParas()
2221 || pFrameNd
->GetRedlineMergeFlag() != SwNode::Merge::Hidden
)
2227 aIdx
= *pFrameNd
->EndOfSectionNode();
2229 pFrameNd
= &aIdx
.GetNode();
2232 else if (pFrameNd
->IsSectionNode()
2233 || (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsSectionNode()))
2235 pFrameNd
= GoNextSection( &aIdx
, true, false );
2236 // did we move *into* a table?
2237 if (pFrameNd
&& ::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true))
2239 for (SwTableNode
* pTable
= pFrameNd
->FindTableNode();
2240 pTable
&& pEnd
->GetIndex() < pTable
->GetIndex();
2241 pTable
= pTable
->StartOfSectionNode()->FindTableNode())
2245 if (pFrameNd
->IsTableNode())
2246 { // GoNextSection() checks that text node isn't section-hidden,
2247 // so table node between can't be section-hidden either
2248 continue; // check other hidden conditions on next iteration
2251 // NEVER leave the section when doing this!
2253 && !(::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true)
2254 && (pFrameNd
->FindTableNode() == pTableNd
&&
2255 // NEVER go out of the table cell at the end
2256 (!pFrameNd
->FindTableNode() || pFrameNd
->FindTableBoxStartNode()
2257 == pSttNd
->FindTableBoxStartNode()))
2258 && (!pSectNd
|| pSttNd
->IsSectionNode() ||
2259 pSectNd
->EndOfSectionIndex() > pFrameNd
->GetIndex()))
2262 pFrameNd
= nullptr; // no following content node, stop search
2267 pFrameNd
= nullptr; // no preceding content node, stop search
2270 while (pFrameNd
!= nullptr);
2275 void SwNodes::ForEach( SwNodeOffset nStart
, SwNodeOffset nEnd
,
2276 FnForEach_SwNodes fn
, void* pArgs
)
2278 assert( nEnd
<= SwNodeOffset(m_nSize
) );
2279 if( nEnd
> SwNodeOffset(m_nSize
) )
2280 nEnd
= SwNodeOffset(m_nSize
);
2282 if( nStart
>= nEnd
)
2285 sal_uInt16 cur
= Index2Block( sal_Int32(nStart
) );
2286 BlockInfo
** pp
= m_ppInf
.get() + cur
;
2288 sal_uInt16 nElem
= sal_uInt16( sal_Int32(nStart
) - p
->nStart
);
2289 auto pElem
= p
->mvData
.begin() + nElem
;
2290 nElem
= p
->nElem
- nElem
;
2293 if( !(*fn
)( static_cast<SwNode
*>(*pElem
++), pArgs
) || ++nStart
>= nEnd
)
2301 pElem
= p
->mvData
.begin();
2307 void SwNodes::ForEach( const SwNodeIndex
& rStart
, const SwNodeIndex
& rEnd
,
2308 FnForEach_SwNodes fnForEach
, void* pArgs
)
2310 ForEach( rStart
.GetIndex(), rEnd
.GetIndex(), fnForEach
, pArgs
);
2313 void SwNodes::ForEach( SwNode
& rStart
, SwNode
& rEnd
,
2314 FnForEach_SwNodes fnForEach
, void* pArgs
)
2316 ForEach( rStart
.GetIndex(), rEnd
.GetIndex(), fnForEach
, pArgs
);
2319 void SwNodes::RemoveNode( SwNodeOffset nDelPos
, SwNodeOffset nSz
, bool bDel
)
2322 SwNode
*const pFirst((*this)[nDelPos
]);
2324 std::vector
<SwTextAttr
*> flys
;
2325 for (SwNodeOffset
nCnt(0); nCnt
< nSz
; nCnt
++)
2327 SwNode
* pNode
= (*this)[ nDelPos
+ nCnt
];
2328 SwTextNode
* pTextNd
= pNode
->GetTextNode();
2332 pTextNd
->RemoveFromList();
2333 // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
2334 // so their anchor still points to correct node when deleted!
2335 // NOTE: this will call RemoveNode() recursively!
2336 // so adjust our indexes to account for removed nodes
2337 SwpHints
*const pHints(pTextNd
->GetpSwpHints());
2340 SwNodeOffset
const nPos
= pTextNd
->GetIndex();
2342 for (size_t i
= 0; i
< pHints
->Count(); ++i
)
2344 SwTextAttr
*const pHint(pHints
->Get(i
));
2345 if (RES_TXTATR_FLYCNT
== pHint
->Which())
2347 flys
.push_back(pHint
);
2350 for (SwTextAttr
* pHint
: flys
)
2352 pTextNd
->DeleteAttribute(pHint
);
2353 } // pHints may be dead now
2354 SwNodeOffset
const nDiff
= nPos
- pTextNd
->GetIndex();
2359 assert(pTextNd
== (*this)[nDelPos
+ nCnt
]);
2360 assert(pFirst
== (*this)[nDelPos
]);
2363 SwTableNode
* pTableNode
= pNode
->GetTableNode();
2366 // The node that is deleted is a table node.
2367 // Need to make sure that all the redlines that are
2368 // related to this table are removed from the
2369 // 'Extra Redlines' array
2370 pTableNode
->RemoveRedlines();
2373 SwSectionNode
* pSectionNode
= pNode
->GetSectionNode();
2374 SfxViewShell
* pKitClipSh
= (comphelper::LibreOfficeKit::isActive() && pSectionNode
&& !GetDoc().IsClipBoard())
2375 ? SfxViewShell::Current() : nullptr;
2378 OUString fieldCommand
= pSectionNode
->GetSection().GetSectionName();
2379 tools::JsonWriter aJson
;
2380 aJson
.put("commandName", ".uno:DeleteSection");
2381 aJson
.put("success", true);
2383 auto result
= aJson
.startNode("result");
2384 aJson
.put("DeleteSection", fieldCommand
);
2387 pKitClipSh
->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT
, aJson
.finishAndGetAsOString());
2391 SwNodeOffset nEnd
= nDelPos
+ nSz
;
2392 SwNode
* pNew
= (*this)[ nEnd
];
2394 for (SwNodeOffset
nCnt(0); nCnt
< nSz
; nCnt
++)
2396 SwNode
* pNode
= (*this)[ nDelPos
+ nCnt
];
2397 // the assignment will de-link the entry from the ring
2398 while (pNode
->m_vIndices
)
2399 (*pNode
->m_vIndices
) = *pNew
;
2402 std::vector
<BigPtrEntry
> aTempEntries
;
2405 SwNodeOffset nCnt
= nSz
;
2406 BigPtrEntry
*pDel
= (*this)[ nDelPos
+nCnt
-1 ], *pPrev
= (*this)[ nDelPos
+nCnt
-2 ];
2408 // set temporary object
2409 // JP 24.08.98: this should actually be removed because one could
2410 // call Remove recursively, e.g. for character bound frames. However,
2411 // since there happens way too much here, this temporary object was
2412 // inserted that will be deleted in Remove again (see Bug 55406)
2413 aTempEntries
.resize(sal_Int32(nCnt
));
2418 // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
2420 BigPtrEntry
* pTempEntry
= &aTempEntries
[sal_Int32(nCnt
)];
2421 pPrev
= ReplaceTheOneAfter(pPrev
, pTempEntry
);
2423 nDelPos
= SwNodeOffset(pDel
->GetPos() + 1);
2426 BigPtrArray::Remove( sal_Int32(nDelPos
), sal_Int32(nSz
) );
2429 void SwNodes::InsertNode( SwNode
* pNode
, const SwNodeIndex
& rPos
)
2431 BigPtrEntry
* pIns
= pNode
;
2432 BigPtrArray::Insert( pIns
, sal_Int32(rPos
.GetIndex()) );
2435 void SwNodes::InsertNode( SwNode
* pNode
, SwNodeOffset nPos
)
2437 BigPtrEntry
* pIns
= pNode
;
2438 BigPtrArray::Insert( pIns
, sal_Int32(nPos
) );
2442 SwNode
* SwNodes::DocumentSectionStartNode(SwNode
* pNode
) const
2444 if (nullptr != pNode
)
2446 SwNodeIndex
aIdx(*pNode
);
2448 if (aIdx
<= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
2449 pNode
= (*this)[SwNodeOffset(0)];
2452 while ((*this)[SwNodeOffset(0)] != pNode
->StartOfSectionNode())
2453 pNode
= pNode
->StartOfSectionNode();
2460 SwNode
* SwNodes::DocumentSectionEndNode(SwNode
* pNode
) const
2462 return DocumentSectionStartNode(pNode
)->EndOfSectionNode();
2465 bool SwNodes::IsDocNodes() const
2467 return this == &m_rMyDoc
.GetNodes();
2470 void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter
) const
2472 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwNodes"));
2473 for (SwNodeOffset
i(0); i
< Count(); ++i
)
2474 (*this)[i
]->dumpAsXml(pWriter
);
2475 (void)xmlTextWriterEndElement(pWriter
);
2478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */