Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/treelist/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 42 kB image not shown  

Quelle  treelist.cxx   Sprache: C

 
/* -*- 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 <cassert>
#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);
};


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
{
    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
{
    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* pChildconst
{
    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!
    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 );

    assert(nCount > 0 && "given do...while");
    return nCount - 1;
}

sal_uInt32 SvTreeList::GetVisibleChildCount(const SvListView* pView, SvTreeListEntry* pParent) const
{
    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 );

    assert(nCount > 0 && "given do...while");
    return nCount - 1;
}

sal_uInt32 SvTreeList::GetChildSelectionCount(const SvListView* pView,SvTreeListEntry* pParent) const
{
    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 );

    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
{
    assert(pActEntry && "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
{
    assert(pView && "View?");
    DBG_ASSERT(pEntry,"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 );
    if (!pViewData)
        return 0;
    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 )
    {
        if (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
{
    if ( !pActEntry )
        return nullptr;

    assert(pView && "NextVisible:No View");

    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
{
    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
{
    assert(pView && pEntry && "NextVis:Wrong Prms!");
    DBG_ASSERT(IsEntryVisible(pView,pEntry), "NextVis:Wrong 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
{
    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 )
{
    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 )
{
    assert(pEntry && "Expand:View/Entry?");
    SvViewDataEntry* pViewData = GetViewData(pEntry);
    if (!pViewData)
        return;

    if (pViewData->IsExpanded())
        return;

    DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Expand: We expected to have child entries.");

    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 )
{
    assert(pEntry && "Collapse:View/Entry?");
    SvViewDataEntry* pViewData = GetViewData( pEntry );
    if (!pViewData)
        return;

    if (!pViewData->IsExpanded())
        return;

    DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Collapse: We expected to have child entries.");

    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 (!pViewData)
        return false;

    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 )
{
    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 );
}

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;
    assert(pParent && "Model not consistent");
    if (pParent != m_rThis.pModel->pRootItem.get() && pParent->m_Children.size() == 1)
    {
        const auto iter = m_DataTable.find(pParent);
        assert(iter != m_DataTable.end());
        SvViewDataEntry* pViewData = iter->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 )
{
    assert(pEntry && "Remove:No Entry");
    const auto iter = m_DataTable.find(pEntry);
    assert(iter != m_DataTable.end());
    SvViewDataEntry* pViewData = iter->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)
    {
        SvDataTable::iterator itr = m_DataTable.find(pCurEntry);
        assert(itr != m_DataTable.end() && "Entry not in Table");
        pViewData = itr->second.get();
        pViewData->SetExpanded(false);
    }
}

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:
            Clear();
            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);
    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));
    assert(itr != m_pImpl->m_DataTable.end() && "Entry not in model or wrong view");
    if (itr == m_pImpl->m_DataTable.end())
        return nullptr;
    return itr->second.get();
}

SvViewDataEntry* SvListView::GetViewData( SvTreeListEntry* pEntry )
{
    return const_cast<SvViewDataEntry*>(const_cast<const SvListView*>(this)->GetViewData(pEntry));
}

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 )
{
    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
{
    const SvTreeListEntry* pParent = pEntry->pParent;
    if (pParent == pRootItem.get())
        pParent = nullptr;
    return pParent;
}

SvTreeListEntry* SvTreeList::GetParent( SvTreeListEntry* pEntry )
{
    SvTreeListEntry* pParent = pEntry->pParent;
    if (pParent == pRootItem.get())
        pParent = nullptr;
    return pParent;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.