summaryrefslogtreecommitdiffstats
path: root/vcl/source/treelist/treelist.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/treelist/treelist.cxx')
-rw-r--r--vcl/source/treelist/treelist.cxx1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/vcl/source/treelist/treelist.cxx b/vcl/source/treelist/treelist.cxx
new file mode 100644
index 000000000..9e057e598
--- /dev/null
+++ b/vcl/source/treelist/treelist.cxx
@@ -0,0 +1,1524 @@
+/* -*- 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 <vcl/toolkit/treelist.hxx>
+#include <vcl/toolkit/treelistentry.hxx>
+#include <vcl/toolkit/viewdataentry.hxx>
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+#include <unordered_map>
+
+
+typedef std::unordered_map<SvTreeListEntry*, std::unique_ptr<SvViewDataEntry>> SvDataTable;
+
+struct SvListView::Impl
+{
+ SvListView & m_rThis;
+
+ SvDataTable m_DataTable; // Mapping SvTreeListEntry -> ViewData
+
+ sal_uInt32 m_nVisibleCount;
+ sal_uInt32 m_nSelectionCount;
+ bool m_bVisPositionsValid;
+
+ explicit Impl(SvListView & rThis)
+ : m_rThis(rThis)
+ , m_nVisibleCount(0)
+ , m_nSelectionCount(0)
+ , m_bVisPositionsValid(false)
+ {}
+
+ void InitTable();
+ void RemoveViewData( SvTreeListEntry* pParent );
+
+ void ActionMoving(SvTreeListEntry* pEntry);
+ void ActionMoved();
+ void ActionInserted(SvTreeListEntry* pEntry);
+ void ActionInsertedTree(SvTreeListEntry* pEntry);
+ void ActionRemoving(SvTreeListEntry* pEntry);
+ void ActionClear();
+};
+
+
+SvTreeList::SvTreeList(SvListView& listView) :
+ mrOwnerListView(listView),
+ mbEnableInvalidate(true)
+{
+ nEntryCount = 0;
+ bAbsPositionsValid = false;
+ pRootItem.reset(new SvTreeListEntry);
+ eSortMode = SvSortMode::None;
+}
+
+SvTreeList::~SvTreeList()
+{
+}
+
+void SvTreeList::Broadcast(
+ SvListAction nActionId,
+ SvTreeListEntry* pEntry1,
+ SvTreeListEntry* pEntry2,
+ sal_uInt32 nPos
+)
+{
+ mrOwnerListView.ModelNotification(nActionId, pEntry1, pEntry2, nPos);
+}
+
+// an entry is visible if all parents are expanded
+bool SvTreeList::IsEntryVisible( const SvListView* pView, SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"IsVisible:Invalid Params");
+ bool bRetVal = false;
+ do
+ {
+ if ( pEntry == pRootItem.get() )
+ {
+ bRetVal = true;
+ break;
+ }
+ pEntry = pEntry->pParent;
+ } while( pView->IsExpanded( pEntry ) );
+ return bRetVal;
+}
+
+sal_uInt16 SvTreeList::GetDepth( const SvTreeListEntry* pEntry ) const
+{
+ if (!pEntry)
+ return 0;
+ DBG_ASSERT(pEntry && pEntry!=pRootItem.get(),"GetDepth:Bad Entry");
+ sal_uInt16 nDepth = 0;
+ while( pEntry && pEntry->pParent != pRootItem.get() )
+ {
+ nDepth++;
+ pEntry = pEntry->pParent;
+ }
+ return nDepth;
+}
+
+bool SvTreeList::IsAtRootDepth( const SvTreeListEntry* pEntry ) const
+{
+ return pEntry->pParent == pRootItem.get();
+}
+
+void SvTreeList::Clear()
+{
+ Broadcast( SvListAction::CLEARING );
+ pRootItem->ClearChildren();
+ nEntryCount = 0;
+ Broadcast( SvListAction::CLEARED );
+}
+
+bool SvTreeList::IsChild(const SvTreeListEntry* pParent, const SvTreeListEntry* pChild) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (pParent->m_Children.empty())
+ return false;
+
+ for (auto const& it : pParent->m_Children)
+ {
+ const SvTreeListEntry* pThis = it.get();
+ if (pThis == pChild)
+ return true;
+ else
+ {
+ bool bIsChild = IsChild(pThis, pChild);
+ if (bIsChild)
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace {
+
+class FindByPointer
+{
+ const SvTreeListEntry* mpEntry;
+public:
+ explicit FindByPointer(const SvTreeListEntry* p) : mpEntry(p) {}
+
+ bool operator() (std::unique_ptr<SvTreeListEntry> const& rpEntry) const
+ {
+ return mpEntry == rpEntry.get();
+ }
+};
+
+sal_uInt32 findEntryPosition(const SvTreeListEntries& rDst, const SvTreeListEntry* pEntry)
+{
+ SvTreeListEntries::const_iterator itPos = std::find_if(rDst.begin(), rDst.end(), FindByPointer(pEntry));
+ if (itPos == rDst.end())
+ return static_cast<sal_uInt32>(~0);
+
+ return static_cast<sal_uInt32>(std::distance(rDst.begin(), itPos));
+}
+
+}
+
+sal_uInt32 SvTreeList::Move(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
+{
+ // pDest may be 0!
+ DBG_ASSERT(pSrcEntry,"Entry?");
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+ DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target");
+
+ Broadcast( SvListAction::MOVING, pSrcEntry, pTargetParent, nListPos );
+
+ if ( pSrcEntry == pTargetParent )
+ // You can't move an entry onto itself as the parent. Just return its
+ // position and bail out.
+ return pSrcEntry->GetChildListPos();
+
+ bAbsPositionsValid = false;
+
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+ SvTreeListEntries& rSrc = pSrcEntry->pParent->m_Children;
+
+ bool bSameParent = pTargetParent == pSrcEntry->pParent;
+
+ // Find the position of the entry being moved in the source container.
+ SvTreeListEntries::iterator itSrcPos = rSrc.begin(), itEnd = rSrc.end();
+ for (; itSrcPos != itEnd; ++itSrcPos)
+ {
+ const SvTreeListEntry* p = (*itSrcPos).get();
+ if (p == pSrcEntry)
+ // Found
+ break;
+ }
+
+ if (itSrcPos == itEnd)
+ {
+ OSL_FAIL("Source entry not found! This should never happen.");
+ return pSrcEntry->GetChildListPos();
+ }
+
+ if (bSameParent)
+ {
+ // Moving within the same parent.
+
+ size_t nSrcPos = std::distance(rSrc.begin(), itSrcPos);
+ if (nSrcPos == nListPos)
+ // Nothing to move here.
+ return pSrcEntry->GetChildListPos();
+
+ if (nSrcPos < nListPos)
+ // Destination position shifts left after removing the original.
+ --nListPos;
+
+ // Release the original.
+ std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
+ assert(pOriginal);
+ rSrc.erase(itSrcPos);
+
+ // Determine the insertion position.
+ SvTreeListEntries::iterator itDstPos = rSrc.end();
+ if (nListPos < rSrc.size())
+ {
+ itDstPos = rSrc.begin();
+ std::advance(itDstPos, nListPos);
+ }
+ rSrc.insert(itDstPos, std::move(pOriginal));
+ }
+ else
+ {
+ // Moving from one parent to another.
+ SvTreeListEntries::iterator itDstPos = rDst.end();
+ if (nListPos < rDst.size())
+ {
+ itDstPos = rDst.begin();
+ std::advance(itDstPos, nListPos);
+ }
+ std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
+ assert(pOriginal);
+ rSrc.erase(itSrcPos);
+ rDst.insert(itDstPos, std::move(pOriginal));
+ }
+
+ // move parent (do this only now, because we need the parent for
+ // deleting the old child list!)
+ pSrcEntry->pParent = pTargetParent;
+
+ // correct list position in target list
+ SetListPositions(rDst);
+ if (!bSameParent)
+ SetListPositions(rSrc);
+
+ sal_uInt32 nRetVal = findEntryPosition(rDst, pSrcEntry);
+ OSL_ENSURE(nRetVal == pSrcEntry->GetChildListPos(), "ListPos not valid");
+ Broadcast( SvListAction::MOVED,pSrcEntry,pTargetParent,nRetVal);
+ return nRetVal;
+}
+
+sal_uInt32 SvTreeList::Copy(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
+{
+ // pDest may be 0!
+ DBG_ASSERT(pSrcEntry,"Entry?");
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+
+ bAbsPositionsValid = false;
+
+ sal_uInt32 nCloneCount = 0;
+ SvTreeListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount );
+ nEntryCount += nCloneCount;
+
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+
+ pClonedEntry->pParent = pTargetParent; // move parent
+
+ if (nListPos < rDst.size())
+ {
+ SvTreeListEntries::iterator itPos = rDst.begin(); // insertion position.
+ std::advance(itPos, nListPos);
+ rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pClonedEntry));
+ }
+ else
+ rDst.push_back(std::unique_ptr<SvTreeListEntry>(pClonedEntry));
+
+ SetListPositions(rDst); // correct list position in target list
+
+ Broadcast( SvListAction::INSERTED_TREE, pClonedEntry );
+ sal_uInt32 nRetVal = findEntryPosition(rDst, pClonedEntry);
+ return nRetVal;
+}
+
+void SvTreeList::Move( SvTreeListEntry* pSrcEntry, SvTreeListEntry* pDstEntry )
+{
+ SvTreeListEntry* pParent;
+ sal_uInt32 nPos;
+
+ if ( !pDstEntry )
+ {
+ pParent = pRootItem.get();
+ nPos = 0;
+ }
+ else
+ {
+ pParent = pDstEntry->pParent;
+ nPos = pDstEntry->GetChildListPos();
+ nPos++; // (On screen:) insert _below_ pDstEntry
+ }
+ Move( pSrcEntry, pParent, nPos );
+}
+
+void SvTreeList::InsertTree(SvTreeListEntry* pSrcEntry,
+ SvTreeListEntry* pTargetParent,sal_uInt32 nListPos)
+{
+ DBG_ASSERT(pSrcEntry,"InsertTree:Entry?");
+ if ( !pSrcEntry )
+ return;
+
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+
+ // take sorting into account
+ GetInsertionPos( pSrcEntry, pTargetParent, nListPos );
+
+ bAbsPositionsValid = false;
+
+ pSrcEntry->pParent = pTargetParent; // move parent
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+
+ if (nListPos < rDst.size())
+ {
+ SvTreeListEntries::iterator itPos = rDst.begin();
+ std::advance(itPos, nListPos);
+ rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pSrcEntry));
+ }
+ else
+ rDst.push_back(std::unique_ptr<SvTreeListEntry>(pSrcEntry));
+
+ SetListPositions(rDst); // correct list position in target list
+ nEntryCount += GetChildCount( pSrcEntry );
+ nEntryCount++; // the parent is new, too
+
+ Broadcast(SvListAction::INSERTED_TREE, pSrcEntry );
+}
+
+SvTreeListEntry* SvTreeList::CloneEntry( SvTreeListEntry* pSource ) const
+{
+ if( aCloneLink.IsSet() )
+ return aCloneLink.Call( pSource );
+ SvTreeListEntry* pEntry = new SvTreeListEntry;
+ pEntry->Clone(pSource);
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::Clone( SvTreeListEntry* pEntry, sal_uInt32& nCloneCount ) const
+{
+ SvTreeListEntry* pClonedEntry = CloneEntry( pEntry );
+ nCloneCount = 1;
+ if (!pEntry->m_Children.empty())
+ // Clone the child entries.
+ CloneChildren(pClonedEntry->m_Children, nCloneCount, pEntry->m_Children, *pClonedEntry);
+
+ return pClonedEntry;
+}
+
+void SvTreeList::CloneChildren(
+ SvTreeListEntries& rDst, sal_uInt32& rCloneCount, SvTreeListEntries& rSrc, SvTreeListEntry& rNewParent) const
+{
+ SvTreeListEntries aClone;
+ for (auto const& elem : rSrc)
+ {
+ SvTreeListEntry& rEntry = *elem;
+ std::unique_ptr<SvTreeListEntry> pNewEntry(CloneEntry(&rEntry));
+ ++rCloneCount;
+ pNewEntry->pParent = &rNewParent;
+ if (!rEntry.m_Children.empty())
+ // Clone entries recursively.
+ CloneChildren(pNewEntry->m_Children, rCloneCount, rEntry.m_Children, *pNewEntry);
+
+ aClone.push_back(std::move(pNewEntry));
+ }
+
+ rDst.swap(aClone);
+}
+
+sal_uInt32 SvTreeList::GetChildCount( const SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ return GetEntryCount();
+
+ if (pParent->m_Children.empty())
+ return 0;
+
+ sal_uInt32 nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = Next(const_cast<SvTreeListEntry*>(pParent), &nActDepth);
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+ nCount--;
+ return nCount;
+}
+
+sal_uInt32 SvTreeList::GetVisibleChildCount(const SvListView* pView, SvTreeListEntry* pParent) const
+{
+ DBG_ASSERT(pView,"GetVisChildCount:No View");
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (!pParent || !pView->IsExpanded(pParent) || pParent->m_Children.empty())
+ return 0;
+
+ sal_uInt32 nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = NextVisible( pView, pParent, &nActDepth );
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+ nCount--;
+ return nCount;
+}
+
+sal_uInt32 SvTreeList::GetChildSelectionCount(const SvListView* pView,SvTreeListEntry* pParent) const
+{
+ DBG_ASSERT(pView,"GetChildSelCount:No View");
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (!pParent || pParent->m_Children.empty())
+ return 0;
+
+ sal_uInt32 nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = Next( pParent, &nActDepth );
+ if( pParent && pView->IsSelected( pParent ) && nRefDepth < nActDepth)
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+// nCount--;
+ return nCount;
+}
+
+SvTreeListEntry* SvTreeList::First() const
+{
+ if ( nEntryCount )
+ return pRootItem->m_Children[0].get();
+ else
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Next( SvTreeListEntry* pActEntry, sal_uInt16* pDepth ) const
+{
+ DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" );
+ if ( !pActEntry || !pActEntry->pParent )
+ return nullptr;
+
+ sal_uInt16 nDepth = 0;
+ bool bWithDepth = false;
+ if ( pDepth )
+ {
+ nDepth = *pDepth;
+ bWithDepth = true;
+ }
+
+ // Get the list where the current entry belongs to (from its parent).
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uInt32 nActualPos = pActEntry->GetChildListPos();
+
+ if (!pActEntry->m_Children.empty())
+ {
+ // The current entry has children. Get its first child entry.
+ nDepth++;
+ pActEntry = pActEntry->m_Children[0].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+
+ if (pActualList->size() > (nActualPos+1))
+ {
+ // Get the next sibling of the current entry.
+ pActEntry = (*pActualList)[nActualPos+1].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+
+ // Move up level(s) until we find the level where the next sibling exists.
+ SvTreeListEntry* pParent = pActEntry->pParent;
+ nDepth--;
+ while( pParent != pRootItem.get() && pParent != nullptr )
+ {
+ DBG_ASSERT(pParent!=nullptr,"TreeData corrupt!");
+ pActualList = &pParent->pParent->m_Children;
+ nActualPos = pParent->GetChildListPos();
+ if (pActualList->size() > (nActualPos+1))
+ {
+ pActEntry = (*pActualList)[nActualPos+1].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+ pParent = pParent->pParent;
+ nDepth--;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Prev( SvTreeListEntry* pActEntry ) const
+{
+ DBG_ASSERT(pActEntry!=nullptr,"Entry?");
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uInt32 nActualPos = pActEntry->GetChildListPos();
+
+ if ( nActualPos > 0 )
+ {
+ pActEntry = (*pActualList)[nActualPos-1].get();
+ while (!pActEntry->m_Children.empty())
+ {
+ pActualList = &pActEntry->m_Children;
+ pActEntry = pActualList->back().get();
+ }
+ return pActEntry;
+ }
+ if ( pActEntry->pParent == pRootItem.get() )
+ return nullptr;
+
+ pActEntry = pActEntry->pParent;
+
+ if ( pActEntry )
+ {
+ return pActEntry;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Last() const
+{
+ SvTreeListEntries* pActList = &pRootItem->m_Children;
+ SvTreeListEntry* pEntry = nullptr;
+ while (!pActList->empty())
+ {
+ pEntry = pActList->back().get();
+ pActList = &pEntry->m_Children;
+ }
+ return pEntry;
+}
+
+sal_uInt32 SvTreeList::GetVisiblePos( const SvListView* pView, SvTreeListEntry const * pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"View/Entry?");
+
+ if (!pView->m_pImpl->m_bVisPositionsValid)
+ {
+ // to make GetVisibleCount refresh the positions
+ const_cast<SvListView*>(pView)->m_pImpl->m_nVisibleCount = 0;
+ GetVisibleCount( const_cast<SvListView*>(pView) );
+ }
+ const SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ return pViewData->nVisPos;
+}
+
+sal_uInt32 SvTreeList::GetVisibleCount( SvListView* pView ) const
+{
+ assert(pView && "GetVisCount:No View");
+ if( !pView->HasViewData() )
+ return 0;
+ if (pView->m_pImpl->m_nVisibleCount)
+ return pView->m_pImpl->m_nVisibleCount;
+
+ sal_uInt32 nPos = 0;
+ SvTreeListEntry* pEntry = First(); // first entry is always visible
+ while ( pEntry )
+ {
+ SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ pViewData->nVisPos = nPos;
+ nPos++;
+ pEntry = NextVisible( pView, pEntry );
+ }
+#ifdef DBG_UTIL
+ if( nPos > 10000000 )
+ {
+ OSL_FAIL("nVisibleCount bad");
+ }
+#endif
+ pView->m_pImpl->m_nVisibleCount = nPos;
+ pView->m_pImpl->m_bVisPositionsValid = true;
+ return nPos;
+}
+
+
+// For performance reasons, this function assumes that the passed entry is
+// already visible.
+SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pActEntry,sal_uInt16* pActDepth) const
+{
+ DBG_ASSERT(pView,"NextVisible:No View");
+ if ( !pActEntry )
+ return nullptr;
+
+ sal_uInt16 nDepth = 0;
+ bool bWithDepth = false;
+ if ( pActDepth )
+ {
+ nDepth = *pActDepth;
+ bWithDepth = true;
+ }
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uInt32 nActualPos = pActEntry->GetChildListPos();
+
+ if ( pView->IsExpanded(pActEntry) )
+ {
+ OSL_ENSURE(!pActEntry->m_Children.empty(), "Pass entry is supposed to have child entries.");
+
+ nDepth++;
+ pActEntry = pActEntry->m_Children[0].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+
+ nActualPos++;
+ if ( pActualList->size() > nActualPos )
+ {
+ pActEntry = (*pActualList)[nActualPos].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+
+ SvTreeListEntry* pParent = pActEntry->pParent;
+ nDepth--;
+ while( pParent != pRootItem.get() )
+ {
+ pActualList = &pParent->pParent->m_Children;
+ nActualPos = pParent->GetChildListPos();
+ nActualPos++;
+ if ( pActualList->size() > nActualPos )
+ {
+ pActEntry = (*pActualList)[nActualPos].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+ pParent = pParent->pParent;
+ nDepth--;
+ }
+ return nullptr;
+}
+
+
+// For performance reasons, this function assumes that the passed entry is
+// already visible.
+
+SvTreeListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvTreeListEntry* pActEntry) const
+{
+ DBG_ASSERT(pView&&pActEntry,"PrevVis:View/Entry?");
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uInt32 nActualPos = pActEntry->GetChildListPos();
+
+ if ( nActualPos > 0 )
+ {
+ pActEntry = (*pActualList)[nActualPos-1].get();
+ while( pView->IsExpanded(pActEntry) )
+ {
+ pActualList = &pActEntry->m_Children;
+ pActEntry = pActualList->back().get();
+ }
+ return pActEntry;
+ }
+
+ if ( pActEntry->pParent == pRootItem.get() )
+ return nullptr;
+
+ pActEntry = pActEntry->pParent;
+ if ( pActEntry )
+ {
+ return pActEntry;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::LastVisible( const SvListView* pView) const
+{
+ DBG_ASSERT(pView,"LastVis:No View");
+ SvTreeListEntry* pEntry = Last();
+ while( pEntry && !IsEntryVisible( pView, pEntry ) )
+ pEntry = PrevVisible( pView, pEntry );
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pEntry,sal_uInt16& nDelta) const
+{
+ DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"NextVis:Wrong Prms/!Vis");
+
+ sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry );
+ // nDelta entries existent?
+ // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7
+ // nNewDelta = 10-nVisPos-1 == 4
+ if (nVisPos+nDelta >= pView->m_pImpl->m_nVisibleCount)
+ {
+ nDelta = static_cast<sal_uInt16>(pView->m_pImpl->m_nVisibleCount-nVisPos);
+ nDelta--;
+ }
+ sal_uInt16 nDeltaTmp = nDelta;
+ while( nDeltaTmp )
+ {
+ pEntry = NextVisible( pView, pEntry );
+ nDeltaTmp--;
+ DBG_ASSERT(pEntry,"Entry?");
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvTreeListEntry* pEntry, sal_uInt16& nDelta ) const
+{
+ DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis");
+
+ sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry );
+ // nDelta entries existent?
+ // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20
+ // nNewDelta = nNewVisPos
+ if ( nDelta > nVisPos )
+ nDelta = static_cast<sal_uInt16>(nVisPos);
+ sal_uInt16 nDeltaTmp = nDelta;
+ while( nDeltaTmp )
+ {
+ pEntry = PrevVisible( pView, pEntry );
+ nDeltaTmp--;
+ DBG_ASSERT(pEntry,"Entry?");
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::FirstSelected( const SvListView* pView) const
+{
+ DBG_ASSERT(pView,"FirstSel:No View");
+ if( !pView )
+ return nullptr;
+ SvTreeListEntry* pActSelEntry = First();
+ while( pActSelEntry && !pView->IsSelected(pActSelEntry) )
+ pActSelEntry = NextVisible( pView, pActSelEntry );
+ return pActSelEntry;
+}
+
+
+SvTreeListEntry* SvTreeList::FirstChild( SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ SvTreeListEntry* pResult;
+ if (!pParent->m_Children.empty())
+ pResult = pParent->m_Children[0].get();
+ else
+ pResult = nullptr;
+ return pResult;
+}
+
+SvTreeListEntry* SvTreeList::NextSelected( const SvListView* pView, SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"NextSel:View/Entry?");
+ pEntry = Next( pEntry );
+ while( pEntry && !pView->IsSelected(pEntry) )
+ pEntry = Next( pEntry );
+ return pEntry;
+}
+
+sal_uInt32 SvTreeList::Insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uInt32 nPos )
+{
+ DBG_ASSERT( pEntry,"Entry?");
+
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ SvTreeListEntries& rList = pParent->m_Children;
+
+ // take sorting into account
+ GetInsertionPos( pEntry, pParent, nPos );
+
+ bAbsPositionsValid = false;
+ pEntry->pParent = pParent;
+
+ if (nPos < rList.size())
+ {
+ SvTreeListEntries::iterator itPos = rList.begin();
+ std::advance(itPos, nPos);
+ rList.insert(itPos, std::unique_ptr<SvTreeListEntry>(pEntry));
+ }
+ else
+ rList.push_back(std::unique_ptr<SvTreeListEntry>(pEntry));
+
+ nEntryCount++;
+ if (nPos != TREELIST_APPEND && (nPos != (rList.size()-1)))
+ SetListPositions(rList);
+ else
+ pEntry->nListPos = rList.size()-1;
+
+ Broadcast( SvListAction::INSERTED, pEntry );
+ return nPos; // pEntry->nListPos;
+}
+
+sal_uInt32 SvTreeList::GetAbsPos( const SvTreeListEntry* pEntry) const
+{
+ if ( !bAbsPositionsValid )
+ const_cast<SvTreeList*>(this)->SetAbsolutePositions();
+ return pEntry->nAbsPos;
+}
+
+sal_uInt32 SvTreeList::GetRelPos( const SvTreeListEntry* pChild )
+{
+ return pChild->GetChildListPos();
+}
+
+void SvTreeList::SetAbsolutePositions()
+{
+ sal_uInt32 nPos = 0;
+ SvTreeListEntry* pEntry = First();
+ while ( pEntry )
+ {
+ pEntry->nAbsPos = nPos;
+ nPos++;
+ pEntry = Next( pEntry );
+ }
+ bAbsPositionsValid = true;
+}
+
+void SvListView::ExpandListEntry( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Expand:View/Entry?");
+ if ( IsExpanded(pEntry) )
+ return;
+
+ DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Expand: We expected to have child entries.");
+
+ SvViewDataEntry* pViewData = GetViewData(pEntry);
+ pViewData->SetExpanded(true);
+ SvTreeListEntry* pParent = pEntry->pParent;
+ // if parent is visible, invalidate status data
+ if ( IsExpanded( pParent ) )
+ {
+ m_pImpl->m_bVisPositionsValid = false;
+ m_pImpl->m_nVisibleCount = 0;
+ }
+}
+
+void SvListView::CollapseListEntry( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Collapse:View/Entry?");
+ if ( !IsExpanded(pEntry) )
+ return;
+
+ DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Collapse: We expected to have child entries.");
+
+ SvViewDataEntry* pViewData = GetViewData( pEntry );
+ pViewData->SetExpanded(false);
+
+ SvTreeListEntry* pParent = pEntry->pParent;
+ if ( IsExpanded(pParent) )
+ {
+ m_pImpl->m_nVisibleCount = 0;
+ m_pImpl->m_bVisPositionsValid = false;
+ }
+}
+
+bool SvListView::SelectListEntry( SvTreeListEntry* pEntry, bool bSelect )
+{
+ DBG_ASSERT(pEntry,"Select:View/Entry?");
+ SvViewDataEntry* pViewData = GetViewData( pEntry );
+ if ( bSelect )
+ {
+ if ( pViewData->IsSelected() || !pViewData->IsSelectable() )
+ return false;
+ else
+ {
+ pViewData->SetSelected(true);
+ m_pImpl->m_nSelectionCount++;
+ }
+ }
+ else
+ {
+ if ( !pViewData->IsSelected() )
+ return false;
+ else
+ {
+ pViewData->SetSelected(false);
+ m_pImpl->m_nSelectionCount--;
+ }
+ }
+ return true;
+}
+
+bool SvTreeList::Remove( const SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Cannot remove root, use clear");
+
+ if( !pEntry->pParent )
+ {
+ OSL_FAIL("Removing entry not in model!");
+ // Under certain circumstances (which?), the explorer deletes entries
+ // from the view that it hasn't inserted into the view. We don't want
+ // to crash, so we catch this case here.
+ return false;
+ }
+
+ Broadcast(SvListAction::REMOVING, const_cast<SvTreeListEntry*>(pEntry));
+ sal_uInt32 nRemoved = 1 + GetChildCount(pEntry);
+ bAbsPositionsValid = false;
+
+ SvTreeListEntry* pParent = pEntry->pParent;
+ SvTreeListEntries& rList = pParent->m_Children;
+ bool bLastEntry = false;
+
+ // Since we need the live instance of SvTreeListEntry for broadcasting,
+ // we first need to pop it from the container, broadcast it, then delete
+ // the instance manually at the end.
+
+ std::unique_ptr<SvTreeListEntry> pEntryDeleter;
+ if ( pEntry->HasChildListPos() )
+ {
+ size_t nListPos = pEntry->GetChildListPos();
+ bLastEntry = (nListPos == (rList.size()-1));
+ SvTreeListEntries::iterator it = rList.begin();
+ std::advance(it, nListPos);
+ pEntryDeleter = std::move(*it);
+ rList.erase(it);
+ }
+ else
+ {
+ SvTreeListEntries::iterator it =
+ std::find_if(rList.begin(), rList.end(), FindByPointer(pEntry));
+ if (it != rList.end())
+ {
+ pEntryDeleter = std::move(*it);
+ rList.erase(it);
+ }
+ }
+
+ if (!rList.empty() && !bLastEntry)
+ SetListPositions(rList);
+
+ nEntryCount -= nRemoved;
+ Broadcast(SvListAction::REMOVED, const_cast<SvTreeListEntry*>(pEntry));
+
+ return true;
+}
+
+SvTreeListEntry* SvTreeList::GetEntryAtAbsPos( sal_uInt32 nAbsPos ) const
+{
+ SvTreeListEntry* pEntry = First();
+ while ( nAbsPos && pEntry )
+ {
+ pEntry = Next( pEntry );
+ nAbsPos--;
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uInt32 nVisPos ) const
+{
+ DBG_ASSERT(pView,"GetEntryAtVisPos:No View");
+ SvTreeListEntry* pEntry = First();
+ while ( nVisPos && pEntry )
+ {
+ pEntry = NextVisible( pView, pEntry );
+ nVisPos--;
+ }
+ return pEntry;
+}
+
+void SvTreeList::SetListPositions( SvTreeListEntries& rEntries )
+{
+ if (rEntries.empty())
+ return;
+
+ SvTreeListEntry& rFirst = *rEntries.front();
+ if (rFirst.pParent)
+ rFirst.pParent->InvalidateChildrensListPositions();
+}
+
+void SvTreeList::EnableInvalidate( bool bEnable )
+{
+ mbEnableInvalidate = bEnable;
+}
+
+void SvTreeList::InvalidateEntry( SvTreeListEntry* pEntry )
+{
+ if (!mbEnableInvalidate)
+ return;
+
+ Broadcast( SvListAction::INVALIDATE_ENTRY, pEntry );
+}
+
+SvTreeListEntry* SvTreeList::GetRootLevelParent( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"GetRootLevelParent:No Entry");
+ SvTreeListEntry* pCurParent = nullptr;
+ if ( pEntry )
+ {
+ pCurParent = pEntry->pParent;
+ if ( pCurParent == pRootItem.get() )
+ return pEntry; // is its own parent
+ while( pCurParent && pCurParent->pParent != pRootItem.get() )
+ pCurParent = pCurParent->pParent;
+ }
+ return pCurParent;
+}
+
+SvListView::SvListView()
+ : m_pImpl(new Impl(*this))
+{
+ pModel.reset(new SvTreeList(*this));
+ m_pImpl->InitTable();
+}
+
+void SvListView::dispose()
+{
+ pModel.reset();
+}
+
+SvListView::~SvListView()
+{
+ m_pImpl->m_DataTable.clear();
+}
+
+sal_uInt32 SvListView::GetSelectionCount() const
+{ return m_pImpl->m_nSelectionCount; }
+
+bool SvListView::HasViewData() const
+{ return m_pImpl->m_DataTable.size() > 1; } // There's always a ROOT
+
+
+void SvListView::Impl::InitTable()
+{
+ DBG_ASSERT(m_rThis.pModel,"InitTable:No Model");
+ DBG_ASSERT(!m_nSelectionCount && !m_nVisibleCount && !m_bVisPositionsValid,
+ "InitTable: Not cleared!");
+
+ if (!m_DataTable.empty())
+ {
+ DBG_ASSERT(m_DataTable.size() == 1, "InitTable: TableCount != 1");
+ // Delete the view data allocated to the Clear in the root.
+ // Attention: The model belonging to the root entry (and thus the entry
+ // itself) might already be deleted.
+ m_DataTable.clear();
+ }
+
+ SvTreeListEntry* pEntry;
+
+ // insert root entry
+ pEntry = m_rThis.pModel->pRootItem.get();
+ std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
+ pViewData->SetExpanded(true);
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ // now all the other entries
+ pEntry = m_rThis.pModel->First();
+ while( pEntry )
+ {
+ pViewData = std::make_unique<SvViewDataEntry>();
+ m_rThis.InitViewData( pViewData.get(), pEntry );
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ pEntry = m_rThis.pModel->Next( pEntry );
+ }
+}
+
+void SvListView::Clear()
+{
+ m_pImpl->m_DataTable.clear();
+ m_pImpl->m_nSelectionCount = 0;
+ m_pImpl->m_nVisibleCount = 0;
+ m_pImpl->m_bVisPositionsValid = false;
+ if( pModel )
+ {
+ // insert root entry
+ SvTreeListEntry* pEntry = pModel->pRootItem.get();
+ std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
+ pViewData->SetExpanded(true);
+ m_pImpl->m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ }
+}
+
+void SvListView::ModelHasCleared()
+{
+}
+
+void SvListView::ModelHasInserted( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelHasInsertedTree( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelIsMoving( SvTreeListEntry* /* pSource */ )
+{
+}
+
+
+void SvListView::ModelHasMoved( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelIsRemoving( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelHasRemoved( SvTreeListEntry* )
+{
+ //WARNING WARNING WARNING
+ //The supplied pointer should have been deleted
+ //before this call. Be careful not to use it!!!
+}
+
+void SvListView::ModelHasEntryInvalidated( SvTreeListEntry*)
+{
+}
+
+void SvListView::Impl::ActionMoving( SvTreeListEntry* pEntry )
+{
+ SvTreeListEntry* pParent = pEntry->pParent;
+ DBG_ASSERT(pParent,"Model not consistent");
+ if (pParent != m_rThis.pModel->pRootItem.get() && pParent->m_Children.size() == 1)
+ {
+ SvViewDataEntry* pViewData = m_DataTable.find( pParent )->second.get();
+ pViewData->SetExpanded(false);
+ }
+ // preliminary
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+}
+
+void SvListView::Impl::ActionMoved()
+{
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+}
+
+void SvListView::Impl::ActionInserted( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Insert:No Entry");
+ std::unique_ptr<SvViewDataEntry> pData(new SvViewDataEntry());
+ m_rThis.InitViewData( pData.get(), pEntry );
+ std::pair<SvDataTable::iterator, bool> aSuccess =
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pData)));
+ DBG_ASSERT(aSuccess.second,"Entry already in View");
+ if (m_nVisibleCount && m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ {
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+ }
+}
+
+void SvListView::Impl::ActionInsertedTree( SvTreeListEntry* pEntry )
+{
+ if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ {
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+ }
+ // iterate over entry and its children
+ SvTreeListEntry* pCurEntry = pEntry;
+ sal_uInt16 nRefDepth = m_rThis.pModel->GetDepth( pCurEntry );
+ while( pCurEntry )
+ {
+ DBG_ASSERT(m_DataTable.find(pCurEntry) != m_DataTable.end(),"Entry already in Table");
+ std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry());
+ m_rThis.InitViewData( pViewData.get(), pEntry );
+ m_DataTable.insert(std::make_pair(pCurEntry, std::move(pViewData)));
+ pCurEntry = m_rThis.pModel->Next( pCurEntry );
+ if ( pCurEntry && m_rThis.pModel->GetDepth(pCurEntry) <= nRefDepth)
+ pCurEntry = nullptr;
+ }
+}
+
+void SvListView::Impl::RemoveViewData( SvTreeListEntry* pParent )
+{
+ for (auto const& it : pParent->m_Children)
+ {
+ SvTreeListEntry& rEntry = *it;
+ m_DataTable.erase(&rEntry);
+ if (rEntry.HasChildren())
+ RemoveViewData(&rEntry);
+ }
+}
+
+
+void SvListView::Impl::ActionRemoving( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Remove:No Entry");
+
+ SvViewDataEntry* pViewData = m_DataTable.find( pEntry )->second.get();
+ sal_uInt32 nSelRemoved = 0;
+ if ( pViewData->IsSelected() )
+ nSelRemoved = 1 + m_rThis.pModel->GetChildSelectionCount(&m_rThis, pEntry);
+ m_nSelectionCount -= nSelRemoved;
+ sal_uInt32 nVisibleRemoved = 0;
+ if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ nVisibleRemoved = 1 + m_rThis.pModel->GetVisibleChildCount(&m_rThis, pEntry);
+ if( m_nVisibleCount )
+ {
+#ifdef DBG_UTIL
+ if (m_nVisibleCount < nVisibleRemoved)
+ {
+ OSL_FAIL("nVisibleRemoved bad");
+ }
+#endif
+ m_nVisibleCount -= nVisibleRemoved;
+ }
+ m_bVisPositionsValid = false;
+
+ m_DataTable.erase(pEntry);
+ RemoveViewData( pEntry );
+
+ SvTreeListEntry* pCurEntry = pEntry->pParent;
+ if (pCurEntry && pCurEntry != m_rThis.pModel->pRootItem.get() && pCurEntry->m_Children.size() == 1)
+ {
+ pViewData = m_DataTable.find(pCurEntry)->second.get();
+ pViewData->SetExpanded(false);
+ }
+}
+
+void SvListView::Impl::ActionClear()
+{
+ m_rThis.Clear();
+}
+
+void SvListView::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
+ SvTreeListEntry* /*pEntry2*/, sal_uInt32 /*nPos*/ )
+{
+
+ switch( nActionId )
+ {
+ case SvListAction::INSERTED:
+ m_pImpl->ActionInserted( pEntry1 );
+ ModelHasInserted( pEntry1 );
+ break;
+ case SvListAction::INSERTED_TREE:
+ m_pImpl->ActionInsertedTree( pEntry1 );
+ ModelHasInsertedTree( pEntry1 );
+ break;
+ case SvListAction::REMOVING:
+ ModelIsRemoving( pEntry1 );
+ m_pImpl->ActionRemoving( pEntry1 );
+ break;
+ case SvListAction::REMOVED:
+ ModelHasRemoved( pEntry1 );
+ break;
+ case SvListAction::MOVING:
+ ModelIsMoving( pEntry1 );
+ m_pImpl->ActionMoving( pEntry1 );
+ break;
+ case SvListAction::MOVED:
+ m_pImpl->ActionMoved();
+ ModelHasMoved( pEntry1 );
+ break;
+ case SvListAction::CLEARING:
+ m_pImpl->ActionClear();
+ ModelHasCleared(); // sic! for compatibility reasons!
+ break;
+ case SvListAction::CLEARED:
+ break;
+ case SvListAction::INVALIDATE_ENTRY:
+ // no action for the base class
+ ModelHasEntryInvalidated( pEntry1 );
+ break;
+ case SvListAction::RESORTED:
+ m_pImpl->m_bVisPositionsValid = false;
+ break;
+ case SvListAction::RESORTING:
+ break;
+ default:
+ OSL_FAIL("unknown ActionId");
+ }
+}
+
+void SvListView::InitViewData( SvViewDataEntry*, SvTreeListEntry* )
+{
+}
+
+bool SvListView::IsExpanded( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"IsExpanded:No Entry");
+ SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(pEntry);
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in Table");
+ if (itr == m_pImpl->m_DataTable.end())
+ return false;
+ return itr->second->IsExpanded();
+}
+
+bool SvListView::IsAllExpanded( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"IsAllExpanded:No Entry");
+ if (!IsExpanded(pEntry))
+ return false;
+ const SvTreeListEntries& rChildren = pEntry->GetChildEntries();
+ for (auto& rChild : rChildren)
+ {
+ if (rChild->HasChildren() || rChild->HasChildrenOnDemand())
+ {
+ if (!IsAllExpanded(rChild.get()))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SvListView::IsSelected(const SvTreeListEntry* pEntry) const
+{
+ DBG_ASSERT(pEntry,"IsExpanded:No Entry");
+ SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry));
+ if (itr == m_pImpl->m_DataTable.end())
+ return false;
+ return itr->second->IsSelected();
+}
+
+void SvListView::SetEntryFocus( SvTreeListEntry* pEntry, bool bFocus )
+{
+ DBG_ASSERT(pEntry,"SetEntryFocus:No Entry");
+ SvDataTable::iterator itr = m_pImpl->m_DataTable.find(pEntry);
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in Table");
+ itr->second->SetFocus(bFocus);
+}
+
+const SvViewDataEntry* SvListView::GetViewData( const SvTreeListEntry* pEntry ) const
+{
+ SvDataTable::const_iterator itr =
+ m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry));
+ if (itr == m_pImpl->m_DataTable.end())
+ return nullptr;
+ return itr->second.get();
+}
+
+SvViewDataEntry* SvListView::GetViewData( SvTreeListEntry* pEntry )
+{
+ SvDataTable::iterator itr = m_pImpl->m_DataTable.find( pEntry );
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in model or wrong view");
+ return itr->second.get();
+}
+
+sal_Int32 SvTreeList::Compare(const SvTreeListEntry* pLeft, const SvTreeListEntry* pRight) const
+{
+ if( aCompareLink.IsSet())
+ {
+ SvSortData aSortData;
+ aSortData.pLeft = pLeft;
+ aSortData.pRight = pRight;
+ return aCompareLink.Call( aSortData );
+ }
+ return 0;
+}
+
+void SvTreeList::Resort()
+{
+ Broadcast( SvListAction::RESORTING );
+ bAbsPositionsValid = false;
+ ResortChildren( pRootItem.get() );
+ Broadcast( SvListAction::RESORTED );
+}
+
+namespace {
+
+class SortComparator
+{
+ SvTreeList& mrList;
+public:
+
+ explicit SortComparator( SvTreeList& rList ) : mrList(rList) {}
+
+ bool operator() (std::unique_ptr<SvTreeListEntry> const& rpLeft,
+ std::unique_ptr<SvTreeListEntry> const& rpRight) const
+ {
+ int nCompare = mrList.Compare(rpLeft.get(), rpRight.get());
+ if (nCompare != 0 && mrList.GetSortMode() == SvSortMode::Descending)
+ {
+ if( nCompare < 0 )
+ nCompare = 1;
+ else
+ nCompare = -1;
+ }
+ return nCompare < 0;
+ }
+};
+
+}
+
+void SvTreeList::ResortChildren( SvTreeListEntry* pParent )
+{
+ DBG_ASSERT(pParent,"Parent not set");
+
+ if (pParent->m_Children.empty())
+ return;
+
+ SortComparator aComp(*this);
+ std::sort(pParent->m_Children.begin(), pParent->m_Children.end(), aComp);
+
+ // Recursively sort child entries.
+ for (auto const& it : pParent->m_Children)
+ {
+ SvTreeListEntry& r = *it;
+ ResortChildren(&r);
+ }
+
+ SetListPositions(pParent->m_Children); // correct list position in target list
+}
+
+void SvTreeList::GetInsertionPos( SvTreeListEntry const * pEntry, SvTreeListEntry* pParent,
+ sal_uInt32& rPos )
+{
+ DBG_ASSERT(pEntry,"No Entry");
+
+ if( eSortMode == SvSortMode::None )
+ return;
+
+ rPos = TREELIST_ENTRY_NOTFOUND;
+ const SvTreeListEntries& rChildList = GetChildList(pParent);
+
+ if (rChildList.empty())
+ return;
+
+ tools::Long i = 0;
+ tools::Long j = rChildList.size()-1;
+ tools::Long k;
+ sal_Int32 nCompare = 1;
+
+ do
+ {
+ k = (i+j)/2;
+ const SvTreeListEntry* pTempEntry = rChildList[k].get();
+ nCompare = Compare( pEntry, pTempEntry );
+ if (nCompare != 0 && eSortMode == SvSortMode::Descending)
+ {
+ if( nCompare < 0 )
+ nCompare = 1;
+ else
+ nCompare = -1;
+ }
+ if( nCompare > 0 )
+ i = k + 1;
+ else
+ j = k - 1;
+ } while( (nCompare != 0) && (i <= j) );
+
+ if( nCompare != 0 )
+ {
+ if (i > static_cast<tools::Long>(rChildList.size()-1)) // not found, end of list
+ rPos = TREELIST_ENTRY_NOTFOUND;
+ else
+ rPos = i; // not found, middle of list
+ }
+ else
+ rPos = k;
+}
+
+SvTreeListEntry* SvTreeList::GetEntry( SvTreeListEntry* pParent, sal_uInt32 nPos ) const
+{ if ( !pParent )
+ pParent = pRootItem.get();
+ SvTreeListEntry* pRet = nullptr;
+ if (nPos < pParent->m_Children.size())
+ pRet = pParent->m_Children[nPos].get();
+ return pRet;
+}
+
+SvTreeListEntry* SvTreeList::GetEntry( sal_uInt32 nRootPos ) const
+{
+ SvTreeListEntry* pRet = nullptr;
+ if (nEntryCount && nRootPos < pRootItem->m_Children.size())
+ pRet = pRootItem->m_Children[nRootPos].get();
+ return pRet;
+}
+
+const SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ return pParent->m_Children;
+}
+
+SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent )
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ return pParent->m_Children;
+}
+
+const SvTreeListEntry* SvTreeList::GetParent( const SvTreeListEntry* pEntry ) const
+{
+ if (!pEntry)
+ return nullptr;
+ const SvTreeListEntry* pParent = pEntry->pParent;
+ if (pParent == pRootItem.get())
+ pParent = nullptr;
+ return pParent;
+}
+
+SvTreeListEntry* SvTreeList::GetParent( SvTreeListEntry* pEntry )
+{
+ if (!pEntry)
+ return nullptr;
+ SvTreeListEntry* pParent = pEntry->pParent;
+ if (pParent == pRootItem.get())
+ pParent = nullptr;
+ return pParent;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */