1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 
22 #include <vcl/svapp.hxx>
23 #include <vcl/settings.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/scrbar.hxx>
26 #include <vcl/toolkit/lstbox.hxx>
27 #include <vcl/i18nhelp.hxx>
28 #include <vcl/naturalsort.hxx>
29 
30 #include <listbox.hxx>
31 #include <controldata.hxx>
32 #include <svdata.hxx>
33 #include <window.h>
34 
35 #include <com/sun/star/accessibility/AccessibleRole.hpp>
36 
37 #include <rtl/instance.hxx>
38 #include <sal/log.hxx>
39 #include <o3tl/safeint.hxx>
40 #include <osl/diagnose.h>
41 #include <comphelper/string.hxx>
42 #include <comphelper/processfactory.hxx>
43 
44 #include <limits>
45 
46 #define MULTILINE_ENTRY_DRAW_FLAGS ( DrawTextFlags::WordBreak | DrawTextFlags::MultiLine | DrawTextFlags::VCenter )
47 
48 using namespace ::com::sun::star;
49 
50 constexpr tools::Long gnBorder = 1;
51 
ImplInitDropDownButton(PushButton * pButton)52 void ImplInitDropDownButton( PushButton* pButton )
53 {
54     pButton->SetSymbol( SymbolType::SPIN_DOWN );
55 
56     if ( pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
57             && ! pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
58         pButton->SetBackground();
59 }
60 
ImplEntryList(vcl::Window * pWindow)61 ImplEntryList::ImplEntryList( vcl::Window* pWindow )
62 {
63     mpWindow = pWindow;
64     mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
65     mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
66     mnImages = 0;
67     mbCallSelectionChangedHdl = true;
68 
69     mnMRUCount = 0;
70     mnMaxMRUCount = 0;
71 }
72 
~ImplEntryList()73 ImplEntryList::~ImplEntryList()
74 {
75     Clear();
76 }
77 
Clear()78 void ImplEntryList::Clear()
79 {
80     mnImages = 0;
81     maEntries.clear();
82 }
83 
SelectEntry(sal_Int32 nPos,bool bSelect)84 void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
85 {
86     if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
87     {
88         std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+nPos;
89 
90         if ( ( (*iter)->mbIsSelected != bSelect ) &&
91            ( ( (*iter)->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE  ) )
92         {
93             (*iter)->mbIsSelected = bSelect;
94             if ( mbCallSelectionChangedHdl )
95                 maSelectionChangedHdl.Call( nPos );
96         }
97     }
98 }
99 
100 namespace
101 {
102     struct theSorter
103         : public rtl::StaticWithInit< comphelper::string::NaturalStringSorter, theSorter >
104     {
operator ()__anon2206311c0111::theSorter105         comphelper::string::NaturalStringSorter operator () ()
106         {
107             return comphelper::string::NaturalStringSorter(
108                 ::comphelper::getProcessComponentContext(),
109                 Application::GetSettings().GetLanguageTag().getLocale());
110         }
111     };
112 }
113 
114 namespace vcl
115 {
NaturalSortCompare(const OUString & rA,const OUString & rB)116     sal_Int32 NaturalSortCompare(const OUString &rA, const OUString &rB)
117     {
118         const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
119         return rSorter.compare(rA, rB);
120     }
121 }
122 
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry,bool bSort)123 sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
124 {
125     assert(nPos >= 0);
126     assert(maEntries.size() < LISTBOX_MAX_ENTRIES);
127 
128     if ( !!pNewEntry->maImage )
129         mnImages++;
130 
131     sal_Int32 insPos = 0;
132     const sal_Int32 nEntriesSize = static_cast<sal_Int32>(maEntries.size());
133 
134     if ( !bSort || maEntries.empty())
135     {
136         if (0 <= nPos && nPos < nEntriesSize)
137         {
138             insPos = nPos;
139             maEntries.insert( maEntries.begin() + nPos, std::unique_ptr<ImplEntryType>(pNewEntry) );
140         }
141         else
142         {
143             insPos = nEntriesSize;
144             maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
145         }
146     }
147     else
148     {
149         const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
150 
151         const OUString& rStr = pNewEntry->maStr;
152 
153         ImplEntryType* pTemp = GetEntry( nEntriesSize-1 );
154 
155         try
156         {
157             sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);
158 
159             // fast insert for sorted data
160             if ( nComp >= 0 )
161             {
162                 insPos = nEntriesSize;
163                 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
164             }
165             else
166             {
167                 pTemp = GetEntry( mnMRUCount );
168 
169                 nComp = rSorter.compare(rStr, pTemp->maStr);
170                 if ( nComp <= 0 )
171                 {
172                     insPos = 0;
173                     maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
174                 }
175                 else
176                 {
177                     sal_uLong nLow = mnMRUCount;
178                     sal_uLong nHigh = maEntries.size()-1;
179                     sal_Int32 nMid;
180 
181                     // binary search
182                     do
183                     {
184                         nMid = static_cast<sal_Int32>((nLow + nHigh) / 2);
185                         pTemp = GetEntry( nMid );
186 
187                         nComp = rSorter.compare(rStr, pTemp->maStr);
188 
189                         if ( nComp < 0 )
190                             nHigh = nMid-1;
191                         else
192                         {
193                             if ( nComp > 0 )
194                                 nLow = nMid + 1;
195                             else
196                                 break;
197                         }
198                     }
199                     while ( nLow <= nHigh );
200 
201                     if ( nComp >= 0 )
202                         nMid++;
203 
204                     insPos = nMid;
205                     maEntries.insert(maEntries.begin()+nMid, std::unique_ptr<ImplEntryType>(pNewEntry));
206                 }
207             }
208         }
209         catch (uno::RuntimeException& )
210         {
211             // XXX this is arguable, if the exception occurred because pNewEntry is
212             // garbage you wouldn't insert it. If the exception occurred because the
213             // Collator implementation is garbage then give the user a chance to see
214             // his stuff
215             insPos = 0;
216             maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
217         }
218 
219     }
220 
221     return insPos;
222 }
223 
RemoveEntry(sal_Int32 nPos)224 void ImplEntryList::RemoveEntry( sal_Int32 nPos )
225 {
226     if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
227     {
228         std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+ nPos;
229 
230         if ( !!(*iter)->maImage )
231             mnImages--;
232 
233         maEntries.erase(iter);
234     }
235 }
236 
FindEntry(std::u16string_view rString,bool bSearchMRUArea) const237 sal_Int32 ImplEntryList::FindEntry( std::u16string_view rString, bool bSearchMRUArea ) const
238 {
239     const sal_Int32 nEntries = static_cast<sal_Int32>(maEntries.size());
240     for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
241     {
242         OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n]->maStr ) );
243         if ( aComp == rString )
244             return n;
245     }
246     return LISTBOX_ENTRY_NOTFOUND;
247 }
248 
FindMatchingEntry(const OUString & rStr,sal_Int32 nStart,bool bLazy) const249 sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStart, bool bLazy ) const
250 {
251     sal_Int32  nPos = LISTBOX_ENTRY_NOTFOUND;
252     sal_Int32  nEntryCount = GetEntryCount();
253 
254     const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
255     for ( sal_Int32 n = nStart; n < nEntryCount; )
256     {
257         ImplEntryType* pImplEntry = GetEntry( n );
258         bool bMatch;
259         if ( bLazy  )
260         {
261             bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
262         }
263         else
264         {
265             bMatch = pImplEntry->maStr.startsWith(rStr);
266         }
267         if ( bMatch )
268         {
269             nPos = n;
270             break;
271         }
272 
273         n++;
274     }
275 
276     return nPos;
277 }
278 
GetAddedHeight(sal_Int32 i_nEndIndex,sal_Int32 i_nBeginIndex) const279 tools::Long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex ) const
280 {
281     tools::Long nHeight = 0;
282     sal_Int32 nStart = std::min(i_nEndIndex, i_nBeginIndex);
283     sal_Int32 nStop  = std::max(i_nEndIndex, i_nBeginIndex);
284     sal_Int32 nEntryCount = GetEntryCount();
285     if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
286     {
287         // sanity check
288         if( nStop > nEntryCount-1 )
289             nStop = nEntryCount-1;
290         if (nStart < 0)
291             nStart = 0;
292         else if( nStart > nEntryCount-1 )
293             nStart = nEntryCount-1;
294 
295         sal_Int32 nIndex = nStart;
296         while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
297         {
298             tools::Long nPosHeight = GetEntryPtr( nIndex )->getHeightWithMargin();
299             if (nHeight > ::std::numeric_limits<tools::Long>::max() - nPosHeight)
300             {
301                 SAL_WARN( "vcl", "ImplEntryList::GetAddedHeight: truncated");
302                 break;
303             }
304             nHeight += nPosHeight;
305             nIndex++;
306         }
307     }
308     else
309         nHeight = 0;
310     return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
311 }
312 
GetEntryHeight(sal_Int32 nPos) const313 tools::Long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
314 {
315     ImplEntryType* pImplEntry = GetEntry( nPos );
316     return pImplEntry ? pImplEntry->getHeightWithMargin() : 0;
317 }
318 
GetEntryText(sal_Int32 nPos) const319 OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
320 {
321     OUString aEntryText;
322     ImplEntryType* pImplEntry = GetEntry( nPos );
323     if ( pImplEntry )
324         aEntryText = pImplEntry->maStr;
325     return aEntryText;
326 }
327 
HasEntryImage(sal_Int32 nPos) const328 bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
329 {
330     bool bImage = false;
331     ImplEntryType* pImplEntry = GetEntry( nPos );
332     if ( pImplEntry )
333         bImage = !!pImplEntry->maImage;
334     return bImage;
335 }
336 
GetEntryImage(sal_Int32 nPos) const337 Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
338 {
339     Image aImage;
340     ImplEntryType* pImplEntry = GetEntry( nPos );
341     if ( pImplEntry )
342         aImage = pImplEntry->maImage;
343     return aImage;
344 }
345 
SetEntryData(sal_Int32 nPos,void * pNewData)346 void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
347 {
348     ImplEntryType* pImplEntry = GetEntry( nPos );
349     if ( pImplEntry )
350         pImplEntry->mpUserData = pNewData;
351 }
352 
GetEntryData(sal_Int32 nPos) const353 void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
354 {
355     ImplEntryType* pImplEntry = GetEntry( nPos );
356     return pImplEntry ? pImplEntry->mpUserData : nullptr;
357 }
358 
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)359 void ImplEntryList::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
360 {
361     ImplEntryType* pImplEntry = GetEntry( nPos );
362     if ( pImplEntry )
363         pImplEntry->mnFlags = nFlags;
364 }
365 
GetSelectedEntryCount() const366 sal_Int32 ImplEntryList::GetSelectedEntryCount() const
367 {
368     sal_Int32 nSelCount = 0;
369     for ( sal_Int32 n = GetEntryCount(); n; )
370     {
371         ImplEntryType* pImplEntry = GetEntry( --n );
372         if ( pImplEntry->mbIsSelected )
373             nSelCount++;
374     }
375     return nSelCount;
376 }
377 
GetSelectedEntry(sal_Int32 nIndex) const378 OUString ImplEntryList::GetSelectedEntry( sal_Int32 nIndex ) const
379 {
380     return GetEntryText( GetSelectedEntryPos( nIndex ) );
381 }
382 
GetSelectedEntryPos(sal_Int32 nIndex) const383 sal_Int32 ImplEntryList::GetSelectedEntryPos( sal_Int32 nIndex ) const
384 {
385     sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
386     sal_Int32 nSel = 0;
387     sal_Int32 nEntryCount = GetEntryCount();
388 
389     for ( sal_Int32 n = 0; n < nEntryCount; n++ )
390     {
391         ImplEntryType* pImplEntry = GetEntry( n );
392         if ( pImplEntry->mbIsSelected )
393         {
394             if ( nSel == nIndex )
395             {
396                 nSelEntryPos = n;
397                 break;
398             }
399             nSel++;
400         }
401     }
402 
403     return nSelEntryPos;
404 }
405 
IsEntryPosSelected(sal_Int32 nIndex) const406 bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
407 {
408     ImplEntryType* pImplEntry = GetEntry( nIndex );
409     return pImplEntry && pImplEntry->mbIsSelected;
410 }
411 
IsEntrySelectable(sal_Int32 nPos) const412 bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
413 {
414     ImplEntryType* pImplEntry = GetEntry( nPos );
415     return pImplEntry == nullptr || ((pImplEntry->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE);
416 }
417 
FindFirstSelectable(sal_Int32 nPos,bool bForward)418 sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ )
419 {
420     if( IsEntrySelectable( nPos ) )
421         return nPos;
422 
423     if( bForward )
424     {
425         for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
426         {
427             if( IsEntrySelectable( nPos ) )
428                 return nPos;
429         }
430     }
431     else
432     {
433         while( nPos )
434         {
435             nPos--;
436             if( IsEntrySelectable( nPos ) )
437                 return nPos;
438         }
439     }
440 
441     return LISTBOX_ENTRY_NOTFOUND;
442 }
443 
ImplListBoxWindow(vcl::Window * pParent,WinBits nWinStyle)444 ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
445     Control( pParent, 0 ),
446     maQuickSelectionEngine( *this )
447 {
448     mpEntryList.reset(new ImplEntryList( this ));
449 
450     mnTop               = 0;
451     mnLeft              = 0;
452     mnSelectModifier    = 0;
453     mnUserDrawEntry     = LISTBOX_ENTRY_NOTFOUND;
454     mbTrack             = false;
455     mbTravelSelect      = false;
456     mbTrackingSelect    = false;
457     mbSelectionChanged  = false;
458     mbMouseMoveSelect   = false;
459     mbMulti             = false;
460     mbGrabFocus         = false;
461     mbUserDrawEnabled   = false;
462     mbInUserDraw        = false;
463     mbReadOnly          = false;
464     mbHasFocusRect      = false;
465     mbRight             = ( nWinStyle & WB_RIGHT );
466     mbCenter            = ( nWinStyle & WB_CENTER );
467     mbSimpleMode        = ( nWinStyle & WB_SIMPLEMODE );
468     mbSort              = ( nWinStyle & WB_SORT );
469     mbIsDropdown        = ( nWinStyle & WB_DROPDOWN );
470     mbEdgeBlending      = false;
471 
472     mnCurrentPos            = LISTBOX_ENTRY_NOTFOUND;
473     mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
474 
475     GetOutDev()->SetLineColor();
476     SetTextFillColor();
477     SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
478 
479     ApplySettings(*GetOutDev());
480     ImplCalcMetrics();
481 }
482 
~ImplListBoxWindow()483 ImplListBoxWindow::~ImplListBoxWindow()
484 {
485     disposeOnce();
486 }
487 
dispose()488 void ImplListBoxWindow::dispose()
489 {
490     mpEntryList.reset();
491     Control::dispose();
492 }
493 
ApplySettings(vcl::RenderContext & rRenderContext)494 void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
495 {
496     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
497 
498     ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
499     ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
500 
501     if (IsControlBackground())
502         rRenderContext.SetBackground(GetControlBackground());
503     else
504         rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
505 }
506 
ImplCalcMetrics()507 void ImplListBoxWindow::ImplCalcMetrics()
508 {
509     mnMaxWidth      = 0;
510     mnMaxTxtWidth   = 0;
511     mnMaxImgWidth   = 0;
512     mnMaxImgTxtWidth= 0;
513     mnMaxImgHeight  = 0;
514 
515     mnTextHeight = static_cast<sal_uInt16>(GetTextHeight());
516     mnMaxTxtHeight = mnTextHeight + gnBorder;
517     mnMaxHeight = mnMaxTxtHeight;
518 
519     if ( maUserItemSize.Height() > mnMaxHeight )
520         mnMaxHeight = static_cast<sal_uInt16>(maUserItemSize.Height());
521     if ( maUserItemSize.Width() > mnMaxWidth )
522         mnMaxWidth= static_cast<sal_uInt16>(maUserItemSize.Width());
523 
524     for ( sal_Int32 n = mpEntryList->GetEntryCount(); n; )
525     {
526         ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
527         ImplUpdateEntryMetrics( *pEntry );
528     }
529 
530     if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
531     {
532         Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->getHeightWithMargin() );
533         maFocusRect.SetSize( aSz );
534     }
535 }
536 
Clear()537 void ImplListBoxWindow::Clear()
538 {
539     mpEntryList->Clear();
540 
541     mnMaxHeight     = mnMaxTxtHeight;
542     mnMaxWidth      = 0;
543     mnMaxTxtWidth   = 0;
544     mnMaxImgTxtWidth= 0;
545     mnMaxImgWidth   = 0;
546     mnMaxImgHeight  = 0;
547     mnTop           = 0;
548     mnLeft          = 0;
549     ImplClearLayoutData();
550 
551     mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
552     maQuickSelectionEngine.Reset();
553 
554     Invalidate();
555 }
556 
SetUserItemSize(const Size & rSz)557 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
558 {
559     ImplClearLayoutData();
560     maUserItemSize = rSz;
561     ImplCalcMetrics();
562 }
563 
564 namespace {
565 
566 struct ImplEntryMetrics
567 {
568     bool    bText;
569     bool    bImage;
570     tools::Long    nEntryWidth;
571     tools::Long    nEntryHeight;
572     tools::Long    nTextWidth;
573     tools::Long    nImgWidth;
574     tools::Long    nImgHeight;
575 };
576 
577 }
578 
getHeightWithMargin() const579 tools::Long ImplEntryType::getHeightWithMargin() const
580 {
581     return mnHeight + ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
582 }
583 
GetTextGlyphs(const OutputDevice * pOutputDevice)584 SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const OutputDevice* pOutputDevice)
585 {
586     if (maStrGlyphs.IsValid())
587         // Use pre-calculated result.
588         return &maStrGlyphs;
589 
590     std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
591         maStr, 0, maStr.getLength(), Point(0, 0), 0, nullptr, SalLayoutFlags::GlyphItemsOnly);
592     if (!pLayout)
593         return nullptr;
594 
595     // Remember the calculation result.
596     maStrGlyphs = pLayout->GetGlyphs();
597 
598     return &maStrGlyphs;
599 }
600 
ImplUpdateEntryMetrics(ImplEntryType & rEntry)601 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
602 {
603     ImplEntryMetrics aMetrics;
604     aMetrics.bText = !rEntry.maStr.isEmpty();
605     aMetrics.bImage = !!rEntry.maImage;
606     aMetrics.nEntryWidth = 0;
607     aMetrics.nEntryHeight = 0;
608     aMetrics.nTextWidth = 0;
609     aMetrics.nImgWidth = 0;
610     aMetrics.nImgHeight = 0;
611 
612     if ( aMetrics.bText )
613     {
614         if( rEntry.mnFlags & ListBoxEntryFlags::MultiLine )
615         {
616             // multiline case
617             Size aCurSize( PixelToLogic( GetSizePixel() ) );
618             // set the current size to a large number
619             // GetTextRect should shrink it to the actual size
620             aCurSize.setHeight( 0x7fffff );
621             tools::Rectangle aTextRect( Point( 0, 0 ), aCurSize );
622             aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
623             aMetrics.nTextWidth = aTextRect.GetWidth();
624             if( aMetrics.nTextWidth > mnMaxTxtWidth )
625                 mnMaxTxtWidth = aMetrics.nTextWidth;
626             aMetrics.nEntryWidth = mnMaxTxtWidth;
627             aMetrics.nEntryHeight = aTextRect.GetHeight() + gnBorder;
628         }
629         else
630         {
631             // normal single line case
632             const SalLayoutGlyphs* pGlyphs = rEntry.GetTextGlyphs(GetOutDev());
633             aMetrics.nTextWidth
634                 = static_cast<sal_uInt16>(GetTextWidth(rEntry.maStr, 0, -1, nullptr, pGlyphs));
635             if( aMetrics.nTextWidth > mnMaxTxtWidth )
636                 mnMaxTxtWidth = aMetrics.nTextWidth;
637             aMetrics.nEntryWidth = mnMaxTxtWidth;
638             aMetrics.nEntryHeight = mnTextHeight + gnBorder;
639         }
640     }
641     if ( aMetrics.bImage )
642     {
643         Size aImgSz = rEntry.maImage.GetSizePixel();
644         aMetrics.nImgWidth  = static_cast<sal_uInt16>(CalcZoom( aImgSz.Width() ));
645         aMetrics.nImgHeight = static_cast<sal_uInt16>(CalcZoom( aImgSz.Height() ));
646 
647         if( aMetrics.nImgWidth > mnMaxImgWidth )
648             mnMaxImgWidth = aMetrics.nImgWidth;
649         if( aMetrics.nImgHeight > mnMaxImgHeight )
650             mnMaxImgHeight = aMetrics.nImgHeight;
651 
652         mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
653         aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
654 
655     }
656 
657     bool bIsUserDrawEnabled = IsUserDrawEnabled();
658     if (bIsUserDrawEnabled || aMetrics.bImage)
659     {
660         aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
661         if (!bIsUserDrawEnabled && aMetrics.bText)
662             aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
663         aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
664                                      aMetrics.nEntryHeight );
665     }
666 
667     if (!aMetrics.bText && !aMetrics.bImage && !bIsUserDrawEnabled)
668     {
669         // entries which have no (aka an empty) text, and no image,
670         // and are not user-drawn, should be shown nonetheless
671         aMetrics.nEntryHeight = mnTextHeight + gnBorder;
672     }
673 
674     if ( aMetrics.nEntryWidth > mnMaxWidth )
675         mnMaxWidth = aMetrics.nEntryWidth;
676     if ( aMetrics.nEntryHeight > mnMaxHeight )
677         mnMaxHeight = aMetrics.nEntryHeight;
678 
679     rEntry.mnHeight = aMetrics.nEntryHeight;
680 }
681 
ImplCallSelect()682 void ImplListBoxWindow::ImplCallSelect()
683 {
684     if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
685     {
686         // Insert the selected entry as MRU, if not already first MRU
687         sal_Int32 nSelected = GetEntryList()->GetSelectedEntryPos( 0 );
688         sal_Int32 nMRUCount = GetEntryList()->GetMRUCount();
689         OUString aSelected = GetEntryList()->GetEntryText( nSelected );
690         sal_Int32 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, true );
691         if ( nFirstMatchingEntryPos || !nMRUCount )
692         {
693             bool bSelectNewEntry = false;
694             if ( nFirstMatchingEntryPos < nMRUCount )
695             {
696                 RemoveEntry( nFirstMatchingEntryPos );
697                 nMRUCount--;
698                 if ( nFirstMatchingEntryPos == nSelected )
699                     bSelectNewEntry = true;
700             }
701             else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
702             {
703                 RemoveEntry( nMRUCount - 1 );
704                 nMRUCount--;
705             }
706 
707             ImplClearLayoutData();
708 
709             ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
710             pNewEntry->mbIsSelected = bSelectNewEntry;
711             GetEntryList()->InsertEntry( 0, pNewEntry, false );
712             ImplUpdateEntryMetrics( *pNewEntry );
713             GetEntryList()->SetMRUCount( ++nMRUCount );
714             SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
715             maMRUChangedHdl.Call( nullptr );
716         }
717     }
718 
719     maSelectHdl.Call( nullptr );
720     mbSelectionChanged = false;
721 }
722 
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry,bool bSort)723 sal_Int32 ImplListBoxWindow::InsertEntry(sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort)
724 {
725     assert(nPos >= 0);
726     assert(mpEntryList->GetEntryCount() < LISTBOX_MAX_ENTRIES);
727 
728     ImplClearLayoutData();
729     sal_Int32 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, bSort );
730 
731     if( GetStyle() & WB_WORDBREAK )
732         pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;
733 
734     ImplUpdateEntryMetrics( *pNewEntry );
735     return nNewPos;
736 }
737 
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry)738 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
739 {
740     return InsertEntry(nPos, pNewEntry, mbSort);
741 }
742 
RemoveEntry(sal_Int32 nPos)743 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
744 {
745     ImplClearLayoutData();
746     mpEntryList->RemoveEntry( nPos );
747     if( mnCurrentPos >= mpEntryList->GetEntryCount() )
748         mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
749     ImplCalcMetrics();
750 }
751 
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)752 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
753 {
754     mpEntryList->SetEntryFlags( nPos, nFlags );
755     ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
756     if( pEntry )
757         ImplUpdateEntryMetrics( *pEntry );
758 }
759 
ImplShowFocusRect()760 void ImplListBoxWindow::ImplShowFocusRect()
761 {
762     if ( mbHasFocusRect )
763         HideFocus();
764     ShowFocus( maFocusRect );
765     mbHasFocusRect = true;
766 }
767 
ImplHideFocusRect()768 void ImplListBoxWindow::ImplHideFocusRect()
769 {
770     if ( mbHasFocusRect )
771     {
772         HideFocus();
773         mbHasFocusRect = false;
774     }
775 }
776 
GetEntryPosForPoint(const Point & rPoint) const777 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
778 {
779     tools::Long nY = gnBorder;
780 
781     sal_Int32 nSelect = mnTop;
782     const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
783     while (pEntry)
784     {
785         tools::Long nEntryHeight = pEntry->getHeightWithMargin();
786         if (rPoint.Y() <= nEntryHeight + nY)
787             break;
788         nY += nEntryHeight;
789         pEntry = mpEntryList->GetEntryPtr( ++nSelect );
790     }
791     if( pEntry == nullptr )
792         nSelect = LISTBOX_ENTRY_NOTFOUND;
793 
794     return nSelect;
795 }
796 
IsVisible(sal_Int32 i_nEntry) const797 bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
798 {
799     bool bRet = false;
800 
801     if( i_nEntry >= mnTop )
802     {
803         if( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
804             PixelToLogic( GetSizePixel() ).Height() )
805         {
806             bRet = true;
807         }
808     }
809 
810     return bRet;
811 }
812 
GetEntryHeightWithMargin() const813 tools::Long ImplListBoxWindow::GetEntryHeightWithMargin() const
814 {
815     tools::Long nMargin = ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
816     return mnMaxHeight + nMargin;
817 }
818 
GetLastVisibleEntry() const819 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
820 {
821     sal_Int32 nPos = mnTop;
822     tools::Long nWindowHeight = GetSizePixel().Height();
823     sal_Int32 nCount = mpEntryList->GetEntryCount();
824     tools::Long nDiff;
825     for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
826         nPos++;
827 
828     if( nDiff > nWindowHeight && nPos > mnTop )
829         nPos--;
830 
831     if( nPos >= nCount )
832         nPos = nCount-1;
833 
834     return nPos;
835 }
836 
MouseButtonDown(const MouseEvent & rMEvt)837 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
838 {
839     mbMouseMoveSelect = false;  // only till the first MouseButtonDown
840     maQuickSelectionEngine.Reset();
841 
842     if ( !IsReadOnly() )
843     {
844         if( rMEvt.GetClicks() == 1 )
845         {
846             sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
847             if( nSelect != LISTBOX_ENTRY_NOTFOUND )
848             {
849                 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
850                     mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
851                 else
852                     mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
853 
854                 mnCurrentPos = nSelect;
855                 mbTrackingSelect = true;
856                 bool bCurPosChange = (mnCurrentPos != nSelect);
857                 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
858                 mbTrackingSelect = false;
859                 if ( mbGrabFocus )
860                     GrabFocus();
861 
862                 StartTracking( StartTrackingFlags::ScrollRepeat );
863             }
864         }
865         if( rMEvt.GetClicks() == 2 )
866         {
867             maDoubleClickHdl.Call( this );
868         }
869     }
870     else // if ( mbGrabFocus )
871     {
872         GrabFocus();
873     }
874 }
875 
MouseMove(const MouseEvent & rMEvt)876 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
877 {
878     if (rMEvt.IsLeaveWindow() || mbMulti || !IsMouseMoveSelect() || !mpEntryList->GetEntryCount())
879         return;
880 
881     tools::Rectangle aRect( Point(), GetOutputSizePixel() );
882     if( !aRect.IsInside( rMEvt.GetPosPixel() ) )
883         return;
884 
885     if ( IsMouseMoveSelect() )
886     {
887         sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
888         if( nSelect == LISTBOX_ENTRY_NOTFOUND )
889             nSelect = mpEntryList->GetEntryCount() - 1;
890         nSelect = std::min( nSelect, GetLastVisibleEntry() );
891         nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
892         // Select only visible Entries with MouseMove, otherwise Tracking...
893         if ( IsVisible( nSelect ) &&
894             mpEntryList->IsEntrySelectable( nSelect ) &&
895             ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() || ( nSelect != GetEntryList()->GetSelectedEntryPos( 0 ) ) ) )
896         {
897             mbTrackingSelect = true;
898             if ( SelectEntries( nSelect, LET_TRACKING ) )
899             {
900                 // When list box selection change by mouse move, notify
901                 // VclEventId::ListboxSelect vcl event.
902                 maListItemSelectHdl.Call(nullptr);
903             }
904             mbTrackingSelect = false;
905         }
906     }
907 
908     // if the DD button was pressed and someone moved into the ListBox
909     // with the mouse button pressed...
910     if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
911     {
912         if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
913             mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
914         else
915             mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
916 
917         StartTracking( StartTrackingFlags::ScrollRepeat );
918     }
919 }
920 
DeselectAll()921 void ImplListBoxWindow::DeselectAll()
922 {
923     while ( GetEntryList()->GetSelectedEntryCount() )
924     {
925         sal_Int32 nS = GetEntryList()->GetSelectedEntryPos( 0 );
926         SelectEntry( nS, false );
927     }
928 }
929 
SelectEntry(sal_Int32 nPos,bool bSelect)930 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
931 {
932     if( (mpEntryList->IsEntryPosSelected( nPos ) == bSelect) || !mpEntryList->IsEntrySelectable( nPos ) )
933         return;
934 
935     ImplHideFocusRect();
936     if( bSelect )
937     {
938         if( !mbMulti )
939         {
940             // deselect the selected entry
941             sal_Int32 nDeselect = GetEntryList()->GetSelectedEntryPos( 0 );
942             if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
943             {
944                 //SelectEntryPos( nDeselect, false );
945                 GetEntryList()->SelectEntry( nDeselect, false );
946                 if (IsUpdateMode() && IsReallyVisible())
947                     Invalidate();
948             }
949         }
950         mpEntryList->SelectEntry( nPos, true );
951         mnCurrentPos = nPos;
952         if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
953         {
954             Invalidate();
955             if ( !IsVisible( nPos ) )
956             {
957                 ImplClearLayoutData();
958                 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
959                 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
960                 {
961                     Resize();
962                     ShowProminentEntry( nPos );
963                 }
964                 else
965                 {
966                     ShowProminentEntry( nPos );
967                 }
968             }
969         }
970     }
971     else
972     {
973         mpEntryList->SelectEntry( nPos, false );
974         Invalidate();
975     }
976     mbSelectionChanged = true;
977 }
978 
SelectEntries(sal_Int32 nSelect,LB_EVENT_TYPE eLET,bool bShift,bool bCtrl,bool bSelectPosChange)979 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
980 {
981     bool bSelectionChanged = false;
982 
983     if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
984     {
985         bool bFocusChanged = false;
986 
987         // here (Single-ListBox) only one entry can be deselected
988         if( !mbMulti )
989         {
990             sal_Int32 nDeselect = mpEntryList->GetSelectedEntryPos( 0 );
991             if( nSelect != nDeselect )
992             {
993                 SelectEntry( nSelect, true );
994                 mpEntryList->SetLastSelected( nSelect );
995                 bFocusChanged = true;
996                 bSelectionChanged = true;
997             }
998         }
999         // MultiListBox without Modifier
1000         else if( mbSimpleMode && !bCtrl && !bShift )
1001         {
1002             sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1003             for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1004             {
1005                 bool bSelect = nPos == nSelect;
1006                 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1007                 {
1008                     SelectEntry( nPos, bSelect );
1009                     bFocusChanged = true;
1010                     bSelectionChanged = true;
1011                 }
1012             }
1013             mpEntryList->SetLastSelected( nSelect );
1014             mpEntryList->SetSelectionAnchor( nSelect );
1015         }
1016         // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1017         else if( ( !mbSimpleMode /* && !bShift */ ) || ( mbSimpleMode && ( bCtrl || bShift ) ) )
1018         {
1019             // Space for selection change
1020             if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1021             {
1022                 bool bSelect = !mpEntryList->IsEntryPosSelected( nSelect );
1023                 SelectEntry( nSelect, bSelect );
1024                 mpEntryList->SetLastSelected( nSelect );
1025                 mpEntryList->SetSelectionAnchor( nSelect );
1026                 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1027                     mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1028                 bFocusChanged = true;
1029                 bSelectionChanged = true;
1030             }
1031             else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1032                      ( bShift && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1033             {
1034                 mnCurrentPos = nSelect;
1035                 bFocusChanged = true;
1036 
1037                 sal_Int32 nAnchor = mpEntryList->GetSelectionAnchor();
1038                 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && mpEntryList->GetSelectedEntryCount() )
1039                 {
1040                     nAnchor = mpEntryList->GetSelectedEntryPos( mpEntryList->GetSelectedEntryCount() - 1 );
1041                 }
1042                 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1043                 {
1044                     // All entries from Anchor to nSelect have to be selected
1045                     sal_Int32 nStart = std::min( nSelect, nAnchor );
1046                     sal_Int32 nEnd = std::max( nSelect, nAnchor );
1047                     for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1048                     {
1049                         if ( !mpEntryList->IsEntryPosSelected( n ) )
1050                         {
1051                             SelectEntry( n, true );
1052                             bSelectionChanged = true;
1053                         }
1054                     }
1055 
1056                     // if appropriate some more has to be deselected...
1057                     sal_Int32 nLast = mpEntryList->GetLastSelected();
1058                     if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1059                     {
1060                         if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1061                         {
1062                             for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1063                             {
1064                                 if ( mpEntryList->IsEntryPosSelected( n ) )
1065                                 {
1066                                     SelectEntry( n, false );
1067                                     bSelectionChanged = true;
1068                                 }
1069                             }
1070                         }
1071                         else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1072                         {
1073                             for ( sal_Int32 n = nLast; n < nSelect; n++ )
1074                             {
1075                                 if ( mpEntryList->IsEntryPosSelected( n ) )
1076                                 {
1077                                     SelectEntry( n, false );
1078                                     bSelectionChanged = true;
1079                                 }
1080                             }
1081                         }
1082                     }
1083                     mpEntryList->SetLastSelected( nSelect );
1084                 }
1085             }
1086             else if( eLET != LET_TRACKING )
1087             {
1088                 ImplHideFocusRect();
1089                 Invalidate();
1090                 bFocusChanged = true;
1091             }
1092         }
1093         else if( bShift )
1094         {
1095             bFocusChanged = true;
1096         }
1097 
1098         if( bSelectionChanged )
1099             mbSelectionChanged = true;
1100 
1101         if( bFocusChanged )
1102         {
1103             tools::Long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop );
1104             maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1105             Size aSz( maFocusRect.GetWidth(),
1106                       mpEntryList->GetEntryHeight( nSelect ) );
1107             maFocusRect.SetSize( aSz );
1108             if( HasFocus() )
1109                 ImplShowFocusRect();
1110             if (bSelectPosChange)
1111             {
1112                 maFocusHdl.Call(nSelect);
1113             }
1114         }
1115         ImplClearLayoutData();
1116     }
1117     return bSelectionChanged;
1118 }
1119 
Tracking(const TrackingEvent & rTEvt)1120 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1121 {
1122     tools::Rectangle aRect( Point(), GetOutputSizePixel() );
1123     bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1124 
1125     if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1126     {
1127         if ( bInside && !rTEvt.IsTrackingCanceled() )
1128         {
1129             mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1130             ImplCallSelect();
1131         }
1132         else
1133         {
1134             maCancelHdl.Call( nullptr );
1135             if ( !mbMulti )
1136             {
1137                 mbTrackingSelect = true;
1138                 SelectEntry( mnTrackingSaveSelection, true );
1139                 mbTrackingSelect = false;
1140                 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1141                 {
1142                     tools::Long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1143                     maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1144                     Size aSz( maFocusRect.GetWidth(),
1145                               mpEntryList->GetEntryHeight( mnCurrentPos ) );
1146                     maFocusRect.SetSize( aSz );
1147                     ImplShowFocusRect();
1148                 }
1149             }
1150         }
1151 
1152         mbTrack = false;
1153     }
1154     else
1155     {
1156         bool bTrackOrQuickClick = mbTrack;
1157         if( !mbTrack )
1158         {
1159             if ( bInside )
1160             {
1161                 mbTrack = true;
1162             }
1163 
1164             // this case only happens, if the mouse button is pressed very briefly
1165             if( rTEvt.IsTrackingEnded() && mbTrack )
1166             {
1167                 bTrackOrQuickClick = true;
1168                 mbTrack = false;
1169             }
1170         }
1171 
1172         if( bTrackOrQuickClick )
1173         {
1174             MouseEvent aMEvt = rTEvt.GetMouseEvent();
1175             Point aPt( aMEvt.GetPosPixel() );
1176             bool bShift = aMEvt.IsShift();
1177             bool bCtrl  = aMEvt.IsMod1();
1178 
1179             sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1180             if( aPt.Y() < 0 )
1181             {
1182                 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1183                 {
1184                     nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1185                     if( nSelect < mnTop )
1186                         SetTopEntry( mnTop-1 );
1187                 }
1188             }
1189             else if( aPt.Y() > GetOutputSizePixel().Height() )
1190             {
1191                 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1192                 {
1193                     nSelect = std::min(  static_cast<sal_Int32>(mnCurrentPos+1), static_cast<sal_Int32>(mpEntryList->GetEntryCount()-1) );
1194                     if( nSelect >= GetLastVisibleEntry() )
1195                         SetTopEntry( mnTop+1 );
1196                 }
1197             }
1198             else
1199             {
1200                 nSelect = static_cast<sal_Int32>( ( aPt.Y() + gnBorder ) / mnMaxHeight ) + mnTop;
1201                 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1202                 nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
1203             }
1204 
1205             if ( bInside )
1206             {
1207                 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() )
1208                 {
1209                     mbTrackingSelect = true;
1210                     SelectEntries(nSelect, LET_TRACKING, bShift, bCtrl);
1211                     mbTrackingSelect = false;
1212                 }
1213             }
1214             else
1215             {
1216                 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
1217                 {
1218                     mbTrackingSelect = true;
1219                     SelectEntry( GetEntryList()->GetSelectedEntryPos( 0 ), false );
1220                     mbTrackingSelect = false;
1221                 }
1222             }
1223             mnCurrentPos = nSelect;
1224             if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1225             {
1226                 ImplHideFocusRect();
1227             }
1228             else
1229             {
1230                 tools::Long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1231                 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1232                 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1233                 maFocusRect.SetSize( aSz );
1234                 ImplShowFocusRect();
1235             }
1236         }
1237     }
1238 }
1239 
KeyInput(const KeyEvent & rKEvt)1240 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1241 {
1242     if( !ProcessKeyInput( rKEvt ) )
1243         Control::KeyInput( rKEvt );
1244 }
1245 
ProcessKeyInput(const KeyEvent & rKEvt)1246 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1247 {
1248     // entry to be selected
1249     sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1250     LB_EVENT_TYPE eLET = LET_KEYMOVE;
1251 
1252     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1253 
1254     bool bShift = aKeyCode.IsShift();
1255     bool bCtrl  = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1256     bool bMod2 = aKeyCode.IsMod2();
1257     bool bDone = false;
1258     bool bHandleKey = false;
1259 
1260     switch( aKeyCode.GetCode() )
1261     {
1262         case KEY_UP:
1263         {
1264             if ( IsReadOnly() )
1265             {
1266                 if ( GetTopEntry() )
1267                     SetTopEntry( GetTopEntry()-1 );
1268             }
1269             else if ( !bMod2 )
1270             {
1271                 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1272                 {
1273                     nSelect = mpEntryList->FindFirstSelectable( 0 );
1274                 }
1275                 else if ( mnCurrentPos )
1276                 {
1277                     // search first selectable above the current position
1278                     nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1279                 }
1280 
1281                 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1282                     SetTopEntry( mnTop-1 );
1283 
1284                 bDone = true;
1285             }
1286             maQuickSelectionEngine.Reset();
1287         }
1288         break;
1289 
1290         case KEY_DOWN:
1291         {
1292             if ( IsReadOnly() )
1293             {
1294                 SetTopEntry( GetTopEntry()+1 );
1295             }
1296             else if ( !bMod2 )
1297             {
1298                 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1299                 {
1300                     nSelect = mpEntryList->FindFirstSelectable( 0 );
1301                 }
1302                 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1303                 {
1304                     // search first selectable below the current position
1305                     nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1 );
1306                 }
1307 
1308                 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1309                     SetTopEntry( mnTop+1 );
1310 
1311                 bDone = true;
1312             }
1313             maQuickSelectionEngine.Reset();
1314         }
1315         break;
1316 
1317         case KEY_PAGEUP:
1318         {
1319             if ( IsReadOnly() )
1320             {
1321                 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1322                 SetTopEntry( ( mnTop > nCurVis ) ?
1323                                 (mnTop-nCurVis) : 0 );
1324             }
1325             else if ( !bCtrl && !bMod2 )
1326             {
1327                 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1328                 {
1329                     nSelect = mpEntryList->FindFirstSelectable( 0 );
1330                 }
1331                 else if ( mnCurrentPos )
1332                 {
1333                     if( mnCurrentPos == mnTop )
1334                     {
1335                         sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1336                         SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1337                     }
1338 
1339                     // find first selectable starting from mnTop looking forward
1340                     nSelect = mpEntryList->FindFirstSelectable( mnTop );
1341                 }
1342                 bDone = true;
1343             }
1344             maQuickSelectionEngine.Reset();
1345         }
1346         break;
1347 
1348         case KEY_PAGEDOWN:
1349         {
1350             if ( IsReadOnly() )
1351             {
1352                 SetTopEntry( GetLastVisibleEntry() );
1353             }
1354             else if ( !bCtrl && !bMod2 )
1355             {
1356                 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1357                 {
1358                     nSelect = mpEntryList->FindFirstSelectable( 0 );
1359                 }
1360                 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1361                 {
1362                     sal_Int32 nCount = mpEntryList->GetEntryCount();
1363                     sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1364                     sal_Int32 nTmp = std::min( nCurVis, nCount );
1365                     nTmp += mnTop - 1;
1366                     if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1367                     {
1368                         tools::Long nTmp2 = std::min( static_cast<tools::Long>(nCount-nCurVis), static_cast<tools::Long>(static_cast<tools::Long>(mnTop)+static_cast<tools::Long>(nCurVis)-1) );
1369                         nTmp2 = std::max( tools::Long(0) , nTmp2 );
1370                         nTmp = static_cast<sal_Int32>(nTmp2+(nCurVis-1) );
1371                         SetTopEntry( static_cast<sal_Int32>(nTmp2) );
1372                     }
1373                     // find first selectable starting from nTmp looking backwards
1374                     nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1375                 }
1376                 bDone = true;
1377             }
1378             maQuickSelectionEngine.Reset();
1379         }
1380         break;
1381 
1382         case KEY_HOME:
1383         {
1384             if ( IsReadOnly() )
1385             {
1386                 SetTopEntry( 0 );
1387             }
1388             else if ( !bCtrl && !bMod2 &&  mnCurrentPos )
1389             {
1390                 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
1391                 if( mnTop != 0 )
1392                     SetTopEntry( 0 );
1393 
1394                 bDone = true;
1395             }
1396             maQuickSelectionEngine.Reset();
1397         }
1398         break;
1399 
1400         case KEY_END:
1401         {
1402             if ( IsReadOnly() )
1403             {
1404                 SetTopEntry( 0xFFFF );
1405             }
1406             else if ( !bCtrl && !bMod2 )
1407             {
1408                 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1409                 {
1410                     nSelect = mpEntryList->FindFirstSelectable( 0 );
1411                 }
1412                 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1413                 {
1414                     sal_Int32 nCount = mpEntryList->GetEntryCount();
1415                     nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1416                     sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1417                     if( nCount > nCurVis )
1418                         SetTopEntry( nCount - nCurVis );
1419                 }
1420                 bDone = true;
1421             }
1422             maQuickSelectionEngine.Reset();
1423         }
1424         break;
1425 
1426         case KEY_LEFT:
1427         {
1428             if ( !bCtrl && !bMod2 )
1429             {
1430                 ScrollHorz( -HORZ_SCROLL );
1431                 bDone = true;
1432             }
1433             maQuickSelectionEngine.Reset();
1434         }
1435         break;
1436 
1437         case KEY_RIGHT:
1438         {
1439             if ( !bCtrl && !bMod2 )
1440             {
1441                 ScrollHorz( HORZ_SCROLL );
1442                 bDone = true;
1443             }
1444             maQuickSelectionEngine.Reset();
1445         }
1446         break;
1447 
1448         case KEY_RETURN:
1449         {
1450             if ( !bMod2 && !IsReadOnly() )
1451             {
1452                 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1453                 ImplCallSelect();
1454                 bDone = false;  // do not catch RETURN
1455             }
1456             maQuickSelectionEngine.Reset();
1457         }
1458         break;
1459 
1460         case KEY_SPACE:
1461         {
1462             if ( !bMod2 && !IsReadOnly() )
1463             {
1464                 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) ) )
1465                 {
1466                     nSelect = mnCurrentPos;
1467                     eLET = LET_KEYSPACE;
1468                 }
1469                 bDone = true;
1470             }
1471             bHandleKey = true;
1472         }
1473         break;
1474 
1475         case KEY_A:
1476         {
1477             if( bCtrl && mbMulti )
1478             {
1479                 // paint only once
1480                 bool bUpdates = IsUpdateMode();
1481                 SetUpdateMode( false );
1482 
1483                 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1484                 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1485                     SelectEntry( i, true );
1486 
1487                 // tdf#97066 - Update selected items
1488                 ImplCallSelect();
1489 
1490                 // restore update mode
1491                 SetUpdateMode( bUpdates );
1492                 Invalidate();
1493 
1494                 maQuickSelectionEngine.Reset();
1495 
1496                 bDone = true;
1497             }
1498             else
1499             {
1500                 bHandleKey = true;
1501             }
1502         }
1503         break;
1504 
1505         default:
1506             bHandleKey = true;
1507             break;
1508     }
1509     if (bHandleKey && !IsReadOnly())
1510     {
1511         bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1512     }
1513 
1514     if  (   ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1515         &&  (   ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1516             ||  ( eLET == LET_KEYSPACE )
1517             )
1518         )
1519     {
1520         SAL_WARN_IF( mpEntryList->IsEntryPosSelected( nSelect ) && !mbMulti, "vcl", "ImplListBox: Selecting same Entry" );
1521         sal_Int32 nCount = mpEntryList->GetEntryCount();
1522         if (nSelect >= nCount)
1523             nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1524         bool bCurPosChange = (mnCurrentPos != nSelect);
1525         mnCurrentPos = nSelect;
1526         if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1527         {
1528             // tdf#129043 Correctly deliver events when changing values with arrow keys in combobox
1529             if (mbIsDropdown && IsReallyVisible())
1530                 mbTravelSelect = true;
1531             mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1532             ImplCallSelect();
1533             mbTravelSelect = false;
1534         }
1535     }
1536 
1537     return bDone;
1538 }
1539 
1540 namespace
1541 {
lcl_getEntry(const ImplEntryList & _rList,sal_Int32 _nPos,OUString & _out_entryText)1542     vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1543     {
1544         OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1545         sal_Int32 nEntryCount( _rList.GetEntryCount() );
1546         if ( _nPos >= nEntryCount )
1547             _nPos = 0;
1548         _out_entryText = _rList.GetEntryText( _nPos );
1549 
1550         // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1551         // => normalize
1552         return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + 1 );
1553     }
1554 
lcl_getEntryPos(vcl::StringEntryIdentifier _entry)1555     sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
1556     {
1557         // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1558         return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1559     }
1560 }
1561 
CurrentEntry(OUString & _out_entryText) const1562 vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1563 {
1564     return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1565 }
1566 
NextEntry(vcl::StringEntryIdentifier _currentEntry,OUString & _out_entryText) const1567 vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1568 {
1569     sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1570     return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1571 }
1572 
SelectEntry(vcl::StringEntryIdentifier _entry)1573 void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
1574 {
1575     sal_Int32 nSelect = lcl_getEntryPos( _entry );
1576     if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1577     {
1578         // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1579         // to select the given entry by typing its starting letters. No need to act.
1580         return;
1581     }
1582 
1583     // normalize
1584     OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1585     sal_Int32 nCount = mpEntryList->GetEntryCount();
1586     if (nSelect >= nCount)
1587         nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1588 
1589     // make visible
1590     ShowProminentEntry( nSelect );
1591 
1592     // actually select
1593     mnCurrentPos = nSelect;
1594     if ( SelectEntries( nSelect, LET_KEYMOVE ) )
1595     {
1596         mbTravelSelect = true;
1597         mnSelectModifier = 0;
1598         ImplCallSelect();
1599         mbTravelSelect = false;
1600     }
1601 }
1602 
ImplPaint(vcl::RenderContext & rRenderContext,sal_Int32 nPos)1603 void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
1604 {
1605     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1606 
1607     const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1608     if (!pEntry)
1609         return;
1610 
1611     tools::Long nWidth = GetOutputSizePixel().Width();
1612     tools::Long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1613     tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));
1614 
1615     bool bSelected = mpEntryList->IsEntryPosSelected(nPos);
1616     if (bSelected)
1617     {
1618         rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor());
1619         rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
1620         rRenderContext.SetLineColor();
1621         rRenderContext.DrawRect(aRect);
1622     }
1623     else
1624     {
1625         ApplySettings(rRenderContext);
1626         if (!IsEnabled())
1627             rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
1628     }
1629     rRenderContext.SetTextFillColor();
1630 
1631     if (IsUserDrawEnabled())
1632     {
1633         mbInUserDraw = true;
1634         mnUserDrawEntry = nPos;
1635         aRect.AdjustLeft( -mnLeft );
1636         if (nPos < GetEntryList()->GetMRUCount())
1637             nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos));
1638         nPos = nPos - GetEntryList()->GetMRUCount();
1639 
1640         UserDrawEvent aUDEvt(&rRenderContext, aRect, nPos, bSelected);
1641         maUserDrawHdl.Call( &aUDEvt );
1642         mbInUserDraw = false;
1643     }
1644     else
1645     {
1646         DrawEntry(rRenderContext, nPos, true, true);
1647     }
1648 }
1649 
DrawEntry(vcl::RenderContext & rRenderContext,sal_Int32 nPos,bool bDrawImage,bool bDrawText)1650 void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText)
1651 {
1652     const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(nPos);
1653     if (!pEntry)
1654         return;
1655 
1656     tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1657 
1658     // when changing this function don't forget to adjust ImplWin::DrawEntry()
1659 
1660     if (mbInUserDraw)
1661         nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1662 
1663     tools::Long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1664 
1665     if (bDrawImage && mpEntryList->HasImages())
1666     {
1667         Image aImage = mpEntryList->GetEntryImage(nPos);
1668         if (!!aImage)
1669         {
1670             Size aImgSz = aImage.GetSizePixel();
1671             Point aPtImg(gnBorder - mnLeft, nY + ((nEntryHeight - aImgSz.Height()) / 2));
1672 
1673             if (!IsZoom())
1674             {
1675                 rRenderContext.DrawImage(aPtImg, aImage);
1676             }
1677             else
1678             {
1679                 aImgSz.setWidth( CalcZoom(aImgSz.Width()) );
1680                 aImgSz.setHeight( CalcZoom(aImgSz.Height()) );
1681                 rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
1682             }
1683 
1684             const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1685             const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1686 
1687             if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1688             {
1689                 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1690                 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1691                 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1692                 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1693 
1694                 if (!aBlendFrame.IsEmpty())
1695                 {
1696                     rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
1697                 }
1698             }
1699         }
1700     }
1701 
1702     if (bDrawText)
1703     {
1704         OUString aStr(mpEntryList->GetEntryText(nPos));
1705         if (!aStr.isEmpty())
1706         {
1707             tools::Long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * gnBorder);
1708             // a multiline entry should only be as wide as the window
1709             if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1710                 nMaxWidth = GetOutputSizePixel().Width() - 2 * gnBorder;
1711 
1712             tools::Rectangle aTextRect(Point(gnBorder - mnLeft, nY),
1713                                 Size(nMaxWidth, nEntryHeight));
1714 
1715             if (mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled())
1716             {
1717                 tools::Long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
1718                 aTextRect.AdjustLeft(nImageWidth + IMG_TXT_DISTANCE );
1719             }
1720 
1721             DrawTextFlags nDrawStyle = ImplGetTextStyle();
1722             if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1723                 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1724             if (pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled)
1725                 nDrawStyle |= DrawTextFlags::Disable;
1726 
1727             rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
1728         }
1729     }
1730 
1731     if ( !maSeparators.empty() && ( isSeparator(nPos) || isSeparator(nPos-1) ) )
1732     {
1733         Color aOldLineColor(rRenderContext.GetLineColor());
1734         rRenderContext.SetLineColor((GetBackground() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
1735         Point aStartPos(0, nY);
1736         if (isSeparator(nPos))
1737             aStartPos.AdjustY(pEntry->getHeightWithMargin() - 1 );
1738         Point aEndPos(aStartPos);
1739         aEndPos.setX( GetOutputSizePixel().Width() );
1740         rRenderContext.DrawLine(aStartPos, aEndPos);
1741         rRenderContext.SetLineColor(aOldLineColor);
1742     }
1743 }
1744 
FillLayoutData() const1745 void ImplListBoxWindow::FillLayoutData() const
1746 {
1747     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1748     const_cast<ImplListBoxWindow*>(this)->Invalidate(tools::Rectangle(Point(0, 0), GetOutDev()->GetOutputSize()));
1749 }
1750 
ImplDoPaint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1751 void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1752 {
1753     sal_Int32 nCount = mpEntryList->GetEntryCount();
1754 
1755     bool bShowFocusRect = mbHasFocusRect;
1756     if (mbHasFocusRect)
1757         ImplHideFocusRect();
1758 
1759     tools::Long nY = 0; // + gnBorder;
1760     tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1761 
1762     for (sal_Int32 i = mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
1763     {
1764         const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(i);
1765         tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1766         if (nY + nEntryHeight >= rRect.Top() &&
1767             nY <= rRect.Bottom() + mnMaxHeight)
1768         {
1769             ImplPaint(rRenderContext, i);
1770         }
1771         nY += nEntryHeight;
1772     }
1773 
1774     tools::Long nHeightDiff = mpEntryList->GetAddedHeight(mnCurrentPos, mnTop);
1775     maFocusRect.SetPos(Point(0, nHeightDiff));
1776     Size aSz(maFocusRect.GetWidth(), mpEntryList->GetEntryHeight(mnCurrentPos));
1777     maFocusRect.SetSize(aSz);
1778     if (HasFocus() && bShowFocusRect)
1779         ImplShowFocusRect();
1780 }
1781 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1782 void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1783 {
1784     if (SupportsDoubleBuffering())
1785     {
1786         // This widget is explicitly double-buffered, so avoid partial paints.
1787         tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
1788         ImplDoPaint(rRenderContext, aRect);
1789     }
1790     else
1791         ImplDoPaint(rRenderContext, rRect);
1792 }
1793 
GetDisplayLineCount() const1794 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1795 {
1796     // FIXME: ListBoxEntryFlags::MultiLine
1797 
1798     const sal_Int32 nCount = mpEntryList->GetEntryCount()-mnTop;
1799     tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1800     sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1801     if( nEntries > nCount )
1802         nEntries = static_cast<sal_uInt16>(nCount);
1803 
1804     return nEntries;
1805 }
1806 
Resize()1807 void ImplListBoxWindow::Resize()
1808 {
1809     Control::Resize();
1810 
1811     bool bShowFocusRect = mbHasFocusRect;
1812     if ( bShowFocusRect )
1813         ImplHideFocusRect();
1814 
1815     if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1816     {
1817         Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1818         maFocusRect.SetSize( aSz );
1819     }
1820 
1821     if ( bShowFocusRect )
1822         ImplShowFocusRect();
1823 
1824     ImplClearLayoutData();
1825 }
1826 
GetFocus()1827 void ImplListBoxWindow::GetFocus()
1828 {
1829     sal_Int32 nPos = mnCurrentPos;
1830     if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1831         nPos = 0;
1832     tools::Long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop );
1833     maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1834     Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
1835     maFocusRect.SetSize( aSz );
1836     ImplShowFocusRect();
1837     Control::GetFocus();
1838 }
1839 
LoseFocus()1840 void ImplListBoxWindow::LoseFocus()
1841 {
1842     ImplHideFocusRect();
1843     Control::LoseFocus();
1844 }
1845 
SetTopEntry(sal_Int32 nTop)1846 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1847 {
1848     if( mpEntryList->GetEntryCount() == 0 )
1849         return;
1850 
1851     tools::Long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1852 
1853     sal_Int32 nLastEntry = mpEntryList->GetEntryCount()-1;
1854     if( nTop > nLastEntry )
1855         nTop = nLastEntry;
1856     const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
1857     while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->getHeightWithMargin() <= nWHeight )
1858         nTop--;
1859 
1860     if ( nTop == mnTop )
1861         return;
1862 
1863     ImplClearLayoutData();
1864     tools::Long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop );
1865     PaintImmediately();
1866     ImplHideFocusRect();
1867     mnTop = nTop;
1868     Scroll( 0, nDiff );
1869     PaintImmediately();
1870     if( HasFocus() )
1871         ImplShowFocusRect();
1872     maScrollHdl.Call( this );
1873 }
1874 
ShowProminentEntry(sal_Int32 nEntryPos)1875 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
1876 {
1877     SetTopEntry( nEntryPos );
1878 }
1879 
SetLeftIndent(tools::Long n)1880 void ImplListBoxWindow::SetLeftIndent( tools::Long n )
1881 {
1882     ScrollHorz( n - mnLeft );
1883 }
1884 
ScrollHorz(tools::Long n)1885 void ImplListBoxWindow::ScrollHorz( tools::Long n )
1886 {
1887     tools::Long nDiff = 0;
1888     if ( n > 0 )
1889     {
1890         tools::Long nWidth = GetOutputSizePixel().Width();
1891         if( ( mnMaxWidth - mnLeft + n ) > nWidth )
1892             nDiff = n;
1893     }
1894     else if ( n < 0 )
1895     {
1896         if( mnLeft )
1897         {
1898             tools::Long nAbs = -n;
1899             nDiff = - std::min( mnLeft, nAbs );
1900         }
1901     }
1902 
1903     if ( nDiff )
1904     {
1905         ImplClearLayoutData();
1906         mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
1907         PaintImmediately();
1908         ImplHideFocusRect();
1909         Scroll( -nDiff, 0 );
1910         PaintImmediately();
1911         if( HasFocus() )
1912             ImplShowFocusRect();
1913         maScrollHdl.Call( this );
1914     }
1915 }
1916 
SetSeparatorPos(sal_Int32 n)1917 void ImplListBoxWindow::SetSeparatorPos( sal_Int32 n )
1918 {
1919     maSeparators.clear();
1920 
1921     if ( n != LISTBOX_ENTRY_NOTFOUND )
1922     {
1923         maSeparators.insert( n );
1924     }
1925 }
1926 
GetSeparatorPos() const1927 sal_Int32 ImplListBoxWindow::GetSeparatorPos() const
1928 {
1929     if (!maSeparators.empty())
1930         return *(maSeparators.begin());
1931     else
1932         return LISTBOX_ENTRY_NOTFOUND;
1933 }
1934 
isSeparator(const sal_Int32 & n) const1935 bool ImplListBoxWindow::isSeparator( const sal_Int32 &n) const
1936 {
1937     return maSeparators.find(n) != maSeparators.end();
1938 }
1939 
CalcSize(sal_Int32 nMaxLines) const1940 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
1941 {
1942     // FIXME: ListBoxEntryFlags::MultiLine
1943 
1944     Size aSz;
1945     aSz.setHeight(nMaxLines * GetEntryHeightWithMargin());
1946     aSz.setWidth( mnMaxWidth + 2*gnBorder );
1947     return aSz;
1948 }
1949 
GetBoundingRectangle(sal_Int32 nItem) const1950 tools::Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
1951 {
1952     const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
1953     Size aSz( GetSizePixel().Width(), pEntry ? pEntry->getHeightWithMargin() : GetEntryHeightWithMargin() );
1954     tools::Long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList()->GetMRUCount()*GetEntryHeightWithMargin();
1955     tools::Rectangle aRect( Point( 0, nY ), aSz );
1956     return aRect;
1957 }
1958 
StateChanged(StateChangedType nType)1959 void ImplListBoxWindow::StateChanged( StateChangedType nType )
1960 {
1961     Control::StateChanged( nType );
1962 
1963     if ( nType == StateChangedType::Zoom )
1964     {
1965         ApplySettings(*GetOutDev());
1966         ImplCalcMetrics();
1967         Invalidate();
1968     }
1969     else if ( nType == StateChangedType::UpdateMode )
1970     {
1971         if ( IsUpdateMode() && IsReallyVisible() )
1972             Invalidate();
1973     }
1974     else if ( nType == StateChangedType::ControlFont )
1975     {
1976         ApplySettings(*GetOutDev());
1977         ImplCalcMetrics();
1978         Invalidate();
1979     }
1980     else if ( nType == StateChangedType::ControlForeground )
1981     {
1982         ApplySettings(*GetOutDev());
1983         Invalidate();
1984     }
1985     else if ( nType == StateChangedType::ControlBackground )
1986     {
1987         ApplySettings(*GetOutDev());
1988         Invalidate();
1989     }
1990     else if( nType == StateChangedType::Enable )
1991     {
1992         Invalidate();
1993     }
1994 
1995     ImplClearLayoutData();
1996 }
1997 
DataChanged(const DataChangedEvent & rDCEvt)1998 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
1999 {
2000     Control::DataChanged( rDCEvt );
2001 
2002     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2003          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2004          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2005           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2006     {
2007         ImplClearLayoutData();
2008         ApplySettings(*GetOutDev());
2009         ImplCalcMetrics();
2010         Invalidate();
2011     }
2012 }
2013 
ImplGetTextStyle() const2014 DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
2015 {
2016     DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2017 
2018     if (mpEntryList->HasImages())
2019         nTextStyle |= DrawTextFlags::Left;
2020     else if (mbCenter)
2021         nTextStyle |= DrawTextFlags::Center;
2022     else if (mbRight)
2023         nTextStyle |= DrawTextFlags::Right;
2024     else
2025         nTextStyle |= DrawTextFlags::Left;
2026 
2027     return nTextStyle;
2028 }
2029 
ImplListBox(vcl::Window * pParent,WinBits nWinStyle)2030 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2031     Control( pParent, nWinStyle ),
2032     maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
2033 {
2034     // for native widget rendering we must be able to detect this window type
2035     SetType( WindowType::LISTBOXWINDOW );
2036 
2037     mpVScrollBar    = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
2038     mpHScrollBar    = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
2039     mpScrollBarBox  = VclPtr<ScrollBarBox>::Create( this );
2040 
2041     Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2042     mpVScrollBar->SetScrollHdl( aLink );
2043     mpHScrollBar->SetScrollHdl( aLink );
2044 
2045     mbVScroll       = false;
2046     mbHScroll       = false;
2047     mbAutoHScroll   = ( nWinStyle & WB_AUTOHSCROLL );
2048     mbEdgeBlending  = false;
2049 
2050     maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2051     maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2052     maLBWindow->SetEdgeBlending(GetEdgeBlending());
2053     maLBWindow->Show();
2054 }
2055 
~ImplListBox()2056 ImplListBox::~ImplListBox()
2057 {
2058     disposeOnce();
2059 }
2060 
dispose()2061 void ImplListBox::dispose()
2062 {
2063     mpHScrollBar.disposeAndClear();
2064     mpVScrollBar.disposeAndClear();
2065     mpScrollBarBox.disposeAndClear();
2066     maLBWindow.disposeAndClear();
2067     Control::dispose();
2068 }
2069 
Clear()2070 void ImplListBox::Clear()
2071 {
2072     maLBWindow->Clear();
2073     if ( GetEntryList()->GetMRUCount() )
2074     {
2075         maLBWindow->GetEntryList()->SetMRUCount( 0 );
2076         maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2077     }
2078     mpVScrollBar->SetThumbPos( 0 );
2079     mpHScrollBar->SetThumbPos( 0 );
2080     CompatStateChanged( StateChangedType::Data );
2081 }
2082 
InsertEntry(sal_Int32 nPos,const OUString & rStr)2083 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2084 {
2085     ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2086     sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2087     CompatStateChanged( StateChangedType::Data );
2088     return nNewPos;
2089 }
2090 
InsertEntry(sal_Int32 nPos,const OUString & rStr,const Image & rImage)2091 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2092 {
2093     ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2094     sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2095     CompatStateChanged( StateChangedType::Data );
2096     return nNewPos;
2097 }
2098 
RemoveEntry(sal_Int32 nPos)2099 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2100 {
2101     maLBWindow->RemoveEntry( nPos );
2102     CompatStateChanged( StateChangedType::Data );
2103 }
2104 
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)2105 void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
2106 {
2107     maLBWindow->SetEntryFlags( nPos, nFlags );
2108 }
2109 
SelectEntry(sal_Int32 nPos,bool bSelect)2110 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2111 {
2112     maLBWindow->SelectEntry( nPos, bSelect );
2113 }
2114 
SetNoSelection()2115 void ImplListBox::SetNoSelection()
2116 {
2117     maLBWindow->DeselectAll();
2118 }
2119 
GetFocus()2120 void ImplListBox::GetFocus()
2121 {
2122     if (maLBWindow)
2123         maLBWindow->GrabFocus();
2124     else
2125         Control::GetFocus();
2126 }
2127 
Resize()2128 void ImplListBox::Resize()
2129 {
2130     Control::Resize();
2131     ImplResizeControls();
2132     ImplCheckScrollBars();
2133 }
2134 
IMPL_LINK_NOARG(ImplListBox,MRUChanged,LinkParamNone *,void)2135 IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
2136 {
2137     CompatStateChanged( StateChangedType::Data );
2138 }
2139 
IMPL_LINK_NOARG(ImplListBox,LBWindowScrolled,ImplListBoxWindow *,void)2140 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
2141 {
2142     tools::Long nSet = GetTopEntry();
2143     if( nSet > mpVScrollBar->GetRangeMax() )
2144         mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2145     mpVScrollBar->SetThumbPos( GetTopEntry() );
2146 
2147     mpHScrollBar->SetThumbPos( GetLeftIndent() );
2148 
2149     maScrollHdl.Call( this );
2150 }
2151 
IMPL_LINK(ImplListBox,ScrollBarHdl,ScrollBar *,pSB,void)2152 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
2153 {
2154     sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
2155     if( pSB == mpVScrollBar )
2156         SetTopEntry( nPos );
2157     else if( pSB == mpHScrollBar )
2158         SetLeftIndent( nPos );
2159     if( GetParent() )
2160         GetParent()->Invalidate( InvalidateFlags::Update );
2161 }
2162 
ImplCheckScrollBars()2163 void ImplListBox::ImplCheckScrollBars()
2164 {
2165     bool bArrange = false;
2166 
2167     Size aOutSz = GetOutputSizePixel();
2168     sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2169     sal_uInt16 nMaxVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2170 
2171     // vertical ScrollBar
2172     if( nEntries > nMaxVisEntries )
2173     {
2174         if( !mbVScroll )
2175             bArrange = true;
2176         mbVScroll = true;
2177 
2178         // check of the scrolled-out region
2179         if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2180             GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2181             ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2182         else
2183             SetTopEntry( GetTopEntry() );   // MaxTop is being checked...
2184     }
2185     else
2186     {
2187         if( mbVScroll )
2188             bArrange = true;
2189         mbVScroll = false;
2190         SetTopEntry( 0 );
2191     }
2192 
2193     // horizontal ScrollBar
2194     if( mbAutoHScroll )
2195     {
2196         tools::Long nWidth = static_cast<sal_uInt16>(aOutSz.Width());
2197         if ( mbVScroll )
2198             nWidth -= mpVScrollBar->GetSizePixel().Width();
2199 
2200         tools::Long nMaxWidth = GetMaxEntryWidth();
2201         if( nWidth < nMaxWidth )
2202         {
2203             if( !mbHScroll )
2204                 bArrange = true;
2205             mbHScroll = true;
2206 
2207             if ( !mbVScroll )   // maybe we do need one now
2208             {
2209                 nMaxVisEntries = static_cast<sal_uInt16>( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeightWithMargin() );
2210                 if( nEntries > nMaxVisEntries )
2211                 {
2212                     bArrange = true;
2213                     mbVScroll = true;
2214 
2215                     // check of the scrolled-out region
2216                     if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2217                         GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2218                         ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2219                     else
2220                         SetTopEntry( GetTopEntry() );   // MaxTop is being checked...
2221                 }
2222             }
2223 
2224             // check of the scrolled-out region
2225             sal_uInt16 nMaxLI = static_cast<sal_uInt16>(nMaxWidth - nWidth);
2226             if ( nMaxLI < GetLeftIndent() )
2227                 SetLeftIndent( nMaxLI );
2228         }
2229         else
2230         {
2231             if( mbHScroll )
2232                 bArrange = true;
2233             mbHScroll = false;
2234             SetLeftIndent( 0 );
2235         }
2236     }
2237 
2238     if( bArrange )
2239         ImplResizeControls();
2240 
2241     ImplInitScrollBars();
2242 }
2243 
ImplInitScrollBars()2244 void ImplListBox::ImplInitScrollBars()
2245 {
2246     Size aOutSz = maLBWindow->GetOutputSizePixel();
2247 
2248     if ( mbVScroll )
2249     {
2250         sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2251         sal_uInt16 nVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2252         mpVScrollBar->SetRangeMax( nEntries );
2253         mpVScrollBar->SetVisibleSize( nVisEntries );
2254         mpVScrollBar->SetPageSize( nVisEntries - 1 );
2255     }
2256 
2257     if ( mbHScroll )
2258     {
2259         mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2260         mpHScrollBar->SetVisibleSize( static_cast<sal_uInt16>(aOutSz.Width()) );
2261         mpHScrollBar->SetLineSize( HORZ_SCROLL );
2262         mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2263     }
2264 }
2265 
ImplResizeControls()2266 void ImplListBox::ImplResizeControls()
2267 {
2268     // Here we only position the Controls; if the Scrollbars are to be
2269     // visible is already determined in ImplCheckScrollBars
2270 
2271     Size aOutSz = GetOutputSizePixel();
2272     tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2273     nSBWidth = CalcZoom( nSBWidth );
2274 
2275     Size aInnerSz( aOutSz );
2276     if ( mbVScroll )
2277         aInnerSz.AdjustWidth( -nSBWidth );
2278     if ( mbHScroll )
2279         aInnerSz.AdjustHeight( -nSBWidth );
2280 
2281     Point aWinPos( 0, 0 );
2282     maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
2283 
2284     // ScrollBarBox
2285     if( mbVScroll && mbHScroll )
2286     {
2287         Point aBoxPos( aInnerSz.Width(), aInnerSz.Height() );
2288         mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2289         mpScrollBarBox->Show();
2290     }
2291     else
2292     {
2293         mpScrollBarBox->Hide();
2294     }
2295 
2296     // vertical ScrollBar
2297     if( mbVScroll )
2298     {
2299         // Scrollbar on left or right side?
2300         Point aVPos( aOutSz.Width() - nSBWidth, 0 );
2301         mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2302         mpVScrollBar->Show();
2303     }
2304     else
2305     {
2306         mpVScrollBar->Hide();
2307         // #107254# Don't reset top entry after resize, but check for max top entry
2308         SetTopEntry( GetTopEntry() );
2309     }
2310 
2311     // horizontal ScrollBar
2312     if( mbHScroll )
2313     {
2314         Point aHPos( 0, aOutSz.Height() - nSBWidth );
2315         mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2316         mpHScrollBar->Show();
2317     }
2318     else
2319     {
2320         mpHScrollBar->Hide();
2321         SetLeftIndent( 0 );
2322     }
2323 }
2324 
StateChanged(StateChangedType nType)2325 void ImplListBox::StateChanged( StateChangedType nType )
2326 {
2327     if ( nType == StateChangedType::InitShow )
2328     {
2329         ImplCheckScrollBars();
2330     }
2331     else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
2332     {
2333         bool bUpdate = IsUpdateMode();
2334         maLBWindow->SetUpdateMode( bUpdate );
2335         if ( bUpdate && IsReallyVisible() )
2336             ImplCheckScrollBars();
2337     }
2338     else if( nType == StateChangedType::Enable )
2339     {
2340         mpHScrollBar->Enable( IsEnabled() );
2341         mpVScrollBar->Enable( IsEnabled() );
2342         mpScrollBarBox->Enable( IsEnabled() );
2343         maLBWindow->Enable( IsEnabled() );
2344 
2345         Invalidate();
2346     }
2347     else if ( nType == StateChangedType::Zoom )
2348     {
2349         maLBWindow->SetZoom( GetZoom() );
2350         Resize();
2351     }
2352     else if ( nType == StateChangedType::ControlFont )
2353     {
2354         maLBWindow->SetControlFont( GetControlFont() );
2355     }
2356     else if ( nType == StateChangedType::ControlForeground )
2357     {
2358         maLBWindow->SetControlForeground( GetControlForeground() );
2359     }
2360     else if ( nType == StateChangedType::ControlBackground )
2361     {
2362         maLBWindow->SetControlBackground( GetControlBackground() );
2363     }
2364     else if( nType == StateChangedType::Mirroring )
2365     {
2366         maLBWindow->EnableRTL( IsRTLEnabled() );
2367         mpHScrollBar->EnableRTL( IsRTLEnabled() );
2368         mpVScrollBar->EnableRTL( IsRTLEnabled() );
2369         ImplResizeControls();
2370     }
2371 
2372     Control::StateChanged( nType );
2373 }
2374 
EventNotify(NotifyEvent & rNEvt)2375 bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
2376 {
2377     bool bDone = false;
2378     if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
2379     {
2380         const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2381         if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2382         {
2383             const CommandWheelData* pData = rCEvt.GetWheelData();
2384             if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2385             {
2386                 bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2387             }
2388         }
2389         else if (rCEvt.GetCommand() == CommandEventId::Gesture)
2390         {
2391             bDone = HandleScrollCommand(rCEvt, mpHScrollBar, mpVScrollBar);
2392         }
2393     }
2394 
2395     return bDone || Window::EventNotify( rNEvt );
2396 }
2397 
GetDisplayBackground() const2398 const Wallpaper& ImplListBox::GetDisplayBackground() const
2399 {
2400     return maLBWindow->GetDisplayBackground();
2401 }
2402 
HandleWheelAsCursorTravel(const CommandEvent & rCEvt,Control & rControl)2403 bool ImplListBox::HandleWheelAsCursorTravel(const CommandEvent& rCEvt, Control& rControl)
2404 {
2405     bool bDone = false;
2406     if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2407     {
2408         const CommandWheelData* pData = rCEvt.GetWheelData();
2409         if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2410         {
2411             if (!rControl.HasChildPathFocus())
2412                 rControl.GrabFocus();
2413             sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2414             KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2415             bDone = ProcessKeyInput( aKeyEvent );
2416         }
2417     }
2418     return bDone;
2419 }
2420 
SetMRUEntries(const OUString & rEntries,sal_Unicode cSep)2421 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2422 {
2423     bool bChanges = GetEntryList()->GetMRUCount() != 0;
2424 
2425     // Remove old MRU entries
2426     for ( sal_Int32 n = GetEntryList()->GetMRUCount();n; )
2427         maLBWindow->RemoveEntry( --n );
2428 
2429     sal_Int32 nMRUCount = 0;
2430     sal_Int32 nIndex = 0;
2431     do
2432     {
2433         OUString aEntry = rEntries.getToken( 0, cSep, nIndex );
2434         // Accept only existing entries
2435         if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2436         {
2437             ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2438             maLBWindow->InsertEntry(nMRUCount++, pNewEntry, false);
2439             bChanges = true;
2440         }
2441     }
2442     while ( nIndex >= 0 );
2443 
2444     if ( bChanges )
2445     {
2446         maLBWindow->GetEntryList()->SetMRUCount( nMRUCount );
2447         SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2448         CompatStateChanged( StateChangedType::Data );
2449     }
2450 }
2451 
GetMRUEntries(sal_Unicode cSep) const2452 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2453 {
2454     OUStringBuffer aEntries;
2455     for ( sal_Int32 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2456     {
2457         aEntries.append(GetEntryList()->GetEntryText( n ));
2458         if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2459             aEntries.append(cSep);
2460     }
2461     return aEntries.makeStringAndClear();
2462 }
2463 
SetEdgeBlending(bool bNew)2464 void ImplListBox::SetEdgeBlending(bool bNew)
2465 {
2466     if(mbEdgeBlending != bNew)
2467     {
2468         mbEdgeBlending = bNew;
2469         maLBWindow->SetEdgeBlending(GetEdgeBlending());
2470     }
2471 }
2472 
ImplWin(vcl::Window * pParent,WinBits nWinStyle)2473 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2474     Control ( pParent, nWinStyle )
2475 {
2476     if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2477             && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2478         SetBackground();
2479     else
2480         SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2481 
2482     ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2483 
2484     mbEdgeBlending = false;
2485     mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2486 }
2487 
MouseButtonDown(const MouseEvent &)2488 void ImplWin::MouseButtonDown( const MouseEvent& )
2489 {
2490     if( IsEnabled() )
2491     {
2492         maMBDownHdl.Call(this);
2493     }
2494 }
2495 
FillLayoutData() const2496 void ImplWin::FillLayoutData() const
2497 {
2498     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2499     ImplWin* pThis = const_cast<ImplWin*>(this);
2500     pThis->ImplDraw(*pThis->GetOutDev(), true);
2501 }
2502 
PreNotify(NotifyEvent & rNEvt)2503 bool ImplWin::PreNotify( NotifyEvent& rNEvt )
2504 {
2505     if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
2506     {
2507         const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
2508         if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
2509         {
2510             // trigger redraw as mouse over state has changed
2511             if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2512             && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2513             {
2514                 GetParent()->GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
2515             }
2516         }
2517     }
2518 
2519     return Control::PreNotify(rNEvt);
2520 }
2521 
ImplDraw(vcl::RenderContext & rRenderContext,bool bLayout)2522 void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
2523 {
2524     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2525 
2526     if (!bLayout)
2527     {
2528         bool bNativeOK = false;
2529         bool bHasFocus = HasFocus();
2530         bool bIsEnabled = IsEnabled();
2531 
2532         ControlState nState = ControlState::ENABLED;
2533         if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2534             && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
2535         {
2536             // Repaint the (focused) area similarly to
2537             // ImplSmallBorderWindowView::DrawWindow() in
2538             // vcl/source/window/brdwin.cxx
2539             vcl::Window *pWin = GetParent();
2540 
2541             ImplControlValue aControlValue;
2542             bIsEnabled &= pWin->IsEnabled();
2543             if ( !bIsEnabled )
2544                 nState &= ~ControlState::ENABLED;
2545             bHasFocus |= pWin->HasFocus();
2546             if ( bHasFocus )
2547                 nState |= ControlState::FOCUSED;
2548 
2549             // The listbox is painted over the entire control including the
2550             // border, but ImplWin does not contain the border => correction
2551             // needed.
2552             sal_Int32 nLeft, nTop, nRight, nBottom;
2553             pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2554             Point aPoint( -nLeft, -nTop );
2555             tools::Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2556 
2557             bool bMouseOver = false;
2558             vcl::Window *pChild = pWin->GetWindow( GetWindowType::FirstChild );
2559             while( pChild )
2560             {
2561                 bMouseOver = pChild->IsMouseOver();
2562                 if (bMouseOver)
2563                     break;
2564                 pChild = pChild->GetWindow( GetWindowType::Next );
2565             }
2566             if( bMouseOver )
2567                 nState |= ControlState::ROLLOVER;
2568 
2569             // if parent has no border, then nobody has drawn the background
2570             // since no border window exists. so draw it here.
2571             WinBits nParentStyle = pWin->GetStyle();
2572             if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2573             {
2574                 tools::Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2575                 pWin->GetOutDev()->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
2576                                          nState, aControlValue, OUString() );
2577             }
2578 
2579             bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
2580                                                          nState, aControlValue, OUString());
2581         }
2582 
2583         if (bIsEnabled)
2584         {
2585             if (bHasFocus && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2586             {
2587                 if ( !ImplGetSVData()->maNWFData.mbNoFocusRects )
2588                 {
2589                     rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
2590                     rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
2591                 }
2592                 else
2593                 {
2594                     rRenderContext.SetLineColor();
2595                     rRenderContext.SetFillColor();
2596                     rRenderContext.SetTextColor( rStyleSettings.GetFieldTextColor() );
2597                 }
2598                 rRenderContext.DrawRect( maFocusRect );
2599             }
2600             else
2601             {
2602                 Color aColor;
2603                 if (IsControlForeground())
2604                     aColor = GetControlForeground();
2605                 else if (ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2606                 {
2607                     if( bNativeOK && (nState & ControlState::ROLLOVER) )
2608                         aColor = rStyleSettings.GetButtonRolloverTextColor();
2609                     else
2610                         aColor = rStyleSettings.GetButtonTextColor();
2611                 }
2612                 else
2613                 {
2614                     if( bNativeOK && (nState & ControlState::ROLLOVER) )
2615                         aColor = rStyleSettings.GetFieldRolloverTextColor();
2616                     else
2617                         aColor = rStyleSettings.GetFieldTextColor();
2618                 }
2619                 rRenderContext.SetTextColor(aColor);
2620                 if (!bNativeOK)
2621                     rRenderContext.Erase(maFocusRect);
2622             }
2623         }
2624         else // Disabled
2625         {
2626             rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
2627             if (!bNativeOK)
2628                 rRenderContext.Erase(maFocusRect);
2629         }
2630     }
2631 
2632     DrawEntry(rRenderContext, bLayout);
2633 }
2634 
ApplySettings(vcl::RenderContext & rRenderContext)2635 void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
2636 {
2637     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2638 
2639     ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
2640     ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
2641 
2642     if (IsControlBackground())
2643         rRenderContext.SetBackground(GetControlBackground());
2644     else
2645         rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
2646 }
2647 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)2648 void ImplWin::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2649 {
2650     ImplDraw(rRenderContext);
2651 }
2652 
DrawEntry(vcl::RenderContext & rRenderContext,bool bLayout)2653 void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bLayout)
2654 {
2655     tools::Long nBorder = 1;
2656     Size aOutSz(GetOutputSizePixel());
2657 
2658     bool bImage = !!maImage;
2659     if (bImage && !bLayout)
2660     {
2661         DrawImageFlags nStyle = DrawImageFlags::NONE;
2662         Size aImgSz = maImage.GetSizePixel();
2663         Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2664         const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2665 
2666         // check for HC mode
2667         Image *pImage = &maImage;
2668 
2669         if ( !IsZoom() )
2670         {
2671             rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
2672         }
2673         else
2674         {
2675             aImgSz.setWidth( CalcZoom( aImgSz.Width() ) );
2676             aImgSz.setHeight( CalcZoom( aImgSz.Height() ) );
2677             rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2678         }
2679 
2680         const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2681 
2682         if(nEdgeBlendingPercent)
2683         {
2684             const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2685             const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2686             const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2687             const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2688 
2689             if(!aBlendFrame.IsEmpty())
2690             {
2691                 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
2692             }
2693         }
2694     }
2695 
2696     if( !maString.isEmpty() )
2697     {
2698         DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2699 
2700         if ( bImage && !bLayout )
2701             nTextStyle |= DrawTextFlags::Left;
2702         else if ( GetStyle() & WB_CENTER )
2703             nTextStyle |= DrawTextFlags::Center;
2704         else if ( GetStyle() & WB_RIGHT )
2705             nTextStyle |= DrawTextFlags::Right;
2706         else
2707             nTextStyle |= DrawTextFlags::Left;
2708 
2709         tools::Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2710 
2711         if ( bImage )
2712         {
2713             aTextRect.AdjustLeft(maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE );
2714         }
2715 
2716         std::vector< tools::Rectangle >* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : nullptr;
2717         OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : nullptr;
2718         rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2719     }
2720 
2721     if( HasFocus() && !bLayout )
2722         ShowFocus( maFocusRect );
2723 }
2724 
Resize()2725 void ImplWin::Resize()
2726 {
2727     Control::Resize();
2728     maFocusRect.SetSize( GetOutputSizePixel() );
2729     Invalidate();
2730 }
2731 
GetFocus()2732 void ImplWin::GetFocus()
2733 {
2734     ShowFocus( maFocusRect );
2735     if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2736         IsNativeWidgetEnabled() &&
2737         IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2738     {
2739         vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2740         if( ! pWin )
2741             pWin = GetParent();
2742         pWin->Invalidate();
2743     }
2744     else
2745         Invalidate();
2746     Control::GetFocus();
2747 }
2748 
LoseFocus()2749 void ImplWin::LoseFocus()
2750 {
2751     HideFocus();
2752     if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2753         IsNativeWidgetEnabled() &&
2754         IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2755     {
2756         vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2757         if( ! pWin )
2758             pWin = GetParent();
2759         pWin->Invalidate();
2760     }
2761     else
2762         Invalidate();
2763     Control::LoseFocus();
2764 }
2765 
ShowFocus(const tools::Rectangle & rRect)2766 void ImplWin::ShowFocus(const tools::Rectangle& rRect)
2767 {
2768     if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
2769     {
2770         ImplControlValue aControlValue;
2771 
2772         vcl::Window *pWin = GetParent();
2773         tools::Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
2774         pWin->GetOutDev()->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
2775                                 ControlState::FOCUSED, aControlValue, OUString());
2776     }
2777     Control::ShowFocus(rRect);
2778 }
2779 
ImplBtn(vcl::Window * pParent,WinBits nWinStyle)2780 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2781     PushButton(  pParent, nWinStyle )
2782 {
2783 }
2784 
MouseButtonDown(const MouseEvent &)2785 void ImplBtn::MouseButtonDown( const MouseEvent& )
2786 {
2787     if( IsEnabled() )
2788         maMBDownHdl.Call(this);
2789 }
2790 
ImplListBoxFloatingWindow(vcl::Window * pParent)2791 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2792     FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW )    // no drop shadow for list boxes
2793 {
2794     // for native widget rendering we must be able to detect this window type
2795     SetType( WindowType::LISTBOXWINDOW );
2796 
2797     mpImplLB = nullptr;
2798     mnDDLineCount = 0;
2799     mbAutoWidth = false;
2800 
2801     mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2802 
2803     vcl::Window * pBorderWindow = ImplGetBorderWindow();
2804     if( pBorderWindow )
2805     {
2806         SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2807         pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2808     }
2809     else
2810     {
2811         SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2812     }
2813 
2814 }
2815 
~ImplListBoxFloatingWindow()2816 ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
2817 {
2818     disposeOnce();
2819 }
2820 
dispose()2821 void ImplListBoxFloatingWindow::dispose()
2822 {
2823     mpImplLB.clear();
2824     FloatingWindow::dispose();
2825 }
2826 
2827 
PreNotify(NotifyEvent & rNEvt)2828 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2829 {
2830     if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2831     {
2832         if( !GetParent()->HasChildPathFocus( true ) )
2833             EndPopupMode();
2834     }
2835 
2836     return FloatingWindow::PreNotify( rNEvt );
2837 }
2838 
setPosSizePixel(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,PosSizeFlags nFlags)2839 void ImplListBoxFloatingWindow::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
2840 {
2841     FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2842 
2843     // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2844     // after a call to Resize(), we adjust its position if necessary
2845     if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
2846     {
2847         Point aPos = GetParent()->GetPosPixel();
2848         aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2849 
2850         if ( nFlags & PosSizeFlags::X )
2851             aPos.setX( nX );
2852 
2853         if ( nFlags & PosSizeFlags::Y )
2854             aPos.setY( nY );
2855 
2856         sal_uInt16 nIndex;
2857         SetPosPixel( ImplCalcPos( this, tools::Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
2858     }
2859 
2860 //  if( !IsReallyVisible() )
2861     {
2862         // The ImplListBox does not get a Resize() as not visible.
2863         // But the windows must get a Resize(), so that the number of
2864         // visible entries is correct for PgUp/PgDown.
2865         // The number also cannot be calculated by List/Combobox, as for
2866         // this the presence of the vertical Scrollbar has to be known.
2867         mpImplLB->SetSizePixel( GetOutputSizePixel() );
2868         static_cast<vcl::Window*>(mpImplLB)->Resize();
2869         static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
2870     }
2871 }
2872 
Resize()2873 void ImplListBoxFloatingWindow::Resize()
2874 {
2875     mpImplLB->GetMainWindow()->ImplClearLayoutData();
2876     FloatingWindow::Resize();
2877 }
2878 
CalcFloatSize()2879 Size ImplListBoxFloatingWindow::CalcFloatSize()
2880 {
2881     Size aFloatSz( maPrefSz );
2882 
2883     sal_Int32 nLeft, nTop, nRight, nBottom;
2884     GetBorder( nLeft, nTop, nRight, nBottom );
2885 
2886     sal_Int32 nLines = mpImplLB->GetEntryList()->GetEntryCount();
2887     if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
2888         nLines = mnDDLineCount;
2889 
2890     Size aSz = mpImplLB->CalcSize( nLines );
2891     tools::Long nMaxHeight = aSz.Height() + nTop + nBottom;
2892 
2893     if ( mnDDLineCount )
2894         aFloatSz.setHeight( nMaxHeight );
2895 
2896     if( mbAutoWidth )
2897     {
2898         // AutoSize first only for width...
2899 
2900         aFloatSz.setWidth( aSz.Width() + nLeft + nRight );
2901         aFloatSz.AdjustWidth(nRight ); // adding some space looks better...
2902 
2903         if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
2904         {
2905             // then we also need the vertical Scrollbar
2906             tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2907             aFloatSz.AdjustWidth(nSBWidth );
2908         }
2909 
2910         tools::Long nDesktopWidth = GetDesktopRectPixel().getWidth();
2911         if (aFloatSz.Width() > nDesktopWidth)
2912             // Don't exceed the desktop width.
2913             aFloatSz.setWidth( nDesktopWidth );
2914     }
2915 
2916     if ( aFloatSz.Height() > nMaxHeight )
2917         aFloatSz.setHeight( nMaxHeight );
2918 
2919     // Minimal height, in case height is not set to Float height.
2920     // The parent of FloatWin must be DropDown-Combo/Listbox.
2921     Size aParentSz = GetParent()->GetSizePixel();
2922     if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
2923         aFloatSz.setHeight( aParentSz.Height() );
2924 
2925     // do not get narrower than the parent...
2926     if( aFloatSz.Width() < aParentSz.Width() )
2927         aFloatSz.setWidth( aParentSz.Width() );
2928 
2929     // align height to entries...
2930     tools::Long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
2931     tools::Long nEntryHeight = mpImplLB->GetEntryHeightWithMargin();
2932     if ( nInnerHeight % nEntryHeight )
2933     {
2934         nInnerHeight /= nEntryHeight;
2935         nInnerHeight++;
2936         nInnerHeight *= nEntryHeight;
2937         aFloatSz.setHeight( nInnerHeight + nTop + nBottom );
2938     }
2939 
2940     if (aFloatSz.Width() < aSz.Width())
2941     {
2942         // The max width of list box entries exceeds the window width.
2943         // Account for the scroll bar height.
2944         tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2945         aFloatSz.AdjustHeight(nSBWidth );
2946     }
2947 
2948     return aFloatSz;
2949 }
2950 
StartFloat(bool bStartTracking)2951 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
2952 {
2953     if( IsInPopupMode() )
2954         return;
2955 
2956     Size aFloatSz = CalcFloatSize();
2957 
2958     SetSizePixel( aFloatSz );
2959     mpImplLB->SetSizePixel( GetOutputSizePixel() );
2960 
2961     sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectedEntryPos( 0 );
2962     mnPopupModeStartSaveSelection = nPos;
2963 
2964     Size aSz = GetParent()->GetSizePixel();
2965     Point aPos = GetParent()->GetPosPixel();
2966     aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2967     // FIXME: this ugly hack is for Mac/Aqua
2968     // should be replaced by a real mechanism to place the float rectangle
2969     if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2970         GetParent()->IsNativeWidgetEnabled() )
2971     {
2972         const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
2973         aPos.AdjustX(nLeft );
2974         aPos.AdjustY(nTop );
2975         aSz.AdjustWidth( -(nLeft + nRight) );
2976         aSz.AdjustHeight( -(nTop + nBottom) );
2977     }
2978     tools::Rectangle aRect( aPos, aSz );
2979 
2980     // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
2981     // where the document is unmirrored
2982     // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
2983     vcl::Window *pGrandparent = GetParent()->GetParent();
2984     const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
2985 
2986     if( pGrandparent->GetOutDev()->ImplIsAntiparallel() )
2987         pGrandparentOutDev->ReMirror( aRect );
2988 
2989     // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
2990     StartPopupMode( aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::NoHorzPlacement | FloatWinPopupFlags::AllMouseButtonClose );
2991 
2992     if( nPos != LISTBOX_ENTRY_NOTFOUND )
2993         mpImplLB->ShowProminentEntry( nPos );
2994 
2995     if( bStartTracking )
2996         mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
2997 
2998     if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
2999         mpImplLB->GetMainWindow()->GrabFocus();
3000 
3001     mpImplLB->GetMainWindow()->ImplClearLayoutData();
3002 
3003 }
3004 
3005 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3006