summaryrefslogtreecommitdiffstats
path: root/sw/source/core/SwNumberTree
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/SwNumberTree
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/SwNumberTree')
-rw-r--r--sw/source/core/SwNumberTree/SwNodeNum.cxx369
-rw-r--r--sw/source/core/SwNumberTree/SwNumberTree.cxx1181
2 files changed, 1550 insertions, 0 deletions
diff --git a/sw/source/core/SwNumberTree/SwNodeNum.cxx b/sw/source/core/SwNumberTree/SwNodeNum.cxx
new file mode 100644
index 0000000000..9c160d3c0f
--- /dev/null
+++ b/sw/source/core/SwNumberTree/SwNodeNum.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <IDocumentListItems.hxx>
+#include <doc.hxx>
+
+SwNodeNum::SwNodeNum(SwTextNode* pTextNode, bool const isHiddenRedlines)
+ : mpTextNode( pTextNode )
+ , mpNumRule( nullptr )
+ , m_isHiddenRedlines(isHiddenRedlines)
+{
+}
+
+SwNodeNum::SwNodeNum( SwNumRule* pNumRule )
+ : mpTextNode( nullptr )
+ , mpNumRule( pNumRule )
+ , m_isHiddenRedlines(false)
+{
+}
+
+SwNodeNum::~SwNodeNum()
+{
+}
+
+
+void SwNodeNum::ChangeNumRule( SwNumRule& rNumRule )
+{
+ OSL_ENSURE( GetNumRule() && GetTextNode(),
+ "<SwNodeNum::ChangeNumRule(..)> - missing list style and/or text node. Serious defect -> please inform OD." );
+ if ( GetNumRule() && GetTextNode() )
+ {
+ GetNumRule()->RemoveTextNode( *(GetTextNode()) );
+ }
+
+ mpNumRule = &rNumRule;
+
+ if ( GetNumRule() && GetTextNode() )
+ {
+ GetNumRule()->AddTextNode( *(GetTextNode()) );
+ }
+}
+
+SwPosition SwNodeNum::GetPosition() const
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::GetPosition()> - no text node set at <SwNodeNum> instance" );
+ return SwPosition(*mpTextNode);
+}
+
+SwNumberTreeNode * SwNodeNum::Create() const
+{
+ SwNodeNum * pResult = new SwNodeNum( GetNumRule() );
+
+ return pResult;
+}
+
+void SwNodeNum::PreAdd()
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::PreAdd()> - no text node set at <SwNodeNum> instance" );
+ if ( !GetNumRule() && GetTextNode() )
+ {
+ mpNumRule = GetTextNode()->GetNumRule();
+ }
+ OSL_ENSURE( GetNumRule(),
+ "<SwNodeNum::PreAdd()> - no list style set at <SwNodeNum> instance" );
+ if (!m_isHiddenRedlines && GetNumRule() && GetTextNode())
+ {
+ GetNumRule()->AddTextNode( *(GetTextNode()) );
+ }
+
+ if (!m_isHiddenRedlines)
+ {
+ if ( GetTextNode() &&
+ GetTextNode()->GetNodes().IsDocNodes() )
+ {
+ GetTextNode()->getIDocumentListItems().addListItem( *this );
+ }
+ }
+}
+
+void SwNodeNum::PostRemove()
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::PostRemove()> - no text node set at <SwNodeNum> instance" );
+ OSL_ENSURE( GetNumRule(),
+ "<SwNodeNum::PostRemove()> - no list style set at <SwNodeNum> instance" );
+
+ if (!m_isHiddenRedlines && GetTextNode())
+ {
+ GetTextNode()->getIDocumentListItems().removeListItem( *this );
+ }
+
+ if ( GetNumRule() )
+ {
+ if (!m_isHiddenRedlines && GetTextNode())
+ {
+ GetNumRule()->RemoveTextNode( *(GetTextNode()) );
+ }
+ mpNumRule = nullptr;
+ }
+}
+
+bool SwNodeNum::IsNotifiable(const SwDoc& rDoc) const
+{
+ bool aResult;
+
+ if (const SwTextNode* pTextNode = GetTextNode())
+ aResult = pTextNode->IsNotifiable();
+ else
+ aResult = IsNotificationEnabled(rDoc);
+
+ return aResult;
+}
+
+bool SwNodeNum::IsNotificationEnabled(const SwDoc& rDoc) const
+{
+ bool aResult;
+
+ if (const SwTextNode* pTextNode = GetTextNode())
+ aResult = pTextNode->IsNotificationEnabled();
+ else
+ aResult = !rDoc.IsInReading() && !rDoc.IsInDtor();
+
+ return aResult;
+}
+
+bool SwNodeNum::IsContinuous() const
+{
+ bool aResult = false;
+
+ // #i64311#
+ if ( GetNumRule() )
+ {
+ aResult = mpNumRule->IsContinusNum();
+ }
+ else if ( GetParent() )
+ {
+ aResult = GetParent()->IsContinuous();
+ }
+ else
+ {
+ OSL_FAIL( "<SwNodeNum::IsContinuous()> - OD debug" );
+ }
+
+ return aResult;
+}
+
+bool SwNodeNum::IsCounted() const
+{
+ bool aResult = false;
+
+ if ( GetTextNode() )
+ {
+ // #i59559#
+ // <SwTextNode::IsCounted()> determines, if a text node is counted for numbering
+ aResult = GetTextNode()->IsCountedInList();
+ }
+ else
+ aResult = SwNumberTreeNode::IsCounted();
+
+ return aResult;
+}
+
+// #i64010#
+bool SwNodeNum::HasCountedChildren() const
+{
+ return std::any_of(mChildren.begin(), mChildren.end(),
+ [](SwNumberTreeNode* pNode) {
+ SwNodeNum* pChild( dynamic_cast<SwNodeNum*>(pNode) );
+ OSL_ENSURE( pChild, "<SwNodeNum::HasCountedChildren()> - unexpected type of child" );
+ return pChild && (pChild->IsCountedForNumbering() || pChild->HasCountedChildren());
+ });
+}
+// #i64010#
+bool SwNodeNum::IsCountedForNumbering() const
+{
+ return IsCounted() &&
+ ( IsPhantom() || // phantoms
+ !GetTextNode() || // root node
+ GetTextNode()->HasNumber() || // text node
+ GetTextNode()->HasBullet() ); // text node
+}
+
+void SwNodeNum::NotifyNode()
+{
+ ValidateMe();
+
+ if (mpTextNode)
+ {
+ mpTextNode->NumRuleChgd();
+ }
+}
+
+bool SwNodeNum::LessThan(const SwNumberTreeNode & rNode) const
+{
+ bool bResult = false;
+ const SwNodeNum & rTmpNode = static_cast<const SwNodeNum &>(rNode);
+
+ if (mpTextNode == nullptr && rTmpNode.mpTextNode != nullptr)
+ bResult = true;
+ else if (mpTextNode != nullptr && rTmpNode.mpTextNode != nullptr)
+ {
+ // #i83479# - refactoring
+ // simplify comparison by comparing the indexes of the text nodes
+ bResult = ( mpTextNode->GetIndex() < rTmpNode.mpTextNode->GetIndex() );
+ }
+
+ return bResult;
+}
+
+bool SwNodeNum::IsRestart() const
+{
+ bool bIsRestart = false;
+
+ if ( GetTextNode() )
+ {
+ bIsRestart = GetTextNode()->IsListRestart();
+ }
+
+ return bIsRestart;
+}
+
+bool SwNodeNum::IsCountPhantoms() const
+{
+ bool bResult = true;
+
+ // #i64311#
+ // phantoms aren't counted in consecutive numbering rules
+ if ( mpNumRule )
+ bResult = !mpNumRule->IsContinusNum() &&
+ mpNumRule->IsCountPhantoms();
+ else
+ {
+ OSL_FAIL( "<SwNodeNum::IsCountPhantoms(): missing numbering rule" );
+ }
+
+ return bResult;
+}
+
+SwNumberTree::tSwNumTreeNumber SwNodeNum::GetStartValue() const
+{
+ SwNumberTree::tSwNumTreeNumber aResult = 1;
+
+ if ( IsRestart() && GetTextNode() )
+ {
+ aResult = GetTextNode()->GetActualListStartValue();
+ }
+ else
+ {
+ SwNumRule * pRule = GetNumRule();
+
+ if (pRule)
+ {
+ int nLevel = GetParent() ? GetLevelInListTree() : 0;
+
+ if (nLevel >= 0 && nLevel < MAXLEVEL)
+ {
+ const SwNumFormat * pFormat = pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(nLevel));
+
+ if (pFormat)
+ aResult = pFormat->GetStart();
+ }
+ }
+ }
+
+ return aResult;
+}
+
+void SwNodeNum::HandleNumberTreeRootNodeDelete( SwNodeNum& rNodeNum )
+{
+ SwNodeNum* pRootNode = rNodeNum.GetParent()
+ ? dynamic_cast<SwNodeNum*>(rNodeNum.GetRoot())
+ : &rNodeNum;
+ if ( !pRootNode )
+ {
+ // no root node -> nothing do.
+ return;
+ }
+
+ // unregister all number tree node entries, which correspond to a text node,
+ // about the deletion of the number tree root node.
+ UnregisterMeAndChildrenDueToRootDelete( *pRootNode );
+}
+
+void SwNodeNum::UnregisterMeAndChildrenDueToRootDelete( SwNodeNum& rNodeNum )
+{
+ const bool bIsPhantom( rNodeNum.IsPhantom() );
+ tSwNumberTreeChildren::size_type nAllowedChildCount( 0 );
+ bool bDone( false );
+ while ( !bDone &&
+ rNodeNum.GetChildCount() > nAllowedChildCount )
+ {
+ SwNodeNum* pChildNode( dynamic_cast<SwNodeNum*>((*rNodeNum.mChildren.begin())) );
+ if ( !pChildNode )
+ {
+ OSL_FAIL( "<SwNodeNum::UnregisterMeAndChildrenDueToRootDelete(..)> - unknown number tree node child" );
+ ++nAllowedChildCount;
+ continue;
+ }
+
+ // Unregistering the last child of a phantom will destroy the phantom.
+ // Thus <rNodeNum> will be destroyed and access on <rNodeNum> has to
+ // be suppressed.
+ if ( bIsPhantom && rNodeNum.GetChildCount() == 1 )
+ {
+ bDone = true;
+ }
+
+ UnregisterMeAndChildrenDueToRootDelete( *pChildNode );
+ }
+
+ if ( bIsPhantom )
+ return;
+
+ SwTextNode* pTextNode( rNodeNum.GetTextNode() );
+ if ( !pTextNode )
+ return;
+
+ pTextNode->RemoveFromList();
+ // --> clear all list attributes and the list style
+ const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray{
+ RES_PARATR_LIST_ID, RES_PARATR_LIST_LEVEL, RES_PARATR_LIST_ISRESTART,
+ RES_PARATR_LIST_RESTARTVALUE, RES_PARATR_LIST_ISCOUNTED, RES_PARATR_NUMRULE
+ };
+ SwPaM aPam( *pTextNode );
+ pTextNode->GetDoc().ResetAttrs( aPam, false,
+ aResetAttrsArray,
+ false );
+}
+
+// #i81002#
+const SwNodeNum* SwNodeNum::GetPrecedingNodeNumOf( const SwTextNode& rTextNode ) const
+{
+ const SwNodeNum* pPrecedingNodeNum( nullptr );
+
+ // #i83479#
+ SwNodeNum aNodeNumForTextNode( const_cast<SwTextNode*>(&rTextNode), false/*doesn't matter*/ );
+
+ pPrecedingNodeNum = dynamic_cast<const SwNodeNum*>(
+ GetRoot()
+ ? GetRoot()->GetPrecedingNodeOf( aNodeNumForTextNode )
+ : GetPrecedingNodeOf( aNodeNumForTextNode ) );
+
+ return pPrecedingNodeNum;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/SwNumberTree/SwNumberTree.cxx b/sw/source/core/SwNumberTree/SwNumberTree.cxx
new file mode 100644
index 0000000000..30cdff34b7
--- /dev/null
+++ b/sw/source/core/SwNumberTree/SwNumberTree.cxx
@@ -0,0 +1,1181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <SwNumberTree.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+using std::vector;
+using std::find;
+
+SwNumberTreeNode::SwNumberTreeNode()
+ : mpParent( nullptr ),
+ mnNumber( 0 ),
+ mbContinueingPreviousSubTree( false ),
+ mbPhantom( false )
+{
+ mItLastValid = mChildren.end();
+}
+
+SwNumberTreeNode::~SwNumberTreeNode()
+{
+ if (GetChildCount() > 0)
+ {
+ if (HasOnlyPhantoms())
+ {
+ delete *mChildren.begin();
+
+ mChildren.clear();
+ mItLastValid = mChildren.end();
+ }
+ else
+ {
+ OSL_FAIL("lost children!");
+ }
+ }
+
+ OSL_ENSURE( IsPhantom() || mpParent == nullptr, ": I'm not supposed to have a parent.");
+
+ mpParent = reinterpret_cast<SwNumberTreeNode *>(0xdeadbeef);
+
+ OSL_ENSURE(mChildren.empty(), "children left!");
+}
+
+SwNumberTreeNode * SwNumberTreeNode::CreatePhantom()
+{
+ SwNumberTreeNode * pNew = nullptr;
+
+ if (! mChildren.empty() &&
+ (*mChildren.begin())->IsPhantom())
+ {
+ OSL_FAIL("phantom already present");
+ }
+ else
+ {
+ pNew = Create();
+ pNew->mbPhantom = true;
+ pNew->mpParent = this;
+
+ std::pair<tSwNumberTreeChildren::iterator, bool> aInsert =
+ mChildren.insert(pNew);
+
+ if (! aInsert.second)
+ {
+ OSL_FAIL("insert of phantom failed!");
+
+ delete pNew;
+ pNew = nullptr;
+ }
+ }
+
+ return pNew;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetRoot() const
+{
+ SwNumberTreeNode * pResult = mpParent;
+
+ if (pResult)
+ while (pResult->mpParent)
+ pResult = pResult->mpParent;
+
+ return pResult;
+}
+
+void SwNumberTreeNode::ClearObsoletePhantoms()
+{
+ tSwNumberTreeChildren::iterator aIt = mChildren.begin();
+
+ if (!(aIt != mChildren.end() && (*aIt)->IsPhantom()))
+ return;
+
+ (*aIt)->ClearObsoletePhantoms();
+
+ if ((*aIt)->mChildren.empty())
+ {
+ // #i60652#
+ // Because <mChildren.erase(aIt)> could destroy the element, which
+ // is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aIt>.
+ SetLastValid(mChildren.end());
+
+ delete *aIt;
+ mChildren.erase(aIt);
+ }
+}
+
+void SwNumberTreeNode::ValidateHierarchical(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aValidateIt =
+ GetIterator(pNode);
+
+ if (aValidateIt == mChildren.end())
+ return;
+
+ OSL_ENSURE((*aValidateIt)->mpParent == this, "wrong parent");
+
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ // -->
+ // improvement:
+ // - Only one time checked for <mChildren.end()>.
+ // - Less checks for each loop run.
+ // correction:
+ // - consider case that current node isn't counted and isn't the first
+ // child of its parent. In this case the number of last counted child
+ // of the previous node determines the start value for the following
+ // children loop, if all children have to be validated and the first
+ // one doesn't restart the counting.
+ SwNumberTree::tSwNumTreeNumber nTmpNumber( 0 );
+ if (aIt != mChildren.end())
+ nTmpNumber = (*aIt)->mnNumber;
+ else
+ {
+ aIt = mChildren.begin();
+ (*aIt)->mbContinueingPreviousSubTree = false;
+
+ // determine default start value
+ // consider the case that the first child isn't counted.
+ nTmpNumber = (*aIt)->GetStartValue();
+ if ( !(*aIt)->IsCounted() &&
+ ( !(*aIt)->HasCountedChildren() || (*aIt)->IsPhantom() ) )
+ {
+ --nTmpNumber;
+ }
+
+ // determine special start value for the case that first child
+ // doesn't restart the numbering and the parent node isn't counted
+ // and isn't the first child.
+ const bool bParentCounted( IsCounted() &&
+ ( !IsPhantom() ||
+ HasPhantomCountedParent() ) );
+ if ( !(*aIt)->IsRestart() &&
+ GetParent() && !bParentCounted )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ while ( aParentChildIt != GetParent()->mChildren.begin() )
+ {
+ --aParentChildIt;
+ SwNumberTreeNode* pPrevNode( *aParentChildIt );
+ if ( pPrevNode->GetChildCount() > 0 )
+ {
+ (*aIt)->mbContinueingPreviousSubTree = true;
+ nTmpNumber = (*(pPrevNode->mChildren.rbegin()))->GetNumber();
+ if ( (*aIt)->IsCounted() &&
+ ( !(*aIt)->IsPhantom() ||
+ (*aIt)->HasPhantomCountedParent() ) )
+ {
+ ++nTmpNumber;
+ }
+ break;
+ }
+ else if ( pPrevNode->IsCounted() )
+ {
+ break;
+ }
+ else
+ {
+ // Previous node has no children and is not counted.
+ // Thus, next turn and check for the previous node.
+ }
+ }
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+
+ while (aIt != aValidateIt)
+ {
+ ++aIt;
+ (*aIt)->mbContinueingPreviousSubTree = false;
+
+ // --> only for counted nodes the number
+ // has to be adjusted, compared to the previous node.
+ // this condition is hold also for nodes, which restart the numbering.
+ if ( (*aIt)->IsCounted() )
+ {
+ if ((*aIt)->IsRestart())
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ ++nTmpNumber;
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+
+ SetLastValid(aIt, true);
+}
+
+void SwNumberTreeNode::ValidateContinuous(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ do
+ {
+ if (aIt == mChildren.end())
+ {
+ aIt = mChildren.begin();
+ }
+ else
+ ++aIt;
+
+ if (aIt != mChildren.end())
+ {
+ SwNumberTree::tSwNumTreeNumber nTmpNumber = 0;
+ SwNumberTreeNode * pPred = (*aIt)->GetPred();
+
+ // #i64311#
+ // correct consideration of phantoms
+ // correct consideration of restart at a number tree node
+ if ( pPred )
+ {
+ if ( !(*aIt)->IsCounted() )
+ // #i65284#
+ nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() );
+ else
+ {
+ if ( (*aIt)->IsRestart() )
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() ) + 1;
+ }
+ }
+ else
+ {
+ if ( !(*aIt)->IsCounted() )
+ nTmpNumber = GetStartValue() - 1;
+ else
+ {
+ if ( (*aIt)->IsRestart() )
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ nTmpNumber = GetStartValue();
+ }
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+ }
+ while (aIt != mChildren.end() && *aIt != pNode);
+
+ // #i74748# - applied patch from garnier_romain
+ // number tree node has to be validated.
+ SetLastValid( aIt, true );
+}
+
+void SwNumberTreeNode::Validate(const SwNumberTreeNode * pNode) const
+{
+ if (! IsValid(pNode))
+ {
+ if (IsContinuous())
+ ValidateContinuous(pNode);
+ else
+ ValidateHierarchical(pNode);
+ }
+}
+
+void SwNumberTreeNode::GetNumberVector_(SwNumberTree::tNumberVector & rVector,
+ bool bValidate) const
+{
+ if (mpParent)
+ {
+ mpParent->GetNumberVector_(rVector, bValidate);
+ rVector.push_back(GetNumber(bValidate));
+ }
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetFirstNonPhantomChild()
+{
+ if (IsPhantom())
+ return (*mChildren.begin())->GetFirstNonPhantomChild();
+
+ return this;
+}
+
+/** Moves all children of this node that are greater than a given node
+ to the destination node.
+*/
+void SwNumberTreeNode::MoveGreaterChildren( SwNumberTreeNode& _rCompareNode,
+ SwNumberTreeNode& _rDestNode )
+{
+ if ( mChildren.empty() )
+ return;
+
+ // determine first child, which has to move to <_rDestNode>
+ tSwNumberTreeChildren::iterator aItUpper( mChildren.end() );
+ if ((*mChildren.begin())->IsPhantom() &&
+ _rCompareNode.LessThan(*(*mChildren.begin())->GetFirstNonPhantomChild()))
+ {
+ aItUpper = mChildren.begin();
+ }
+ else
+ {
+ aItUpper = mChildren.upper_bound(&_rCompareNode);
+ }
+
+ // move children
+ if (aItUpper != mChildren.end())
+ {
+ tSwNumberTreeChildren::iterator aIt;
+ for (aIt = aItUpper; aIt != mChildren.end(); ++aIt)
+ (*aIt)->mpParent = &_rDestNode;
+
+ _rDestNode.mChildren.insert(aItUpper, mChildren.end());
+
+ // #i60652#
+ // Because <mChildren.erase(aItUpper, mChildren.end())> could destroy
+ // the element, which is referenced by <mItLastValid>, it's needed to
+ // adjust <mItLastValid> before erasing <aIt>.
+ SetLastValid( mChildren.end() );
+
+ mChildren.erase(aItUpper, mChildren.end());
+
+ // #i60652#
+ if ( !mChildren.empty() )
+ {
+ SetLastValid( --(mChildren.end()) );
+ }
+ }
+
+#ifdef DBG_UTIL
+ IsSane(false);
+ _rDestNode.IsSane(true);
+#endif
+}
+
+void SwNumberTreeNode::MoveChildren(SwNumberTreeNode * pDest)
+{
+ if (! mChildren.empty())
+ {
+ tSwNumberTreeChildren::iterator aItBegin = mChildren.begin();
+ SwNumberTreeNode * pMyFirst = *mChildren.begin();
+
+ // #i60652#
+ // Because <mChildren.erase(aItBegin)> could destroy the element,
+ // which is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aItBegin>.
+ SetLastValid(mChildren.end());
+
+ if (pMyFirst->IsPhantom())
+ {
+ SwNumberTreeNode * pDestLast = nullptr;
+
+ if (pDest->mChildren.empty())
+ pDestLast = pDest->CreatePhantom();
+ else
+ pDestLast = *pDest->mChildren.rbegin();
+
+ pMyFirst->MoveChildren(pDestLast);
+
+ delete pMyFirst;
+ mChildren.erase(aItBegin);
+
+ aItBegin = mChildren.begin();
+ }
+
+ for (auto& rpChild : mChildren)
+ rpChild->mpParent = pDest;
+
+ pDest->mChildren.insert(mChildren.begin(), mChildren.end());
+ mChildren.clear();
+ // <stl::set.clear()> destroys all existing iterators.
+ // Thus, <mItLastValid> is also destroyed and reset becomes necessary
+ mItLastValid = mChildren.end();
+ }
+
+ OSL_ENSURE(mChildren.empty(), "MoveChildren failed!");
+
+#ifdef DBG_UTIL
+ IsSane(false);
+ pDest->IsSane(false);
+#endif
+}
+
+void SwNumberTreeNode::AddChild(SwNumberTreeNode* pChild,
+ const int nDepth,
+ const SwDoc& rDoc)
+{
+ /*
+ Algorithm:
+
+ Search first child A that is greater than pChild,
+ A may be the end of children.
+ If nDepth > 0 then
+ {
+ if A is first child then
+ create new phantom child B at beginning of child list
+ else
+ B is A
+
+ Add child to B with depth nDepth - 1.
+ }
+ else
+ {
+ Insert pNode before A.
+
+ if A has predecessor B then
+ remove children of B that are greater as A and insert them as
+ children of A.
+ }
+
+*/
+
+ if ( nDepth < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::AddChild(..)> - parameter <nDepth> out of valid range. Serious defect." );
+ return;
+ }
+
+ if ( pChild->GetParent() != nullptr || pChild->GetChildCount() > 0 )
+ {
+ OSL_FAIL("only orphans allowed.");
+ return;
+ }
+
+ if (nDepth > 0)
+ {
+ tSwNumberTreeChildren::iterator aInsertDeepIt =
+ mChildren.upper_bound(pChild);
+
+ OSL_ENSURE(! (aInsertDeepIt != mChildren.end() &&
+ (*aInsertDeepIt)->IsPhantom()), " unexpected phantom");
+
+ if (aInsertDeepIt == mChildren.begin())
+ {
+ SwNumberTreeNode * pNew = CreatePhantom();
+
+ SetLastValid(mChildren.end());
+
+ if (pNew)
+ pNew->AddChild(pChild, nDepth - 1, rDoc);
+ }
+ else
+ {
+ --aInsertDeepIt;
+ (*aInsertDeepIt)->AddChild(pChild, nDepth - 1, rDoc);
+ }
+
+ }
+ else
+ {
+ pChild->PreAdd();
+ std::pair<tSwNumberTreeChildren::iterator, bool> aResult =
+ mChildren.insert(pChild);
+
+ if (aResult.second)
+ {
+ pChild->mpParent = this;
+ bool bNotification = pChild->IsNotificationEnabled(rDoc);
+ tSwNumberTreeChildren::iterator aInsertedIt = aResult.first;
+
+ if (aInsertedIt != mChildren.begin())
+ {
+ tSwNumberTreeChildren::iterator aPredIt = aInsertedIt;
+ --aPredIt;
+
+ // -->
+ // Move greater children of previous node to new child.
+ // This has to be done recursively on the children levels.
+ // Initialize loop variables <pPrevChildNode> and <pDestNode>
+ // for loop on children levels.
+ SwNumberTreeNode* pPrevChildNode( *aPredIt );
+ SwNumberTreeNode* pDestNode( pChild );
+ while ( pDestNode && pPrevChildNode &&
+ pPrevChildNode->GetChildCount() > 0 )
+ {
+ // move children
+ pPrevChildNode->MoveGreaterChildren( *pChild, *pDestNode );
+
+ // prepare next loop:
+ // - search of last child of <pPrevChildNode
+ // - If found, determine destination node
+ if ( pPrevChildNode->GetChildCount() > 0 )
+ {
+ tSwNumberTreeChildren::reverse_iterator aIt =
+ pPrevChildNode->mChildren.rbegin();
+ pPrevChildNode = *aIt;
+ // determine new destination node
+ if ( pDestNode->GetChildCount() > 0 )
+ {
+ pDestNode = *(pDestNode->mChildren.begin());
+ if ( !pDestNode->IsPhantom() )
+ {
+ pDestNode = pDestNode->mpParent->CreatePhantom();
+ }
+ }
+ else
+ {
+ pDestNode = pDestNode->CreatePhantom();
+ }
+ }
+ else
+ {
+ // ready -> break loop.
+ break;
+ }
+ }
+ // assure that unnecessary created phantoms at <pChild> are deleted.
+ pChild->ClearObsoletePhantoms();
+
+ if ((*aPredIt)->IsValid())
+ SetLastValid(aPredIt);
+ }
+ else
+ SetLastValid(mChildren.end());
+
+ ClearObsoletePhantoms();
+
+ if( bNotification )
+ {
+ // invalidation of not counted parent
+ // and notification of its siblings.
+ if ( !IsCounted() )
+ {
+ InvalidateMe();
+ NotifyInvalidSiblings(rDoc);
+ }
+ NotifyInvalidChildren(rDoc);
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ IsSane(false);
+#endif
+}
+
+void SwNumberTreeNode::RemoveChild(SwNumberTreeNode* pChild, const SwDoc& rDoc)
+{
+ /*
+ Algorithm:
+
+ if pChild has predecessor A then
+ B is A
+ else
+ create phantom child B at beginning of child list
+
+ Move children of pChild to B.
+ */
+
+ if (pChild->IsPhantom())
+ {
+ OSL_FAIL("not applicable to phantoms!");
+
+ return;
+ }
+
+ tSwNumberTreeChildren::const_iterator aRemoveIt = GetIterator(pChild);
+
+ if (aRemoveIt != mChildren.end())
+ {
+ SwNumberTreeNode * pRemove = *aRemoveIt;
+
+ pRemove->mpParent = nullptr;
+
+ tSwNumberTreeChildren::const_iterator aItPred = mChildren.end();
+
+ if (aRemoveIt == mChildren.begin())
+ {
+ if (! pRemove->mChildren.empty())
+ {
+ CreatePhantom();
+
+ aItPred = mChildren.begin();
+ }
+ }
+ else
+ {
+ aItPred = aRemoveIt;
+ --aItPred;
+ }
+
+ if (! pRemove->mChildren.empty())
+ {
+ pRemove->MoveChildren(*aItPred);
+ (*aItPred)->InvalidateTree();
+ (*aItPred)->NotifyInvalidChildren(rDoc);
+ }
+
+ // #i60652#
+ // Because <mChildren.erase(aRemoveIt)> could destroy the element,
+ // which is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aRemoveIt>.
+ if (aItPred != mChildren.end() && (*aItPred)->IsPhantom())
+ SetLastValid(mChildren.end());
+ else
+ SetLastValid(aItPred);
+
+ mChildren.erase(aRemoveIt);
+
+ NotifyInvalidChildren(rDoc);
+ }
+ else
+ {
+ OSL_FAIL("RemoveChild: failed!");
+ }
+
+ pChild->PostRemove();
+}
+
+void SwNumberTreeNode::RemoveMe(const SwDoc& rDoc)
+{
+ if (!mpParent)
+ return;
+
+ SwNumberTreeNode * pSavedParent = mpParent;
+
+ pSavedParent->RemoveChild(this, rDoc);
+
+ while (pSavedParent && pSavedParent->IsPhantom() &&
+ pSavedParent->HasOnlyPhantoms())
+ pSavedParent = pSavedParent->GetParent();
+
+ if (pSavedParent)
+ pSavedParent->ClearObsoletePhantoms();
+
+#ifdef DBG_UTIL
+ IsSane(false);
+#endif
+}
+
+bool SwNumberTreeNode::IsValid() const
+{
+ return mpParent && mpParent->IsValid(this);
+}
+
+SwNumberTree::tSwNumTreeNumber SwNumberTreeNode::GetNumber(bool bValidate)
+ const
+{
+ if (bValidate && mpParent)
+ mpParent->Validate(this);
+
+ return mnNumber;
+}
+
+
+SwNumberTree::tNumberVector SwNumberTreeNode::GetNumberVector() const
+{
+ vector<SwNumberTree::tSwNumTreeNumber> aResult;
+
+ GetNumberVector_(aResult);
+
+ return aResult;
+}
+
+bool SwNumberTreeNode::IsValid(const SwNumberTreeNode * pChild) const
+{
+ bool bResult = false;
+
+ if (mItLastValid != mChildren.end())
+ {
+ if (pChild && pChild->mpParent == this)
+ {
+ bResult = ! (*mItLastValid)->LessThan(*pChild);
+ }
+ }
+
+ return bResult;
+}
+
+
+bool SwNumberTreeNode::HasOnlyPhantoms() const
+{
+ bool bResult = false;
+
+ if (GetChildCount() == 1)
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mChildren.begin();
+
+ bResult = (*aIt)->IsPhantom() && (*aIt)->HasOnlyPhantoms();
+ }
+ else if (GetChildCount() == 0)
+ bResult = true;
+
+ return bResult;
+}
+
+bool SwNumberTreeNode::IsCounted() const
+{
+ return !IsPhantom() ||
+ ( IsCountPhantoms() && HasCountedChildren() );
+}
+
+bool SwNumberTreeNode::HasPhantomCountedParent() const
+{
+ bool bRet( false );
+
+ OSL_ENSURE( IsPhantom(),
+ "<SwNumberTreeNode::HasPhantomCountedParent()> - wrong usage of method - it's only for phantoms" );
+ if ( IsPhantom() && mpParent )
+ {
+ if ( mpParent == GetRoot() )
+ {
+ bRet = true;
+ }
+ else if ( !mpParent->IsPhantom() )
+ {
+ bRet = mpParent->IsCounted();
+ }
+ else
+ {
+ bRet = mpParent->IsCounted() && mpParent->HasPhantomCountedParent();
+ }
+ }
+
+ return bRet;
+}
+
+bool SwNumberTreeNode::IsFirst(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aIt = mChildren.begin();
+
+ if ((*aIt)->IsPhantom())
+ ++aIt;
+
+ return *aIt == pNode;
+}
+
+bool SwNumberTreeNode::IsFirst() const
+{
+ bool bResult = true;
+
+ if (GetParent())
+ {
+ if (GetParent()->IsFirst(this))
+ {
+ SwNumberTreeNode * pNode = GetParent();
+
+ while (pNode)
+ {
+ if (!pNode->IsPhantom() && pNode->GetParent())
+ {
+ bResult = false;
+ break;
+ }
+
+ pNode = pNode->GetParent();
+ }
+
+ // If node isn't the first child, it is the second child and the
+ // first child is a phantom. In this case check, if the first phantom
+ // child have only phantom children
+ if ( bResult &&
+ this != *(GetParent()->mChildren.begin()) &&
+ !(*(GetParent()->mChildren.begin()))->HasOnlyPhantoms() )
+ {
+ bResult = false;
+ }
+ }
+ else
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+void SwNumberTreeNode::SetLevelInListTree(const int nLevel, const SwDoc& rDoc)
+{
+ if ( nLevel < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::SetLevelInListTree(..)> - parameter <nLevel> out of valid range. Serious defect." );
+ return;
+ }
+
+ OSL_ENSURE( GetParent(),
+ "<SwNumberTreeNode::SetLevelInListTree(..)> - can only be called for number tree nodes in a list tree" );
+ if ( GetParent() )
+ {
+ if ( nLevel != GetLevelInListTree() )
+ {
+ SwNumberTreeNode* pRootTreeNode = GetRoot();
+ OSL_ENSURE( pRootTreeNode,
+ "<SwNumberTreeNode::SetLevelInListTree(..)> - no root tree node found. Serious defect." );
+
+ RemoveMe(rDoc);
+ pRootTreeNode->AddChild(this, nLevel, rDoc);
+ }
+ }
+}
+
+int SwNumberTreeNode::GetLevelInListTree() const
+{
+ if (mpParent)
+ return mpParent->GetLevelInListTree() + 1;
+
+ return -1;
+}
+
+SwNumberTreeNode::tSwNumberTreeChildren::size_type
+SwNumberTreeNode::GetChildCount() const
+{
+ return mChildren.size();
+}
+
+#ifdef DBG_UTIL
+void SwNumberTreeNode::IsSane(bool bRecursive) const
+{
+ vector<const SwNumberTreeNode*> aParents;
+
+ return IsSane(bRecursive, std::move(aParents));
+}
+
+void SwNumberTreeNode::IsSane(bool bRecursive,
+ vector<const SwNumberTreeNode *> rParents)
+ const
+{
+ assert(find(rParents.begin(), rParents.end(), this) == rParents.end());
+
+ assert(rParents.empty() || rParents.back() == mpParent);
+
+ rParents.push_back(this);
+
+ bool bFirst = true;
+ for (const auto& rpChild : mChildren)
+ {
+ if (rpChild)
+ {
+ if (rpChild->IsPhantom())
+ {
+ SAL_WARN_IF(rpChild->HasOnlyPhantoms(), "sw.core",
+ "HasOnlyPhantoms: is this an error?");
+
+ assert(bFirst && "found phantom not at first position.");
+ }
+
+ assert(rpChild->mpParent == this);
+
+ if (mpParent)
+ {
+ assert(rpChild->IsPhantom() || !rpChild->LessThan(*this));
+ }
+ }
+ else
+ {
+ assert(!"found child that is NULL");
+ }
+
+ if (bRecursive)
+ {
+ rpChild->IsSane(bRecursive, rParents);
+ }
+
+ bFirst = false;
+ }
+
+ rParents.pop_back();
+}
+#endif // DBG_UTIL
+
+SwNumberTreeNode::tSwNumberTreeChildren::const_iterator
+SwNumberTreeNode::GetIterator(const SwNumberTreeNode * pChild) const
+{
+ tSwNumberTreeChildren::const_iterator aItResult =
+ mChildren.find(const_cast<SwNumberTreeNode *>(pChild));
+
+ OSL_ENSURE( aItResult != mChildren.end(),
+ "something went wrong getting the iterator for a child");
+
+ return aItResult;
+}
+
+bool SwNumberTreeNodeLessThan(const SwNumberTreeNode * pA,
+ const SwNumberTreeNode * pB)
+{
+ bool bResult = false;
+
+ if (pA == nullptr && pB != nullptr)
+ bResult = true;
+ else if (pA != nullptr && pB != nullptr)
+ bResult = pA->LessThan(*pB);
+
+ return bResult;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetLastDescendant() const
+{
+ SwNumberTreeNode * pResult = nullptr;
+ tSwNumberTreeChildren::const_reverse_iterator aIt = mChildren.rbegin();
+
+ if (aIt != mChildren.rend())
+ {
+ pResult = (*aIt)->GetLastDescendant();
+
+ if (! pResult)
+ pResult = *aIt;
+ }
+
+ return pResult;
+}
+
+bool SwNumberTreeNode::LessThan(const SwNumberTreeNode & rTreeNode) const
+{
+ return this < &rTreeNode;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetPred(bool bSibling) const
+{
+ SwNumberTreeNode * pResult = nullptr;
+
+ if (mpParent)
+ {
+ tSwNumberTreeChildren::const_iterator aIt =
+ mpParent->GetIterator(this);
+
+ if ( aIt == mpParent->mChildren.begin() )
+ {
+ // #i64311#
+ // root node is no valid predecessor
+ pResult = mpParent->GetParent() ? mpParent : nullptr;
+ }
+ else
+ {
+ --aIt;
+
+ if ( !bSibling )
+ pResult = (*aIt)->GetLastDescendant();
+ else
+ pResult = (*aIt);
+
+ if (! pResult)
+ pResult = (*aIt);
+ }
+ }
+
+ return pResult;
+}
+
+void SwNumberTreeNode::SetLastValid
+ ( const SwNumberTreeNode::tSwNumberTreeChildren::const_iterator& aItValid,
+ bool bValidating ) const
+{
+ OSL_ENSURE( (aItValid == mChildren.end() || GetIterator(*aItValid) != mChildren.end()),
+ "last-valid iterator");
+
+ if (
+ bValidating ||
+ aItValid == mChildren.end() ||
+ (mItLastValid != mChildren.end() &&
+ (*aItValid)->LessThan(**mItLastValid))
+ )
+ {
+ mItLastValid = aItValid;
+ // invalidation of children of next not counted is needed
+ if ( GetParent() )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ ++aParentChildIt;
+ if ( aParentChildIt != GetParent()->mChildren.end() )
+ {
+ SwNumberTreeNode* pNextNode( *aParentChildIt );
+ if ( !pNextNode->IsCounted() )
+ {
+ pNextNode->InvalidateChildren();
+ }
+ }
+ }
+ }
+
+ {
+ if (IsContinuous())
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ if (aIt != mChildren.end())
+ ++aIt;
+ else
+ aIt = mChildren.begin();
+
+ while (aIt != mChildren.end())
+ {
+ (*aIt)->InvalidateTree();
+
+ ++aIt;
+ }
+
+ if (mpParent)
+ {
+ mpParent->SetLastValid(mpParent->GetIterator(this), bValidating);
+ }
+ }
+ }
+}
+
+void SwNumberTreeNode::InvalidateTree() const
+{
+ // do not call SetInvalid, would cause loop !!!
+ mItLastValid = mChildren.end();
+
+ for (const auto& rpChild : mChildren)
+ rpChild->InvalidateTree();
+}
+
+void SwNumberTreeNode::Invalidate(SwNumberTreeNode const * pChild)
+{
+ if (pChild->IsValid())
+ {
+ tSwNumberTreeChildren::const_iterator aIt = GetIterator(pChild);
+
+ if (aIt != mChildren.begin())
+ --aIt;
+ else
+ aIt = mChildren.end();
+
+ SetLastValid(aIt);
+
+ }
+}
+
+void SwNumberTreeNode::InvalidateMe()
+{
+ if (mpParent)
+ mpParent->Invalidate(this);
+}
+
+void SwNumberTreeNode::ValidateMe()
+{
+ if (mpParent)
+ mpParent->Validate(this);
+}
+
+void SwNumberTreeNode::Notify(const SwDoc& rDoc)
+{
+ if (IsNotifiable(rDoc))
+ {
+ if (! IsPhantom())
+ NotifyNode();
+
+ for (auto& rpChild : mChildren)
+ rpChild->Notify(rDoc);
+ }
+}
+
+void SwNumberTreeNode::NotifyInvalidChildren(const SwDoc& rDoc)
+{
+ if (IsNotifiable(rDoc))
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ if (aIt == mChildren.end())
+ aIt = mChildren.begin();
+ else
+ ++aIt;
+
+ while (aIt != mChildren.end())
+ {
+ (*aIt)->Notify(rDoc);
+
+ ++aIt;
+ }
+ // notification of next not counted node is also needed.
+ if ( GetParent() )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ ++aParentChildIt;
+ if ( aParentChildIt != GetParent()->mChildren.end() )
+ {
+ SwNumberTreeNode* pNextNode( *aParentChildIt );
+ if ( !pNextNode->IsCounted() )
+ {
+ pNextNode->NotifyInvalidChildren(rDoc);
+ }
+ }
+ }
+
+ }
+
+ if (IsContinuous() && mpParent)
+ mpParent->NotifyInvalidChildren(rDoc);
+}
+
+void SwNumberTreeNode::NotifyInvalidSiblings(const SwDoc& rDoc)
+{
+ if (mpParent != nullptr)
+ mpParent->NotifyInvalidChildren(rDoc);
+}
+
+// #i81002#
+const SwNumberTreeNode* SwNumberTreeNode::GetPrecedingNodeOf(
+ const SwNumberTreeNode& rNode ) const
+{
+ const SwNumberTreeNode* pPrecedingNode( nullptr );
+
+ if ( GetChildCount() > 0 )
+ {
+ tSwNumberTreeChildren::const_iterator aUpperBoundIt =
+ mChildren.upper_bound( const_cast<SwNumberTreeNode*>(&rNode) );
+ if ( aUpperBoundIt != mChildren.begin() )
+ {
+ --aUpperBoundIt;
+ pPrecedingNode = (*aUpperBoundIt)->GetPrecedingNodeOf( rNode );
+ }
+ }
+
+ if ( pPrecedingNode == nullptr && GetRoot() )
+ {
+ // <this> node has no children or the given node precedes all its children
+ // and the <this> node isn't the root node.
+ // Thus, compare the given node with the <this> node in order to check,
+ // if the <this> node precedes the given node.
+ if ( !(rNode.LessThan( *this )) )
+ {
+ pPrecedingNode = this;
+ }
+ }
+
+ return pPrecedingNode;
+}
+
+void SwNumberTreeNode::NotifyNodesOnListLevel( const int nListLevel )
+{
+ if ( nListLevel < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::NotifyNodesOnListLevel(..)> - invalid list level provided" );
+ return;
+ }
+
+ SwNumberTreeNode* pRootNode = GetParent() ? GetRoot() : this;
+
+ pRootNode->NotifyChildrenOnDepth( nListLevel );
+}
+
+void SwNumberTreeNode::NotifyChildrenOnDepth( const int nDepth )
+{
+ OSL_ENSURE( nDepth >= 0,
+ "<SwNumberTreeNode::NotifyChildrenOnDepth(..)> - misusage" );
+
+ for ( const auto& rpChild : mChildren )
+ {
+ if ( nDepth == 0 )
+ {
+ rpChild->NotifyNode();
+ }
+ else
+ {
+ rpChild->NotifyChildrenOnDepth( nDepth - 1 );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */