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_vIndices(nullptr), 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
->SetInvalidRule( true );
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 const 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 const_cast<SwDDEFieldType
*>(static_cast<const SwDDEFieldType
*>(pTyp
))->DecRefCnt();
301 const_cast<SwDDEFieldType
*>(static_cast<const 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 SwContentNode
* SwNodes::GoNext(SwNodeIndex
*pIdx
) const
1307 if( pIdx
->GetIndex() >= Count() - 1 )
1310 SwNodeIndex
aTmp(*pIdx
, +1);
1311 SwNode
* pNd
= nullptr;
1312 while( aTmp
< Count()-1 && !( pNd
= &aTmp
.GetNode())->IsContentNode() )
1315 if( aTmp
== Count()-1 )
1319 return static_cast<SwContentNode
*>(pNd
);
1322 SwContentNode
* SwNodes::GoNext(SwPosition
*pIdx
) const
1324 if( pIdx
->GetNodeIndex() >= Count() - 1 )
1327 SwNodeIndex
aTmp(pIdx
->GetNode(), +1);
1328 SwNode
* pNd
= nullptr;
1329 while( aTmp
< Count()-1 && !( pNd
= &aTmp
.GetNode())->IsContentNode() )
1332 if( aTmp
== Count()-1 )
1336 return static_cast<SwContentNode
*>(pNd
);
1339 SwContentNode
* SwNodes::GoPrevious(SwNodeIndex
*pIdx
)
1341 if( !pIdx
->GetIndex() )
1344 SwNodeIndex
aTmp( *pIdx
, -1 );
1345 SwNode
* pNd
= nullptr;
1346 while( aTmp
.GetIndex() && !( pNd
= &aTmp
.GetNode())->IsContentNode() )
1349 if( !aTmp
.GetIndex() )
1353 return static_cast<SwContentNode
*>(pNd
);
1356 SwContentNode
* SwNodes::GoPrevious(SwPosition
*pIdx
)
1358 if( !pIdx
->GetNodeIndex() )
1361 SwNodeIndex
aTmp( pIdx
->GetNode(), -1 );
1362 SwNode
* pNd
= nullptr;
1363 while( aTmp
.GetIndex() && !( pNd
= &aTmp
.GetNode())->IsContentNode() )
1366 if( !aTmp
.GetIndex() )
1370 return static_cast<SwContentNode
*>(pNd
);
1373 /** Delete a number of nodes
1375 * @param rStart starting position in this nodes array
1376 * @param nCnt number of nodes to delete
1378 void SwNodes::DelNodes( const SwNodeIndex
& rStart
, SwNodeOffset nCnt
)
1380 SwNodeOffset nSttIdx
= rStart
.GetIndex();
1382 if( !nSttIdx
&& nCnt
== GetEndOfContent().GetIndex()+1 )
1384 // The whole nodes array will be destroyed, you're in the Doc's DTOR!
1385 // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
1386 SwNode
* aEndNdArr
[] = { m_pEndOfContent
.get(),
1387 m_pEndOfPostIts
, m_pEndOfInserts
,
1388 m_pEndOfAutotext
, m_pEndOfRedlines
,
1392 SwNode
** ppEndNdArr
= aEndNdArr
;
1393 while( *ppEndNdArr
)
1395 nSttIdx
= (*ppEndNdArr
)->StartOfSectionIndex() + 1;
1396 SwNodeOffset nEndIdx
= (*ppEndNdArr
)->GetIndex();
1398 if( nSttIdx
!= nEndIdx
)
1399 RemoveNode( nSttIdx
, nEndIdx
- nSttIdx
, true );
1407 for( SwNodeOffset n
= nSttIdx
, nEnd
= nSttIdx
+ nCnt
; n
< nEnd
; ++n
)
1409 SwNode
* pNd
= (*this)[ n
];
1411 if (pNd
->IsTextNode() && pNd
->GetTextNode()->IsOutline())
1413 // remove the outline indices
1414 if (m_aOutlineNodes
.erase(pNd
))
1417 if( pNd
->IsContentNode() )
1419 static_cast<SwContentNode
*>(pNd
)->InvalidateNumRule();
1420 static_cast<SwContentNode
*>(pNd
)->DelFrames(nullptr);
1423 RemoveNode( nSttIdx
, nCnt
, true );
1427 UpdateOutlineIdx( rStart
.GetNode() );
1435 sal_uInt16 nLevel
, nTop
;
1436 explicit HighLevel( sal_uInt16 nLv
) : nLevel( nLv
), nTop( nLv
) {}
1441 static bool lcl_HighestLevel( SwNode
* pNode
, void * pPara
)
1443 HighLevel
* pHL
= static_cast<HighLevel
*>(pPara
);
1444 if( pNode
->GetStartNode() )
1447 if( pHL
->nTop
> pHL
->nLevel
)
1448 pHL
->nTop
= pHL
->nLevel
;
1450 else if( pNode
->GetEndNode() )
1456 /** Calculate the highest level in a range
1458 * @param rNodes the nodes array
1459 * @param rRange the range to inspect
1460 * @return the highest level
1462 sal_uInt16
HighestLevel( SwNodes
& rNodes
, const SwNodeRange
& rRange
)
1464 HighLevel
aPara( SwNodes::GetSectionLevel( rRange
.aStart
.GetNode() ));
1465 rNodes
.ForEach( rRange
.aStart
, rRange
.aEnd
, lcl_HighestLevel
, &aPara
);
1472 * @param rPam the range to move
1473 * @param rPos to destination position in the given nodes array
1474 * @param rNodes the node array to move the range into
1476 void SwNodes::MoveRange( SwPaM
& rPam
, SwPosition
& rPos
, SwNodes
& rNodes
)
1478 auto [pStt
, pEnd
] = rPam
.StartEnd(); // SwPosition*
1480 if( !rPam
.HasMark() || *pStt
>= *pEnd
)
1483 if( this == &rNodes
&& *pStt
<= rPos
&& rPos
< *pEnd
)
1486 SwNodeIndex
aEndIdx( pEnd
->GetNode() );
1487 SwNodeIndex
aSttIdx( pStt
->GetNode() );
1488 SwTextNode
*const pSrcNd
= aSttIdx
.GetNode().GetTextNode();
1489 SwTextNode
* pDestNd
= rPos
.GetNode().GetTextNode();
1490 bool bSplitDestNd
= true;
1491 bool bCopyCollFormat
= pDestNd
&& pDestNd
->GetText().isEmpty();
1495 // if the first node is a TextNode, then there must
1496 // be also a TextNode in the NodesArray to store the content
1499 pDestNd
= rNodes
.MakeTextNode( rPos
.GetNode(), pSrcNd
->GetTextColl() );
1501 rPos
.nContent
.Assign( pDestNd
, 0 );
1502 bCopyCollFormat
= true;
1504 bSplitDestNd
= pDestNd
->Len() > rPos
.GetContentIndex() ||
1505 pEnd
->GetNode().IsTextNode();
1507 // move the content into the new node
1508 bool bOneNd
= pStt
->GetNode() == pEnd
->GetNode();
1509 const sal_Int32 nLen
=
1510 ( bOneNd
? std::min(pEnd
->GetContentIndex(), pSrcNd
->Len()) : pSrcNd
->Len() )
1511 - pStt
->GetContentIndex();
1513 if( !pEnd
->GetNode().IsContentNode() )
1516 SwNodeOffset nSttNdIdx
= pStt
->GetNodeIndex() + 1;
1517 const SwNodeOffset nEndNdIdx
= pEnd
->GetNodeIndex();
1518 for( ; nSttNdIdx
< nEndNdIdx
; ++nSttNdIdx
)
1520 if( (*this)[ nSttNdIdx
]->IsContentNode() )
1528 // templates must be copied/set after a split
1529 if( !bOneNd
&& bSplitDestNd
)
1531 if( !rPos
.GetContentIndex() )
1533 bCopyCollFormat
= true;
1535 if( rNodes
.IsDocNodes() )
1537 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1538 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1539 rInsDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
1543 pDestNd
->SplitContentNode(rPos
, nullptr);
1546 if( rPos
.GetNode() == aEndIdx
.GetNode() )
1550 bSplitDestNd
= true;
1552 pDestNd
= rNodes
[ rPos
.GetNodeIndex() - 1 ]->GetTextNode();
1555 pSrcNd
->CutText( pDestNd
, SwContentIndex( pDestNd
, pDestNd
->Len()),
1556 pStt
->nContent
, nLen
);
1561 pSrcNd
->CutText( pDestNd
, rPos
.nContent
, pStt
->nContent
, nLen
);
1564 if( bCopyCollFormat
)
1566 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1567 ::sw::UndoGuard
const undoGuard(rInsDoc
.GetIDocumentUndoRedo());
1568 pSrcNd
->CopyCollFormat( *pDestNd
);
1573 // Correct the PaM, because it might have happened that the move
1574 // went over the node borders (so the data might be in different nodes).
1575 // Also, a selection is invalidated.
1576 pEnd
->nContent
= pStt
->nContent
;
1578 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1579 rNodes
.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED
: SwFormatFieldHintWhich::REMOVED
) );
1587 if( rPos
.GetContentIndex() )
1589 if( rPos
.GetContentIndex() == pDestNd
->Len() )
1593 else if( rPos
.GetContentIndex() )
1595 // if the EndNode is split than correct the EndIdx
1596 const bool bCorrEnd
= aEndIdx
== rPos
.nNode
;
1598 // if no text is attached to the TextNode, split it
1599 if( rNodes
.IsDocNodes() )
1601 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1602 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1603 rInsDoc
.getIDocumentContentOperations().SplitNode( rPos
, false );
1607 pDestNd
->SplitContentNode(rPos
, nullptr);
1616 // at the end only an empty TextNode is left over
1617 bSplitDestNd
= true;
1620 SwTextNode
* const pEndSrcNd
= aEndIdx
.GetNode().GetTextNode();
1623 // at the end of this range a new TextNode will be created
1626 if( rPos
.GetNode() < rNodes
.GetEndOfContent() )
1632 rNodes
.MakeTextNode( rPos
.GetNode(), pEndSrcNd
->GetTextColl() );
1634 rPos
.nContent
.Assign( pDestNd
, 0 );
1638 pDestNd
= rPos
.GetNode().GetTextNode();
1641 if (pDestNd
&& pEnd
->GetContentIndex())
1643 // move the content into the new node
1644 SwContentIndex
aIdx( pEndSrcNd
, 0 );
1645 pEndSrcNd
->CutText( pDestNd
, rPos
.nContent
, aIdx
,
1646 pEnd
->GetContentIndex());
1649 if (pDestNd
&& bCopyCollFormat
)
1651 SwDoc
& rInsDoc
= pDestNd
->GetDoc();
1652 ::sw::UndoGuard
const ug(rInsDoc
.GetIDocumentUndoRedo());
1653 pEndSrcNd
->CopyCollFormat( *pDestNd
);
1658 if ( pSrcNd
&& aEndIdx
.GetNode().IsContentNode() )
1664 rPos
.Adjust(SwNodeOffset(1));
1668 if( aEndIdx
!= aSttIdx
)
1670 // move the nodes into the NodesArray
1671 const SwNodeOffset nSttDiff
= aSttIdx
.GetIndex() - pStt
->GetNodeIndex();
1672 SwNodeRange
aRg( aSttIdx
, aEndIdx
);
1673 MoveNodes( aRg
, rNodes
, rPos
.GetNode() );
1675 // if in the same node array, all indices are now at new positions (so correct them)
1676 if( &rNodes
== this )
1678 pStt
->nNode
= aRg
.aEnd
.GetIndex() - nSttDiff
;
1682 // if the StartNode was moved to whom the cursor pointed, so
1683 // the content must be registered in the current content!
1684 if ( pStt
->GetNode() == GetEndOfContent() )
1686 const bool bSuccess
= GoPrevious( &pStt
->nNode
);
1687 OSL_ENSURE( bSuccess
, "Move() - no ContentNode here" );
1689 pStt
->nContent
.Assign( pStt
->GetNode().GetContentNode(),
1690 pStt
->GetContentIndex() );
1691 // Correct the PaM, because it might have happened that the move
1692 // went over the node borders (so the data might be in different nodes).
1693 // Also, a selection is invalidated.
1696 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1697 rNodes
.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED
: SwFormatFieldHintWhich::REMOVED
) );
1700 ///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
1701 void SwNodes::CopyNodes( const SwNodeRange
& rRange
,
1702 SwNode
& rPos
, bool bNewFrames
, bool bTableInsDummyNode
) const
1704 SwDoc
& rDoc
= rPos
.GetDoc();
1706 SwNode
* pCurrentNode
;
1707 if( rPos
.GetIndex() == SwNodeOffset(0) ||
1708 ( (pCurrentNode
= &rPos
)->GetStartNode() &&
1709 !pCurrentNode
->StartOfSectionIndex() ))
1712 SwNodeRange
aRg( rRange
);
1714 // skip "simple" StartNodes or EndNodes
1715 while( SwNodeType::Start
== (pCurrentNode
= & aRg
.aStart
.GetNode())->GetNodeType()
1716 || ( pCurrentNode
->IsEndNode() &&
1717 !pCurrentNode
->m_pStartOfSection
->IsSectionNode() ) )
1720 const SwNode
*aEndNode
= &aRg
.aEnd
.GetNode();
1721 SwNodeOffset
nIsEndOfContent((aEndNode
== &aEndNode
->GetNodes().GetEndOfContent()) ? 1 : 0);
1723 if (SwNodeOffset(0) == nIsEndOfContent
)
1725 // if aEnd-1 points to no ContentNode, search previous one
1727 // #i107142#: if aEnd is start node of a special section, do nothing.
1728 // Otherwise this could lead to crash: going through all previous
1729 // special section nodes and then one before the first.
1730 if (aRg
.aEnd
.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
1732 while( ((pCurrentNode
= & aRg
.aEnd
.GetNode())->GetStartNode() &&
1733 !pCurrentNode
->IsSectionNode() ) ||
1734 ( pCurrentNode
->IsEndNode() &&
1735 SwNodeType::Start
== pCurrentNode
->m_pStartOfSection
->GetNodeType()) )
1743 // is there anything left to copy?
1744 if( aRg
.aStart
>= aRg
.aEnd
)
1747 // when inserting into the source range, nothing need to be done
1748 OSL_ENSURE( &aRg
.aStart
.GetNodes() == this,
1749 "aRg should use this node array" );
1750 OSL_ENSURE( &aRg
.aStart
.GetNodes() == &aRg
.aEnd
.GetNodes(),
1751 "Range across different nodes arrays? You deserve punishment!");
1752 if( &rPos
.GetNodes() == &aRg
.aStart
.GetNodes() &&
1753 rPos
.GetIndex() >= aRg
.aStart
.GetIndex() &&
1754 rPos
.GetIndex() < aRg
.aEnd
.GetIndex() )
1757 SwNodeIndex
aInsPos( rPos
);
1758 SwNodeIndex
aOrigInsPos( rPos
, -1 ); // original insertion position
1759 int nLevel
= 0; // level counter
1761 for( SwNodeOffset nNodeCnt
= aRg
.aEnd
.GetIndex() - aRg
.aStart
.GetIndex();
1762 nNodeCnt
> SwNodeOffset(0); --nNodeCnt
)
1764 pCurrentNode
= &aRg
.aStart
.GetNode();
1765 switch( pCurrentNode
->GetNodeType() )
1767 case SwNodeType::Table
:
1768 // Does it copy a table in(to) a footnote?
1769 if( aInsPos
< rDoc
.GetNodes().GetEndOfInserts().GetIndex() &&
1770 rDoc
.GetNodes().GetEndOfInserts().StartOfSectionIndex()
1771 < aInsPos
.GetIndex() )
1773 const SwNodeOffset nDistance
=
1774 pCurrentNode
->EndOfSectionIndex() -
1775 aRg
.aStart
.GetIndex();
1776 if (nDistance
< nNodeCnt
)
1777 nNodeCnt
-= nDistance
;
1779 nNodeCnt
= SwNodeOffset(1);
1781 // insert a DummyNode for a TableNode
1782 if( bTableInsDummyNode
)
1783 new SwPlaceholderNode(aInsPos
.GetNode());
1785 // copy all of the table's nodes into the current cell
1786 for( ++aRg
.aStart
; aRg
.aStart
.GetIndex() <
1787 pCurrentNode
->EndOfSectionIndex();
1790 // insert a DummyNode for the box-StartNode?
1791 if( bTableInsDummyNode
)
1792 new SwPlaceholderNode(aInsPos
.GetNode());
1794 SwStartNode
* pSttNd
= aRg
.aStart
.GetNode().GetStartNode();
1795 CopyNodes( SwNodeRange( *pSttNd
, SwNodeOffset(+ 1),
1796 *pSttNd
->EndOfSectionNode() ),
1797 aInsPos
.GetNode(), bNewFrames
);
1799 // insert a DummyNode for the box-EndNode?
1800 if( bTableInsDummyNode
)
1801 new SwPlaceholderNode(aInsPos
.GetNode());
1802 aRg
.aStart
= *pSttNd
->EndOfSectionNode();
1804 // insert a DummyNode for the table-EndNode
1805 if( bTableInsDummyNode
)
1806 new SwPlaceholderNode(aInsPos
.GetNode());
1807 aRg
.aStart
= *pCurrentNode
->EndOfSectionNode();
1811 SwNodeIndex
nStt( aInsPos
, -1 );
1812 SwTableNode
* pTableNd
= static_cast<SwTableNode
*>(pCurrentNode
)->
1813 MakeCopy( rDoc
, aInsPos
);
1814 const SwNodeOffset nDistance
= aInsPos
.GetIndex() - nStt
.GetIndex() - 2;
1815 if (nDistance
< nNodeCnt
)
1816 nNodeCnt
-= nDistance
;
1818 nNodeCnt
= SwNodeOffset(1) - nIsEndOfContent
;
1820 aRg
.aStart
= pCurrentNode
->EndOfSectionIndex();
1822 if( bNewFrames
&& pTableNd
)
1823 pTableNd
->MakeOwnFrames();
1827 case SwNodeType::Section
:
1828 // If the end of the section is outside the copy range,
1829 // the section node will skipped, not copied!
1830 // If someone want to change this behaviour, he has to adjust the function
1831 // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it.
1832 if( pCurrentNode
->EndOfSectionIndex() < aRg
.aEnd
.GetIndex() )
1834 // copy of the whole section, so create a new SectionNode
1835 SwNodeIndex
nStt( aInsPos
, -1 );
1836 SwSectionNode
* pSectNd
= static_cast<SwSectionNode
*>(pCurrentNode
)->
1837 MakeCopy( rDoc
, aInsPos
);
1839 const SwNodeOffset nDistance
= aInsPos
.GetIndex() - nStt
.GetIndex() - 2;
1840 if (nDistance
< nNodeCnt
)
1841 nNodeCnt
-= nDistance
;
1843 nNodeCnt
= SwNodeOffset(1) - nIsEndOfContent
;
1844 aRg
.aStart
= pCurrentNode
->EndOfSectionIndex();
1846 if( bNewFrames
&& pSectNd
&&
1847 !pSectNd
->GetSection().IsHidden() )
1848 pSectNd
->MakeOwnFrames(&nStt
);
1852 case SwNodeType::Start
:
1854 SwStartNode
* pTmp
= new SwStartNode( aInsPos
.GetNode(), SwNodeType::Start
,
1855 static_cast<SwStartNode
*>(pCurrentNode
)->GetStartNodeType() );
1856 new SwEndNode( aInsPos
.GetNode(), *pTmp
);
1862 case SwNodeType::End
:
1863 if( nLevel
) // complete section
1866 ++aInsPos
; // EndNode already exists
1868 else if( SwNodeOffset(1) == nNodeCnt
&& SwNodeOffset(1) == nIsEndOfContent
)
1869 // we have reached the EndOfContent node - nothing to do!
1871 else if( !pCurrentNode
->m_pStartOfSection
->IsSectionNode() )
1873 // create a section at the original InsertPosition
1874 SwNodeRange
aTmpRg( aOrigInsPos
, SwNodeOffset(1), aInsPos
);
1875 rDoc
.GetNodes().SectionDown( &aTmpRg
,
1876 pCurrentNode
->m_pStartOfSection
->GetStartNodeType() );
1880 case SwNodeType::Text
:
1881 case SwNodeType::Grf
:
1882 case SwNodeType::Ole
:
1884 static_cast<SwContentNode
*>(pCurrentNode
)->MakeCopy(
1885 rDoc
, aInsPos
.GetNode(), bNewFrames
);
1889 case SwNodeType::PlaceHolder
:
1890 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
1892 // than a SectionNode (start/end) is needed at the current
1893 // InsPos; if so skip it, otherwise ignore current node
1894 SwNode
*const pTmpNd
= & aInsPos
.GetNode();
1895 if( pTmpNd
->IsSectionNode() ||
1896 pTmpNd
->StartOfSectionNode()->IsSectionNode() )
1900 assert(!"How can this node be in the node array?");
1911 void SwNodes::DelDummyNodes( const SwNodeRange
& rRg
)
1913 SwNodeIndex
aIdx( rRg
.aStart
);
1914 while( aIdx
.GetIndex() < rRg
.aEnd
.GetIndex() )
1916 if (SwNodeType::PlaceHolder
== aIdx
.GetNode().GetNodeType())
1917 RemoveNode( aIdx
.GetIndex(), SwNodeOffset(1), true );
1923 SwStartNode
* SwNodes::MakeEmptySection( SwNode
& rWhere
,
1924 SwStartNodeType eSttNdTyp
)
1926 SwStartNode
* pSttNd
= new SwStartNode( rWhere
, SwNodeType::Start
, eSttNdTyp
);
1927 new SwEndNode( rWhere
, *pSttNd
);
1931 SwStartNode
* SwNodes::MakeTextSection( const SwNode
& rWhere
,
1932 SwStartNodeType eSttNdTyp
,
1933 SwTextFormatColl
*pColl
)
1935 SwStartNode
* pSttNd
= new SwStartNode( rWhere
, SwNodeType::Start
, eSttNdTyp
);
1936 new SwEndNode( rWhere
, *pSttNd
);
1937 MakeTextNode( SwNodeIndex( rWhere
, - 1 ).GetNode(), pColl
);
1941 //TODO: provide better documentation
1942 /** go to next section that is not protected nor hidden
1944 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
1947 * @param bSkipHidden
1948 * @param bSkipProtect
1950 * @see SwNodes::GoNext
1951 * @see SwNodes::GoPrevious
1952 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
1954 SwContentNode
* SwNodes::GoNextSection( SwNodeIndex
* pIdx
,
1955 bool bSkipHidden
, bool bSkipProtect
) const
1958 SwNodeIndex
aTmp( *pIdx
);
1960 while( aTmp
< Count() - 1 )
1962 pNd
= & aTmp
.GetNode();
1963 if (SwNodeType::Section
== pNd
->GetNodeType())
1965 const SwSection
& rSect
= static_cast<const SwSectionNode
*>(pNd
)->GetSection();
1966 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
1967 (bSkipProtect
&& rSect
.IsProtectFlag()) )
1968 // than skip the section
1969 aTmp
= *pNd
->EndOfSectionNode();
1973 if( pNd
->m_pStartOfSection
->IsSectionNode() )
1975 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
1976 m_pStartOfSection
)->GetSection();
1977 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
1978 (bSkipProtect
&& rSect
.IsProtectFlag()) )
1979 // than skip the section
1980 aTmp
= *pNd
->EndOfSectionNode();
1983 else if( SwNodeType::ContentMask
& pNd
->GetNodeType() )
1985 const SwSectionNode
* pSectNd
;
1986 if( ( bSkipHidden
|| bSkipProtect
) &&
1987 nullptr != (pSectNd
= pNd
->FindSectionNode() ) &&
1988 ( ( bSkipHidden
&& pSectNd
->GetSection().IsHiddenFlag() ) ||
1989 ( bSkipProtect
&& pSectNd
->GetSection().IsProtectFlag() )) )
1991 aTmp
= *pSectNd
->EndOfSectionNode();
1996 return const_cast<SwContentNode
*>(static_cast<const SwContentNode
*>(pNd
));
2005 //TODO: provide better documentation
2006 /** go to next section that is not protected nor hidden
2008 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
2011 * @param bSkipHidden
2012 * @param bSkipProtect
2014 * @see SwNodes::GoNext
2015 * @see SwNodes::GoPrevious
2016 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2018 SwContentNode
* SwNodes::GoNextSection( SwPosition
* pIdx
,
2019 bool bSkipHidden
, bool bSkipProtect
) const
2022 SwNodeIndex
aTmp( pIdx
->GetNode() );
2024 while( aTmp
< Count() - 1 )
2026 pNd
= & aTmp
.GetNode();
2027 if (SwNodeType::Section
== pNd
->GetNodeType())
2029 const SwSection
& rSect
= static_cast<const SwSectionNode
*>(pNd
)->GetSection();
2030 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2031 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2032 // than skip the section
2033 aTmp
= *pNd
->EndOfSectionNode();
2037 if( pNd
->m_pStartOfSection
->IsSectionNode() )
2039 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
2040 m_pStartOfSection
)->GetSection();
2041 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2042 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2043 // than skip the section
2044 aTmp
= *pNd
->EndOfSectionNode();
2047 else if( SwNodeType::ContentMask
& pNd
->GetNodeType() )
2049 const SwSectionNode
* pSectNd
;
2050 if( ( bSkipHidden
|| bSkipProtect
) &&
2051 nullptr != (pSectNd
= pNd
->FindSectionNode() ) &&
2052 ( ( bSkipHidden
&& pSectNd
->GetSection().IsHiddenFlag() ) ||
2053 ( bSkipProtect
&& pSectNd
->GetSection().IsProtectFlag() )) )
2055 aTmp
= *pSectNd
->EndOfSectionNode();
2060 return const_cast<SwContentNode
*>(static_cast<const SwContentNode
*>(pNd
));
2069 ///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2070 SwContentNode
* SwNodes::GoPrevSection( SwNodeIndex
* pIdx
,
2071 bool bSkipHidden
, bool bSkipProtect
)
2074 SwNodeIndex
aTmp( *pIdx
);
2076 while( aTmp
> SwNodeOffset(0) )
2078 pNd
= & aTmp
.GetNode();
2079 if (SwNodeType::End
== pNd
->GetNodeType())
2081 if( pNd
->m_pStartOfSection
->IsSectionNode() )
2083 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
2084 m_pStartOfSection
)->GetSection();
2085 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2086 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2087 // than skip section
2088 aTmp
= *pNd
->StartOfSectionNode();
2095 if( pNd
->m_pStartOfSection
->IsSectionNode() )
2097 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
2098 m_pStartOfSection
)->GetSection();
2099 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2100 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2101 // than skip section
2102 aTmp
= *pNd
->StartOfSectionNode();
2105 else if( SwNodeType::ContentMask
& pNd
->GetNodeType() )
2107 const SwSectionNode
* pSectNd
;
2108 if( ( bSkipHidden
|| bSkipProtect
) &&
2109 nullptr != (pSectNd
= pNd
->FindSectionNode() ) &&
2110 ( ( bSkipHidden
&& pSectNd
->GetSection().IsHiddenFlag() ) ||
2111 ( bSkipProtect
&& pSectNd
->GetSection().IsProtectFlag() )) )
2118 return const_cast<SwContentNode
*>(static_cast<const SwContentNode
*>(pNd
));
2126 ///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2127 SwContentNode
* SwNodes::GoPrevSection( SwPosition
* pIdx
,
2128 bool bSkipHidden
, bool bSkipProtect
)
2131 SwNodeIndex
aTmp( pIdx
->GetNode() );
2133 while( aTmp
> SwNodeOffset(0) )
2135 pNd
= & aTmp
.GetNode();
2136 if (SwNodeType::End
== pNd
->GetNodeType())
2138 if( pNd
->m_pStartOfSection
->IsSectionNode() )
2140 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
2141 m_pStartOfSection
)->GetSection();
2142 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2143 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2144 // than skip section
2145 aTmp
= *pNd
->StartOfSectionNode();
2152 if( pNd
->m_pStartOfSection
->IsSectionNode() )
2154 const SwSection
& rSect
= static_cast<SwSectionNode
*>(pNd
->
2155 m_pStartOfSection
)->GetSection();
2156 if( (bSkipHidden
&& rSect
.IsHiddenFlag()) ||
2157 (bSkipProtect
&& rSect
.IsProtectFlag()) )
2158 // than skip section
2159 aTmp
= *pNd
->StartOfSectionNode();
2162 else if( SwNodeType::ContentMask
& pNd
->GetNodeType() )
2164 const SwSectionNode
* pSectNd
;
2165 if( ( bSkipHidden
|| bSkipProtect
) &&
2166 nullptr != (pSectNd
= pNd
->FindSectionNode() ) &&
2167 ( ( bSkipHidden
&& pSectNd
->GetSection().IsHiddenFlag() ) ||
2168 ( bSkipProtect
&& pSectNd
->GetSection().IsProtectFlag() )) )
2175 return const_cast<SwContentNode
*>(static_cast<const SwContentNode
*>(pNd
));
2183 //TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
2184 /** find the next/previous ContentNode or table node that should have layout
2185 * frames that are siblings to the ones of the node at rFrameNd.
2187 * Search is started backward with the one before rFrameNd and
2188 * forward after pEnd.
2190 * @param rFrameNd node with frames to search in
2191 * @param pEnd last node after rFrameNd that should be excluded from search
2192 * @return result node; nullptr if not found
2194 SwNode
* SwNodes::FindPrvNxtFrameNode( const SwNode
& rFrameNd
,
2195 SwNode
const*const pEnd
,
2196 SwRootFrame
const*const pLayout
) const
2198 assert(pEnd
!= nullptr); // every caller currently
2200 // no layout -> skip
2201 if (!GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell())
2204 const SwNode
*const pSttNd
= &rFrameNd
;
2206 // inside a hidden section?
2207 const SwSectionNode
*const pSectNd
= pSttNd
->IsSectionNode()
2208 ? pSttNd
->StartOfSectionNode()->FindSectionNode()
2209 : pSttNd
->FindSectionNode();
2210 if (pSectNd
&& pSectNd
->GetSection().CalcHiddenFlag())
2213 // in a table in table situation we have to assure that we don't leave the
2214 // outer table cell when the inner table is looking for a PrvNxt...
2215 const SwTableNode
*const pTableNd
= pSttNd
->IsTableNode()
2216 ? pSttNd
->StartOfSectionNode()->FindTableNode()
2217 : pSttNd
->FindTableNode();
2218 SwNodeIndex
aIdx( rFrameNd
);
2220 // search backward for a content or table node
2223 SwNode
* pFrameNd
= &aIdx
.GetNode();
2227 if (pFrameNd
->IsContentNode())
2229 // TODO why does this not check for nested tables like forward direction
2232 else if (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsTableNode())
2234 if (pLayout
== nullptr
2235 || !pLayout
->HasMergedParas()
2236 || pFrameNd
->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden
)
2238 pFrameNd
= pFrameNd
->StartOfSectionNode();
2243 aIdx
= *pFrameNd
->StartOfSectionNode();
2245 pFrameNd
= &aIdx
.GetNode();
2248 else if (pFrameNd
->IsSectionNode()
2249 || (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsSectionNode()))
2251 pFrameNd
= GoPrevSection( &aIdx
, true, false );
2252 // did we move *into* a table?
2253 if (pFrameNd
&& ::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true))
2255 for (SwTableNode
* pTable
= pFrameNd
->FindTableNode();
2256 pTable
&& pTable
->EndOfSectionIndex() < rFrameNd
.GetIndex();
2257 pTable
= pTable
->StartOfSectionNode()->FindTableNode())
2259 pFrameNd
= pTable
->EndOfSectionNode();
2261 if (pFrameNd
->IsEndNode())
2262 { // GoPrevSection() checks that text node isn't section-hidden,
2263 // so table node between can't be section-hidden either
2264 assert(pFrameNd
->StartOfSectionNode()->IsTableNode());
2265 continue; // check other hidden conditions on next iteration
2268 if ( nullptr != pFrameNd
&& !(
2269 ::CheckNodesRange( aIdx
.GetNode(), rFrameNd
, true ) &&
2270 // Never out of the table at the start
2271 pFrameNd
->FindTableNode() == pTableNd
&&
2272 // Bug 37652: Never out of the table at the end
2273 (!pFrameNd
->FindTableNode() || pFrameNd
->FindTableBoxStartNode()
2274 == pSttNd
->FindTableBoxStartNode() ) &&
2275 (!pSectNd
|| pSttNd
->IsSectionNode() ||
2276 pSectNd
->GetIndex() < pFrameNd
->GetIndex())
2279 pFrameNd
= nullptr; // no preceding content node, stop search
2284 pFrameNd
= nullptr; // no preceding content node, stop search
2287 while (pFrameNd
!= nullptr);
2289 // search forward for a content or table node
2291 aIdx
= pEnd
->GetIndex() + 1;
2292 pFrameNd
= &aIdx
.GetNode();
2296 if (pFrameNd
->IsContentNode())
2298 // Undo when merging a table with one before, if there is also one after it.
2299 // However, if the node is in a table, it needs to be returned if the
2300 // SttNode is a section or a table!
2301 SwTableNode
*const pTableNode
= pFrameNd
->FindTableNode();
2302 if (pSttNd
->IsTableNode() &&
2303 nullptr != pTableNode
&&
2305 pTableNode
!= pSttNd
->StartOfSectionNode()->FindTableNode())
2307 pFrameNd
= pTableNode
;
2311 else if (pFrameNd
->IsTableNode())
2313 if (pLayout
== nullptr
2314 || !pLayout
->HasMergedParas()
2315 || pFrameNd
->GetRedlineMergeFlag() != SwNode::Merge::Hidden
)
2321 aIdx
= *pFrameNd
->EndOfSectionNode();
2323 pFrameNd
= &aIdx
.GetNode();
2326 else if (pFrameNd
->IsSectionNode()
2327 || (pFrameNd
->IsEndNode() && pFrameNd
->StartOfSectionNode()->IsSectionNode()))
2329 pFrameNd
= GoNextSection( &aIdx
, true, false );
2330 // did we move *into* a table?
2331 if (pFrameNd
&& ::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true))
2333 for (SwTableNode
* pTable
= pFrameNd
->FindTableNode();
2334 pTable
&& pEnd
->GetIndex() < pTable
->GetIndex();
2335 pTable
= pTable
->StartOfSectionNode()->FindTableNode())
2339 if (pFrameNd
->IsTableNode())
2340 { // GoNextSection() checks that text node isn't section-hidden,
2341 // so table node between can't be section-hidden either
2342 continue; // check other hidden conditions on next iteration
2345 // NEVER leave the section when doing this!
2347 && !(::CheckNodesRange(aIdx
.GetNode(), rFrameNd
, true)
2348 && (pFrameNd
->FindTableNode() == pTableNd
&&
2349 // NEVER go out of the table cell at the end
2350 (!pFrameNd
->FindTableNode() || pFrameNd
->FindTableBoxStartNode()
2351 == pSttNd
->FindTableBoxStartNode()))
2352 && (!pSectNd
|| pSttNd
->IsSectionNode() ||
2353 pSectNd
->EndOfSectionIndex() > pFrameNd
->GetIndex()))
2356 pFrameNd
= nullptr; // no following content node, stop search
2361 pFrameNd
= nullptr; // no preceding content node, stop search
2364 while (pFrameNd
!= nullptr);
2369 void SwNodes::ForEach( SwNodeOffset nStart
, SwNodeOffset nEnd
,
2370 FnForEach_SwNodes fn
, void* pArgs
)
2372 if( nEnd
> SwNodeOffset(m_nSize
) )
2373 nEnd
= SwNodeOffset(m_nSize
);
2375 if( nStart
>= nEnd
)
2378 sal_uInt16 cur
= Index2Block( sal_Int32(nStart
) );
2379 BlockInfo
** pp
= m_ppInf
.get() + cur
;
2381 sal_uInt16 nElem
= sal_uInt16( sal_Int32(nStart
) - p
->nStart
);
2382 auto pElem
= p
->mvData
.begin() + nElem
;
2383 nElem
= p
->nElem
- nElem
;
2386 if( !(*fn
)( static_cast<SwNode
*>(*pElem
++), pArgs
) || ++nStart
>= nEnd
)
2394 pElem
= p
->mvData
.begin();
2400 void SwNodes::ForEach( const SwNodeIndex
& rStart
, const SwNodeIndex
& rEnd
,
2401 FnForEach_SwNodes fnForEach
, void* pArgs
)
2403 ForEach( rStart
.GetIndex(), rEnd
.GetIndex(), fnForEach
, pArgs
);
2406 void SwNodes::ForEach( SwNode
& rStart
, SwNode
& rEnd
,
2407 FnForEach_SwNodes fnForEach
, void* pArgs
)
2409 ForEach( rStart
.GetIndex(), rEnd
.GetIndex(), fnForEach
, pArgs
);
2412 void SwNodes::RemoveNode( SwNodeOffset nDelPos
, SwNodeOffset nSz
, bool bDel
)
2415 SwNode
*const pFirst((*this)[nDelPos
]);
2417 for (SwNodeOffset
nCnt(0); nCnt
< nSz
; nCnt
++)
2419 SwNode
* pNode
= (*this)[ nDelPos
+ nCnt
];
2420 SwTextNode
* pTextNd
= pNode
->GetTextNode();
2424 pTextNd
->RemoveFromList();
2425 // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
2426 // so their anchor still points to correct node when deleted!
2427 // NOTE: this will call RemoveNode() recursively!
2428 // so adjust our indexes to account for removed nodes
2429 SwNodeOffset
const nPos
= pTextNd
->GetIndex();
2430 SwpHints
*const pHints(pTextNd
->GetpSwpHints());
2433 std::vector
<SwTextAttr
*> flys
;
2434 for (size_t i
= 0; i
< pHints
->Count(); ++i
)
2436 SwTextAttr
*const pHint(pHints
->Get(i
));
2437 if (RES_TXTATR_FLYCNT
== pHint
->Which())
2439 flys
.push_back(pHint
);
2442 for (SwTextAttr
* pHint
: flys
)
2444 pTextNd
->DeleteAttribute(pHint
);
2445 } // pHints may be dead now
2446 SwNodeOffset
const nDiff
= nPos
- pTextNd
->GetIndex();
2451 assert(pTextNd
== (*this)[nDelPos
+ nCnt
]);
2452 assert(pFirst
== (*this)[nDelPos
]);
2455 SwTableNode
* pTableNode
= pNode
->GetTableNode();
2458 // The node that is deleted is a table node.
2459 // Need to make sure that all the redlines that are
2460 // related to this table are removed from the
2461 // 'Extra Redlines' array
2462 pTableNode
->RemoveRedlines();
2465 SwSectionNode
* pSectionNode
= pNode
->GetSectionNode();
2466 if (comphelper::LibreOfficeKit::isActive() && pSectionNode
&& !GetDoc().IsClipBoard() && SfxViewShell::Current())
2468 OUString fieldCommand
= pSectionNode
->GetSection().GetSectionName();
2469 tools::JsonWriter aJson
;
2470 aJson
.put("commandName", ".uno:DeleteSection");
2471 aJson
.put("success", true);
2473 auto result
= aJson
.startNode("result");
2474 aJson
.put("DeleteSection", fieldCommand
);
2477 SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT
, aJson
.finishAndGetAsOString());
2482 SwNodeOffset nEnd
= nDelPos
+ nSz
;
2483 SwNode
* pNew
= (*this)[ nEnd
];
2485 for (SwNodeIndex
& rIndex
: m_vIndices
->GetRingContainer())
2487 SwNodeOffset
const nIdx
= rIndex
.GetIndex();
2488 if (nDelPos
<= nIdx
&& nIdx
< nEnd
)
2492 std::vector
<BigPtrEntry
> aTempEntries
;
2495 SwNodeOffset nCnt
= nSz
;
2496 BigPtrEntry
*pDel
= (*this)[ nDelPos
+nCnt
-1 ], *pPrev
= (*this)[ nDelPos
+nCnt
-2 ];
2498 // set temporary object
2499 // JP 24.08.98: this should actually be removed because one could
2500 // call Remove recursively, e.g. for character bound frames. However,
2501 // since there happens way too much here, this temporary object was
2502 // inserted that will be deleted in Remove again (see Bug 55406)
2503 aTempEntries
.resize(sal_Int32(nCnt
));
2508 // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
2510 sal_uLong nPrevNdIdx
= pPrev
->GetPos();
2511 BigPtrEntry
* pTempEntry
= &aTempEntries
[sal_Int32(nCnt
)];
2512 BigPtrArray::Replace( nPrevNdIdx
+1, pTempEntry
);
2514 pPrev
= BigPtrArray::operator []( nPrevNdIdx
- 1 );
2515 // the accessed element can be a naked BigPtrEntry from
2516 // aTempEntries, so the downcast to SwNode* in
2517 // SwNodes::operator[] would be illegal (and unnecessary)
2519 nDelPos
= SwNodeOffset(pDel
->GetPos() + 1);
2522 BigPtrArray::Remove( sal_Int32(nDelPos
), sal_Int32(nSz
) );
2525 void SwNodes::InsertNode( SwNode
* pNode
, const SwNodeIndex
& rPos
)
2527 BigPtrEntry
* pIns
= pNode
;
2528 BigPtrArray::Insert( pIns
, sal_Int32(rPos
.GetIndex()) );
2531 void SwNodes::InsertNode( SwNode
* pNode
, SwNodeOffset nPos
)
2533 BigPtrEntry
* pIns
= pNode
;
2534 BigPtrArray::Insert( pIns
, sal_Int32(nPos
) );
2538 SwNode
* SwNodes::DocumentSectionStartNode(SwNode
* pNode
) const
2540 if (nullptr != pNode
)
2542 SwNodeIndex
aIdx(*pNode
);
2544 if (aIdx
<= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
2545 pNode
= (*this)[SwNodeOffset(0)];
2548 while ((*this)[SwNodeOffset(0)] != pNode
->StartOfSectionNode())
2549 pNode
= pNode
->StartOfSectionNode();
2556 SwNode
* SwNodes::DocumentSectionEndNode(SwNode
* pNode
) const
2558 return DocumentSectionStartNode(pNode
)->EndOfSectionNode();
2561 bool SwNodes::IsDocNodes() const
2563 return this == &m_rMyDoc
.GetNodes();
2566 void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter
) const
2568 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwNodes"));
2569 for (SwNodeOffset
i(0); i
< Count(); ++i
)
2570 (*this)[i
]->dumpAsXml(pWriter
);
2571 (void)xmlTextWriterEndElement(pWriter
);
2574 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */