Simplify using designated initializers
[LibreOffice.git] / vcl / source / control / imp_listbox.cxx
blob45b84c7db4eabe596c390b0ebe4099bda16a3c73
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
22 #include <vcl/svapp.hxx>
23 #include <vcl/settings.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/i18nhelp.hxx>
26 #include <vcl/naturalsort.hxx>
27 #include <vcl/vcllayout.hxx>
28 #include <vcl/toolkit/lstbox.hxx>
29 #include <vcl/toolkit/scrbar.hxx>
31 #include <listbox.hxx>
32 #include <svdata.hxx>
33 #include <window.h>
35 #include <com/sun/star/accessibility/AccessibleRole.hpp>
37 #include <sal/log.hxx>
38 #include <o3tl/safeint.hxx>
39 #include <o3tl/string_view.hxx>
40 #include <osl/diagnose.h>
41 #include <comphelper/string.hxx>
42 #include <comphelper/processfactory.hxx>
44 #include <limits>
46 #define MULTILINE_ENTRY_DRAW_FLAGS ( DrawTextFlags::WordBreak | DrawTextFlags::MultiLine | DrawTextFlags::VCenter )
48 using namespace ::com::sun::star;
50 constexpr tools::Long gnBorder = 1;
52 void ImplInitDropDownButton( PushButton* pButton )
54 pButton->SetSymbol( SymbolType::SPIN_DOWN );
56 if ( pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
57 && ! pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
58 pButton->SetBackground();
61 ImplEntryList::ImplEntryList( vcl::Window* pWindow )
63 mpWindow = pWindow;
64 mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
65 mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
66 mnImages = 0;
67 mbCallSelectionChangedHdl = true;
69 mnMRUCount = 0;
70 mnMaxMRUCount = 0;
73 ImplEntryList::~ImplEntryList()
75 Clear();
78 void ImplEntryList::Clear()
80 mnImages = 0;
81 maEntries.clear();
84 void ImplEntryList::dispose()
86 Clear();
87 mpWindow.clear();
90 void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
92 if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
94 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+nPos;
96 if ( ( (*iter)->mbIsSelected != bSelect ) &&
97 ( ( (*iter)->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE ) )
99 (*iter)->mbIsSelected = bSelect;
100 if ( mbCallSelectionChangedHdl )
101 maSelectionChangedHdl.Call( nPos );
106 namespace
108 comphelper::string::NaturalStringSorter& GetSorter()
110 static comphelper::string::NaturalStringSorter gSorter(
111 ::comphelper::getProcessComponentContext(),
112 Application::GetSettings().GetLanguageTag().getLocale());
113 return gSorter;
117 namespace vcl
119 sal_Int32 NaturalSortCompare(const OUString &rA, const OUString &rB)
121 const comphelper::string::NaturalStringSorter &rSorter = GetSorter();
122 return rSorter.compare(rA, rB);
126 sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
128 assert(nPos >= 0);
129 assert(maEntries.size() < LISTBOX_MAX_ENTRIES);
131 if ( !!pNewEntry->maImage )
132 mnImages++;
134 sal_Int32 insPos = 0;
135 const sal_Int32 nEntriesSize = static_cast<sal_Int32>(maEntries.size());
137 if ( !bSort || maEntries.empty())
139 if (0 <= nPos && nPos < nEntriesSize)
141 insPos = nPos;
142 maEntries.insert( maEntries.begin() + nPos, std::unique_ptr<ImplEntryType>(pNewEntry) );
144 else
146 insPos = nEntriesSize;
147 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
150 else
152 const comphelper::string::NaturalStringSorter &rSorter = GetSorter();
154 const OUString& rStr = pNewEntry->maStr;
156 ImplEntryType* pTemp = GetEntry( nEntriesSize-1 );
160 sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);
162 // fast insert for sorted data
163 if ( nComp >= 0 )
165 insPos = nEntriesSize;
166 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
168 else
170 pTemp = GetEntry( mnMRUCount );
172 nComp = rSorter.compare(rStr, pTemp->maStr);
173 if ( nComp <= 0 )
175 insPos = 0;
176 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
178 else
180 sal_uLong nLow = mnMRUCount;
181 sal_uLong nHigh = maEntries.size()-1;
182 sal_Int32 nMid;
184 // binary search
187 nMid = static_cast<sal_Int32>((nLow + nHigh) / 2);
188 pTemp = GetEntry( nMid );
190 nComp = rSorter.compare(rStr, pTemp->maStr);
192 if ( nComp < 0 )
193 nHigh = nMid-1;
194 else
196 if ( nComp > 0 )
197 nLow = nMid + 1;
198 else
199 break;
202 while ( nLow <= nHigh );
204 if ( nComp >= 0 )
205 nMid++;
207 insPos = nMid;
208 maEntries.insert(maEntries.begin()+nMid, std::unique_ptr<ImplEntryType>(pNewEntry));
212 catch (uno::RuntimeException& )
214 // XXX this is arguable, if the exception occurred because pNewEntry is
215 // garbage you wouldn't insert it. If the exception occurred because the
216 // Collator implementation is garbage then give the user a chance to see
217 // his stuff
218 insPos = 0;
219 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
224 return insPos;
227 void ImplEntryList::RemoveEntry( sal_Int32 nPos )
229 if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
231 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+ nPos;
233 if ( !!(*iter)->maImage )
234 mnImages--;
236 maEntries.erase(iter);
240 sal_Int32 ImplEntryList::FindEntry( std::u16string_view rString, bool bSearchMRUArea ) const
242 const sal_Int32 nEntries = static_cast<sal_Int32>(maEntries.size());
243 for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
245 OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n]->maStr ) );
246 if ( aComp == rString )
247 return n;
249 return LISTBOX_ENTRY_NOTFOUND;
252 sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStart, bool bLazy ) const
254 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
255 sal_Int32 nEntryCount = GetEntryCount();
257 const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
258 for ( sal_Int32 n = nStart; n < nEntryCount; )
260 ImplEntryType* pImplEntry = GetEntry( n );
261 bool bMatch;
262 if ( bLazy )
264 bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
266 else
268 bMatch = pImplEntry->maStr.startsWith(rStr);
270 if ( bMatch )
272 nPos = n;
273 break;
276 n++;
279 return nPos;
282 tools::Long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex ) const
284 tools::Long nHeight = 0;
285 sal_Int32 nStart = std::min(i_nEndIndex, i_nBeginIndex);
286 sal_Int32 nStop = std::max(i_nEndIndex, i_nBeginIndex);
287 sal_Int32 nEntryCount = GetEntryCount();
288 if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
290 // sanity check
291 if( nStop > nEntryCount-1 )
292 nStop = nEntryCount-1;
293 if (nStart < 0)
294 nStart = 0;
295 else if( nStart > nEntryCount-1 )
296 nStart = nEntryCount-1;
298 sal_Int32 nIndex = nStart;
299 while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
301 tools::Long nPosHeight = GetEntryPtr( nIndex )->getHeightWithMargin();
302 if (nHeight > ::std::numeric_limits<tools::Long>::max() - nPosHeight)
304 SAL_WARN( "vcl", "ImplEntryList::GetAddedHeight: truncated");
305 break;
307 nHeight += nPosHeight;
308 nIndex++;
311 else
312 nHeight = 0;
313 return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
316 tools::Long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
318 ImplEntryType* pImplEntry = GetEntry( nPos );
319 return pImplEntry ? pImplEntry->getHeightWithMargin() : 0;
322 OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
324 OUString aEntryText;
325 ImplEntryType* pImplEntry = GetEntry( nPos );
326 if ( pImplEntry )
327 aEntryText = pImplEntry->maStr;
328 return aEntryText;
331 bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
333 bool bImage = false;
334 ImplEntryType* pImplEntry = GetEntry( nPos );
335 if ( pImplEntry )
336 bImage = !!pImplEntry->maImage;
337 return bImage;
340 Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
342 Image aImage;
343 ImplEntryType* pImplEntry = GetEntry( nPos );
344 if ( pImplEntry )
345 aImage = pImplEntry->maImage;
346 return aImage;
349 void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
351 ImplEntryType* pImplEntry = GetEntry( nPos );
352 if ( pImplEntry )
353 pImplEntry->mpUserData = pNewData;
356 void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
358 ImplEntryType* pImplEntry = GetEntry( nPos );
359 return pImplEntry ? pImplEntry->mpUserData : nullptr;
362 void ImplEntryList::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
364 ImplEntryType* pImplEntry = GetEntry( nPos );
365 if ( pImplEntry )
366 pImplEntry->mnFlags = nFlags;
369 sal_Int32 ImplEntryList::GetSelectedEntryCount() const
371 sal_Int32 nSelCount = 0;
372 for ( sal_Int32 n = GetEntryCount(); n; )
374 ImplEntryType* pImplEntry = GetEntry( --n );
375 if ( pImplEntry->mbIsSelected )
376 nSelCount++;
378 return nSelCount;
381 OUString ImplEntryList::GetSelectedEntry( sal_Int32 nIndex ) const
383 return GetEntryText( GetSelectedEntryPos( nIndex ) );
386 sal_Int32 ImplEntryList::GetSelectedEntryPos( sal_Int32 nIndex ) const
388 sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
389 sal_Int32 nSel = 0;
390 sal_Int32 nEntryCount = GetEntryCount();
392 for ( sal_Int32 n = 0; n < nEntryCount; n++ )
394 ImplEntryType* pImplEntry = GetEntry( n );
395 if ( pImplEntry->mbIsSelected )
397 if ( nSel == nIndex )
399 nSelEntryPos = n;
400 break;
402 nSel++;
406 return nSelEntryPos;
409 bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
411 ImplEntryType* pImplEntry = GetEntry( nIndex );
412 return pImplEntry && pImplEntry->mbIsSelected;
415 bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
417 ImplEntryType* pImplEntry = GetEntry( nPos );
418 return pImplEntry == nullptr || ((pImplEntry->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE);
421 sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ ) const
423 if( IsEntrySelectable( nPos ) )
424 return nPos;
426 if( bForward )
428 for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
430 if( IsEntrySelectable( nPos ) )
431 return nPos;
434 else
436 while( nPos )
438 nPos--;
439 if( IsEntrySelectable( nPos ) )
440 return nPos;
444 return LISTBOX_ENTRY_NOTFOUND;
447 ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
448 Control( pParent, 0 ),
449 maEntryList( this ),
450 maQuickSelectionEngine( *this )
453 mnTop = 0;
454 mnLeft = 0;
455 mnSelectModifier = 0;
456 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
457 mbTrack = false;
458 mbTravelSelect = false;
459 mbTrackingSelect = false;
460 mbSelectionChanged = false;
461 mbMouseMoveSelect = false;
462 mbMulti = false;
463 mbGrabFocus = false;
464 mbUserDrawEnabled = false;
465 mbInUserDraw = false;
466 mbReadOnly = false;
467 mbHasFocusRect = false;
468 mbRight = ( nWinStyle & WB_RIGHT );
469 mbCenter = ( nWinStyle & WB_CENTER );
470 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
471 mbSort = ( nWinStyle & WB_SORT );
472 mbIsDropdown = ( nWinStyle & WB_DROPDOWN );
473 mbEdgeBlending = false;
475 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
476 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
478 GetOutDev()->SetLineColor();
479 SetTextFillColor();
480 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
482 ApplySettings(*GetOutDev());
483 ImplCalcMetrics();
486 ImplListBoxWindow::~ImplListBoxWindow()
488 disposeOnce();
491 void ImplListBoxWindow::dispose()
493 maEntryList.dispose();
494 Control::dispose();
497 void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
499 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
501 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
502 ApplyControlForeground(rRenderContext, rStyleSettings.GetListBoxWindowTextColor());
504 if (IsControlBackground())
505 rRenderContext.SetBackground(GetControlBackground());
506 else
507 rRenderContext.SetBackground(rStyleSettings.GetListBoxWindowBackgroundColor());
510 void ImplListBoxWindow::ImplCalcMetrics()
512 mnMaxWidth = 0;
513 mnMaxTxtWidth = 0;
514 mnMaxImgWidth = 0;
515 mnMaxImgTxtWidth= 0;
516 mnMaxImgHeight = 0;
518 mnTextHeight = static_cast<sal_uInt16>(GetTextHeight());
519 mnMaxTxtHeight = mnTextHeight + gnBorder;
520 mnMaxHeight = mnMaxTxtHeight;
522 if ( maUserItemSize.Height() > mnMaxHeight )
523 mnMaxHeight = static_cast<sal_uInt16>(maUserItemSize.Height());
524 if ( maUserItemSize.Width() > mnMaxWidth )
525 mnMaxWidth= static_cast<sal_uInt16>(maUserItemSize.Width());
527 for ( sal_Int32 n = maEntryList.GetEntryCount(); n; )
529 ImplEntryType* pEntry = maEntryList.GetMutableEntryPtr( --n );
530 ImplUpdateEntryMetrics( *pEntry );
533 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
535 Size aSz( GetOutputSizePixel().Width(), maEntryList.GetEntryPtr( mnCurrentPos )->getHeightWithMargin() );
536 maFocusRect.SetSize( aSz );
540 void ImplListBoxWindow::Clear()
542 maEntryList.Clear();
544 mnMaxHeight = mnMaxTxtHeight;
545 mnMaxWidth = 0;
546 mnMaxTxtWidth = 0;
547 mnMaxImgTxtWidth= 0;
548 mnMaxImgWidth = 0;
549 mnMaxImgHeight = 0;
550 mnTop = 0;
551 mnLeft = 0;
552 ImplClearLayoutData();
554 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
555 maQuickSelectionEngine.Reset();
557 Invalidate();
560 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
562 ImplClearLayoutData();
563 maUserItemSize = rSz;
564 ImplCalcMetrics();
567 namespace {
569 struct ImplEntryMetrics
571 bool bText;
572 bool bImage;
573 tools::Long nEntryWidth;
574 tools::Long nEntryHeight;
575 tools::Long nTextWidth;
576 tools::Long nImgWidth;
577 tools::Long nImgHeight;
582 tools::Long ImplEntryType::getHeightWithMargin() const
584 return mnHeight + ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
587 SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const OutputDevice* pOutputDevice)
589 if (maStrGlyphs.IsValid())
590 // Use pre-calculated result.
591 return &maStrGlyphs;
593 std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
594 maStr, 0, maStr.getLength(), Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
595 if (!pLayout)
596 return nullptr;
598 // Remember the calculation result.
599 maStrGlyphs = pLayout->GetGlyphs();
601 return &maStrGlyphs;
604 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
606 ImplEntryMetrics aMetrics;
607 aMetrics.bText = !rEntry.maStr.isEmpty();
608 aMetrics.bImage = !!rEntry.maImage;
609 aMetrics.nEntryWidth = 0;
610 aMetrics.nEntryHeight = 0;
611 aMetrics.nTextWidth = 0;
612 aMetrics.nImgWidth = 0;
613 aMetrics.nImgHeight = 0;
615 if ( aMetrics.bText )
617 if( rEntry.mnFlags & ListBoxEntryFlags::MultiLine )
619 // multiline case
620 Size aCurSize( PixelToLogic( GetSizePixel() ) );
621 // set the current size to a large number
622 // GetTextRect should shrink it to the actual size
623 aCurSize.setHeight( 0x7fffff );
624 tools::Rectangle aTextRect( Point( 0, 0 ), aCurSize );
625 aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
626 aMetrics.nTextWidth = aTextRect.GetWidth();
627 if( aMetrics.nTextWidth > mnMaxTxtWidth )
628 mnMaxTxtWidth = aMetrics.nTextWidth;
629 aMetrics.nEntryWidth = mnMaxTxtWidth;
630 aMetrics.nEntryHeight = aTextRect.GetHeight() + gnBorder;
632 else
634 // normal single line case
635 const SalLayoutGlyphs* pGlyphs = rEntry.GetTextGlyphs(GetOutDev());
636 aMetrics.nTextWidth
637 = static_cast<sal_uInt16>(GetTextWidth(rEntry.maStr, 0, -1, nullptr, pGlyphs));
638 if( aMetrics.nTextWidth > mnMaxTxtWidth )
639 mnMaxTxtWidth = aMetrics.nTextWidth;
640 aMetrics.nEntryWidth = mnMaxTxtWidth;
641 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
644 if ( aMetrics.bImage )
646 Size aImgSz = rEntry.maImage.GetSizePixel();
647 aMetrics.nImgWidth = static_cast<sal_uInt16>(CalcZoom( aImgSz.Width() ));
648 aMetrics.nImgHeight = static_cast<sal_uInt16>(CalcZoom( aImgSz.Height() ));
650 if( aMetrics.nImgWidth > mnMaxImgWidth )
651 mnMaxImgWidth = aMetrics.nImgWidth;
652 if( aMetrics.nImgHeight > mnMaxImgHeight )
653 mnMaxImgHeight = aMetrics.nImgHeight;
655 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
656 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
660 bool bIsUserDrawEnabled = IsUserDrawEnabled();
661 if (bIsUserDrawEnabled || aMetrics.bImage)
663 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
664 if (!bIsUserDrawEnabled && aMetrics.bText)
665 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
666 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
667 aMetrics.nEntryHeight );
670 if (!aMetrics.bText && !aMetrics.bImage && !bIsUserDrawEnabled)
672 // entries which have no (aka an empty) text, and no image,
673 // and are not user-drawn, should be shown nonetheless
674 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
677 if ( aMetrics.nEntryWidth > mnMaxWidth )
678 mnMaxWidth = aMetrics.nEntryWidth;
679 if ( aMetrics.nEntryHeight > mnMaxHeight )
680 mnMaxHeight = aMetrics.nEntryHeight;
682 rEntry.mnHeight = aMetrics.nEntryHeight;
685 void ImplListBoxWindow::ImplCallSelect()
687 if ( !IsTravelSelect() && GetEntryList().GetMaxMRUCount() )
689 // Insert the selected entry as MRU, if not already first MRU
690 sal_Int32 nSelected = GetEntryList().GetSelectedEntryPos( 0 );
691 sal_Int32 nMRUCount = GetEntryList().GetMRUCount();
692 OUString aSelected = GetEntryList().GetEntryText( nSelected );
693 sal_Int32 nFirstMatchingEntryPos = GetEntryList().FindEntry( aSelected, true );
694 if ( nFirstMatchingEntryPos || !nMRUCount )
696 bool bSelectNewEntry = false;
697 if ( nFirstMatchingEntryPos < nMRUCount )
699 RemoveEntry( nFirstMatchingEntryPos );
700 nMRUCount--;
701 if ( nFirstMatchingEntryPos == nSelected )
702 bSelectNewEntry = true;
704 else if ( nMRUCount == GetEntryList().GetMaxMRUCount() )
706 RemoveEntry( nMRUCount - 1 );
707 nMRUCount--;
710 ImplClearLayoutData();
712 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
713 pNewEntry->mbIsSelected = bSelectNewEntry;
714 GetEntryList().InsertEntry( 0, pNewEntry, false );
715 ImplUpdateEntryMetrics( *pNewEntry );
716 GetEntryList().SetMRUCount( ++nMRUCount );
717 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
718 maMRUChangedHdl.Call( nullptr );
722 maSelectHdl.Call( nullptr );
723 mbSelectionChanged = false;
726 sal_Int32 ImplListBoxWindow::InsertEntry(sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort)
728 assert(nPos >= 0);
729 assert(maEntryList.GetEntryCount() < LISTBOX_MAX_ENTRIES);
731 ImplClearLayoutData();
732 sal_Int32 nNewPos = maEntryList.InsertEntry( nPos, pNewEntry, bSort );
734 if( GetStyle() & WB_WORDBREAK )
735 pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;
737 ImplUpdateEntryMetrics( *pNewEntry );
738 return nNewPos;
741 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
743 return InsertEntry(nPos, pNewEntry, mbSort);
746 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
748 ImplClearLayoutData();
749 maEntryList.RemoveEntry( nPos );
750 if( mnCurrentPos >= maEntryList.GetEntryCount() )
751 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
752 ImplCalcMetrics();
755 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
757 maEntryList.SetEntryFlags( nPos, nFlags );
758 ImplEntryType* pEntry = maEntryList.GetMutableEntryPtr( nPos );
759 if( pEntry )
760 ImplUpdateEntryMetrics( *pEntry );
763 void ImplListBoxWindow::ImplShowFocusRect()
765 if ( mbHasFocusRect )
766 HideFocus();
767 ShowFocus( maFocusRect );
768 mbHasFocusRect = true;
771 void ImplListBoxWindow::ImplHideFocusRect()
773 if ( mbHasFocusRect )
775 HideFocus();
776 mbHasFocusRect = false;
780 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
782 tools::Long nY = gnBorder;
784 sal_Int32 nSelect = mnTop;
785 const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nSelect );
786 while (pEntry)
788 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
789 if (rPoint.Y() <= nEntryHeight + nY)
790 break;
791 nY += nEntryHeight;
792 pEntry = maEntryList.GetEntryPtr( ++nSelect );
794 if( pEntry == nullptr )
795 nSelect = LISTBOX_ENTRY_NOTFOUND;
797 return nSelect;
800 bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
802 bool bRet = false;
804 if( i_nEntry >= mnTop )
806 if( maEntryList.GetAddedHeight( i_nEntry, mnTop ) <
807 PixelToLogic( GetSizePixel() ).Height() )
809 bRet = true;
813 return bRet;
816 tools::Long ImplListBoxWindow::GetEntryHeightWithMargin() const
818 tools::Long nMargin = ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
819 return mnMaxHeight + nMargin;
822 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
824 sal_Int32 nPos = mnTop;
825 tools::Long nWindowHeight = GetSizePixel().Height();
826 sal_Int32 nCount = maEntryList.GetEntryCount();
827 tools::Long nDiff;
828 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = maEntryList.GetAddedHeight( nPos, mnTop ) )
829 nPos++;
831 if( nDiff > nWindowHeight && nPos > mnTop )
832 nPos--;
834 if( nPos >= nCount )
835 nPos = nCount-1;
837 return nPos;
840 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
842 mbMouseMoveSelect = false; // only till the first MouseButtonDown
843 maQuickSelectionEngine.Reset();
845 if ( !IsReadOnly() )
847 if( rMEvt.GetClicks() == 1 )
849 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
850 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
852 if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
853 mnTrackingSaveSelection = GetEntryList().GetSelectedEntryPos( 0 );
854 else
855 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
857 mnCurrentPos = nSelect;
858 mbTrackingSelect = true;
859 bool bCurPosChange = (mnCurrentPos != nSelect);
860 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
861 mbTrackingSelect = false;
862 if ( mbGrabFocus )
863 GrabFocus();
865 StartTracking( StartTrackingFlags::ScrollRepeat );
868 if( rMEvt.GetClicks() == 2 )
870 maDoubleClickHdl.Call( this );
873 else // if ( mbGrabFocus )
875 GrabFocus();
879 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
881 if (rMEvt.IsLeaveWindow() || mbMulti || !IsMouseMoveSelect() || !maEntryList.GetEntryCount())
882 return;
884 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
885 if( !aRect.Contains( rMEvt.GetPosPixel() ) )
886 return;
888 if ( IsMouseMoveSelect() )
890 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
891 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
892 nSelect = maEntryList.GetEntryCount() - 1;
893 nSelect = std::min( nSelect, GetLastVisibleEntry() );
894 nSelect = std::min( nSelect, static_cast<sal_Int32>( maEntryList.GetEntryCount() - 1 ) );
895 // Select only visible Entries with MouseMove, otherwise Tracking...
896 if ( IsVisible( nSelect ) &&
897 maEntryList.IsEntrySelectable( nSelect ) &&
898 ( ( nSelect != mnCurrentPos ) || !GetEntryList().GetSelectedEntryCount() || ( nSelect != GetEntryList().GetSelectedEntryPos( 0 ) ) ) )
900 mbTrackingSelect = true;
901 if ( SelectEntries( nSelect, LET_TRACKING ) )
903 // When list box selection change by mouse move, notify
904 // VclEventId::ListboxSelect vcl event.
905 maListItemSelectHdl.Call(nullptr);
907 mbTrackingSelect = false;
911 // if the DD button was pressed and someone moved into the ListBox
912 // with the mouse button pressed...
913 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
915 if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
916 mnTrackingSaveSelection = GetEntryList().GetSelectedEntryPos( 0 );
917 else
918 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
920 StartTracking( StartTrackingFlags::ScrollRepeat );
924 void ImplListBoxWindow::DeselectAll()
926 while ( GetEntryList().GetSelectedEntryCount() )
928 sal_Int32 nS = GetEntryList().GetSelectedEntryPos( 0 );
929 SelectEntry( nS, false );
933 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
935 if( (maEntryList.IsEntryPosSelected( nPos ) == bSelect) || !maEntryList.IsEntrySelectable( nPos ) )
936 return;
938 ImplHideFocusRect();
939 if( bSelect )
941 if( !mbMulti )
943 // deselect the selected entry
944 sal_Int32 nDeselect = GetEntryList().GetSelectedEntryPos( 0 );
945 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
947 //SelectEntryPos( nDeselect, false );
948 GetEntryList().SelectEntry( nDeselect, false );
949 if (IsUpdateMode() && IsReallyVisible())
950 Invalidate();
953 maEntryList.SelectEntry( nPos, true );
954 mnCurrentPos = nPos;
955 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
957 Invalidate();
958 if ( !IsVisible( nPos ) )
960 ImplClearLayoutData();
961 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
962 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
964 Resize();
965 ShowProminentEntry( nPos );
967 else
969 ShowProminentEntry( nPos );
974 else
976 maEntryList.SelectEntry( nPos, false );
977 Invalidate();
979 mbSelectionChanged = true;
982 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
984 bool bSelectionChanged = false;
986 if( IsEnabled() && maEntryList.IsEntrySelectable( nSelect ) )
988 bool bFocusChanged = false;
990 // here (Single-ListBox) only one entry can be deselected
991 if( !mbMulti )
993 sal_Int32 nDeselect = maEntryList.GetSelectedEntryPos( 0 );
994 if( nSelect != nDeselect )
996 SelectEntry( nSelect, true );
997 maEntryList.SetLastSelected( nSelect );
998 bFocusChanged = true;
999 bSelectionChanged = true;
1002 // MultiListBox without Modifier
1003 else if( mbSimpleMode && !bCtrl && !bShift )
1005 sal_Int32 nEntryCount = maEntryList.GetEntryCount();
1006 for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1008 bool bSelect = nPos == nSelect;
1009 if ( maEntryList.IsEntryPosSelected( nPos ) != bSelect )
1011 SelectEntry( nPos, bSelect );
1012 bFocusChanged = true;
1013 bSelectionChanged = true;
1016 maEntryList.SetLastSelected( nSelect );
1017 maEntryList.SetSelectionAnchor( nSelect );
1019 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1020 else if( ( !mbSimpleMode /* && !bShift */ ) || ( mbSimpleMode && ( bCtrl || bShift ) ) )
1022 // Space for selection change
1023 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1025 bool bSelect = !maEntryList.IsEntryPosSelected( nSelect );
1026 SelectEntry( nSelect, bSelect );
1027 maEntryList.SetLastSelected( nSelect );
1028 maEntryList.SetSelectionAnchor( nSelect );
1029 if ( !maEntryList.IsEntryPosSelected( nSelect ) )
1030 maEntryList.SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1031 bFocusChanged = true;
1032 bSelectionChanged = true;
1034 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1035 ( bShift && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1037 mnCurrentPos = nSelect;
1038 bFocusChanged = true;
1040 sal_Int32 nAnchor = maEntryList.GetSelectionAnchor();
1041 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && maEntryList.GetSelectedEntryCount() )
1043 nAnchor = maEntryList.GetSelectedEntryPos( maEntryList.GetSelectedEntryCount() - 1 );
1045 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1047 // All entries from Anchor to nSelect have to be selected
1048 sal_Int32 nStart = std::min( nSelect, nAnchor );
1049 sal_Int32 nEnd = std::max( nSelect, nAnchor );
1050 for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1052 if ( !maEntryList.IsEntryPosSelected( n ) )
1054 SelectEntry( n, true );
1055 bSelectionChanged = true;
1059 // if appropriate some more has to be deselected...
1060 sal_Int32 nLast = maEntryList.GetLastSelected();
1061 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1063 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1065 for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1067 if ( maEntryList.IsEntryPosSelected( n ) )
1069 SelectEntry( n, false );
1070 bSelectionChanged = true;
1074 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1076 for ( sal_Int32 n = nLast; n < nSelect; n++ )
1078 if ( maEntryList.IsEntryPosSelected( n ) )
1080 SelectEntry( n, false );
1081 bSelectionChanged = true;
1086 maEntryList.SetLastSelected( nSelect );
1089 else if( eLET != LET_TRACKING )
1091 ImplHideFocusRect();
1092 Invalidate();
1093 bFocusChanged = true;
1096 else if( bShift )
1098 bFocusChanged = true;
1101 if( bSelectionChanged )
1102 mbSelectionChanged = true;
1104 if( bFocusChanged )
1106 tools::Long nHeightDiff = maEntryList.GetAddedHeight( nSelect, mnTop );
1107 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1108 Size aSz( maFocusRect.GetWidth(),
1109 maEntryList.GetEntryHeight( nSelect ) );
1110 maFocusRect.SetSize( aSz );
1111 if( HasFocus() )
1112 ImplShowFocusRect();
1113 if (bSelectPosChange)
1115 maFocusHdl.Call(nSelect);
1118 ImplClearLayoutData();
1120 return bSelectionChanged;
1123 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1125 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
1126 bool bInside = aRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() );
1128 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1130 if ( bInside && !rTEvt.IsTrackingCanceled() )
1132 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1133 ImplCallSelect();
1135 else
1137 maCancelHdl.Call( nullptr );
1138 if ( !mbMulti )
1140 mbTrackingSelect = true;
1141 SelectEntry( mnTrackingSaveSelection, true );
1142 mbTrackingSelect = false;
1143 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1145 tools::Long nHeightDiff = maEntryList.GetAddedHeight( mnCurrentPos, mnTop );
1146 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1147 Size aSz( maFocusRect.GetWidth(),
1148 maEntryList.GetEntryHeight( mnCurrentPos ) );
1149 maFocusRect.SetSize( aSz );
1150 ImplShowFocusRect();
1155 mbTrack = false;
1157 else
1159 bool bTrackOrQuickClick = mbTrack;
1160 if( !mbTrack )
1162 if ( bInside )
1164 mbTrack = true;
1167 // this case only happens, if the mouse button is pressed very briefly
1168 if( rTEvt.IsTrackingEnded() && mbTrack )
1170 bTrackOrQuickClick = true;
1171 mbTrack = false;
1175 if( bTrackOrQuickClick )
1177 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1178 Point aPt( aMEvt.GetPosPixel() );
1179 bool bShift = aMEvt.IsShift();
1180 bool bCtrl = aMEvt.IsMod1();
1182 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1183 if( aPt.Y() < 0 )
1185 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1187 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1188 if( nSelect < mnTop )
1189 SetTopEntry( mnTop-1 );
1192 else if( aPt.Y() > GetOutputSizePixel().Height() )
1194 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1196 nSelect = std::min( static_cast<sal_Int32>(mnCurrentPos+1), static_cast<sal_Int32>(maEntryList.GetEntryCount()-1) );
1197 if( nSelect >= GetLastVisibleEntry() )
1198 SetTopEntry( mnTop+1 );
1201 else
1203 nSelect = static_cast<sal_Int32>( ( aPt.Y() + gnBorder ) / mnMaxHeight ) + mnTop;
1204 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1205 nSelect = std::min( nSelect, static_cast<sal_Int32>( maEntryList.GetEntryCount() - 1 ) );
1208 if ( bInside )
1210 if ( ( nSelect != mnCurrentPos ) || !GetEntryList().GetSelectedEntryCount() )
1212 mbTrackingSelect = true;
1213 SelectEntries(nSelect, LET_TRACKING, bShift, bCtrl);
1214 mbTrackingSelect = false;
1217 else
1219 if ( !mbMulti && GetEntryList().GetSelectedEntryCount() )
1221 mbTrackingSelect = true;
1222 SelectEntry( GetEntryList().GetSelectedEntryPos( 0 ), false );
1223 mbTrackingSelect = false;
1226 mnCurrentPos = nSelect;
1227 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1229 ImplHideFocusRect();
1231 else
1233 tools::Long nHeightDiff = maEntryList.GetAddedHeight( mnCurrentPos, mnTop );
1234 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1235 Size aSz( maFocusRect.GetWidth(), maEntryList.GetEntryHeight( mnCurrentPos ) );
1236 maFocusRect.SetSize( aSz );
1237 ImplShowFocusRect();
1243 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1245 if( !ProcessKeyInput( rKEvt ) )
1246 Control::KeyInput( rKEvt );
1249 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1251 // entry to be selected
1252 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1253 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1255 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1257 bool bShift = aKeyCode.IsShift();
1258 bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1259 bool bMod2 = aKeyCode.IsMod2();
1260 bool bDone = false;
1261 bool bHandleKey = false;
1263 switch( aKeyCode.GetCode() )
1265 case KEY_UP:
1267 if ( IsReadOnly() )
1269 if ( GetTopEntry() )
1270 SetTopEntry( GetTopEntry()-1 );
1272 else if ( !bMod2 )
1274 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1276 nSelect = maEntryList.FindFirstSelectable( 0 );
1278 else if ( mnCurrentPos )
1280 // search first selectable above the current position
1281 nSelect = maEntryList.FindFirstSelectable( mnCurrentPos - 1, false );
1284 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1285 SetTopEntry( mnTop-1 );
1287 bDone = true;
1289 maQuickSelectionEngine.Reset();
1291 break;
1293 case KEY_DOWN:
1295 if ( IsReadOnly() )
1297 SetTopEntry( GetTopEntry()+1 );
1299 else if ( !bMod2 )
1301 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1303 nSelect = maEntryList.FindFirstSelectable( 0 );
1305 else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
1307 // search first selectable below the current position
1308 nSelect = maEntryList.FindFirstSelectable( mnCurrentPos + 1 );
1311 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1312 SetTopEntry( mnTop+1 );
1314 bDone = true;
1316 maQuickSelectionEngine.Reset();
1318 break;
1320 case KEY_PAGEUP:
1322 if ( IsReadOnly() )
1324 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1325 SetTopEntry( ( mnTop > nCurVis ) ?
1326 (mnTop-nCurVis) : 0 );
1328 else if ( !bCtrl && !bMod2 )
1330 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1332 nSelect = maEntryList.FindFirstSelectable( 0 );
1334 else if ( mnCurrentPos )
1336 if( mnCurrentPos == mnTop )
1338 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1339 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1342 // find first selectable starting from mnTop looking forward
1343 nSelect = maEntryList.FindFirstSelectable( mnTop );
1345 bDone = true;
1347 maQuickSelectionEngine.Reset();
1349 break;
1351 case KEY_PAGEDOWN:
1353 if ( IsReadOnly() )
1355 SetTopEntry( GetLastVisibleEntry() );
1357 else if ( !bCtrl && !bMod2 )
1359 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1361 nSelect = maEntryList.FindFirstSelectable( 0 );
1363 else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
1365 sal_Int32 nCount = maEntryList.GetEntryCount();
1366 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1367 sal_Int32 nTmp = std::min( nCurVis, nCount );
1368 nTmp += mnTop - 1;
1369 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1371 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) );
1372 nTmp2 = std::max( tools::Long(0) , nTmp2 );
1373 nTmp = static_cast<sal_Int32>(nTmp2+(nCurVis-1) );
1374 SetTopEntry( static_cast<sal_Int32>(nTmp2) );
1376 // find first selectable starting from nTmp looking backwards
1377 nSelect = maEntryList.FindFirstSelectable( nTmp, false );
1379 bDone = true;
1381 maQuickSelectionEngine.Reset();
1383 break;
1385 case KEY_HOME:
1387 if ( IsReadOnly() )
1389 SetTopEntry( 0 );
1391 else if ( !bCtrl && !bMod2 && mnCurrentPos )
1393 nSelect = maEntryList.FindFirstSelectable( maEntryList.GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
1394 if( mnTop != 0 )
1395 SetTopEntry( 0 );
1397 bDone = true;
1399 maQuickSelectionEngine.Reset();
1401 break;
1403 case KEY_END:
1405 if ( IsReadOnly() )
1407 SetTopEntry( 0xFFFF );
1409 else if ( !bCtrl && !bMod2 )
1411 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1413 nSelect = maEntryList.FindFirstSelectable( 0 );
1415 else if ( (mnCurrentPos+1) < maEntryList.GetEntryCount() )
1417 sal_Int32 nCount = maEntryList.GetEntryCount();
1418 nSelect = maEntryList.FindFirstSelectable( nCount - 1, false );
1419 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1420 if( nCount > nCurVis )
1421 SetTopEntry( nCount - nCurVis );
1423 bDone = true;
1425 maQuickSelectionEngine.Reset();
1427 break;
1429 case KEY_LEFT:
1431 if ( !bCtrl && !bMod2 )
1433 ScrollHorz( -HORZ_SCROLL );
1434 bDone = true;
1436 maQuickSelectionEngine.Reset();
1438 break;
1440 case KEY_RIGHT:
1442 if ( !bCtrl && !bMod2 )
1444 ScrollHorz( HORZ_SCROLL );
1445 bDone = true;
1447 maQuickSelectionEngine.Reset();
1449 break;
1451 case KEY_RETURN:
1453 if ( !bMod2 && !IsReadOnly() )
1455 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1456 ImplCallSelect();
1457 bDone = false; // do not catch RETURN
1459 maQuickSelectionEngine.Reset();
1461 break;
1463 case KEY_SPACE:
1465 if ( !bMod2 && !IsReadOnly() )
1467 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) ) )
1469 nSelect = mnCurrentPos;
1470 eLET = LET_KEYSPACE;
1472 bDone = true;
1474 bHandleKey = true;
1476 break;
1478 case KEY_A:
1480 if( bCtrl && mbMulti )
1482 // paint only once
1483 bool bUpdates = IsUpdateMode();
1484 SetUpdateMode( false );
1486 sal_Int32 nEntryCount = maEntryList.GetEntryCount();
1487 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1488 SelectEntry( i, true );
1490 // tdf#97066 - Update selected items
1491 ImplCallSelect();
1493 // restore update mode
1494 SetUpdateMode( bUpdates );
1495 Invalidate();
1497 maQuickSelectionEngine.Reset();
1499 bDone = true;
1501 else
1503 bHandleKey = true;
1506 break;
1508 default:
1509 bHandleKey = true;
1510 break;
1512 if (bHandleKey && !IsReadOnly())
1514 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1517 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1518 && ( ( !maEntryList.IsEntryPosSelected( nSelect ) )
1519 || ( eLET == LET_KEYSPACE )
1523 SAL_WARN_IF( maEntryList.IsEntryPosSelected( nSelect ) && !mbMulti, "vcl", "ImplListBox: Selecting same Entry" );
1524 sal_Int32 nCount = maEntryList.GetEntryCount();
1525 if (nSelect >= nCount)
1526 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1527 bool bCurPosChange = (mnCurrentPos != nSelect);
1528 mnCurrentPos = nSelect;
1529 if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1531 // tdf#129043 Correctly deliver events when changing values with arrow keys in combobox
1532 if (mbIsDropdown && IsReallyVisible())
1533 mbTravelSelect = true;
1534 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1535 ImplCallSelect();
1536 mbTravelSelect = false;
1540 return bDone;
1543 namespace
1545 vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1547 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1548 sal_Int32 nEntryCount( _rList.GetEntryCount() );
1549 if ( _nPos >= nEntryCount )
1550 _nPos = 0;
1551 _out_entryText = _rList.GetEntryText( _nPos );
1553 // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1554 // => normalize
1555 return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + sal_IntPtr(1) );
1558 sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
1560 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1561 return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1565 vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1567 return lcl_getEntry( GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1570 vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1572 sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1573 return lcl_getEntry( GetEntryList(), nNextPos, _out_entryText );
1576 void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
1578 sal_Int32 nSelect = lcl_getEntryPos( _entry );
1579 if ( maEntryList.IsEntryPosSelected( nSelect ) )
1581 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1582 // to select the given entry by typing its starting letters. No need to act.
1583 return;
1586 // normalize
1587 OSL_ENSURE( nSelect < maEntryList.GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1588 sal_Int32 nCount = maEntryList.GetEntryCount();
1589 if (nSelect >= nCount)
1590 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1592 // make visible
1593 ShowProminentEntry( nSelect );
1595 // actually select
1596 mnCurrentPos = nSelect;
1597 if ( SelectEntries( nSelect, LET_KEYMOVE ) )
1599 mbTravelSelect = true;
1600 mnSelectModifier = 0;
1601 ImplCallSelect();
1602 mbTravelSelect = false;
1606 void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
1608 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1610 const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nPos );
1611 if (!pEntry)
1612 return;
1614 tools::Long nWidth = GetOutputSizePixel().Width();
1615 tools::Long nY = maEntryList.GetAddedHeight(nPos, mnTop);
1616 tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));
1618 bool bSelected = maEntryList.IsEntryPosSelected(nPos);
1619 if (bSelected)
1621 rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetListBoxWindowHighlightTextColor());
1622 rRenderContext.SetFillColor(rStyleSettings.GetListBoxWindowHighlightColor());
1623 rRenderContext.SetLineColor();
1624 rRenderContext.DrawRect(aRect);
1626 else
1628 ApplySettings(rRenderContext);
1629 if (!IsEnabled())
1630 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
1632 rRenderContext.SetTextFillColor();
1634 if (IsUserDrawEnabled())
1636 mbInUserDraw = true;
1637 mnUserDrawEntry = nPos;
1638 aRect.AdjustLeft( -mnLeft );
1639 if (nPos < GetEntryList().GetMRUCount())
1640 nPos = GetEntryList().FindEntry(GetEntryList().GetEntryText(nPos));
1641 nPos = nPos - GetEntryList().GetMRUCount();
1643 UserDrawEvent aUDEvt(&rRenderContext, aRect, nPos, bSelected);
1644 maUserDrawHdl.Call( &aUDEvt );
1645 mbInUserDraw = false;
1647 else
1649 DrawEntry(rRenderContext, nPos, true, true);
1653 void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText)
1655 const ImplEntryType* pEntry = maEntryList.GetEntryPtr(nPos);
1656 if (!pEntry)
1657 return;
1659 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1661 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1663 if (mbInUserDraw)
1664 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1666 tools::Long nY = maEntryList.GetAddedHeight(nPos, mnTop);
1668 if (bDrawImage && maEntryList.HasImages())
1670 Image aImage = maEntryList.GetEntryImage(nPos);
1671 if (!!aImage)
1673 Size aImgSz = aImage.GetSizePixel();
1674 Point aPtImg(gnBorder - mnLeft, nY + ((nEntryHeight - aImgSz.Height()) / 2));
1676 if (!IsZoom())
1678 rRenderContext.DrawImage(aPtImg, aImage);
1680 else
1682 aImgSz.setWidth( CalcZoom(aImgSz.Width()) );
1683 aImgSz.setHeight( CalcZoom(aImgSz.Height()) );
1684 rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
1687 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1688 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1690 if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1692 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1693 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1694 const sal_uInt8 nAlpha(255 - ((nEdgeBlendingPercent * 255) / 100));
1695 const BitmapEx aBlendFrame(createAlphaBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1697 if (!aBlendFrame.IsEmpty())
1699 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
1705 if (bDrawText)
1707 OUString aStr(maEntryList.GetEntryText(nPos));
1708 if (!aStr.isEmpty())
1710 tools::Long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * gnBorder);
1711 // a multiline entry should only be as wide as the window
1712 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1713 nMaxWidth = GetOutputSizePixel().Width() - 2 * gnBorder;
1715 tools::Rectangle aTextRect(Point(gnBorder - mnLeft, nY),
1716 Size(nMaxWidth, nEntryHeight));
1718 if (maEntryList.HasEntryImage(nPos) || IsUserDrawEnabled())
1720 tools::Long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
1721 aTextRect.AdjustLeft(nImageWidth + IMG_TXT_DISTANCE );
1724 DrawTextFlags nDrawStyle = ImplGetTextStyle();
1725 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1726 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1727 if (pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled)
1728 nDrawStyle |= DrawTextFlags::Disable;
1730 rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
1734 if ( !maSeparators.empty() && ( isSeparator(nPos) || isSeparator(nPos-1) ) )
1736 Color aOldLineColor(rRenderContext.GetLineColor());
1737 rRenderContext.SetLineColor((GetBackground() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
1738 Point aStartPos(0, nY);
1739 if (isSeparator(nPos))
1740 aStartPos.AdjustY(pEntry->getHeightWithMargin() - 1 );
1741 Point aEndPos(aStartPos);
1742 aEndPos.setX( GetOutputSizePixel().Width() );
1743 rRenderContext.DrawLine(aStartPos, aEndPos);
1744 rRenderContext.SetLineColor(aOldLineColor);
1748 void ImplListBoxWindow::FillLayoutData() const
1750 mxLayoutData.emplace();
1751 const_cast<ImplListBoxWindow*>(this)->Invalidate(tools::Rectangle(Point(0, 0), GetOutDev()->GetOutputSize()));
1754 void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1756 sal_Int32 nCount = maEntryList.GetEntryCount();
1758 bool bShowFocusRect = mbHasFocusRect;
1759 if (mbHasFocusRect)
1760 ImplHideFocusRect();
1762 tools::Long nY = 0; // + gnBorder;
1763 tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1765 for (sal_Int32 i = mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
1767 const ImplEntryType* pEntry = maEntryList.GetEntryPtr(i);
1768 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1769 if (nY + nEntryHeight >= rRect.Top() &&
1770 nY <= rRect.Bottom() + mnMaxHeight)
1772 ImplPaint(rRenderContext, i);
1774 nY += nEntryHeight;
1777 tools::Long nHeightDiff = maEntryList.GetAddedHeight(mnCurrentPos, mnTop);
1778 maFocusRect.SetPos(Point(0, nHeightDiff));
1779 Size aSz(maFocusRect.GetWidth(), maEntryList.GetEntryHeight(mnCurrentPos));
1780 maFocusRect.SetSize(aSz);
1781 if (HasFocus() && bShowFocusRect)
1782 ImplShowFocusRect();
1785 void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1787 if (SupportsDoubleBuffering())
1789 // This widget is explicitly double-buffered, so avoid partial paints.
1790 tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
1791 ImplDoPaint(rRenderContext, aRect);
1793 else
1794 ImplDoPaint(rRenderContext, rRect);
1797 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1799 // FIXME: ListBoxEntryFlags::MultiLine
1801 const sal_Int32 nCount = maEntryList.GetEntryCount()-mnTop;
1802 tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1803 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1804 if( nEntries > nCount )
1805 nEntries = static_cast<sal_uInt16>(nCount);
1807 return nEntries;
1810 void ImplListBoxWindow::Resize()
1812 Control::Resize();
1814 bool bShowFocusRect = mbHasFocusRect;
1815 if ( bShowFocusRect )
1816 ImplHideFocusRect();
1818 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1820 Size aSz( GetOutputSizePixel().Width(), maEntryList.GetEntryHeight( mnCurrentPos ) );
1821 maFocusRect.SetSize( aSz );
1824 if ( bShowFocusRect )
1825 ImplShowFocusRect();
1827 ImplClearLayoutData();
1830 void ImplListBoxWindow::GetFocus()
1832 sal_Int32 nPos = mnCurrentPos;
1833 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1834 nPos = 0;
1835 tools::Long nHeightDiff = maEntryList.GetAddedHeight( nPos, mnTop );
1836 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1837 Size aSz( maFocusRect.GetWidth(), maEntryList.GetEntryHeight( nPos ) );
1838 maFocusRect.SetSize( aSz );
1839 ImplShowFocusRect();
1840 Control::GetFocus();
1843 void ImplListBoxWindow::LoseFocus()
1845 ImplHideFocusRect();
1846 Control::LoseFocus();
1849 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1851 if( maEntryList.GetEntryCount() == 0 )
1852 return;
1854 tools::Long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1856 sal_Int32 nLastEntry = maEntryList.GetEntryCount()-1;
1857 if( nTop > nLastEntry )
1858 nTop = nLastEntry;
1859 const ImplEntryType* pLast = maEntryList.GetEntryPtr( nLastEntry );
1860 while( nTop > 0 && maEntryList.GetAddedHeight( nLastEntry, nTop-1 ) + pLast->getHeightWithMargin() <= nWHeight )
1861 nTop--;
1863 if ( nTop == mnTop )
1864 return;
1866 ImplClearLayoutData();
1867 tools::Long nDiff = maEntryList.GetAddedHeight( mnTop, nTop );
1868 PaintImmediately();
1869 ImplHideFocusRect();
1870 mnTop = nTop;
1871 Scroll( 0, nDiff );
1872 PaintImmediately();
1873 if( HasFocus() )
1874 ImplShowFocusRect();
1875 maScrollHdl.Call( this );
1878 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
1880 sal_Int32 nPos = nEntryPos;
1881 auto nWHeight = PixelToLogic( GetSizePixel() ).Height();
1882 while( nEntryPos > 0 && maEntryList.GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
1883 nEntryPos--;
1885 SetTopEntry( nEntryPos );
1888 void ImplListBoxWindow::SetLeftIndent( tools::Long n )
1890 ScrollHorz( n - mnLeft );
1893 void ImplListBoxWindow::ScrollHorz( tools::Long n )
1895 tools::Long nDiff = 0;
1896 if ( n > 0 )
1898 tools::Long nWidth = GetOutputSizePixel().Width();
1899 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
1900 nDiff = n;
1902 else if ( n < 0 )
1904 if( mnLeft )
1906 tools::Long nAbs = -n;
1907 nDiff = - std::min( mnLeft, nAbs );
1911 if ( nDiff )
1913 ImplClearLayoutData();
1914 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
1915 PaintImmediately();
1916 ImplHideFocusRect();
1917 Scroll( -nDiff, 0 );
1918 PaintImmediately();
1919 if( HasFocus() )
1920 ImplShowFocusRect();
1921 maScrollHdl.Call( this );
1925 void ImplListBoxWindow::SetSeparatorPos( sal_Int32 n )
1927 maSeparators.clear();
1929 if ( n != LISTBOX_ENTRY_NOTFOUND )
1931 maSeparators.insert( n );
1935 sal_Int32 ImplListBoxWindow::GetSeparatorPos() const
1937 if (!maSeparators.empty())
1938 return *(maSeparators.begin());
1939 else
1940 return LISTBOX_ENTRY_NOTFOUND;
1943 bool ImplListBoxWindow::isSeparator( const sal_Int32 &n) const
1945 return maSeparators.find(n) != maSeparators.end();
1948 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
1950 // FIXME: ListBoxEntryFlags::MultiLine
1952 Size aSz;
1953 aSz.setHeight(nMaxLines * GetEntryHeightWithMargin());
1954 aSz.setWidth( mnMaxWidth + 2*gnBorder );
1955 return aSz;
1958 tools::Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
1960 const ImplEntryType* pEntry = maEntryList.GetEntryPtr( nItem );
1961 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->getHeightWithMargin() : GetEntryHeightWithMargin() );
1962 tools::Long nY = maEntryList.GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList().GetMRUCount()*GetEntryHeightWithMargin();
1963 tools::Rectangle aRect( Point( 0, nY ), aSz );
1964 return aRect;
1967 void ImplListBoxWindow::StateChanged( StateChangedType nType )
1969 Control::StateChanged( nType );
1971 if ( nType == StateChangedType::Zoom )
1973 ApplySettings(*GetOutDev());
1974 ImplCalcMetrics();
1975 Invalidate();
1977 else if ( nType == StateChangedType::UpdateMode )
1979 if ( IsUpdateMode() && IsReallyVisible() )
1980 Invalidate();
1982 else if ( nType == StateChangedType::ControlFont )
1984 ApplySettings(*GetOutDev());
1985 ImplCalcMetrics();
1986 Invalidate();
1988 else if ( nType == StateChangedType::ControlForeground )
1990 ApplySettings(*GetOutDev());
1991 Invalidate();
1993 else if ( nType == StateChangedType::ControlBackground )
1995 ApplySettings(*GetOutDev());
1996 Invalidate();
1998 else if( nType == StateChangedType::Enable )
2000 Invalidate();
2003 ImplClearLayoutData();
2006 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
2008 Control::DataChanged( rDCEvt );
2010 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2011 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2012 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2013 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2015 ImplClearLayoutData();
2016 ApplySettings(*GetOutDev());
2017 ImplCalcMetrics();
2018 Invalidate();
2022 DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
2024 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2026 if (maEntryList.HasImages())
2027 nTextStyle |= DrawTextFlags::Left;
2028 else if (mbCenter)
2029 nTextStyle |= DrawTextFlags::Center;
2030 else if (mbRight)
2031 nTextStyle |= DrawTextFlags::Right;
2032 else
2033 nTextStyle |= DrawTextFlags::Left;
2035 return nTextStyle;
2038 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2039 Control( pParent, nWinStyle ),
2040 maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
2042 // for native widget rendering we must be able to detect this window type
2043 SetType( WindowType::LISTBOXWINDOW );
2045 mpVScrollBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
2046 mpHScrollBar = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
2047 mpScrollBarBox = VclPtr<ScrollBarBox>::Create( this );
2049 Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2050 mpVScrollBar->SetScrollHdl( aLink );
2051 mpHScrollBar->SetScrollHdl( aLink );
2053 mbVScroll = false;
2054 mbHScroll = false;
2055 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2056 mbEdgeBlending = false;
2058 maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2059 maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2060 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2061 maLBWindow->Show();
2064 ImplListBox::~ImplListBox()
2066 disposeOnce();
2069 void ImplListBox::dispose()
2071 mpHScrollBar.disposeAndClear();
2072 mpVScrollBar.disposeAndClear();
2073 mpScrollBarBox.disposeAndClear();
2074 maLBWindow.disposeAndClear();
2075 Control::dispose();
2078 void ImplListBox::Clear()
2080 maLBWindow->Clear();
2081 if ( GetEntryList().GetMRUCount() )
2083 maLBWindow->GetEntryList().SetMRUCount( 0 );
2084 maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2086 mpVScrollBar->SetThumbPos( 0 );
2087 mpHScrollBar->SetThumbPos( 0 );
2088 CompatStateChanged( StateChangedType::Data );
2091 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2093 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2094 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2095 CompatStateChanged( StateChangedType::Data );
2096 return nNewPos;
2099 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2101 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2102 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2103 CompatStateChanged( StateChangedType::Data );
2104 return nNewPos;
2107 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2109 maLBWindow->RemoveEntry( nPos );
2110 CompatStateChanged( StateChangedType::Data );
2113 void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
2115 maLBWindow->SetEntryFlags( nPos, nFlags );
2118 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2120 maLBWindow->SelectEntry( nPos, bSelect );
2123 void ImplListBox::SetNoSelection()
2125 maLBWindow->DeselectAll();
2128 void ImplListBox::GetFocus()
2130 if (maLBWindow)
2131 maLBWindow->GrabFocus();
2132 else
2133 Control::GetFocus();
2136 void ImplListBox::Resize()
2138 Control::Resize();
2139 ImplResizeControls();
2140 ImplCheckScrollBars();
2143 IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
2145 CompatStateChanged( StateChangedType::Data );
2148 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
2150 tools::Long nSet = GetTopEntry();
2151 if( nSet > mpVScrollBar->GetRangeMax() )
2152 mpVScrollBar->SetRangeMax( GetEntryList().GetEntryCount() );
2153 mpVScrollBar->SetThumbPos( GetTopEntry() );
2155 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2157 maScrollHdl.Call( this );
2160 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
2162 sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
2163 if( pSB == mpVScrollBar )
2164 SetTopEntry( nPos );
2165 else if( pSB == mpHScrollBar )
2166 SetLeftIndent( nPos );
2167 if( GetParent() )
2168 GetParent()->Invalidate( InvalidateFlags::Update );
2171 void ImplListBox::ImplCheckScrollBars()
2173 bool bArrange = false;
2175 Size aOutSz = GetOutputSizePixel();
2176 sal_Int32 nEntries = GetEntryList().GetEntryCount();
2177 sal_uInt16 nMaxVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2179 // vertical ScrollBar
2180 if( nEntries > nMaxVisEntries )
2182 if( !mbVScroll )
2183 bArrange = true;
2184 mbVScroll = true;
2186 // check of the scrolled-out region
2187 if( GetEntryList().GetSelectedEntryCount() == 1 &&
2188 GetEntryList().GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2189 ShowProminentEntry( GetEntryList().GetSelectedEntryPos( 0 ) );
2190 else
2191 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2193 else
2195 if( mbVScroll )
2196 bArrange = true;
2197 mbVScroll = false;
2198 SetTopEntry( 0 );
2201 // horizontal ScrollBar
2202 if( mbAutoHScroll )
2204 tools::Long nWidth = static_cast<sal_uInt16>(aOutSz.Width());
2205 if ( mbVScroll )
2206 nWidth -= mpVScrollBar->GetSizePixel().Width();
2208 tools::Long nMaxWidth = GetMaxEntryWidth();
2209 if( nWidth < nMaxWidth )
2211 if( !mbHScroll )
2212 bArrange = true;
2213 mbHScroll = true;
2215 if ( !mbVScroll ) // maybe we do need one now
2217 nMaxVisEntries = static_cast<sal_uInt16>( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeightWithMargin() );
2218 if( nEntries > nMaxVisEntries )
2220 bArrange = true;
2221 mbVScroll = true;
2223 // check of the scrolled-out region
2224 if( GetEntryList().GetSelectedEntryCount() == 1 &&
2225 GetEntryList().GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2226 ShowProminentEntry( GetEntryList().GetSelectedEntryPos( 0 ) );
2227 else
2228 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2232 // check of the scrolled-out region
2233 sal_uInt16 nMaxLI = static_cast<sal_uInt16>(nMaxWidth - nWidth);
2234 if ( nMaxLI < GetLeftIndent() )
2235 SetLeftIndent( nMaxLI );
2237 else
2239 if( mbHScroll )
2240 bArrange = true;
2241 mbHScroll = false;
2242 SetLeftIndent( 0 );
2246 if( bArrange )
2247 ImplResizeControls();
2249 ImplInitScrollBars();
2252 void ImplListBox::ImplInitScrollBars()
2254 Size aOutSz = maLBWindow->GetOutputSizePixel();
2256 if ( mbVScroll )
2258 sal_Int32 nEntries = GetEntryList().GetEntryCount();
2259 sal_uInt16 nVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2260 mpVScrollBar->SetRangeMax( nEntries );
2261 mpVScrollBar->SetVisibleSize( nVisEntries );
2262 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2265 if ( mbHScroll )
2267 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2268 mpHScrollBar->SetVisibleSize( static_cast<sal_uInt16>(aOutSz.Width()) );
2269 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2270 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2274 void ImplListBox::ImplResizeControls()
2276 // Here we only position the Controls; if the Scrollbars are to be
2277 // visible is already determined in ImplCheckScrollBars
2279 Size aOutSz = GetOutputSizePixel();
2280 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2281 nSBWidth = CalcZoom( nSBWidth );
2283 Size aInnerSz( aOutSz );
2284 if ( mbVScroll )
2285 aInnerSz.AdjustWidth( -nSBWidth );
2286 if ( mbHScroll )
2287 aInnerSz.AdjustHeight( -nSBWidth );
2289 Point aWinPos( 0, 0 );
2290 maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
2292 // ScrollBarBox
2293 if( mbVScroll && mbHScroll )
2295 Point aBoxPos( aInnerSz.Width(), aInnerSz.Height() );
2296 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2297 mpScrollBarBox->Show();
2299 else
2301 mpScrollBarBox->Hide();
2304 // vertical ScrollBar
2305 if( mbVScroll )
2307 // Scrollbar on left or right side?
2308 Point aVPos( aOutSz.Width() - nSBWidth, 0 );
2309 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2310 mpVScrollBar->Show();
2312 else
2314 mpVScrollBar->Hide();
2315 // #107254# Don't reset top entry after resize, but check for max top entry
2316 SetTopEntry( GetTopEntry() );
2319 // horizontal ScrollBar
2320 if( mbHScroll )
2322 Point aHPos( 0, aOutSz.Height() - nSBWidth );
2323 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2324 mpHScrollBar->Show();
2326 else
2328 mpHScrollBar->Hide();
2329 SetLeftIndent( 0 );
2333 void ImplListBox::StateChanged( StateChangedType nType )
2335 if ( nType == StateChangedType::InitShow )
2337 ImplCheckScrollBars();
2339 else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
2341 bool bUpdate = IsUpdateMode();
2342 maLBWindow->SetUpdateMode( bUpdate );
2343 if ( bUpdate && IsReallyVisible() )
2344 ImplCheckScrollBars();
2346 else if( nType == StateChangedType::Enable )
2348 mpHScrollBar->Enable( IsEnabled() );
2349 mpVScrollBar->Enable( IsEnabled() );
2350 mpScrollBarBox->Enable( IsEnabled() );
2351 maLBWindow->Enable( IsEnabled() );
2353 Invalidate();
2355 else if ( nType == StateChangedType::Zoom )
2357 maLBWindow->SetZoom( GetZoom() );
2358 Resize();
2360 else if ( nType == StateChangedType::ControlFont )
2362 maLBWindow->SetControlFont( GetControlFont() );
2364 else if ( nType == StateChangedType::ControlForeground )
2366 maLBWindow->SetControlForeground( GetControlForeground() );
2368 else if ( nType == StateChangedType::ControlBackground )
2370 maLBWindow->SetControlBackground( GetControlBackground() );
2372 else if( nType == StateChangedType::Mirroring )
2374 maLBWindow->EnableRTL( IsRTLEnabled() );
2375 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2376 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2377 ImplResizeControls();
2380 Control::StateChanged( nType );
2383 bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
2385 bool bDone = false;
2386 if ( rNEvt.GetType() == NotifyEventType::COMMAND )
2388 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2389 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2391 const CommandWheelData* pData = rCEvt.GetWheelData();
2392 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2394 bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2397 else if (rCEvt.GetCommand() == CommandEventId::GesturePan)
2399 bDone = HandleScrollCommand(rCEvt, mpHScrollBar, mpVScrollBar);
2403 return bDone || Window::EventNotify( rNEvt );
2406 const Wallpaper& ImplListBox::GetDisplayBackground() const
2408 return maLBWindow->GetDisplayBackground();
2411 bool ImplListBox::HandleWheelAsCursorTravel(const CommandEvent& rCEvt, Control& rControl)
2413 bool bDone = false;
2414 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2416 const CommandWheelData* pData = rCEvt.GetWheelData();
2417 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2419 if (!rControl.HasChildPathFocus())
2420 rControl.GrabFocus();
2421 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2422 KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2423 bDone = ProcessKeyInput( aKeyEvent );
2426 return bDone;
2429 void ImplListBox::SetMRUEntries( std::u16string_view rEntries, sal_Unicode cSep )
2431 bool bChanges = GetEntryList().GetMRUCount() != 0;
2433 // Remove old MRU entries
2434 for ( sal_Int32 n = GetEntryList().GetMRUCount();n; )
2435 maLBWindow->RemoveEntry( --n );
2437 sal_Int32 nMRUCount = 0;
2438 sal_Int32 nIndex = 0;
2441 OUString aEntry( o3tl::getToken(rEntries, 0, cSep, nIndex ) );
2442 // Accept only existing entries
2443 if ( GetEntryList().FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2445 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2446 maLBWindow->InsertEntry(nMRUCount++, pNewEntry, false);
2447 bChanges = true;
2450 while ( nIndex >= 0 );
2452 if ( bChanges )
2454 maLBWindow->GetEntryList().SetMRUCount( nMRUCount );
2455 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2456 CompatStateChanged( StateChangedType::Data );
2460 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2462 OUStringBuffer aEntries;
2463 for ( sal_Int32 n = 0; n < GetEntryList().GetMRUCount(); n++ )
2465 aEntries.append(GetEntryList().GetEntryText( n ));
2466 if( n < ( GetEntryList().GetMRUCount() - 1 ) )
2467 aEntries.append(cSep);
2469 return aEntries.makeStringAndClear();
2472 void ImplListBox::SetEdgeBlending(bool bNew)
2474 if(mbEdgeBlending != bNew)
2476 mbEdgeBlending = bNew;
2477 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2481 void ImplListBox::SetHighlightColor(const Color& rColor)
2483 AllSettings aSettings(GetSettings());
2484 StyleSettings aStyle(aSettings.GetStyleSettings());
2485 aStyle.SetHighlightColor(rColor);
2486 aSettings.SetStyleSettings(aStyle);
2487 SetSettings(aSettings);
2489 AllSettings aSettingsLB(maLBWindow->GetSettings());
2490 StyleSettings aStyleLB(aSettingsLB.GetStyleSettings());
2491 aStyleLB.SetListBoxWindowHighlightColor(rColor);
2492 aSettingsLB.SetStyleSettings(aStyleLB);
2493 maLBWindow->SetSettings(aSettingsLB);
2496 void ImplListBox::SetHighlightTextColor(const Color& rColor)
2498 AllSettings aSettings(GetSettings());
2499 StyleSettings aStyle(aSettings.GetStyleSettings());
2500 aStyle.SetHighlightTextColor(rColor);
2501 aSettings.SetStyleSettings(aStyle);
2502 SetSettings(aSettings);
2504 AllSettings aSettingsLB(maLBWindow->GetSettings());
2505 StyleSettings aStyleLB(aSettingsLB.GetStyleSettings());
2506 aStyleLB.SetListBoxWindowHighlightTextColor(rColor);
2507 aSettingsLB.SetStyleSettings(aStyleLB);
2508 maLBWindow->SetSettings(aSettingsLB);
2511 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2512 Control ( pParent, nWinStyle )
2514 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2515 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2516 SetBackground();
2517 else
2518 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2520 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2522 mbEdgeBlending = false;
2523 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2526 void ImplWin::MouseButtonDown( const MouseEvent& )
2528 if( IsEnabled() )
2530 maMBDownHdl.Call(this);
2534 void ImplWin::FillLayoutData() const
2536 mxLayoutData.emplace();
2537 ImplWin* pThis = const_cast<ImplWin*>(this);
2538 pThis->ImplDraw(*pThis->GetOutDev(), true);
2541 void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
2543 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2545 if (!bLayout)
2547 bool bNativeOK = false;
2548 bool bHasFocus = HasFocus();
2549 bool bIsEnabled = IsEnabled();
2551 ControlState nState = ControlState::ENABLED;
2552 if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2553 && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
2555 // Repaint the (focused) area similarly to
2556 // ImplSmallBorderWindowView::DrawWindow() in
2557 // vcl/source/window/brdwin.cxx
2558 vcl::Window *pWin = GetParent();
2560 ImplControlValue aControlValue;
2561 bIsEnabled &= pWin->IsEnabled();
2562 if ( !bIsEnabled )
2563 nState &= ~ControlState::ENABLED;
2564 bHasFocus |= pWin->HasFocus();
2565 if ( bHasFocus )
2566 nState |= ControlState::FOCUSED;
2568 // The listbox is painted over the entire control including the
2569 // border, but ImplWin does not contain the border => correction
2570 // needed.
2571 sal_Int32 nLeft, nTop, nRight, nBottom;
2572 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2573 Point aPoint( -nLeft, -nTop );
2574 tools::Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2576 bool bMouseOver = pWin->IsMouseOver();
2577 if (!bMouseOver)
2579 vcl::Window *pChild = pWin->GetWindow( GetWindowType::FirstChild );
2580 while( pChild )
2582 bMouseOver = pChild->IsMouseOver();
2583 if (bMouseOver)
2584 break;
2585 pChild = pChild->GetWindow( GetWindowType::Next );
2588 if( bMouseOver )
2589 nState |= ControlState::ROLLOVER;
2591 Color aBackgroundColor = COL_AUTO;
2592 if (IsControlBackground())
2593 aBackgroundColor = GetControlBackground();
2595 // if parent has no border, then nobody has drawn the background
2596 // since no border window exists. so draw it here.
2597 WinBits nParentStyle = pWin->GetStyle();
2598 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2600 tools::Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2601 pWin->GetOutDev()->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
2602 nState, aControlValue, OUString(), aBackgroundColor);
2605 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
2606 nState, aControlValue, OUString(), aBackgroundColor);
2609 if (bIsEnabled)
2611 if (bHasFocus && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2613 if ( !ImplGetSVData()->maNWFData.mbNoFocusRects )
2615 rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
2616 rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
2618 else
2620 rRenderContext.SetLineColor();
2621 rRenderContext.SetFillColor();
2622 rRenderContext.SetTextColor( rStyleSettings.GetFieldTextColor() );
2624 rRenderContext.DrawRect( maFocusRect );
2626 else
2628 Color aColor;
2629 if (IsControlForeground())
2630 aColor = GetControlForeground();
2631 else if (ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2633 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2634 aColor = rStyleSettings.GetButtonRolloverTextColor();
2635 else
2636 aColor = rStyleSettings.GetButtonTextColor();
2638 else
2640 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2641 aColor = rStyleSettings.GetFieldRolloverTextColor();
2642 else
2643 aColor = rStyleSettings.GetFieldTextColor();
2645 rRenderContext.SetTextColor(aColor);
2646 if (!bNativeOK)
2647 rRenderContext.Erase(maFocusRect);
2650 else // Disabled
2652 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
2653 if (!bNativeOK)
2654 rRenderContext.Erase(maFocusRect);
2658 DrawEntry(rRenderContext, bLayout);
2661 void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
2663 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2665 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
2666 ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
2668 if (IsControlBackground())
2669 rRenderContext.SetBackground(GetControlBackground());
2670 else
2671 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
2674 void ImplWin::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2676 ImplDraw(rRenderContext);
2679 void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bLayout)
2681 tools::Long nBorder = 1;
2682 Size aOutSz(GetOutputSizePixel());
2684 bool bImage = !!maImage;
2685 if (bImage && !bLayout)
2687 DrawImageFlags nStyle = DrawImageFlags::NONE;
2688 Size aImgSz = maImage.GetSizePixel();
2689 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2690 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2692 // check for HC mode
2693 Image *pImage = &maImage;
2695 if ( !IsZoom() )
2697 rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
2699 else
2701 aImgSz.setWidth( CalcZoom( aImgSz.Width() ) );
2702 aImgSz.setHeight( CalcZoom( aImgSz.Height() ) );
2703 rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2706 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2708 if(nEdgeBlendingPercent)
2710 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2711 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2712 const sal_uInt8 nAlpha(255 - ((nEdgeBlendingPercent * 255) / 100));
2713 const BitmapEx aBlendFrame(createAlphaBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2715 if(!aBlendFrame.IsEmpty())
2717 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
2722 if( !maString.isEmpty() )
2724 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2726 if ( bImage && !bLayout )
2727 nTextStyle |= DrawTextFlags::Left;
2728 else if ( GetStyle() & WB_CENTER )
2729 nTextStyle |= DrawTextFlags::Center;
2730 else if ( GetStyle() & WB_RIGHT )
2731 nTextStyle |= DrawTextFlags::Right;
2732 else
2733 nTextStyle |= DrawTextFlags::Left;
2735 tools::Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2737 if ( bImage )
2739 aTextRect.AdjustLeft(maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE );
2742 std::vector< tools::Rectangle >* pVector = bLayout ? &mxLayoutData->m_aUnicodeBoundRects : nullptr;
2743 OUString* pDisplayText = bLayout ? &mxLayoutData->m_aDisplayText : nullptr;
2744 rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2747 if( HasFocus() && !bLayout )
2748 ShowFocus( maFocusRect );
2751 void ImplWin::Resize()
2753 Control::Resize();
2754 maFocusRect.SetSize( GetOutputSizePixel() );
2755 Invalidate();
2758 void ImplWin::GetFocus()
2760 ShowFocus( maFocusRect );
2761 if (IsNativeWidgetEnabled() &&
2762 IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire))
2764 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2765 if( ! pWin )
2766 pWin = GetParent();
2767 pWin->Invalidate();
2769 else
2770 Invalidate();
2771 Control::GetFocus();
2774 void ImplWin::LoseFocus()
2776 HideFocus();
2777 if (IsNativeWidgetEnabled() &&
2778 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire))
2780 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2781 if( ! pWin )
2782 pWin = GetParent();
2783 pWin->Invalidate();
2785 else
2786 Invalidate();
2787 Control::LoseFocus();
2790 void ImplWin::ShowFocus(const tools::Rectangle& rRect)
2792 if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
2794 ImplControlValue aControlValue;
2796 vcl::Window *pWin = GetParent();
2797 tools::Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
2798 pWin->GetOutDev()->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
2799 ControlState::FOCUSED, aControlValue, OUString());
2801 Control::ShowFocus(rRect);
2804 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2805 PushButton( pParent, nWinStyle )
2809 void ImplBtn::MouseButtonDown( const MouseEvent& )
2811 if( IsEnabled() )
2812 maMBDownHdl.Call(this);
2815 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2816 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
2818 // for native widget rendering we must be able to detect this window type
2819 SetType( WindowType::LISTBOXWINDOW );
2821 mpImplLB = nullptr;
2822 mnDDLineCount = 0;
2823 mbAutoWidth = false;
2825 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2827 vcl::Window * pBorderWindow = ImplGetBorderWindow();
2828 if( pBorderWindow )
2830 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2831 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2833 else
2835 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2840 ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
2842 disposeOnce();
2845 void ImplListBoxFloatingWindow::dispose()
2847 mpImplLB.clear();
2848 FloatingWindow::dispose();
2852 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2854 if( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
2856 if( !GetParent()->HasChildPathFocus( true ) )
2857 EndPopupMode();
2860 return FloatingWindow::PreNotify( rNEvt );
2863 void ImplListBoxFloatingWindow::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
2865 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2867 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2868 // after a call to Resize(), we adjust its position if necessary
2869 if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
2871 Point aPos = GetParent()->GetPosPixel();
2872 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2874 if ( nFlags & PosSizeFlags::X )
2875 aPos.setX( nX );
2877 if ( nFlags & PosSizeFlags::Y )
2878 aPos.setY( nY );
2880 sal_uInt16 nIndex;
2881 SetPosPixel( ImplCalcPos( this, tools::Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
2884 // if( !IsReallyVisible() )
2886 // The ImplListBox does not get a Resize() as not visible.
2887 // But the windows must get a Resize(), so that the number of
2888 // visible entries is correct for PgUp/PgDown.
2889 // The number also cannot be calculated by List/Combobox, as for
2890 // this the presence of the vertical Scrollbar has to be known.
2891 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2892 static_cast<vcl::Window*>(mpImplLB)->Resize();
2893 static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
2897 void ImplListBoxFloatingWindow::Resize()
2899 mpImplLB->GetMainWindow()->ImplClearLayoutData();
2900 FloatingWindow::Resize();
2903 Size ImplListBoxFloatingWindow::CalcFloatSize() const
2905 Size aFloatSz( maPrefSz );
2907 sal_Int32 nLeft, nTop, nRight, nBottom;
2908 GetBorder( nLeft, nTop, nRight, nBottom );
2910 sal_Int32 nLines = mpImplLB->GetEntryList().GetEntryCount();
2911 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
2912 nLines = mnDDLineCount;
2914 Size aSz = mpImplLB->CalcSize( nLines );
2915 tools::Long nMaxHeight = aSz.Height() + nTop + nBottom;
2917 if ( mnDDLineCount )
2918 aFloatSz.setHeight( nMaxHeight );
2920 if( mbAutoWidth )
2922 // AutoSize first only for width...
2924 aFloatSz.setWidth( aSz.Width() + nLeft + nRight );
2925 aFloatSz.AdjustWidth(nRight ); // adding some space looks better...
2927 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList().GetEntryCount() ) ) )
2929 // then we also need the vertical Scrollbar
2930 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2931 aFloatSz.AdjustWidth(nSBWidth );
2934 tools::Long nDesktopWidth = GetDesktopRectPixel().getOpenWidth();
2935 if (aFloatSz.Width() > nDesktopWidth)
2936 // Don't exceed the desktop width.
2937 aFloatSz.setWidth( nDesktopWidth );
2940 if ( aFloatSz.Height() > nMaxHeight )
2941 aFloatSz.setHeight( nMaxHeight );
2943 // Minimal height, in case height is not set to Float height.
2944 // The parent of FloatWin must be DropDown-Combo/Listbox.
2945 Size aParentSz = GetParent()->GetSizePixel();
2946 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
2947 aFloatSz.setHeight( aParentSz.Height() );
2949 // do not get narrower than the parent...
2950 if( aFloatSz.Width() < aParentSz.Width() )
2951 aFloatSz.setWidth( aParentSz.Width() );
2953 // align height to entries...
2954 tools::Long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
2955 tools::Long nEntryHeight = mpImplLB->GetEntryHeightWithMargin();
2956 if ( nInnerHeight % nEntryHeight )
2958 nInnerHeight /= nEntryHeight;
2959 nInnerHeight++;
2960 nInnerHeight *= nEntryHeight;
2961 aFloatSz.setHeight( nInnerHeight + nTop + nBottom );
2964 if (aFloatSz.Width() < aSz.Width())
2966 // The max width of list box entries exceeds the window width.
2967 // Account for the scroll bar height.
2968 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2969 aFloatSz.AdjustHeight(nSBWidth );
2972 return aFloatSz;
2975 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
2977 if( IsInPopupMode() )
2978 return;
2980 Size aFloatSz = CalcFloatSize();
2982 SetSizePixel( aFloatSz );
2983 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2985 sal_Int32 nPos = mpImplLB->GetEntryList().GetSelectedEntryPos( 0 );
2986 mnPopupModeStartSaveSelection = nPos;
2988 Size aSz = GetParent()->GetSizePixel();
2989 Point aPos = GetParent()->GetPosPixel();
2990 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2991 // FIXME: this ugly hack is for Mac/Aqua
2992 // should be replaced by a real mechanism to place the float rectangle
2993 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2994 GetParent()->IsNativeWidgetEnabled() )
2996 const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
2997 aPos.AdjustX(nLeft );
2998 aPos.AdjustY(nTop );
2999 aSz.AdjustWidth( -(nLeft + nRight) );
3000 aSz.AdjustHeight( -(nTop + nBottom) );
3002 tools::Rectangle aRect( aPos, aSz );
3004 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
3005 // where the document is unmirrored
3006 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
3007 vcl::Window *pGrandparent = GetParent()->GetParent();
3008 const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
3010 if( pGrandparent->GetOutDev()->ImplIsAntiparallel() )
3011 pGrandparentOutDev->ReMirror( aRect );
3013 // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
3014 StartPopupMode( aRect, LISTBOX_FLOATWINPOPUPFLAGS );
3016 if( nPos != LISTBOX_ENTRY_NOTFOUND )
3017 mpImplLB->ShowProminentEntry( nPos );
3019 if( bStartTracking )
3020 mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
3022 if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
3023 mpImplLB->GetMainWindow()->GrabFocus();
3025 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3029 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */