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 105 kB image not shown  

Quelle  treelistbox.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 .
 */


/*
    TODO:
        - delete anchor in SelectionEngine when selecting manually
        - SelectAll( false ) => only repaint the deselected entries
*/


#include <accessibility/accessiblelistbox.hxx>

#include <vcl/toolkit/treelistbox.hxx>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <vcl/help.hxx>
#include <vcl/svapp.hxx>
#include <vcl/builder.hxx>
#include <vcl/toolkit/edit.hxx>
#include <vcl/settings.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/decoview.hxx>
#include <vcl/uitest/uiobject.hxx>
#include <sot/formats.hxx>
#include <comphelper/string.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>

#include <vcl/toolkit/svlbitm.hxx>
#include <vcl/toolkit/treelistentry.hxx>
#include <vcl/toolkit/viewdataentry.hxx>
#include <accel.hxx>
#include <svimpbox.hxx>

#include <set>
#include <string.h>
#include <vector>

using namespace css::accessibility;

// Drag&Drop
static VclPtr<SvTreeListBox> g_pDDSource;
static VclPtr<SvTreeListBox> g_pDDTarget;

#define SVLBOX_ACC_RETURN 1
#define SVLBOX_ACC_ESCAPE 2

class SvInplaceEdit2
{
    Link<SvInplaceEdit2&,void> aCallBackHdl;
    Accelerator   aAccReturn;
    Accelerator   aAccEscape;
    Idle          aIdle { "svtools::SvInplaceEdit2 aIdle" };
    VclPtr<Edit>  pEdit;
    bool          bCanceled;
    bool          bAlreadyInCallBack;

    void        CallCallBackHdl_Impl();
    DECL_LINK( Timeout_Impl, Timer *, void );
    DECL_LINK( ReturnHdl_Impl, Accelerator&, void );
    DECL_LINK( EscapeHdl_Impl, Accelerator&, void );

public:
                SvInplaceEdit2( vcl::Window* pParent, const Point& rPos, const Size& rSize,
                   const OUString& rData, const Link<SvInplaceEdit2&,void>& rNotifyEditEnd,
                   const Selection& );
               ~SvInplaceEdit2();
    bool        KeyInput( const KeyEvent& rKEvt );
    void        LoseFocus();
    bool        EditingCanceled() const { return bCanceled; }
    OUString    GetText() const;
    OUString const & GetSavedValue() const;
    void        StopEditing( bool bCancel );
    void        Hide();
    const VclPtr<Edit> & GetEditWidget() const { return pEdit; };

    void RemoveEscapeAccel() { Application::RemoveAccel(&aAccEscape); }
    void InsertEscapeAccel() { Application::InsertAccel(&aAccEscape); }
};
// ***************************************************************

namespace {

class MyEdit_Impl : public Edit
{
    SvInplaceEdit2* pOwner;
public:
                 MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* pOwner );
    virtual     ~MyEdit_Impl() override { disposeOnce(); }
    virtual void dispose() override { pOwner = nullptr; Edit::dispose(); }
    virtual void KeyInput( const KeyEvent& rKEvt ) override;
    virtual void LoseFocus() override;
    virtual void Command(const CommandEvent& rCEvt) override;
};

}

MyEdit_Impl::MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* _pOwner ) :

    Edit( pParent, WB_LEFT ),

    pOwner( _pOwner )

{
}

void MyEdit_Impl::KeyInput( const KeyEvent& rKEvt )
{
    if( !pOwner->KeyInput( rKEvt ))
        Edit::KeyInput( rKEvt );
}

void MyEdit_Impl::LoseFocus()
{
    if (pOwner)
        pOwner->LoseFocus();
}

void MyEdit_Impl::Command(const CommandEvent& rCEvt)
{
    if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
    {
        pOwner->RemoveEscapeAccel();  // so escape ends the popup
        Edit::Command(rCEvt);
        pOwner->InsertEscapeAccel();
    }
    else
        Edit::Command(rCEvt);
}

SvInplaceEdit2::SvInplaceEdit2
(
    vcl::Window* pParent, const Point& rPos,
    const Size& rSize,
    const OUString& rData,
    const Link<SvInplaceEdit2&,void>& rNotifyEditEnd,
    const Selection& rSelection
) :

    aCallBackHdl       ( rNotifyEditEnd ),
    bCanceled           ( false ),
    bAlreadyInCallBack  ( false )

{

    pEdit = VclPtr<MyEdit_Impl>::Create( pParent, this );

    vcl::Font aFont( pParent->GetFont() );
    aFont.SetTransparent( false );
    Color aColor( pParent->GetBackground().GetColor() );
    aFont.SetFillColor(aColor );
    pEdit->SetFont( aFont );
    pEdit->SetBackground( pParent->GetBackground() );
    pEdit->SetPosPixel( rPos );
    pEdit->SetSizePixel( rSize );
    pEdit->SetText( rData );
    pEdit->SetSelection( rSelection );
    pEdit->SaveValue();

    aAccReturn.InsertItem( SVLBOX_ACC_RETURN, vcl::KeyCode(KEY_RETURN) );
    aAccEscape.InsertItem( SVLBOX_ACC_ESCAPE, vcl::KeyCode(KEY_ESCAPE) );

    aAccReturn.SetActivateHdl( LINK( this, SvInplaceEdit2, ReturnHdl_Impl) );
    aAccEscape.SetActivateHdl( LINK( this, SvInplaceEdit2, EscapeHdl_Impl) );
    Application::InsertAccel( &aAccReturn );
    Application::InsertAccel( &aAccEscape );

    pEdit->Show();
    pEdit->GrabFocus();
}

SvInplaceEdit2::~SvInplaceEdit2()
{
    if( !bAlreadyInCallBack )
    {
        Application::RemoveAccel( &aAccReturn );
        Application::RemoveAccel( &aAccEscape );
    }
    pEdit.disposeAndClear();
}

OUString const & SvInplaceEdit2::GetSavedValue() const
{
    return pEdit->GetSavedValue();
}

void SvInplaceEdit2::Hide()
{
    pEdit->Hide();
}


IMPL_LINK_NOARG(SvInplaceEdit2, ReturnHdl_Impl, Accelerator&, void)
{
    bCanceled = false;
    CallCallBackHdl_Impl();
}

IMPL_LINK_NOARG(SvInplaceEdit2, EscapeHdl_Impl, Accelerator&, void)
{
    bCanceled = true;
    CallCallBackHdl_Impl();
}

bool SvInplaceEdit2::KeyInput( const KeyEvent& rKEvt )
{
    vcl::KeyCode aCode = rKEvt.GetKeyCode();
    sal_uInt16 nCode = aCode.GetCode();

    switch ( nCode )
    {
        case KEY_ESCAPE:
            bCanceled = true;
            CallCallBackHdl_Impl();
            return true;

        case KEY_RETURN:
            bCanceled = false;
            CallCallBackHdl_Impl();
            return true;
    }
    return false;
}

void SvInplaceEdit2::StopEditing( bool bCancel )
{
    if ( !bAlreadyInCallBack )
    {
        bCanceled = bCancel;
        CallCallBackHdl_Impl();
    }
}

void SvInplaceEdit2::LoseFocus()
{
    if (!bAlreadyInCallBack && !pEdit->IsActivePopup()
        && ((!Application::GetFocusWindow()) || !pEdit->IsChild(Application::GetFocusWindow())))
    {
        bCanceled = false;
        aIdle.SetPriority(TaskPriority::REPAINT);
        aIdle.SetInvokeHandler(LINK(this,SvInplaceEdit2,Timeout_Impl));
        aIdle.Start();
    }
}

IMPL_LINK_NOARG(SvInplaceEdit2, Timeout_Impl, Timer *, void)
{
    CallCallBackHdl_Impl();
}

void SvInplaceEdit2::CallCallBackHdl_Impl()
{
    aIdle.Stop();
    if ( !bAlreadyInCallBack )
    {
        bAlreadyInCallBack = true;
        Application::RemoveAccel( &aAccReturn );
        Application::RemoveAccel( &aAccEscape );
        pEdit->Hide();
        aCallBackHdl.Call( *this );
    }
}

OUString SvInplaceEdit2::GetText() const
{
    return pEdit->GetText();
}

// ***************************************************************
// class SvLBoxTab
// ***************************************************************


SvLBoxTab::SvLBoxTab()
{
    nPos = 0;
    nFlags = SvLBoxTabFlags::NONE;
}

SvLBoxTab::SvLBoxTab( tools::Long nPosition, SvLBoxTabFlags nTabFlags )
{
    nPos = nPosition;
    nFlags = nTabFlags;
}

SvLBoxTab::SvLBoxTab( const SvLBoxTab& rTab )
{
    nPos = rTab.nPos;
    nFlags = rTab.nFlags;
}

tools::Long SvLBoxTab::CalcOffset( tools::Long nItemWidth, tools::Long nTabWidth )
{
    tools::Long nOffset = 0;
    if ( nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
    {
        nOffset = nTabWidth - nItemWidth;
        if( nOffset < 0 )
            nOffset = 0;
    }
    else if ( nFlags & SvLBoxTabFlags::ADJUST_CENTER )
    {
        if( nFlags & SvLBoxTabFlags::FORCE )
        {
            // correct implementation of centering
            nOffset = ( nTabWidth - nItemWidth ) / 2;
            if( nOffset < 0 )
                nOffset = 0;
        }
        else
        {
            // historically grown, wrong calculation of tabs which is needed by
            // Abo-Tabbox, Tools/Options/Customize etc.
            nItemWidth++;
            nOffset = -( nItemWidth / 2 );
        }
    }
    return nOffset;
}

// ***************************************************************
// class SvLBoxItem
// ***************************************************************


SvLBoxItem::SvLBoxItem()
    : mbDisabled(false)
{
}

SvLBoxItem::~SvLBoxItem()
{
}

int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const
{
    const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this );
    int nWidth = pViewData->mnWidth;
    if (nWidth == -1)
    {
        nWidth = CalcWidth(pView);
        const_cast<SvViewDataItem*>(pViewData)->mnWidth = nWidth;
    }
    return nWidth;
}

int SvLBoxItem::GetHeight(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const
{
    const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this );
    return pViewData->mnHeight;
}

int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvViewDataEntry* pData, sal_uInt16 nItemPos) const
{
    const SvViewDataItem& rIData = pData->GetItem(nItemPos);
    int nWidth = rIData.mnWidth;
    if (nWidth == -1)
    {
        nWidth = CalcWidth(pView);
        const_cast<SvViewDataItem&>(rIData).mnWidth = nWidth;
    }
    return nWidth;
}

int SvLBoxItem::GetHeight(const SvViewDataEntry* pData, sal_uInt16 nItemPos)
{
    const SvViewDataItem& rIData = pData->GetItem(nItemPos);
    return rIData.mnHeight;
}

int SvLBoxItem::CalcWidth(const SvTreeListBox* /*pView*/) const
{
    return 0;
}

struct SvTreeListBoxImpl
{
    bool m_bDoingQuickSelection:1;

    vcl::QuickSelectionEngine m_aQuickSelectionEngine;

    explicit SvTreeListBoxImpl(SvTreeListBox& _rBox) :
        m_bDoingQuickSelection(false),
        m_aQuickSelectionEngine(_rBox) {}
};

SvTreeListBox::SvTreeListBox(vcl::Window* pParent, WinBits nWinStyle) :
    Control(pParent, nWinStyle | WB_CLIPCHILDREN),
    DropTargetHelper(this),
    DragSourceHelper(this),
    mpImpl(new SvTreeListBoxImpl(*this)),
    mbContextBmpExpanded(false),
    mbQuickSearch(false),
    mbActivateOnSingleClick(false),
    mbHoverSelection(false),
    mbSelectingByHover(false),
    mbIsTextColumEnabled(false),
    mnClicksToToggle(0), //at default clicking on a row won't toggle its default checkbox
    eSelMode(SelectionMode::NONE),
    nMinWidthInChars(0),
    mnDragAction(DND_ACTION_COPYMOVE | DND_ACTION_LINK),
    mbCenterAndClipText(false)
{
    nImpFlags = SvTreeListBoxFlags::NONE;
    pTargetEntry = nullptr;
    nDragDropMode = DragDropMode::NONE;
    pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
    pHdlEntry = nullptr;
    eSelMode = SelectionMode::Single;
    nDragDropMode = DragDropMode::NONE;
    SetType(WindowType::TREELISTBOX);

    InitTreeView();
    pImpl->SetModel( pModel.get() );

    SetSublistOpenWithLeftRight();
}

void SvTreeListBox::Clear()
{
    if (pModel)
        pModel->Clear();  // Model calls SvTreeListBox::ModelHasCleared()
}

IMPL_LINK( SvTreeListBox, CloneHdl_Impl, SvTreeListEntry*, pEntry, SvTreeListEntry* )
{
    return CloneEntry(pEntry);
}

sal_uInt32 SvTreeListBox::Insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uInt32 nPos )
{
    sal_uInt32 nInsPos = pModel->Insert( pEntry, pParent, nPos );
    return nInsPos;
}

sal_uInt32 SvTreeListBox::Insert( SvTreeListEntry* pEntry,sal_uInt32 nRootPos )
{
    sal_uInt32 nInsPos = pModel->Insert( pEntry, nRootPos );
    return nInsPos;
}

bool SvTreeListBox::ExpandingHdl()
{
    return !aExpandingHdl.IsSet() || aExpandingHdl.Call( this );
}

void SvTreeListBox::ExpandedHdl()
{
    aExpandedHdl.Call( this );
}

void SvTreeListBox::SelectHdl()
{
    aSelectHdl.Call( this );
}

void SvTreeListBox::DeselectHdl()
{
    aDeselectHdl.Call( this );
}

bool SvTreeListBox::DoubleClickHdl()
{
    return !aDoubleClickHdl.IsSet() || aDoubleClickHdl.Call(this);
}

bool SvTreeListBox::CheckDragAndDropMode( SvTreeListBox const * pSource, sal_Int8 nAction )
{
    if ( pSource != this )
        return false// no drop

    if ( !(nDragDropMode & DragDropMode::CTRL_MOVE) )
        return false// D&D locked within list

    if( DND_ACTION_MOVE == nAction )
    {
        if ( !(nDragDropMode & DragDropMode::CTRL_MOVE) )
             return false// no local move
    }
    else
        return false// no local copy

    return true;
}


/*
    NotifyMoving/Copying
    ====================

    default behavior:

    1. target doesn't have children
        - entry becomes sibling of target. entry comes after target
          (->Window: below the target)
    2. target is an expanded parent
        - entry inserted at the beginning of the target childlist
    3. target is a collapsed parent
        - entry is inserted at the end of the target childlist
*/

TriState SvTreeListBox::NotifyMoving(
    SvTreeListEntry*  pTarget,       // D&D dropping position in GetModel()
    const SvTreeListEntry*  pEntry,        // entry that we want to move, from
                                 // GetSourceListBox()->GetModel()
    SvTreeListEntry*& rpNewParent,   // new target parent
    sal_uInt32&        rNewChildPos)  // position in childlist of target parent
{
    DBG_ASSERT(pEntry,"NotifyMoving:SourceEntry?");
    if( !pTarget )
    {
        rpNewParent = nullptr;
        rNewChildPos = 0;
        return TRISTATE_TRUE;
    }
    if ( !pTarget->HasChildren() && !pTarget->HasChildrenOnDemand() )
    {
        // case 1
        rpNewParent = GetParent( pTarget );
        rNewChildPos = SvTreeList::GetRelPos( pTarget ) + 1;
        rNewChildPos += nCurEntrySelPos;
        nCurEntrySelPos++;
    }
    else
    {
        // cases 2 & 3
        rpNewParent = pTarget;
        if( IsExpanded(pTarget))
            rNewChildPos = 0;
        else
            rNewChildPos = TREELIST_APPEND;
    }
    return TRISTATE_TRUE;
}

TriState SvTreeListBox::NotifyCopying(
    SvTreeListEntry*  pTarget,       // D&D dropping position in GetModel()
    const SvTreeListEntry*  pEntry,        // entry that we want to move, from
                                 // GetSourceListBox()->GetModel()
    SvTreeListEntry*& rpNewParent,   // new target parent
    sal_uInt32&        rNewChildPos)  // position in childlist of target parent
{
    return NotifyMoving(pTarget,pEntry,rpNewParent,rNewChildPos);
}

SvTreeListEntry* SvTreeListBox::FirstChild( SvTreeListEntry* pParent ) const
{
    return pModel->FirstChild(pParent);
}

// return: all entries copied
bool SvTreeListBox::CopySelection( SvTreeListBox* pSource, SvTreeListEntry* pTarget )
{
    nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
    bool bSuccess = true;
    std::vector<SvTreeListEntry*> aList;
    bool bClone = ( pSource->GetModel() != GetModel() );
    Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
    pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));

    // cache selection to simplify iterating over the selection when doing a D&D
    // exchange within the same listbox
    SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
    while ( pSourceEntry )
    {
        // children are copied automatically
        pSource->SelectChildren( pSourceEntry, false );
        aList.push_back( pSourceEntry );
        pSourceEntry = pSource->NextSelected( pSourceEntry );
    }

    for (auto const& elem : aList)
    {
        pSourceEntry = elem;
        SvTreeListEntry* pNewParent = nullptr;
        sal_uInt32 nInsertionPos = TREELIST_APPEND;
        TriState nOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
        if ( nOk )
        {
            if ( bClone )
            {
                sal_uInt32 nCloneCount = 0;
                pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
                pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
            }
            else
            {
                sal_uInt32 nListPos = pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
                pSourceEntry = GetEntry( pNewParent, nListPos );
            }
        }
        else
            bSuccess = false;

        if (nOk == TRISTATE_INDET)  // HACK: make visible moved entry
            MakeVisible( pSourceEntry );
    }
    pModel->SetCloneLink( aCloneLink );
    return bSuccess;
}

// return: all entries were moved
bool SvTreeListBox::MoveSelectionCopyFallbackPossible( SvTreeListBox* pSource, SvTreeListEntry* pTarget, bool bAllowCopyFallback )
{
    nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
    bool bSuccess = true;
    std::vector<SvTreeListEntry*> aList;
    bool bClone = ( pSource->GetModel() != GetModel() );
    Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
    if ( bClone )
        pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));

    SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
    while ( pSourceEntry )
    {
        // children are automatically moved
        pSource->SelectChildren( pSourceEntry, false );
        aList.push_back( pSourceEntry );
        pSourceEntry = pSource->NextSelected( pSourceEntry );
    }

    for (auto const& elem : aList)
    {
        pSourceEntry = elem;
        SvTreeListEntry* pNewParent = nullptr;
        sal_uInt32 nInsertionPos = TREELIST_APPEND;
        TriState nOk = NotifyMoving(pTarget,pSourceEntry,pNewParent,nInsertionPos);
        TriState nCopyOk = nOk;
        if ( !nOk && bAllowCopyFallback )
        {
            nInsertionPos = TREELIST_APPEND;
            nCopyOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
        }

        if ( nOk || nCopyOk )
        {
            if ( bClone )
            {
                sal_uInt32 nCloneCount = 0;
                pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
                pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
            }
            else
            {
                if ( nOk )
                    pModel->Move(pSourceEntry, pNewParent, nInsertionPos);
                else
                    pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
            }
        }
        else
            bSuccess = false;

        if (nOk == TRISTATE_INDET)  // HACK: make moved entry visible
            MakeVisible( pSourceEntry );
    }
    pModel->SetCloneLink( aCloneLink );
    return bSuccess;
}

void SvTreeListBox::RemoveSelection()
{
    std::vector<const SvTreeListEntry*> aList;
    // cache selection, as the implementation deselects everything on the first
    // remove
    SvTreeListEntry* pEntry = FirstSelected();
    while ( pEntry )
    {
        aList.push_back( pEntry );
        if ( pEntry->HasChildren() )
            // remove deletes all children automatically
            SelectChildren(pEntry, false);
        pEntry = NextSelected( pEntry );
    }

    for (auto const& elem : aList)
        pModel->Remove(elem);
}

void SvTreeListBox::RemoveEntry(SvTreeListEntry const * pEntry)
{
    pModel->Remove(pEntry);
}

void SvTreeListBox::RecalcViewData()
{
    SvTreeListEntry* pEntry = First();
    while( pEntry )
    {
        sal_uInt16 nCount = pEntry->ItemCount();
        sal_uInt16 nCurPos = 0;
        while ( nCurPos < nCount )
        {
            SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
            rItem.InitViewData( this, pEntry );
            nCurPos++;
        }
        pEntry = Next( pEntry );
    }
}

void SvTreeListBox::ImplShowTargetEmphasis( SvTreeListEntry* pEntry, bool bShow)
{
    if ( bShow && (nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
        return;
    if ( !bShow && !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
        return;
    pImpl->PaintDDCursor( pEntry, bShow);
    if( bShow )
        nImpFlags |= SvTreeListBoxFlags::TARGEMPH_VIS;
    else
        nImpFlags &= ~SvTreeListBoxFlags::TARGEMPH_VIS;
}

void SvTreeListBox::OnCurrentEntryChanged()
{
    if ( !mpImpl->m_bDoingQuickSelection )
        mpImpl->m_aQuickSelectionEngine.Reset();
}

SvTreeListEntry* SvTreeListBox::GetEntry( SvTreeListEntry* pParent, sal_uInt32 nPos ) const
{
    return pModel->GetEntry(pParent, nPos);
}

SvTreeListEntry* SvTreeListBox::GetEntry( sal_uInt32 nRootPos ) const
{
    return pModel->GetEntry(nRootPos);
}

SvTreeListEntry* SvTreeListBox::GetEntryFromPath( const ::std::deque< sal_Int32 >& _rPath ) const
{

    SvTreeListEntry* pEntry = nullptr;
    SvTreeListEntry* pParent = nullptr;
    for (auto const& elem : _rPath)
    {
        pEntry = GetEntry( pParent, elem );
        if ( !pEntry )
            break;
        pParent = pEntry;
    }

    return pEntry;
}

void SvTreeListBox::FillEntryPath( SvTreeListEntry* pEntry, ::std::deque< sal_Int32 >&&nbsp;_rPath ) const
{

    if ( !pEntry )
        return;

    SvTreeListEntry* pParentEntry = GetParent( pEntry );
    while ( true )
    {
        sal_uInt32 i, nCount = GetLevelChildCount( pParentEntry );
        for ( i = 0; i < nCount; ++i )
        {
            SvTreeListEntry* pTemp = GetEntry( pParentEntry, i );
            DBG_ASSERT( pEntry, "invalid entry" );
            if ( pEntry == pTemp )
            {
                _rPath.push_front( static_cast<sal_Int32>(i) );
                break;
            }
        }

        if ( pParentEntry )
        {
            pEntry = pParentEntry;
            pParentEntry = GetParent( pParentEntry );
        }
        else
            break;
    }
}

SvTreeListEntry* SvTreeListBox::GetParent( SvTreeListEntry* pEntry ) const
{
    return pModel->GetParent(pEntry);
}

sal_uInt32 SvTreeListBox::GetChildCount( SvTreeListEntry const * pParent ) const
{
    return pModel->GetChildCount(pParent);
}

sal_uInt32 SvTreeListBox::GetLevelChildCount( SvTreeListEntry* _pParent ) const
{

    //if _pParent is 0, then pEntry is the first child of the root.
    SvTreeListEntry* pEntry = FirstChild( _pParent );

    if( !pEntry )//there is only root, root don't have children
        return 0;

    if( !_pParent )//root and children of root
        return pEntry->pParent->m_Children.size();

    return _pParent->m_Children.size();
}

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

SvViewDataItem* SvTreeListBox::GetViewDataItem(SvTreeListEntry const * pEntry, SvLBoxItem const * pItem)
{
    return const_cast<SvViewDataItem*>(static_cast<const SvTreeListBox*>(this)->GetViewDataItem(pEntry, pItem));
}

const SvViewDataItem* SvTreeListBox::GetViewDataItem(const SvTreeListEntry* pEntry, const SvLBoxItem* pItem) const
{
    const SvViewDataEntry* pEntryData = SvListView::GetViewData(pEntry);
    assert(pEntryData && "Entry not in View");
    sal_uInt16 nItemPos = pEntry->GetPos(pItem);
    return &pEntryData->GetItem(nItemPos);
}

void SvTreeListBox::InitViewData( SvViewDataEntry* pData, SvTreeListEntry* pEntry )
{
    SvTreeListEntry* pInhEntry = pEntry;
    SvViewDataEntry* pEntryData = pData;

    pEntryData->Init(pInhEntry->ItemCount());
    sal_uInt16 nCount = pInhEntry->ItemCount();
    sal_uInt16 nCurPos = 0;
    while( nCurPos < nCount )
    {
        SvLBoxItem& rItem = pInhEntry->GetItem( nCurPos );
        SvViewDataItem& rItemData = pEntryData->GetItem(nCurPos);
        rItem.InitViewData( this, pInhEntry, &rItemData );
        nCurPos++;
    }
}

void SvTreeListBox::EnableSelectionAsDropTarget( bool bEnable )
{
    sal_uInt16 nRefDepth;
    SvTreeListEntry* pTemp;

    SvTreeListEntry* pSelEntry = FirstSelected();
    while( pSelEntry )
    {
        if ( !bEnable )
        {
            pSelEntry->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
            nRefDepth = pModel->GetDepth( pSelEntry );
            pTemp = Next( pSelEntry );
            while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
            {
                pTemp->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
                pTemp = Next( pTemp );
            }
        }
        else
        {
            pSelEntry->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP;
            nRefDepth = pModel->GetDepth( pSelEntry );
            pTemp = Next( pSelEntry );
            while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
            {
                pTemp->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP;
                pTemp = Next( pTemp );
            }
        }
        pSelEntry = NextSelected( pSelEntry );
    }
}

// ******************************************************************
// InplaceEditing
// ******************************************************************

VclPtr<Edit> SvTreeListBox::GetEditWidget() const
{
    return pEdCtrl ? pEdCtrl->GetEditWidget() : nullptr;
}

void SvTreeListBox::EditText( const OUString& rStr, const tools::Rectangle& rRect,
    const Selection& rSel )
{
    pEdCtrl.reset();
    nImpFlags |= SvTreeListBoxFlags::IN_EDT;
    nImpFlags &= ~SvTreeListBoxFlags::EDTEND_CALLED;
    HideFocus();
    pEdCtrl.reset( new SvInplaceEdit2(
        this, rRect.TopLeft(), rRect.GetSize(), rStr,
        LINK( this, SvTreeListBox, TextEditEndedHdl_Impl ),
        rSel ) );
}

IMPL_LINK_NOARG(SvTreeListBox, TextEditEndedHdl_Impl, SvInplaceEdit2&, void)
{
    if ( nImpFlags & SvTreeListBoxFlags::EDTEND_CALLED ) // avoid nesting
        return;
    nImpFlags |= SvTreeListBoxFlags::EDTEND_CALLED;
    OUString aStr;
    if ( !pEdCtrl->EditingCanceled() )
        aStr = pEdCtrl->GetText();
    else
        aStr = pEdCtrl->GetSavedValue();
    EditedText( aStr );
    // Hide may only be called after the new text was put into the entry, so
    // that we don't call the selection handler in the GetFocus of the listbox
    // with the old entry text.
    pEdCtrl->Hide();
    nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
    GrabFocus();
}

void SvTreeListBox::CancelTextEditing()
{
    if ( pEdCtrl )
        pEdCtrl->StopEditing( true );
    nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
}

void SvTreeListBox::EndEditing( bool bCancel )
{
    if( pEdCtrl )
        pEdCtrl->StopEditing( bCancel );
    nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
}

vcl::StringEntryIdentifier SvTreeListBox::CurrentEntry( OUString& _out_entryText ) const
{
    // always accept the current entry if there is one
    SvTreeListEntry* pEntry( GetCurEntry() );
    if (pEntry)
    {
        _out_entryText = GetEntryText(pEntry);
        return pEntry;
    }

    pEntry = FirstSelected();
    if ( !pEntry )
        pEntry = First();

    if ( pEntry )
        _out_entryText = GetEntryText( pEntry );

    return pEntry;
}

vcl::StringEntryIdentifier SvTreeListBox::NextEntry(vcl::StringEntryIdentifier _pCurrentSearchEntry, OUString& _out_entryText) const
{
    SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_castconst SvTreeListEntry* >( _pCurrentSearchEntry ) );

    if  (   (   ( GetChildCount( pEntry ) > 0 )
            ||  ( pEntry->HasChildrenOnDemand() )
            )
        &&  !IsExpanded( pEntry )
        )
    {
        SvTreeListEntry* pNextSiblingEntry = pEntry->NextSibling();
        if ( !pNextSiblingEntry )
            pEntry = Next( pEntry );
        else
            pEntry = pNextSiblingEntry;
    }
    else
    {
        pEntry = Next( pEntry );
    }

    if ( !pEntry )
        pEntry = First();

    if ( pEntry )
        _out_entryText = GetEntryText( pEntry );

    return pEntry;
}

void SvTreeListBox::SelectEntry(vcl::StringEntryIdentifier _pEntry)
{
    SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_castconst SvTreeListEntry* >( _pEntry ) );
    DBG_ASSERT( pEntry, "SvTreeListBox::SelectSearchEntry: invalid entry!" );
    if ( !pEntry )
        return;

    SelectAll( false );
    SetCurEntry( pEntry );
    Select( pEntry );
}

bool SvTreeListBox::HandleKeyInput( const KeyEvent& _rKEvt )
{
    if ( _rKEvt.GetKeyCode().IsMod1() )
        return false;

    if (mbQuickSearch)
    {
        mpImpl->m_bDoingQuickSelection = true;
        const bool bHandled = mpImpl->m_aQuickSelectionEngine.HandleKeyEvent( _rKEvt );
        mpImpl->m_bDoingQuickSelection = false;
        if ( bHandled )
            return true;
    }

    return false;
}


//JP 28.3.2001: new Drag & Drop API
sal_Int8 SvTreeListBox::AcceptDrop( const AcceptDropEvent& rEvt )
{
    sal_Int8 nRet = DND_ACTION_NONE;

    if (rEvt.mbLeaving || !CheckDragAndDropMode(g_pDDSource, rEvt.mnAction))
    {
        ImplShowTargetEmphasis( pTargetEntry, false );
    }
    else if( nDragDropMode == DragDropMode::NONE )
    {
        SAL_WARN( "svtools.contnr""SvTreeListBox::QueryDrop(): no target" );
    }
    else
    {
        SvTreeListEntry* pEntry = GetDropTarget( rEvt.maPosPixel );
        if( !IsDropFormatSupported( SotClipboardFormatId::TREELISTBOX ) )
        {
            SAL_WARN( "svtools.contnr""SvTreeListBox::QueryDrop(): no format" );
        }
        else
        {
            DBG_ASSERT(g_pDDSource, "SvTreeListBox::QueryDrop(): SourceBox == 0");
            if (!( pEntry && g_pDDSource->GetModel() == GetModel()
                    && DND_ACTION_MOVE == rEvt.mnAction
                    && (pEntry->nEntryFlags & SvTLEntryFlags::DISABLE_DROP)))
            {
                nRet = rEvt.mnAction;
            }
        }

        // **** draw emphasis ****
        if( DND_ACTION_NONE == nRet )
               ImplShowTargetEmphasis( pTargetEntry, false );
        else if( pEntry != pTargetEntry || !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
        {
            ImplShowTargetEmphasis( pTargetEntry, false );
            pTargetEntry = pEntry;
            ImplShowTargetEmphasis( pTargetEntry, true );
        }
    }
    return nRet;
}

sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt, SvTreeListBox* pSourceView )
{
    assert(pSourceView);
    pSourceView->EnableSelectionAsDropTarget();

    ImplShowTargetEmphasis( pTargetEntry, false );
    g_pDDTarget = this;

    TransferableDataHelper aData( rEvt.maDropEvent.Transferable );

    sal_Int8 nRet;
    if( aData.HasFormat( SotClipboardFormatId::TREELISTBOX ))
        nRet = rEvt.mnAction;
    else
        nRet = DND_ACTION_NONE;

    if( DND_ACTION_NONE != nRet )
    {
        nRet = DND_ACTION_NONE;

        SvTreeListEntry* pTarget = pTargetEntry; // may be 0!

        if( DND_ACTION_COPY == rEvt.mnAction )
        {
            if (CopySelection(g_pDDSource, pTarget))
                nRet = rEvt.mnAction;
        }
        else if( DND_ACTION_MOVE == rEvt.mnAction )
        {
            if (MoveSelectionCopyFallbackPossible( g_pDDSource, pTarget, false ))
                nRet = rEvt.mnAction;
        }
        else if( DND_ACTION_COPYMOVE == rEvt.mnAction )
        {
            if (MoveSelectionCopyFallbackPossible(g_pDDSource, pTarget, true))
                nRet = rEvt.mnAction;
        }
    }
    return nRet;
}

sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt )
{
    return ExecuteDrop( rEvt, g_pDDSource );
}

/**
 * This sets the global variables used to determine the
 * in-process drag source.
 */

void SvTreeListBox::SetupDragOrigin()
{
    g_pDDSource = this;
    g_pDDTarget = nullptr;
}

void SvTreeListBox::StartDrag( sal_Int8, const Point& rPosPixel )
{
    if(!isDisposed())
    {
        // tdf#143114 do not start drag when a Button/Checkbox is in
        // drag-before-ButtonUp mode (CaptureMouse() active)
        if(pImpl->IsCaptureOnButtonActive())
            return;
    }

    nOldDragMode = GetDragDropMode();
    if ( nOldDragMode == DragDropMode::NONE )
        return;

    ReleaseMouse();

    SvTreeListEntry* pEntry = GetEntry( rPosPixel ); // GetDropTarget( rPos );
    if( !pEntry )
    {
        DragFinished( DND_ACTION_NONE );
        return;
    }

    rtl::Reference<TransferDataContainer> xContainer = m_xTransferHelper;

    if (!xContainer)
    {
        xContainer.set(new TransferDataContainer);
        // apparently some (unused) content is needed
        xContainer->CopyAnyData( SotClipboardFormatId::TREELISTBOX,
                                    "unused", SAL_N_ELEMENTS("unused") );
    }

    nDragDropMode = NotifyStartDrag();
    if( nDragDropMode == DragDropMode::NONE || 0 == GetSelectionCount() )
    {
        nDragDropMode = nOldDragMode;
        DragFinished( DND_ACTION_NONE );
        return;
    }

    SetupDragOrigin();

    bool bOldUpdateMode = Control::IsUpdateMode();
    Control::SetUpdateMode( true );
    PaintImmediately();
    Control::SetUpdateMode( bOldUpdateMode );

    // Disallow using the selection and its children as drop targets.
    // Important: If the selection of the SourceListBox is changed in the
    // DropHandler, the entries have to be allowed as drop targets again:
    // (GetSourceListBox()->EnableSelectionAsDropTarget( true, true );)
    EnableSelectionAsDropTarget( false );

    xContainer->StartDrag(this, mnDragAction, GetDragFinishedHdl());
}

void SvTreeListBox::SetDragHelper(const rtl::Reference<TransferDataContainer>& rHelper, sal_uInt8 eDNDConstants)
{
    m_xTransferHelper = rHelper;
    mnDragAction = eDNDConstants;
}

void SvTreeListBox::DragFinished( sal_Int8
#ifndef UNX
nAction
#endif
)
{
    EnableSelectionAsDropTarget();

#ifndef UNX
    if (   (nAction == DND_ACTION_MOVE)
        && (   (g_pDDTarget && (g_pDDTarget->GetModel() != GetModel()))
            || !g_pDDTarget))
    {
        RemoveSelection();
    }
#endif

    UnsetDropTarget();
    g_pDDSource = nullptr;
    g_pDDTarget = nullptr;
    nDragDropMode = nOldDragMode;
}

void SvTreeListBox::UnsetDropTarget()
{
    if (pTargetEntry)
    {
        ImplShowTargetEmphasis(pTargetEntry, false);
        pTargetEntry = nullptr;
    }
}

DragDropMode SvTreeListBox::NotifyStartDrag()
{
    return DragDropMode(0xffff);
}

// Handler and methods for Drag - finished handler.
// The with get GetDragFinishedHdl() get link can set on the
// TransferDataContainer. This link is a callback for the DragFinished
// call. AddBox method is called from the GetDragFinishedHdl() and the
// remove is called in link callback and in the destructor. So it can't
// called to a deleted object.

namespace
{
    // void* to avoid loplugin:vclwidgets, we don't need ownership here
    std::set<const void*> gSortLBoxes;
}

void SvTreeListBox::AddBoxToDDList_Impl( const SvTreeListBox& rB )
{
    gSortLBoxes.insert( &rB );
}

void SvTreeListBox::RemoveBoxFromDDList_Impl( const SvTreeListBox& rB )
{
    gSortLBoxes.erase( &rB );
}

IMPL_LINK( SvTreeListBox, DragFinishHdl_Impl, sal_Int8, nAction, void )
{
    auto &rSortLBoxes = gSortLBoxes;
    auto it = rSortLBoxes.find(this);
    if( it != rSortLBoxes.end() )
    {
        DragFinished( nAction );
        rSortLBoxes.erase( it );
    }
}

Link<sal_Int8,void> SvTreeListBox::GetDragFinishedHdl() const
{
    AddBoxToDDList_Impl( *this );
    return LINK( const_cast<SvTreeListBox*>(this), SvTreeListBox, DragFinishHdl_Impl );
}

/*
    Bugs/TODO

    - calculate rectangle when editing in-place (bug with some fonts)
    - SetSpaceBetweenEntries: offset is not taken into account in SetEntryHeight
*/


#define SV_LBOX_DEFAULT_INDENT_PIXEL 20

void SvTreeListBox::InitTreeView()
{
    pCheckButtonData = nullptr;
    pEdEntry = nullptr;
    pEdItem = nullptr;
    nEntryHeight = 0;
    pEdCtrl = nullptr;
    nFirstSelTab = 0;
    nLastSelTab = 0;
    nFocusWidth = -1;
    mnCheckboxItemWidth = 0;

    nTreeFlags = SvTreeFlags::RECALCTABS;
    nIndent = SV_LBOX_DEFAULT_INDENT_PIXEL;
    nEntryHeightOffs = SV_ENTRYHEIGHTOFFS_PIXEL;
    pImpl.reset( new SvImpLBox( this, GetModel(), GetStyle() ) );

    mbContextBmpExpanded = true;
    nContextBmpWidthMax = 0;

    SetFont( GetFont() );
    AdjustEntryHeightAndRecalc();

    SetSpaceBetweenEntries( 0 );
    GetOutDev()->SetLineColor();
    InitSettings();
    ImplInitStyle();
    SetTabs();
}

OUString SvTreeListBox::SearchEntryTextWithHeadTitle( SvTreeListEntry* pEntry )
{
    assert(pEntry);
    OUStringBuffer sRet;

    sal_uInt16 nCount = pEntry->ItemCount();
    sal_uInt16 nCur = 0;
    while( nCur < nCount )
    {
        SvLBoxItem& rItem = pEntry->GetItem( nCur );
        if ( (rItem.GetType() == SvLBoxItemType::String) &&
             !static_cast<SvLBoxString&>( rItem ).GetText().isEmpty() )
        {
            sRet.append(static_cast<SvLBoxString&>( rItem ).GetText() + ",");
        }
        nCur++;
    }

    if (!sRet.isEmpty())
        sRet.remove(sRet.getLength() - 1, 1);
    return sRet.makeStringAndClear();
}

SvTreeListBox::~SvTreeListBox()
{
    disposeOnce();
}

void SvTreeListBox::dispose()
{
    if (IsMouseCaptured())
        ReleaseMouse();

    if( pImpl )
    {
        pImpl->CallEventListeners( VclEventId::ObjectDying );
        pImpl.reset();
    }
    if( mpImpl )
    {
        ClearTabList();

        pEdCtrl.reset();

        SvListView::dispose();

        SvTreeListBox::RemoveBoxFromDDList_Impl( *this );

        if (this == g_pDDSource)
            g_pDDSource = nullptr;
        if (this == g_pDDTarget)
            g_pDDTarget = nullptr;
        mpImpl.reset();
    }

    DropTargetHelper::dispose();
    DragSourceHelper::dispose();
    Control::dispose();
}

void SvTreeListBox::SetNoAutoCurEntry( bool b )
{
    pImpl->SetNoAutoCurEntry( b );
}

void SvTreeListBox::SetSublistOpenWithLeftRight()
{
    pImpl->m_bSubLstOpLR = true;
}

void SvTreeListBox::Resize()
{
    if( IsEditingActive() )
        EndEditing( true );

    Control::Resize();

    pImpl->Resize();
    nFocusWidth = -1;
    pImpl->ShowCursor( false );
    pImpl->ShowCursor( true );
}


namespace {

/* Cases:

   A) entries have bitmaps
       0. no buttons
       1. node buttons (can optionally also be on root items)
       2. node buttons (can optionally also be on root items) + CheckButton
       3. CheckButton
   B) entries don't have bitmaps  (=>via WindowBits because of D&D!)
       0. no buttons
       1. node buttons (can optionally also be on root items)
       2. node buttons (can optionally also be on root items) + CheckButton
       3. CheckButton
*/

enum class TreeListButtonType
{
    NO_BUTTONS,
    NODE_BUTTONS,
    NODE_AND_CHECK_BUTTONS,
    CHECK_BUTTONS,
};

}

#define TABFLAGS_TEXT (SvLBoxTabFlags::DYNAMIC |        \
                       SvLBoxTabFlags::ADJUST_LEFT |    \
                       SvLBoxTabFlags::EDITABLE |       \
                       SvLBoxTabFlags::SHOW_SELECTION)

#define TABFLAGS_CONTEXTBMP (SvLBoxTabFlags::DYNAMIC | SvLBoxTabFlags::ADJUST_CENTER)

#define TABFLAGS_CHECKBTN (SvLBoxTabFlags::DYNAMIC |        \
                           SvLBoxTabFlags::ADJUST_CENTER)

#define TAB_STARTPOS    2

// take care of GetTextOffset when doing changes
void SvTreeListBox::SetTabs()
{
    // Moved to SvTreeListBox::Paint to make inplace editing work for X11 in the enhancement patch
    // tdf#139663 Rename objects from tree view in navigator.
    // if( IsEditingActive() )
    //   EndEditing( true );
    nTreeFlags &= ~SvTreeFlags::RECALCTABS;
    nFocusWidth = -1;
    const WinBits nStyle( GetStyle() );
    bool bHasButtons = (nStyle & WB_HASBUTTONS)!=0;
    bool bHasButtonsAtRoot = (nStyle & (WB_HASLINESATROOT |
                                              WB_HASBUTTONSATROOT))!=0;
    tools::Long nStartPos = TAB_STARTPOS;
    tools::Long nNodeWidthPixel = GetExpandedNodeBmp().GetSizePixel().Width();

    // pCheckButtonData->Width() knows nothing about the native checkbox width,
    // so we have mnCheckboxItemWidth which becomes valid when something is added.
    tools::Long nCheckWidth = 0;
    if( nTreeFlags & SvTreeFlags::CHKBTN )
        nCheckWidth = mnCheckboxItemWidth;
    tools::Long nCheckWidthDIV2 = nCheckWidth / 2;

    tools::Long nContextWidth = nContextBmpWidthMax;
    tools::Long nContextWidthDIV2 = nContextWidth / 2;

    ClearTabList();

    TreeListButtonType eButtonType = TreeListButtonType::NO_BUTTONS;
    if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
    {
        if( bHasButtons )
            eButtonType = TreeListButtonType::NODE_BUTTONS;
    }
    else
    {
        if( bHasButtons )
            eButtonType = TreeListButtonType::NODE_AND_CHECK_BUTTONS;
        else
            eButtonType = TreeListButtonType::CHECK_BUTTONS;
    }

    switch(eButtonType)
    {
        case TreeListButtonType::NO_BUTTONS:
            nStartPos += nContextWidthDIV2;  // because of centering
            AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
            nStartPos += nContextWidthDIV2;  // right edge of context bitmap
            // only set a distance if there are bitmaps
            if( nContextBmpWidthMax )
                nStartPos += 5; // distance context bitmap to text
            AddTab( nStartPos, TABFLAGS_TEXT );
            break;

        case TreeListButtonType::NODE_BUTTONS:
            if( bHasButtonsAtRoot )
                nStartPos += ( nIndent + (nNodeWidthPixel/2) );
            else
                nStartPos += nContextWidthDIV2;
            AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
            // add an indent if the context bitmap can't be centered without touching the expander
            if (nContextBmpWidthMax > nIndent + (nNodeWidthPixel / 2))
                nStartPos += nIndent;
            nStartPos += nContextWidthDIV2;  // right edge of context bitmap
            // only set a distance if there are bitmaps
            if( nContextBmpWidthMax )
                nStartPos += 5; // distance context bitmap to text
            AddTab( nStartPos, TABFLAGS_TEXT );
            break;

        case TreeListButtonType::NODE_AND_CHECK_BUTTONS:
            if( bHasButtonsAtRoot )
                nStartPos += ( nIndent + nNodeWidthPixel );
            else
                nStartPos += nCheckWidthDIV2;
            AddTab( nStartPos, TABFLAGS_CHECKBTN );
            nStartPos += nCheckWidthDIV2;  // right edge of CheckButton
            nStartPos += 3;  // distance CheckButton to context bitmap
            nStartPos += nContextWidthDIV2;  // center of context bitmap
            AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
            nStartPos += nContextWidthDIV2;  // right edge of context bitmap
            // only set a distance if there are bitmaps
            if( nContextBmpWidthMax )
                nStartPos += 5; // distance context bitmap to text
            AddTab( nStartPos, TABFLAGS_TEXT );
            break;

        case TreeListButtonType::CHECK_BUTTONS:
            nStartPos += nCheckWidthDIV2;
            AddTab( nStartPos, TABFLAGS_CHECKBTN );
            nStartPos += nCheckWidthDIV2;  // right edge of CheckButton
            nStartPos += 3;  // distance CheckButton to context bitmap
            nStartPos += nContextWidthDIV2;  // center of context bitmap
            AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
            nStartPos += nContextWidthDIV2;  // right edge of context bitmap
            // only set a distance if there are bitmaps
            if( nContextBmpWidthMax )
                nStartPos += 5; // distance context bitmap to text
            AddTab( nStartPos, TABFLAGS_TEXT );
            break;
    }
    pImpl->NotifyTabsChanged();
}

void SvTreeListBox::InitEntry(SvTreeListEntry* pEntry,
    const OUString& aStr, const Image& aCollEntryBmp, const Image& aExpEntryBmp)
{
    if( nTreeFlags & SvTreeFlags::CHKBTN )
    {
        pEntry->AddItem(std::make_unique<SvLBoxButton>(pCheckButtonData));
    }

    pEntry->AddItem(std::make_unique<SvLBoxContextBmp>( aCollEntryBmp,aExpEntryBmp, mbContextBmpExpanded));

    pEntry->AddItem(std::make_unique<SvLBoxString>(aStr));
}

OUString SvTreeListBox::GetEntryText(SvTreeListEntry* pEntry) const
{
    assert(pEntry);
    SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
    if (pItem) // There may be entries without text items, e.g. in IconView
        return pItem->GetText();
    return {};
}

const Image& SvTreeListBox::GetExpandedEntryBmp(const SvTreeListEntry* pEntry)
{
    assert(pEntry);
    const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
    assert(pItem);
    return pItem->GetBitmap2( );
}

const Image& SvTreeListBox::GetCollapsedEntryBmp( const SvTreeListEntry* pEntry )
{
    assert(pEntry);
    const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
    assert(pItem);
    return pItem->GetBitmap1( );
}

IMPL_LINK( SvTreeListBox, CheckButtonClick, SvLBoxButtonData *, pData, void )
{
    pHdlEntry = pData->GetActEntry();
    CheckButtonHdl();
}

SvTreeListEntry* SvTreeListBox::InsertEntry(
    const OUString& rText,
    SvTreeListEntry* pParent,
    bool bChildrenOnDemand, sal_uInt32 nPos,
    void* pUser
)
{
    nTreeFlags |= SvTreeFlags::MANINS;

    const Image& rDefExpBmp = pImpl->GetDefaultEntryExpBmp( );
    const Image& rDefColBmp = pImpl->GetDefaultEntryColBmp( );

    aCurInsertedExpBmp = rDefExpBmp;
    aCurInsertedColBmp = rDefColBmp;

    SvTreeListEntry* pEntry = new SvTreeListEntry;
    pEntry->SetUserData( pUser );
    InitEntry( pEntry, rText, rDefColBmp, rDefExpBmp );
    pEntry->EnableChildrenOnDemand( bChildrenOnDemand );

    if( !pParent )
        Insert( pEntry, nPos );
    else
        Insert( pEntry, pParent, nPos );

    aPrevInsertedExpBmp = rDefExpBmp;
    aPrevInsertedColBmp = rDefColBmp;

    nTreeFlags &= ~SvTreeFlags::MANINS;

    return pEntry;
}

void SvTreeListBox::SetEntryText(SvTreeListEntry* pEntry, const OUString& rStr)
{
    SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
    assert(pItem);
    pItem->SetText(rStr);
    pItem->InitViewData( this, pEntry );
    GetModel()->InvalidateEntry( pEntry );
}

void SvTreeListBox::SetExpandedEntryBmp( SvTreeListEntry* pEntry, const Image& aBmp )
{
    SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));

    assert(pItem);
    pItem->SetBitmap2( aBmp );

    ModelHasEntryInvalidated(pEntry);
    CalcEntryHeight( pEntry );
    Size aSize = aBmp.GetSizePixel();
    short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
    if( nWidth > nContextBmpWidthMax )
    {
        nContextBmpWidthMax = nWidth;
        SetTabs();
    }
}

void SvTreeListBox::SetCollapsedEntryBmp(SvTreeListEntry* pEntry,const Image& aBmp )
{
    SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));

    assert(pItem);
    pItem->SetBitmap1( aBmp );

    ModelHasEntryInvalidated(pEntry);
    CalcEntryHeight( pEntry );
    Size aSize = aBmp.GetSizePixel();
    short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
    if( nWidth > nContextBmpWidthMax )
    {
        nContextBmpWidthMax = nWidth;
        SetTabs();
    }
}

void SvTreeListBox::CheckBoxInserted(SvTreeListEntry* pEntry)
{
    SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
    if( pItem )
    {
        auto nWidth = pItem->GetWidth(this, pEntry);
        if( mnCheckboxItemWidth < nWidth )
        {
            mnCheckboxItemWidth = nWidth;
            nTreeFlags |= SvTreeFlags::RECALCTABS;
        }
    }
}

void SvTreeListBox::ImpEntryInserted( SvTreeListEntry* pEntry )
{

    SvTreeListEntry* pParent = pModel->GetParent( pEntry );
    if( pParent )
    {
        SvTLEntryFlags nFlags = pParent->GetFlags();
        nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
        pParent->SetFlags( nFlags );
    }

    if(!((nTreeFlags & SvTreeFlags::MANINS) &&
         (aPrevInsertedExpBmp == aCurInsertedExpBmp)  &&
         (aPrevInsertedColBmp == aCurInsertedColBmp) ))
    {
        Size aSize = GetCollapsedEntryBmp( pEntry ).GetSizePixel();
        if( aSize.Width() > nContextBmpWidthMax )
        {
            nContextBmpWidthMax = static_cast<short>(aSize.Width());
            nTreeFlags |= SvTreeFlags::RECALCTABS;
        }
        aSize = GetExpandedEntryBmp( pEntry ).GetSizePixel();
        if( aSize.Width() > nContextBmpWidthMax )
        {
            nContextBmpWidthMax = static_cast<short>(aSize.Width());
            nTreeFlags |= SvTreeFlags::RECALCTABS;
        }
    }
    CalcEntryHeight( pEntry );

    if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
        return;

    CheckBoxInserted(pEntry);
}

void SvTreeListBox::SetCheckButtonState( SvTreeListEntry* pEntry, SvButtonState eState)
{
    if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
        return;

    SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
    if(!pItem)
        return ;
    switch( eState )
    {
        case SvButtonState::Checked:
            pItem->SetStateChecked();
            break;

        case SvButtonState::Unchecked:
            pItem->SetStateUnchecked();
            break;

        case SvButtonState::Tristate:
            pItem->SetStateTristate();
            break;
    }
    InvalidateEntry( pEntry );
}

SvButtonState SvTreeListBox::GetCheckButtonState( SvTreeListEntry* pEntry ) const
{
    SvButtonState eState = SvButtonState::Unchecked;
    if( pEntry && ( nTreeFlags & SvTreeFlags::CHKBTN ) )
    {
        SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
        if(!pItem)
            return SvButtonState::Tristate;
        SvItemStateFlags nButtonFlags = pItem->GetButtonFlags();
        eState = SvLBoxButtonData::ConvertToButtonState( nButtonFlags );
    }
    return eState;
}

bool SvTreeListBox::GetCheckButtonEnabled(SvTreeListEntry* pEntry) const
{
    if (pEntry && (nTreeFlags & SvTreeFlags::CHKBTN))
    {
        SvLBoxButton* pItem
            = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
        if (pItem)
            return pItem->isEnable();
    }
    return false;
}

void SvTreeListBox::CheckButtonHdl()
{
    if ( pCheckButtonData )
        pImpl->CallEventListeners( VclEventId::CheckboxToggle, static_cast<void*>(pCheckButtonData->GetActEntry()) );
}


// TODO: Currently all data is cloned so that they conform to the default tree
// view format. Actually, the model should be used as a reference here. This
// leads to us _not_ calling SvTreeListEntry::Clone, but only its base class
// SvTreeListEntry.


SvTreeListEntry* SvTreeListBox::CloneEntry( SvTreeListEntry* pSource )
{
    OUString aStr;
    Image aCollEntryBmp;
    Image aExpEntryBmp;

    SvLBoxString* pStringItem = static_cast<SvLBoxString*>(pSource->GetFirstItem(SvLBoxItemType::String));
    if( pStringItem )
        aStr = pStringItem->GetText();
    SvLBoxContextBmp* pBmpItem = static_cast<SvLBoxContextBmp*>(pSource->GetFirstItem(SvLBoxItemType::ContextBmp));
    if( pBmpItem )
    {
        aCollEntryBmp = pBmpItem->GetBitmap1( );
        aExpEntryBmp  = pBmpItem->GetBitmap2( );
    }
    SvTreeListEntry* pClone = new SvTreeListEntry;
    InitEntry( pClone, aStr, aCollEntryBmp, aExpEntryBmp );
    pClone->SvTreeListEntry::Clone( pSource );
    pClone->EnableChildrenOnDemand( pSource->HasChildrenOnDemand() );
    pClone->SetUserData( pSource->GetUserData() );

    return pClone;
}

const Image& SvTreeListBox::GetDefaultExpandedEntryBmp( ) const
{
    return pImpl->GetDefaultEntryExpBmp( );
}

const Image& SvTreeListBox::GetDefaultCollapsedEntryBmp( ) const
{
    return pImpl->GetDefaultEntryColBmp( );
}

void SvTreeListBox::SetDefaultExpandedEntryBmp( const Image& aBmp )
{
    Size aSize = aBmp.GetSizePixel();
    if( aSize.Width() > nContextBmpWidthMax )
        nContextBmpWidthMax = static_cast<short>(aSize.Width());
    SetTabs();

    pImpl->SetDefaultEntryExpBmp( aBmp );
}

void SvTreeListBox::SetDefaultCollapsedEntryBmp( const Image& aBmp )
{
    Size aSize = aBmp.GetSizePixel();
    if( aSize.Width() > nContextBmpWidthMax )
        nContextBmpWidthMax = static_cast<short>(aSize.Width());
    SetTabs();

    pImpl->SetDefaultEntryColBmp( aBmp );
}

void SvTreeListBox::EnableCheckButton(SvLBoxButtonData& rData)
{
    pCheckButtonData = &rData;
    nTreeFlags |= SvTreeFlags::CHKBTN;
    rData.SetLink( LINK(this, SvTreeListBox, CheckButtonClick));

    SetTabs();
    if( IsUpdateMode() )
        Invalidate();
}

const Image& SvTreeListBox::GetDefaultExpandedNodeImage( )
{
    return SvImpLBox::GetDefaultExpandedNodeImage( );
}

const Image& SvTreeListBox::GetDefaultCollapsedNodeImage( )
{
    return SvImpLBox::GetDefaultCollapsedNodeImage( );
}

void SvTreeListBox::SetNodeDefaultImages()
{
    SetExpandedNodeBmp(GetDefaultExpandedNodeImage());
    SetCollapsedNodeBmp(GetDefaultCollapsedNodeImage());
    SetTabs();
}

bool SvTreeListBox::EditingEntry( SvTreeListEntry* )
{
    return true;
}

bool SvTreeListBox::EditedEntry( SvTreeListEntry* /*pEntry*/,const OUString& /*rNewText*/)
{
    return true;
}

void SvTreeListBox::EnableInplaceEditing( bool bOn )
{
    if (bOn)
        nImpFlags |= SvTreeListBoxFlags::EDT_ENABLED;
    else
        nImpFlags &= ~SvTreeListBoxFlags::EDT_ENABLED;
}

void SvTreeListBox::KeyInput( const KeyEvent& rKEvt )
{
    // under OS/2, we get key up/down even while editing
    if( IsEditingActive() )
        return;

    if( !pImpl->KeyInput( rKEvt ) )
    {
        bool bHandled = HandleKeyInput( rKEvt );
        if ( !bHandled )
            Control::KeyInput( rKEvt );
    }
}

void SvTreeListBox::RequestingChildren( SvTreeListEntry* pParent )
{
    if( !pParent->HasChildren() )
        InsertEntry( u"<dummy>"_ustr, pParent );
}

void SvTreeListBox::GetFocus()
{
    //If there is no item in the tree, draw focus.
    if( !First())
    {
        Invalidate();
    }
    pImpl->GetFocus();
    Control::GetFocus();

    SvTreeListEntry* pEntry = FirstSelected();
    if ( !pEntry )
    {
        pEntry = pImpl->GetCurEntry();
    }
    if (pImpl->m_pCursor)
    {
        if (pEntry != pImpl->m_pCursor)
            pEntry = pImpl->m_pCursor;
    }
    if ( pEntry )
        pImpl->CallEventListeners( VclEventId::ListboxTreeFocus, pEntry );

}

void SvTreeListBox::LoseFocus()
{
    // If there is no item in the tree, delete visual focus.
    if ( !First() )
        Invalidate();
    if ( pImpl )
        pImpl->LoseFocus();
    Control::LoseFocus();
}

void SvTreeListBox::ModelHasCleared()
{
    pImpl->m_pCursor = nullptr; // else we crash in GetFocus when editing in-place
    pTargetEntry = nullptr;
    pEdCtrl.reset();
    pImpl->Clear();
    nFocusWidth = -1;

    nContextBmpWidthMax = 0;
    SetDefaultExpandedEntryBmp( GetDefaultExpandedEntryBmp() );
    SetDefaultCollapsedEntryBmp( GetDefaultCollapsedEntryBmp() );

    if( !(nTreeFlags & SvTreeFlags::FIXEDHEIGHT ))
        nEntryHeight = 0;
    AdjustEntryHeight();
    AdjustEntryHeight( GetDefaultExpandedEntryBmp() );
    AdjustEntryHeight( GetDefaultCollapsedEntryBmp() );

    SvListView::ModelHasCleared();
}

bool SvTreeListBox::PosOverBody(const Point& rPos) const
{
    if (rPos.X() < 0 || rPos.Y() < 0)
        return false;
    Size aSize(GetSizePixel());
    if (rPos.X() > aSize.Width() || rPos.Y() > aSize.Height())
        return false;
    if (pImpl->m_aVerSBar->IsVisible())
    {
        tools::Rectangle aRect(pImpl->m_aVerSBar->GetPosPixel(), pImpl->m_aVerSBar->GetSizePixel());
        if (aRect.Contains(rPos))
            return false;
    }
    if (pImpl->m_aHorSBar->IsVisible())
    {
        tools::Rectangle aRect(pImpl->m_aHorSBar->GetPosPixel(), pImpl->m_aHorSBar->GetSizePixel());
        if (aRect.Contains(rPos))
            return false;
    }
    return true;
}

void SvTreeListBox::ScrollOutputArea( short nDeltaEntries )
{
    if( !nDeltaEntries || !pImpl->m_aVerSBar->IsVisible() )
        return;

    tools::Long nThumb = pImpl->m_aVerSBar->GetThumbPos();
    tools::Long nMax = pImpl->m_aVerSBar->GetRange().Max();

    if( nDeltaEntries < 0 )
    {
        // move window up
        nDeltaEntries *= -1;
        tools::Long nVis = pImpl->m_aVerSBar->GetVisibleSize();
        tools::Long nTemp = nThumb + nVis;
        if( nDeltaEntries > (nMax - nTemp) )
            nDeltaEntries = static_cast<short>(nMax - nTemp);
        pImpl->PageDown( static_cast<sal_uInt16>(nDeltaEntries) );
    }
    else
    {
        if( nDeltaEntries > nThumb )
            nDeltaEntries = static_cast<short>(nThumb);
        pImpl->PageUp( static_cast<sal_uInt16>(nDeltaEntries) );
    }
    pImpl->SyncVerThumb();
}

void SvTreeListBox::ScrollToAbsPos( tools::Long nPos )
{
    pImpl->ScrollToAbsPos( nPos );
}

void SvTreeListBox::SetSelectionMode( SelectionMode eSelectMode )
{
    eSelMode = eSelectMode;
    pImpl->SetSelectionMode( eSelectMode );
}

void SvTreeListBox::SetDragDropMode( DragDropMode nDDMode )
{
    nDragDropMode = nDDMode;
    pImpl->SetDragDropMode( nDDMode );
}

void SvTreeListBox::CalcEntryHeight( SvTreeListEntry const * pEntry )
{
    short nHeightMax=0;
    sal_uInt16 nCount = pEntry->ItemCount();
    sal_uInt16 nCur = 0;
    SvViewDataEntry* pViewData = GetViewDataEntry( pEntry );
    while( nCur < nCount )
    {
        auto nHeight = SvLBoxItem::GetHeight(pViewData, nCur);
        if( nHeight > nHeightMax )
            nHeightMax = nHeight;
        nCur++;
    }

    if( nHeightMax > nEntryHeight )
    {
        nEntryHeight = nHeightMax;
        Control::SetFont( GetFont() );
        pImpl->SetEntryHeight();
    }
}

void SvTreeListBox::SetEntryHeight( short nHeight )
{
    if( nHeight > nEntryHeight )
    {
        nEntryHeight = nHeight;
        if( nEntryHeight )
            nTreeFlags |= SvTreeFlags::FIXEDHEIGHT;
        else
            nTreeFlags &= ~SvTreeFlags::FIXEDHEIGHT;
        Control::SetFont( GetFont() );
        pImpl->SetEntryHeight();
    }
}

void SvTreeListBox::SetEntryWidth( short nWidth )
{
    nEntryWidth = nWidth;
}

void SvTreeListBox::AdjustEntryHeight( const Image& rBmp )
{
    const Size aSize( rBmp.GetSizePixel() );
    if( aSize.Height() > nEntryHeight )
    {
        nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs;
        pImpl->SetEntryHeight();
    }
}

void SvTreeListBox::AdjustEntryHeight()
{
    tools::Long nHeight = GetTextHeight();
    if( nHeight  >  nEntryHeight )
    {
        nEntryHeight = static_cast<short>(nHeight) + nEntryHeightOffs;
        pImpl->SetEntryHeight();
    }
}

bool SvTreeListBox::Expand( SvTreeListEntry* pParent )
{
    pHdlEntry = pParent;
    bool bExpanded = false;
    SvTLEntryFlags nFlags;

    if( pParent->HasChildrenOnDemand() )
        RequestingChildren( pParent );
    bool bExpandAllowed = pParent->HasChildren() && ExpandingHdl();
    // double check if the expander callback ended up removing all children
    if (pParent->HasChildren())
    {
        if (bExpandAllowed)
        {
            bExpanded = true;
            ExpandListEntry( pParent );
            pImpl->EntryExpanded( pParent );
            pHdlEntry = pParent;
            ExpandedHdl();
        }
        nFlags = pParent->GetFlags();
        nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
        nFlags |= SvTLEntryFlags::HAD_CHILDREN;
        pParent->SetFlags( nFlags );
    }
    else
    {
        nFlags = pParent->GetFlags();
        nFlags |= SvTLEntryFlags::NO_NODEBMP;
        pParent->SetFlags( nFlags );
        GetModel()->InvalidateEntry( pParent ); // repaint
    }

    // #i92103#
    if ( bExpanded )
    {
        pImpl->CallEventListeners( VclEventId::ItemExpanded, pParent );
    }

    return bExpanded;
}

bool SvTreeListBox::Collapse( SvTreeListEntry* pParent )
{
    pHdlEntry = pParent;
    bool bCollapsed = false;

    if( ExpandingHdl() )
    {
        bCollapsed = true;
        pImpl->CollapsingEntry( pParent );
        CollapseListEntry( pParent );
        pImpl->EntryCollapsed( pParent );
        pHdlEntry = pParent;
        ExpandedHdl();
    }

    // #i92103#
    if ( bCollapsed )
    {
        pImpl->CallEventListeners( VclEventId::ItemCollapsed, pParent );
    }

    return bCollapsed;
}

bool SvTreeListBox::Select( SvTreeListEntry* pEntry, bool bSelect )
{
    DBG_ASSERT(pEntry,"Select: Null-Ptr");
    bool bRetVal = SelectListEntry( pEntry, bSelect );
    DBG_ASSERT(IsSelected(pEntry)==bSelect,"Select failed");
    if( bRetVal )
    {
        pImpl->EntrySelected( pEntry, bSelect );
        pHdlEntry = pEntry;
        if( bSelect )
        {
            SelectHdl();
            CallEventListeners( VclEventId::ListboxTreeSelect, pEntry);
        }
        else
            DeselectHdl();
    }
    return bRetVal;
}

sal_uInt32 SvTreeListBox::SelectChildren( SvTreeListEntry* pParent, bool bSelect )
{
    pImpl->DestroyAnchor();
    sal_uInt32 nRet = 0;
    if( !pParent->HasChildren() )
        return 0;
    sal_uInt16 nRefDepth = pModel->GetDepth( pParent );
    SvTreeListEntry* pChild = FirstChild( pParent );
    do {
        nRet++;
        Select( pChild, bSelect );
        pChild = Next( pChild );
    } while( pChild && pModel->GetDepth( pChild ) > nRefDepth );
    return nRet;
}

void SvTreeListBox::SelectAll( bool bSelect )
{
    pImpl->SelAllDestrAnch(
        bSelect,
        true,       // delete anchor,
        true );     // even when using SelectionMode::Single, deselect the cursor
}

void SvTreeListBox::ModelHasInsertedTree( SvTreeListEntry* pEntry )
{
    sal_uInt16 nRefDepth = pModel->GetDepth( pEntry );
    SvTreeListEntry* pTmp = pEntry;
    do
    {
        ImpEntryInserted( pTmp );
        pTmp = Next( pTmp );
    } while( pTmp && nRefDepth < pModel->GetDepth( pTmp ) );
    pImpl->TreeInserted( pEntry );
}

void SvTreeListBox::ModelHasInserted( SvTreeListEntry* pEntry )
{
    ImpEntryInserted( pEntry );
    pImpl->EntryInserted( pEntry );
}

void SvTreeListBox::ModelIsMoving(SvTreeListEntry* pSource )
{
    pImpl->MovingEntry( pSource );
}

void SvTreeListBox::ModelHasMoved( SvTreeListEntry* pSource )
{
    pImpl->EntryMoved( pSource );
}

void SvTreeListBox::ModelIsRemoving( SvTreeListEntry* pEntry )
{
    if(pEdEntry == pEntry)
        pEdEntry = nullptr;

    pImpl->RemovingEntry( pEntry );
}

void SvTreeListBox::ModelHasRemoved( SvTreeListEntry* pEntry  )
{
    if (pEntry == pHdlEntry)
        pHdlEntry = nullptr;

    if (pEntry == pTargetEntry)
        pTargetEntry = nullptr;

    pImpl->EntryRemoved();
}

void SvTreeListBox::SetCollapsedNodeBmp( const Image& rBmp)
{
    AdjustEntryHeight( rBmp );
    pImpl->SetCollapsedNodeBmp( rBmp );
}

void SvTreeListBox::SetExpandedNodeBmp( const Image& rBmp )
{
    AdjustEntryHeight( rBmp );
    pImpl->SetExpandedNodeBmp( rBmp );
}


void SvTreeListBox::SetFont( const vcl::Font& rFont )
{
    vcl::Font aTempFont( rFont );
    vcl::Font aOrigFont( GetFont() );
    aTempFont.SetTransparent( true );
    if (aTempFont == aOrigFont)
        return;
    Control::SetFont( aTempFont );

    aTempFont.SetColor(aOrigFont.GetColor());
    aTempFont.SetFillColor(aOrigFont.GetFillColor());
    aTempFont.SetTransparent(aOrigFont.IsTransparent());

    if (aTempFont == aOrigFont)
        return;

    AdjustEntryHeightAndRecalc();
}

void SvTreeListBox::AdjustEntryHeightAndRecalc()
{
    AdjustEntryHeight();
    // always invalidate, else things go wrong in SetEntryHeight
    RecalcViewData();
}

void SvTreeListBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    Control::Paint(rRenderContext, rRect);
    if (nTreeFlags & SvTreeFlags::RECALCTABS)
    {
        if (IsEditingActive())
            EndEditing(true);
        SetTabs();
    }
    pImpl->Paint(rRenderContext, rRect);

    //Add visual focus draw
    if (First())
        return;

    if (HasFocus())
    {
        tools::Long nHeight = rRenderContext.GetTextHeight();
        tools::Rectangle aRect(Point(0, 0), Size(GetSizePixel().Width(), nHeight));
        ShowFocus(aRect);
    }
    else
    {
        HideFocus();
    }
}

void SvTreeListBox::MouseButtonDown( const MouseEvent& rMEvt )
{
    // tdf#143114 remember the *correct* starting entry
    pImpl->m_pCursorOld = (rMEvt.IsLeft() && (nTreeFlags & SvTreeFlags::CHKBTN) && mnClicksToToggle > 0)
        ? GetEntry(rMEvt.GetPosPixel())
        : nullptr;

    pImpl->MouseButtonDown( rMEvt );
}

void SvTreeListBox::MouseButtonUp( const MouseEvent& rMEvt )
{
    // tdf#116675 clicking on an entry should toggle its checkbox
    // tdf#143114 use the already created starting entry and if it exists
    if (nullptr != pImpl->m_pCursorOld)
    {
        const Point aPnt = rMEvt.GetPosPixel();
        SvTreeListEntry* pEntry = GetEntry(aPnt);

        // compare if MouseButtonUp *is* on the same entry, regardless of scrolling
        // or other things
        if (pEntry && pEntry->m_Items.size() > 0 && 1 == mnClicksToToggle && pEntry == pImpl->m_pCursorOld)
        {
            SvLBoxItem* pItem = GetItem(pEntry, aPnt.X());
            // if the checkbox button was clicked, that will be toggled later, do not toggle here
            // anyway users probably don't want to toggle the checkbox by clickink on another button
            if (!pItem || pItem->GetType() != SvLBoxItemType::Button)
            {
                SvLBoxButton* pItemCheckBox
                    = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
                if (pItemCheckBox && pItemCheckBox->isEnable() && GetItemPos(pEntry, 0).first < aPnt.X() - GetMapMode().GetOrigin().X())
                {
                    pItemCheckBox->ClickHdl(pEntry);
                    InvalidateEntry(pEntry);
                }
            }
        }
    }

    pImpl->MouseButtonUp( rMEvt );
}

void SvTreeListBox::MouseMove( const MouseEvent& rMEvt )
{
    pImpl->MouseMove( rMEvt );
}

void SvTreeListBox::SetUpdateMode( bool bUpdate )
{
    pImpl->SetUpdateMode( bUpdate );
}

void SvTreeListBox::SetSpaceBetweenEntries( short nOffsLogic )
{
    if( nOffsLogic != nEntryHeightOffs )
    {
        nEntryHeight = nEntryHeight - nEntryHeightOffs;
        nEntryHeightOffs = nOffsLogic;
        nEntryHeight = nEntryHeight + nOffsLogic;
        AdjustEntryHeightAndRecalc();
        pImpl->SetEntryHeight();
    }
}

void SvTreeListBox::SetCurEntry( SvTreeListEntry* pEntry )
{
    pImpl->SetCurEntry( pEntry );
}

Image const & SvTreeListBox::GetExpandedNodeBmp( ) const
{
    return pImpl->GetExpandedNodeBmp( );
}

Point SvTreeListBox::GetEntryPosition(const SvTreeListEntry* pEntry) const
{
    return pImpl->GetEntryPosition( pEntry );
}

void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry )
{
    pImpl->MakeVisible(pEntry);
}

void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
{
    pImpl->MakeVisible( pEntry, bMoveToTop );
}

void SvTreeListBox::ModelHasEntryInvalidated( SvTreeListEntry* pEntry )
{

    // reinitialize the separate items of the entries
    sal_uInt16 nCount = pEntry->ItemCount();
    for( sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++ )
    {
        SvLBoxItem& rItem = pEntry->GetItem( nIdx );
        rItem.InitViewData( this, pEntry );
    }

    // repaint
    pImpl->InvalidateEntry( pEntry );
}

void SvTreeListBox::EditItemText(SvTreeListEntry* pEntry, SvLBoxString* pItem, const Selection& rSelection)
{
    assert(pEntry && pItem);
    if( IsSelected( pEntry ))
    {
        pImpl->ShowCursor( false );
        SelectListEntry( pEntry, false );
        pImpl->InvalidateEntry(pEntry);
        SelectListEntry( pEntry, true );
        pImpl->ShowCursor( true );
    }
    pEdEntry = pEntry;
    pEdItem = pItem;
    SvLBoxTab* pTab = GetTab( pEntry, pItem );
    DBG_ASSERT(pTab,"EditItemText:Tab not found");

    auto nItemHeight( pItem->GetHeight(this, pEntry) );
    Point aPos = GetEntryPosition( pEntry );
    aPos.AdjustY(( nEntryHeight - nItemHeight ) / 2 );
    aPos.setX( GetTabPos( pEntry, pTab ) );
    tools::Long nOutputWidth = pImpl->GetOutputSize().Width();
    Size aSize( nOutputWidth - aPos.X(), nItemHeight );
    sal_uInt16 nPos = std::find_if( aTabs.begin(), aTabs.end(),
                        [pTab](const std::unique_ptr<SvLBoxTab>& p) { return p.get() == pTab; })
                      - aTabs.begin();
    if( nPos+1 < static_cast<sal_uInt16>(aTabs.size()) )
    {
        SvLBoxTab* pRightTab = aTabs[ nPos + 1 ].get();
        tools::Long nRight = GetTabPos( pEntry, pRightTab );
        if( nRight <= nOutputWidth )
            aSize.setWidth( nRight - aPos.X() );
    }
    Point aOrigin( GetMapMode().GetOrigin() );
    aPos += aOrigin; // convert to win coordinates
    aSize.AdjustWidth( -(aOrigin.X()) );
    tools::Rectangle aRect( aPos, aSize );
    EditText( pItem->GetText(), aRect, rSelection );
}

void SvTreeListBox::EditEntry( SvTreeListEntry* pEntry )
{
    pImpl->m_aEditClickPos = Point( -1, -1 );
    ImplEditEntry( pEntry );
}

void SvTreeListBox::ImplEditEntry( SvTreeListEntry* pEntry )
{
    if( IsEditingActive() )
        EndEditing();
    if( !pEntry )
        pEntry = GetCurEntry();
    if( !pEntry )
        return;

    tools::Long nClickX = pImpl->m_aEditClickPos.X();
    bool bIsMouseTriggered = nClickX >= 0;

    SvLBoxString* pItem = nullptr;
    sal_uInt16 nCount = pEntry->ItemCount();
    tools::Long nTabPos, nNextTabPos = 0;
    for( sal_uInt16 i = 0 ; i < nCount ; i++ )
    {
        SvLBoxItem& rTmpItem = pEntry->GetItem( i );
        if (rTmpItem.GetType() != SvLBoxItemType::String)
            continue;

        SvLBoxTab* pTab = GetTab( pEntry, &rTmpItem );
        nNextTabPos = -1;
        if( i < nCount - 1 )
        {
            SvLBoxItem& rNextItem = pEntry->GetItem( i + 1 );
            SvLBoxTab* pNextTab = GetTab( pEntry, &rNextItem );
            nNextTabPos = pNextTab->GetPos();
        }

        if( pTab && pTab->IsEditable() )
        {
            nTabPos = pTab->GetPos();
            if( !bIsMouseTriggered || (nClickX > nTabPos && (nNextTabPos == -1 || nClickX < nNextTabPos ) ) )
            {
                pItem = static_cast<SvLBoxString*>( &rTmpItem );
                break;
            }
        }
    }

    if( pItem && EditingEntry( pEntry ) )
    {
        Selection aSel( SELECTION_MIN, SELECTION_MAX );
        SelectAll( false );
        MakeVisible( pEntry );
        EditItemText( pEntry, pItem, aSel );
    }
}

void SvTreeListBox::EditedText( const OUString& rStr )

{
    if(pEdEntry) // we have to check if this entry is null that means that it is removed while editing
    {
        if( EditedEntry( pEdEntry, rStr ) )
        {
            pEdItem->SetText( rStr );
            pModel->InvalidateEntry( pEdEntry );
        }
        if( GetSelectionCount() == 0 )
            Select( pEdEntry );
        if( GetSelectionMode() == SelectionMode::Multiple && !GetCurEntry() )
            SetCurEntry( pEdEntry );
    }
}

SvTreeListEntry* SvTreeListBox::GetDropTarget( const Point& rPos )
{
    // scroll
    if( rPos.Y() < 12 )
    {
        ImplShowTargetEmphasis(pTargetEntry, false);
        ScrollOutputArea( +1 );
    }
    else
    {
        Size aSize( pImpl->GetOutputSize() );
        if( rPos.Y() > aSize.Height() - 12 )
        {
            ImplShowTargetEmphasis(pTargetEntry, false);
            ScrollOutputArea( -1 );
        }
    }

    SvTreeListEntry* pTarget = pImpl->GetEntry( rPos );
    // when dropping in a vacant space, use the last entry
    if( !pTarget )
        return LastVisible();
    else if( (GetDragDropMode() & DragDropMode::ENABLE_TOP) &&
             pTarget == First() && rPos.Y() < 6 )
        return nullptr;

    return pTarget;
}


SvTreeListEntry* SvTreeListBox::GetEntry( const Point& rPos, bool bHit ) const
{
    SvTreeListEntry* pEntry = pImpl->GetEntry( rPos );
    if( pEntry && bHit )
    {
        tools::Long nLine = pImpl->GetEntryLine( pEntry );
        if( !(pImpl->EntryReallyHit( pEntry, rPos, nLine)) )
            return nullptr;
    }
    return pEntry;
}

SvTreeListEntry* SvTreeListBox::GetCurEntry() const
{
    return pImpl ? pImpl->GetCurEntry() : nullptr;
}

void SvTreeListBox::ImplInitStyle()
{
    const WinBits nWindowStyle = GetStyle();

    nTreeFlags |= SvTreeFlags::RECALCTABS;
    if (nWindowStyle & WB_SORT)
    {
        GetModel()->SetSortMode(SvSortMode::Ascending);
        GetModel()->SetCompareHdl(LINK(this, SvTreeListBox, DefaultCompare));
    }
    else
    {
        GetModel()->SetSortMode(SvSortMode::None);
        GetModel()->SetCompareHdl(Link<const SvSortData&,sal_Int32>());
    }
    pImpl->SetStyle(nWindowStyle);
    pImpl->Resize();
    Invalidate();
}

void SvTreeListBox::InvalidateEntry(SvTreeListEntry* pEntry)
{
    DBG_ASSERT(pEntry,"InvalidateEntry:No Entry");
    if (pEntry)
    {
        GetModel()->InvalidateEntry(pEntry);
    }
}

void SvTreeListBox::PaintEntry1(SvTreeListEntry& rEntry, tools::Long nLine, vcl::RenderContext& rRenderContext)
{
    tools::Rectangle aRect; // multi purpose

    bool bHorSBar = pImpl->HasHorScrollBar();

    pImpl->UpdateContextBmpWidthMax(&rEntry);

    if (nTreeFlags & SvTreeFlags::RECALCTABS)
        SetTabs();

    short nTempEntryHeight = GetEntryHeight();
    tools::Long nWidth = pImpl->GetOutputSize().Width();

    // Did we turn on the scrollbar within PreparePaints? If yes, we have to set
    // the ClipRegion anew.
    if (!bHorSBar && pImpl->HasHorScrollBar())
        rRenderContext.SetClipRegion(vcl::Region(pImpl->GetClipRegionRect()));

    Point aEntryPos(rRenderContext.GetMapMode().GetOrigin());
    aEntryPos.setX( aEntryPos.X() * -1 ); // conversion document coordinates
    tools::Long nMaxRight = nWidth + aEntryPos.X() - 1;

    Color aBackupTextColor(rRenderContext.GetTextColor());
    vcl::Font aBackupFont(rRenderContext.GetFont());
    Color aBackupColor = rRenderContext.GetFillColor();

    bool bCurFontIsSel = false;
    // if a ClipRegion was set from outside, we don't have to reset it
    const WinBits nWindowStyle = GetStyle();
    const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus();
    const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();

    vcl::Font aHighlightFont(rRenderContext.GetFont());
    const Color aHighlightTextColor(rSettings.GetHighlightTextColor());
    aHighlightFont.SetColor(aHighlightTextColor);

    Size aRectSize(0, nTempEntryHeight);

    SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry );
    const bool bSeparator(rEntry.GetFlags() & SvTLEntryFlags::IS_SEPARATOR);

    const auto nMaxContextBmpWidthBeforeIndentIsNeeded =
            nIndent + GetExpandedNodeBmp().GetSizePixel().Width() / 2;
    const bool bHasButtonsAtRoot = nWindowStyle & WB_HASBUTTONSATROOT;

    const size_t nTabCount = aTabs.size();

    // Get image information

    sal_uInt16 nFirstDynTabPos(0);
    SvLBoxTab* pFirstDynamicTab = GetFirstDynamicTab(nFirstDynTabPos);

    const Image* pImg = nullptr;
    Point aImagePos;
    Size aImageSize;
    bool bDefaultImage = false;
    bool bExpanded = false;

    const bool bNeedsImage = (!(rEntry.GetFlags() & SvTLEntryFlags::NO_NODEBMP))
                             && (nWindowStyle & WB_HASBUTTONS) && pFirstDynamicTab
                             && (rEntry.HasChildren() || rEntry.HasChildrenOnDemand());
    if (bNeedsImage)
    {
        tools::Long nDynTabPos = GetTabPos(&rEntry, pFirstDynamicTab);
        nDynTabPos += pImpl->m_nNodeBmpTabDistance;
        nDynTabPos += pImpl->m_nNodeBmpWidth / 2;
        nDynTabPos += 4; // 4 pixels of buffer, so the node bitmap is not too close to the next tab

        // find first tab and check if the node bitmap extends into it
        sal_uInt16 nNextTab = nFirstDynTabPos;
        SvLBoxTab* pNextTab;
        do
        {
            nNextTab++;
            pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
        } while (pNextTab && pNextTab->IsDynamic());

        if (pNextTab && (GetTabPos(&rEntry, pNextTab) <= nDynTabPos))
            return;

        if (!((nWindowStyle & WB_HASBUTTONSATROOT) || pModel->GetDepth(&rEntry) > 0))
            return;

        aImagePos = Point(GetTabPos(&rEntry, pFirstDynamicTab), nLine);
        aImagePos.AdjustX(pImpl->m_nNodeBmpTabDistance);

        bExpanded = IsExpanded(&rEntry);
        if (bExpanded)
            pImg = &pImpl->GetExpandedNodeBmp();
        else
            pImg = &pImpl->GetCollapsedNodeBmp();
        bDefaultImage = bExpanded ? *pImg == GetDefaultExpandedNodeImage()
                                  : *pImg == GetDefaultCollapsedNodeImage();
        aImageSize = pImg->GetSizePixel();
        aImagePos.AdjustY((nTempEntryHeight - aImageSize.Height()) / 2);
    }

    // Draw items

    const size_t nItemCount = rEntry.ItemCount();
    size_t nCurTab = 0;
    size_t nCurItem = 0;

    const bool bEntryHighlighted = pViewDataEntry->IsHighlighted();
    // We need to track, if the area for the image area has selection background; otherwise,
    // the symbol may be drawn using aHighlightTextColor (usually white) on white background
    bool bImageHighlighted = false;

    while (nCurTab < nTabCount && nCurItem < nItemCount)
    {
        SvLBoxTab* pTab = aTabs[nCurTab].get();
        const size_t nNextTab = nCurTab + 1;
        SvLBoxTab* pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
        SvLBoxItem& rItem = rEntry.GetItem(nCurItem);

        SvLBoxTabFlags nFlags = pTab->nFlags;
        Size aSize(rItem.GetWidth(this, pViewDataEntry, nCurItem),
                   SvLBoxItem::GetHeight(pViewDataEntry, nCurItem));
        tools::Long nTabPos = GetTabPos(&rEntry, pTab);

        tools::Long nNextTabPos;
        if (pNextTab)
            nNextTabPos = GetTabPos(&rEntry, pNextTab);
        else
        {
            nNextTabPos = nMaxRight;
            if (nTabPos > nMaxRight)
                nNextTabPos += 50;
        }

        tools::Long nX;
        if( pTab->nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
            // avoid cutting the right edge off the tab separation
            nX = nTabPos + pTab->CalcOffset(aSize.Width(), (nNextTabPos - SV_TAB_BORDER - 1) - nTabPos);
        else
            nX = nTabPos + pTab->CalcOffset(aSize.Width(), nNextTabPos - nTabPos);

        // add an indent if the context bitmap can't be centered without touching the expander
        if (nCurTab == 0 && !(nTreeFlags & SvTreeFlags::CHKBTN) && bHasButtonsAtRoot &&
                pTab->nFlags & SvLBoxTabFlags::ADJUST_CENTER &&
                !(pTab->nFlags & SvLBoxTabFlags::FORCE) &&
                aSize.Width() > nMaxContextBmpWidthBeforeIndentIsNeeded)
            nX += nIndent;

        aEntryPos.setX( nX );
        aEntryPos.setY( nLine );

        // set background pattern/color

        Wallpaper aWallpaper = rRenderContext.GetBackground();

        bool bSelTab = bool(nFlags & SvLBoxTabFlags::SHOW_SELECTION);

        if (bEntryHighlighted && bSelTab)
        {
            Color aNewWallColor = rSettings.GetHighlightColor();
            // if the face color is bright then the deactivate color is also bright
            // -> so you can't see any deactivate selection
            if (bHideSelection && !rSettings.GetFaceColor().IsBright()
               && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright())
            {
                aNewWallColor = rSettings.GetDeactiveColor();
            }
            // set font color to highlight
            if (!bCurFontIsSel)
            {
                rRenderContext.SetTextColor(aHighlightTextColor);
                rRenderContext.SetFont(aHighlightFont);
                bCurFontIsSel = true;
            }
            aWallpaper.SetColor(aNewWallColor);
        }
        else  // no selection
        {
            if (bCurFontIsSel || rEntry.GetTextColor())
            {
                bCurFontIsSel = false;
                if (const auto & xCustomTextColor = rEntry.GetTextColor())
                    rRenderContext.SetTextColor(*xCustomTextColor);
                else
                    rRenderContext.SetTextColor(aBackupTextColor);
                rRenderContext.SetFont(aBackupFont);
            }
        }

        // draw background
        if (!(nTreeFlags & SvTreeFlags::USESEL))
        {
            // only draw the area that is used by the item
            aRectSize.setWidth( aSize.Width() );
            aRect.SetPos(aEntryPos);
            aRect.SetSize(aRectSize);
        }
        else
        {
            // draw from the current to the next tab
            if (nCurTab != 0)
                aRect.SetLeft( nTabPos );
            else
                // if we're in the 0th tab, always draw from column 0 --
                // else we get problems with centered tabs
                aRect.SetLeft( 0 );
            aRect.SetTop( nLine );
            aRect.SetBottom( nLine + nTempEntryHeight - 1 );
            if (pNextTab)
            {
                tools::Long nRight;
                nRight = GetTabPos(&rEntry, pNextTab) - 1;
                if (nRight > nMaxRight)
                    nRight = nMaxRight;
                aRect.SetRight( nRight );
            }
            else
            {
                aRect.SetRight( nMaxRight );
            }
        }
        // A custom selection that starts at a tab position > 0, do not fill
        // the background of the 0th item, else e.g. we might not be able to
        // realize tab listboxes with lines.
        if (!(nCurTab == 0 && (nTreeFlags & SvTreeFlags::USESEL) && nFirstSelTab))
        {
            Color aBackgroundColor = aWallpaper.GetColor();
            if (aBackgroundColor != COL_TRANSPARENT)
            {
                rRenderContext.SetFillColor(aBackgroundColor);
                // this case may occur for smaller horizontal resizes
                if (aRect.Left() < aRect.Right())
                {
                    rRenderContext.DrawRect(aRect);
                    if (!bImageHighlighted && bNeedsImage && bDefaultImage && bEntryHighlighted)
                    {
                        if (!aRect.GetIntersection({ aImagePos, aImageSize }).IsEmpty())
                            bImageHighlighted = true;
                    }
                }
            }
        }
        // draw item
        // center vertically
        aEntryPos.AdjustY((nTempEntryHeight - aSize.Height()) / 2 );

        rItem.Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry);

        // division line between tabs (but not if this is a separator line)
        if (!bSeparator && pNextTab && rItem.GetType() == SvLBoxItemType::String &&
            // not at the right edge of the window!
            aRect.Right() < nMaxRight)
        {
            aRect.SetLeft( aRect.Right() - SV_TAB_BORDER );
            rRenderContext.DrawRect(aRect);
        }

        rRenderContext.SetFillColor(aBackupColor);

        nCurItem++;
        nCurTab++;
    }

    if (pViewDataEntry->IsDragTarget())
    {
        rRenderContext.Push();
        rRenderContext.SetLineColor(rSettings.GetDeactiveColor());
        rRenderContext.SetFillColor(rSettings.GetDeactiveColor());

        const bool bAsTree = GetStyle() & (WB_HASLINES | WB_HASLINESATROOT);
        if (bAsTree)
        {
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nLine + nTempEntryHeight - 2), Size(nWidth, 2)));
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nLine), Size(nWidth, 2)));
        }
        else
        {
            rRenderContext.DrawRect(tools::Rectangle(Point(0, nLine), Size(nWidth, 2)));
        }

        rRenderContext.Pop();
    }

    if (bCurFontIsSel || rEntry.GetTextColor())
    {
        rRenderContext.SetTextColor(aBackupTextColor);
        rRenderContext.SetFont(aBackupFont);
    }

    if (!bNeedsImage)
        return;

    if (!bDefaultImage)
    {
        // If it's a custom image then draw what was explicitly set to use
        DrawImageFlags nStyle = DrawImageFlags::NONE;
        if (!IsEnabled())
            nStyle |= DrawImageFlags::Disable;
        rRenderContext.DrawImage(aImagePos, *pImg, nStyle);
    }
    else
    {
        bool bNativeOK = false;
        // native
        if (rRenderContext.IsNativeControlSupported(ControlType::ListNode, ControlPart::Entire))
        {
            ImplControlValue aControlValue;
            tools::Rectangle aCtrlRegion(aImagePos,  pImg->GetSizePixel());
            ControlState nState = ControlState::NONE;

            if (IsEnabled())
                nState |= ControlState::ENABLED;

            if (bExpanded)
                aControlValue.setTristateVal(ButtonValue::On); //expanded node
            else
            {
                if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() &&
                    (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)))
                {
                    aControlValue.setTristateVal( ButtonValue::DontKnow ); //don't know
                }
                else
                {
                    aControlValue.setTristateVal( ButtonValue::Off ); //collapsed node
                }
            }

            bNativeOK = rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion, nState, aControlValue, OUString());
        }
        if (!bNativeOK)
        {
            DecorationView aDecoView(&rRenderContext);
            DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
            if (!IsEnabled())
                nSymbolStyle |= DrawSymbolFlags::Disable;

            Color aCol = aBackupTextColor;
            if (bImageHighlighted)
                aCol = aHighlightTextColor;

            SymbolType eSymbol = bExpanded ? SymbolType::SPIN_DOWN : SymbolType::SPIN_RIGHT;
            aDecoView.DrawSymbol(tools::Rectangle(aImagePos, pImg->GetSizePixel()), eSymbol, aCol, nSymbolStyle);
        }
    }
}

void SvTreeListBox::DrawCustomEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const SvTreeListEntry& rEntry)
{
    aCustomRenderHdl.Call(std::tuple<vcl::RenderContext&, const tools::Rectangle&, ;const SvTreeListEntry&>(rRenderContext, rRect, rEntry));
}

Size SvTreeListBox::MeasureCustomEntry(vcl::RenderContext& rRenderContext, const SvTreeListEntry& rEntry) const
{
    return aCustomMeasureHdl.Call(std::pair<vcl::RenderContext&, const SvTreeListEntry&>(rRenderContext, rEntry));
}

tools::Rectangle SvTreeListBox::GetFocusRect(const SvTreeListEntry* pEntry, tools::Long nLine )
{
    pImpl->UpdateContextBmpWidthMax( pEntry );

    Size aSize;
    tools::Rectangle aRect;
    aRect.SetTop( nLine );
    aSize.setHeight( GetEntryHeight() );

    tools::Long nRealWidth = pImpl->GetOutputSize().Width();
    nRealWidth -= GetMapMode().GetOrigin().X();

    sal_uInt16 nCurTab;
    SvLBoxTab* pTab = GetFirstTab( SvLBoxTabFlags::SHOW_SELECTION, nCurTab );
    tools::Long nTabPos = 0;
    if( pTab )
        nTabPos = GetTabPos( pEntry, pTab );
    tools::Long nNextTabPos;
    if( pTab && nCurTab < aTabs.size() - 1 )
    {
        SvLBoxTab* pNextTab = aTabs[ nCurTab + 1 ].get();
        nNextTabPos = GetTabPos( pEntry, pNextTab );
    }
    else
    {
        nNextTabPos = nRealWidth;
        if( nTabPos > nRealWidth )
            nNextTabPos += 50;
    }

    bool bUserSelection = bool( nTreeFlags & SvTreeFlags::USESEL );
    if( !bUserSelection )
    {
        if( pTab && nCurTab < pEntry->ItemCount() )
        {
            const SvLBoxItem& rItem = pEntry->GetItem( nCurTab );
            aSize.setWidth(rItem.GetWidth(this, pEntry));
            if( !aSize.Width() )
                aSize.setWidth( 15 );
            tools::Long nX = nTabPos; //GetTabPos( pEntry, pTab );
            // alignment
            nX += pTab->CalcOffset( aSize.Width(), nNextTabPos - nTabPos );
            aRect.SetLeft( nX );
            // make sure that first and last letter aren't cut off slightly
            aRect.SetSize( aSize );
            if( aRect.Left() > 0 )
                aRect.AdjustLeft( -1 );
            aRect.AdjustRight( 1 );
        }
    }
    else
    {
        // if SelTab != 0, we have to calculate also
        if( nFocusWidth == -1 || nFirstSelTab )
        {
            SvLBoxTab* pLastTab = nullptr; // default to select whole width

            sal_uInt16 nLastTab;
            GetLastTab(SvLBoxTabFlags::SHOW_SELECTION,nLastTab);
            nLastTab++;
            if( nLastTab < aTabs.size() ) // is there another one?
                pLastTab = aTabs[ nLastTab ].get();

            aSize.setWidth( pLastTab ? pLastTab->GetPos() : 0x0fffffff );
            nFocusWidth = static_cast<short>(aSize.Width());
            if( pTab )
                nFocusWidth = nFocusWidth - static_cast<short>(nTabPos); //pTab->GetPos();
        }
        else
        {
            aSize.setWidth( nFocusWidth );
            if( pTab )
            {
                if( nCurTab )
                    aSize.AdjustWidth(nTabPos );
                else
                    aSize.AdjustWidth(pTab->GetPos() ); // Tab0 always from the leftmost position
            }
        }
        // if selection starts with 0th tab, draw from column 0 on
        if( nCurTab != 0 )
        {
            aRect.SetLeft( nTabPos );
            aSize.AdjustWidth( -nTabPos );
        }
        aRect.SetSize( aSize );
    }
    // adjust right edge because of clipping
    if( aRect.Right() >= nRealWidth )
    {
        aRect.SetRight( nRealWidth-1 );
        nFocusWidth = static_cast<short>(aRect.GetWidth());
    }
    return aRect;
}

sal_IntPtr SvTreeListBox::GetTabPos(const SvTreeListEntry* pEntry, const SvLBoxTab* pTab) const
{
    assert(pTab);
    sal_IntPtr nPos = pTab->GetPos();
    if( pTab->IsDynamic() )
    {
        sal_uInt16 nDepth = pModel->GetDepth( pEntry );
        nDepth = nDepth * static_cast<sal_uInt16>(nIndent);
        nPos += static_cast<sal_IntPtr>(nDepth);
    }
    return nPos + (pEntry->GetExtraIndent() * nIndent);
}

SvLBoxItem* SvTreeListBox::GetItem_Impl( SvTreeListEntry* pEntry, tools::Long nX,
    SvLBoxTab** ppTab )
{
    SvLBoxItem* pItemClicked = nullptr;
    sal_uInt16 nTabCount = aTabs.size();
    sal_uInt16 nItemCount = pEntry->ItemCount();
    SvLBoxTab* pTab = aTabs.front().get();
    SvLBoxItem* pItem = &pEntry->GetItem(0);
    sal_uInt16 nNextItem = 1;
    nX -= GetMapMode().GetOrigin().X();
    tools::Long nRealWidth = pImpl->GetOutputSize().Width();
    nRealWidth -= GetMapMode().GetOrigin().X();

    whiletrue )
    {
        SvLBoxTab* pNextTab=nNextItem<nTabCount ? aTabs[nNextItem].get() : nullptr;
        tools::Long nStart = GetTabPos( pEntry, pTab );

        tools::Long nNextTabPos;
        if( pNextTab )
            nNextTabPos = GetTabPos( pEntry, pNextTab );
        else
        {
            nNextTabPos = nRealWidth;
            if( nStart > nRealWidth )
                nNextTabPos += 50;
        }

        auto nItemWidth(pItem->GetWidth(this, pEntry));
        nStart += pTab->CalcOffset(nItemWidth, nNextTabPos - nStart);
        auto nLen = nItemWidth;
        if( pNextTab )
        {
            tools::Long nTabWidth = GetTabPos( pEntry, pNextTab ) - nStart;
            if( nTabWidth < nLen )
                nLen = nTabWidth;
        }

        if( nX >= nStart && nX < (nStart+nLen ) )
        {
            pItemClicked = pItem;
            if( ppTab )
            {
                *ppTab = pTab;
                break;
            }
        }
        if( nNextItem >= nItemCount || nNextItem >= nTabCount)
            break;
        pTab = aTabs[ nNextItem ].get();
        pItem = &pEntry->GetItem( nNextItem );
        nNextItem++;
    }
    return pItemClicked;
}

std::pair<tools::Long, tools::Long> SvTreeListBox::GetItemPos(SvTreeListEntry* pEntry, sal_uInt16 nTabIdx)
{
    sal_uInt16 nTabCount = aTabs.size();
    sal_uInt16 nItemCount = pEntry->ItemCount();
    if (nTabIdx >= nItemCount || nTabIdx >= nTabCount)
        return std::make_pair(-1, -1);

    SvLBoxTab* pTab = aTabs.front().get();
    SvLBoxItem* pItem = &pEntry->GetItem(nTabIdx);
    sal_uInt16 nNextItem = nTabIdx + 1;

    tools::Long nRealWidth = pImpl->GetOutputSize().Width();
    nRealWidth -= GetMapMode().GetOrigin().X();

    SvLBoxTab* pNextTab = nNextItem < nTabCount ? aTabs[nNextItem].get() : nullptr;
    tools::Long nStart = GetTabPos(pEntry, pTab);

    tools::Long nNextTabPos;
    if (pNextTab)
        nNextTabPos = GetTabPos(pEntry, pNextTab);
    else
    {
        nNextTabPos = nRealWidth;
        if (nStart > nRealWidth)
            nNextTabPos += 50;
    }

    auto nItemWidth(pItem->GetWidth(this, pEntry));
    nStart += pTab->CalcOffset(nItemWidth, nNextTabPos - nStart);
    auto nLen = nItemWidth;
    if (pNextTab)
    {
        tools::Long nTabWidth = GetTabPos(pEntry, pNextTab) - nStart;
        if (nTabWidth < nLen)
            nLen = nTabWidth;
    }
    return std::make_pair(nStart, nLen);
}

tools::Long SvTreeListBox::getPreferredDimensions(std::vector<tools::Long> &rWidths) const
{
    tools::Long nHeight = 0;
    rWidths.clear();
    SvTreeListEntry* pEntry = First();
    while (pEntry)
    {
        sal_uInt16 nCount = pEntry->ItemCount();
        sal_uInt16 nCurPos = 0;
        if (nCount > rWidths.size())
            rWidths.resize(nCount);
        while (nCurPos < nCount)
        {
            SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
            auto nWidth = rItem.GetWidth(this, pEntry);
            if (nWidth)
            {
                nWidth += SV_TAB_BORDER * 2;
                if (nWidth > rWidths[nCurPos])
                   rWidths[nCurPos] = nWidth;
            }
            ++nCurPos;
        }
        pEntry = Next( pEntry );
        nHeight += GetEntryHeight();
    }
    return nHeight;
}

Size SvTreeListBox::GetOptimalSize() const
{
    std::vector<tools::Long> aWidths;
    Size aRet(0, getPreferredDimensions(aWidths));
    for (tools::Long aWidth : aWidths)
        aRet.AdjustWidth(aWidth );

    sal_Int32 nLeftBorder(0), nTopBorder(0), nRightBorder(0), nBottomBorder(0);
    GetBorder(nLeftBorder, nTopBorder, nRightBorder, nBottomBorder);
    aRet.AdjustWidth(nLeftBorder + nRightBorder);
    aRet.AdjustHeight(nTopBorder + nBottomBorder);

    tools::Long nMinWidth = nMinWidthInChars * approximate_char_width();
    aRet.setWidth( std::max(aRet.Width(), nMinWidth) );

    if (GetStyle() & WB_VSCROLL)
        aRet.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize());

    return aRet;
}

void SvTreeListBox::SetForceMakeVisible( bool bEnable )
{
    pImpl->SetForceMakeVisible(bEnable);
}

SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,tools::Long nX,SvLBoxTab** ppTab)
{
    return GetItem_Impl( pEntry, nX, ppTab );
}

SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,tools::Long nX )
{
    SvLBoxTab* pDummyTab;
    return GetItem_Impl( pEntry, nX, &pDummyTab );
}

void SvTreeListBox::AddTab(tools::Long nTabPos, SvLBoxTabFlags nFlags )
{
    nFocusWidth = -1;
    SvLBoxTab* pTab = new SvLBoxTab( nTabPos, nFlags );
    aTabs.emplace_back( pTab );
    if( nTreeFlags & SvTreeFlags::USESEL )
    {
        sal_uInt16 nPos = aTabs.size() - 1;
        if( nPos >= nFirstSelTab && nPos <= nLastSelTab )
            pTab->nFlags |= SvLBoxTabFlags::SHOW_SELECTION;
        else
            // string items usually have to be selected -- turn this off
            // explicitly
            pTab->nFlags &= ~SvLBoxTabFlags::SHOW_SELECTION;
    }
}


SvLBoxTab* SvTreeListBox::GetFirstDynamicTab( sal_uInt16& rPos ) const
{
    sal_uInt16 nCurTab = 0;
    sal_uInt16 nTabCount = aTabs.size();
    while( nCurTab < nTabCount )
    {
        SvLBoxTab* pTab = aTabs[nCurTab].get();
        if( pTab->nFlags & SvLBoxTabFlags::DYNAMIC )
        {
            rPos = nCurTab;
            return pTab;
        }
        nCurTab++;
    }
    return nullptr;
}

SvLBoxTab* SvTreeListBox::GetFirstDynamicTab() const
{
    sal_uInt16 nDummy;
    return GetFirstDynamicTab( nDummy );
}

SvLBoxTab* SvTreeListBox::GetTab( SvTreeListEntry const * pEntry, SvLBoxItem const * pItem) const
{
    sal_uInt16 nPos = pEntry->GetPos( pItem );
    return aTabs[ nPos ].get();
}

void SvTreeListBox::ClearTabList()
{
    aTabs.clear();
}


Size SvTreeListBox::GetOutputSizePixel() const
{
    Size aSize = pImpl->GetOutputSize();
    return aSize;
}

void SvTreeListBox::NotifyScrolled()
{
    aScrolledHdl.Call( this );
}

void SvTreeListBox::ImplInvalidate( const vcl::Region* pRegion, InvalidateFlags nInvalidateFlags )
{
    if (!pImpl)
        return;
    if( nFocusWidth == -1 )
        // to make sure that the control doesn't show the wrong focus rectangle
        // after painting
        pImpl->RecalcFocusRect();
    Control::ImplInvalidate( pRegion, nInvalidateFlags );
    pImpl->Invalidate();
}

void SvTreeListBox::SetHighlightRange( sal_uInt16 nStart, sal_uInt16 nEnd)
{

    nTreeFlags |= SvTreeFlags::USESEL;
    if( nStart > nEnd )
        std::swap(nStart, nEnd);
    // select all tabs that lie within the area
    nTreeFlags |= SvTreeFlags::RECALCTABS;
    nFirstSelTab = nStart;
    nLastSelTab = nEnd;
    pImpl->RecalcFocusRect();
}

void SvTreeListBox::Command(const CommandEvent& rCEvt)
{
    if (!aPopupMenuHdl.Call(rCEvt))
        pImpl->Command(rCEvt);
    //pass at least alt press/release to parent impl
    if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
        Control::Command(rCEvt);
}

SvLBoxTab* SvTreeListBox::GetFirstTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rPos )
{
    sal_uInt16 nTabCount = aTabs.size();
    for( sal_uInt16 nPos = 0; nPos < nTabCount; nPos++ )
    {
        SvLBoxTab* pTab = aTabs[ nPos ].get();
        if( pTab->nFlags & nFlagMask )
        {
            rPos = nPos;
            return pTab;
        }
    }
    rPos = 0xffff;
    return nullptr;
}

void SvTreeListBox::GetLastTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rTabPos )
{
    sal_uInt16 nPos = static_cast<sal_uInt16>(aTabs.size());
    while( nPos )
    {
        --nPos;
        SvLBoxTab* pTab = aTabs[ nPos ].get();
        if( pTab->nFlags & nFlagMask )
        {
            rTabPos = nPos;
            return;
        }
    }
    rTabPos = 0xffff;
}

void SvTreeListBox::RequestHelp( const HelpEvent& rHEvt )
{
    if (aTooltipHdl.IsSet())
    {
        const Point pos(ScreenToOutputPixel(rHEvt.GetMousePosPixel()));
        if (SvTreeListEntry* entry = GetEntry(pos))
        {
            const OUString tooltip = aTooltipHdl.Call(entry);
            if (!tooltip.isEmpty())
            {
                const Size size(GetOutputSizePixel().Width(), GetEntryHeight());
                tools::Rectangle screenRect(OutputToScreenPixel(GetEntryPosition(entry)), size);
                Help::ShowQuickHelp(this, screenRect, tooltip);
                return;
            }
        }
    }

    if( !pImpl->RequestHelp( rHEvt ) )
        Control::RequestHelp( rHEvt );
}

sal_Int32 SvTreeListBox::DefaultCompare(const SvLBoxString* pLeftText, const SvLBoxString* pRightText)
{
    OUString aLeft = pLeftText ? pLeftText->GetText() : OUString();
    OUString aRight = pRightText ? pRightText->GetText() : OUString();
    pImpl->UpdateStringSorter();
    return pImpl->m_pStringSorter->compare(aLeft, aRight);
}

IMPL_LINK( SvTreeListBox, DefaultCompare, const SvSortData&, rData, sal_Int32 )
{
    const SvTreeListEntry* pLeft = rData.pLeft;
    const SvTreeListEntry* pRight = rData.pRight;
    const SvLBoxString* pLeftText = static_cast<const SvLBoxString*>(pLeft->GetFirstItem(SvLBoxItemType::String));
    const SvLBoxString* pRightText = static_cast<const SvLBoxString*>(pRight->GetFirstItem(SvLBoxItemType::String));
    return DefaultCompare(pLeftText, pRightText);
}

void SvTreeListBox::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
                        SvTreeListEntry* pEntry2, sal_uInt32 nPos )
{
    SolarMutexGuard aSolarGuard;

    if( nActionId == SvListAction::CLEARING )
        CancelTextEditing();

    SvListView::ModelNotification( nActionId, pEntry1, pEntry2, nPos );
    switch( nActionId )
    {
        case SvListAction::INSERTED:
        {
            SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry1->GetFirstItem( SvLBoxItemType::ContextBmp ) );
            if ( !pBmpItem )
                break;
            const Image& rBitmap1( pBmpItem->GetBitmap1() );
            const Image& rBitmap2( pBmpItem->GetBitmap2() );
            short nMaxWidth = short( std::max( rBitmap1.GetSizePixel().Width(), rBitmap2.GetSizePixel().Width() ) );
            nMaxWidth = pImpl->UpdateContextBmpWidthVector( pEntry1, nMaxWidth );
            if( nMaxWidth > nContextBmpWidthMax )
            {
                nContextBmpWidthMax = nMaxWidth;
                SetTabs();
            }
            if (get_width_request() == -1)
                queue_resize();
        }
        break;

        case SvListAction::RESORTING:
            SetUpdateMode( false );
            break;

        case SvListAction::RESORTED:
            // after a selection: show first entry and also keep the selection
            MakeVisible( pModel->First(), true );
            SetUpdateMode( true );
            break;

        case SvListAction::CLEARED:
            if( IsUpdateMode() )
                PaintImmediately();
            break;

        defaultbreak;
    }
}

SvTreeListEntry* SvTreeListBox::GetFirstEntryInView() const
{
    return GetEntry( Point() );
}

SvTreeListEntry* SvTreeListBox::GetNextEntryInView(SvTreeListEntry* pEntry ) const
{
    SvTreeListEntry* pNext = NextVisible( pEntry );
    if( pNext )
    {
        Point aPos( GetEntryPosition(pNext) );
        const Size& rSize = pImpl->GetOutputSize();
        if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() )
            return nullptr;
    }
    return pNext;
}


void SvTreeListBox::DataChanged( const DataChangedEvent& rDCEvt )
{
    if( (rDCEvt.GetType()==DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        nEntryHeight = 0;   // _together_ with true of 1. par (bFont) of InitSettings() a zero-height
                            //  forces complete recalc of heights!
        InitSettings();
        Invalidate();
    }
    else
        Control::DataChanged( rDCEvt );
}

void SvTreeListBox::StateChanged( StateChangedType eType )
{
    if( eType == StateChangedType::Enable )
        Invalidate( InvalidateFlags::Children );

    Control::StateChanged( eType );

    if ( eType == StateChangedType::Style )
        ImplInitStyle();
}

void SvTreeListBox::ApplySettings(vcl::RenderContext& rRenderContext)
{
    SetPointFont(rRenderContext, GetPointFont(*GetOutDev()));

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
    rRenderContext.SetTextFillColor();
    rRenderContext.SetBackground(rStyleSettings.GetFieldColor());

    // always try to re-create default-SvLBoxButtonData
    if (pCheckButtonData)
        pCheckButtonData->SetDefaultImages(*this);
}

void SvTreeListBox::InitSettings()
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
    vcl::Font aFont = rStyleSettings.GetFieldFont();
    SetPointFont(*GetOutDev(), aFont);
    AdjustEntryHeightAndRecalc();

    SetTextColor(rStyleSettings.GetFieldTextColor());
    SetTextFillColor();

    SetBackground(rStyleSettings.GetFieldColor());

    // always try to re-create default-SvLBoxButtonData
    if( pCheckButtonData)
        pCheckButtonData->SetDefaultImages(*this);
}

css::uno::Reference< XAccessible > SvTreeListBox::CreateAccessible()
{
    css::uno::Reference< XAccessible > xAccessible;
    css::uno::Reference<XAccessible> xAccParent = GetAccessibleParent();
    if ( xAccParent.is() )
        xAccessible = new AccessibleListBox(*this, xAccParent);

    return xAccessible;
}

void SvTreeListBox::FillAccessibleEntryStateSet( SvTreeListEntry* pEntry, sal_Int64&&nbsp;rStateSet ) const
{
    assert(pEntry && "SvTreeListBox::FillAccessibleEntryStateSet: invalid entry");

    if ( pEntry->HasChildrenOnDemand() || pEntry->HasChildren() )
    {
        rStateSet |= AccessibleStateType::EXPANDABLE;
        if ( IsExpanded( pEntry ) )
            rStateSet |= AccessibleStateType::EXPANDED;
    }

    if (nTreeFlags & SvTreeFlags::CHKBTN)
        rStateSet |= AccessibleStateType::CHECKABLE;
    if ( GetCheckButtonState( pEntry ) == SvButtonState::Checked )
        rStateSet |= AccessibleStateType::CHECKED;
    if ( IsEntryVisible( pEntry ) )
        rStateSet |= AccessibleStateType::VISIBLE;
    if ( IsSelected( pEntry ) )
        rStateSet |= AccessibleStateType::SELECTED;
    if ( IsEnabled() )
    {
        rStateSet |= AccessibleStateType::ENABLED;
        rStateSet |= AccessibleStateType::FOCUSABLE;
        rStateSet |= AccessibleStateType::SELECTABLE;
        SvViewDataEntry* pViewDataNewCur = GetViewDataEntry(pEntry);
        if (pViewDataNewCur && pViewDataNewCur->HasFocus())
            rStateSet |= AccessibleStateType::FOCUSED;
    }
}

tools::Rectangle SvTreeListBox::GetBoundingRect(const SvTreeListEntry* pEntry)
{
    Point aPos = GetEntryPosition( pEntry );
    tools::Rectangle aRect = GetFocusRect( pEntry, aPos.Y() );
    return aRect;
}

void SvTreeListBox::CallImplEventListeners(VclEventId nEvent, void* pData)
{
    CallEventListeners(nEvent, pData);
}

void SvTreeListBox::set_min_width_in_chars(sal_Int32 nChars)
{
    nMinWidthInChars = nChars;
    queue_resize();
}

bool SvTreeListBox::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "min-width-chars")
    {
        set_min_width_in_chars(rValue.toInt32());
    }
    else if (rKey == "enable-tree-lines")
    {
        auto nStyle = GetStyle();
        nStyle &= ~(WB_HASLINES | WB_HASLINESATROOT);
        if (toBool(rValue))
            nStyle |= (WB_HASLINES | WB_HASLINESATROOT);
        SetStyle(nStyle);
    }
    else if (rKey == "show-expanders")
    {
        auto nStyle = GetStyle();
        nStyle &= ~(WB_HASBUTTONS | WB_HASBUTTONSATROOT);
        if (toBool(rValue))
            nStyle |= (WB_HASBUTTONS | WB_HASBUTTONSATROOT);
        SetStyle(nStyle);
    }
    else if (rKey == "enable-search")
    {
        SetQuickSearch(toBool(rValue));
    }
    else if (rKey == "activate-on-single-click")
    {
        SetActivateOnSingleClick(toBool(rValue));
    }
    else if (rKey == "hover-selection")
    {
        SetHoverSelection(toBool(rValue));
    }
    else if (rKey == "reorderable")
    {
        if (toBool(rValue))
            SetDragDropMode(DragDropMode::CTRL_MOVE | DragDropMode::ENABLE_TOP);
    }
    else if (rKey == "text-column")
    {
        SetTextColumnEnabled(toBool(rValue));
    }
    else
        return Control::set_property(rKey, rValue);
    return true;
}

void SvTreeListBox::EnableRTL(bool bEnable)
{
    Control::EnableRTL(bEnable);
    pImpl->m_aHorSBar->EnableRTL(bEnable);
    pImpl->m_aVerSBar->EnableRTL(bEnable);
    pImpl->m_aScrBarBox->EnableRTL(bEnable);
}

FactoryFunction SvTreeListBox::GetUITestFactory() const
{
    return TreeListUIObject::create;
}

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

Messung V0.5 in Prozent
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.46 Sekunden  (vorverarbeitet am  2026-05-05) ¤

*© 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.