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

Quelle  imp_listbox.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <memory>

#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/event.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/naturalsort.hxx>
#include <vcl/vcllayout.hxx>
#include <vcl/toolkit/lstbox.hxx>
#include <vcl/toolkit/scrbar.hxx>

#include <listbox.hxx>
#include <svdata.hxx>
#include <window.h>

#include <com/sun/star/accessibility/AccessibleRole.hpp>

#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <comphelper/string.hxx>
#include <comphelper/processfactory.hxx>

#include <limits>

#define MULTILINE_ENTRY_DRAW_FLAGS ( DrawTextFlags::WordBreak | DrawTextFlags::MultiLine | DrawTextFlags::VCenter )

using namespace ::com::sun::star;

constexpr tools::Long gnBorder = 1;

void ImplInitDropDownButton( PushButton* pButton )
{
    pButton->SetSymbol( SymbolType::SPIN_DOWN );

    if ( pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
            && ! pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
        pButton->SetBackground();
}

ImplEntryList::ImplEntryList( vcl::Window* pWindow )
{
    mpWindow = pWindow;
    mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
    mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
    mnImages = 0;
    mbCallSelectionChangedHdl = true;

    mnMRUCount = 0;
    mnMaxMRUCount = 0;
}

ImplEntryList::~ImplEntryList()
{
    Clear();
}

void ImplEntryList::Clear()
{
    mnImages = 0;
    maEntries.clear();
}

void ImplEntryList::dispose()
{
    Clear();
    mpWindow.reset();
}

void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
{
    if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
    {
        std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+nPos;

        if ( ( (*iter)->mbIsSelected != bSelect ) &&
           ( ( (*iter)->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE  ) )
        {
            (*iter)->mbIsSelected = bSelect;
            if ( mbCallSelectionChangedHdl )
                maSelectionChangedHdl.Call( nPos );
        }
    }
}

namespace
{
    comphelper::string::NaturalStringSorter& GetSorter()
    {
        static comphelper::string::NaturalStringSorter gSorter(
                    ::comphelper::getProcessComponentContext(),
                    Application::GetSettings().GetLanguageTag().getLocale());
        return gSorter;
    };
}

namespace vcl
{
    sal_Int32 NaturalSortCompare(const OUString &rA, const OUString &rB)
    {
        const comphelper::string::NaturalStringSorter &rSorter = GetSorter();
        return rSorter.compare(rA, rB);
    }
}

sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
{
    assert(nPos >= 0);
    assert(maEntries.size() < LISTBOX_MAX_ENTRIES);

    if ( !!pNewEntry->maImage )
        mnImages++;

    sal_Int32 insPos = 0;
    const sal_Int32 nEntriesSize = static_cast<sal_Int32>(maEntries.size());

    if ( !bSort || maEntries.empty())
    {
        if (0 <= nPos && nPos < nEntriesSize)
        {
            insPos = nPos;
            maEntries.insert( maEntries.begin() + nPos, std::unique_ptr<ImplEntryType>(pNewEntry) );
        }
        else
        {
            insPos = nEntriesSize;
            maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
        }
    }
    else
    {
        const comphelper::string::NaturalStringSorter &rSorter = GetSorter();

        const OUString& rStr = pNewEntry->maStr;

        ImplEntryType* pTemp = GetEntry( nEntriesSize-1 );

        try
        {
            sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);

            // fast insert for sorted data
            if ( nComp >= 0 )
            {
                insPos = nEntriesSize;
                maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
            }
            else
            {
                pTemp = GetEntry( mnMRUCount );

                nComp = rSorter.compare(rStr, pTemp->maStr);
                if ( nComp <= 0 )
                {
                    insPos = 0;
                    maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
                }
                else
                {
                    sal_uLong nLow = mnMRUCount;
                    sal_uLong nHigh = maEntries.size()-1;
                    sal_Int32 nMid;

                    // binary search
                    do
                    {
                        nMid = static_cast<sal_Int32>((nLow + nHigh) / 2);
                        pTemp = GetEntry( nMid );

                        nComp = rSorter.compare(rStr, pTemp->maStr);

                        if ( nComp < 0 )
                            nHigh = nMid-1;
                        else
                        {
                            if ( nComp > 0 )
                                nLow = nMid + 1;
                            else
                                break;
                        }
                    }
                    while ( nLow <= nHigh );

                    if ( nComp >= 0 )
                        nMid++;

                    insPos = nMid;
                    maEntries.insert(maEntries.begin()+nMid, std::unique_ptr<ImplEntryType>(pNewEntry));
                }
            }
        }
        catch (uno::RuntimeException& )
        {
            // XXX this is arguable, if the exception occurred because pNewEntry is
            // garbage you wouldn't insert it. If the exception occurred because the
            // Collator implementation is garbage then give the user a chance to see
            // his stuff
            insPos = 0;
            maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
        }

    }

    return insPos;
}

void ImplEntryList::RemoveEntry( sal_Int32 nPos )
{
    if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
    {
        std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+ nPos;

        if ( !!(*iter)->maImage )
            mnImages--;

        maEntries.erase(iter);
    }
}

sal_Int32 ImplEntryList::FindEntry( std::u16string_view rString, bool bSearchMRUArea ) const
{
    const sal_Int32 nEntries = static_cast<sal_Int32>(maEntries.size());
    for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
    {
        OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n]->maStr ) );
        if ( aComp == rString )
            return n;
    }
    return LISTBOX_ENTRY_NOTFOUND;
}

sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStartbool bLazy ) const
{
    sal_Int32  nPos = LISTBOX_ENTRY_NOTFOUND;
    sal_Int32  nEntryCount = GetEntryCount();

    const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
    for ( sal_Int32 n = nStart; n < nEntryCount; )
    {
        ImplEntryType* pImplEntry = GetEntry( n );
        bool bMatch;
        if ( bLazy  )
        {
            bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
        }
        else
        {
            bMatch = pImplEntry->maStr.startsWith(rStr);
        }
        if ( bMatch )
        {
            nPos = n;
            break;
        }

        n++;
    }

    return nPos;
}

tools::Long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex ) const
{
    tools::Long nHeight = 0;
    sal_Int32 nStart = std::min(i_nEndIndex, i_nBeginIndex);
    sal_Int32 nStop  = std::max(i_nEndIndex, i_nBeginIndex);
    sal_Int32 nEntryCount = GetEntryCount();
    if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
    {
        // sanity check
        if( nStop > nEntryCount-1 )
            nStop = nEntryCount-1;
        if (nStart < 0)
            nStart = 0;
        else if( nStart > nEntryCount-1 )
            nStart = nEntryCount-1;

        sal_Int32 nIndex = nStart;
        while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
        {
            tools::Long nPosHeight = GetEntryPtr( nIndex )->getHeightWithMargin();
            if (nHeight > ::std::numeric_limits<tools::Long>::max() - nPosHeight)
            {
                SAL_WARN( "vcl""ImplEntryList::GetAddedHeight: truncated");
                break;
            }
            nHeight += nPosHeight;
            nIndex++;
        }
    }
    else
        nHeight = 0;
    return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
}

tools::Long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
{
    ImplEntryType* pImplEntry = GetEntry( nPos );
    return pImplEntry ? pImplEntry->getHeightWithMargin() : 0;
}

OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
{
    OUString aEntryText;
    ImplEntryType* pImplEntry = GetEntry( nPos );
    if ( pImplEntry )
        aEntryText = pImplEntry->maStr;
    return aEntryText;
}

bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
{
    bool bImage = false;
    ImplEntryType* pImplEntry = GetEntry( nPos );
    if ( pImplEntry )
        bImage = !!pImplEntry->maImage;
    return bImage;
}

Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
{
    Image aImage;
    ImplEntryType* pImplEntry = GetEntry( nPos );
    if ( pImplEntry )
        aImage = pImplEntry->maImage;
    return aImage;
}

void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
{
    ImplEntryType* pImplEntry = GetEntry( nPos );
    if ( pImplEntry )
        pImplEntry->mpUserData = pNewData;
}

void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
{
    ImplEntryType* pImplEntry = GetEntry( nPos );
    return pImplEntry ? pImplEntry->mpUserData : nullptr;
}

void ImplEntryList::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
{
    ImplEntryType* pImplEntry = GetEntry( nPos );
    if ( pImplEntry )
        pImplEntry->mnFlags = nFlags;
}

sal_Int32 ImplEntryList::GetSelectedEntryCount() const
{
    sal_Int32 nSelCount = 0;
    for ( sal_Int32 n = GetEntryCount(); n; )
    {
        ImplEntryType* pImplEntry = GetEntry( --n );
        if ( pImplEntry->mbIsSelected )
            nSelCount++;
    }
    return nSelCount;
}

OUString ImplEntryList::GetSelectedEntry( sal_Int32 nIndex ) const
{
    return GetEntryText( GetSelectedEntryPos( nIndex ) );
}

sal_Int32 ImplEntryList::GetSelectedEntryPos( sal_Int32 nIndex ) const
{
    sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
    sal_Int32 nSel = 0;
    sal_Int32 nEntryCount = GetEntryCount();

    for ( sal_Int32 n = 0; n < nEntryCount; n++ )
    {
        ImplEntryType* pImplEntry = GetEntry( n );
        if ( pImplEntry->mbIsSelected )
        {
            if ( nSel == nIndex )
            {
                nSelEntryPos = n;
                break;
            }
            nSel++;
        }
    }

    return nSelEntryPos;
}

bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
{
    ImplEntryType* pImplEntry = GetEntry( nIndex );
    return pImplEntry && pImplEntry->mbIsSelected;
}

bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
{
    ImplEntryType* pImplEntry = GetEntry( nPos );
    return pImplEntry == nullptr || ((pImplEntry->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE);
}

sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ ) const
{
    if( IsEntrySelectable( nPos ) )
        return nPos;

    if( bForward )
    {
        for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
        {
            if( IsEntrySelectable( nPos ) )
                return nPos;
        }
    }
    else
    {
        while( nPos )
        {
            nPos--;
            if( IsEntrySelectable( nPos ) )
                return nPos;
        }
    }

    return LISTBOX_ENTRY_NOTFOUND;
}

ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
    Control( pParent, 0 ),
    maEntryList( this ),
    maQuickSelectionEngine( *this )
{

    mnTop               = 0;
    mnLeft              = 0;
    mnSelectModifier    = 0;
    mnUserDrawEntry     = LISTBOX_ENTRY_NOTFOUND;
    mbTrack             = false;
    mbTravelSelect      = false;
    mbTrackingSelect    = false;
    mbSelectionChanged  = false;
    mbMouseMoveSelect   = false;
    mbMulti             = false;
    mbGrabFocus         = false;
    mbUserDrawEnabled   = false;
    mbInUserDraw        = false;
    mbReadOnly          = false;
    mbHasFocusRect      = false;
    mbRight             = ( nWinStyle & WB_RIGHT );
    mbCenter            = ( nWinStyle & WB_CENTER );
    mbSimpleMode        = ( nWinStyle & WB_SIMPLEMODE );
    mbSort              = ( nWinStyle & WB_SORT );
    mbIsDropdown        = ( nWinStyle & WB_DROPDOWN );
    mbEdgeBlending      = false;

    mnCurrentPos            = LISTBOX_ENTRY_NOTFOUND;
    mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;

    GetOutDev()->SetLineColor();
    SetTextFillColor();
    SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );

    ApplySettings(*GetOutDev());
    ImplCalcMetrics();
}

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

void ImplListBoxWindow::dispose()
{
    maEntryList.dispose();
    Control::dispose();
}

void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
    ApplyControlForeground(rRenderContext, rStyleSettings.GetListBoxWindowTextColor());

    if (IsControlBackground())
        rRenderContext.SetBackground(GetControlBackground());
    else
        rRenderContext.SetBackground(rStyleSettings.GetListBoxWindowBackgroundColor());
}

void ImplListBoxWindow::ImplCalcMetrics()
{
    mnMaxWidth      = 0;
    mnMaxTxtWidth   = 0;
    mnMaxImgWidth   = 0;
    mnMaxImgTxtWidth= 0;
    mnMaxImgHeight  = 0;

    mnTextHeight = static_cast<sal_uInt16>(GetTextHeight());
    mnMaxTxtHeight = mnTextHeight + gnBorder;
    mnMaxHeight = mnMaxTxtHeight;

    if ( maUserItemSize.Height() > mnMaxHeight )
        mnMaxHeight = static_cast<sal_uInt16>(maUserItemSize.Height());
    if ( maUserItemSize.Width() > mnMaxWidth )
        mnMaxWidth= static_cast<sal_uInt16>(maUserItemSize.Width());

    for ( sal_Int32 n = maEntryList.GetEntryCount(); n; )
    {
        ImplEntryType* pEntry = maEntryList.GetMutableEntryPtr( --n );
        ImplUpdateEntryMetrics( *pEntry );
    }

    if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
    {
        Size aSz( GetOutputSizePixel().Width(), maEntryList.GetEntryPtr( mnCurrentPos )->getHeightWithMargin() );
        maFocusRect.SetSize( aSz );
    }
}

void ImplListBoxWindow::Clear()
{
    maEntryList.Clear();

    mnMaxHeight     = mnMaxTxtHeight;
    mnMaxWidth      = 0;
    mnMaxTxtWidth   = 0;
    mnMaxImgTxtWidth= 0;
    mnMaxImgWidth   = 0;
    mnMaxImgHeight  = 0;
    mnTop           = 0;
    mnLeft          = 0;
    ImplClearLayoutData();

    mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
    maQuickSelectionEngine.Reset();

    Invalidate();
}

void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
{
    ImplClearLayoutData();
    maUserItemSize = rSz;
    ImplCalcMetrics();
}

namespace {

struct ImplEntryMetrics
{
    bool    bText;
    bool    bImage;
    tools::Long    nEntryWidth;
    tools::Long    nEntryHeight;
    tools::Long    nTextWidth;
    tools::Long    nImgWidth;
    tools::Long    nImgHeight;
};

}

tools::Long ImplEntryType::getHeightWithMargin() const
{
    return mnHeight + ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
}

SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const OutputDevice* pOutputDevice)
{
    if (maStrGlyphs.IsValid())
        // Use pre-calculated result.
        return &maStrGlyphs;

    std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
        maStr, 0, maStr.getLength(), Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
    if (!pLayout)
        return nullptr;

    // Remember the calculation result.
    maStrGlyphs = pLayout->GetGlyphs();

    return &maStrGlyphs;
}

void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
{
    const bool bIsUserDrawEnabled = IsUserDrawEnabled();

    ImplEntryMetrics aMetrics;
    aMetrics.bText = !rEntry.maStr.isEmpty();
    aMetrics.bImage = !!rEntry.maImage;
    aMetrics.nEntryWidth = 0;
    aMetrics.nEntryHeight = 0;
    aMetrics.nTextWidth = 0;
    aMetrics.nImgWidth = 0;
    aMetrics.nImgHeight = 0;

    if (aMetrics.bText && !bIsUserDrawEnabled)
    {
        if( rEntry.mnFlags & ListBoxEntryFlags::MultiLine )
        {
            // multiline case
            Size aCurSize( PixelToLogic( GetSizePixel() ) );
            // set the current size to a large number
            // GetTextRect should shrink it to the actual size
            aCurSize.setHeight( 0x7fffff );
            tools::Rectangle aTextRect( Point( 0, 0 ), aCurSize );
            aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
            aMetrics.nTextWidth = aTextRect.GetWidth();
            if( aMetrics.nTextWidth > mnMaxTxtWidth )
                mnMaxTxtWidth = aMetrics.nTextWidth;
            aMetrics.nEntryWidth = mnMaxTxtWidth;
            aMetrics.nEntryHeight = aTextRect.GetHeight() + gnBorder;
        }
        else
        {
            // normal single line case
            const SalLayoutGlyphs* pGlyphs = rEntry.GetTextGlyphs(GetOutDev());
            aMetrics.nTextWidth
                = static_cast<sal_uInt16>(GetTextWidth(rEntry.maStr, 0, -1, nullptr, pGlyphs));
            if( aMetrics.nTextWidth > mnMaxTxtWidth )
                mnMaxTxtWidth = aMetrics.nTextWidth;
            aMetrics.nEntryWidth = mnMaxTxtWidth;
            aMetrics.nEntryHeight = mnTextHeight + gnBorder;
        }
    }
    if ( aMetrics.bImage )
    {
        Size aImgSz = rEntry.maImage.GetSizePixel();
        aMetrics.nImgWidth  = static_cast<sal_uInt16>(CalcZoom( aImgSz.Width() ));
        aMetrics.nImgHeight = static_cast<sal_uInt16>(CalcZoom( aImgSz.Height() ));

        if( aMetrics.nImgWidth > mnMaxImgWidth )
            mnMaxImgWidth = aMetrics.nImgWidth;
        if( aMetrics.nImgHeight > mnMaxImgHeight )
            mnMaxImgHeight = aMetrics.nImgHeight;

        mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
        aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );

    }

    if (bIsUserDrawEnabled || aMetrics.bImage)
    {
        aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
        if (!bIsUserDrawEnabled && aMetrics.bText)
            aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
        aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
                                     aMetrics.nEntryHeight );
    }

    if (!aMetrics.bText && !aMetrics.bImage && !bIsUserDrawEnabled)
    {
        // entries which have no (aka an empty) text, and no image,
        // and are not user-drawn, should be shown nonetheless
        aMetrics.nEntryHeight = mnTextHeight + gnBorder;
    }

    if ( aMetrics.nEntryWidth > mnMaxWidth )
        mnMaxWidth = aMetrics.nEntryWidth;
    if ( aMetrics.nEntryHeight > mnMaxHeight )
        mnMaxHeight = aMetrics.nEntryHeight;

    rEntry.mnHeight = aMetrics.nEntryHeight;
}

void ImplListBoxWindow::ImplCallSelect()
{
    if ( !IsTravelSelect() && GetEntryList().GetMaxMRUCount() )
    {
        // Insert the selected entry as MRU, if not already first MRU
        sal_Int32 nSelected = GetEntryList().GetSelectedEntryPos( 0 );
        sal_Int32 nMRUCount = GetEntryList().GetMRUCount();
        OUString aSelected = GetEntryList().GetEntryText( nSelected );
        sal_Int32 nFirstMatchingEntryPos = GetEntryList().FindEntry( aSelected, true );
        if ( nFirstMatchingEntryPos || !nMRUCount )
        {
            bool bSelectNewEntry = false;
            if ( nFirstMatchingEntryPos < nMRUCount )
            {
                RemoveEntry( nFirstMatchingEntryPos );
                nMRUCount--;
                if ( nFirstMatchingEntryPos == nSelected )
                    bSelectNewEntry = true;
            }
            else if ( nMRUCount == GetEntryList().GetMaxMRUCount() )
            {
                RemoveEntry( nMRUCount - 1 );
                nMRUCount--;
            }

            ImplClearLayoutData();

            ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
            pNewEntry->mbIsSelected = bSelectNewEntry;
            GetEntryList().InsertEntry( 0, pNewEntry, false );
            ImplUpdateEntryMetrics( *pNewEntry );
            GetEntryList().SetMRUCount( ++nMRUCount );
            SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
            maMRUChangedHdl.Call( nullptr );
        }
    }

    maSelectHdl.Call( nullptr );
    mbSelectionChanged = false;
}

sal_Int32 ImplListBoxWindow::InsertEntry(sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort)
{
    assert(nPos >= 0);
    assert(maEntryList.GetEntryCount() < LISTBOX_MAX_ENTRIES);

    ImplClearLayoutData();
    sal_Int32 nNewPos = maEntryList.InsertEntry( nPos, pNewEntry, bSort );

    if( GetStyle() & WB_WORDBREAK )
        pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;

    ImplUpdateEntryMetrics( *pNewEntry );
    return nNewPos;
}

sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
{
    return InsertEntry(nPos, pNewEntry, mbSort);
}

void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
{
    ImplClearLayoutData();
    maEntryList.RemoveEntry( nPos );
    if( mnCurrentPos >= maEntryList.GetEntryCount() )
        mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
    ImplCalcMetrics();
}

void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
{
    maEntryList.SetEntryFlags( nPos, nFlags );
    ImplEntryType* pEntry = maEntryList.GetMutableEntryPtr( nPos );
    if( pEntry )
        ImplUpdateEntryMetrics( *pEntry );
}

void ImplListBoxWindow::ImplShowFocusRect()
{
    if ( mbHasFocusRect )
        HideFocus();
    ShowFocus( maFocusRect );
    mbHasFocusRect = true;
}

void ImplListBoxWindow::ImplHideFocusRect()
{
    if ( mbHasFocusRect )
    {
        HideFocus();
        mbHasFocusRect = false;
    }
}

sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
{
    tools::Long nY = gnBorder;

    sal_Int32 nSelect = mnTop;
    const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nSelect );
    while (pEntry)
    {
        tools::Long nEntryHeight = pEntry->getHeightWithMargin();
        if (rPoint.Y() <= nEntryHeight + nY)
            break;
        nY += nEntryHeight;
        pEntry = maEntryList.GetEntryPtr( ++nSelect );
    }
    if( pEntry == nullptr )
        nSelect = LISTBOX_ENTRY_NOTFOUND;

    return nSelect;
}

bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
{
    bool bRet = false;

    if( i_nEntry >= mnTop )
    {
        if( maEntryList.GetAddedHeight( i_nEntry, mnTop ) <
            PixelToLogic( GetSizePixel() ).Height() )
        {
            bRet = true;
        }
    }

    return bRet;
}

tools::Long ImplListBoxWindow::GetEntryHeightWithMargin() const
{
    tools::Long nMargin = ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
    return mnMaxHeight + nMargin;
}

sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
{
    sal_Int32 nPos = mnTop;
    tools::Long nWindowHeight = GetSizePixel().Height();
    sal_Int32 nCount = maEntryList.GetEntryCount();
    tools::Long nDiff;
    for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = maEntryList.GetAddedHeight( nPos, mnTop ) )
        nPos++;

    if( nDiff > nWindowHeight && nPos > mnTop )
        nPos--;

    if( nPos >= nCount )
        nPos = nCount-1;

    return nPos;
}

void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    mbMouseMoveSelect = false;  // only till the first MouseButtonDown
    maQuickSelectionEngine.Reset();

    if ( !IsReadOnly() )
    {
        if( rMEvt.GetClicks() == 1 )
        {
            sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
            if( nSelect != LISTBOX_ENTRY_NOTFOUND )
            {
                if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
                    mnTrackingSaveSelection = GetEntryList().GetSelectedEntryPos( 0 );
                else
                    mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;

                mnCurrentPos = nSelect;
                mbTrackingSelect = true;
                bool bCurPosChange = (mnCurrentPos != nSelect);
                (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
                mbTrackingSelect = false;
                if ( mbGrabFocus )
                    GrabFocus();

                StartTracking( StartTrackingFlags::ScrollRepeat );
            }
        }
        if( rMEvt.GetClicks() == 2 )
        {
            maDoubleClickHdl.Call( this );
        }
    }
    else // if ( mbGrabFocus )
    {
        GrabFocus();
    }
}

void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
{
    if (rMEvt.IsLeaveWindow() || mbMulti || !IsMouseMoveSelect() || !maEntryList.GetEntryCount())
        return;

    tools::Rectangle aRect( Point(), GetOutputSizePixel() );
    if( !aRect.Contains( rMEvt.GetPosPixel() ) )
        return;

    // prevent initial mouse move event from selecting an entry when the listbox is started
    if (mnLastPosPixel == Point())
    {
        mnLastPosPixel = rMEvt.GetPosPixel();
        return;
    }
    if (mnLastPosPixel == rMEvt.GetPosPixel())
        return;

    if ( IsMouseMoveSelect() )
    {
        sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
        if( nSelect == LISTBOX_ENTRY_NOTFOUND )
            nSelect = maEntryList.GetEntryCount() - 1;
        nSelect = std::min( nSelect, GetLastVisibleEntry() );
        nSelect = std::min( nSelect, static_cast<sal_Int32>( maEntryList.GetEntryCount() - 1 ) );
        // Select only visible Entries with MouseMove, otherwise Tracking...
        if ( IsVisible( nSelect ) &&
            maEntryList.IsEntrySelectable( nSelect ) &&
            ( ( nSelect != mnCurrentPos ) || !GetEntryList().GetSelectedEntryCount() || ( nSelect != GetEntryList().GetSelectedEntryPos( 0 ) ) ) )
        {
            mbTrackingSelect = true;
            if ( SelectEntries( nSelect, LET_TRACKING ) )
            {
                // When list box selection change by mouse move, notify
                // VclEventId::ListboxSelect vcl event.
                maListItemSelectHdl.Call(nullptr);
            }
            mbTrackingSelect = false;
        }
    }

    // if the DD button was pressed and someone moved into the ListBox
    // with the mouse button pressed...
    if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
    {
        if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
            mnTrackingSaveSelection = GetEntryList().GetSelectedEntryPos( 0 );
        else
            mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;

        StartTracking( StartTrackingFlags::ScrollRepeat );
    }
}

void ImplListBoxWindow::DeselectAll()
{
    while ( GetEntryList().GetSelectedEntryCount() )
    {
        sal_Int32 nS = GetEntryList().GetSelectedEntryPos( 0 );
        SelectEntry( nS, false );
    }
}

void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
{
    if( (maEntryList.IsEntryPosSelected( nPos ) == bSelect) || !maEntryList.IsEntrySelectable( nPos ) )
        return;

    ImplHideFocusRect();
    if( bSelect )
    {
        if( !mbMulti )
        {
            // deselect the selected entry
            sal_Int32 nDeselect = GetEntryList().GetSelectedEntryPos( 0 );
            if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
            {
                //SelectEntryPos( nDeselect, false );
                GetEntryList().SelectEntry( nDeselect, false );
                if (IsUpdateMode() && IsReallyVisible())
                    Invalidate();
            }
        }
        maEntryList.SelectEntry( nPos, true );
        mnCurrentPos = nPos;
        if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
        {
            Invalidate();
            if ( !IsVisible( nPos ) )
            {
                ImplClearLayoutData();
                sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
                if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
                {
                    Resize();
                    ShowProminentEntry( nPos );
                }
                else
                {
                    ShowProminentEntry( nPos );
                }
            }
        }
    }
    else
    {
        maEntryList.SelectEntry( nPos, false );
        Invalidate();
    }
    mbSelectionChanged = true;
}

bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
{
    bool bSelectionChanged = false;

    if( IsEnabled() && maEntryList.IsEntrySelectable( nSelect ) )
    {
        bool bFocusChanged = false;

        // here (Single-ListBox) only one entry can be deselected
        if( !mbMulti )
        {
            sal_Int32 nDeselect = maEntryList.GetSelectedEntryPos( 0 );
            if( nSelect != nDeselect )
            {
                SelectEntry( nSelect, true );
                maEntryList.SetLastSelected( nSelect );
                bFocusChanged = true;
                bSelectionChanged = true;
            }
        }
        // MultiListBox without Modifier
        else if( mbSimpleMode && !bCtrl && !bShift )
        {
            sal_Int32 nEntryCount = maEntryList.GetEntryCount();
            for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
            {
                bool bSelect = nPos == nSelect;
                if ( maEntryList.IsEntryPosSelected( nPos ) != bSelect )
                {
                    SelectEntry( nPos, bSelect );
                    bFocusChanged = true;
                    bSelectionChanged = true;
                }
            }
            maEntryList.SetLastSelected( nSelect );
            maEntryList.SetSelectionAnchor( nSelect );
        }
        // MultiListBox only with CTRL/SHIFT or not in SimpleMode
        else if( ( !mbSimpleMode /* && !bShift */ ) || ( mbSimpleMode && ( bCtrl || bShift ) ) )
        {
            // Space for selection change
            if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
            {
                bool bSelect = !maEntryList.IsEntryPosSelected( nSelect );
                SelectEntry( nSelect, bSelect );
                maEntryList.SetLastSelected( nSelect );
                maEntryList.SetSelectionAnchor( nSelect );
                if ( !maEntryList.IsEntryPosSelected( nSelect ) )
                    maEntryList.SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
                bFocusChanged = true;
                bSelectionChanged = true;
            }
            else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
                     ( bShift && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
            {
                mnCurrentPos = nSelect;
                bFocusChanged = true;

                sal_Int32 nAnchor = maEntryList.GetSelectionAnchor();
                if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && maEntryList.GetSelectedEntryCount() )
                {
                    nAnchor = maEntryList.GetSelectedEntryPos( maEntryList.GetSelectedEntryCount() - 1 );
                }
                if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
                {
                    // All entries from Anchor to nSelect have to be selected
                    sal_Int32 nStart = std::min( nSelect, nAnchor );
                    sal_Int32 nEnd = std::max( nSelect, nAnchor );
                    for ( sal_Int32 n = nStart; n <= nEnd; n++ )
                    {
                        if ( !maEntryList.IsEntryPosSelected( n ) )
                        {
                            SelectEntry( n, true );
                            bSelectionChanged = true;
                        }
                    }

                    // if appropriate some more has to be deselected...
                    sal_Int32 nLast = maEntryList.GetLastSelected();
                    if ( nLast != LISTBOX_ENTRY_NOTFOUND )
                    {
                        if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
                        {
                            for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
                            {
                                if ( maEntryList.IsEntryPosSelected( n ) )
                                {
                                    SelectEntry( n, false );
                                    bSelectionChanged = true;
                                }
                            }
                        }
                        else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
                        {
                            for ( sal_Int32 n = nLast; n < nSelect; n++ )
                            {
                                if ( maEntryList.IsEntryPosSelected( n ) )
                                {
                                    SelectEntry( n, false );
                                    bSelectionChanged = true;
                                }
                            }
                        }
                    }
                    maEntryList.SetLastSelected( nSelect );
                }
            }
            else if( eLET != LET_TRACKING )
            {
                ImplHideFocusRect();
                Invalidate();
                bFocusChanged = true;
            }
        }
        else if( bShift )
        {
            bFocusChanged = true;
        }

        if( bSelectionChanged )
            mbSelectionChanged = true;

        if( bFocusChanged )
        {
            tools::Long nHeightDiff = maEntryList.GetAddedHeight( nSelect, mnTop );
            maFocusRect.SetPos( Point( 0, nHeightDiff ) );
            Size aSz( maFocusRect.GetWidth(),
                      maEntryList.GetEntryHeight( nSelect ) );
            maFocusRect.SetSize( aSz );
            if( HasFocus() )
                ImplShowFocusRect();
            if (bSelectPosChange)
            {
                maFocusHdl.Call(nSelect);
            }
        }
        ImplClearLayoutData();
    }
    return bSelectionChanged;
}

void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
{
    tools::Rectangle aRect( Point(), GetOutputSizePixel() );
    bool bInside = aRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() );

    if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
    {
        if ( bInside && !rTEvt.IsTrackingCanceled() )
        {
            mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
            ImplCallSelect();
        }
        else
        {
            maCancelHdl.Call( nullptr );
            if ( !mbMulti )
            {
                mbTrackingSelect = true;
                SelectEntry( mnTrackingSaveSelection, true );
                mbTrackingSelect = false;
                if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
                {
                    tools::Long nHeightDiff = maEntryList.GetAddedHeight( mnCurrentPos, mnTop );
                    maFocusRect.SetPos( Point( 0, nHeightDiff ) );
                    Size aSz( maFocusRect.GetWidth(),
                              maEntryList.GetEntryHeight( mnCurrentPos ) );
                    maFocusRect.SetSize( aSz );
                    ImplShowFocusRect();
                }
            }
        }

        mbTrack = false;
    }
    else
    {
        bool bTrackOrQuickClick = mbTrack;
        if( !mbTrack )
        {
            if ( bInside )
            {
                mbTrack = true;
            }

            // this case only happens, if the mouse button is pressed very briefly
            if( rTEvt.IsTrackingEnded() && mbTrack )
            {
                bTrackOrQuickClick = true;
                mbTrack = false;
            }
        }

        if( bTrackOrQuickClick )
        {
            MouseEvent aMEvt = rTEvt.GetMouseEvent();
            Point aPt( aMEvt.GetPosPixel() );
            bool bShift = aMEvt.IsShift();
            bool bCtrl  = aMEvt.IsMod1();

            sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
            if( aPt.Y() < 0 )
            {
                if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
                    if( nSelect < mnTop )
                        SetTopEntry( mnTop-1 );
                }
            }
            else if( aPt.Y() > GetOutputSizePixel().Height() )
            {
                if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = std::min(  static_cast<sal_Int32>(mnCurrentPos+1), static_cast<sal_Int32>(maEntryList.GetEntryCount()-1) );
                    if( nSelect >= GetLastVisibleEntry() )
                        SetTopEntry( mnTop+1 );
                }
            }
            else
            {
                nSelect = static_cast<sal_Int32>( ( aPt.Y() + gnBorder ) / mnMaxHeight ) + mnTop;
                nSelect = std::min( nSelect, GetLastVisibleEntry() );
                nSelect = std::min( nSelect, static_cast<sal_Int32>( maEntryList.GetEntryCount() - 1 ) );
            }

            if ( bInside )
            {
                if ( ( nSelect != mnCurrentPos ) || !GetEntryList().GetSelectedEntryCount() )
                {
                    mbTrackingSelect = true;
                    SelectEntries(nSelect, LET_TRACKING, bShift, bCtrl);
                    mbTrackingSelect = false;
                }
            }
            else
            {
                if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
                {
                    mbTrackingSelect = true;
                    SelectEntry( GetEntryList().GetSelectedEntryPos( 0 ), false );
                    mbTrackingSelect = false;
                }
            }
            mnCurrentPos = nSelect;
            if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
            {
                ImplHideFocusRect();
            }
            else
            {
                tools::Long nHeightDiff = maEntryList.GetAddedHeight( mnCurrentPos, mnTop );
                maFocusRect.SetPos( Point( 0, nHeightDiff ) );
                Size aSz( maFocusRect.GetWidth(), maEntryList.GetEntryHeight( mnCurrentPos ) );
                maFocusRect.SetSize( aSz );
                ImplShowFocusRect();
            }
        }
    }
}

void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
{
    if( !ProcessKeyInput( rKEvt ) )
        Control::KeyInput( rKEvt );
}

bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
{
    // entry to be selected
    sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
    LB_EVENT_TYPE eLET = LET_KEYMOVE;

    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();

    bool bShift = aKeyCode.IsShift();
    bool bCtrl  = aKeyCode.IsMod1() || aKeyCode.IsMod3();
    bool bMod2 = aKeyCode.IsMod2();
    bool bDone = false;
    bool bHandleKey = false;

    switch( aKeyCode.GetCode() )
    {
        case KEY_UP:
        {
            if ( IsReadOnly() )
            {
                if ( GetTopEntry() )
                    SetTopEntry( GetTopEntry()-1 );
            }
            else if ( !bMod2 )
            {
                if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = maEntryList.FindFirstSelectable( 0 );
                }
                else if ( mnCurrentPos )
                {
                    // search first selectable above the current position
                    nSelect = maEntryList.FindFirstSelectable( mnCurrentPos - 1, false );
                }

                if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
                    SetTopEntry( mnTop-1 );

                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_DOWN:
        {
            if ( IsReadOnly() )
            {
                SetTopEntry( GetTopEntry()+1 );
            }
            else if ( !bMod2 )
            {
                if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = maEntryList.FindFirstSelectable( 0 );
                }
                else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
                {
                    // search first selectable below the current position
                    nSelect = maEntryList.FindFirstSelectable( mnCurrentPos + 1 );
                }

                if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
                    SetTopEntry( mnTop+1 );

                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_PAGEUP:
        {
            if ( IsReadOnly() )
            {
                sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
                SetTopEntry( ( mnTop > nCurVis ) ?
                                (mnTop-nCurVis) : 0 );
            }
            else if ( !bCtrl && !bMod2 )
            {
                if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = maEntryList.FindFirstSelectable( 0 );
                }
                else if ( mnCurrentPos )
                {
                    if( mnCurrentPos == mnTop )
                    {
                        sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
                        SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
                    }

                    // find first selectable starting from mnTop looking forward
                    nSelect = maEntryList.FindFirstSelectable( mnTop );
                }
                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_PAGEDOWN:
        {
            if ( IsReadOnly() )
            {
                SetTopEntry( GetLastVisibleEntry() );
            }
            else if ( !bCtrl && !bMod2 )
            {
                if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = maEntryList.FindFirstSelectable( 0 );
                }
                else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
                {
                    sal_Int32 nCount = maEntryList.GetEntryCount();
                    sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
                    sal_Int32 nTmp = std::min( nCurVis, nCount );
                    nTmp += mnTop - 1;
                    if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
                    {
                        tools::Long nTmp2 = std::min( static_cast<tools::Long>(nCount-nCurVis), static_cast<tools::Long>(static_cast<tools::Long>(mnTop)+static_cast<tools::Long>(nCurVis)-1) );
                        nTmp2 = std::max( tools::Long(0) , nTmp2 );
                        nTmp = static_cast<sal_Int32>(nTmp2+(nCurVis-1) );
                        SetTopEntry( static_cast<sal_Int32>(nTmp2) );
                    }
                    // find first selectable starting from nTmp looking backwards
                    nSelect = maEntryList.FindFirstSelectable( nTmp, false );
                }
                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_HOME:
        {
            if ( IsReadOnly() )
            {
                SetTopEntry( 0 );
            }
            else if ( !bCtrl && !bMod2 &&  mnCurrentPos )
            {
                nSelect = maEntryList.FindFirstSelectable( maEntryList.GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
                if( mnTop != 0 )
                    SetTopEntry( 0 );

                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_END:
        {
            if ( IsReadOnly() )
            {
                SetTopEntry( 0xFFFF );
            }
            else if ( !bCtrl && !bMod2 )
            {
                if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
                {
                    nSelect = maEntryList.FindFirstSelectable( 0 );
                }
                else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
                {
                    sal_Int32 nCount = maEntryList.GetEntryCount();
                    nSelect = maEntryList.FindFirstSelectable( nCount - 1, false );
                    sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
                    if( nCount > nCurVis )
                        SetTopEntry( nCount - nCurVis );
                }
                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_LEFT:
        {
            if ( !bCtrl && !bMod2 )
            {
                ScrollHorz( -HORZ_SCROLL );
                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_RIGHT:
        {
            if ( !bCtrl && !bMod2 )
            {
                ScrollHorz( HORZ_SCROLL );
                bDone = true;
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_RETURN:
        {
            if ( !bMod2 && !IsReadOnly() )
            {
                mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
                ImplCallSelect();
                bDone = false;  // do not catch RETURN
            }
            maQuickSelectionEngine.Reset();
        }
        break;

        case KEY_SPACE:
        {
            if ( !bMod2 && !IsReadOnly() )
            {
                if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) ) )
                {
                    nSelect = mnCurrentPos;
                    eLET = LET_KEYSPACE;
                }
                bDone = true;
            }
            bHandleKey = true;
        }
        break;

        case KEY_A:
        {
            if( bCtrl && mbMulti )
            {
                // paint only once
                bool bUpdates = IsUpdateMode();
                SetUpdateMode( false );

                sal_Int32 nEntryCount = maEntryList.GetEntryCount();
                for( sal_Int32 i = 0; i < nEntryCount; i++ )
                    SelectEntry( i, true );

                // tdf#97066 - Update selected items
                ImplCallSelect();

                // restore update mode
                SetUpdateMode( bUpdates );
                Invalidate();

                maQuickSelectionEngine.Reset();

                bDone = true;
            }
            else
            {
                bHandleKey = true;
            }
        }
        break;

        default:
            bHandleKey = true;
            break;
    }
    if (bHandleKey && !IsReadOnly())
    {
        bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
    }

    if  (   ( nSelect != LISTBOX_ENTRY_NOTFOUND )
        &&  (   ( !maEntryList.IsEntryPosSelected( nSelect ) )
            ||  ( eLET == LET_KEYSPACE )
            )
        )
    {
        SAL_WARN_IF( maEntryList.IsEntryPosSelected( nSelect ) && !mbMulti, "vcl""ImplListBox: Selecting same Entry" );
        sal_Int32 nCount = maEntryList.GetEntryCount();
        if (nSelect >= nCount)
            nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
        bool bCurPosChange = (mnCurrentPos != nSelect);
        mnCurrentPos = nSelect;
        if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
        {
            // tdf#129043 Correctly deliver events when changing values with arrow keys in combobox
            if (mbIsDropdown && IsReallyVisible())
                mbTravelSelect = true;
            mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
            ImplCallSelect();
            mbTravelSelect = false;
        }
    }

    return bDone;
}

namespace
{
    vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
    {
        OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
        sal_Int32 nEntryCount( _rList.GetEntryCount() );
        if ( _nPos >= nEntryCount )
            _nPos = 0;
        _out_entryText = _rList.GetEntryText( _nPos );

        // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
        // => normalize
        return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + sal_IntPtr(1) );
    }

    sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
    {
        // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
        return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
    }
}

vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
{
    return lcl_getEntry( GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
}

vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
{
    sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
    return lcl_getEntry( GetEntryList(), nNextPos, _out_entryText );
}

void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
{
    sal_Int32 nSelect = lcl_getEntryPos( _entry );
    if ( maEntryList.IsEntryPosSelected( nSelect ) )
    {
        // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
        // to select the given entry by typing its starting letters. No need to act.
        return;
    }

    // normalize
    OSL_ENSURE( nSelect < maEntryList.GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
    sal_Int32 nCount = maEntryList.GetEntryCount();
    if (nSelect >= nCount)
        nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;

    // make visible
    ShowProminentEntry( nSelect );

    // actually select
    mnCurrentPos = nSelect;
    if ( SelectEntries( nSelect, LET_KEYMOVE ) )
    {
        mbTravelSelect = true;
        mnSelectModifier = 0;
        ImplCallSelect();
        mbTravelSelect = false;
    }
}

void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nPos );
    if (!pEntry)
        return;

    tools::Long nWidth = GetOutputSizePixel().Width();
    tools::Long nY = maEntryList.GetAddedHeight(nPos, mnTop);
    tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));

    bool bSelected = maEntryList.IsEntryPosSelected(nPos);
    if (bSelected)
    {
        rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetListBoxWindowHighlightTextColor());
        rRenderContext.SetFillColor(rStyleSettings.GetListBoxWindowHighlightColor());
        rRenderContext.SetLineColor();
        rRenderContext.DrawRect(aRect);
    }
    else
    {
        ApplySettings(rRenderContext);
        if (!IsEnabled())
            rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
    }
    rRenderContext.SetTextFillColor();

    if (IsUserDrawEnabled())
    {
        mbInUserDraw = true;
        mnUserDrawEntry = nPos;
        aRect.AdjustLeft( -mnLeft );
        if (nPos < GetEntryList().GetMRUCount())
            nPos = GetEntryList().FindEntry(GetEntryList().GetEntryText(nPos));
        nPos = nPos - GetEntryList().GetMRUCount();

        UserDrawEvent aUDEvt(&rRenderContext, aRect, nPos, bSelected);
        maUserDrawHdl.Call( &aUDEvt );
        mbInUserDraw = false;
    }
    else
    {
        DrawEntry(rRenderContext, nPos, truetrue);
    }
}

void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText)
{
    const ImplEntryType* pEntry = maEntryList.GetEntryPtr(nPos);
    if (!pEntry)
        return;

    tools::Long nEntryHeight = pEntry->getHeightWithMargin();

    // when changing this function don't forget to adjust ImplWin::DrawEntry()

    if (mbInUserDraw)
        nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU

    tools::Long nY = maEntryList.GetAddedHeight(nPos, mnTop);

    if (bDrawImage && maEntryList.HasImages())
    {
        Image aImage = maEntryList.GetEntryImage(nPos);
        if (!!aImage)
        {
            Size aImgSz = aImage.GetSizePixel();
            Point aPtImg(gnBorder - mnLeft, nY + ((nEntryHeight - aImgSz.Height()) / 2));

            if (!IsZoom())
            {
                rRenderContext.DrawImage(aPtImg, aImage);
            }
            else
            {
                aImgSz.setWidth( CalcZoom(aImgSz.Width()) );
                aImgSz.setHeight( CalcZoom(aImgSz.Height()) );
                rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
            }

            const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
            const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);

            if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
            {
                const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
                const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
                const sal_uInt8 nAlpha(255 - ((nEdgeBlendingPercent * 255) / 100));
                const BitmapEx aBlendFrame(createAlphaBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));

                if (!aBlendFrame.IsEmpty())
                {
                    rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
                }
            }
        }
    }

    if (bDrawText)
    {
        OUString aStr(maEntryList.GetEntryText(nPos));
        if (!aStr.isEmpty())
        {
            tools::Long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * gnBorder);
            // a multiline entry should only be as wide as the window
            if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
                nMaxWidth = GetOutputSizePixel().Width() - 2 * gnBorder;

            tools::Rectangle aTextRect(Point(gnBorder - mnLeft, nY),
                                Size(nMaxWidth, nEntryHeight));

            if (maEntryList.HasEntryImage(nPos) || IsUserDrawEnabled())
            {
                tools::Long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
                aTextRect.AdjustLeft(nImageWidth + IMG_TXT_DISTANCE );
            }

            DrawTextFlags nDrawStyle = ImplGetTextStyle();
            if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
                nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
            if (pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled)
                nDrawStyle |= DrawTextFlags::Disable;

            rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
        }
    }

    if ( !maSeparators.empty() && ( isSeparator(nPos) || isSeparator(nPos-1) ) )
    {
        Color aOldLineColor(rRenderContext.GetLineColor());
        rRenderContext.SetLineColor((GetBackground() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
        Point aStartPos(0, nY);
        if (isSeparator(nPos))
            aStartPos.AdjustY(pEntry->getHeightWithMargin() - 1 );
        Point aEndPos(aStartPos);
        aEndPos.setX( GetOutputSizePixel().Width() );
        rRenderContext.DrawLine(aStartPos, aEndPos);
        rRenderContext.SetLineColor(aOldLineColor);
    }
}

void ImplListBoxWindow::FillLayoutData() const
{
    mxLayoutData.emplace();
    const_cast<ImplListBoxWindow*>(this)->Invalidate(tools::Rectangle(Point(0, 0), GetOutDev()->GetOutputSize()));
}

void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    sal_Int32 nCount = maEntryList.GetEntryCount();

    bool bShowFocusRect = mbHasFocusRect;
    if (mbHasFocusRect)
        ImplHideFocusRect();

    tools::Long nY = 0; // + gnBorder;
    tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;

    for (sal_Int32 i = mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
    {
        const ImplEntryType* pEntry = maEntryList.GetEntryPtr(i);
        tools::Long nEntryHeight = pEntry->getHeightWithMargin();
        if (nY + nEntryHeight >= rRect.Top() &&
            nY <= rRect.Bottom() + mnMaxHeight)
        {
            ImplPaint(rRenderContext, i);
        }
        nY += nEntryHeight;
    }

    tools::Long nHeightDiff = maEntryList.GetAddedHeight(mnCurrentPos, mnTop);
    maFocusRect.SetPos(Point(0, nHeightDiff));
    Size aSz(maFocusRect.GetWidth(), maEntryList.GetEntryHeight(mnCurrentPos));
    maFocusRect.SetSize(aSz);
    if (HasFocus() && bShowFocusRect)
        ImplShowFocusRect();
}

void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    if (SupportsDoubleBuffering())
    {
        // This widget is explicitly double-buffered, so avoid partial paints.
        tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
        ImplDoPaint(rRenderContext, aRect);
    }
    else
        ImplDoPaint(rRenderContext, rRect);
}

sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
{
    // FIXME: ListBoxEntryFlags::MultiLine

    const sal_Int32 nCount = maEntryList.GetEntryCount()-mnTop;
    tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
    sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
    if( nEntries > nCount )
        nEntries = static_cast<sal_uInt16>(nCount);

    return nEntries;
}

void ImplListBoxWindow::Resize()
{
    Control::Resize();

    bool bShowFocusRect = mbHasFocusRect;
    if ( bShowFocusRect )
        ImplHideFocusRect();

    if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
    {
        Size aSz( GetOutputSizePixel().Width(), maEntryList.GetEntryHeight( mnCurrentPos ) );
        maFocusRect.SetSize( aSz );
    }

    if ( bShowFocusRect )
        ImplShowFocusRect();

    ImplClearLayoutData();
}

void ImplListBoxWindow::GetFocus()
{
    sal_Int32 nPos = mnCurrentPos;
    if ( nPos == LISTBOX_ENTRY_NOTFOUND )
        nPos = 0;
    tools::Long nHeightDiff = maEntryList.GetAddedHeight( nPos, mnTop );
    maFocusRect.SetPos( Point( 0, nHeightDiff ) );
    Size aSz( maFocusRect.GetWidth(), maEntryList.GetEntryHeight( nPos ) );
    maFocusRect.SetSize( aSz );
    ImplShowFocusRect();
    Control::GetFocus();
}

void ImplListBoxWindow::LoseFocus()
{
    ImplHideFocusRect();
    Control::LoseFocus();
}

void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
{
    if( maEntryList.GetEntryCount() == 0 )
        return;

    tools::Long nWHeight = PixelToLogic( GetSizePixel() ).Height();

    sal_Int32 nLastEntry = maEntryList.GetEntryCount()-1;
    if( nTop > nLastEntry )
        nTop = nLastEntry;
    const ImplEntryType* pLast = maEntryList.GetEntryPtr( nLastEntry );
    while( nTop > 0 && maEntryList.GetAddedHeight( nLastEntry, nTop-1 ) + pLast->getHeightWithMargin() <= nWHeight )
        nTop--;

    if ( nTop == mnTop )
        return;

    ImplClearLayoutData();
    tools::Long nDiff = maEntryList.GetAddedHeight( mnTop, nTop );
    PaintImmediately();
    ImplHideFocusRect();
    mnTop = nTop;
    Scroll( 0, nDiff );
    PaintImmediately();
    if( HasFocus() )
        ImplShowFocusRect();
    maScrollHdl.Call( this );
}

void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
{
    sal_Int32 nPos = nEntryPos;
    auto nWHeight = PixelToLogic( GetSizePixel() ).Height();
    while( nEntryPos > 0 && maEntryList.GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
        nEntryPos--;

    SetTopEntry( nEntryPos );
}

void ImplListBoxWindow::SetLeftIndent( tools::Long n )
{
    ScrollHorz( n - mnLeft );
}

void ImplListBoxWindow::ScrollHorz( tools::Long n )
{
    tools::Long nDiff = 0;
    if ( n > 0 )
    {
        tools::Long nWidth = GetOutputSizePixel().Width();
        if( ( mnMaxWidth - mnLeft + n ) > nWidth )
            nDiff = n;
    }
    else if ( n < 0 )
    {
        if( mnLeft )
        {
            tools::Long nAbs = -n;
            nDiff = - std::min( mnLeft, nAbs );
        }
    }

    if ( nDiff )
    {
        ImplClearLayoutData();
        mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
        PaintImmediately();
        ImplHideFocusRect();
        Scroll( -nDiff, 0 );
        PaintImmediately();
        if( HasFocus() )
            ImplShowFocusRect();
        maScrollHdl.Call( this );
    }
}

void ImplListBoxWindow::SetSeparatorPos( sal_Int32 n )
{
    maSeparators.clear();

    if ( n != LISTBOX_ENTRY_NOTFOUND )
    {
        maSeparators.insert( n );
    }
}

sal_Int32 ImplListBoxWindow::GetSeparatorPos() const
{
    if (!maSeparators.empty())
        return *(maSeparators.begin());
    else
        return LISTBOX_ENTRY_NOTFOUND;
}

bool ImplListBoxWindow::isSeparator( const sal_Int32 &n) const
{
    return maSeparators.find(n) != maSeparators.end();
}

Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
{
    // FIXME: ListBoxEntryFlags::MultiLine

    Size aSz;
    aSz.setHeight(nMaxLines * GetEntryHeightWithMargin());
    aSz.setWidth( mnMaxWidth + 2*gnBorder );
    return aSz;
}

tools::Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
{
    const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nItem );
    Size aSz( GetSizePixel().Width(), pEntry ? pEntry->getHeightWithMargin() : GetEntryHeightWithMargin() );
    tools::Long nY = maEntryList.GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList().GetMRUCount()*GetEntryHeightWithMargin();
    tools::Rectangle aRect( Point( 0, nY ), aSz );
    return aRect;
}

void ImplListBoxWindow::StateChanged( StateChangedType nType )
{
    Control::StateChanged( nType );

    if ( nType == StateChangedType::Zoom )
    {
        ApplySettings(*GetOutDev());
        ImplCalcMetrics();
        Invalidate();
    }
    else if ( nType == StateChangedType::UpdateMode )
    {
        if ( IsUpdateMode() && IsReallyVisible() )
            Invalidate();
    }
    else if ( nType == StateChangedType::ControlFont )
    {
        ApplySettings(*GetOutDev());
        ImplCalcMetrics();
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ApplySettings(*GetOutDev());
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ApplySettings(*GetOutDev());
        Invalidate();
    }
    else if( nType == StateChangedType::Enable )
    {
        Invalidate();
    }

    ImplClearLayoutData();
}

void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
    Control::DataChanged( rDCEvt );

    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        ImplClearLayoutData();
        ApplySettings(*GetOutDev());
        ImplCalcMetrics();
        Invalidate();
    }
}

DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
{
    DrawTextFlags nTextStyle = DrawTextFlags::VCenter;

    if (maEntryList.HasImages())
        nTextStyle |= DrawTextFlags::Left;
    else if (mbCenter)
        nTextStyle |= DrawTextFlags::Center;
    else if (mbRight)
        nTextStyle |= DrawTextFlags::Right;
    else
        nTextStyle |= DrawTextFlags::Left;

    return nTextStyle;
}

ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
    Control( pParent, nWinStyle ),
    maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
{
    // for native widget rendering we must be able to detect this window type
    SetType( WindowType::LISTBOXWINDOW );

--> --------------------

--> maximum size reached

--> --------------------

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

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.