summaryrefslogtreecommitdiffstats
path: root/vcl/source/control/imp_listbox.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/control/imp_listbox.cxx')
-rw-r--r--vcl/source/control/imp_listbox.cxx3012
1 files changed, 3012 insertions, 0 deletions
diff --git a/vcl/source/control/imp_listbox.cxx b/vcl/source/control/imp_listbox.cxx
new file mode 100644
index 000000000..fa01c647e
--- /dev/null
+++ b/vcl/source/control/imp_listbox.cxx
@@ -0,0 +1,3012 @@
+/* -*- 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/scrbar.hxx>
+#include <vcl/toolkit/lstbox.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/naturalsort.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.clear();
+}
+
+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 nStart, bool 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.GetFieldTextColor());
+
+ if (IsControlBackground())
+ rRenderContext.SetBackground(GetControlBackground());
+ else
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+}
+
+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 )
+{
+ 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 )
+ {
+ 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 );
+
+ }
+
+ bool bIsUserDrawEnabled = IsUserDrawEnabled();
+ 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;
+
+ 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 + 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.GetHighlightTextColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
+ 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, true, true);
+ }
+}
+
+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((nEdgeBlendingPercent * 255) / 100);
+ const BitmapEx aBlendFrame(createBlendFrame(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 );
+
+ mpVScrollBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
+ mpHScrollBar = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
+ mpScrollBarBox = VclPtr<ScrollBarBox>::Create( this );
+
+ Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
+ mpVScrollBar->SetScrollHdl( aLink );
+ mpHScrollBar->SetScrollHdl( aLink );
+
+ mbVScroll = false;
+ mbHScroll = false;
+ mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
+ mbEdgeBlending = false;
+
+ maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
+ maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
+ maLBWindow->SetEdgeBlending(GetEdgeBlending());
+ maLBWindow->Show();
+}
+
+ImplListBox::~ImplListBox()
+{
+ disposeOnce();
+}
+
+void ImplListBox::dispose()
+{
+ mpHScrollBar.disposeAndClear();
+ mpVScrollBar.disposeAndClear();
+ mpScrollBarBox.disposeAndClear();
+ maLBWindow.disposeAndClear();
+ Control::dispose();
+}
+
+void ImplListBox::Clear()
+{
+ maLBWindow->Clear();
+ if ( GetEntryList().GetMRUCount() )
+ {
+ maLBWindow->GetEntryList().SetMRUCount( 0 );
+ maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
+ }
+ mpVScrollBar->SetThumbPos( 0 );
+ mpHScrollBar->SetThumbPos( 0 );
+ CompatStateChanged( StateChangedType::Data );
+}
+
+sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
+{
+ ImplEntryType* pNewEntry = new ImplEntryType( rStr );
+ sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
+ CompatStateChanged( StateChangedType::Data );
+ return nNewPos;
+}
+
+sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
+{
+ ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
+ sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
+ CompatStateChanged( StateChangedType::Data );
+ return nNewPos;
+}
+
+void ImplListBox::RemoveEntry( sal_Int32 nPos )
+{
+ maLBWindow->RemoveEntry( nPos );
+ CompatStateChanged( StateChangedType::Data );
+}
+
+void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
+{
+ maLBWindow->SetEntryFlags( nPos, nFlags );
+}
+
+void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
+{
+ maLBWindow->SelectEntry( nPos, bSelect );
+}
+
+void ImplListBox::SetNoSelection()
+{
+ maLBWindow->DeselectAll();
+}
+
+void ImplListBox::GetFocus()
+{
+ if (maLBWindow)
+ maLBWindow->GrabFocus();
+ else
+ Control::GetFocus();
+}
+
+void ImplListBox::Resize()
+{
+ Control::Resize();
+ ImplResizeControls();
+ ImplCheckScrollBars();
+}
+
+IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
+{
+ CompatStateChanged( StateChangedType::Data );
+}
+
+IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
+{
+ tools::Long nSet = GetTopEntry();
+ if( nSet > mpVScrollBar->GetRangeMax() )
+ mpVScrollBar->SetRangeMax( GetEntryList().GetEntryCount() );
+ mpVScrollBar->SetThumbPos( GetTopEntry() );
+
+ mpHScrollBar->SetThumbPos( GetLeftIndent() );
+
+ maScrollHdl.Call( this );
+}
+
+IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
+{
+ sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
+ if( pSB == mpVScrollBar )
+ SetTopEntry( nPos );
+ else if( pSB == mpHScrollBar )
+ SetLeftIndent( nPos );
+ if( GetParent() )
+ GetParent()->Invalidate( InvalidateFlags::Update );
+}
+
+void ImplListBox::ImplCheckScrollBars()
+{
+ bool bArrange = false;
+
+ Size aOutSz = GetOutputSizePixel();
+ sal_Int32 nEntries = GetEntryList().GetEntryCount();
+ sal_uInt16 nMaxVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
+
+ // vertical ScrollBar
+ if( nEntries > nMaxVisEntries )
+ {
+ if( !mbVScroll )
+ bArrange = true;
+ mbVScroll = true;
+
+ // check of the scrolled-out region
+ if( GetEntryList().GetSelectedEntryCount() == 1 &&
+ GetEntryList().GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
+ ShowProminentEntry( GetEntryList().GetSelectedEntryPos( 0 ) );
+ else
+ SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
+ }
+ else
+ {
+ if( mbVScroll )
+ bArrange = true;
+ mbVScroll = false;
+ SetTopEntry( 0 );
+ }
+
+ // horizontal ScrollBar
+ if( mbAutoHScroll )
+ {
+ tools::Long nWidth = static_cast<sal_uInt16>(aOutSz.Width());
+ if ( mbVScroll )
+ nWidth -= mpVScrollBar->GetSizePixel().Width();
+
+ tools::Long nMaxWidth = GetMaxEntryWidth();
+ if( nWidth < nMaxWidth )
+ {
+ if( !mbHScroll )
+ bArrange = true;
+ mbHScroll = true;
+
+ if ( !mbVScroll ) // maybe we do need one now
+ {
+ nMaxVisEntries = static_cast<sal_uInt16>( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeightWithMargin() );
+ if( nEntries > nMaxVisEntries )
+ {
+ bArrange = true;
+ mbVScroll = true;
+
+ // check of the scrolled-out region
+ if( GetEntryList().GetSelectedEntryCount() == 1 &&
+ GetEntryList().GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
+ ShowProminentEntry( GetEntryList().GetSelectedEntryPos( 0 ) );
+ else
+ SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
+ }
+ }
+
+ // check of the scrolled-out region
+ sal_uInt16 nMaxLI = static_cast<sal_uInt16>(nMaxWidth - nWidth);
+ if ( nMaxLI < GetLeftIndent() )
+ SetLeftIndent( nMaxLI );
+ }
+ else
+ {
+ if( mbHScroll )
+ bArrange = true;
+ mbHScroll = false;
+ SetLeftIndent( 0 );
+ }
+ }
+
+ if( bArrange )
+ ImplResizeControls();
+
+ ImplInitScrollBars();
+}
+
+void ImplListBox::ImplInitScrollBars()
+{
+ Size aOutSz = maLBWindow->GetOutputSizePixel();
+
+ if ( mbVScroll )
+ {
+ sal_Int32 nEntries = GetEntryList().GetEntryCount();
+ sal_uInt16 nVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
+ mpVScrollBar->SetRangeMax( nEntries );
+ mpVScrollBar->SetVisibleSize( nVisEntries );
+ mpVScrollBar->SetPageSize( nVisEntries - 1 );
+ }
+
+ if ( mbHScroll )
+ {
+ mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
+ mpHScrollBar->SetVisibleSize( static_cast<sal_uInt16>(aOutSz.Width()) );
+ mpHScrollBar->SetLineSize( HORZ_SCROLL );
+ mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
+ }
+}
+
+void ImplListBox::ImplResizeControls()
+{
+ // Here we only position the Controls; if the Scrollbars are to be
+ // visible is already determined in ImplCheckScrollBars
+
+ Size aOutSz = GetOutputSizePixel();
+ tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
+ nSBWidth = CalcZoom( nSBWidth );
+
+ Size aInnerSz( aOutSz );
+ if ( mbVScroll )
+ aInnerSz.AdjustWidth( -nSBWidth );
+ if ( mbHScroll )
+ aInnerSz.AdjustHeight( -nSBWidth );
+
+ Point aWinPos( 0, 0 );
+ maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
+
+ // ScrollBarBox
+ if( mbVScroll && mbHScroll )
+ {
+ Point aBoxPos( aInnerSz.Width(), aInnerSz.Height() );
+ mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
+ mpScrollBarBox->Show();
+ }
+ else
+ {
+ mpScrollBarBox->Hide();
+ }
+
+ // vertical ScrollBar
+ if( mbVScroll )
+ {
+ // Scrollbar on left or right side?
+ Point aVPos( aOutSz.Width() - nSBWidth, 0 );
+ mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
+ mpVScrollBar->Show();
+ }
+ else
+ {
+ mpVScrollBar->Hide();
+ // #107254# Don't reset top entry after resize, but check for max top entry
+ SetTopEntry( GetTopEntry() );
+ }
+
+ // horizontal ScrollBar
+ if( mbHScroll )
+ {
+ Point aHPos( 0, aOutSz.Height() - nSBWidth );
+ mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
+ mpHScrollBar->Show();
+ }
+ else
+ {
+ mpHScrollBar->Hide();
+ SetLeftIndent( 0 );
+ }
+}
+
+void ImplListBox::StateChanged( StateChangedType nType )
+{
+ if ( nType == StateChangedType::InitShow )
+ {
+ ImplCheckScrollBars();
+ }
+ else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
+ {
+ bool bUpdate = IsUpdateMode();
+ maLBWindow->SetUpdateMode( bUpdate );
+ if ( bUpdate && IsReallyVisible() )
+ ImplCheckScrollBars();
+ }
+ else if( nType == StateChangedType::Enable )
+ {
+ mpHScrollBar->Enable( IsEnabled() );
+ mpVScrollBar->Enable( IsEnabled() );
+ mpScrollBarBox->Enable( IsEnabled() );
+ maLBWindow->Enable( IsEnabled() );
+
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::Zoom )
+ {
+ maLBWindow->SetZoom( GetZoom() );
+ Resize();
+ }
+ else if ( nType == StateChangedType::ControlFont )
+ {
+ maLBWindow->SetControlFont( GetControlFont() );
+ }
+ else if ( nType == StateChangedType::ControlForeground )
+ {
+ maLBWindow->SetControlForeground( GetControlForeground() );
+ }
+ else if ( nType == StateChangedType::ControlBackground )
+ {
+ maLBWindow->SetControlBackground( GetControlBackground() );
+ }
+ else if( nType == StateChangedType::Mirroring )
+ {
+ maLBWindow->EnableRTL( IsRTLEnabled() );
+ mpHScrollBar->EnableRTL( IsRTLEnabled() );
+ mpVScrollBar->EnableRTL( IsRTLEnabled() );
+ ImplResizeControls();
+ }
+
+ Control::StateChanged( nType );
+}
+
+bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
+{
+ bool bDone = false;
+ if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
+ {
+ const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
+ if ( rCEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
+ {
+ bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
+ }
+ }
+ else if (rCEvt.GetCommand() == CommandEventId::Gesture)
+ {
+ bDone = HandleScrollCommand(rCEvt, mpHScrollBar, mpVScrollBar);
+ }
+ }
+
+ return bDone || Window::EventNotify( rNEvt );
+}
+
+const Wallpaper& ImplListBox::GetDisplayBackground() const
+{
+ return maLBWindow->GetDisplayBackground();
+}
+
+bool ImplListBox::HandleWheelAsCursorTravel(const CommandEvent& rCEvt, Control& rControl)
+{
+ bool bDone = false;
+ if ( rCEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
+ {
+ if (!rControl.HasChildPathFocus())
+ rControl.GrabFocus();
+ sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
+ KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
+ bDone = ProcessKeyInput( aKeyEvent );
+ }
+ }
+ return bDone;
+}
+
+void ImplListBox::SetMRUEntries( std::u16string_view rEntries, sal_Unicode cSep )
+{
+ bool bChanges = GetEntryList().GetMRUCount() != 0;
+
+ // Remove old MRU entries
+ for ( sal_Int32 n = GetEntryList().GetMRUCount();n; )
+ maLBWindow->RemoveEntry( --n );
+
+ sal_Int32 nMRUCount = 0;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aEntry( o3tl::getToken(rEntries, 0, cSep, nIndex ) );
+ // Accept only existing entries
+ if ( GetEntryList().FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
+ {
+ ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
+ maLBWindow->InsertEntry(nMRUCount++, pNewEntry, false);
+ bChanges = true;
+ }
+ }
+ while ( nIndex >= 0 );
+
+ if ( bChanges )
+ {
+ maLBWindow->GetEntryList().SetMRUCount( nMRUCount );
+ SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
+ CompatStateChanged( StateChangedType::Data );
+ }
+}
+
+OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
+{
+ OUStringBuffer aEntries;
+ for ( sal_Int32 n = 0; n < GetEntryList().GetMRUCount(); n++ )
+ {
+ aEntries.append(GetEntryList().GetEntryText( n ));
+ if( n < ( GetEntryList().GetMRUCount() - 1 ) )
+ aEntries.append(cSep);
+ }
+ return aEntries.makeStringAndClear();
+}
+
+void ImplListBox::SetEdgeBlending(bool bNew)
+{
+ if(mbEdgeBlending != bNew)
+ {
+ mbEdgeBlending = bNew;
+ maLBWindow->SetEdgeBlending(GetEdgeBlending());
+ }
+}
+
+ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
+ Control ( pParent, nWinStyle )
+{
+ if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
+ && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
+ SetBackground();
+ else
+ SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
+
+ ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
+
+ mbEdgeBlending = false;
+ mnItemPos = LISTBOX_ENTRY_NOTFOUND;
+}
+
+void ImplWin::MouseButtonDown( const MouseEvent& )
+{
+ if( IsEnabled() )
+ {
+ maMBDownHdl.Call(this);
+ }
+}
+
+void ImplWin::FillLayoutData() const
+{
+ mxLayoutData.emplace();
+ ImplWin* pThis = const_cast<ImplWin*>(this);
+ pThis->ImplDraw(*pThis->GetOutDev(), true);
+}
+
+bool ImplWin::PreNotify( NotifyEvent& rNEvt )
+{
+ if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
+ {
+ const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
+ if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
+ {
+ // trigger redraw as mouse over state has changed
+ if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
+ && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
+ {
+ GetParent()->GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
+ }
+ }
+ }
+
+ return Control::PreNotify(rNEvt);
+}
+
+void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (!bLayout)
+ {
+ bool bNativeOK = false;
+ bool bHasFocus = HasFocus();
+ bool bIsEnabled = IsEnabled();
+
+ ControlState nState = ControlState::ENABLED;
+ if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
+ && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
+ {
+ // Repaint the (focused) area similarly to
+ // ImplSmallBorderWindowView::DrawWindow() in
+ // vcl/source/window/brdwin.cxx
+ vcl::Window *pWin = GetParent();
+
+ ImplControlValue aControlValue;
+ bIsEnabled &= pWin->IsEnabled();
+ if ( !bIsEnabled )
+ nState &= ~ControlState::ENABLED;
+ bHasFocus |= pWin->HasFocus();
+ if ( bHasFocus )
+ nState |= ControlState::FOCUSED;
+
+ // The listbox is painted over the entire control including the
+ // border, but ImplWin does not contain the border => correction
+ // needed.
+ sal_Int32 nLeft, nTop, nRight, nBottom;
+ pWin->GetBorder( nLeft, nTop, nRight, nBottom );
+ Point aPoint( -nLeft, -nTop );
+ tools::Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
+
+ bool bMouseOver = false;
+ vcl::Window *pChild = pWin->GetWindow( GetWindowType::FirstChild );
+ while( pChild )
+ {
+ bMouseOver = pChild->IsMouseOver();
+ if (bMouseOver)
+ break;
+ pChild = pChild->GetWindow( GetWindowType::Next );
+ }
+ if( bMouseOver )
+ nState |= ControlState::ROLLOVER;
+
+ // if parent has no border, then nobody has drawn the background
+ // since no border window exists. so draw it here.
+ WinBits nParentStyle = pWin->GetStyle();
+ if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
+ {
+ tools::Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
+ pWin->GetOutDev()->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
+ nState, aControlValue, OUString() );
+ }
+
+ bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
+ nState, aControlValue, OUString());
+ }
+
+ if (bIsEnabled)
+ {
+ if (bHasFocus && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
+ {
+ if ( !ImplGetSVData()->maNWFData.mbNoFocusRects )
+ {
+ rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
+ rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
+ }
+ else
+ {
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor();
+ rRenderContext.SetTextColor( rStyleSettings.GetFieldTextColor() );
+ }
+ rRenderContext.DrawRect( maFocusRect );
+ }
+ else
+ {
+ Color aColor;
+ if (IsControlForeground())
+ aColor = GetControlForeground();
+ else if (ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
+ {
+ if( bNativeOK && (nState & ControlState::ROLLOVER) )
+ aColor = rStyleSettings.GetButtonRolloverTextColor();
+ else
+ aColor = rStyleSettings.GetButtonTextColor();
+ }
+ else
+ {
+ if( bNativeOK && (nState & ControlState::ROLLOVER) )
+ aColor = rStyleSettings.GetFieldRolloverTextColor();
+ else
+ aColor = rStyleSettings.GetFieldTextColor();
+ }
+ rRenderContext.SetTextColor(aColor);
+ if (!bNativeOK)
+ rRenderContext.Erase(maFocusRect);
+ }
+ }
+ else // Disabled
+ {
+ rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
+ if (!bNativeOK)
+ rRenderContext.Erase(maFocusRect);
+ }
+ }
+
+ DrawEntry(rRenderContext, bLayout);
+}
+
+void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
+ ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
+
+ if (IsControlBackground())
+ rRenderContext.SetBackground(GetControlBackground());
+ else
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+}
+
+void ImplWin::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ ImplDraw(rRenderContext);
+}
+
+void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bLayout)
+{
+ tools::Long nBorder = 1;
+ Size aOutSz(GetOutputSizePixel());
+
+ bool bImage = !!maImage;
+ if (bImage && !bLayout)
+ {
+ DrawImageFlags nStyle = DrawImageFlags::NONE;
+ Size aImgSz = maImage.GetSizePixel();
+ Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ // check for HC mode
+ Image *pImage = &maImage;
+
+ if ( !IsZoom() )
+ {
+ rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
+ }
+ else
+ {
+ aImgSz.setWidth( CalcZoom( aImgSz.Width() ) );
+ aImgSz.setHeight( CalcZoom( aImgSz.Height() ) );
+ rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
+ }
+
+ const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
+
+ if(nEdgeBlendingPercent)
+ {
+ const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
+ const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
+ const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
+ const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
+
+ if(!aBlendFrame.IsEmpty())
+ {
+ rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
+ }
+ }
+ }
+
+ if( !maString.isEmpty() )
+ {
+ DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
+
+ if ( bImage && !bLayout )
+ nTextStyle |= DrawTextFlags::Left;
+ else if ( GetStyle() & WB_CENTER )
+ nTextStyle |= DrawTextFlags::Center;
+ else if ( GetStyle() & WB_RIGHT )
+ nTextStyle |= DrawTextFlags::Right;
+ else
+ nTextStyle |= DrawTextFlags::Left;
+
+ tools::Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
+
+ if ( bImage )
+ {
+ aTextRect.AdjustLeft(maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE );
+ }
+
+ std::vector< tools::Rectangle >* pVector = bLayout ? &mxLayoutData->m_aUnicodeBoundRects : nullptr;
+ OUString* pDisplayText = bLayout ? &mxLayoutData->m_aDisplayText : nullptr;
+ rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
+ }
+
+ if( HasFocus() && !bLayout )
+ ShowFocus( maFocusRect );
+}
+
+void ImplWin::Resize()
+{
+ Control::Resize();
+ maFocusRect.SetSize( GetOutputSizePixel() );
+ Invalidate();
+}
+
+void ImplWin::GetFocus()
+{
+ ShowFocus( maFocusRect );
+ if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
+ IsNativeWidgetEnabled() &&
+ IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
+ {
+ vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
+ if( ! pWin )
+ pWin = GetParent();
+ pWin->Invalidate();
+ }
+ else
+ Invalidate();
+ Control::GetFocus();
+}
+
+void ImplWin::LoseFocus()
+{
+ HideFocus();
+ if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
+ IsNativeWidgetEnabled() &&
+ IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
+ {
+ vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
+ if( ! pWin )
+ pWin = GetParent();
+ pWin->Invalidate();
+ }
+ else
+ Invalidate();
+ Control::LoseFocus();
+}
+
+void ImplWin::ShowFocus(const tools::Rectangle& rRect)
+{
+ if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
+ {
+ ImplControlValue aControlValue;
+
+ vcl::Window *pWin = GetParent();
+ tools::Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
+ pWin->GetOutDev()->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
+ ControlState::FOCUSED, aControlValue, OUString());
+ }
+ Control::ShowFocus(rRect);
+}
+
+ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
+ PushButton( pParent, nWinStyle )
+{
+}
+
+void ImplBtn::MouseButtonDown( const MouseEvent& )
+{
+ if( IsEnabled() )
+ maMBDownHdl.Call(this);
+}
+
+ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
+ FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
+{
+ // for native widget rendering we must be able to detect this window type
+ SetType( WindowType::LISTBOXWINDOW );
+
+ mpImplLB = nullptr;
+ mnDDLineCount = 0;
+ mbAutoWidth = false;
+
+ mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
+
+ vcl::Window * pBorderWindow = ImplGetBorderWindow();
+ if( pBorderWindow )
+ {
+ SetAccessibleRole(accessibility::AccessibleRole::PANEL);
+ pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
+ }
+ else
+ {
+ SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
+ }
+
+}
+
+ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
+{
+ disposeOnce();
+}
+
+void ImplListBoxFloatingWindow::dispose()
+{
+ mpImplLB.clear();
+ FloatingWindow::dispose();
+}
+
+
+bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
+{
+ if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
+ {
+ if( !GetParent()->HasChildPathFocus( true ) )
+ EndPopupMode();
+ }
+
+ return FloatingWindow::PreNotify( rNEvt );
+}
+
+void ImplListBoxFloatingWindow::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
+{
+ FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+
+ // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
+ // after a call to Resize(), we adjust its position if necessary
+ if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
+ {
+ Point aPos = GetParent()->GetPosPixel();
+ aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
+
+ if ( nFlags & PosSizeFlags::X )
+ aPos.setX( nX );
+
+ if ( nFlags & PosSizeFlags::Y )
+ aPos.setY( nY );
+
+ sal_uInt16 nIndex;
+ SetPosPixel( ImplCalcPos( this, tools::Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
+ }
+
+// if( !IsReallyVisible() )
+ {
+ // The ImplListBox does not get a Resize() as not visible.
+ // But the windows must get a Resize(), so that the number of
+ // visible entries is correct for PgUp/PgDown.
+ // The number also cannot be calculated by List/Combobox, as for
+ // this the presence of the vertical Scrollbar has to be known.
+ mpImplLB->SetSizePixel( GetOutputSizePixel() );
+ static_cast<vcl::Window*>(mpImplLB)->Resize();
+ static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
+ }
+}
+
+void ImplListBoxFloatingWindow::Resize()
+{
+ mpImplLB->GetMainWindow()->ImplClearLayoutData();
+ FloatingWindow::Resize();
+}
+
+Size ImplListBoxFloatingWindow::CalcFloatSize() const
+{
+ Size aFloatSz( maPrefSz );
+
+ sal_Int32 nLeft, nTop, nRight, nBottom;
+ GetBorder( nLeft, nTop, nRight, nBottom );
+
+ sal_Int32 nLines = mpImplLB->GetEntryList().GetEntryCount();
+ if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
+ nLines = mnDDLineCount;
+
+ Size aSz = mpImplLB->CalcSize( nLines );
+ tools::Long nMaxHeight = aSz.Height() + nTop + nBottom;
+
+ if ( mnDDLineCount )
+ aFloatSz.setHeight( nMaxHeight );
+
+ if( mbAutoWidth )
+ {
+ // AutoSize first only for width...
+
+ aFloatSz.setWidth( aSz.Width() + nLeft + nRight );
+ aFloatSz.AdjustWidth(nRight ); // adding some space looks better...
+
+ if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList().GetEntryCount() ) ) )
+ {
+ // then we also need the vertical Scrollbar
+ tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
+ aFloatSz.AdjustWidth(nSBWidth );
+ }
+
+ tools::Long nDesktopWidth = GetDesktopRectPixel().getWidth();
+ if (aFloatSz.Width() > nDesktopWidth)
+ // Don't exceed the desktop width.
+ aFloatSz.setWidth( nDesktopWidth );
+ }
+
+ if ( aFloatSz.Height() > nMaxHeight )
+ aFloatSz.setHeight( nMaxHeight );
+
+ // Minimal height, in case height is not set to Float height.
+ // The parent of FloatWin must be DropDown-Combo/Listbox.
+ Size aParentSz = GetParent()->GetSizePixel();
+ if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
+ aFloatSz.setHeight( aParentSz.Height() );
+
+ // do not get narrower than the parent...
+ if( aFloatSz.Width() < aParentSz.Width() )
+ aFloatSz.setWidth( aParentSz.Width() );
+
+ // align height to entries...
+ tools::Long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
+ tools::Long nEntryHeight = mpImplLB->GetEntryHeightWithMargin();
+ if ( nInnerHeight % nEntryHeight )
+ {
+ nInnerHeight /= nEntryHeight;
+ nInnerHeight++;
+ nInnerHeight *= nEntryHeight;
+ aFloatSz.setHeight( nInnerHeight + nTop + nBottom );
+ }
+
+ if (aFloatSz.Width() < aSz.Width())
+ {
+ // The max width of list box entries exceeds the window width.
+ // Account for the scroll bar height.
+ tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
+ aFloatSz.AdjustHeight(nSBWidth );
+ }
+
+ return aFloatSz;
+}
+
+void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
+{
+ if( IsInPopupMode() )
+ return;
+
+ Size aFloatSz = CalcFloatSize();
+
+ SetSizePixel( aFloatSz );
+ mpImplLB->SetSizePixel( GetOutputSizePixel() );
+
+ sal_Int32 nPos = mpImplLB->GetEntryList().GetSelectedEntryPos( 0 );
+ mnPopupModeStartSaveSelection = nPos;
+
+ Size aSz = GetParent()->GetSizePixel();
+ Point aPos = GetParent()->GetPosPixel();
+ aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
+ // FIXME: this ugly hack is for Mac/Aqua
+ // should be replaced by a real mechanism to place the float rectangle
+ if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
+ GetParent()->IsNativeWidgetEnabled() )
+ {
+ const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
+ aPos.AdjustX(nLeft );
+ aPos.AdjustY(nTop );
+ aSz.AdjustWidth( -(nLeft + nRight) );
+ aSz.AdjustHeight( -(nTop + nBottom) );
+ }
+ tools::Rectangle aRect( aPos, aSz );
+
+ // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
+ // where the document is unmirrored
+ // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
+ vcl::Window *pGrandparent = GetParent()->GetParent();
+ const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
+
+ if( pGrandparent->GetOutDev()->ImplIsAntiparallel() )
+ pGrandparentOutDev->ReMirror( aRect );
+
+ // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
+ StartPopupMode( aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::NoHorzPlacement | FloatWinPopupFlags::AllMouseButtonClose );
+
+ if( nPos != LISTBOX_ENTRY_NOTFOUND )
+ mpImplLB->ShowProminentEntry( nPos );
+
+ if( bStartTracking )
+ mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
+
+ if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
+ mpImplLB->GetMainWindow()->GrabFocus();
+
+ mpImplLB->GetMainWindow()->ImplClearLayoutData();
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */