docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / core / docnode / nodes.cxx
blobcbe6b8df11ec8e1e24350dd5a4dfcd768a8c56b7
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_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->Invalidate();
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 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 static_cast<SwDDEFieldType*>(pTyp)->DecRefCnt();
300 else
301 static_cast<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 static SwContentNode* goNext(const SwNodeIndex& rIdx)
1307 const SwNodes& rNodes = rIdx.GetNodes();
1308 const SwNodeOffset last = rNodes.Count() - 1;
1309 for (SwNodeOffset i(rIdx.GetIndex() + 1); i < last; ++i)
1310 if (SwContentNode* pNd = rNodes[i]->GetContentNode())
1311 return pNd;
1313 return nullptr;
1316 SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx)
1318 SwContentNode* pNd = goNext(*pIdx);
1319 if (pNd)
1320 *pIdx = *pNd;
1321 return pNd;
1324 SwContentNode* SwNodes::GoNext(SwPosition *pIdx)
1326 SwContentNode* pNd = goNext(pIdx->nNode);
1327 if (pNd)
1328 pIdx->AssignStartIndex(*pNd);
1329 return pNd;
1332 static SwNodeOffset startOfGlobalSection(const SwNode& node)
1334 const SwNodes& rNodes = node.GetNodes();
1335 const SwNodeOffset pos = node.GetIndex();
1336 if (rNodes.GetEndOfExtras().GetIndex() < pos)
1337 // Regular ContentSection
1338 return rNodes.GetEndOfExtras().GetIndex() + SwNodeOffset(1);
1339 if (rNodes.GetEndOfAutotext().GetIndex() < pos)
1340 // Redlines
1341 return rNodes.GetEndOfAutotext().GetIndex() + SwNodeOffset(1);
1342 if (rNodes.GetEndOfInserts().GetIndex() < pos)
1344 // Flys/Headers/Footers
1345 if (auto* p = node.FindFlyStartNode())
1346 return p->GetIndex();
1347 if (auto* p = node.FindHeaderStartNode())
1348 return p->GetIndex();
1349 if (auto* p = node.FindFooterStartNode())
1350 return p->GetIndex();
1351 return rNodes.GetEndOfInserts().GetIndex() + SwNodeOffset(1);
1353 if (rNodes.GetEndOfPostIts().GetIndex() < pos)
1355 // Footnotes
1356 if (auto* p = node.FindFootnoteStartNode())
1357 return p->GetIndex();
1358 return rNodes.GetEndOfPostIts().GetIndex() + SwNodeOffset(1);
1360 return SwNodeOffset(0);
1363 static SwContentNode* goPrevious(const SwNodeIndex& rIdx, bool canCrossBoundary)
1365 const SwNodes& rNodes = rIdx.GetNodes();
1366 const SwNodeOffset first(canCrossBoundary ? SwNodeOffset(0)
1367 : startOfGlobalSection(rIdx.GetNode()));
1368 for (SwNodeOffset i(rIdx.GetIndex() - 1); i > first; --i)
1369 if (SwContentNode* pNd = rNodes[i]->GetContentNode())
1370 return pNd;
1372 return nullptr;
1375 SwContentNode* SwNodes::GoPrevious(SwNodeIndex* pIdx, bool canCrossBoundary)
1377 SwContentNode* pNd = goPrevious(*pIdx, canCrossBoundary);
1378 if (pNd)
1379 *pIdx = *pNd;
1380 return pNd;
1383 SwContentNode* SwNodes::GoPrevious(SwPosition* pIdx, bool canCrossBoundary)
1385 SwContentNode* pNd = goPrevious(pIdx->nNode, canCrossBoundary);
1386 if (pNd)
1387 pIdx->AssignStartIndex(*pNd);
1388 return pNd;
1391 /** Delete a number of nodes
1393 * @param rStart starting position in this nodes array
1394 * @param nCnt number of nodes to delete
1396 void SwNodes::DelNodes( const SwNodeIndex & rStart, SwNodeOffset nCnt )
1398 SwNodeOffset nSttIdx = rStart.GetIndex();
1400 if( !nSttIdx && nCnt == GetEndOfContent().GetIndex()+1 )
1402 // The whole nodes array will be destroyed, you're in the Doc's DTOR!
1403 // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
1404 SwNode* aEndNdArr[] = { m_pEndOfContent.get(),
1405 m_pEndOfPostIts, m_pEndOfInserts,
1406 m_pEndOfAutotext, m_pEndOfRedlines,
1407 nullptr
1410 SwNode** ppEndNdArr = aEndNdArr;
1411 while( *ppEndNdArr )
1413 nSttIdx = (*ppEndNdArr)->StartOfSectionIndex() + 1;
1414 SwNodeOffset nEndIdx = (*ppEndNdArr)->GetIndex();
1416 if( nSttIdx != nEndIdx )
1417 RemoveNode( nSttIdx, nEndIdx - nSttIdx, true );
1419 ++ppEndNdArr;
1422 else
1424 int bUpdateNum = 0;
1425 for( SwNodeOffset n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n )
1427 SwNode* pNd = (*this)[ n ];
1429 if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline())
1431 // remove the outline indices
1432 if (m_aOutlineNodes.erase(pNd))
1433 bUpdateNum = 1;
1435 if( pNd->IsContentNode() )
1437 static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
1438 static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
1441 RemoveNode( nSttIdx, nCnt, true );
1443 // update numbering
1444 if( bUpdateNum )
1445 UpdateOutlineIdx( rStart.GetNode() );
1449 namespace {
1451 struct HighLevel
1453 sal_uInt16 nLevel, nTop;
1454 explicit HighLevel( sal_uInt16 nLv ) : nLevel( nLv ), nTop( nLv ) {}
1459 static bool lcl_HighestLevel( SwNode* pNode, void * pPara )
1461 HighLevel * pHL = static_cast<HighLevel*>(pPara);
1462 if( pNode->GetStartNode() )
1464 pHL->nLevel++;
1465 if( pHL->nTop > pHL->nLevel )
1466 pHL->nTop = pHL->nLevel;
1468 else if( pNode->GetEndNode() )
1469 pHL->nLevel--;
1470 return true;
1474 /** Calculate the highest level in a range
1476 * @param rNodes the nodes array
1477 * @param rRange the range to inspect
1478 * @return the highest level
1480 sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange )
1482 HighLevel aPara( SwNodes::GetSectionLevel( rRange.aStart.GetNode() ));
1483 rNodes.ForEach( rRange.aStart, rRange.aEnd, lcl_HighestLevel, &aPara );
1484 return aPara.nTop;
1488 /** move a range
1490 * @param rPam the range to move
1491 * @param rPos to destination position in the given nodes array
1492 * @param rNodes the node array to move the range into
1494 void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes )
1496 auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
1498 if( !rPam.HasMark() || *pStart >= *pEnd )
1499 return;
1501 if( this == &rNodes && *pStart <= rPos && rPos < *pEnd )
1502 return;
1504 SwNodeIndex aEndIdx( pEnd->GetNode() );
1505 SwNodeIndex aSttIdx( pStart->GetNode() );
1506 SwTextNode *const pSrcNd = aSttIdx.GetNode().GetTextNode();
1507 SwTextNode * pDestNd = rPos.GetNode().GetTextNode();
1508 bool bSplitDestNd = true;
1509 bool bCopyCollFormat = pDestNd && pDestNd->GetText().isEmpty();
1511 if( pSrcNd )
1513 // if the first node is a TextNode, then there must
1514 // be also a TextNode in the NodesArray to store the content
1515 if( !pDestNd )
1517 pDestNd = rNodes.MakeTextNode( rPos.GetNode(), pSrcNd->GetTextColl() );
1518 --rPos.nNode;
1519 rPos.nContent.Assign( pDestNd, 0 );
1520 bCopyCollFormat = true;
1522 bSplitDestNd = pDestNd->Len() > rPos.GetContentIndex() ||
1523 pEnd->GetNode().IsTextNode();
1525 // move the content into the new node
1526 bool bOneNd = pStart->GetNode() == pEnd->GetNode();
1527 const sal_Int32 nLen =
1528 ( bOneNd ? std::min(pEnd->GetContentIndex(), pSrcNd->Len()) : pSrcNd->Len() )
1529 - pStart->GetContentIndex();
1531 if( !pEnd->GetNode().IsContentNode() )
1533 bOneNd = true;
1534 SwNodeOffset nSttNdIdx = pStart->GetNodeIndex() + 1;
1535 const SwNodeOffset nEndNdIdx = pEnd->GetNodeIndex();
1536 for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
1538 if( (*this)[ nSttNdIdx ]->IsContentNode() )
1540 bOneNd = false;
1541 break;
1546 // templates must be copied/set after a split
1547 if( !bOneNd && bSplitDestNd )
1549 if( !rPos.GetContentIndex() )
1551 bCopyCollFormat = true;
1553 if( rNodes.IsDocNodes() )
1555 SwDoc& rInsDoc = pDestNd->GetDoc();
1556 ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
1557 rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
1559 else
1561 pDestNd->SplitContentNode(rPos, nullptr);
1564 if( rPos.GetNode() == aEndIdx.GetNode() )
1566 --aEndIdx;
1568 bSplitDestNd = true;
1570 pDestNd = rNodes[ rPos.GetNodeIndex() - 1 ]->GetTextNode();
1571 if( nLen )
1573 pSrcNd->CutText( pDestNd, SwContentIndex( pDestNd, pDestNd->Len()),
1574 pStart->nContent, nLen );
1577 else if ( nLen )
1579 pSrcNd->CutText( pDestNd, rPos.nContent, pStart->nContent, nLen );
1582 if( bCopyCollFormat )
1584 SwDoc& rInsDoc = pDestNd->GetDoc();
1585 ::sw::UndoGuard const undoGuard(rInsDoc.GetIDocumentUndoRedo());
1586 pSrcNd->CopyCollFormat( *pDestNd );
1589 if( bOneNd )
1591 // Correct the PaM, because it might have happened that the move
1592 // went over the node borders (so the data might be in different nodes).
1593 // Also, a selection is invalidated.
1594 pEnd->nContent = pStart->nContent;
1595 rPam.DeleteMark();
1596 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1597 rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
1598 return;
1601 ++aSttIdx;
1603 else if( pDestNd )
1605 if( rPos.GetContentIndex() )
1607 if( rPos.GetContentIndex() == pDestNd->Len() )
1609 ++rPos.nNode;
1611 else if( rPos.GetContentIndex() )
1613 // if the EndNode is split than correct the EndIdx
1614 const bool bCorrEnd = aEndIdx == rPos.nNode;
1616 // if no text is attached to the TextNode, split it
1617 if( rNodes.IsDocNodes() )
1619 SwDoc& rInsDoc = pDestNd->GetDoc();
1620 ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
1621 rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
1623 else
1625 pDestNd->SplitContentNode(rPos, nullptr);
1628 if ( bCorrEnd )
1630 --aEndIdx;
1634 // at the end only an empty TextNode is left over
1635 bSplitDestNd = true;
1638 SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
1639 if ( pEndSrcNd )
1641 // at the end of this range a new TextNode will be created
1642 if( !bSplitDestNd )
1644 if( rPos.GetNode() < rNodes.GetEndOfContent() )
1646 ++rPos.nNode;
1649 pDestNd =
1650 rNodes.MakeTextNode( rPos.GetNode(), pEndSrcNd->GetTextColl() );
1651 --rPos.nNode;
1652 rPos.nContent.Assign( pDestNd, 0 );
1654 else
1656 pDestNd = rPos.GetNode().GetTextNode();
1659 if (pDestNd && pEnd->GetContentIndex())
1661 // move the content into the new node
1662 SwContentIndex aIdx( pEndSrcNd, 0 );
1663 pEndSrcNd->CutText( pDestNd, rPos.nContent, aIdx,
1664 pEnd->GetContentIndex());
1667 if (pDestNd && bCopyCollFormat)
1669 SwDoc& rInsDoc = pDestNd->GetDoc();
1670 ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
1671 pEndSrcNd->CopyCollFormat( *pDestNd );
1674 else
1676 if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
1678 ++aEndIdx;
1680 if( !bSplitDestNd )
1682 rPos.Adjust(SwNodeOffset(1));
1686 if( aEndIdx != aSttIdx )
1688 // move the nodes into the NodesArray
1689 const SwNodeOffset nSttDiff = aSttIdx.GetIndex() - pStart->GetNodeIndex();
1690 SwNodeRange aRg( aSttIdx, aEndIdx );
1691 MoveNodes( aRg, rNodes, rPos.GetNode() );
1693 // if in the same node array, all indices are now at new positions (so correct them)
1694 if( &rNodes == this )
1696 pStart->nNode = aRg.aEnd.GetIndex() - nSttDiff;
1700 // if the StartNode was moved to whom the cursor pointed, so
1701 // the content must be registered in the current content!
1702 if ( pStart->GetNode() == GetEndOfContent() )
1704 const bool bSuccess = GoPrevious( &pStart->nNode );
1705 OSL_ENSURE( bSuccess, "Move() - no ContentNode here" );
1707 pStart->nContent.Assign( pStart->GetNode().GetContentNode(),
1708 pStart->GetContentIndex() );
1709 // Correct the PaM, because it might have happened that the move
1710 // went over the node borders (so the data might be in different nodes).
1711 // Also, a selection is invalidated.
1712 *pEnd = *pStart;
1713 rPam.DeleteMark();
1714 GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
1715 rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
1718 ///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
1719 void SwNodes::CopyNodes( const SwNodeRange& rRange,
1720 SwNode& rPos, bool bNewFrames, bool bTableInsDummyNode ) const
1722 SwDoc& rDoc = rPos.GetDoc();
1724 SwNode * pCurrentNode;
1725 if( rPos.GetIndex() == SwNodeOffset(0) ||
1726 ( (pCurrentNode = &rPos)->GetStartNode() &&
1727 !pCurrentNode->StartOfSectionIndex() ))
1728 return;
1730 SwNodeRange aRg( rRange );
1732 // skip "simple" StartNodes or EndNodes
1733 while( SwNodeType::Start == (pCurrentNode = & aRg.aStart.GetNode())->GetNodeType()
1734 || ( pCurrentNode->IsEndNode() &&
1735 !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
1736 ++aRg.aStart;
1738 const SwNode *aEndNode = &aRg.aEnd.GetNode();
1739 SwNodeOffset nIsEndOfContent((aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0);
1741 if (SwNodeOffset(0) == nIsEndOfContent)
1743 // if aEnd-1 points to no ContentNode, search previous one
1744 --aRg.aEnd;
1745 // #i107142#: if aEnd is start node of a special section, do nothing.
1746 // Otherwise this could lead to crash: going through all previous
1747 // special section nodes and then one before the first.
1748 if (aRg.aEnd.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
1750 while( ((pCurrentNode = & aRg.aEnd.GetNode())->GetStartNode() &&
1751 !pCurrentNode->IsSectionNode() ) ||
1752 ( pCurrentNode->IsEndNode() &&
1753 SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) )
1755 --aRg.aEnd;
1758 ++aRg.aEnd;
1761 // is there anything left to copy?
1762 if( aRg.aStart >= aRg.aEnd )
1763 return;
1765 // when inserting into the source range, nothing need to be done
1766 assert(&aRg.aStart.GetNodes() == this);
1767 assert(&aRg.aStart.GetNodes() == &aRg.aEnd.GetNodes());
1768 if( &rPos.GetNodes() == &aRg.aStart.GetNodes() &&
1769 rPos.GetIndex() >= aRg.aStart.GetIndex() &&
1770 rPos.GetIndex() < aRg.aEnd.GetIndex() )
1771 return;
1773 SwNodeIndex aInsPos( rPos );
1774 SwNodeIndex aOrigInsPos( rPos, -1 ); // original insertion position
1775 int nLevel = 0; // level counter
1777 for( SwNodeOffset nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
1778 nNodeCnt > SwNodeOffset(0); --nNodeCnt )
1780 pCurrentNode = &aRg.aStart.GetNode();
1781 switch( pCurrentNode->GetNodeType() )
1783 case SwNodeType::Table:
1784 // Does it copy a table in(to) a footnote?
1785 if( aInsPos < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
1786 rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex()
1787 < aInsPos.GetIndex() )
1789 const SwNodeOffset nDistance =
1790 pCurrentNode->EndOfSectionIndex() -
1791 aRg.aStart.GetIndex();
1792 if (nDistance < nNodeCnt)
1793 nNodeCnt -= nDistance;
1794 else
1795 nNodeCnt = SwNodeOffset(1);
1797 // insert a DummyNode for a TableNode
1798 if( bTableInsDummyNode )
1799 new SwPlaceholderNode(aInsPos.GetNode());
1801 // copy all of the table's nodes into the current cell
1802 for( ++aRg.aStart; aRg.aStart.GetIndex() <
1803 pCurrentNode->EndOfSectionIndex();
1804 ++aRg.aStart )
1806 // insert a DummyNode for the box-StartNode?
1807 if( bTableInsDummyNode )
1808 new SwPlaceholderNode(aInsPos.GetNode());
1810 SwStartNode* pSttNd = aRg.aStart.GetNode().GetStartNode();
1811 CopyNodes( SwNodeRange( *pSttNd, SwNodeOffset(+ 1),
1812 *pSttNd->EndOfSectionNode() ),
1813 aInsPos.GetNode(), bNewFrames );
1815 // insert a DummyNode for the box-EndNode?
1816 if( bTableInsDummyNode )
1817 new SwPlaceholderNode(aInsPos.GetNode());
1818 aRg.aStart = *pSttNd->EndOfSectionNode();
1820 // insert a DummyNode for the table-EndNode
1821 if( bTableInsDummyNode )
1822 new SwPlaceholderNode(aInsPos.GetNode());
1823 aRg.aStart = *pCurrentNode->EndOfSectionNode();
1825 else
1827 SwNodeIndex nStt( aInsPos, -1 );
1828 SwTableNode* pTableNd = static_cast<SwTableNode*>(pCurrentNode)->
1829 MakeCopy( rDoc, aInsPos );
1830 const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
1831 if (nDistance < nNodeCnt)
1832 nNodeCnt -= nDistance;
1833 else
1834 nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
1836 aRg.aStart = pCurrentNode->EndOfSectionIndex();
1838 if( bNewFrames && pTableNd )
1839 pTableNd->MakeOwnFrames();
1841 break;
1843 case SwNodeType::Section:
1844 // If the end of the section is outside the copy range,
1845 // the section node will skipped, not copied!
1846 // If someone want to change this behaviour, he has to adjust the function
1847 // lcl_NonCopyCount() which relies on it.
1848 if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() )
1850 // copy of the whole section, so create a new SectionNode
1851 SwNodeIndex nStt( aInsPos, -1 );
1852 SwSectionNode* pSectNd = static_cast<SwSectionNode*>(pCurrentNode)->
1853 MakeCopy( rDoc, aInsPos );
1855 const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
1856 if (nDistance < nNodeCnt)
1857 nNodeCnt -= nDistance;
1858 else
1859 nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
1860 aRg.aStart = pCurrentNode->EndOfSectionIndex();
1862 if( bNewFrames && pSectNd &&
1863 !pSectNd->GetSection().IsHidden() )
1864 pSectNd->MakeOwnFrames(&nStt);
1866 break;
1868 case SwNodeType::Start:
1870 SwStartNode* pTmp = new SwStartNode( aInsPos.GetNode(), SwNodeType::Start,
1871 static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
1872 new SwEndNode( aInsPos.GetNode(), *pTmp );
1873 --aInsPos;
1874 nLevel++;
1876 break;
1878 case SwNodeType::End:
1879 if( nLevel ) // complete section
1881 --nLevel;
1882 ++aInsPos; // EndNode already exists
1884 else if( SwNodeOffset(1) == nNodeCnt && SwNodeOffset(1) == nIsEndOfContent )
1885 // we have reached the EndOfContent node - nothing to do!
1886 continue;
1887 else if( !pCurrentNode->m_pStartOfSection->IsSectionNode() )
1889 // create a section at the original InsertPosition
1890 SwNodeRange aTmpRg( aOrigInsPos, SwNodeOffset(1), aInsPos );
1891 rDoc.GetNodes().SectionDown( &aTmpRg,
1892 pCurrentNode->m_pStartOfSection->GetStartNodeType() );
1894 break;
1896 case SwNodeType::Text:
1897 case SwNodeType::Grf:
1898 case SwNodeType::Ole:
1900 static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
1901 rDoc, aInsPos.GetNode(), bNewFrames);
1903 break;
1905 case SwNodeType::PlaceHolder:
1906 if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
1908 // than a SectionNode (start/end) is needed at the current
1909 // InsPos; if so skip it, otherwise ignore current node
1910 SwNode *const pTmpNd = & aInsPos.GetNode();
1911 if( pTmpNd->IsSectionNode() ||
1912 pTmpNd->StartOfSectionNode()->IsSectionNode() )
1913 ++aInsPos; // skip
1915 else {
1916 assert(!"How can this node be in the node array?");
1918 break;
1920 default:
1921 assert(false);
1923 ++aRg.aStart;
1927 void SwNodes::DelDummyNodes( const SwNodeRange& rRg )
1929 SwNodeIndex aIdx( rRg.aStart );
1930 while( aIdx.GetIndex() < rRg.aEnd.GetIndex() )
1932 if (SwNodeType::PlaceHolder == aIdx.GetNode().GetNodeType())
1933 RemoveNode( aIdx.GetIndex(), SwNodeOffset(1), true );
1934 else
1935 ++aIdx;
1939 SwStartNode* SwNodes::MakeEmptySection( SwNode& rWhere,
1940 SwStartNodeType eSttNdTyp )
1942 SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
1943 new SwEndNode( rWhere, *pSttNd );
1944 return pSttNd;
1947 SwStartNode* SwNodes::MakeTextSection( const SwNode & rWhere,
1948 SwStartNodeType eSttNdTyp,
1949 SwTextFormatColl *pColl )
1951 SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
1952 new SwEndNode( rWhere, *pSttNd );
1953 MakeTextNode( SwNodeIndex( rWhere, - 1 ).GetNode(), pColl );
1954 return pSttNd;
1957 static bool shouldSkipSection(const SwSectionNode& rSectNode, bool bSkipHidden, bool bSkipProtect)
1959 const SwSection& rSect = rSectNode.GetSection();
1960 return (bSkipHidden && rSect.CalcHiddenFlag()) || (bSkipProtect && rSect.IsProtectFlag());
1963 static SwContentNode* goNextSection(const SwNode& rNode, bool bSkipHidden, bool bSkipProtect)
1965 const SwNodes& rNodes = rNode.GetNodes();
1966 const SwNodeOffset last = rNodes.Count() - 1;
1967 for (SwNodeOffset i(rNode.GetIndex()); i < last; ++i)
1969 SwNode* pNd = rNodes[i];
1970 if (SwSectionNode* pSectNd = pNd->GetSectionNode())
1972 if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
1973 // than skip the section
1974 i = pSectNd->EndOfSectionIndex();
1976 else if (i == rNode.GetIndex()) // The first iteration
1978 if ((pSectNd = pNd->StartOfSectionNode()->GetSectionNode()))
1979 if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
1980 // than skip the section
1981 i = pSectNd->EndOfSectionIndex();
1983 else if (SwContentNode* pContentNode = pNd->GetContentNode())
1985 if (bSkipHidden || bSkipProtect)
1986 if ((pSectNd = pNd->FindSectionNode()))
1987 if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
1989 i = pSectNd->EndOfSectionIndex();
1990 continue;
1992 return pContentNode;
1995 return nullptr;
1998 //TODO: provide better documentation
1999 /** go to next section that is not protected nor hidden
2001 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
2003 * @param pIdx
2004 * @param bSkipHidden
2005 * @param bSkipProtect
2006 * @return
2007 * @see SwNodes::GoNext
2008 * @see SwNodes::GoPrevious
2009 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2011 SwContentNode* SwNodes::GoNextSection(SwNodeIndex* pIdx, bool bSkipHidden, bool bSkipProtect)
2013 SwContentNode* pNd = goNextSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
2014 if (pNd)
2015 *pIdx = *pNd;
2016 return pNd;
2019 //TODO: provide better documentation
2020 /** go to next section that is not protected nor hidden
2022 * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
2024 * @param pIdx
2025 * @param bSkipHidden
2026 * @param bSkipProtect
2027 * @return
2028 * @see SwNodes::GoNext
2029 * @see SwNodes::GoPrevious
2030 * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
2032 SwContentNode* SwNodes::GoNextSection(SwPosition* pIdx, bool bSkipHidden, bool bSkipProtect)
2034 SwContentNode* pNd = goNextSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
2035 if (pNd)
2036 pIdx->AssignStartIndex(*pNd);
2037 return pNd;
2040 static SwContentNode* goPrevSection(const SwNode& rNode, bool bSkipHidden, bool bSkipProtect)
2042 const SwNodes& rNodes = rNode.GetNodes();
2043 SwNodeOffset first(startOfGlobalSection(rNode));
2044 for (SwNodeOffset i(rNode.GetIndex()); i > first; --i)
2046 SwNode* pNd = rNodes[i];
2047 if (pNd->IsEndNode() || i == rNode.GetIndex() /* the first iteration */)
2049 if (SwSectionNode* pSectNd = pNd->StartOfSectionNode()->GetSectionNode())
2051 if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
2052 // then skip section
2053 i = pSectNd->GetIndex();
2056 else if (SwContentNode* pContentNode = pNd->GetContentNode())
2058 if (bSkipHidden || bSkipProtect)
2059 if (const SwSectionNode* pSectNd = pNd->FindSectionNode())
2060 if (shouldSkipSection(*pSectNd, bSkipHidden, bSkipProtect))
2062 i = pSectNd->GetIndex();
2063 continue;
2065 return pContentNode;
2068 return nullptr;
2071 ///@see SwNodes::GoNextSection
2072 SwContentNode* SwNodes::GoPrevSection(SwNodeIndex* pIdx, bool bSkipHidden, bool bSkipProtect)
2074 SwContentNode* pNd = goPrevSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
2075 if (pNd)
2076 *pIdx = *pNd;
2077 return pNd;
2080 ///@see SwNodes::GoNextSection
2081 SwContentNode* SwNodes::GoPrevSection(SwPosition* pIdx, bool bSkipHidden, bool bSkipProtect)
2083 SwContentNode* pNd = goPrevSection(pIdx->GetNode(), bSkipHidden, bSkipProtect);
2084 if (pNd)
2085 pIdx->AssignStartIndex(*pNd);
2086 return pNd;
2089 //TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
2090 /** find the next/previous ContentNode or table node that should have layout
2091 * frames that are siblings to the ones of the node at rFrameNd.
2093 * Search is started backward with the one before rFrameNd and
2094 * forward after pEnd.
2096 * @param rFrameNd node with frames to search in
2097 * @param pEnd last node after rFrameNd that should be excluded from search
2098 * @return result node; nullptr if not found
2100 SwNode* SwNodes::FindPrvNxtFrameNode( const SwNode& rFrameNd,
2101 SwNode const*const pEnd,
2102 SwRootFrame const*const pLayout) const
2104 assert(pEnd != nullptr); // every caller currently
2106 // no layout -> skip
2107 if (!GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell())
2108 return nullptr;
2110 const SwNode *const pSttNd = &rFrameNd;
2112 // inside a hidden section?
2113 const SwSectionNode *const pSectNd = pSttNd->IsSectionNode()
2114 ? pSttNd->StartOfSectionNode()->FindSectionNode()
2115 : pSttNd->FindSectionNode();
2116 if (pSectNd && pSectNd->GetSection().CalcHiddenFlag())
2117 return nullptr;
2119 // in a table in table situation we have to assure that we don't leave the
2120 // outer table cell when the inner table is looking for a PrvNxt...
2121 const SwTableNode *const pTableNd = pSttNd->IsTableNode()
2122 ? pSttNd->StartOfSectionNode()->FindTableNode()
2123 : pSttNd->FindTableNode();
2124 SwNodeIndex aIdx( rFrameNd );
2126 // search backward for a content or table node
2128 --aIdx;
2129 SwNode* pFrameNd = &aIdx.GetNode();
2133 if (pFrameNd->IsContentNode())
2135 // TODO why does this not check for nested tables like forward direction
2136 return pFrameNd;
2138 else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode())
2140 if (pLayout == nullptr
2141 || !pLayout->HasMergedParas()
2142 || pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
2144 pFrameNd = pFrameNd->StartOfSectionNode();
2145 return pFrameNd;
2147 else
2149 aIdx = *pFrameNd->StartOfSectionNode();
2150 --aIdx;
2151 pFrameNd = &aIdx.GetNode();
2154 else if (pFrameNd->IsSectionNode()
2155 || (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsSectionNode()))
2157 pFrameNd = GoPrevSection( &aIdx, true, false );
2158 // did we move *into* a table?
2159 if (pFrameNd && ::CheckNodesRange(aIdx.GetNode(), rFrameNd, true))
2161 for (SwTableNode * pTable = pFrameNd->FindTableNode();
2162 pTable && pTable->EndOfSectionIndex() < rFrameNd.GetIndex();
2163 pTable = pTable->StartOfSectionNode()->FindTableNode())
2165 pFrameNd = pTable->EndOfSectionNode();
2167 if (pFrameNd->IsEndNode())
2168 { // GoPrevSection() checks that text node isn't section-hidden,
2169 // so table node between can't be section-hidden either
2170 assert(pFrameNd->StartOfSectionNode()->IsTableNode());
2171 continue; // check other hidden conditions on next iteration
2174 if ( nullptr != pFrameNd && !(
2175 ::CheckNodesRange( aIdx.GetNode(), rFrameNd, true ) &&
2176 // Never out of the table at the start
2177 pFrameNd->FindTableNode() == pTableNd &&
2178 // Bug 37652: Never out of the table at the end
2179 (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
2180 == pSttNd->FindTableBoxStartNode() ) &&
2181 (!pSectNd || pSttNd->IsSectionNode() ||
2182 pSectNd->GetIndex() < pFrameNd->GetIndex())
2185 pFrameNd = nullptr; // no preceding content node, stop search
2188 else
2190 pFrameNd = nullptr; // no preceding content node, stop search
2193 while (pFrameNd != nullptr);
2195 // search forward for a content or table node
2197 aIdx = pEnd->GetIndex() + 1;
2198 pFrameNd = &aIdx.GetNode();
2202 if (pFrameNd->IsContentNode())
2204 // Undo when merging a table with one before, if there is also one after it.
2205 // However, if the node is in a table, it needs to be returned if the
2206 // SttNode is a section or a table!
2207 SwTableNode *const pTableNode = pFrameNd->FindTableNode();
2208 if (pSttNd->IsTableNode() &&
2209 nullptr != pTableNode &&
2210 // TABLE IN TABLE:
2211 pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
2213 pFrameNd = pTableNode;
2215 return pFrameNd;
2217 else if (pFrameNd->IsTableNode())
2219 if (pLayout == nullptr
2220 || !pLayout->HasMergedParas()
2221 || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
2223 return pFrameNd;
2225 else
2227 aIdx = *pFrameNd->EndOfSectionNode();
2228 ++aIdx;
2229 pFrameNd = &aIdx.GetNode();
2232 else if (pFrameNd->IsSectionNode()
2233 || (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsSectionNode()))
2235 pFrameNd = GoNextSection( &aIdx, true, false );
2236 // did we move *into* a table?
2237 if (pFrameNd && ::CheckNodesRange(aIdx.GetNode(), rFrameNd, true))
2239 for (SwTableNode * pTable = pFrameNd->FindTableNode();
2240 pTable && pEnd->GetIndex() < pTable->GetIndex();
2241 pTable = pTable->StartOfSectionNode()->FindTableNode())
2243 pFrameNd = pTable;
2245 if (pFrameNd->IsTableNode())
2246 { // GoNextSection() checks that text node isn't section-hidden,
2247 // so table node between can't be section-hidden either
2248 continue; // check other hidden conditions on next iteration
2251 // NEVER leave the section when doing this!
2252 if (pFrameNd
2253 && !(::CheckNodesRange(aIdx.GetNode(), rFrameNd, true)
2254 && (pFrameNd->FindTableNode() == pTableNd &&
2255 // NEVER go out of the table cell at the end
2256 (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
2257 == pSttNd->FindTableBoxStartNode()))
2258 && (!pSectNd || pSttNd->IsSectionNode() ||
2259 pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
2262 pFrameNd = nullptr; // no following content node, stop search
2265 else
2267 pFrameNd = nullptr; // no preceding content node, stop search
2270 while (pFrameNd != nullptr);
2272 return pFrameNd;
2275 void SwNodes::ForEach( SwNodeOffset nStart, SwNodeOffset nEnd,
2276 FnForEach_SwNodes fn, void* pArgs )
2278 assert( nEnd <= SwNodeOffset(m_nSize) );
2279 if( nEnd > SwNodeOffset(m_nSize) )
2280 nEnd = SwNodeOffset(m_nSize);
2282 if( nStart >= nEnd )
2283 return;
2285 sal_uInt16 cur = Index2Block( sal_Int32(nStart) );
2286 BlockInfo** pp = m_ppInf.get() + cur;
2287 BlockInfo* p = *pp;
2288 sal_uInt16 nElem = sal_uInt16( sal_Int32(nStart) - p->nStart );
2289 auto pElem = p->mvData.begin() + nElem;
2290 nElem = p->nElem - nElem;
2291 for(;;)
2293 if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
2294 break;
2296 // next element
2297 if( !--nElem )
2299 // new block
2300 p = *++pp;
2301 pElem = p->mvData.begin();
2302 nElem = p->nElem;
2307 void SwNodes::ForEach( const SwNodeIndex& rStart, const SwNodeIndex& rEnd,
2308 FnForEach_SwNodes fnForEach, void* pArgs )
2310 ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
2313 void SwNodes::ForEach( SwNode& rStart, SwNode& rEnd,
2314 FnForEach_SwNodes fnForEach, void* pArgs )
2316 ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
2319 void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel )
2321 #ifndef NDEBUG
2322 SwNode *const pFirst((*this)[nDelPos]);
2323 #endif
2324 std::vector<SwTextAttr*> flys;
2325 for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
2327 SwNode* pNode = (*this)[ nDelPos + nCnt ];
2328 SwTextNode * pTextNd = pNode->GetTextNode();
2330 if (pTextNd)
2332 pTextNd->RemoveFromList();
2333 // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
2334 // so their anchor still points to correct node when deleted!
2335 // NOTE: this will call RemoveNode() recursively!
2336 // so adjust our indexes to account for removed nodes
2337 SwpHints *const pHints(pTextNd->GetpSwpHints());
2338 if (pHints)
2340 SwNodeOffset const nPos = pTextNd->GetIndex();
2341 flys.clear();
2342 for (size_t i = 0; i < pHints->Count(); ++i)
2344 SwTextAttr *const pHint(pHints->Get(i));
2345 if (RES_TXTATR_FLYCNT == pHint->Which())
2347 flys.push_back(pHint);
2350 for (SwTextAttr * pHint : flys)
2352 pTextNd->DeleteAttribute(pHint);
2353 } // pHints may be dead now
2354 SwNodeOffset const nDiff = nPos - pTextNd->GetIndex();
2355 if (nDiff)
2357 nDelPos -= nDiff;
2359 assert(pTextNd == (*this)[nDelPos + nCnt]);
2360 assert(pFirst == (*this)[nDelPos]);
2363 SwTableNode* pTableNode = pNode->GetTableNode();
2364 if (pTableNode)
2366 // The node that is deleted is a table node.
2367 // Need to make sure that all the redlines that are
2368 // related to this table are removed from the
2369 // 'Extra Redlines' array
2370 pTableNode->RemoveRedlines();
2373 SwSectionNode* pSectionNode = pNode->GetSectionNode();
2374 SfxViewShell* pKitClipSh = (comphelper::LibreOfficeKit::isActive() && pSectionNode && !GetDoc().IsClipBoard())
2375 ? SfxViewShell::Current() : nullptr;
2376 if (pKitClipSh)
2378 OUString fieldCommand = pSectionNode->GetSection().GetSectionName();
2379 tools::JsonWriter aJson;
2380 aJson.put("commandName", ".uno:DeleteSection");
2381 aJson.put("success", true);
2383 auto result = aJson.startNode("result");
2384 aJson.put("DeleteSection", fieldCommand);
2387 pKitClipSh->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
2391 SwNodeOffset nEnd = nDelPos + nSz;
2392 SwNode* pNew = (*this)[ nEnd ];
2394 for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
2396 SwNode* pNode = (*this)[ nDelPos + nCnt ];
2397 // the assignment will de-link the entry from the ring
2398 while (pNode->m_vIndices)
2399 (*pNode->m_vIndices) = *pNew;
2402 std::vector<BigPtrEntry> aTempEntries;
2403 if( bDel )
2405 SwNodeOffset nCnt = nSz;
2406 BigPtrEntry *pDel = (*this)[ nDelPos+nCnt-1 ], *pPrev = (*this)[ nDelPos+nCnt-2 ];
2408 // set temporary object
2409 // JP 24.08.98: this should actually be removed because one could
2410 // call Remove recursively, e.g. for character bound frames. However,
2411 // since there happens way too much here, this temporary object was
2412 // inserted that will be deleted in Remove again (see Bug 55406)
2413 aTempEntries.resize(sal_Int32(nCnt));
2415 while( nCnt-- )
2417 delete pDel;
2418 // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
2419 pDel = pPrev;
2420 BigPtrEntry* pTempEntry = &aTempEntries[sal_Int32(nCnt)];
2421 pPrev = ReplaceTheOneAfter(pPrev, pTempEntry);
2423 nDelPos = SwNodeOffset(pDel->GetPos() + 1);
2426 BigPtrArray::Remove( sal_Int32(nDelPos), sal_Int32(nSz) );
2429 void SwNodes::InsertNode( SwNode* pNode, const SwNodeIndex& rPos )
2431 BigPtrEntry* pIns = pNode;
2432 BigPtrArray::Insert( pIns, sal_Int32(rPos.GetIndex()) );
2435 void SwNodes::InsertNode( SwNode* pNode, SwNodeOffset nPos )
2437 BigPtrEntry* pIns = pNode;
2438 BigPtrArray::Insert( pIns, sal_Int32(nPos) );
2441 // ->#112139#
2442 SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const
2444 if (nullptr != pNode)
2446 SwNodeIndex aIdx(*pNode);
2448 if (aIdx <= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
2449 pNode = (*this)[SwNodeOffset(0)];
2450 else
2452 while ((*this)[SwNodeOffset(0)] != pNode->StartOfSectionNode())
2453 pNode = pNode->StartOfSectionNode();
2457 return pNode;
2460 SwNode * SwNodes::DocumentSectionEndNode(SwNode * pNode) const
2462 return DocumentSectionStartNode(pNode)->EndOfSectionNode();
2465 bool SwNodes::IsDocNodes() const
2467 return this == &m_rMyDoc.GetNodes();
2470 void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const
2472 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes"));
2473 for (SwNodeOffset i(0); i < Count(); ++i)
2474 (*this)[i]->dumpAsXml(pWriter);
2475 (void)xmlTextWriterEndElement(pWriter);
2478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */