bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / control / imp_listbox.cxx
blobe87281447bf1c7719c30bb60c0a07bf36669ad82
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/scrbar.hxx>
26 #include <vcl/lstbox.hxx>
27 #include <vcl/i18nhelp.hxx>
29 #include <listbox.hxx>
30 #include <controldata.hxx>
31 #include <svdata.hxx>
32 #include <window.h>
34 #include <com/sun/star/accessibility/AccessibleRole.hpp>
36 #include <rtl/instance.hxx>
37 #include <sal/log.hxx>
38 #include <osl/diagnose.h>
39 #include <comphelper/string.hxx>
40 #include <comphelper/processfactory.hxx>
42 #include <limits>
44 #define MULTILINE_ENTRY_DRAW_FLAGS ( DrawTextFlags::WordBreak | DrawTextFlags::MultiLine | DrawTextFlags::VCenter )
46 using namespace ::com::sun::star;
48 static constexpr long gnBorder = 1;
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 assert(nPos >= 0);
121 assert(maEntries.size() < LISTBOX_MAX_ENTRIES);
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 = std::min(i_nEndIndex, i_nBeginIndex);
293 sal_Int32 nStop = std::max(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 )->getHeightWithMargin();
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->getHeightWithMargin() : 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::GetSelectedEntryCount() 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::GetSelectedEntry( sal_Int32 nIndex ) const
396 return GetEntryText( GetSelectedEntryPos( nIndex ) );
399 sal_Int32 ImplEntryList::GetSelectedEntryPos( 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.reset(new ImplEntryList( this ));
466 mnTop = 0;
467 mnLeft = 0;
468 mnSelectModifier = 0;
469 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
470 mbTrack = false;
471 mbTravelSelect = false;
472 mbTrackingSelect = false;
473 mbSelectionChanged = false;
474 mbMouseMoveSelect = false;
475 mbMulti = false;
476 mbStackMode = false;
477 mbGrabFocus = false;
478 mbUserDrawEnabled = false;
479 mbInUserDraw = false;
480 mbReadOnly = false;
481 mbHasFocusRect = false;
482 mbRight = ( nWinStyle & WB_RIGHT );
483 mbCenter = ( nWinStyle & WB_CENTER );
484 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
485 mbSort = ( nWinStyle & WB_SORT );
486 mbIsComboboxDropdown = ( nWinStyle & WB_DROPDOWN );
487 mbEdgeBlending = false;
489 // pb: #106948# explicit mirroring for calc
490 mbMirroring = false;
492 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
493 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
494 meProminentType = ProminentEntry::TOP;
496 SetLineColor();
497 SetTextFillColor();
498 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
500 ApplySettings(*this);
501 ImplCalcMetrics();
504 ImplListBoxWindow::~ImplListBoxWindow()
506 disposeOnce();
509 void ImplListBoxWindow::dispose()
511 mpEntryList.reset();
512 Control::dispose();
515 void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
517 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
519 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
520 ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
522 if (IsControlBackground())
523 rRenderContext.SetBackground(GetControlBackground());
524 else
525 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
528 void ImplListBoxWindow::ImplCalcMetrics()
530 mnMaxWidth = 0;
531 mnMaxTxtWidth = 0;
532 mnMaxImgWidth = 0;
533 mnMaxImgTxtWidth= 0;
534 mnMaxImgHeight = 0;
536 mnTextHeight = static_cast<sal_uInt16>(GetTextHeight());
537 mnMaxTxtHeight = mnTextHeight + gnBorder;
538 mnMaxHeight = mnMaxTxtHeight;
540 if ( maUserItemSize.Height() > mnMaxHeight )
541 mnMaxHeight = static_cast<sal_uInt16>(maUserItemSize.Height());
542 if ( maUserItemSize.Width() > mnMaxWidth )
543 mnMaxWidth= static_cast<sal_uInt16>(maUserItemSize.Width());
545 for ( sal_Int32 n = mpEntryList->GetEntryCount(); n; )
547 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
548 ImplUpdateEntryMetrics( *pEntry );
551 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
553 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->getHeightWithMargin() );
554 maFocusRect.SetSize( aSz );
558 void ImplListBoxWindow::Clear()
560 mpEntryList->Clear();
562 mnMaxHeight = mnMaxTxtHeight;
563 mnMaxWidth = 0;
564 mnMaxTxtWidth = 0;
565 mnMaxImgTxtWidth= 0;
566 mnMaxImgWidth = 0;
567 mnMaxImgHeight = 0;
568 mnTop = 0;
569 mnLeft = 0;
570 ImplClearLayoutData();
572 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
573 maQuickSelectionEngine.Reset();
575 Invalidate();
578 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
580 ImplClearLayoutData();
581 maUserItemSize = rSz;
582 ImplCalcMetrics();
585 struct ImplEntryMetrics
587 bool bText;
588 bool bImage;
589 long nEntryWidth;
590 long nEntryHeight;
591 long nTextWidth;
592 long nImgWidth;
593 long nImgHeight;
596 long ImplEntryType::getHeightWithMargin() const
598 return mnHeight + ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
601 SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const OutputDevice* pOutputDevice)
603 if (maStrGlyphs.IsValid())
604 // Use pre-calculated result.
605 return &maStrGlyphs;
607 std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
608 maStr, 0, maStr.getLength(), Point(0, 0), 0, nullptr, SalLayoutFlags::GlyphItemsOnly);
609 if (!pLayout)
610 return nullptr;
612 const SalLayoutGlyphs* pGlyphs = pLayout->GetGlyphs();
613 if (!pGlyphs)
614 return nullptr;
616 // Remember the calculation result.
617 maStrGlyphs = *pGlyphs;
619 return &maStrGlyphs;
622 void ImplListBoxWindow::EnableQuickSelection( bool b )
624 maQuickSelectionEngine.SetEnabled( b );
627 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
629 ImplEntryMetrics aMetrics;
630 aMetrics.bText = !rEntry.maStr.isEmpty();
631 aMetrics.bImage = !!rEntry.maImage;
632 aMetrics.nEntryWidth = 0;
633 aMetrics.nEntryHeight = 0;
634 aMetrics.nTextWidth = 0;
635 aMetrics.nImgWidth = 0;
636 aMetrics.nImgHeight = 0;
638 if ( aMetrics.bText )
640 if( rEntry.mnFlags & ListBoxEntryFlags::MultiLine )
642 // multiline case
643 Size aCurSize( PixelToLogic( GetSizePixel() ) );
644 // set the current size to a large number
645 // GetTextRect should shrink it to the actual size
646 aCurSize.setHeight( 0x7fffff );
647 tools::Rectangle aTextRect( Point( 0, 0 ), aCurSize );
648 aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
649 aMetrics.nTextWidth = aTextRect.GetWidth();
650 if( aMetrics.nTextWidth > mnMaxTxtWidth )
651 mnMaxTxtWidth = aMetrics.nTextWidth;
652 aMetrics.nEntryWidth = mnMaxTxtWidth;
653 aMetrics.nEntryHeight = aTextRect.GetHeight() + gnBorder;
655 else
657 // normal single line case
658 const SalLayoutGlyphs* pGlyphs = rEntry.GetTextGlyphs(this);
659 aMetrics.nTextWidth
660 = static_cast<sal_uInt16>(GetTextWidth(rEntry.maStr, 0, -1, nullptr, pGlyphs));
661 if( aMetrics.nTextWidth > mnMaxTxtWidth )
662 mnMaxTxtWidth = aMetrics.nTextWidth;
663 aMetrics.nEntryWidth = mnMaxTxtWidth;
664 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
667 if ( aMetrics.bImage )
669 Size aImgSz = rEntry.maImage.GetSizePixel();
670 aMetrics.nImgWidth = static_cast<sal_uInt16>(CalcZoom( aImgSz.Width() ));
671 aMetrics.nImgHeight = static_cast<sal_uInt16>(CalcZoom( aImgSz.Height() ));
673 if( aMetrics.nImgWidth > mnMaxImgWidth )
674 mnMaxImgWidth = aMetrics.nImgWidth;
675 if( aMetrics.nImgHeight > mnMaxImgHeight )
676 mnMaxImgHeight = aMetrics.nImgHeight;
678 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
679 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
682 if ( IsUserDrawEnabled() || aMetrics.bImage )
684 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
685 if ( aMetrics.bText )
686 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
687 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
688 aMetrics.nEntryHeight );
691 if ( !aMetrics.bText && !aMetrics.bImage && !IsUserDrawEnabled() )
693 // entries which have no (aka an empty) text, and no image,
694 // and are not user-drawn, should be shown nonetheless
695 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
698 if ( aMetrics.nEntryWidth > mnMaxWidth )
699 mnMaxWidth = aMetrics.nEntryWidth;
700 if ( aMetrics.nEntryHeight > mnMaxHeight )
701 mnMaxHeight = aMetrics.nEntryHeight;
703 rEntry.mnHeight = aMetrics.nEntryHeight;
706 void ImplListBoxWindow::ImplCallSelect()
708 if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
710 // Insert the selected entry as MRU, if not already first MRU
711 sal_Int32 nSelected = GetEntryList()->GetSelectedEntryPos( 0 );
712 sal_Int32 nMRUCount = GetEntryList()->GetMRUCount();
713 OUString aSelected = GetEntryList()->GetEntryText( nSelected );
714 sal_Int32 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, true );
715 if ( nFirstMatchingEntryPos || !nMRUCount )
717 bool bSelectNewEntry = false;
718 if ( nFirstMatchingEntryPos < nMRUCount )
720 RemoveEntry( nFirstMatchingEntryPos );
721 nMRUCount--;
722 if ( nFirstMatchingEntryPos == nSelected )
723 bSelectNewEntry = true;
725 else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
727 RemoveEntry( nMRUCount - 1 );
728 nMRUCount--;
731 ImplClearLayoutData();
733 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
734 pNewEntry->mbIsSelected = bSelectNewEntry;
735 GetEntryList()->InsertEntry( 0, pNewEntry, false );
736 ImplUpdateEntryMetrics( *pNewEntry );
737 GetEntryList()->SetMRUCount( ++nMRUCount );
738 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
739 maMRUChangedHdl.Call( nullptr );
743 maSelectHdl.Call( nullptr );
744 mbSelectionChanged = false;
747 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
749 assert(nPos >= 0);
750 assert(mpEntryList->GetEntryCount() < LISTBOX_MAX_ENTRIES);
752 ImplClearLayoutData();
753 sal_Int32 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, mbSort );
755 if( GetStyle() & WB_WORDBREAK )
756 pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;
758 ImplUpdateEntryMetrics( *pNewEntry );
759 return nNewPos;
762 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
764 ImplClearLayoutData();
765 mpEntryList->RemoveEntry( nPos );
766 if( mnCurrentPos >= mpEntryList->GetEntryCount() )
767 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
768 ImplCalcMetrics();
771 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
773 mpEntryList->SetEntryFlags( nPos, nFlags );
774 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
775 if( pEntry )
776 ImplUpdateEntryMetrics( *pEntry );
779 void ImplListBoxWindow::ImplShowFocusRect()
781 if ( mbHasFocusRect )
782 HideFocus();
783 ShowFocus( maFocusRect );
784 mbHasFocusRect = true;
787 void ImplListBoxWindow::ImplHideFocusRect()
789 if ( mbHasFocusRect )
791 HideFocus();
792 mbHasFocusRect = false;
796 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
798 long nY = gnBorder;
800 sal_Int32 nSelect = mnTop;
801 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
802 while (pEntry)
804 long nEntryHeight = pEntry->getHeightWithMargin();
805 if (rPoint.Y() <= nEntryHeight + nY)
806 break;
807 nY += nEntryHeight;
808 pEntry = mpEntryList->GetEntryPtr( ++nSelect );
810 if( pEntry == nullptr )
811 nSelect = LISTBOX_ENTRY_NOTFOUND;
813 return nSelect;
816 bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
818 bool bRet = false;
820 if( i_nEntry >= mnTop )
822 if( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
823 PixelToLogic( GetSizePixel() ).Height() )
825 bRet = true;
829 return bRet;
832 long ImplListBoxWindow::GetEntryHeightWithMargin() const
834 long nMargin = ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
835 return mnMaxHeight + nMargin;
838 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
840 sal_Int32 nPos = mnTop;
841 long nWindowHeight = GetSizePixel().Height();
842 sal_Int32 nCount = mpEntryList->GetEntryCount();
843 long nDiff;
844 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
845 nPos++;
847 if( nDiff > nWindowHeight && nPos > mnTop )
848 nPos--;
850 if( nPos >= nCount )
851 nPos = nCount-1;
853 return nPos;
856 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
858 mbMouseMoveSelect = false; // only till the first MouseButtonDown
859 maQuickSelectionEngine.Reset();
861 if ( !IsReadOnly() )
863 if( rMEvt.GetClicks() == 1 )
865 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
866 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
868 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
869 mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
870 else
871 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
873 mnCurrentPos = nSelect;
874 mbTrackingSelect = true;
875 bool bCurPosChange = (mnCurrentPos != nSelect);
876 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
877 mbTrackingSelect = false;
878 if ( mbGrabFocus )
879 GrabFocus();
881 StartTracking( StartTrackingFlags::ScrollRepeat );
884 if( rMEvt.GetClicks() == 2 )
886 maDoubleClickHdl.Call( this );
889 else // if ( mbGrabFocus )
891 GrabFocus();
895 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
897 if ( rMEvt.IsLeaveWindow() )
899 if ( mbStackMode && IsMouseMoveSelect() && IsReallyVisible() )
901 if ( rMEvt.GetPosPixel().Y() < 0 )
903 DeselectAll();
904 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
905 SetTopEntry( 0 );
906 if ( mbStackMode )
908 mbTravelSelect = true;
909 mnSelectModifier = rMEvt.GetModifier();
910 ImplCallSelect();
911 mbTravelSelect = false;
917 else if ( ( ( !mbMulti && IsMouseMoveSelect() ) || mbStackMode ) && mpEntryList->GetEntryCount() )
919 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
920 if( aRect.IsInside( rMEvt.GetPosPixel() ) )
922 if ( IsMouseMoveSelect() )
924 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
925 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
926 nSelect = mpEntryList->GetEntryCount() - 1;
927 nSelect = std::min( nSelect, GetLastVisibleEntry() );
928 nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
929 // Select only visible Entries with MouseMove, otherwise Tracking...
930 if ( IsVisible( nSelect ) &&
931 mpEntryList->IsEntrySelectable( nSelect ) &&
932 ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() || ( nSelect != GetEntryList()->GetSelectedEntryPos( 0 ) ) ) )
934 mbTrackingSelect = true;
935 if ( SelectEntries( nSelect, LET_TRACKING ) )
937 if ( mbStackMode )
939 mbTravelSelect = true;
940 mnSelectModifier = rMEvt.GetModifier();
941 ImplCallSelect();
942 mbTravelSelect = false;
944 // When list box selection change by mouse move, notify
945 // VclEventId::ListboxSelect vcl event.
946 else
948 maListItemSelectHdl.Call(nullptr);
951 mbTrackingSelect = false;
955 // if the DD button was pressed and someone moved into the ListBox
956 // with the mouse button pressed...
957 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
959 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
960 mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
961 else
962 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
964 if ( mbStackMode && ( mpEntryList->GetSelectionAnchor() == LISTBOX_ENTRY_NOTFOUND ) )
965 mpEntryList->SetSelectionAnchor( 0 );
967 StartTracking( StartTrackingFlags::ScrollRepeat );
973 void ImplListBoxWindow::DeselectAll()
975 while ( GetEntryList()->GetSelectedEntryCount() )
977 sal_Int32 nS = GetEntryList()->GetSelectedEntryPos( 0 );
978 SelectEntry( nS, false );
982 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
984 if( (mpEntryList->IsEntryPosSelected( nPos ) != bSelect) && mpEntryList->IsEntrySelectable( nPos ) )
986 ImplHideFocusRect();
987 if( bSelect )
989 if( !mbMulti )
991 // deselect the selected entry
992 sal_Int32 nDeselect = GetEntryList()->GetSelectedEntryPos( 0 );
993 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
995 //SelectEntryPos( nDeselect, false );
996 GetEntryList()->SelectEntry( nDeselect, false );
997 if (IsUpdateMode() && IsReallyVisible())
998 Invalidate();
1001 mpEntryList->SelectEntry( nPos, true );
1002 mnCurrentPos = nPos;
1003 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
1005 Invalidate();
1006 if ( !IsVisible( nPos ) )
1008 ImplClearLayoutData();
1009 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
1010 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
1012 Resize();
1013 ShowProminentEntry( nPos );
1015 else
1017 ShowProminentEntry( nPos );
1022 else
1024 mpEntryList->SelectEntry( nPos, false );
1025 Invalidate();
1027 mbSelectionChanged = true;
1031 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
1033 bool bSelectionChanged = false;
1035 if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
1037 bool bFocusChanged = false;
1039 // here (Single-ListBox) only one entry can be deselected
1040 if( !mbMulti )
1042 sal_Int32 nDeselect = mpEntryList->GetSelectedEntryPos( 0 );
1043 if( nSelect != nDeselect )
1045 SelectEntry( nSelect, true );
1046 mpEntryList->SetLastSelected( nSelect );
1047 bFocusChanged = true;
1048 bSelectionChanged = true;
1051 // MultiListBox without Modifier
1052 else if( mbSimpleMode && !bCtrl && !bShift )
1054 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1055 for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1057 bool bSelect = nPos == nSelect;
1058 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1060 SelectEntry( nPos, bSelect );
1061 bFocusChanged = true;
1062 bSelectionChanged = true;
1065 mpEntryList->SetLastSelected( nSelect );
1066 mpEntryList->SetSelectionAnchor( nSelect );
1068 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1069 else if( ( !mbSimpleMode /* && !bShift */ ) || ( (mbSimpleMode && ( bCtrl || bShift )) || mbStackMode ) )
1071 // Space for selection change
1072 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1074 bool bSelect = ( mbStackMode && IsMouseMoveSelect() ) || !mpEntryList->IsEntryPosSelected( nSelect );
1075 if ( mbStackMode )
1077 sal_Int32 n;
1078 if ( bSelect )
1080 // All entries before nSelect must be selected...
1081 for ( n = 0; n < nSelect; n++ )
1082 SelectEntry( n, true );
1084 if ( !bSelect )
1086 for ( n = nSelect+1; n < mpEntryList->GetEntryCount(); n++ )
1087 SelectEntry( n, false );
1090 SelectEntry( nSelect, bSelect );
1091 mpEntryList->SetLastSelected( nSelect );
1092 mpEntryList->SetSelectionAnchor( mbStackMode ? 0 : nSelect );
1093 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1094 mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1095 bFocusChanged = true;
1096 bSelectionChanged = true;
1098 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1099 ( (bShift||mbStackMode) && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1101 mnCurrentPos = nSelect;
1102 bFocusChanged = true;
1104 sal_Int32 nAnchor = mpEntryList->GetSelectionAnchor();
1105 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && ( mpEntryList->GetSelectedEntryCount() || mbStackMode ) )
1107 nAnchor = mbStackMode ? 0 : mpEntryList->GetSelectedEntryPos( mpEntryList->GetSelectedEntryCount() - 1 );
1109 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1111 // All entries from Anchor to nSelect have to be selected
1112 sal_Int32 nStart = std::min( nSelect, nAnchor );
1113 sal_Int32 nEnd = std::max( nSelect, nAnchor );
1114 for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1116 if ( !mpEntryList->IsEntryPosSelected( n ) )
1118 SelectEntry( n, true );
1119 bSelectionChanged = true;
1123 // if appropriate some more has to be deselected...
1124 sal_Int32 nLast = mpEntryList->GetLastSelected();
1125 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1127 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1129 for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1131 if ( mpEntryList->IsEntryPosSelected( n ) )
1133 SelectEntry( n, false );
1134 bSelectionChanged = true;
1138 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1140 for ( sal_Int32 n = nLast; n < nSelect; n++ )
1142 if ( mpEntryList->IsEntryPosSelected( n ) )
1144 SelectEntry( n, false );
1145 bSelectionChanged = true;
1150 mpEntryList->SetLastSelected( nSelect );
1153 else if( eLET != LET_TRACKING )
1155 ImplHideFocusRect();
1156 Invalidate();
1157 bFocusChanged = true;
1160 else if( bShift )
1162 bFocusChanged = true;
1165 if( bSelectionChanged )
1166 mbSelectionChanged = true;
1168 if( bFocusChanged )
1170 long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop );
1171 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1172 Size aSz( maFocusRect.GetWidth(),
1173 mpEntryList->GetEntryHeight( nSelect ) );
1174 maFocusRect.SetSize( aSz );
1175 if( HasFocus() )
1176 ImplShowFocusRect();
1177 if (bSelectPosChange)
1179 maFocusHdl.Call(nSelect);
1182 ImplClearLayoutData();
1184 return bSelectionChanged;
1187 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1189 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
1190 bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1192 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1194 if ( bInside && !rTEvt.IsTrackingCanceled() )
1196 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1197 ImplCallSelect();
1199 else
1201 maCancelHdl.Call( nullptr );
1202 if ( !mbMulti )
1204 mbTrackingSelect = true;
1205 SelectEntry( mnTrackingSaveSelection, true );
1206 mbTrackingSelect = false;
1207 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1209 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1210 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1211 Size aSz( maFocusRect.GetWidth(),
1212 mpEntryList->GetEntryHeight( mnCurrentPos ) );
1213 maFocusRect.SetSize( aSz );
1214 ImplShowFocusRect();
1219 mbTrack = false;
1221 else
1223 bool bTrackOrQuickClick = mbTrack;
1224 if( !mbTrack )
1226 if ( bInside )
1228 mbTrack = true;
1231 // this case only happens, if the mouse button is pressed very briefly
1232 if( rTEvt.IsTrackingEnded() && mbTrack )
1234 bTrackOrQuickClick = true;
1235 mbTrack = false;
1239 if( bTrackOrQuickClick )
1241 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1242 Point aPt( aMEvt.GetPosPixel() );
1243 bool bShift = aMEvt.IsShift();
1244 bool bCtrl = aMEvt.IsMod1();
1246 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1247 if( aPt.Y() < 0 )
1249 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1251 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1252 if( nSelect < mnTop )
1253 SetTopEntry( mnTop-1 );
1256 else if( aPt.Y() > GetOutputSizePixel().Height() )
1258 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1260 nSelect = std::min( static_cast<sal_Int32>(mnCurrentPos+1), static_cast<sal_Int32>(mpEntryList->GetEntryCount()-1) );
1261 if( nSelect >= GetLastVisibleEntry() )
1262 SetTopEntry( mnTop+1 );
1265 else
1267 nSelect = static_cast<sal_Int32>( ( aPt.Y() + gnBorder ) / mnMaxHeight ) + mnTop;
1268 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1269 nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
1272 if ( bInside )
1274 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() )
1276 mbTrackingSelect = true;
1277 if ( SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl ) )
1279 if ( mbStackMode )
1281 mbTravelSelect = true;
1282 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1283 ImplCallSelect();
1284 mbTravelSelect = false;
1287 mbTrackingSelect = false;
1290 else
1292 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
1294 mbTrackingSelect = true;
1295 SelectEntry( GetEntryList()->GetSelectedEntryPos( 0 ), false );
1296 mbTrackingSelect = false;
1298 else if ( mbStackMode )
1300 if ( ( rTEvt.GetMouseEvent().GetPosPixel().X() > 0 ) && ( rTEvt.GetMouseEvent().GetPosPixel().X() < aRect.Right() ) )
1302 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 ) || ( rTEvt.GetMouseEvent().GetPosPixel().Y() > GetOutputSizePixel().Height() ) )
1304 bool bSelectionChanged = false;
1305 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 )
1306 && !mnCurrentPos )
1308 if ( mpEntryList->IsEntryPosSelected( 0 ) )
1310 SelectEntry( 0, false );
1311 bSelectionChanged = true;
1312 nSelect = LISTBOX_ENTRY_NOTFOUND;
1316 else
1318 mbTrackingSelect = true;
1319 bSelectionChanged = SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl );
1320 mbTrackingSelect = false;
1323 if ( bSelectionChanged )
1325 mbSelectionChanged = true;
1326 mbTravelSelect = true;
1327 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1328 ImplCallSelect();
1329 mbTravelSelect = false;
1335 mnCurrentPos = nSelect;
1336 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1338 ImplHideFocusRect();
1340 else
1342 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1343 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1344 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1345 maFocusRect.SetSize( aSz );
1346 ImplShowFocusRect();
1352 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1354 if( !ProcessKeyInput( rKEvt ) )
1355 Control::KeyInput( rKEvt );
1358 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1360 // entry to be selected
1361 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1362 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1364 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1366 bool bShift = aKeyCode.IsShift();
1367 bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1368 bool bMod2 = aKeyCode.IsMod2();
1369 bool bDone = false;
1370 bool bHandleKey = false;
1372 switch( aKeyCode.GetCode() )
1374 case KEY_UP:
1376 if ( IsReadOnly() )
1378 if ( GetTopEntry() )
1379 SetTopEntry( GetTopEntry()-1 );
1381 else if ( !bMod2 )
1383 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1385 nSelect = mpEntryList->FindFirstSelectable( 0 );
1387 else if ( mnCurrentPos )
1389 // search first selectable above the current position
1390 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1393 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1394 SetTopEntry( mnTop-1 );
1396 bDone = true;
1398 maQuickSelectionEngine.Reset();
1400 break;
1402 case KEY_DOWN:
1404 if ( IsReadOnly() )
1406 SetTopEntry( GetTopEntry()+1 );
1408 else if ( !bMod2 )
1410 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1412 nSelect = mpEntryList->FindFirstSelectable( 0 );
1414 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1416 // search first selectable below the current position
1417 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1 );
1420 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1421 SetTopEntry( mnTop+1 );
1423 bDone = true;
1425 maQuickSelectionEngine.Reset();
1427 break;
1429 case KEY_PAGEUP:
1431 if ( IsReadOnly() )
1433 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1434 SetTopEntry( ( mnTop > nCurVis ) ?
1435 (mnTop-nCurVis) : 0 );
1437 else if ( !bCtrl && !bMod2 )
1439 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1441 nSelect = mpEntryList->FindFirstSelectable( 0 );
1443 else if ( mnCurrentPos )
1445 if( mnCurrentPos == mnTop )
1447 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1448 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1451 // find first selectable starting from mnTop looking forward
1452 nSelect = mpEntryList->FindFirstSelectable( mnTop );
1454 bDone = true;
1456 maQuickSelectionEngine.Reset();
1458 break;
1460 case KEY_PAGEDOWN:
1462 if ( IsReadOnly() )
1464 SetTopEntry( GetLastVisibleEntry() );
1466 else if ( !bCtrl && !bMod2 )
1468 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1470 nSelect = mpEntryList->FindFirstSelectable( 0 );
1472 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1474 sal_Int32 nCount = mpEntryList->GetEntryCount();
1475 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1476 sal_Int32 nTmp = std::min( nCurVis, nCount );
1477 nTmp += mnTop - 1;
1478 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1480 long nTmp2 = std::min( static_cast<long>(nCount-nCurVis), static_cast<long>(static_cast<long>(mnTop)+static_cast<long>(nCurVis)-1) );
1481 nTmp2 = std::max( long(0) , nTmp2 );
1482 nTmp = static_cast<sal_Int32>(nTmp2+(nCurVis-1) );
1483 SetTopEntry( static_cast<sal_Int32>(nTmp2) );
1485 // find first selectable starting from nTmp looking backwards
1486 nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1488 bDone = true;
1490 maQuickSelectionEngine.Reset();
1492 break;
1494 case KEY_HOME:
1496 if ( IsReadOnly() )
1498 SetTopEntry( 0 );
1500 else if ( !bCtrl && !bMod2 && mnCurrentPos )
1502 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
1503 if( mnTop != 0 )
1504 SetTopEntry( 0 );
1506 bDone = true;
1508 maQuickSelectionEngine.Reset();
1510 break;
1512 case KEY_END:
1514 if ( IsReadOnly() )
1516 SetTopEntry( 0xFFFF );
1518 else if ( !bCtrl && !bMod2 )
1520 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1522 nSelect = mpEntryList->FindFirstSelectable( 0 );
1524 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1526 sal_Int32 nCount = mpEntryList->GetEntryCount();
1527 nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1528 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1529 if( nCount > nCurVis )
1530 SetTopEntry( nCount - nCurVis );
1532 bDone = true;
1534 maQuickSelectionEngine.Reset();
1536 break;
1538 case KEY_LEFT:
1540 if ( !bCtrl && !bMod2 )
1542 ScrollHorz( -HORZ_SCROLL );
1543 bDone = true;
1545 maQuickSelectionEngine.Reset();
1547 break;
1549 case KEY_RIGHT:
1551 if ( !bCtrl && !bMod2 )
1553 ScrollHorz( HORZ_SCROLL );
1554 bDone = true;
1556 maQuickSelectionEngine.Reset();
1558 break;
1560 case KEY_RETURN:
1562 if ( !bMod2 && !IsReadOnly() )
1564 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1565 ImplCallSelect();
1566 bDone = false; // do not catch RETURN
1568 maQuickSelectionEngine.Reset();
1570 break;
1572 case KEY_SPACE:
1574 if ( !bMod2 && !IsReadOnly() )
1576 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) || mbStackMode ) )
1578 nSelect = mnCurrentPos;
1579 eLET = LET_KEYSPACE;
1581 bDone = true;
1583 bHandleKey = true;
1585 break;
1587 case KEY_A:
1589 if( bCtrl && mbMulti )
1591 // paint only once
1592 bool bUpdates = IsUpdateMode();
1593 SetUpdateMode( false );
1595 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1596 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1597 SelectEntry( i, true );
1599 // restore update mode
1600 SetUpdateMode( bUpdates );
1601 Invalidate();
1603 maQuickSelectionEngine.Reset();
1605 bDone = true;
1607 else
1609 bHandleKey = true;
1612 break;
1614 default:
1615 bHandleKey = true;
1616 break;
1618 if (bHandleKey && !IsReadOnly())
1620 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1623 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1624 && ( ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1625 || ( eLET == LET_KEYSPACE )
1629 SAL_WARN_IF( mpEntryList->IsEntryPosSelected( nSelect ) && !mbMulti, "vcl", "ImplListBox: Selecting same Entry" );
1630 sal_Int32 nCount = mpEntryList->GetEntryCount();
1631 if (nSelect >= nCount)
1632 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1633 bool bCurPosChange = (mnCurrentPos != nSelect);
1634 mnCurrentPos = nSelect;
1635 if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1637 // tdf#129043 Correctly deliver events when changing values with arrow keys in combobox
1638 if (mbIsComboboxDropdown && IsReallyVisible())
1639 mbTravelSelect = true;
1640 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1641 ImplCallSelect();
1642 mbTravelSelect = false;
1646 return bDone;
1649 namespace
1651 vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1653 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1654 sal_Int32 nEntryCount( _rList.GetEntryCount() );
1655 if ( _nPos >= nEntryCount )
1656 _nPos = 0;
1657 _out_entryText = _rList.GetEntryText( _nPos );
1659 // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1660 // => normalize
1661 return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + 1 );
1664 sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
1666 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1667 return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1671 vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1673 return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1676 vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1678 sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1679 return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1682 void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
1684 sal_Int32 nSelect = lcl_getEntryPos( _entry );
1685 if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1687 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1688 // to select the given entry by typing its starting letters. No need to act.
1689 return;
1692 // normalize
1693 OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1694 sal_Int32 nCount = mpEntryList->GetEntryCount();
1695 if (nSelect >= nCount)
1696 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1698 // make visible
1699 ShowProminentEntry( nSelect );
1701 // actually select
1702 mnCurrentPos = nSelect;
1703 if ( SelectEntries( nSelect, LET_KEYMOVE ) )
1705 mbTravelSelect = true;
1706 mnSelectModifier = 0;
1707 ImplCallSelect();
1708 mbTravelSelect = false;
1712 void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
1714 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1716 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1717 if (!pEntry)
1718 return;
1720 long nWidth = GetOutputSizePixel().Width();
1721 long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1722 tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));
1724 if (mpEntryList->IsEntryPosSelected(nPos))
1726 rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor());
1727 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
1728 rRenderContext.SetLineColor();
1729 rRenderContext.DrawRect(aRect);
1731 else
1733 ApplySettings(rRenderContext);
1734 if (!IsEnabled())
1735 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
1737 rRenderContext.SetTextFillColor();
1739 if (IsUserDrawEnabled())
1741 mbInUserDraw = true;
1742 mnUserDrawEntry = nPos;
1743 aRect.AdjustLeft( -mnLeft );
1744 if (nPos < GetEntryList()->GetMRUCount())
1745 nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos));
1746 nPos = nPos - GetEntryList()->GetMRUCount();
1747 sal_Int32 nCurr = mnCurrentPos;
1748 if (mnCurrentPos < GetEntryList()->GetMRUCount())
1749 nCurr = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nCurr));
1750 nCurr = sal::static_int_cast<sal_Int32>(nCurr - GetEntryList()->GetMRUCount());
1752 UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos, nCurr);
1753 maUserDrawHdl.Call( &aUDEvt );
1754 mbInUserDraw = false;
1756 else
1758 DrawEntry(rRenderContext, nPos, true, true);
1762 void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText, bool bDrawTextAtImagePos)
1764 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(nPos);
1765 if (!pEntry)
1766 return;
1768 long nEntryHeight = pEntry->getHeightWithMargin();
1770 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1772 if (mbInUserDraw)
1773 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1775 long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1776 Size aImgSz;
1778 if (bDrawImage && mpEntryList->HasImages())
1780 Image aImage = mpEntryList->GetEntryImage(nPos);
1781 if (!!aImage)
1783 aImgSz = aImage.GetSizePixel();
1784 Point aPtImg(gnBorder - mnLeft, nY + ((nEntryHeight - aImgSz.Height()) / 2));
1786 // pb: #106948# explicit mirroring for calc
1787 if (mbMirroring)
1788 // right aligned
1789 aPtImg.setX( mnMaxWidth + gnBorder - aImgSz.Width() - mnLeft );
1791 if (!IsZoom())
1793 rRenderContext.DrawImage(aPtImg, aImage);
1795 else
1797 aImgSz.setWidth( CalcZoom(aImgSz.Width()) );
1798 aImgSz.setHeight( CalcZoom(aImgSz.Height()) );
1799 rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
1802 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1803 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1805 if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1807 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1808 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1809 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1810 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1812 if (!aBlendFrame.IsEmpty())
1814 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
1820 if (bDrawText)
1822 OUString aStr(mpEntryList->GetEntryText(nPos));
1823 if (!aStr.isEmpty())
1825 long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * gnBorder);
1826 // a multiline entry should only be as wide as the window
1827 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1828 nMaxWidth = GetOutputSizePixel().Width() - 2 * gnBorder;
1830 tools::Rectangle aTextRect(Point(gnBorder - mnLeft, nY),
1831 Size(nMaxWidth, nEntryHeight));
1833 if (!bDrawTextAtImagePos && (mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled()))
1835 long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
1836 aTextRect.AdjustLeft(nImageWidth + IMG_TXT_DISTANCE );
1839 // pb: #106948# explicit mirroring for calc
1840 if (mbMirroring)
1842 // right aligned
1843 aTextRect.SetLeft( nMaxWidth + gnBorder - rRenderContext.GetTextWidth(aStr) - mnLeft );
1844 if (aImgSz.Width() > 0)
1845 aTextRect.AdjustLeft( -(aImgSz.Width() + IMG_TXT_DISTANCE) );
1848 DrawTextFlags nDrawStyle = ImplGetTextStyle();
1849 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1850 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1851 if (pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled)
1852 nDrawStyle |= DrawTextFlags::Disable;
1854 rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
1858 if ( !maSeparators.empty() && ( isSeparator(nPos) || isSeparator(nPos-1) ) )
1860 Color aOldLineColor(rRenderContext.GetLineColor());
1861 rRenderContext.SetLineColor((GetBackground() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
1862 Point aStartPos(0, nY);
1863 if (isSeparator(nPos))
1864 aStartPos.AdjustY(pEntry->getHeightWithMargin() - 1 );
1865 Point aEndPos(aStartPos);
1866 aEndPos.setX( GetOutputSizePixel().Width() );
1867 rRenderContext.DrawLine(aStartPos, aEndPos);
1868 rRenderContext.SetLineColor(aOldLineColor);
1872 void ImplListBoxWindow::FillLayoutData() const
1874 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1875 const_cast<ImplListBoxWindow*>(this)->Invalidate(tools::Rectangle(Point(0, 0), GetOutputSize()));
1878 void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1880 sal_Int32 nCount = mpEntryList->GetEntryCount();
1882 bool bShowFocusRect = mbHasFocusRect;
1883 if (mbHasFocusRect)
1884 ImplHideFocusRect();
1886 long nY = 0; // + gnBorder;
1887 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1889 for (sal_Int32 i = mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
1891 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(i);
1892 long nEntryHeight = pEntry->getHeightWithMargin();
1893 if (nY + nEntryHeight >= rRect.Top() &&
1894 nY <= rRect.Bottom() + mnMaxHeight)
1896 ImplPaint(rRenderContext, i);
1898 nY += nEntryHeight;
1901 long nHeightDiff = mpEntryList->GetAddedHeight(mnCurrentPos, mnTop);
1902 maFocusRect.SetPos(Point(0, nHeightDiff));
1903 Size aSz(maFocusRect.GetWidth(), mpEntryList->GetEntryHeight(mnCurrentPos));
1904 maFocusRect.SetSize(aSz);
1905 if (HasFocus() && bShowFocusRect)
1906 ImplShowFocusRect();
1909 void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1911 if (SupportsDoubleBuffering())
1913 // This widget is explicitly double-buffered, so avoid partial paints.
1914 tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
1915 ImplDoPaint(rRenderContext, aRect);
1917 else
1918 ImplDoPaint(rRenderContext, rRect);
1921 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1923 // FIXME: ListBoxEntryFlags::MultiLine
1925 const sal_Int32 nCount = mpEntryList->GetEntryCount()-mnTop;
1926 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1927 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1928 if( nEntries > nCount )
1929 nEntries = static_cast<sal_uInt16>(nCount);
1931 return nEntries;
1934 void ImplListBoxWindow::Resize()
1936 Control::Resize();
1938 bool bShowFocusRect = mbHasFocusRect;
1939 if ( bShowFocusRect )
1940 ImplHideFocusRect();
1942 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1944 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1945 maFocusRect.SetSize( aSz );
1948 if ( bShowFocusRect )
1949 ImplShowFocusRect();
1951 ImplClearLayoutData();
1954 void ImplListBoxWindow::GetFocus()
1956 sal_Int32 nPos = mnCurrentPos;
1957 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1958 nPos = 0;
1959 long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop );
1960 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1961 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
1962 maFocusRect.SetSize( aSz );
1963 ImplShowFocusRect();
1964 Control::GetFocus();
1967 void ImplListBoxWindow::LoseFocus()
1969 ImplHideFocusRect();
1970 Control::LoseFocus();
1973 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1975 if( mpEntryList->GetEntryCount() == 0 )
1976 return;
1978 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1980 sal_Int32 nLastEntry = mpEntryList->GetEntryCount()-1;
1981 if( nTop > nLastEntry )
1982 nTop = nLastEntry;
1983 const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
1984 while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->getHeightWithMargin() <= nWHeight )
1985 nTop--;
1987 if ( nTop != mnTop )
1989 ImplClearLayoutData();
1990 long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop );
1991 Update();
1992 ImplHideFocusRect();
1993 mnTop = nTop;
1994 Scroll( 0, nDiff );
1995 Update();
1996 if( HasFocus() )
1997 ImplShowFocusRect();
1998 maScrollHdl.Call( this );
2002 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
2004 if( meProminentType == ProminentEntry::MIDDLE )
2006 sal_Int32 nPos = nEntryPos;
2007 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
2008 while( nEntryPos > 0 && mpEntryList->GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
2009 nEntryPos--;
2011 SetTopEntry( nEntryPos );
2014 void ImplListBoxWindow::SetLeftIndent( long n )
2016 ScrollHorz( n - mnLeft );
2019 void ImplListBoxWindow::ScrollHorz( long n )
2021 long nDiff = 0;
2022 if ( n > 0 )
2024 long nWidth = GetOutputSizePixel().Width();
2025 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
2026 nDiff = n;
2028 else if ( n < 0 )
2030 if( mnLeft )
2032 long nAbs = -n;
2033 nDiff = - std::min( mnLeft, nAbs );
2037 if ( nDiff )
2039 ImplClearLayoutData();
2040 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
2041 Update();
2042 ImplHideFocusRect();
2043 Scroll( -nDiff, 0 );
2044 Update();
2045 if( HasFocus() )
2046 ImplShowFocusRect();
2047 maScrollHdl.Call( this );
2051 void ImplListBoxWindow::SetSeparatorPos( sal_Int32 n )
2053 maSeparators.clear();
2055 if ( n != LISTBOX_ENTRY_NOTFOUND )
2057 maSeparators.insert( n );
2061 sal_Int32 ImplListBoxWindow::GetSeparatorPos() const
2063 if (!maSeparators.empty())
2064 return *(maSeparators.begin());
2065 else
2066 return LISTBOX_ENTRY_NOTFOUND;
2069 bool ImplListBoxWindow::isSeparator( const sal_Int32 &n) const
2071 return maSeparators.find(n) != maSeparators.end();
2074 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
2076 // FIXME: ListBoxEntryFlags::MultiLine
2078 Size aSz;
2079 aSz.setHeight(nMaxLines * GetEntryHeightWithMargin());
2080 aSz.setWidth( mnMaxWidth + 2*gnBorder );
2081 return aSz;
2084 tools::Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
2086 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
2087 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->getHeightWithMargin() : GetEntryHeightWithMargin() );
2088 long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList()->GetMRUCount()*GetEntryHeightWithMargin();
2089 tools::Rectangle aRect( Point( 0, nY ), aSz );
2090 return aRect;
2093 void ImplListBoxWindow::StateChanged( StateChangedType nType )
2095 Control::StateChanged( nType );
2097 if ( nType == StateChangedType::Zoom )
2099 ApplySettings(*this);
2100 ImplCalcMetrics();
2101 Invalidate();
2103 else if ( nType == StateChangedType::UpdateMode )
2105 if ( IsUpdateMode() && IsReallyVisible() )
2106 Invalidate();
2108 else if ( nType == StateChangedType::ControlFont )
2110 ApplySettings(*this);
2111 ImplCalcMetrics();
2112 Invalidate();
2114 else if ( nType == StateChangedType::ControlForeground )
2116 ApplySettings(*this);
2117 Invalidate();
2119 else if ( nType == StateChangedType::ControlBackground )
2121 ApplySettings(*this);
2122 Invalidate();
2124 else if( nType == StateChangedType::Enable )
2126 Invalidate();
2129 ImplClearLayoutData();
2132 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
2134 Control::DataChanged( rDCEvt );
2136 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2137 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2138 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2139 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2141 ImplClearLayoutData();
2142 ApplySettings(*this);
2143 ImplCalcMetrics();
2144 Invalidate();
2148 DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
2150 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2152 if (mpEntryList->HasImages())
2153 nTextStyle |= DrawTextFlags::Left;
2154 else if (mbCenter)
2155 nTextStyle |= DrawTextFlags::Center;
2156 else if (mbRight)
2157 nTextStyle |= DrawTextFlags::Right;
2158 else
2159 nTextStyle |= DrawTextFlags::Left;
2161 return nTextStyle;
2164 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2165 Control( pParent, nWinStyle ),
2166 maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
2168 // for native widget rendering we must be able to detect this window type
2169 SetType( WindowType::LISTBOXWINDOW );
2171 mpVScrollBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
2172 mpHScrollBar = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
2173 mpScrollBarBox = VclPtr<ScrollBarBox>::Create( this );
2175 Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2176 mpVScrollBar->SetScrollHdl( aLink );
2177 mpHScrollBar->SetScrollHdl( aLink );
2179 mbVScroll = false;
2180 mbHScroll = false;
2181 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2182 mbEdgeBlending = false;
2184 maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2185 maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2186 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2187 maLBWindow->Show();
2190 ImplListBox::~ImplListBox()
2192 disposeOnce();
2195 void ImplListBox::dispose()
2197 mpHScrollBar.disposeAndClear();
2198 mpVScrollBar.disposeAndClear();
2199 mpScrollBarBox.disposeAndClear();
2200 maLBWindow.disposeAndClear();
2201 Control::dispose();
2204 void ImplListBox::Clear()
2206 maLBWindow->Clear();
2207 if ( GetEntryList()->GetMRUCount() )
2209 maLBWindow->GetEntryList()->SetMRUCount( 0 );
2210 maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2212 mpVScrollBar->SetThumbPos( 0 );
2213 mpHScrollBar->SetThumbPos( 0 );
2214 CompatStateChanged( StateChangedType::Data );
2217 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2219 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2220 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2221 CompatStateChanged( StateChangedType::Data );
2222 return nNewPos;
2225 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2227 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2228 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2229 CompatStateChanged( StateChangedType::Data );
2230 return nNewPos;
2233 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2235 maLBWindow->RemoveEntry( nPos );
2236 CompatStateChanged( StateChangedType::Data );
2239 void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
2241 maLBWindow->SetEntryFlags( nPos, nFlags );
2244 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2246 maLBWindow->SelectEntry( nPos, bSelect );
2249 void ImplListBox::SetNoSelection()
2251 maLBWindow->DeselectAll();
2254 void ImplListBox::GetFocus()
2256 if (maLBWindow)
2257 maLBWindow->GrabFocus();
2258 else
2259 Control::GetFocus();
2262 void ImplListBox::Resize()
2264 Control::Resize();
2265 ImplResizeControls();
2266 ImplCheckScrollBars();
2269 IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
2271 CompatStateChanged( StateChangedType::Data );
2274 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
2276 long nSet = GetTopEntry();
2277 if( nSet > mpVScrollBar->GetRangeMax() )
2278 mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2279 mpVScrollBar->SetThumbPos( GetTopEntry() );
2281 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2283 maScrollHdl.Call( this );
2286 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
2288 sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
2289 if( pSB == mpVScrollBar )
2290 SetTopEntry( nPos );
2291 else if( pSB == mpHScrollBar )
2292 SetLeftIndent( nPos );
2293 if( GetParent() )
2294 GetParent()->Invalidate( InvalidateFlags::Update );
2297 void ImplListBox::ImplCheckScrollBars()
2299 bool bArrange = false;
2301 Size aOutSz = GetOutputSizePixel();
2302 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2303 sal_uInt16 nMaxVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2305 // vertical ScrollBar
2306 if( nEntries > nMaxVisEntries )
2308 if( !mbVScroll )
2309 bArrange = true;
2310 mbVScroll = true;
2312 // check of the scrolled-out region
2313 if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2314 GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2315 ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2316 else
2317 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2319 else
2321 if( mbVScroll )
2322 bArrange = true;
2323 mbVScroll = false;
2324 SetTopEntry( 0 );
2327 // horizontal ScrollBar
2328 if( mbAutoHScroll )
2330 long nWidth = static_cast<sal_uInt16>(aOutSz.Width());
2331 if ( mbVScroll )
2332 nWidth -= mpVScrollBar->GetSizePixel().Width();
2334 long nMaxWidth = GetMaxEntryWidth();
2335 if( nWidth < nMaxWidth )
2337 if( !mbHScroll )
2338 bArrange = true;
2339 mbHScroll = true;
2341 if ( !mbVScroll ) // maybe we do need one now
2343 nMaxVisEntries = static_cast<sal_uInt16>( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeightWithMargin() );
2344 if( nEntries > nMaxVisEntries )
2346 bArrange = true;
2347 mbVScroll = true;
2349 // check of the scrolled-out region
2350 if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2351 GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2352 ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2353 else
2354 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2358 // check of the scrolled-out region
2359 sal_uInt16 nMaxLI = static_cast<sal_uInt16>(nMaxWidth - nWidth);
2360 if ( nMaxLI < GetLeftIndent() )
2361 SetLeftIndent( nMaxLI );
2363 else
2365 if( mbHScroll )
2366 bArrange = true;
2367 mbHScroll = false;
2368 SetLeftIndent( 0 );
2372 if( bArrange )
2373 ImplResizeControls();
2375 ImplInitScrollBars();
2378 void ImplListBox::ImplInitScrollBars()
2380 Size aOutSz = maLBWindow->GetOutputSizePixel();
2382 if ( mbVScroll )
2384 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2385 sal_uInt16 nVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2386 mpVScrollBar->SetRangeMax( nEntries );
2387 mpVScrollBar->SetVisibleSize( nVisEntries );
2388 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2391 if ( mbHScroll )
2393 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2394 mpHScrollBar->SetVisibleSize( static_cast<sal_uInt16>(aOutSz.Width()) );
2395 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2396 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2400 void ImplListBox::ImplResizeControls()
2402 // Here we only position the Controls; if the Scrollbars are to be
2403 // visible is already determined in ImplCheckScrollBars
2405 Size aOutSz = GetOutputSizePixel();
2406 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2407 nSBWidth = CalcZoom( nSBWidth );
2409 Size aInnerSz( aOutSz );
2410 if ( mbVScroll )
2411 aInnerSz.AdjustWidth( -nSBWidth );
2412 if ( mbHScroll )
2413 aInnerSz.AdjustHeight( -nSBWidth );
2415 // pb: #106948# explicit mirroring for calc
2416 // Scrollbar on left or right side?
2417 bool bMirroring = maLBWindow->IsMirroring();
2418 Point aWinPos( bMirroring && mbVScroll ? nSBWidth : 0, 0 );
2419 maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
2421 // ScrollBarBox
2422 if( mbVScroll && mbHScroll )
2424 Point aBoxPos( bMirroring ? 0 : aInnerSz.Width(), aInnerSz.Height() );
2425 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2426 mpScrollBarBox->Show();
2428 else
2430 mpScrollBarBox->Hide();
2433 // vertical ScrollBar
2434 if( mbVScroll )
2436 // Scrollbar on left or right side?
2437 Point aVPos( bMirroring ? 0 : aOutSz.Width() - nSBWidth, 0 );
2438 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2439 mpVScrollBar->Show();
2441 else
2443 mpVScrollBar->Hide();
2444 // #107254# Don't reset top entry after resize, but check for max top entry
2445 SetTopEntry( GetTopEntry() );
2448 // horizontal ScrollBar
2449 if( mbHScroll )
2451 Point aHPos( ( bMirroring && mbVScroll ) ? nSBWidth : 0, aOutSz.Height() - nSBWidth );
2452 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2453 mpHScrollBar->Show();
2455 else
2457 mpHScrollBar->Hide();
2458 SetLeftIndent( 0 );
2462 void ImplListBox::StateChanged( StateChangedType nType )
2464 if ( nType == StateChangedType::InitShow )
2466 ImplCheckScrollBars();
2468 else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
2470 bool bUpdate = IsUpdateMode();
2471 maLBWindow->SetUpdateMode( bUpdate );
2472 if ( bUpdate && IsReallyVisible() )
2473 ImplCheckScrollBars();
2475 else if( nType == StateChangedType::Enable )
2477 mpHScrollBar->Enable( IsEnabled() );
2478 mpVScrollBar->Enable( IsEnabled() );
2479 mpScrollBarBox->Enable( IsEnabled() );
2480 maLBWindow->Enable( IsEnabled() );
2482 Invalidate();
2484 else if ( nType == StateChangedType::Zoom )
2486 maLBWindow->SetZoom( GetZoom() );
2487 Resize();
2489 else if ( nType == StateChangedType::ControlFont )
2491 maLBWindow->SetControlFont( GetControlFont() );
2493 else if ( nType == StateChangedType::ControlForeground )
2495 maLBWindow->SetControlForeground( GetControlForeground() );
2497 else if ( nType == StateChangedType::ControlBackground )
2499 maLBWindow->SetControlBackground( GetControlBackground() );
2501 else if( nType == StateChangedType::Mirroring )
2503 maLBWindow->EnableRTL( IsRTLEnabled() );
2504 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2505 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2506 ImplResizeControls();
2509 Control::StateChanged( nType );
2512 bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
2514 bool bDone = false;
2515 if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
2517 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2518 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2520 const CommandWheelData* pData = rCEvt.GetWheelData();
2521 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2523 bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2526 else if (rCEvt.GetCommand() == CommandEventId::Gesture)
2528 bDone = HandleScrollCommand(rCEvt, mpHScrollBar, mpVScrollBar);
2532 return bDone || Window::EventNotify( rNEvt );
2535 const Wallpaper& ImplListBox::GetDisplayBackground() const
2537 return maLBWindow->GetDisplayBackground();
2540 bool ImplListBox::HandleWheelAsCursorTravel( const CommandEvent& rCEvt )
2542 bool bDone = false;
2543 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2545 const CommandWheelData* pData = rCEvt.GetWheelData();
2546 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2548 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2549 KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2550 bDone = ProcessKeyInput( aKeyEvent );
2553 return bDone;
2556 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2558 bool bChanges = GetEntryList()->GetMRUCount() != 0;
2560 // Remove old MRU entries
2561 for ( sal_Int32 n = GetEntryList()->GetMRUCount();n; )
2562 maLBWindow->RemoveEntry( --n );
2564 sal_Int32 nMRUCount = 0;
2565 sal_Int32 nIndex = 0;
2568 OUString aEntry = rEntries.getToken( 0, cSep, nIndex );
2569 // Accept only existing entries
2570 if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2572 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2573 maLBWindow->GetEntryList()->InsertEntry( nMRUCount++, pNewEntry, false );
2574 bChanges = true;
2577 while ( nIndex >= 0 );
2579 if ( bChanges )
2581 maLBWindow->GetEntryList()->SetMRUCount( nMRUCount );
2582 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2583 CompatStateChanged( StateChangedType::Data );
2587 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2589 OUStringBuffer aEntries;
2590 for ( sal_Int32 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2592 aEntries.append(GetEntryList()->GetEntryText( n ));
2593 if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2594 aEntries.append(cSep);
2596 return aEntries.makeStringAndClear();
2599 void ImplListBox::SetEdgeBlending(bool bNew)
2601 if(mbEdgeBlending != bNew)
2603 mbEdgeBlending = bNew;
2604 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2608 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2609 Control ( pParent, nWinStyle )
2611 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2612 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2613 SetBackground();
2614 else
2615 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2617 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2619 mbUserDrawEnabled = false;
2620 mbEdgeBlending = false;
2621 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2624 void ImplWin::MouseButtonDown( const MouseEvent& )
2626 if( IsEnabled() )
2628 maMBDownHdl.Call(this);
2632 void ImplWin::FillLayoutData() const
2634 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2635 ImplWin* pThis = const_cast<ImplWin*>(this);
2636 pThis->ImplDraw(*pThis, true);
2639 bool ImplWin::PreNotify( NotifyEvent& rNEvt )
2641 const MouseEvent* pMouseEvt = nullptr;
2643 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
2645 if( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() )
2647 // trigger redraw as mouse over state has changed
2648 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2649 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2651 GetParent()->GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
2652 GetParent()->GetWindow( GetWindowType::Border )->Update();
2657 return Control::PreNotify(rNEvt);
2660 void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
2662 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2664 if (!bLayout)
2666 bool bNativeOK = false;
2667 bool bHasFocus = HasFocus();
2668 bool bIsEnabled = IsEnabled();
2670 ControlState nState = ControlState::ENABLED;
2671 if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2672 && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
2674 // Repaint the (focused) area similarly to
2675 // ImplSmallBorderWindowView::DrawWindow() in
2676 // vcl/source/window/brdwin.cxx
2677 vcl::Window *pWin = GetParent();
2679 ImplControlValue aControlValue;
2680 bIsEnabled &= pWin->IsEnabled();
2681 if ( !bIsEnabled )
2682 nState &= ~ControlState::ENABLED;
2683 bHasFocus |= pWin->HasFocus();
2684 if ( bHasFocus )
2685 nState |= ControlState::FOCUSED;
2687 // The listbox is painted over the entire control including the
2688 // border, but ImplWin does not contain the border => correction
2689 // needed.
2690 sal_Int32 nLeft, nTop, nRight, nBottom;
2691 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2692 Point aPoint( -nLeft, -nTop );
2693 tools::Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2695 bool bMouseOver = false;
2696 vcl::Window *pChild = pWin->GetWindow( GetWindowType::FirstChild );
2697 while( pChild && !(bMouseOver = pChild->IsMouseOver()) )
2698 pChild = pChild->GetWindow( GetWindowType::Next );
2699 if( bMouseOver )
2700 nState |= ControlState::ROLLOVER;
2702 // if parent has no border, then nobody has drawn the background
2703 // since no border window exists. so draw it here.
2704 WinBits nParentStyle = pWin->GetStyle();
2705 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2707 tools::Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2708 pWin->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
2709 nState, aControlValue, OUString() );
2712 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
2713 nState, aControlValue, OUString());
2716 if (bIsEnabled)
2718 if (bHasFocus && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2720 if ( !ImplGetSVData()->maNWFData.mbNoFocusRects )
2722 rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
2723 rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
2725 else
2727 rRenderContext.SetLineColor();
2728 rRenderContext.SetFillColor();
2729 rRenderContext.SetTextColor( rStyleSettings.GetFieldTextColor() );
2731 rRenderContext.DrawRect( maFocusRect );
2733 else
2735 Color aColor;
2736 if (IsControlForeground())
2737 aColor = GetControlForeground();
2738 else if (ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2740 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2741 aColor = rStyleSettings.GetButtonRolloverTextColor();
2742 else
2743 aColor = rStyleSettings.GetButtonTextColor();
2745 else
2747 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2748 aColor = rStyleSettings.GetFieldRolloverTextColor();
2749 else
2750 aColor = rStyleSettings.GetFieldTextColor();
2752 rRenderContext.SetTextColor(aColor);
2753 if (!bNativeOK)
2754 rRenderContext.Erase(maFocusRect);
2757 else // Disabled
2759 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
2760 if (!bNativeOK)
2761 rRenderContext.Erase(maFocusRect);
2765 if ( IsUserDrawEnabled() )
2767 UserDrawEvent aUDEvt(this, &rRenderContext, maFocusRect, mnItemPos, 0);
2768 maUserDrawHdl.Call( &aUDEvt );
2770 else
2772 DrawEntry(rRenderContext, bLayout);
2776 void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
2778 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2780 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
2781 ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
2783 if (IsControlBackground())
2784 rRenderContext.SetBackground(GetControlBackground());
2785 else
2786 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
2789 void ImplWin::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2791 ImplDraw(rRenderContext);
2794 void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bLayout)
2796 long nBorder = 1;
2797 Size aOutSz(GetOutputSizePixel());
2799 bool bImage = !!maImage;
2800 if (bImage && !bLayout)
2802 DrawImageFlags nStyle = DrawImageFlags::NONE;
2803 Size aImgSz = maImage.GetSizePixel();
2804 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2805 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2807 // check for HC mode
2808 Image *pImage = &maImage;
2810 if ( !IsZoom() )
2812 rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
2814 else
2816 aImgSz.setWidth( CalcZoom( aImgSz.Width() ) );
2817 aImgSz.setHeight( CalcZoom( aImgSz.Height() ) );
2818 rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2821 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2823 if(nEdgeBlendingPercent)
2825 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2826 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2827 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2828 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2830 if(!aBlendFrame.IsEmpty())
2832 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
2837 if( !maString.isEmpty() )
2839 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2841 if ( bImage && !bLayout )
2842 nTextStyle |= DrawTextFlags::Left;
2843 else if ( GetStyle() & WB_CENTER )
2844 nTextStyle |= DrawTextFlags::Center;
2845 else if ( GetStyle() & WB_RIGHT )
2846 nTextStyle |= DrawTextFlags::Right;
2847 else
2848 nTextStyle |= DrawTextFlags::Left;
2850 tools::Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2852 if ( bImage || IsUserDrawEnabled() )
2854 aTextRect.AdjustLeft(maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE );
2857 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : nullptr;
2858 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : nullptr;
2859 rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2862 if( HasFocus() && !bLayout )
2863 ShowFocus( maFocusRect );
2866 void ImplWin::Resize()
2868 Control::Resize();
2869 maFocusRect.SetSize( GetOutputSizePixel() );
2870 Invalidate();
2873 void ImplWin::GetFocus()
2875 ShowFocus( maFocusRect );
2876 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2877 IsNativeWidgetEnabled() &&
2878 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2880 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2881 if( ! pWin )
2882 pWin = GetParent();
2883 pWin->Invalidate();
2885 else
2886 Invalidate();
2887 Control::GetFocus();
2890 void ImplWin::LoseFocus()
2892 HideFocus();
2893 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2894 IsNativeWidgetEnabled() &&
2895 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2897 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2898 if( ! pWin )
2899 pWin = GetParent();
2900 pWin->Invalidate();
2902 else
2903 Invalidate();
2904 Control::LoseFocus();
2907 void ImplWin::ShowFocus(const tools::Rectangle& rRect)
2909 if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
2911 ImplControlValue aControlValue;
2913 vcl::Window *pWin = GetParent();
2914 tools::Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
2915 pWin->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
2916 ControlState::FOCUSED, aControlValue, OUString());
2918 Control::ShowFocus(rRect);
2921 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2922 PushButton( pParent, nWinStyle )
2926 void ImplBtn::MouseButtonDown( const MouseEvent& )
2928 if( IsEnabled() )
2929 maMBDownHdl.Call(this);
2932 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2933 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
2935 mpImplLB = nullptr;
2936 mnDDLineCount = 0;
2937 mbAutoWidth = false;
2939 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2941 vcl::Window * pBorderWindow = ImplGetBorderWindow();
2942 if( pBorderWindow )
2944 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2945 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2947 else
2949 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2954 ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
2956 disposeOnce();
2959 void ImplListBoxFloatingWindow::dispose()
2961 mpImplLB.clear();
2962 FloatingWindow::dispose();
2966 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2968 if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2970 if( !GetParent()->HasChildPathFocus( true ) )
2971 EndPopupMode();
2974 return FloatingWindow::PreNotify( rNEvt );
2977 void ImplListBoxFloatingWindow::setPosSizePixel( long nX, long nY, long nWidth, long nHeight, PosSizeFlags nFlags )
2979 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2981 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2982 // after a call to Resize(), we adjust its position if necessary
2983 if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
2985 Point aPos = GetParent()->GetPosPixel();
2986 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2988 if ( nFlags & PosSizeFlags::X )
2989 aPos.setX( nX );
2991 if ( nFlags & PosSizeFlags::Y )
2992 aPos.setY( nY );
2994 sal_uInt16 nIndex;
2995 SetPosPixel( ImplCalcPos( this, tools::Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
2998 // if( !IsReallyVisible() )
3000 // The ImplListBox does not get a Resize() as not visible.
3001 // But the windows must get a Resize(), so that the number of
3002 // visible entries is correct for PgUp/PgDown.
3003 // The number also cannot be calculated by List/Combobox, as for
3004 // this the presence of the vertical Scrollbar has to be known.
3005 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3006 static_cast<vcl::Window*>(mpImplLB)->Resize();
3007 static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
3011 void ImplListBoxFloatingWindow::Resize()
3013 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3014 FloatingWindow::Resize();
3017 Size ImplListBoxFloatingWindow::CalcFloatSize()
3019 Size aFloatSz( maPrefSz );
3021 sal_Int32 nLeft, nTop, nRight, nBottom;
3022 GetBorder( nLeft, nTop, nRight, nBottom );
3024 sal_Int32 nLines = mpImplLB->GetEntryList()->GetEntryCount();
3025 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
3026 nLines = mnDDLineCount;
3028 Size aSz = mpImplLB->CalcSize( nLines );
3029 long nMaxHeight = aSz.Height() + nTop + nBottom;
3031 if ( mnDDLineCount )
3032 aFloatSz.setHeight( nMaxHeight );
3034 if( mbAutoWidth )
3036 // AutoSize first only for width...
3038 aFloatSz.setWidth( aSz.Width() + nLeft + nRight );
3039 aFloatSz.AdjustWidth(nRight ); // adding some space looks better...
3041 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
3043 // then we also need the vertical Scrollbar
3044 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3045 aFloatSz.AdjustWidth(nSBWidth );
3048 long nDesktopWidth = GetDesktopRectPixel().getWidth();
3049 if (aFloatSz.Width() > nDesktopWidth)
3050 // Don't exceed the desktop width.
3051 aFloatSz.setWidth( nDesktopWidth );
3054 if ( aFloatSz.Height() > nMaxHeight )
3055 aFloatSz.setHeight( nMaxHeight );
3057 // Minimal height, in case height is not set to Float height.
3058 // The parent of FloatWin must be DropDown-Combo/Listbox.
3059 Size aParentSz = GetParent()->GetSizePixel();
3060 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
3061 aFloatSz.setHeight( aParentSz.Height() );
3063 // do not get narrower than the parent...
3064 if( aFloatSz.Width() < aParentSz.Width() )
3065 aFloatSz.setWidth( aParentSz.Width() );
3067 // align height to entries...
3068 long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
3069 long nEntryHeight = mpImplLB->GetEntryHeightWithMargin();
3070 if ( nInnerHeight % nEntryHeight )
3072 nInnerHeight /= nEntryHeight;
3073 nInnerHeight++;
3074 nInnerHeight *= nEntryHeight;
3075 aFloatSz.setHeight( nInnerHeight + nTop + nBottom );
3078 if (aFloatSz.Width() < aSz.Width())
3080 // The max width of list box entries exceeds the window width.
3081 // Account for the scroll bar height.
3082 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3083 aFloatSz.AdjustHeight(nSBWidth );
3086 return aFloatSz;
3089 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
3091 if( IsInPopupMode() )
3092 return;
3094 Size aFloatSz = CalcFloatSize();
3096 SetSizePixel( aFloatSz );
3097 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3099 sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectedEntryPos( 0 );
3100 mnPopupModeStartSaveSelection = nPos;
3102 Size aSz = GetParent()->GetSizePixel();
3103 Point aPos = GetParent()->GetPosPixel();
3104 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
3105 // FIXME: this ugly hack is for Mac/Aqua
3106 // should be replaced by a real mechanism to place the float rectangle
3107 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3108 GetParent()->IsNativeWidgetEnabled() )
3110 const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
3111 aPos.AdjustX(nLeft );
3112 aPos.AdjustY(nTop );
3113 aSz.AdjustWidth( -(nLeft + nRight) );
3114 aSz.AdjustHeight( -(nTop + nBottom) );
3116 tools::Rectangle aRect( aPos, aSz );
3118 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
3119 // where the document is unmirrored
3120 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
3121 vcl::Window *pGrandparent = GetParent()->GetParent();
3122 const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
3124 if( pGrandparent->ImplIsAntiparallel() )
3125 pGrandparentOutDev->ReMirror( aRect );
3127 // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
3128 StartPopupMode( aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::AllMouseButtonClose );
3130 if( nPos != LISTBOX_ENTRY_NOTFOUND )
3131 mpImplLB->ShowProminentEntry( nPos );
3133 if( bStartTracking )
3134 mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
3136 if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
3137 mpImplLB->GetMainWindow()->GrabFocus();
3139 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3143 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */