Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / docnode / nodes.cxx
blobc6ecb9ccf9749f262b3c8133955cff882f88c13d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <stdlib.h>
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>
28 #include <node.hxx>
29 #include <doc.hxx>
30 #include <IDocumentUndoRedo.hxx>
31 #include <IDocumentFieldsAccess.hxx>
32 #include <IDocumentLayoutAccess.hxx>
33 #include <pam.hxx>
34 #include <txtfld.hxx>
35 #include <fmtfld.hxx>
36 #include <numrule.hxx>
37 #include <ndtxt.hxx>
38 #include <ndnotxt.hxx>
39 #include <swtable.hxx>
40 #include <section.hxx>
41 #include <ddefld.hxx>
42 #include <swddetbl.hxx>
43 #include <txtatr.hxx>
44 #include <tox.hxx>
45 #include <fmtrfmrk.hxx>
46 #include <fmtftn.hxx>
47 #include <docsh.hxx>
48 #include <rootfrm.hxx>
49 #include <txtfrm.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 );
56 /** Constructor
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;
67 SwNodeOffset nPos(0);
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();
89 /** Destructor
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.
95 SwNodes::~SwNodes()
97 m_aOutlineNodes.clear();
100 SwNodeIndex aNdIdx( *this );
101 while( true )
103 SwNode *pNode = &aNdIdx.GetNode();
104 if( pNode == m_pEndOfContent.get() )
105 break;
107 ++aNdIdx;
108 delete pNode;
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())
120 return false;
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);
177 rTextNd.AddToList();
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();
193 else
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;
204 OUString sNumRule;
205 for( SwNodeOffset n(0); n < nSz; n++ )
207 SwNode* pNd = &rDelPos.GetNode();
209 // NoTextNode keep their persistent data
210 if( pNd->IsNoTextNode() )
212 if( bSavePersData )
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
226 if( pDestDoc )
228 const SwNumRule* pNumRule = pTextNd->GetNumRule();
229 if( pNumRule && sNumRule != pNumRule->GetName() )
231 sNumRule = pNumRule->GetName();
232 SwNumRule* pDestRule = pDestDoc->FindNumRulePtr( sNumRule );
233 if( pDestRule )
234 pDestRule->SetInvalidRule( true );
235 else
236 pDestDoc->MakeNumRule( sNumRule, pNumRule );
239 else
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 );
255 if( pCNd )
257 SwTextNode* pTextNd = pCNd->GetTextNode();
258 if( pTextNd )
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(
290 SwFormatFieldHint(
291 &pTextField->GetFormatField(),
292 ( pTextField->GetFormatField().IsFieldInDoc()
293 ? SwFormatFieldHintWhich::INSERTED
294 : SwFormatFieldHintWhich::REMOVED ) ) );
296 else if( SwFieldIds::Dde == pTyp->Which() )
298 if( bToUndo )
299 const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->DecRefCnt();
300 else
301 const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->IncRefCnt();
303 static_cast<SwFormatField&>(pAttr->GetAttr())
304 .InvalidateField();
306 break;
308 case RES_TXTATR_FTN:
309 static_cast<SwFormatFootnote&>(pAttr->GetAttr())
310 .InvalidateFootnote();
311 break;
313 case RES_TXTATR_TOXMARK:
314 static_cast<SwTOXMark&>(pAttr->GetAttr())
315 .InvalidateTOXMark();
316 break;
318 case RES_TXTATR_REFMARK:
319 static_cast<SwFormatRefMark&>(pAttr->GetAttr())
320 .InvalidateRefMark();
321 break;
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);
332 break;
334 default:
335 break;
340 if( RES_CONDTXTFMTCOLL == pTextNd->GetTextColl()->Which() )
341 pTextNd->ChkCondColl();
343 else
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) );
362 if( bNewFrames )
363 bNewFrames = &GetDoc().GetNodes() == &rNds &&
364 GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
366 if( !bNewFrames )
367 return;
369 // get the frames:
370 SwNodeIndex aIdx( *pPrevInsNd, 1 );
371 SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aIdx.GetNode(),
372 rNds[ rInsPos.GetIndex() - 1 ] );
374 if( !pFrameNd )
375 return;
377 while( aIdx != rInsPos )
379 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
380 if( pCNd )
382 if( pFrameNd->IsTableNode() )
383 static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
384 else if( pFrameNd->IsSectionNode() )
385 static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
386 else
387 static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
388 pFrameNd = pCNd;
390 ++aIdx;
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)
405 * @return
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() ))
414 return false;
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() ) )
422 ++aRg.aStart;
423 --aRg.aStart;
425 // if aEnd-1 points to no ContentNode, search previous one
426 --aRg.aEnd;
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 )
432 --aRg.aEnd;
434 // if in same array, check insertion position
435 if( aRg.aStart >= aRg.aEnd )
436 return false;
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() ) )
443 return false;
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
452 // set start index
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
461 int nSectNdCnt = 0;
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 );
478 aIdx -= nInsPos;
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;
490 aRg.aEnd -= nInsPos;
492 // NEVER include nodes from the RedLineArea
493 SwNodeOffset nNd = aIdx.GetIndex();
494 bool const bInsOutlineIdx = IsInsertOutline(rNodes, nNd);
496 if( bNewFrames )
497 // delete all frames
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 );
520 else
521 pCNd = nullptr;
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();
532 else
534 // get StartNode
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
542 // searching)
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
550 if( bOutlNd )
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();
575 if( pTyp )
577 if( rNodes.IsDocNodes() )
578 pTyp->IncRefCnt();
579 else
580 pTyp->DecRefCnt();
584 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
586 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
587 pTableFormat->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
590 if( bNewFrames )
592 pTableNd->MakeOwnFrames();
594 aIdx -= nInsPos;
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(),
607 SwNodeType::Start,
608 /*?? NodeType ??*/ SwNormalStartNode );
610 nLevel++; // put the index to StartNode on the stack
611 aSttNdStack.insert( aSttNdStack.begin() + nLevel, pTmp );
613 // create EndNode
614 new SwEndNode( aIdx.GetNode(), *pTmp );
616 else if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(
617 rNodes))
619 // use placeholder in UndoNodes array
620 new SwPlaceholderNode(aIdx.GetNode());
622 else
624 // JP 18.5.2001 (Bug 70454) creating new section?
625 --aRg.aEnd;
626 break;
630 --aRg.aEnd;
631 --aIdx;
632 } while( false );
634 else
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 )
641 --aRg.aStart;
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 );
664 --aIdx;
665 pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
667 --aRg.aEnd;
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
673 if( pSctNd )
675 pSctNd->NodesArrChgd();
676 ++nSectNdCnt;
677 // tdf#132326 do not let frames survive in undo nodes
678 if (!GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
680 bNewFrames = false;
685 break;
687 case SwNodeType::Section:
688 if( !nLevel &&
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 );
698 aIdx -= nInsPos;
699 nInsPos = SwNodeOffset(0);
701 new SwPlaceholderNode(aIdx.GetNode());
702 --aRg.aEnd;
703 --aIdx;
704 break;
706 [[fallthrough]];
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 )
714 --aRg.aEnd;
715 break;
718 if( !nLevel ) // level is decreasing
720 // create decrease
721 SwNodeIndex aTmpSIdx( aOrigInsPos.aStart, 1 );
722 SwStartNode* pTmpStt = new SwStartNode( aTmpSIdx.GetNode(),
723 SwNodeType::Start,
724 static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
726 --aTmpSIdx;
728 SwNodeIndex aTmpEIdx( aOrigInsPos.aEnd );
729 new SwEndNode( aTmpEIdx.GetNode(), *pTmpStt );
730 --aTmpEIdx;
731 ++aTmpSIdx;
733 // set correct StartOfSection
734 ++aRg.aEnd;
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();
745 else
747 pCurrentNode->m_pStartOfSection = pTmpStt;
748 --aTmpEIdx;
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 );
757 aIdx -= nInsPos+1;
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()),
765 "wrong StartNode" );
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 );
774 --aRg.aEnd;
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
784 nLevel--;
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) );
794 --aRg.aEnd;
797 break;
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 ];
805 nInsPos++;
806 --aRg.aEnd;
808 break;
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 ];
816 nInsPos++;
817 --aRg.aEnd;
819 // reset Accessibility issue state
820 pCurrentNode->resetAndQueueAccessibilityCheck();
822 break;
824 case SwNodeType::PlaceHolder:
825 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
827 if( &rNodes == this ) // inside UndoNodesArray
829 // move everything
830 pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
831 nInsPos++;
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 );
843 aIdx -= nInsPos;
844 nInsPos = SwNodeOffset(0);
846 SwNode* pTmpNd = &aIdx.GetNode();
847 if( pTmpNd->IsSectionNode() ||
848 pTmpNd->StartOfSectionNode()->IsSectionNode() )
849 --aIdx; // skip
852 else {
853 assert(!"How can this node be in the node array?");
855 --aRg.aEnd;
856 break;
858 default:
859 assert(!"Unknown node type");
860 break;
864 if( nInsPos ) // copy remaining rest
866 // rest should be ok
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() );
887 else
889 UpdateOutlineIdx( aRg.aEnd.GetNode() );
890 rNodes.UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
893 return true;
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))
918 return;
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
929 else
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.
940 --pRange->aEnd;
941 if( pRange->aEnd.GetNode().GetStartNode() )
942 DelNodes( pRange->aEnd );
943 else
945 ++pRange->aEnd;
946 // insert a new EndNode
947 new SwEndNode( pRange->aEnd.GetNode(), *pRange->aStart.GetNode().GetStartNode() );
949 --pRange->aEnd;
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 ))
973 return;
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;
998 else
999 ++aTmpIdx;
1001 return ;
1003 DelNodes( pRange->aStart );
1005 else if( aIdx == pRange->aStart.GetIndex()-1 ) // before StartNode
1006 DelNodes( aIdx );
1007 else
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 );
1016 else
1018 new SwStartNode( pRange->aEnd.GetNode() );
1019 /*?? which NodeType ??*/
1020 aTmpIdx = *pRange->aEnd.GetNode().EndOfSectionNode();
1021 --pRange->aEnd;
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)
1047 for( ;; ++aTmpIdx )
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
1071 break;
1076 void SwNodes::Delete(const SwNodeIndex &rIndex, SwNodeOffset nNodes)
1078 Delete(rIndex.GetNode(), nNodes);
1081 /** delete nodes
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
1099 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))
1107 return;
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() ))
1114 --aRg.aEnd;
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)
1120 --aRg.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();
1137 if( pTableNd )
1138 pTableNd->DelFrames();
1140 SwNode *pNd, *pChkNd = pCurrentNode->m_pStartOfSection;
1141 SwOutlineNodes::size_type nIdxPos;
1142 do {
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();
1161 --aRg.aEnd;
1162 nCnt++;
1164 } while( pNd != pChkNd );
1166 else
1168 RemoveNode( aRg.aEnd.GetIndex()+1, nCnt, true ); // delete
1169 nCnt = SwNodeOffset(0);
1170 --aRg.aEnd; // before the EndNode
1171 nLevel++;
1174 else if( pCurrentNode->GetStartNode() ) // found StartNode
1176 if( nLevel == 0 ) // decrease one level
1178 if( nCnt )
1180 // now delete array
1181 ++aRg.aEnd;
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);
1190 nLevel--;
1193 // after deletion, aEnd might point to an EndNode...
1194 // delete all empty start/end node pairs
1195 SwNode* pTmpNode = aRg.aEnd.GetNode().GetEndNode();
1196 --aRg.aEnd;
1197 while( pTmpNode &&
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();
1204 --aRg.aEnd;
1207 else // "normal" node, so insert into TmpArray
1209 SwTextNode* pTextNd = pCurrentNode->GetTextNode();
1210 if( pTextNd )
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();
1226 --aRg.aEnd;
1227 nCnt++;
1231 ++aRg.aEnd;
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
1242 --aRg.aStart;
1245 m_bInNodesDel = bSaveInNodesDel;
1247 if( !m_bInNodesDel )
1249 // update numbering
1250 if( bUpdateOutline || m_bInDelUpdOutline )
1252 UpdateOutlineIdx( aRg.aEnd.GetNode() );
1253 m_bInDelUpdOutline = false;
1257 else
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
1290 if( *pIdx <= aTmp )
1291 return; // ERROR: already after the section
1292 aTmp = aTmp.GetNode().EndOfSectionIndex()+1;
1293 if( *pIdx <= aTmp )
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 )
1308 return nullptr;
1310 SwNodeIndex aTmp(*pIdx, +1);
1311 SwNode* pNd = nullptr;
1312 while( aTmp < Count()-1 && !( pNd = &aTmp.GetNode())->IsContentNode() )
1313 ++aTmp;
1315 if( aTmp == Count()-1 )
1316 pNd = nullptr;
1317 else
1318 (*pIdx) = aTmp;
1319 return static_cast<SwContentNode*>(pNd);
1322 SwContentNode* SwNodes::GoNext(SwPosition *pIdx) const
1324 if( pIdx->GetNodeIndex() >= Count() - 1 )
1325 return nullptr;
1327 SwNodeIndex aTmp(pIdx->GetNode(), +1);
1328 SwNode* pNd = nullptr;
1329 while( aTmp < Count()-1 && !( pNd = &aTmp.GetNode())->IsContentNode() )
1330 ++aTmp;
1332 if( aTmp == Count()-1 )
1333 pNd = nullptr;
1334 else
1335 pIdx->Assign(aTmp);
1336 return static_cast<SwContentNode*>(pNd);
1339 SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx)
1341 if( !pIdx->GetIndex() )
1342 return nullptr;
1344 SwNodeIndex aTmp( *pIdx, -1 );
1345 SwNode* pNd = nullptr;
1346 while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() )
1347 --aTmp;
1349 if( !aTmp.GetIndex() )
1350 pNd = nullptr;
1351 else
1352 (*pIdx) = aTmp;
1353 return static_cast<SwContentNode*>(pNd);
1356 SwContentNode* SwNodes::GoPrevious(SwPosition *pIdx)
1358 if( !pIdx->GetNodeIndex() )
1359 return nullptr;
1361 SwNodeIndex aTmp( pIdx->GetNode(), -1 );
1362 SwNode* pNd = nullptr;
1363 while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() )
1364 --aTmp;
1366 if( !aTmp.GetIndex() )
1367 pNd = nullptr;
1368 else
1369 pIdx->Assign(aTmp);
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,
1389 nullptr
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 );
1401 ++ppEndNdArr;
1404 else
1406 int bUpdateNum = 0;
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))
1415 bUpdateNum = 1;
1417 if( pNd->IsContentNode() )
1419 static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
1420 static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
1423 RemoveNode( nSttIdx, nCnt, true );
1425 // update numbering
1426 if( bUpdateNum )
1427 UpdateOutlineIdx( rStart.GetNode() );
1431 namespace {
1433 struct HighLevel
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() )
1446 pHL->nLevel++;
1447 if( pHL->nTop > pHL->nLevel )
1448 pHL->nTop = pHL->nLevel;
1450 else if( pNode->GetEndNode() )
1451 pHL->nLevel--;
1452 return true;
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 );
1466 return aPara.nTop;
1470 /** move a range
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 )
1481 return;
1483 if( this == &rNodes && *pStt <= rPos && rPos < *pEnd )
1484 return;
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();
1493 if( pSrcNd )
1495 // if the first node is a TextNode, then there must
1496 // be also a TextNode in the NodesArray to store the content
1497 if( !pDestNd )
1499 pDestNd = rNodes.MakeTextNode( rPos.GetNode(), pSrcNd->GetTextColl() );
1500 --rPos.nNode;
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() )
1515 bOneNd = true;
1516 SwNodeOffset nSttNdIdx = pStt->GetNodeIndex() + 1;
1517 const SwNodeOffset nEndNdIdx = pEnd->GetNodeIndex();
1518 for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
1520 if( (*this)[ nSttNdIdx ]->IsContentNode() )
1522 bOneNd = false;
1523 break;
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 );
1541 else
1543 pDestNd->SplitContentNode(rPos, nullptr);
1546 if( rPos.GetNode() == aEndIdx.GetNode() )
1548 --aEndIdx;
1550 bSplitDestNd = true;
1552 pDestNd = rNodes[ rPos.GetNodeIndex() - 1 ]->GetTextNode();
1553 if( nLen )
1555 pSrcNd->CutText( pDestNd, SwContentIndex( pDestNd, pDestNd->Len()),
1556 pStt->nContent, nLen );
1559 else if ( 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 );
1571 if( bOneNd )
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;
1577 rPam.DeleteMark();
1578 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1579 rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
1580 return;
1583 ++aSttIdx;
1585 else if( pDestNd )
1587 if( rPos.GetContentIndex() )
1589 if( rPos.GetContentIndex() == pDestNd->Len() )
1591 ++rPos.nNode;
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 );
1605 else
1607 pDestNd->SplitContentNode(rPos, nullptr);
1610 if ( bCorrEnd )
1612 --aEndIdx;
1616 // at the end only an empty TextNode is left over
1617 bSplitDestNd = true;
1620 SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
1621 if ( pEndSrcNd )
1623 // at the end of this range a new TextNode will be created
1624 if( !bSplitDestNd )
1626 if( rPos.GetNode() < rNodes.GetEndOfContent() )
1628 ++rPos.nNode;
1631 pDestNd =
1632 rNodes.MakeTextNode( rPos.GetNode(), pEndSrcNd->GetTextColl() );
1633 --rPos.nNode;
1634 rPos.nContent.Assign( pDestNd, 0 );
1636 else
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 );
1656 else
1658 if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
1660 ++aEndIdx;
1662 if( !bSplitDestNd )
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.
1694 *pEnd = *pStt;
1695 rPam.DeleteMark();
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() ))
1710 return;
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() ) )
1718 ++aRg.aStart;
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
1726 --aRg.aEnd;
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()) )
1737 --aRg.aEnd;
1740 ++aRg.aEnd;
1743 // is there anything left to copy?
1744 if( aRg.aStart >= aRg.aEnd )
1745 return;
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() )
1755 return;
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;
1778 else
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();
1788 ++aRg.aStart )
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();
1809 else
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;
1817 else
1818 nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
1820 aRg.aStart = pCurrentNode->EndOfSectionIndex();
1822 if( bNewFrames && pTableNd )
1823 pTableNd->MakeOwnFrames();
1825 break;
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;
1842 else
1843 nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
1844 aRg.aStart = pCurrentNode->EndOfSectionIndex();
1846 if( bNewFrames && pSectNd &&
1847 !pSectNd->GetSection().IsHidden() )
1848 pSectNd->MakeOwnFrames(&nStt);
1850 break;
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 );
1857 --aInsPos;
1858 nLevel++;
1860 break;
1862 case SwNodeType::End:
1863 if( nLevel ) // complete section
1865 --nLevel;
1866 ++aInsPos; // EndNode already exists
1868 else if( SwNodeOffset(1) == nNodeCnt && SwNodeOffset(1) == nIsEndOfContent )
1869 // we have reached the EndOfContent node - nothing to do!
1870 continue;
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() );
1878 break;
1880 case SwNodeType::Text:
1881 case SwNodeType::Grf:
1882 case SwNodeType::Ole:
1884 static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
1885 rDoc, aInsPos.GetNode(), bNewFrames);
1887 break;
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() )
1897 ++aInsPos; // skip
1899 else {
1900 assert(!"How can this node be in the node array?");
1902 break;
1904 default:
1905 assert(false);
1907 ++aRg.aStart;
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 );
1918 else
1919 ++aIdx;
1923 SwStartNode* SwNodes::MakeEmptySection( SwNode& rWhere,
1924 SwStartNodeType eSttNdTyp )
1926 SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
1927 new SwEndNode( rWhere, *pSttNd );
1928 return 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 );
1938 return pSttNd;
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
1946 * @param pIdx
1947 * @param bSkipHidden
1948 * @param bSkipProtect
1949 * @return
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
1957 bool bFirst = true;
1958 SwNodeIndex aTmp( *pIdx );
1959 const SwNode* pNd;
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();
1971 else if( bFirst )
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();
1993 else
1995 (*pIdx) = aTmp;
1996 return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
1999 ++aTmp;
2000 bFirst = false;
2002 return nullptr;
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
2010 * @param pIdx
2011 * @param bSkipHidden
2012 * @param bSkipProtect
2013 * @return
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
2021 bool bFirst = true;
2022 SwNodeIndex aTmp( pIdx->GetNode() );
2023 const SwNode* pNd;
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();
2035 else if( bFirst )
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();
2057 else
2059 pIdx->Assign(aTmp);
2060 return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
2063 ++aTmp;
2064 bFirst = false;
2066 return nullptr;
2069 ///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2070 SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx,
2071 bool bSkipHidden, bool bSkipProtect )
2073 bool bFirst = true;
2074 SwNodeIndex aTmp( *pIdx );
2075 const SwNode* pNd;
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();
2090 bFirst = false;
2092 else if( bFirst )
2094 bFirst = false;
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() )) )
2113 aTmp = *pSectNd;
2115 else
2117 (*pIdx) = aTmp;
2118 return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
2121 --aTmp;
2123 return nullptr;
2126 ///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2127 SwContentNode* SwNodes::GoPrevSection( SwPosition * pIdx,
2128 bool bSkipHidden, bool bSkipProtect )
2130 bool bFirst = true;
2131 SwNodeIndex aTmp( pIdx->GetNode() );
2132 const SwNode* pNd;
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();
2147 bFirst = false;
2149 else if( bFirst )
2151 bFirst = false;
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() )) )
2170 aTmp = *pSectNd;
2172 else
2174 pIdx->Assign(aTmp);
2175 return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
2178 --aTmp;
2180 return nullptr;
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())
2202 return nullptr;
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())
2211 return nullptr;
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
2222 --aIdx;
2223 SwNode* pFrameNd = &aIdx.GetNode();
2227 if (pFrameNd->IsContentNode())
2229 // TODO why does this not check for nested tables like forward direction
2230 return pFrameNd;
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();
2239 return pFrameNd;
2241 else
2243 aIdx = *pFrameNd->StartOfSectionNode();
2244 --aIdx;
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
2282 else
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 &&
2304 // TABLE IN TABLE:
2305 pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
2307 pFrameNd = pTableNode;
2309 return pFrameNd;
2311 else if (pFrameNd->IsTableNode())
2313 if (pLayout == nullptr
2314 || !pLayout->HasMergedParas()
2315 || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
2317 return pFrameNd;
2319 else
2321 aIdx = *pFrameNd->EndOfSectionNode();
2322 ++aIdx;
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())
2337 pFrameNd = pTable;
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!
2346 if (pFrameNd
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
2359 else
2361 pFrameNd = nullptr; // no preceding content node, stop search
2364 while (pFrameNd != nullptr);
2366 return pFrameNd;
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 )
2376 return;
2378 sal_uInt16 cur = Index2Block( sal_Int32(nStart) );
2379 BlockInfo** pp = m_ppInf.get() + cur;
2380 BlockInfo* p = *pp;
2381 sal_uInt16 nElem = sal_uInt16( sal_Int32(nStart) - p->nStart );
2382 auto pElem = p->mvData.begin() + nElem;
2383 nElem = p->nElem - nElem;
2384 for(;;)
2386 if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
2387 break;
2389 // next element
2390 if( !--nElem )
2392 // new block
2393 p = *++pp;
2394 pElem = p->mvData.begin();
2395 nElem = p->nElem;
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 )
2414 #ifndef NDEBUG
2415 SwNode *const pFirst((*this)[nDelPos]);
2416 #endif
2417 for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
2419 SwNode* pNode = (*this)[ nDelPos + nCnt ];
2420 SwTextNode * pTextNd = pNode->GetTextNode();
2422 if (pTextNd)
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());
2431 if (pHints)
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();
2447 if (nDiff)
2449 nDelPos -= nDiff;
2451 assert(pTextNd == (*this)[nDelPos + nCnt]);
2452 assert(pFirst == (*this)[nDelPos]);
2455 SwTableNode* pTableNode = pNode->GetTableNode();
2456 if (pTableNode)
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)
2489 rIndex = *pNew;
2492 std::vector<BigPtrEntry> aTempEntries;
2493 if( bDel )
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));
2505 while( nCnt-- )
2507 delete pDel;
2508 // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
2509 pDel = pPrev;
2510 sal_uLong nPrevNdIdx = pPrev->GetPos();
2511 BigPtrEntry* pTempEntry = &aTempEntries[sal_Int32(nCnt)];
2512 BigPtrArray::Replace( nPrevNdIdx+1, pTempEntry );
2513 if( nCnt )
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) );
2537 // ->#112139#
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)];
2546 else
2548 while ((*this)[SwNodeOffset(0)] != pNode->StartOfSectionNode())
2549 pNode = pNode->StartOfSectionNode();
2553 return pNode;
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: */