build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / imp_listbox.cxx
blob25c7fa2c5b5bcc55fea0dfc6f4ae5fcfe3bc3466
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 <tools/debug.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/settings.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/scrbar.hxx>
26 #include <vcl/help.hxx>
27 #include <vcl/lstbox.hxx>
28 #include <vcl/unohelp.hxx>
29 #include <vcl/i18nhelp.hxx>
31 #include "listbox.hxx"
32 #include <controldata.hxx>
33 #include <svdata.hxx>
34 #include <window.h>
36 #include <com/sun/star/i18n/XCollator.hpp>
37 #include <com/sun/star/accessibility/XAccessible.hpp>
38 #include <com/sun/star/accessibility/AccessibleRole.hpp>
40 #include <rtl/instance.hxx>
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 void ImplInitDropDownButton( PushButton* pButton )
52 pButton->SetSymbol( SymbolType::SPIN_DOWN );
54 if ( pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
55 && ! pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
56 pButton->SetBackground();
59 ImplEntryList::ImplEntryList( vcl::Window* pWindow )
61 mpWindow = pWindow;
62 mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
63 mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
64 mnImages = 0;
65 mbCallSelectionChangedHdl = true;
67 mnMRUCount = 0;
68 mnMaxMRUCount = 0;
71 ImplEntryList::~ImplEntryList()
73 Clear();
76 void ImplEntryList::Clear()
78 mnImages = 0;
79 maEntries.clear();
82 void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
84 if (0 <= nPos && static_cast<size_t>(nPos) < maEntries.size())
86 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+nPos;
88 if ( ( (*iter)->mbIsSelected != bSelect ) &&
89 ( ( (*iter)->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE ) )
91 (*iter)->mbIsSelected = bSelect;
92 if ( mbCallSelectionChangedHdl )
93 maSelectionChangedHdl.Call( nPos );
98 namespace
100 struct theSorter
101 : public rtl::StaticWithInit< comphelper::string::NaturalStringSorter, theSorter >
103 comphelper::string::NaturalStringSorter operator () ()
105 return comphelper::string::NaturalStringSorter(
106 ::comphelper::getProcessComponentContext(),
107 Application::GetSettings().GetLanguageTag().getLocale());
112 sal_Int32 ListBox::NaturalSortCompare(const OUString &rA, const OUString &rB)
114 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
115 return rSorter.compare(rA, rB);
118 sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
120 if (nPos < 0 || LISTBOX_MAX_ENTRIES <= maEntries.size())
121 return LISTBOX_ERROR;
123 if ( !!pNewEntry->maImage )
124 mnImages++;
126 sal_Int32 insPos = 0;
127 const sal_Int32 nEntriesSize = static_cast<sal_Int32>(maEntries.size());
129 if ( !bSort || maEntries.empty())
131 if (0 <= nPos && nPos < nEntriesSize)
133 insPos = nPos;
134 maEntries.insert( maEntries.begin() + nPos, std::unique_ptr<ImplEntryType>(pNewEntry) );
136 else
138 insPos = nEntriesSize;
139 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
142 else
144 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
146 const OUString& rStr = pNewEntry->maStr;
148 ImplEntryType* pTemp = GetEntry( nEntriesSize-1 );
152 sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);
154 // fast insert for sorted data
155 if ( nComp >= 0 )
157 insPos = nEntriesSize;
158 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
160 else
162 pTemp = GetEntry( mnMRUCount );
164 nComp = rSorter.compare(rStr, pTemp->maStr);
165 if ( nComp <= 0 )
167 insPos = 0;
168 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
170 else
172 sal_uLong nLow = mnMRUCount;
173 sal_uLong nHigh = maEntries.size()-1;
174 sal_Int32 nMid;
176 // binary search
179 nMid = static_cast<sal_Int32>((nLow + nHigh) / 2);
180 pTemp = GetEntry( nMid );
182 nComp = rSorter.compare(rStr, pTemp->maStr);
184 if ( nComp < 0 )
185 nHigh = nMid-1;
186 else
188 if ( nComp > 0 )
189 nLow = nMid + 1;
190 else
191 break;
194 while ( nLow <= nHigh );
196 if ( nComp >= 0 )
197 nMid++;
199 insPos = nMid;
200 maEntries.insert(maEntries.begin()+nMid, std::unique_ptr<ImplEntryType>(pNewEntry));
204 catch (uno::RuntimeException& )
206 // XXX this is arguable, if the exception occurred because pNewEntry is
207 // garbage you wouldn't insert it. If the exception occurred because the
208 // Collator implementation is garbage then give the user a chance to see
209 // his stuff
210 insPos = 0;
211 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
216 return insPos;
219 void ImplEntryList::RemoveEntry( sal_Int32 nPos )
221 if (0 <= nPos && static_cast<size_t>(nPos) < maEntries.size())
223 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+ nPos;
225 if ( !!(*iter)->maImage )
226 mnImages--;
228 maEntries.erase(iter);
232 sal_Int32 ImplEntryList::FindEntry( const OUString& rString, bool bSearchMRUArea ) const
234 const sal_Int32 nEntries = static_cast<sal_Int32>(maEntries.size());
235 for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
237 OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n]->maStr ) );
238 if ( aComp == rString )
239 return n;
241 return LISTBOX_ENTRY_NOTFOUND;
244 sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStart, bool bLazy ) const
246 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
247 sal_Int32 nEntryCount = GetEntryCount();
249 const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
250 for ( sal_Int32 n = nStart; n < nEntryCount; )
252 ImplEntryType* pImplEntry = GetEntry( n );
253 bool bMatch;
254 if ( bLazy )
256 bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
258 else
260 bMatch = pImplEntry->maStr.startsWith(rStr);
262 if ( bMatch )
264 nPos = n;
265 break;
268 n++;
271 return nPos;
274 sal_Int32 ImplEntryList::FindEntry( const void* pData ) const
276 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
277 for ( sal_Int32 n = GetEntryCount(); n; )
279 ImplEntryType* pImplEntry = GetEntry( --n );
280 if ( pImplEntry->mpUserData == pData )
282 nPos = n;
283 break;
286 return nPos;
289 long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex ) const
291 long nHeight = 0;
292 sal_Int32 nStart = i_nEndIndex > i_nBeginIndex ? i_nBeginIndex : i_nEndIndex;
293 sal_Int32 nStop = i_nEndIndex > i_nBeginIndex ? i_nEndIndex : i_nBeginIndex;
294 sal_Int32 nEntryCount = GetEntryCount();
295 if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
297 // sanity check
298 if( nStop > nEntryCount-1 )
299 nStop = nEntryCount-1;
300 if (nStart < 0)
301 nStart = 0;
302 else if( nStart > nEntryCount-1 )
303 nStart = nEntryCount-1;
305 sal_Int32 nIndex = nStart;
306 while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
308 long nPosHeight = GetEntryPtr( nIndex )->mnHeight;
309 if (nHeight > ::std::numeric_limits<long>::max() - nPosHeight)
311 SAL_WARN( "vcl", "ImplEntryList::GetAddedHeight: truncated");
312 break;
314 nHeight += nPosHeight;
315 nIndex++;
318 else
319 nHeight = 0;
320 return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
323 long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
325 ImplEntryType* pImplEntry = GetEntry( nPos );
326 return pImplEntry ? pImplEntry->mnHeight : 0;
329 OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
331 OUString aEntryText;
332 ImplEntryType* pImplEntry = GetEntry( nPos );
333 if ( pImplEntry )
334 aEntryText = pImplEntry->maStr;
335 return aEntryText;
338 bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
340 bool bImage = false;
341 ImplEntryType* pImplEntry = GetEntry( nPos );
342 if ( pImplEntry )
343 bImage = !!pImplEntry->maImage;
344 return bImage;
347 Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
349 Image aImage;
350 ImplEntryType* pImplEntry = GetEntry( nPos );
351 if ( pImplEntry )
352 aImage = pImplEntry->maImage;
353 return aImage;
356 void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
358 ImplEntryType* pImplEntry = GetEntry( nPos );
359 if ( pImplEntry )
360 pImplEntry->mpUserData = pNewData;
363 void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
365 ImplEntryType* pImplEntry = GetEntry( nPos );
366 return pImplEntry ? pImplEntry->mpUserData : nullptr;
369 void ImplEntryList::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
371 ImplEntryType* pImplEntry = GetEntry( nPos );
372 if ( pImplEntry )
373 pImplEntry->mnFlags = nFlags;
376 ListBoxEntryFlags ImplEntryList::GetEntryFlags( sal_Int32 nPos ) const
378 ImplEntryType* pImplEntry = GetEntry( nPos );
379 return pImplEntry ? pImplEntry->mnFlags : ListBoxEntryFlags::NONE;
382 sal_Int32 ImplEntryList::GetSelectEntryCount() const
384 sal_Int32 nSelCount = 0;
385 for ( sal_Int32 n = GetEntryCount(); n; )
387 ImplEntryType* pImplEntry = GetEntry( --n );
388 if ( pImplEntry->mbIsSelected )
389 nSelCount++;
391 return nSelCount;
394 OUString ImplEntryList::GetSelectEntry( sal_Int32 nIndex ) const
396 return GetEntryText( GetSelectEntryPos( nIndex ) );
399 sal_Int32 ImplEntryList::GetSelectEntryPos( sal_Int32 nIndex ) const
401 sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
402 sal_Int32 nSel = 0;
403 sal_Int32 nEntryCount = GetEntryCount();
405 for ( sal_Int32 n = 0; n < nEntryCount; n++ )
407 ImplEntryType* pImplEntry = GetEntry( n );
408 if ( pImplEntry->mbIsSelected )
410 if ( nSel == nIndex )
412 nSelEntryPos = n;
413 break;
415 nSel++;
419 return nSelEntryPos;
422 bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
424 ImplEntryType* pImplEntry = GetEntry( nIndex );
425 return pImplEntry && pImplEntry->mbIsSelected;
428 bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
430 ImplEntryType* pImplEntry = GetEntry( nPos );
431 return pImplEntry == nullptr || ((pImplEntry->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE);
434 sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ )
436 if( IsEntrySelectable( nPos ) )
437 return nPos;
439 if( bForward )
441 for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
443 if( IsEntrySelectable( nPos ) )
444 return nPos;
447 else
449 while( nPos )
451 nPos--;
452 if( IsEntrySelectable( nPos ) )
453 return nPos;
457 return LISTBOX_ENTRY_NOTFOUND;
460 ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
461 Control( pParent, 0 ),
462 maQuickSelectionEngine( *this )
464 mpEntryList = new ImplEntryList( this );
466 mnTop = 0;
467 mnLeft = 0;
468 mnBorder = 1;
469 mnSelectModifier = 0;
470 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
471 mbTrack = false;
472 mbImgsDiffSz = false;
473 mbTravelSelect = false;
474 mbTrackingSelect = false;
475 mbSelectionChanged = false;
476 mbMouseMoveSelect = false;
477 mbMulti = false;
478 mbStackMode = false;
479 mbGrabFocus = false;
480 mbUserDrawEnabled = false;
481 mbInUserDraw = false;
482 mbReadOnly = false;
483 mbHasFocusRect = false;
484 mbRight = ( nWinStyle & WB_RIGHT );
485 mbCenter = ( nWinStyle & WB_CENTER );
486 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
487 mbSort = ( nWinStyle & WB_SORT );
488 mbEdgeBlending = false;
490 // pb: #106948# explicit mirroring for calc
491 mbMirroring = false;
493 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
494 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
495 mnSeparatorPos = LISTBOX_ENTRY_NOTFOUND;
496 meProminentType = ProminentEntry::TOP;
498 SetLineColor();
499 SetTextFillColor();
500 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
502 ApplySettings(*this);
503 ImplCalcMetrics();
506 ImplListBoxWindow::~ImplListBoxWindow()
508 disposeOnce();
511 void ImplListBoxWindow::dispose()
513 delete mpEntryList;
514 Control::dispose();
517 void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
519 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
521 vcl::Font aFont = rStyleSettings.GetFieldFont();
522 if (IsControlFont())
523 aFont.Merge(GetControlFont());
524 SetZoomedPointFont(rRenderContext, aFont);
526 Color aTextColor = rStyleSettings.GetFieldTextColor();
527 if (IsControlForeground())
528 aTextColor = GetControlForeground();
529 rRenderContext.SetTextColor(aTextColor);
531 if (IsControlBackground())
532 rRenderContext.SetBackground(GetControlBackground());
533 else
534 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
537 void ImplListBoxWindow::ImplCalcMetrics()
539 mnMaxWidth = 0;
540 mnMaxTxtWidth = 0;
541 mnMaxImgWidth = 0;
542 mnMaxImgTxtWidth= 0;
543 mnMaxImgHeight = 0;
545 mnTextHeight = (sal_uInt16)GetTextHeight();
546 mnMaxTxtHeight = mnTextHeight + mnBorder;
547 mnMaxHeight = mnMaxTxtHeight;
549 if ( maUserItemSize.Height() > mnMaxHeight )
550 mnMaxHeight = (sal_uInt16) maUserItemSize.Height();
551 if ( maUserItemSize.Width() > mnMaxWidth )
552 mnMaxWidth= (sal_uInt16) maUserItemSize.Width();
554 for ( sal_Int32 n = mpEntryList->GetEntryCount(); n; )
556 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
557 ImplUpdateEntryMetrics( *pEntry );
560 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
562 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->mnHeight );
563 maFocusRect.SetSize( aSz );
567 void ImplListBoxWindow::Clear()
569 mpEntryList->Clear();
571 mnMaxHeight = mnMaxTxtHeight;
572 mnMaxWidth = 0;
573 mnMaxTxtWidth = 0;
574 mnMaxImgTxtWidth= 0;
575 mnMaxImgWidth = 0;
576 mnMaxImgHeight = 0;
577 mnTop = 0;
578 mnLeft = 0;
579 mbImgsDiffSz = false;
580 ImplClearLayoutData();
582 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
583 maQuickSelectionEngine.Reset();
585 Invalidate();
588 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
590 ImplClearLayoutData();
591 maUserItemSize = rSz;
592 ImplCalcMetrics();
595 struct ImplEntryMetrics
597 bool bText;
598 bool bImage;
599 long nEntryWidth;
600 long nEntryHeight;
601 long nTextWidth;
602 long nImgWidth;
603 long nImgHeight;
606 void ImplListBoxWindow::EnableQuickSelection( bool b )
608 maQuickSelectionEngine.SetEnabled( b );
611 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
613 ImplEntryMetrics aMetrics;
614 aMetrics.bText = !rEntry.maStr.isEmpty();
615 aMetrics.bImage = !!rEntry.maImage;
616 aMetrics.nEntryWidth = 0;
617 aMetrics.nEntryHeight = 0;
618 aMetrics.nTextWidth = 0;
619 aMetrics.nImgWidth = 0;
620 aMetrics.nImgHeight = 0;
622 if ( aMetrics.bText )
624 if( (rEntry.mnFlags & ListBoxEntryFlags::MultiLine) )
626 // multiline case
627 Size aCurSize( PixelToLogic( GetSizePixel() ) );
628 // set the current size to a large number
629 // GetTextRect should shrink it to the actual size
630 aCurSize.Height() = 0x7fffff;
631 Rectangle aTextRect( Point( 0, 0 ), aCurSize );
632 aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
633 aMetrics.nTextWidth = aTextRect.GetWidth();
634 if( aMetrics.nTextWidth > mnMaxTxtWidth )
635 mnMaxTxtWidth = aMetrics.nTextWidth;
636 aMetrics.nEntryWidth = mnMaxTxtWidth;
637 aMetrics.nEntryHeight = aTextRect.GetHeight() + mnBorder;
639 else
641 // normal single line case
642 aMetrics.nTextWidth = (sal_uInt16)GetTextWidth( rEntry.maStr );
643 if( aMetrics.nTextWidth > mnMaxTxtWidth )
644 mnMaxTxtWidth = aMetrics.nTextWidth;
645 aMetrics.nEntryWidth = mnMaxTxtWidth;
646 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
649 if ( aMetrics.bImage )
651 Size aImgSz = rEntry.maImage.GetSizePixel();
652 aMetrics.nImgWidth = (sal_uInt16) CalcZoom( aImgSz.Width() );
653 aMetrics.nImgHeight = (sal_uInt16) CalcZoom( aImgSz.Height() );
655 if( mnMaxImgWidth && ( aMetrics.nImgWidth != mnMaxImgWidth ) )
656 mbImgsDiffSz = true;
657 else if ( mnMaxImgHeight && ( aMetrics.nImgHeight != mnMaxImgHeight ) )
658 mbImgsDiffSz = true;
660 if( aMetrics.nImgWidth > mnMaxImgWidth )
661 mnMaxImgWidth = aMetrics.nImgWidth;
662 if( aMetrics.nImgHeight > mnMaxImgHeight )
663 mnMaxImgHeight = aMetrics.nImgHeight;
665 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
666 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
669 if ( IsUserDrawEnabled() || aMetrics.bImage )
671 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
672 if ( aMetrics.bText )
673 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
674 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
675 aMetrics.nEntryHeight );
678 if ( !aMetrics.bText && !aMetrics.bImage && !IsUserDrawEnabled() )
680 // entries which have no (aka an empty) text, and no image,
681 // and are not user-drawn, should be shown nonetheless
682 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
685 if ( aMetrics.nEntryWidth > mnMaxWidth )
686 mnMaxWidth = aMetrics.nEntryWidth;
687 if ( aMetrics.nEntryHeight > mnMaxHeight )
688 mnMaxHeight = aMetrics.nEntryHeight;
690 rEntry.mnHeight = aMetrics.nEntryHeight;
693 void ImplListBoxWindow::ImplCallSelect()
695 if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
697 // Insert the selected entry as MRU, if not already first MRU
698 sal_Int32 nSelected = GetEntryList()->GetSelectEntryPos( 0 );
699 sal_Int32 nMRUCount = GetEntryList()->GetMRUCount();
700 OUString aSelected = GetEntryList()->GetEntryText( nSelected );
701 sal_Int32 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, true );
702 if ( nFirstMatchingEntryPos || !nMRUCount )
704 bool bSelectNewEntry = false;
705 if ( nFirstMatchingEntryPos < nMRUCount )
707 RemoveEntry( nFirstMatchingEntryPos );
708 nMRUCount--;
709 if ( nFirstMatchingEntryPos == nSelected )
710 bSelectNewEntry = true;
712 else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
714 RemoveEntry( nMRUCount - 1 );
715 nMRUCount--;
718 ImplClearLayoutData();
720 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
721 pNewEntry->mbIsSelected = bSelectNewEntry;
722 GetEntryList()->InsertEntry( 0, pNewEntry, false );
723 ImplUpdateEntryMetrics( *pNewEntry );
724 GetEntryList()->SetMRUCount( ++nMRUCount );
725 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
726 maMRUChangedHdl.Call( nullptr );
730 maSelectHdl.Call( nullptr );
731 mbSelectionChanged = false;
734 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
736 if (nPos < 0 || LISTBOX_MAX_ENTRIES <= mpEntryList->GetEntryCount())
737 return LISTBOX_ERROR;
739 ImplClearLayoutData();
740 sal_Int32 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, mbSort );
742 if( (GetStyle() & WB_WORDBREAK) )
743 pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;
745 ImplUpdateEntryMetrics( *pNewEntry );
746 return nNewPos;
749 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
751 ImplClearLayoutData();
752 mpEntryList->RemoveEntry( nPos );
753 if( mnCurrentPos >= mpEntryList->GetEntryCount() )
754 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
755 ImplCalcMetrics();
758 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
760 mpEntryList->SetEntryFlags( nPos, nFlags );
761 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
762 if( pEntry )
763 ImplUpdateEntryMetrics( *pEntry );
766 void ImplListBoxWindow::ImplShowFocusRect()
768 if ( mbHasFocusRect )
769 HideFocus();
770 ShowFocus( maFocusRect );
771 mbHasFocusRect = true;
774 void ImplListBoxWindow::ImplHideFocusRect()
776 if ( mbHasFocusRect )
778 HideFocus();
779 mbHasFocusRect = false;
783 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
785 long nY = mnBorder;
787 sal_Int32 nSelect = mnTop;
788 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
789 while( pEntry && rPoint.Y() > pEntry->mnHeight + nY )
791 nY += pEntry->mnHeight;
792 pEntry = mpEntryList->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( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
807 PixelToLogic( GetSizePixel() ).Height() )
809 bRet = true;
813 return bRet;
816 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
818 sal_Int32 nPos = mnTop;
819 long nWindowHeight = GetSizePixel().Height();
820 sal_Int32 nCount = mpEntryList->GetEntryCount();
821 long nDiff;
822 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
823 nPos++;
825 if( nDiff > nWindowHeight && nPos > mnTop )
826 nPos--;
828 if( nPos >= nCount )
829 nPos = nCount-1;
831 return nPos;
834 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
836 mbMouseMoveSelect = false; // only till the first MouseButtonDown
837 maQuickSelectionEngine.Reset();
839 if ( !IsReadOnly() )
841 if( rMEvt.GetClicks() == 1 )
843 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
844 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
846 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
847 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
848 else
849 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
851 mnCurrentPos = nSelect;
852 mbTrackingSelect = true;
853 bool bCurPosChange = (mnCurrentPos != nSelect);
854 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
855 mbTrackingSelect = false;
856 if ( mbGrabFocus )
857 GrabFocus();
859 StartTracking( StartTrackingFlags::ScrollRepeat );
862 if( rMEvt.GetClicks() == 2 )
864 maDoubleClickHdl.Call( this );
867 else // if ( mbGrabFocus )
869 GrabFocus();
873 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
875 if ( rMEvt.IsLeaveWindow() )
877 if ( mbStackMode && IsMouseMoveSelect() && IsReallyVisible() )
879 if ( rMEvt.GetPosPixel().Y() < 0 )
881 DeselectAll();
882 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
883 SetTopEntry( 0 );
884 if ( mbStackMode )
886 mbTravelSelect = true;
887 mnSelectModifier = rMEvt.GetModifier();
888 ImplCallSelect();
889 mbTravelSelect = false;
895 else if ( ( ( !mbMulti && IsMouseMoveSelect() ) || mbStackMode ) && mpEntryList->GetEntryCount() )
897 Point aPoint;
898 Rectangle aRect( aPoint, GetOutputSizePixel() );
899 if( aRect.IsInside( rMEvt.GetPosPixel() ) )
901 if ( IsMouseMoveSelect() )
903 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
904 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
905 nSelect = mpEntryList->GetEntryCount() - 1;
906 nSelect = std::min( nSelect, GetLastVisibleEntry() );
907 nSelect = std::min( nSelect, (sal_Int32) ( mpEntryList->GetEntryCount() - 1 ) );
908 // Select only visible Entries with MouseMove, otherwise Tracking...
909 if ( IsVisible( nSelect ) &&
910 mpEntryList->IsEntrySelectable( nSelect ) &&
911 ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() || ( nSelect != GetEntryList()->GetSelectEntryPos( 0 ) ) ) )
913 mbTrackingSelect = true;
914 if ( SelectEntries( nSelect, LET_TRACKING ) )
916 if ( mbStackMode )
918 mbTravelSelect = true;
919 mnSelectModifier = rMEvt.GetModifier();
920 ImplCallSelect();
921 mbTravelSelect = false;
923 // When list box selection change by mouse move, notify
924 // VCLEVENT_LISTBOX_SELECT vcl event.
925 else
927 maListItemSelectHdl.Call(nullptr);
930 mbTrackingSelect = false;
934 // if the DD button was pressed and someone moved into the ListBox
935 // with the mouse button pressed...
936 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
938 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
939 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
940 else
941 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
943 if ( mbStackMode && ( mpEntryList->GetSelectionAnchor() == LISTBOX_ENTRY_NOTFOUND ) )
944 mpEntryList->SetSelectionAnchor( 0 );
946 StartTracking( StartTrackingFlags::ScrollRepeat );
952 void ImplListBoxWindow::DeselectAll()
954 while ( GetEntryList()->GetSelectEntryCount() )
956 sal_Int32 nS = GetEntryList()->GetSelectEntryPos( 0 );
957 SelectEntry( nS, false );
961 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
963 if( (mpEntryList->IsEntryPosSelected( nPos ) != bSelect) && mpEntryList->IsEntrySelectable( nPos ) )
965 ImplHideFocusRect();
966 if( bSelect )
968 if( !mbMulti )
970 // deselect the selected entry
971 sal_Int32 nDeselect = GetEntryList()->GetSelectEntryPos( 0 );
972 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
974 //SelectEntryPos( nDeselect, false );
975 GetEntryList()->SelectEntry( nDeselect, false );
976 if (IsUpdateMode() && IsReallyVisible())
977 Invalidate();
980 mpEntryList->SelectEntry( nPos, true );
981 mnCurrentPos = nPos;
982 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
984 Invalidate();
985 if ( !IsVisible( nPos ) )
987 ImplClearLayoutData();
988 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
989 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
991 Resize();
992 ShowProminentEntry( nPos );
994 else
996 ShowProminentEntry( nPos );
1001 else
1003 mpEntryList->SelectEntry( nPos, false );
1004 Invalidate();
1006 mbSelectionChanged = true;
1010 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
1012 bool bSelectionChanged = false;
1014 if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
1016 bool bFocusChanged = false;
1018 // here (Single-ListBox) only one entry can be deselected
1019 if( !mbMulti )
1021 sal_Int32 nDeselect = mpEntryList->GetSelectEntryPos( 0 );
1022 if( nSelect != nDeselect )
1024 SelectEntry( nSelect, true );
1025 mpEntryList->SetLastSelected( nSelect );
1026 bFocusChanged = true;
1027 bSelectionChanged = true;
1030 // MultiListBox without Modifier
1031 else if( mbSimpleMode && !bCtrl && !bShift )
1033 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1034 for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1036 bool bSelect = nPos == nSelect;
1037 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1039 SelectEntry( nPos, bSelect );
1040 bFocusChanged = true;
1041 bSelectionChanged = true;
1044 mpEntryList->SetLastSelected( nSelect );
1045 mpEntryList->SetSelectionAnchor( nSelect );
1047 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1048 else if( ( !mbSimpleMode /* && !bShift */ ) || ( (mbSimpleMode && ( bCtrl || bShift )) || mbStackMode ) )
1050 // Space for selection change
1051 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1053 bool bSelect = ( mbStackMode && IsMouseMoveSelect() ) || !mpEntryList->IsEntryPosSelected( nSelect );
1054 if ( mbStackMode )
1056 sal_Int32 n;
1057 if ( bSelect )
1059 // All entries before nSelect must be selected...
1060 for ( n = 0; n < nSelect; n++ )
1061 SelectEntry( n, true );
1063 if ( !bSelect )
1065 for ( n = nSelect+1; n < mpEntryList->GetEntryCount(); n++ )
1066 SelectEntry( n, false );
1069 SelectEntry( nSelect, bSelect );
1070 mpEntryList->SetLastSelected( nSelect );
1071 mpEntryList->SetSelectionAnchor( mbStackMode ? 0 : nSelect );
1072 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1073 mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1074 bFocusChanged = true;
1075 bSelectionChanged = true;
1077 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1078 ( (bShift||mbStackMode) && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1080 mnCurrentPos = nSelect;
1081 bFocusChanged = true;
1083 sal_Int32 nAnchor = mpEntryList->GetSelectionAnchor();
1084 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && ( mpEntryList->GetSelectEntryCount() || mbStackMode ) )
1086 nAnchor = mbStackMode ? 0 : mpEntryList->GetSelectEntryPos( mpEntryList->GetSelectEntryCount() - 1 );
1088 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1090 // All entries from Anchor to nSelect have to be selected
1091 sal_Int32 nStart = std::min( nSelect, nAnchor );
1092 sal_Int32 nEnd = std::max( nSelect, nAnchor );
1093 for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1095 if ( !mpEntryList->IsEntryPosSelected( n ) )
1097 SelectEntry( n, true );
1098 bSelectionChanged = true;
1102 // if appropriate some more has to be deselected...
1103 sal_Int32 nLast = mpEntryList->GetLastSelected();
1104 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1106 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1108 for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1110 if ( mpEntryList->IsEntryPosSelected( n ) )
1112 SelectEntry( n, false );
1113 bSelectionChanged = true;
1117 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1119 for ( sal_Int32 n = nLast; n < nSelect; n++ )
1121 if ( mpEntryList->IsEntryPosSelected( n ) )
1123 SelectEntry( n, false );
1124 bSelectionChanged = true;
1129 mpEntryList->SetLastSelected( nSelect );
1132 else if( eLET != LET_TRACKING )
1134 ImplHideFocusRect();
1135 Invalidate();
1136 bFocusChanged = true;
1139 else if( bShift )
1141 bFocusChanged = true;
1144 if( bSelectionChanged )
1145 mbSelectionChanged = true;
1147 if( bFocusChanged )
1149 long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop );
1150 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1151 Size aSz( maFocusRect.GetWidth(),
1152 mpEntryList->GetEntryHeight( nSelect ) );
1153 maFocusRect.SetSize( aSz );
1154 if( HasFocus() )
1155 ImplShowFocusRect();
1156 if (bSelectPosChange)
1158 maFocusHdl.Call(nSelect);
1161 ImplClearLayoutData();
1163 return bSelectionChanged;
1166 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1168 Point aPoint;
1169 Rectangle aRect( aPoint, GetOutputSizePixel() );
1170 bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1172 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1174 if ( bInside && !rTEvt.IsTrackingCanceled() )
1176 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1177 ImplCallSelect();
1179 else
1181 maCancelHdl.Call( nullptr );
1182 if ( !mbMulti )
1184 mbTrackingSelect = true;
1185 SelectEntry( mnTrackingSaveSelection, true );
1186 mbTrackingSelect = false;
1187 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1189 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1190 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1191 Size aSz( maFocusRect.GetWidth(),
1192 mpEntryList->GetEntryHeight( mnCurrentPos ) );
1193 maFocusRect.SetSize( aSz );
1194 ImplShowFocusRect();
1199 mbTrack = false;
1201 else
1203 bool bTrackOrQuickClick = mbTrack;
1204 if( !mbTrack )
1206 if ( bInside )
1208 mbTrack = true;
1211 // this case only happens, if the mouse button is pressed very briefly
1212 if( rTEvt.IsTrackingEnded() && mbTrack )
1214 bTrackOrQuickClick = true;
1215 mbTrack = false;
1219 if( bTrackOrQuickClick )
1221 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1222 Point aPt( aMEvt.GetPosPixel() );
1223 bool bShift = aMEvt.IsShift();
1224 bool bCtrl = aMEvt.IsMod1();
1226 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1227 if( aPt.Y() < 0 )
1229 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1231 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1232 if( nSelect < mnTop )
1233 SetTopEntry( mnTop-1 );
1236 else if( aPt.Y() > GetOutputSizePixel().Height() )
1238 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1240 nSelect = std::min( (sal_Int32)(mnCurrentPos+1), (sal_Int32)(mpEntryList->GetEntryCount()-1) );
1241 if( nSelect >= GetLastVisibleEntry() )
1242 SetTopEntry( mnTop+1 );
1245 else
1247 nSelect = (sal_Int32) ( ( aPt.Y() + mnBorder ) / mnMaxHeight ) + (sal_Int32) mnTop;
1248 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1249 nSelect = std::min( nSelect, (sal_Int32) ( mpEntryList->GetEntryCount() - 1 ) );
1252 if ( bInside )
1254 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() )
1256 mbTrackingSelect = true;
1257 if ( SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl ) )
1259 if ( mbStackMode )
1261 mbTravelSelect = true;
1262 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1263 ImplCallSelect();
1264 mbTravelSelect = false;
1267 mbTrackingSelect = false;
1270 else
1272 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
1274 mbTrackingSelect = true;
1275 SelectEntry( GetEntryList()->GetSelectEntryPos( 0 ), false );
1276 mbTrackingSelect = false;
1278 else if ( mbStackMode )
1280 if ( ( rTEvt.GetMouseEvent().GetPosPixel().X() > 0 ) && ( rTEvt.GetMouseEvent().GetPosPixel().X() < aRect.Right() ) )
1282 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 ) || ( rTEvt.GetMouseEvent().GetPosPixel().Y() > GetOutputSizePixel().Height() ) )
1284 bool bSelectionChanged = false;
1285 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 )
1286 && !mnCurrentPos )
1288 if ( mpEntryList->IsEntryPosSelected( 0 ) )
1290 SelectEntry( 0, false );
1291 bSelectionChanged = true;
1292 nSelect = LISTBOX_ENTRY_NOTFOUND;
1296 else
1298 mbTrackingSelect = true;
1299 bSelectionChanged = SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl );
1300 mbTrackingSelect = false;
1303 if ( bSelectionChanged )
1305 mbSelectionChanged = true;
1306 mbTravelSelect = true;
1307 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1308 ImplCallSelect();
1309 mbTravelSelect = false;
1315 mnCurrentPos = nSelect;
1316 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1318 ImplHideFocusRect();
1320 else
1322 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1323 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1324 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1325 maFocusRect.SetSize( aSz );
1326 ImplShowFocusRect();
1332 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1334 if( !ProcessKeyInput( rKEvt ) )
1335 Control::KeyInput( rKEvt );
1338 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1340 // entry to be selected
1341 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1342 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1344 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1346 bool bShift = aKeyCode.IsShift();
1347 bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1348 bool bMod2 = aKeyCode.IsMod2();
1349 bool bDone = false;
1350 bool bHandleKey = false;
1352 switch( aKeyCode.GetCode() )
1354 case KEY_UP:
1356 if ( IsReadOnly() )
1358 if ( GetTopEntry() )
1359 SetTopEntry( GetTopEntry()-1 );
1361 else if ( !bMod2 )
1363 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1365 nSelect = mpEntryList->FindFirstSelectable( 0 );
1367 else if ( mnCurrentPos )
1369 // search first selectable above the current position
1370 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1373 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1374 SetTopEntry( mnTop-1 );
1376 bDone = true;
1378 maQuickSelectionEngine.Reset();
1380 break;
1382 case KEY_DOWN:
1384 if ( IsReadOnly() )
1386 SetTopEntry( GetTopEntry()+1 );
1388 else if ( !bMod2 )
1390 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1392 nSelect = mpEntryList->FindFirstSelectable( 0 );
1394 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1396 // search first selectable below the current position
1397 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1 );
1400 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1401 SetTopEntry( mnTop+1 );
1403 bDone = true;
1405 maQuickSelectionEngine.Reset();
1407 break;
1409 case KEY_PAGEUP:
1411 if ( IsReadOnly() )
1413 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1414 SetTopEntry( ( mnTop > nCurVis ) ?
1415 (mnTop-nCurVis) : 0 );
1417 else if ( !bCtrl && !bMod2 )
1419 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1421 nSelect = mpEntryList->FindFirstSelectable( 0 );
1423 else if ( mnCurrentPos )
1425 if( mnCurrentPos == mnTop )
1427 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1428 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1431 // find first selectable starting from mnTop looking forward
1432 nSelect = mpEntryList->FindFirstSelectable( mnTop );
1434 bDone = true;
1436 maQuickSelectionEngine.Reset();
1438 break;
1440 case KEY_PAGEDOWN:
1442 if ( IsReadOnly() )
1444 SetTopEntry( GetLastVisibleEntry() );
1446 else if ( !bCtrl && !bMod2 )
1448 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1450 nSelect = mpEntryList->FindFirstSelectable( 0 );
1452 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1454 sal_Int32 nCount = mpEntryList->GetEntryCount();
1455 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1456 sal_Int32 nTmp = std::min( nCurVis, nCount );
1457 nTmp += mnTop - 1;
1458 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1460 long nTmp2 = std::min( (long)(nCount-nCurVis), (long)((long)mnTop+(long)nCurVis-1) );
1461 nTmp2 = std::max( (long)0 , nTmp2 );
1462 nTmp = (sal_Int32)(nTmp2+(nCurVis-1) );
1463 SetTopEntry( (sal_Int32)nTmp2 );
1465 // find first selectable starting from nTmp looking backwards
1466 nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1468 bDone = true;
1470 maQuickSelectionEngine.Reset();
1472 break;
1474 case KEY_HOME:
1476 if ( IsReadOnly() )
1478 SetTopEntry( 0 );
1480 else if ( !bCtrl && !bMod2 )
1482 if ( mnCurrentPos )
1484 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
1485 if( mnTop != 0 )
1486 SetTopEntry( 0 );
1488 bDone = true;
1491 maQuickSelectionEngine.Reset();
1493 break;
1495 case KEY_END:
1497 if ( IsReadOnly() )
1499 SetTopEntry( 0xFFFF );
1501 else if ( !bCtrl && !bMod2 )
1503 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1505 nSelect = mpEntryList->FindFirstSelectable( 0 );
1507 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1509 sal_Int32 nCount = mpEntryList->GetEntryCount();
1510 nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1511 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1512 if( nCount > nCurVis )
1513 SetTopEntry( nCount - nCurVis );
1515 bDone = true;
1517 maQuickSelectionEngine.Reset();
1519 break;
1521 case KEY_LEFT:
1523 if ( !bCtrl && !bMod2 )
1525 ScrollHorz( -HORZ_SCROLL );
1526 bDone = true;
1528 maQuickSelectionEngine.Reset();
1530 break;
1532 case KEY_RIGHT:
1534 if ( !bCtrl && !bMod2 )
1536 ScrollHorz( HORZ_SCROLL );
1537 bDone = true;
1539 maQuickSelectionEngine.Reset();
1541 break;
1543 case KEY_RETURN:
1545 if ( !bMod2 && !IsReadOnly() )
1547 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1548 ImplCallSelect();
1549 bDone = false; // do not catch RETURN
1551 maQuickSelectionEngine.Reset();
1553 break;
1555 case KEY_SPACE:
1557 if ( !bMod2 && !IsReadOnly() )
1559 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) || mbStackMode ) )
1561 nSelect = mnCurrentPos;
1562 eLET = LET_KEYSPACE;
1564 bDone = true;
1566 bHandleKey = true;
1568 break;
1570 case KEY_A:
1572 if( bCtrl && mbMulti )
1574 // paint only once
1575 bool bUpdates = IsUpdateMode();
1576 SetUpdateMode( false );
1578 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1579 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1580 SelectEntry( i, true );
1582 // restore update mode
1583 SetUpdateMode( bUpdates );
1584 Invalidate();
1586 maQuickSelectionEngine.Reset();
1588 bDone = true;
1590 else
1592 bHandleKey = true;
1595 break;
1597 default:
1598 bHandleKey = true;
1599 break;
1601 if (bHandleKey && !IsReadOnly())
1603 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1606 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1607 && ( ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1608 || ( eLET == LET_KEYSPACE )
1612 SAL_WARN_IF( mpEntryList->IsEntryPosSelected( nSelect ) && !mbMulti, "vcl", "ImplListBox: Selecting same Entry" );
1613 sal_Int32 nCount = mpEntryList->GetEntryCount();
1614 if (nSelect >= nCount)
1615 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1616 bool bCurPosChange = (mnCurrentPos != nSelect);
1617 mnCurrentPos = nSelect;
1618 if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1620 mbTravelSelect = true;
1621 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1622 ImplCallSelect();
1623 mbTravelSelect = false;
1627 return bDone;
1630 namespace
1632 vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1634 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1635 sal_Int32 nEntryCount( _rList.GetEntryCount() );
1636 if ( _nPos >= nEntryCount )
1637 _nPos = 0;
1638 _out_entryText = _rList.GetEntryText( _nPos );
1640 // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1641 // => normalize
1642 return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + 1 );
1645 sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
1647 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1648 return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1652 vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1654 return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1657 vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1659 sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1660 return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1663 void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
1665 sal_Int32 nSelect = lcl_getEntryPos( _entry );
1666 if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1668 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1669 // to select the given entry by typing its starting letters. No need to act.
1670 return;
1673 // normalize
1674 OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1675 sal_Int32 nCount = mpEntryList->GetEntryCount();
1676 if (nSelect >= nCount)
1677 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1679 // make visible
1680 ShowProminentEntry( nSelect );
1682 // actually select
1683 mnCurrentPos = nSelect;
1684 if ( SelectEntries( nSelect, LET_KEYMOVE ) )
1686 mbTravelSelect = true;
1687 mnSelectModifier = 0;
1688 ImplCallSelect();
1689 mbTravelSelect = false;
1693 void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
1695 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1697 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1698 if (!pEntry)
1699 return;
1701 long nWidth = GetOutputSizePixel().Width();
1702 long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1703 Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->mnHeight));
1705 if (mpEntryList->IsEntryPosSelected(nPos))
1707 rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor());
1708 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
1709 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
1710 rRenderContext.DrawRect(aRect);
1712 else
1714 ApplySettings(rRenderContext);
1715 if (!IsEnabled())
1716 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
1717 rRenderContext.SetTextFillColor();
1720 if (IsUserDrawEnabled())
1722 mbInUserDraw = true;
1723 mnUserDrawEntry = nPos;
1724 aRect.Left() -= mnLeft;
1725 if (nPos < GetEntryList()->GetMRUCount())
1726 nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos));
1727 nPos = nPos - GetEntryList()->GetMRUCount();
1728 sal_Int32 nCurr = mnCurrentPos;
1729 if (mnCurrentPos < GetEntryList()->GetMRUCount())
1730 nCurr = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nCurr));
1731 nCurr = sal::static_int_cast<sal_Int32>(nCurr - GetEntryList()->GetMRUCount());
1733 UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos, nCurr);
1734 maUserDrawHdl.Call( &aUDEvt );
1735 mbInUserDraw = false;
1737 else
1739 DrawEntry(rRenderContext, nPos, true, true);
1743 void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText, bool bDrawTextAtImagePos)
1745 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(nPos);
1746 if (!pEntry)
1747 return;
1749 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1751 if (mbInUserDraw)
1752 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1754 long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1755 Size aImgSz;
1757 if (bDrawImage && mpEntryList->HasImages())
1759 Image aImage = mpEntryList->GetEntryImage(nPos);
1760 if (!!aImage)
1762 aImgSz = aImage.GetSizePixel();
1763 Point aPtImg(mnBorder - mnLeft, nY + ((pEntry->mnHeight - aImgSz.Height()) / 2));
1765 // pb: #106948# explicit mirroring for calc
1766 if (mbMirroring)
1767 // right aligned
1768 aPtImg.X() = mnMaxWidth + mnBorder - aImgSz.Width() - mnLeft;
1770 if (!IsZoom())
1772 rRenderContext.DrawImage(aPtImg, aImage);
1774 else
1776 aImgSz.Width() = CalcZoom(aImgSz.Width());
1777 aImgSz.Height() = CalcZoom(aImgSz.Height());
1778 rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
1781 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1782 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1784 if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1786 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1787 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1788 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1789 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1791 if (!aBlendFrame.IsEmpty())
1793 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
1799 if (bDrawText)
1801 OUString aStr(mpEntryList->GetEntryText(nPos));
1802 if (!aStr.isEmpty())
1804 long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * mnBorder);
1805 // a multiline entry should only be as wide a the window
1806 if ((pEntry->mnFlags & ListBoxEntryFlags::MultiLine))
1807 nMaxWidth = GetOutputSizePixel().Width() - 2 * mnBorder;
1809 Rectangle aTextRect(Point(mnBorder - mnLeft, nY),
1810 Size(nMaxWidth, pEntry->mnHeight));
1812 if (!bDrawTextAtImagePos && (mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled()))
1814 long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
1815 aTextRect.Left() += nImageWidth + IMG_TXT_DISTANCE;
1818 // pb: #106948# explicit mirroring for calc
1819 if (mbMirroring)
1821 // right aligned
1822 aTextRect.Left() = nMaxWidth + mnBorder - rRenderContext.GetTextWidth(aStr) - mnLeft;
1823 if (aImgSz.Width() > 0)
1824 aTextRect.Left() -= (aImgSz.Width() + IMG_TXT_DISTANCE);
1827 DrawTextFlags nDrawStyle = ImplGetTextStyle();
1828 if ((pEntry->mnFlags & ListBoxEntryFlags::MultiLine))
1829 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1830 if ((pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled))
1831 nDrawStyle |= DrawTextFlags::Disable;
1833 rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
1837 if ((mnSeparatorPos != LISTBOX_ENTRY_NOTFOUND) &&
1838 ((nPos == mnSeparatorPos) || (nPos == mnSeparatorPos + 1)))
1840 Color aOldLineColor(rRenderContext.GetLineColor());
1841 rRenderContext.SetLineColor((GetBackground().GetColor() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
1842 Point aStartPos(0, nY);
1843 if (nPos == mnSeparatorPos)
1844 aStartPos.Y() += pEntry->mnHeight - 1;
1845 Point aEndPos(aStartPos);
1846 aEndPos.X() = GetOutputSizePixel().Width();
1847 rRenderContext.DrawLine(aStartPos, aEndPos);
1848 rRenderContext.SetLineColor(aOldLineColor);
1852 void ImplListBoxWindow::FillLayoutData() const
1854 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1855 const_cast<ImplListBoxWindow*>(this)->Invalidate(Rectangle(Point(0, 0), GetOutputSize()));
1858 void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
1860 sal_Int32 nCount = mpEntryList->GetEntryCount();
1862 bool bShowFocusRect = mbHasFocusRect;
1863 if (mbHasFocusRect)
1864 ImplHideFocusRect();
1866 long nY = 0; // + mnBorder;
1867 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
1869 for (sal_Int32 i = (sal_Int32)mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
1871 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(i);
1872 if (nY + pEntry->mnHeight >= rRect.Top() &&
1873 nY <= rRect.Bottom() + mnMaxHeight)
1875 ImplPaint(rRenderContext, i);
1877 nY += pEntry->mnHeight;
1880 long nHeightDiff = mpEntryList->GetAddedHeight(mnCurrentPos, mnTop);
1881 maFocusRect.SetPos(Point(0, nHeightDiff));
1882 Size aSz(maFocusRect.GetWidth(), mpEntryList->GetEntryHeight(mnCurrentPos));
1883 maFocusRect.SetSize(aSz);
1884 if (HasFocus() && bShowFocusRect)
1885 ImplShowFocusRect();
1888 void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
1890 ImplDoPaint(rRenderContext, rRect);
1893 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1895 // FIXME: ListBoxEntryFlags::MultiLine
1897 const sal_Int32 nCount = mpEntryList->GetEntryCount()-mnTop;
1898 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
1899 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1900 if( nEntries > nCount )
1901 nEntries = static_cast<sal_uInt16>(nCount);
1903 return nEntries;
1906 void ImplListBoxWindow::Resize()
1908 Control::Resize();
1910 bool bShowFocusRect = mbHasFocusRect;
1911 if ( bShowFocusRect )
1912 ImplHideFocusRect();
1914 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1916 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1917 maFocusRect.SetSize( aSz );
1920 if ( bShowFocusRect )
1921 ImplShowFocusRect();
1923 ImplClearLayoutData();
1926 void ImplListBoxWindow::GetFocus()
1928 sal_Int32 nPos = mnCurrentPos;
1929 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1930 nPos = 0;
1931 long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop );
1932 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1933 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
1934 maFocusRect.SetSize( aSz );
1935 ImplShowFocusRect();
1936 Control::GetFocus();
1939 void ImplListBoxWindow::LoseFocus()
1941 ImplHideFocusRect();
1942 Control::LoseFocus();
1945 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1947 if( mpEntryList->GetEntryCount() == 0 )
1948 return;
1950 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1952 sal_Int32 nLastEntry = mpEntryList->GetEntryCount()-1;
1953 if( nTop > nLastEntry )
1954 nTop = nLastEntry;
1955 const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
1956 while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->mnHeight <= nWHeight )
1957 nTop--;
1959 if ( nTop != mnTop )
1961 ImplClearLayoutData();
1962 long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop );
1963 Update();
1964 ImplHideFocusRect();
1965 mnTop = nTop;
1966 Scroll( 0, nDiff );
1967 Update();
1968 if( HasFocus() )
1969 ImplShowFocusRect();
1970 maScrollHdl.Call( this );
1974 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
1976 if( meProminentType == ProminentEntry::MIDDLE )
1978 sal_Int32 nPos = nEntryPos;
1979 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1980 while( nEntryPos > 0 && mpEntryList->GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
1981 nEntryPos--;
1983 SetTopEntry( nEntryPos );
1986 void ImplListBoxWindow::SetLeftIndent( long n )
1988 ScrollHorz( n - mnLeft );
1991 void ImplListBoxWindow::ScrollHorz( long n )
1993 long nDiff = 0;
1994 if ( n > 0 )
1996 long nWidth = GetOutputSizePixel().Width();
1997 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
1998 nDiff = n;
2000 else if ( n < 0 )
2002 if( mnLeft )
2004 long nAbs = -n;
2005 nDiff = - ( ( mnLeft > nAbs ) ? nAbs : mnLeft );
2009 if ( nDiff )
2011 ImplClearLayoutData();
2012 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
2013 Update();
2014 ImplHideFocusRect();
2015 Scroll( -nDiff, 0 );
2016 Update();
2017 if( HasFocus() )
2018 ImplShowFocusRect();
2019 maScrollHdl.Call( this );
2023 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
2025 // FIXME: ListBoxEntryFlags::MultiLine
2027 Size aSz;
2028 aSz.Height() = nMaxLines * mnMaxHeight;
2029 aSz.Width() = mnMaxWidth + 2*mnBorder;
2030 return aSz;
2033 Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
2035 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
2036 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->mnHeight : GetEntryHeight() );
2037 long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList()->GetMRUCount()*GetEntryHeight();
2038 Rectangle aRect( Point( 0, nY ), aSz );
2039 return aRect;
2042 void ImplListBoxWindow::StateChanged( StateChangedType nType )
2044 Control::StateChanged( nType );
2046 if ( nType == StateChangedType::Zoom )
2048 ApplySettings(*this);
2049 ImplCalcMetrics();
2050 Invalidate();
2052 else if ( nType == StateChangedType::UpdateMode )
2054 if ( IsUpdateMode() && IsReallyVisible() )
2055 Invalidate();
2057 else if ( nType == StateChangedType::ControlFont )
2059 ApplySettings(*this);
2060 ImplCalcMetrics();
2061 Invalidate();
2063 else if ( nType == StateChangedType::ControlForeground )
2065 ApplySettings(*this);
2066 Invalidate();
2068 else if ( nType == StateChangedType::ControlBackground )
2070 ApplySettings(*this);
2071 Invalidate();
2073 else if( nType == StateChangedType::Enable )
2075 Invalidate();
2078 ImplClearLayoutData();
2081 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
2083 Control::DataChanged( rDCEvt );
2085 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2086 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2087 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2088 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2090 ImplClearLayoutData();
2091 ApplySettings(*this);
2092 ImplCalcMetrics();
2093 Invalidate();
2097 DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
2099 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2101 if (mpEntryList->HasImages())
2102 nTextStyle |= DrawTextFlags::Left;
2103 else if (mbCenter)
2104 nTextStyle |= DrawTextFlags::Center;
2105 else if (mbRight)
2106 nTextStyle |= DrawTextFlags::Right;
2107 else
2108 nTextStyle |= DrawTextFlags::Left;
2110 return nTextStyle;
2113 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2114 Control( pParent, nWinStyle ),
2115 maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
2117 // for native widget rendering we must be able to detect this window type
2118 SetType( WINDOW_LISTBOXWINDOW );
2120 mpVScrollBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
2121 mpHScrollBar = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
2122 mpScrollBarBox = VclPtr<ScrollBarBox>::Create( this );
2124 Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2125 mpVScrollBar->SetScrollHdl( aLink );
2126 mpHScrollBar->SetScrollHdl( aLink );
2128 mbVScroll = false;
2129 mbHScroll = false;
2130 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2131 mbEdgeBlending = false;
2133 maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2134 maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2135 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2136 maLBWindow->Show();
2139 ImplListBox::~ImplListBox()
2141 disposeOnce();
2144 void ImplListBox::dispose()
2146 mpHScrollBar.disposeAndClear();
2147 mpVScrollBar.disposeAndClear();
2148 mpScrollBarBox.disposeAndClear();
2149 maLBWindow.disposeAndClear();
2150 Control::dispose();
2153 void ImplListBox::Clear()
2155 maLBWindow->Clear();
2156 if ( GetEntryList()->GetMRUCount() )
2158 maLBWindow->GetEntryList()->SetMRUCount( 0 );
2159 maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2161 mpVScrollBar->SetThumbPos( 0 );
2162 mpHScrollBar->SetThumbPos( 0 );
2163 CompatStateChanged( StateChangedType::Data );
2166 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2168 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2169 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2170 if (nNewPos == LISTBOX_ERROR)
2172 delete pNewEntry;
2173 return nNewPos;
2175 CompatStateChanged( StateChangedType::Data );
2176 return nNewPos;
2179 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2181 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2182 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2183 if (nNewPos == LISTBOX_ERROR)
2185 delete pNewEntry;
2186 return nNewPos;
2188 CompatStateChanged( StateChangedType::Data );
2189 return nNewPos;
2192 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2194 maLBWindow->RemoveEntry( nPos );
2195 CompatStateChanged( StateChangedType::Data );
2198 void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
2200 maLBWindow->SetEntryFlags( nPos, nFlags );
2203 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2205 maLBWindow->SelectEntry( nPos, bSelect );
2208 void ImplListBox::SetNoSelection()
2210 maLBWindow->DeselectAll();
2213 void ImplListBox::GetFocus()
2215 if (maLBWindow)
2216 maLBWindow->GrabFocus();
2217 else
2218 Control::GetFocus();
2221 void ImplListBox::Resize()
2223 Control::Resize();
2224 ImplResizeControls();
2225 ImplCheckScrollBars();
2228 IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
2230 CompatStateChanged( StateChangedType::Data );
2233 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
2235 long nSet = GetTopEntry();
2236 if( nSet > mpVScrollBar->GetRangeMax() )
2237 mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2238 mpVScrollBar->SetThumbPos( GetTopEntry() );
2240 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2242 maScrollHdl.Call( this );
2245 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
2247 sal_uInt16 nPos = (sal_uInt16) pSB->GetThumbPos();
2248 if( pSB == mpVScrollBar )
2249 SetTopEntry( nPos );
2250 else if( pSB == mpHScrollBar )
2251 SetLeftIndent( nPos );
2254 void ImplListBox::ImplCheckScrollBars()
2256 bool bArrange = false;
2258 Size aOutSz = GetOutputSizePixel();
2259 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2260 sal_uInt16 nMaxVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2262 // vertical ScrollBar
2263 if( nEntries > nMaxVisEntries )
2265 if( !mbVScroll )
2266 bArrange = true;
2267 mbVScroll = true;
2269 // check of the scrolled-out region
2270 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2271 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2272 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2273 else
2274 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2276 else
2278 if( mbVScroll )
2279 bArrange = true;
2280 mbVScroll = false;
2281 SetTopEntry( 0 );
2284 // horizontal ScrollBar
2285 if( mbAutoHScroll )
2287 long nWidth = (sal_uInt16) aOutSz.Width();
2288 if ( mbVScroll )
2289 nWidth -= mpVScrollBar->GetSizePixel().Width();
2291 long nMaxWidth = GetMaxEntryWidth();
2292 if( nWidth < nMaxWidth )
2294 if( !mbHScroll )
2295 bArrange = true;
2296 mbHScroll = true;
2298 if ( !mbVScroll ) // maybe we do need one now
2300 nMaxVisEntries = (sal_uInt16) ( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeight() );
2301 if( nEntries > nMaxVisEntries )
2303 bArrange = true;
2304 mbVScroll = true;
2306 // check of the scrolled-out region
2307 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2308 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2309 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2310 else
2311 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2315 // check of the scrolled-out region
2316 sal_uInt16 nMaxLI = (sal_uInt16) (nMaxWidth - nWidth);
2317 if ( nMaxLI < GetLeftIndent() )
2318 SetLeftIndent( nMaxLI );
2320 else
2322 if( mbHScroll )
2323 bArrange = true;
2324 mbHScroll = false;
2325 SetLeftIndent( 0 );
2329 if( bArrange )
2330 ImplResizeControls();
2332 ImplInitScrollBars();
2335 void ImplListBox::ImplInitScrollBars()
2337 Size aOutSz = maLBWindow->GetOutputSizePixel();
2339 if ( mbVScroll )
2341 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2342 sal_uInt16 nVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2343 mpVScrollBar->SetRangeMax( nEntries );
2344 mpVScrollBar->SetVisibleSize( nVisEntries );
2345 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2348 if ( mbHScroll )
2350 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2351 mpHScrollBar->SetVisibleSize( (sal_uInt16)aOutSz.Width() );
2352 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2353 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2357 void ImplListBox::ImplResizeControls()
2359 // Here we only position the Controls; if the Scrollbars are to be
2360 // visible is already determined in ImplCheckScrollBars
2362 Size aOutSz = GetOutputSizePixel();
2363 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2364 nSBWidth = CalcZoom( nSBWidth );
2366 Size aInnerSz( aOutSz );
2367 if ( mbVScroll )
2368 aInnerSz.Width() -= nSBWidth;
2369 if ( mbHScroll )
2370 aInnerSz.Height() -= nSBWidth;
2372 // pb: #106948# explicit mirroring for calc
2373 // Scrollbar on left or right side?
2374 bool bMirroring = maLBWindow->IsMirroring();
2375 Point aWinPos( bMirroring && mbVScroll ? nSBWidth : 0, 0 );
2376 maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
2378 // ScrollBarBox
2379 if( mbVScroll && mbHScroll )
2381 Point aBoxPos( bMirroring ? 0 : aInnerSz.Width(), aInnerSz.Height() );
2382 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2383 mpScrollBarBox->Show();
2385 else
2387 mpScrollBarBox->Hide();
2390 // vertical ScrollBar
2391 if( mbVScroll )
2393 // Scrollbar on left or right side?
2394 Point aVPos( bMirroring ? 0 : aOutSz.Width() - nSBWidth, 0 );
2395 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2396 mpVScrollBar->Show();
2398 else
2400 mpVScrollBar->Hide();
2401 // #107254# Don't reset top entry after resize, but check for max top entry
2402 SetTopEntry( GetTopEntry() );
2405 // horizontal ScrollBar
2406 if( mbHScroll )
2408 Point aHPos( ( bMirroring && mbVScroll ) ? nSBWidth : 0, aOutSz.Height() - nSBWidth );
2409 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2410 mpHScrollBar->Show();
2412 else
2414 mpHScrollBar->Hide();
2415 SetLeftIndent( 0 );
2419 void ImplListBox::StateChanged( StateChangedType nType )
2421 if ( nType == StateChangedType::InitShow )
2423 ImplCheckScrollBars();
2425 else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
2427 bool bUpdate = IsUpdateMode();
2428 maLBWindow->SetUpdateMode( bUpdate );
2429 if ( bUpdate && IsReallyVisible() )
2430 ImplCheckScrollBars();
2432 else if( nType == StateChangedType::Enable )
2434 mpHScrollBar->Enable( IsEnabled() );
2435 mpVScrollBar->Enable( IsEnabled() );
2436 mpScrollBarBox->Enable( IsEnabled() );
2437 maLBWindow->Enable( IsEnabled() );
2439 Invalidate();
2441 else if ( nType == StateChangedType::Zoom )
2443 maLBWindow->SetZoom( GetZoom() );
2444 Resize();
2446 else if ( nType == StateChangedType::ControlFont )
2448 maLBWindow->SetControlFont( GetControlFont() );
2450 else if ( nType == StateChangedType::ControlForeground )
2452 maLBWindow->SetControlForeground( GetControlForeground() );
2454 else if ( nType == StateChangedType::ControlBackground )
2456 maLBWindow->SetControlBackground( GetControlBackground() );
2458 else if( nType == StateChangedType::Mirroring )
2460 maLBWindow->EnableRTL( IsRTLEnabled() );
2461 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2462 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2463 ImplResizeControls();
2466 Control::StateChanged( nType );
2469 bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
2471 bool bDone = false;
2472 if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
2474 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2475 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2477 const CommandWheelData* pData = rCEvt.GetWheelData();
2478 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2480 bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2485 return bDone || Window::EventNotify( rNEvt );
2488 const Wallpaper& ImplListBox::GetDisplayBackground() const
2490 return maLBWindow->GetDisplayBackground();
2493 bool ImplListBox::HandleWheelAsCursorTravel( const CommandEvent& rCEvt )
2495 bool bDone = false;
2496 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2498 const CommandWheelData* pData = rCEvt.GetWheelData();
2499 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2501 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2502 KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2503 bDone = ProcessKeyInput( aKeyEvent );
2506 return bDone;
2509 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2511 bool bChanges = GetEntryList()->GetMRUCount() != 0;
2513 // Remove old MRU entries
2514 for ( sal_Int32 n = GetEntryList()->GetMRUCount();n; )
2515 maLBWindow->RemoveEntry( --n );
2517 sal_Int32 nMRUCount = 0;
2518 sal_Int32 nIndex = 0;
2521 OUString aEntry = rEntries.getToken( 0, cSep, nIndex );
2522 // Accept only existing entries
2523 if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2525 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2526 maLBWindow->GetEntryList()->InsertEntry( nMRUCount++, pNewEntry, false );
2527 bChanges = true;
2530 while ( nIndex >= 0 );
2532 if ( bChanges )
2534 maLBWindow->GetEntryList()->SetMRUCount( nMRUCount );
2535 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2536 CompatStateChanged( StateChangedType::Data );
2540 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2542 OUStringBuffer aEntries;
2543 for ( sal_Int32 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2545 aEntries.append(GetEntryList()->GetEntryText( n ));
2546 if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2547 aEntries.append(cSep);
2549 return aEntries.makeStringAndClear();
2552 void ImplListBox::SetEdgeBlending(bool bNew)
2554 if(mbEdgeBlending != bNew)
2556 mbEdgeBlending = bNew;
2557 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2561 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2562 Control ( pParent, nWinStyle )
2564 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2565 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2566 SetBackground();
2567 else
2568 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2570 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2572 mbInUserDraw = false;
2573 mbUserDrawEnabled = false;
2574 mbEdgeBlending = false;
2575 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2578 void ImplWin::MouseButtonDown( const MouseEvent& )
2580 if( IsEnabled() )
2582 maMBDownHdl.Call(this);
2586 void ImplWin::FillLayoutData() const
2588 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2589 ImplWin* pThis = const_cast<ImplWin*>(this);
2590 pThis->ImplDraw(*pThis, true);
2593 bool ImplWin::PreNotify( NotifyEvent& rNEvt )
2595 const MouseEvent* pMouseEvt = nullptr;
2597 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
2599 if( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() )
2601 // trigger redraw as mouse over state has changed
2602 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2603 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2605 GetParent()->GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
2606 GetParent()->GetWindow( GetWindowType::Border )->Update();
2611 return Control::PreNotify(rNEvt);
2614 void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
2616 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2618 if (!bLayout)
2620 bool bNativeOK = false;
2622 ControlState nState = ControlState::ENABLED;
2623 if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2624 && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
2626 // Repaint the (focused) area similarly to
2627 // ImplSmallBorderWindowView::DrawWindow() in
2628 // vcl/source/window/brdwin.cxx
2629 vcl::Window *pWin = GetParent();
2631 ImplControlValue aControlValue;
2632 if ( !pWin->IsEnabled() )
2633 nState &= ~ControlState::ENABLED;
2634 if ( pWin->HasFocus() )
2635 nState |= ControlState::FOCUSED;
2637 // The listbox is painted over the entire control including the
2638 // border, but ImplWin does not contain the border => correction
2639 // needed.
2640 sal_Int32 nLeft, nTop, nRight, nBottom;
2641 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2642 Point aPoint( -nLeft, -nTop );
2643 Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2645 bool bMouseOver = false;
2646 if( GetParent() )
2648 vcl::Window *pChild = GetParent()->GetWindow( GetWindowType::FirstChild );
2649 while( pChild && !(bMouseOver = pChild->IsMouseOver()) )
2650 pChild = pChild->GetWindow( GetWindowType::Next );
2653 if( bMouseOver )
2654 nState |= ControlState::ROLLOVER;
2656 // if parent has no border, then nobody has drawn the background
2657 // since no border window exists. so draw it here.
2658 WinBits nParentStyle = pWin->GetStyle();
2659 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2661 Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2662 pWin->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
2663 nState, aControlValue, OUString() );
2666 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
2667 nState, aControlValue, OUString());
2670 if (IsEnabled())
2672 if (HasFocus() && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2674 rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
2675 rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
2676 rRenderContext.DrawRect( maFocusRect );
2678 else
2680 Color aColor;
2681 if( ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea )
2683 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2684 aColor = rStyleSettings.GetButtonRolloverTextColor();
2685 else
2686 aColor = rStyleSettings.GetButtonTextColor();
2688 else
2690 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2691 aColor = rStyleSettings.GetFieldRolloverTextColor();
2692 else
2693 aColor = rStyleSettings.GetFieldTextColor();
2695 if (IsControlForeground())
2696 aColor = GetControlForeground();
2697 rRenderContext.SetTextColor(aColor);
2698 if (!bNativeOK)
2699 rRenderContext.Erase(maFocusRect);
2702 else // Disabled
2704 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
2705 if (!bNativeOK)
2706 rRenderContext.Erase(maFocusRect);
2710 if ( IsUserDrawEnabled() )
2712 mbInUserDraw = true;
2713 UserDrawEvent aUDEvt(this, &rRenderContext, maFocusRect, mnItemPos, 0);
2714 maUserDrawHdl.Call( &aUDEvt );
2715 mbInUserDraw = false;
2717 else
2719 DrawEntry(rRenderContext, true, false, bLayout);
2723 void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
2725 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2727 vcl::Font aFont = rStyleSettings.GetFieldFont();
2728 if (IsControlFont())
2729 aFont.Merge(GetControlFont());
2730 SetZoomedPointFont(rRenderContext, aFont);
2732 Color aTextColor = rStyleSettings.GetFieldTextColor();
2733 if (IsControlForeground())
2734 aTextColor = GetControlForeground();
2735 rRenderContext.SetTextColor(aTextColor);
2737 if (IsControlBackground())
2738 rRenderContext.SetBackground(GetControlBackground());
2739 else
2740 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
2743 void ImplWin::Paint( vcl::RenderContext& rRenderContext, const Rectangle& )
2745 ImplDraw(rRenderContext);
2748 void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bDrawImage, bool bDrawTextAtImagePos, bool bLayout)
2750 long nBorder = 1;
2751 Size aOutSz(GetOutputSizePixel());
2753 bool bImage = !!maImage;
2754 if (bDrawImage && bImage && !bLayout)
2756 DrawImageFlags nStyle = DrawImageFlags::NONE;
2757 Size aImgSz = maImage.GetSizePixel();
2758 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2759 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2761 // check for HC mode
2762 Image *pImage = &maImage;
2764 if ( !IsZoom() )
2766 rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
2768 else
2770 aImgSz.Width() = CalcZoom( aImgSz.Width() );
2771 aImgSz.Height() = CalcZoom( aImgSz.Height() );
2772 rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2775 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2777 if(nEdgeBlendingPercent)
2779 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2780 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2781 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2782 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2784 if(!aBlendFrame.IsEmpty())
2786 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
2791 if( !maString.isEmpty() )
2793 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2795 if ( bDrawImage && bImage && !bLayout )
2796 nTextStyle |= DrawTextFlags::Left;
2797 else if ( GetStyle() & WB_CENTER )
2798 nTextStyle |= DrawTextFlags::Center;
2799 else if ( GetStyle() & WB_RIGHT )
2800 nTextStyle |= DrawTextFlags::Right;
2801 else
2802 nTextStyle |= DrawTextFlags::Left;
2804 Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2806 if ( !bDrawTextAtImagePos && ( bImage || IsUserDrawEnabled() ) )
2808 aTextRect.Left() += maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE;
2811 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : nullptr;
2812 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : nullptr;
2813 rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2816 if( HasFocus() && !bLayout )
2817 ShowFocus( maFocusRect );
2820 void ImplWin::Resize()
2822 Control::Resize();
2823 maFocusRect.SetSize( GetOutputSizePixel() );
2824 Invalidate();
2827 void ImplWin::GetFocus()
2829 ShowFocus( maFocusRect );
2830 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2831 IsNativeWidgetEnabled() &&
2832 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2834 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2835 if( ! pWin )
2836 pWin = GetParent();
2837 pWin->Invalidate();
2839 else
2840 Invalidate();
2841 Control::GetFocus();
2844 void ImplWin::LoseFocus()
2846 HideFocus();
2847 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2848 IsNativeWidgetEnabled() &&
2849 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2851 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2852 if( ! pWin )
2853 pWin = GetParent();
2854 pWin->Invalidate();
2856 else
2857 Invalidate();
2858 Control::LoseFocus();
2861 void ImplWin::ShowFocus(const Rectangle& rRect)
2863 if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
2865 ImplControlValue aControlValue;
2867 vcl::Window *pWin = GetParent();
2868 Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
2869 pWin->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
2870 ControlState::FOCUSED, aControlValue, OUString());
2872 Control::ShowFocus(rRect);
2875 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2876 PushButton( pParent, nWinStyle ),
2877 mbDown ( false )
2881 void ImplBtn::MouseButtonDown( const MouseEvent& )
2883 //PushButton::MouseButtonDown( rMEvt );
2884 if( IsEnabled() )
2886 maMBDownHdl.Call(this);
2887 mbDown = true;
2891 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2892 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
2894 mpImplLB = nullptr;
2895 mnDDLineCount = 0;
2896 mbAutoWidth = false;
2898 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2900 vcl::Window * pBorderWindow = ImplGetBorderWindow();
2901 if( pBorderWindow )
2903 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2904 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2906 else
2908 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2913 ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
2915 disposeOnce();
2918 void ImplListBoxFloatingWindow::dispose()
2920 mpImplLB.clear();
2921 FloatingWindow::dispose();
2925 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2927 if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2929 if( !GetParent()->HasChildPathFocus( true ) )
2930 EndPopupMode();
2933 return FloatingWindow::PreNotify( rNEvt );
2936 void ImplListBoxFloatingWindow::setPosSizePixel( long nX, long nY, long nWidth, long nHeight, PosSizeFlags nFlags )
2938 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2940 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2941 // after a call to Resize(), we adjust its position if necessary
2942 if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
2944 Point aPos = GetParent()->GetPosPixel();
2945 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2947 if ( nFlags & PosSizeFlags::X )
2948 aPos.X() = nX;
2950 if ( nFlags & PosSizeFlags::Y )
2951 aPos.Y() = nY;
2953 sal_uInt16 nIndex;
2954 SetPosPixel( ImplCalcPos( this, Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
2957 // if( !IsReallyVisible() )
2959 // The ImplListBox does not get a Resize() as not visible.
2960 // But the windows must get a Resize(), so that the number of
2961 // visible entries is correct for PgUp/PgDown.
2962 // The number also cannot be calculated by List/Combobox, as for
2963 // this the presence of the vertical Scrollbar has to be known.
2964 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2965 static_cast<vcl::Window*>(mpImplLB)->Resize();
2966 static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
2970 void ImplListBoxFloatingWindow::Resize()
2972 mpImplLB->GetMainWindow()->ImplClearLayoutData();
2973 FloatingWindow::Resize();
2976 Size ImplListBoxFloatingWindow::CalcFloatSize()
2978 Size aFloatSz( maPrefSz );
2980 sal_Int32 nLeft, nTop, nRight, nBottom;
2981 GetBorder( nLeft, nTop, nRight, nBottom );
2983 sal_Int32 nLines = mpImplLB->GetEntryList()->GetEntryCount();
2984 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
2985 nLines = mnDDLineCount;
2987 Size aSz = mpImplLB->CalcSize( nLines );
2988 long nMaxHeight = aSz.Height() + nTop + nBottom;
2990 if ( mnDDLineCount )
2991 aFloatSz.Height() = nMaxHeight;
2993 if( mbAutoWidth )
2995 // AutoSize first only for width...
2997 aFloatSz.Width() = aSz.Width() + nLeft + nRight;
2998 aFloatSz.Width() += nRight; // adding some space looks better...
3000 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
3002 // then we also need the vertical Scrollbar
3003 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3004 aFloatSz.Width() += nSBWidth;
3007 long nDesktopWidth = GetDesktopRectPixel().getWidth();
3008 if (aFloatSz.Width() > nDesktopWidth)
3009 // Don't exceed the desktop width.
3010 aFloatSz.Width() = nDesktopWidth;
3013 if ( aFloatSz.Height() > nMaxHeight )
3014 aFloatSz.Height() = nMaxHeight;
3016 // Minimal height, in case height is not set to Float height.
3017 // The parent of FloatWin must be DropDown-Combo/Listbox.
3018 Size aParentSz = GetParent()->GetSizePixel();
3019 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
3020 aFloatSz.Height() = aParentSz.Height();
3022 // do not get narrower than the parent...
3023 if( aFloatSz.Width() < aParentSz.Width() )
3024 aFloatSz.Width() = aParentSz.Width();
3026 // align height to entries...
3027 long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
3028 long nEntryHeight = mpImplLB->GetEntryHeight();
3029 if ( nInnerHeight % nEntryHeight )
3031 nInnerHeight /= nEntryHeight;
3032 nInnerHeight++;
3033 nInnerHeight *= nEntryHeight;
3034 aFloatSz.Height() = nInnerHeight + nTop + nBottom;
3037 if (aFloatSz.Width() < aSz.Width())
3039 // The max width of list box entries exceeds the window width.
3040 // Account for the scroll bar height.
3041 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3042 aFloatSz.Height() += nSBWidth;
3045 return aFloatSz;
3048 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
3050 if( !IsInPopupMode() )
3052 Size aFloatSz = CalcFloatSize();
3054 SetSizePixel( aFloatSz );
3055 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3057 sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectEntryPos( 0 );
3058 mnPopupModeStartSaveSelection = nPos;
3060 Size aSz = GetParent()->GetSizePixel();
3061 Point aPos = GetParent()->GetPosPixel();
3062 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
3063 // FIXME: this ugly hack is for Mac/Aqua
3064 // should be replaced by a real mechanism to place the float rectangle
3065 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3066 GetParent()->IsNativeWidgetEnabled() )
3068 const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
3069 aPos.X() += nLeft;
3070 aPos.Y() += nTop;
3071 aSz.Width() -= nLeft + nRight;
3072 aSz.Height() -= nTop + nBottom;
3074 Rectangle aRect( aPos, aSz );
3076 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
3077 // where the document is unmirrored
3078 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
3079 vcl::Window *pGrandparent = GetParent()->GetParent();
3080 const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
3082 if( pGrandparent->ImplIsAntiparallel() )
3083 pGrandparentOutDev->ReMirror( aRect );
3085 // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
3086 StartPopupMode( aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::AllMouseButtonClose );
3088 if( nPos != LISTBOX_ENTRY_NOTFOUND )
3089 mpImplLB->ShowProminentEntry( nPos );
3091 if( bStartTracking )
3092 mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
3094 if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
3095 mpImplLB->GetMainWindow()->GrabFocus();
3097 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3101 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */