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 <vcl/builder.hxx>
21 #include <vcl/commandevent.hxx>
22 #include <vcl/event.hxx>
23 #include <vcl/toolkit/lstbox.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/uitest/uiobject.hxx>
26 #include <sal/log.hxx>
27 
28 #include <svdata.hxx>
29 #include <controldata.hxx>
30 #include <listbox.hxx>
31 #include <dndeventdispatcher.hxx>
32 #include <comphelper/lok.hxx>
33 
34 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
35 #include <boost/property_tree/ptree.hpp>
36 #include <tools/json_writer.hxx>
37 
ListBox(WindowType nType)38 ListBox::ListBox(WindowType nType)
39     : Control(nType)
40     , mpImplLB(nullptr)
41 {
42     ImplInitListBoxData();
43 }
44 
ListBox(vcl::Window * pParent,WinBits nStyle)45 ListBox::ListBox( vcl::Window* pParent, WinBits nStyle ) : Control( WindowType::LISTBOX )
46 {
47     ImplInitListBoxData();
48     ImplInit( pParent, nStyle );
49 }
50 
~ListBox()51 ListBox::~ListBox()
52 {
53     disposeOnce();
54 }
55 
dispose()56 void ListBox::dispose()
57 {
58     CallEventListeners( VclEventId::ObjectDying );
59 
60     mpImplLB.disposeAndClear();
61     mpFloatWin.disposeAndClear();
62     mpImplWin.disposeAndClear();
63     mpBtn.disposeAndClear();
64 
65     Control::dispose();
66 }
67 
ImplInitListBoxData()68 void ListBox::ImplInitListBoxData()
69 {
70     mpFloatWin      = nullptr;
71     mpImplWin       = nullptr;
72     mpBtn           = nullptr;
73     mnDDHeight      = 0;
74     mnLineCount     = 0;
75     m_nMaxWidthChars = -1;
76     mbDDAutoSize    = true;
77 }
78 
ImplInit(vcl::Window * pParent,WinBits nStyle)79 void ListBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
80 {
81     nStyle = ImplInitStyle( nStyle );
82     if ( !(nStyle & WB_NOBORDER) && ( nStyle & WB_DROPDOWN ) )
83         nStyle |= WB_BORDER;
84 
85     Control::ImplInit( pParent, nStyle, nullptr );
86 
87     css::uno::Reference< css::datatransfer::dnd::XDropTargetListener> xDrop = new DNDEventDispatcher(this);
88 
89     if( nStyle & WB_DROPDOWN )
90     {
91         sal_Int32 nLeft, nTop, nRight, nBottom;
92         GetBorder( nLeft, nTop, nRight, nBottom );
93         mnDDHeight = static_cast<sal_uInt16>(GetTextHeight() + nTop + nBottom + 4);
94 
95         if( IsNativeWidgetEnabled() &&
96             IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
97         {
98                 ImplControlValue aControlValue;
99                 tools::Rectangle aCtrlRegion( Point( 0, 0 ), Size( 20, mnDDHeight ) );
100                 tools::Rectangle aBoundingRgn( aCtrlRegion );
101                 tools::Rectangle aContentRgn( aCtrlRegion );
102                 if( GetNativeControlRegion( ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
103                                             ControlState::ENABLED, aControlValue,
104                                             aBoundingRgn, aContentRgn ) )
105                 {
106                     sal_Int32 nHeight = aBoundingRgn.GetHeight();
107                     if( nHeight > mnDDHeight )
108                         mnDDHeight = static_cast<sal_uInt16>(nHeight);
109                 }
110         }
111 
112         mpFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
113         if (!IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
114             mpFloatWin->RequestDoubleBuffering(true);
115         mpFloatWin->SetAutoWidth( true );
116         mpFloatWin->SetPopupModeEndHdl( LINK( this, ListBox, ImplPopupModeEndHdl ) );
117         mpFloatWin->GetDropTarget()->addDropTargetListener(xDrop);
118 
119         mpImplWin = VclPtr<ImplWin>::Create( this, (nStyle & (WB_LEFT|WB_RIGHT|WB_CENTER))|WB_NOBORDER );
120         mpImplWin->SetMBDownHdl( LINK( this, ListBox, ImplClickBtnHdl ) );
121         mpImplWin->Show();
122         mpImplWin->GetDropTarget()->addDropTargetListener(xDrop);
123         mpImplWin->SetEdgeBlending(false);
124 
125         mpBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
126         ImplInitDropDownButton( mpBtn );
127         mpBtn->SetMBDownHdl( LINK( this, ListBox, ImplClickBtnHdl ) );
128         mpBtn->Show();
129         mpBtn->GetDropTarget()->addDropTargetListener(xDrop);
130     }
131 
132     vcl::Window* pLBParent = this;
133     if ( mpFloatWin )
134         pLBParent = mpFloatWin;
135     mpImplLB = VclPtr<ImplListBox>::Create( pLBParent, nStyle&(~WB_BORDER) );
136     mpImplLB->SetSelectHdl( LINK( this, ListBox, ImplSelectHdl ) );
137     mpImplLB->SetScrollHdl( LINK( this, ListBox, ImplScrollHdl ) );
138     mpImplLB->SetCancelHdl( LINK( this, ListBox, ImplCancelHdl ) );
139     mpImplLB->SetDoubleClickHdl( LINK( this, ListBox, ImplDoubleClickHdl ) );
140     mpImplLB->SetFocusHdl( LINK( this, ListBox, ImplFocusHdl ) );
141     mpImplLB->SetListItemSelectHdl( LINK( this, ListBox, ImplListItemSelectHdl ) );
142     mpImplLB->SetPosPixel( Point() );
143     mpImplLB->SetEdgeBlending(false);
144     mpImplLB->Show();
145 
146     mpImplLB->GetDropTarget()->addDropTargetListener(xDrop);
147 
148     if ( mpFloatWin )
149     {
150         mpFloatWin->SetImplListBox( mpImplLB );
151         mpImplLB->SetSelectionChangedHdl( LINK( this, ListBox, ImplSelectionChangedHdl ) );
152     }
153     else
154         mpImplLB->GetMainWindow()->AllowGrabFocus( true );
155 
156     SetCompoundControl( true );
157 }
158 
ImplInitStyle(WinBits nStyle)159 WinBits ListBox::ImplInitStyle( WinBits nStyle )
160 {
161     if ( !(nStyle & WB_NOTABSTOP) )
162         nStyle |= WB_TABSTOP;
163     if ( !(nStyle & WB_NOGROUP) )
164         nStyle |= WB_GROUP;
165     return nStyle;
166 }
167 
IMPL_LINK_NOARG(ListBox,ImplSelectHdl,LinkParamNone *,void)168 IMPL_LINK_NOARG(ListBox, ImplSelectHdl, LinkParamNone*, void)
169 {
170     bool bPopup = IsInDropDown();
171     if( IsDropDownBox() )
172     {
173         if( !mpImplLB->IsTravelSelect() )
174         {
175             mpFloatWin->EndPopupMode();
176             mpImplWin->GrabFocus();
177         }
178 
179         mpImplWin->SetItemPos( GetSelectedEntryPos() );
180         mpImplWin->SetString( GetSelectedEntry() );
181         if( mpImplLB->GetEntryList()->HasImages() )
182         {
183             Image aImage = mpImplLB->GetEntryList()->GetEntryImage( GetSelectedEntryPos() );
184             mpImplWin->SetImage( aImage );
185         }
186         mpImplWin->Invalidate();
187     }
188 
189     if ( ( !IsTravelSelect() || mpImplLB->IsSelectionChanged() ) || ( bPopup && !IsMultiSelectionEnabled() ) )
190         Select();
191 }
192 
IMPL_LINK(ListBox,ImplFocusHdl,sal_Int32,nPos,void)193 IMPL_LINK( ListBox, ImplFocusHdl, sal_Int32, nPos, void )
194 {
195     CallEventListeners( VclEventId::ListboxFocus, reinterpret_cast<void*>(nPos) );
196 }
197 
IMPL_LINK_NOARG(ListBox,ImplListItemSelectHdl,LinkParamNone *,void)198 IMPL_LINK_NOARG( ListBox, ImplListItemSelectHdl, LinkParamNone*, void )
199 {
200     CallEventListeners( VclEventId::DropdownSelect );
201 }
202 
IMPL_LINK_NOARG(ListBox,ImplScrollHdl,ImplListBox *,void)203 IMPL_LINK_NOARG(ListBox, ImplScrollHdl, ImplListBox*, void)
204 {
205     CallEventListeners( VclEventId::ListboxScrolled );
206 }
207 
IMPL_LINK_NOARG(ListBox,ImplCancelHdl,LinkParamNone *,void)208 IMPL_LINK_NOARG(ListBox, ImplCancelHdl, LinkParamNone*, void)
209 {
210     if( IsInDropDown() )
211         mpFloatWin->EndPopupMode();
212 }
213 
IMPL_LINK(ListBox,ImplSelectionChangedHdl,sal_Int32,nChanged,void)214 IMPL_LINK( ListBox, ImplSelectionChangedHdl, sal_Int32, nChanged, void )
215 {
216     if ( mpImplLB->IsTrackingSelect() )
217         return;
218 
219     const ImplEntryList* pEntryList = mpImplLB->GetEntryList();
220     if ( pEntryList->IsEntryPosSelected( nChanged ) )
221     {
222         // FIXME? This should've been turned into an ImplPaintEntry some time ago...
223         if ( nChanged < pEntryList->GetMRUCount() )
224             nChanged = pEntryList->FindEntry( pEntryList->GetEntryText( nChanged ) );
225         mpImplWin->SetItemPos( nChanged );
226         mpImplWin->SetString( mpImplLB->GetEntryList()->GetEntryText( nChanged ) );
227         if( mpImplLB->GetEntryList()->HasImages() )
228         {
229             Image aImage = mpImplLB->GetEntryList()->GetEntryImage( nChanged );
230             mpImplWin->SetImage( aImage );
231         }
232     }
233     else
234     {
235         mpImplWin->SetItemPos( LISTBOX_ENTRY_NOTFOUND );
236         mpImplWin->SetString( OUString() );
237         Image aImage;
238         mpImplWin->SetImage( aImage );
239     }
240     mpImplWin->Invalidate();
241 }
242 
IMPL_LINK_NOARG(ListBox,ImplDoubleClickHdl,ImplListBoxWindow *,void)243 IMPL_LINK_NOARG(ListBox, ImplDoubleClickHdl, ImplListBoxWindow*, void)
244 {
245     DoubleClick();
246 }
247 
IMPL_LINK_NOARG(ListBox,ImplClickBtnHdl,void *,void)248 IMPL_LINK_NOARG(ListBox, ImplClickBtnHdl, void*, void)
249 {
250     if( mpFloatWin->IsInPopupMode() )
251         return;
252 
253     CallEventListeners( VclEventId::DropdownPreOpen );
254     mpImplWin->GrabFocus();
255     mpBtn->SetPressed( true );
256     mpFloatWin->StartFloat( true );
257     CallEventListeners( VclEventId::DropdownOpen );
258 
259     ImplClearLayoutData();
260     if( mpImplLB )
261         mpImplLB->GetMainWindow()->ImplClearLayoutData();
262     if( mpImplWin )
263         mpImplWin->ImplClearLayoutData();
264 }
265 
IMPL_LINK_NOARG(ListBox,ImplPopupModeEndHdl,FloatingWindow *,void)266 IMPL_LINK_NOARG(ListBox, ImplPopupModeEndHdl, FloatingWindow*, void)
267 {
268     if( mpFloatWin->IsPopupModeCanceled() )
269     {
270         if ( ( mpFloatWin->GetPopupModeStartSaveSelection() != LISTBOX_ENTRY_NOTFOUND )
271                 && !IsEntryPosSelected( mpFloatWin->GetPopupModeStartSaveSelection() ) )
272         {
273             mpImplLB->SelectEntry( mpFloatWin->GetPopupModeStartSaveSelection(), true );
274             bool bTravelSelect = mpImplLB->IsTravelSelect();
275             mpImplLB->SetTravelSelect( true );
276 
277             VclPtr<vcl::Window> xWindow = this;
278             Select();
279             if ( xWindow->isDisposed() )
280                 return;
281 
282             mpImplLB->SetTravelSelect( bTravelSelect );
283         }
284     }
285 
286     ImplClearLayoutData();
287     if( mpImplLB )
288         mpImplLB->GetMainWindow()->ImplClearLayoutData();
289     if( mpImplWin )
290         mpImplWin->ImplClearLayoutData();
291 
292     mpBtn->SetPressed( false );
293     CallEventListeners( VclEventId::DropdownClose );
294 }
295 
ToggleDropDown()296 void ListBox::ToggleDropDown()
297 {
298     if( !IsDropDownBox() )
299         return;
300 
301     if( mpFloatWin->IsInPopupMode() )
302         mpFloatWin->EndPopupMode();
303     else
304     {
305         CallEventListeners( VclEventId::DropdownPreOpen );
306         mpImplWin->GrabFocus();
307         mpBtn->SetPressed( true );
308         mpFloatWin->StartFloat( true );
309         CallEventListeners( VclEventId::DropdownOpen );
310     }
311 }
312 
ApplySettings(vcl::RenderContext & rRenderContext)313 void ListBox::ApplySettings(vcl::RenderContext& rRenderContext)
314 {
315     rRenderContext.SetBackground();
316 }
317 
Draw(OutputDevice * pDev,const Point & rPos,DrawFlags nFlags)318 void ListBox::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags )
319 {
320     mpImplLB->GetMainWindow()->ApplySettings(*pDev);
321 
322     Point aPos = pDev->LogicToPixel( rPos );
323     Size aSize = GetSizePixel();
324     vcl::Font aFont = mpImplLB->GetMainWindow()->GetDrawPixelFont( pDev );
325 
326     pDev->Push();
327     pDev->SetMapMode();
328     pDev->SetFont( aFont );
329     pDev->SetTextFillColor();
330 
331     // Border/Background
332     pDev->SetLineColor();
333     pDev->SetFillColor();
334     bool bBorder = (GetStyle() & WB_BORDER);
335     bool bBackground = IsControlBackground();
336     if ( bBorder || bBackground )
337     {
338         tools::Rectangle aRect( aPos, aSize );
339         if ( bBorder )
340         {
341             ImplDrawFrame( pDev, aRect );
342         }
343         if ( bBackground )
344         {
345             pDev->SetFillColor( GetControlBackground() );
346             pDev->DrawRect( aRect );
347         }
348     }
349 
350     // Content
351     if ( nFlags & DrawFlags::Mono )
352     {
353         pDev->SetTextColor( COL_BLACK );
354     }
355     else
356     {
357         if ( !IsEnabled() )
358         {
359             const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
360             pDev->SetTextColor( rStyleSettings.GetDisableColor() );
361         }
362         else
363         {
364             pDev->SetTextColor( GetTextColor() );
365         }
366     }
367 
368     const tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
369     const tools::Long nOffX = 3*nOnePixel;
370     DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
371     tools::Rectangle aTextRect( aPos, aSize );
372 
373     if ( GetStyle() & WB_CENTER )
374         nTextStyle |= DrawTextFlags::Center;
375     else if ( GetStyle() & WB_RIGHT )
376         nTextStyle |= DrawTextFlags::Right;
377     else
378         nTextStyle |= DrawTextFlags::Left;
379 
380     aTextRect.AdjustLeft(nOffX );
381     aTextRect.AdjustRight( -nOffX );
382 
383     if ( IsDropDownBox() )
384     {
385         OUString   aText = GetSelectedEntry();
386         tools::Long       nTextHeight = pDev->GetTextHeight();
387         tools::Long       nTextWidth = pDev->GetTextWidth( aText );
388         tools::Long       nOffY = (aSize.Height()-nTextHeight) / 2;
389 
390         // Clipping?
391         if ( (nOffY < 0) ||
392              ((nOffY+nTextHeight) > aSize.Height()) ||
393              ((nOffX+nTextWidth) > aSize.Width()) )
394         {
395             tools::Rectangle aClip( aPos, aSize );
396             if ( nTextHeight > aSize.Height() )
397                 aClip.AdjustBottom(nTextHeight-aSize.Height()+1 );  // So that HP Printers don't optimize this away
398             pDev->IntersectClipRegion( aClip );
399         }
400 
401         pDev->DrawText( aTextRect, aText, nTextStyle );
402     }
403     else
404     {
405         tools::Long        nTextHeight = pDev->GetTextHeight();
406         sal_uInt16  nLines = ( nTextHeight > 0 ) ? static_cast<sal_uInt16>(aSize.Height() / nTextHeight) : 1;
407         tools::Rectangle   aClip( aPos, aSize );
408 
409         pDev->IntersectClipRegion( aClip );
410 
411         if ( !nLines )
412             nLines = 1;
413 
414         for ( sal_uInt16 n = 0; n < nLines; n++ )
415         {
416             sal_Int32 nEntry = n+mpImplLB->GetTopEntry();
417             bool bSelected = mpImplLB->GetEntryList()->IsEntryPosSelected( nEntry );
418             if ( bSelected )
419             {
420                 pDev->SetFillColor( COL_BLACK );
421                 pDev->DrawRect( tools::Rectangle(  Point( aPos.X(), aPos.Y() + n*nTextHeight ),
422                                             Point( aPos.X() + aSize.Width(), aPos.Y() + (n+1)*nTextHeight + 2*nOnePixel ) ) );
423                 pDev->SetFillColor();
424                 pDev->SetTextColor( COL_WHITE );
425             }
426 
427             aTextRect.SetTop( aPos.Y() + n*nTextHeight );
428             aTextRect.SetBottom( aTextRect.Top() + nTextHeight );
429 
430             pDev->DrawText( aTextRect, mpImplLB->GetEntryList()->GetEntryText( nEntry ), nTextStyle );
431 
432             if ( bSelected )
433                 pDev->SetTextColor( COL_BLACK );
434         }
435     }
436 
437     pDev->Pop();
438 }
439 
GetFocus()440 void ListBox::GetFocus()
441 {
442     if ( mpImplLB )
443     {
444         if( IsDropDownBox() )
445             mpImplWin->GrabFocus();
446         else
447             mpImplLB->GrabFocus();
448     }
449 
450     Control::GetFocus();
451 }
452 
LoseFocus()453 void ListBox::LoseFocus()
454 {
455     if( IsDropDownBox() )
456     {
457         if (mpImplWin)
458             mpImplWin->HideFocus();
459     }
460     else
461     {
462         if (mpImplLB)
463             mpImplLB->HideFocus();
464     }
465 
466     Control::LoseFocus();
467 }
468 
DataChanged(const DataChangedEvent & rDCEvt)469 void ListBox::DataChanged( const DataChangedEvent& rDCEvt )
470 {
471     Control::DataChanged( rDCEvt );
472 
473     if ( !((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
474          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
475          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
476           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
477         return;
478 
479     SetBackground();    // Due to a hack in Window::UpdateSettings the background must be reset
480                         // otherwise it will overpaint NWF drawn listboxes
481     Resize();
482     mpImplLB->Resize(); // Is not called by ListBox::Resize() if the ImplLB does not change
483 
484     if ( mpImplWin )
485     {
486         mpImplWin->GetOutDev()->SetSettings( GetSettings() ); // If not yet set...
487         mpImplWin->ApplySettings(*mpImplWin->GetOutDev());
488 
489         mpBtn->GetOutDev()->SetSettings( GetSettings() );
490         ImplInitDropDownButton( mpBtn );
491     }
492 
493     if ( IsDropDownBox() )
494         Invalidate();
495 }
496 
EnableAutoSize(bool bAuto)497 void ListBox::EnableAutoSize( bool bAuto )
498 {
499     mbDDAutoSize = bAuto;
500     if ( mpFloatWin )
501     {
502         if ( bAuto && !mpFloatWin->GetDropDownLineCount() )
503         {
504             // use GetListBoxMaximumLineCount here; before, was on fixed number of five
505             AdaptDropDownLineCountToMaximum();
506         }
507         else if ( !bAuto )
508         {
509             mpFloatWin->SetDropDownLineCount( 0 );
510         }
511     }
512 }
513 
SetDropDownLineCount(sal_uInt16 nLines)514 void ListBox::SetDropDownLineCount( sal_uInt16 nLines )
515 {
516     mnLineCount = nLines;
517     if ( mpFloatWin )
518         mpFloatWin->SetDropDownLineCount( mnLineCount );
519 }
520 
AdaptDropDownLineCountToMaximum()521 void ListBox::AdaptDropDownLineCountToMaximum()
522 {
523     // Adapt to maximum allowed number.
524     // Limit for LOK as we can't render outside of the dialog canvas.
525     if (comphelper::LibreOfficeKit::isActive())
526         SetDropDownLineCount(11);
527     else
528         SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
529 }
530 
GetDropDownLineCount() const531 sal_uInt16 ListBox::GetDropDownLineCount() const
532 {
533     if ( mpFloatWin )
534         return mpFloatWin->GetDropDownLineCount();
535     return mnLineCount;
536 }
537 
setPosSizePixel(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,PosSizeFlags nFlags)538 void ListBox::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
539 {
540     if( IsDropDownBox() && ( nFlags & PosSizeFlags::Size ) )
541     {
542         Size aPrefSz = mpFloatWin->GetPrefSize();
543         if ( ( nFlags & PosSizeFlags::Height ) && ( nHeight >= 2*mnDDHeight ) )
544             aPrefSz.setHeight( nHeight-mnDDHeight );
545         if ( nFlags & PosSizeFlags::Width )
546             aPrefSz.setWidth( nWidth );
547         mpFloatWin->SetPrefSize( aPrefSz );
548 
549         if (IsAutoSizeEnabled())
550             nHeight = mnDDHeight;
551     }
552 
553     Control::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
554 }
555 
Resize()556 void ListBox::Resize()
557 {
558     Size aOutSz = GetOutputSizePixel();
559     if( IsDropDownBox() )
560     {
561         // Initialize the dropdown button size with the standard scrollbar width
562         tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
563         tools::Long nBottom = aOutSz.Height();
564 
565         // Note: in case of no border, pBorder will actually be this
566         vcl::Window *pBorder = GetWindow( GetWindowType::Border );
567         ImplControlValue aControlValue;
568         Point aPoint;
569         tools::Rectangle aContent, aBound;
570 
571         // Use the full extent of the control
572         tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
573 
574         if ( GetNativeControlRegion( ControlType::Listbox, ControlPart::ButtonDown,
575                     aArea, ControlState::NONE, aControlValue, aBound, aContent) )
576         {
577             // Convert back from border space to local coordinates
578             aPoint = pBorder->ScreenToOutputPixel( OutputToScreenPixel( aPoint ) );
579             aContent.Move( -aPoint.X(), -aPoint.Y() );
580 
581             // Use the themes drop down size for the button
582             aOutSz.setWidth( aContent.Left() );
583             mpBtn->setPosSizePixel( aContent.Left(), 0, aContent.GetWidth(), nBottom );
584 
585             // Adjust the size of the edit field
586             if ( GetNativeControlRegion( ControlType::Listbox, ControlPart::SubEdit,
587                         aArea, ControlState::NONE, aControlValue, aBound, aContent) )
588             {
589                 // Convert back from border space to local coordinates
590                 aContent.Move( -aPoint.X(), -aPoint.Y() );
591 
592                 // Use the themes drop down size
593                 if( ! (GetStyle() & WB_BORDER) && ImplGetSVData()->maNWFData.mbNoFocusRects )
594                 {
595                     // No border but focus ring behavior -> we have a problem; the
596                     // native rect relies on the border to draw the focus
597                     // let's do the best we can and center vertically, so it doesn't look
598                     // completely wrong.
599                     Size aSz( GetOutputSizePixel() );
600                     tools::Long nDiff = aContent.Top() - (aSz.Height() - aContent.GetHeight())/2;
601                     aContent.AdjustTop( -nDiff );
602                     aContent.AdjustBottom( -nDiff );
603                 }
604                 mpImplWin->SetPosSizePixel( aContent.TopLeft(), aContent.GetSize() );
605             }
606             else
607                 mpImplWin->SetSizePixel( aOutSz );
608         }
609         else
610         {
611             nSBWidth = CalcZoom( nSBWidth );
612             mpImplWin->setPosSizePixel( 0, 0, aOutSz.Width() - nSBWidth, aOutSz.Height() );
613             mpBtn->setPosSizePixel( aOutSz.Width() - nSBWidth, 0, nSBWidth, aOutSz.Height() );
614         }
615     }
616     else
617     {
618         mpImplLB->SetSizePixel( aOutSz );
619     }
620 
621     // Retain FloatingWindow size even when it's invisible, as we still process KEY_PGUP/DOWN ...
622     if ( mpFloatWin )
623         mpFloatWin->SetSizePixel( mpFloatWin->CalcFloatSize() );
624 
625     Control::Resize();
626 }
627 
FillLayoutData() const628 void ListBox::FillLayoutData() const
629 {
630     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
631     const ImplListBoxWindow* rMainWin = mpImplLB->GetMainWindow();
632     if( mpFloatWin )
633     {
634         // Dropdown mode
635         AppendLayoutData( *mpImplWin );
636         mpImplWin->SetLayoutDataParent( this );
637         if( mpFloatWin->IsReallyVisible() )
638         {
639             AppendLayoutData( *rMainWin );
640             rMainWin->SetLayoutDataParent( this );
641         }
642     }
643     else
644     {
645         AppendLayoutData( *rMainWin );
646         rMainWin->SetLayoutDataParent( this );
647     }
648 }
649 
GetIndexForPoint(const Point & rPoint,sal_Int32 & rPos) const650 tools::Long ListBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
651 {
652     if( !HasLayoutData() )
653         FillLayoutData();
654 
655     // Check whether rPoint fits at all
656     tools::Long nIndex = Control::GetIndexForPoint( rPoint );
657     if( nIndex != -1 )
658     {
659         // Point must be either in main list window
660         // or in impl window (dropdown case)
661         ImplListBoxWindow* rMain = mpImplLB->GetMainWindow();
662 
663         // Convert coordinates to ImplListBoxWindow pixel coordinate space
664         Point aConvPoint = LogicToPixel( rPoint );
665         aConvPoint = OutputToAbsoluteScreenPixel( aConvPoint );
666         aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPoint );
667         aConvPoint = rMain->PixelToLogic( aConvPoint );
668 
669         // Try to find entry
670         sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
671         if( nEntry == LISTBOX_ENTRY_NOTFOUND )
672         {
673             // Not found, maybe dropdown case
674             if( mpImplWin && mpImplWin->IsReallyVisible() )
675             {
676                 // Convert to impl window pixel coordinates
677                 aConvPoint = LogicToPixel( rPoint );
678                 aConvPoint = OutputToAbsoluteScreenPixel( aConvPoint );
679                 aConvPoint = mpImplWin->AbsoluteScreenToOutputPixel( aConvPoint );
680 
681                 // Check whether converted point is inside impl window
682                 Size aImplWinSize = mpImplWin->GetOutputSizePixel();
683                 if( aConvPoint.X() >= 0 && aConvPoint.Y() >= 0 && aConvPoint.X() < aImplWinSize.Width() && aConvPoint.Y() < aImplWinSize.Height() )
684                 {
685                     // Inside the impl window, the position is the current item pos
686                     rPos = mpImplWin->GetItemPos();
687                 }
688                 else
689                     nIndex = -1;
690             }
691             else
692                 nIndex = -1;
693         }
694         else
695             rPos = nEntry;
696 
697         SAL_WARN_IF( nIndex == -1, "vcl", "found index for point, but relative index failed" );
698     }
699 
700     // Get line relative index
701     if( nIndex != -1 )
702         nIndex = ToRelativeLineIndex( nIndex );
703 
704     return nIndex;
705 }
706 
StateChanged(StateChangedType nType)707 void ListBox::StateChanged( StateChangedType nType )
708 {
709     if( nType == StateChangedType::ReadOnly )
710     {
711         if( mpImplWin )
712             mpImplWin->Enable( !IsReadOnly() );
713         if( mpBtn )
714             mpBtn->Enable( !IsReadOnly() );
715     }
716     else if( nType == StateChangedType::Enable )
717     {
718         mpImplLB->Enable( IsEnabled() );
719         if( mpImplWin )
720         {
721             mpImplWin->Enable( IsEnabled() );
722             if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
723                     && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
724             {
725                 GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
726             }
727             else
728                 mpImplWin->Invalidate();
729         }
730         if( mpBtn )
731             mpBtn->Enable( IsEnabled() );
732     }
733     else if( nType == StateChangedType::UpdateMode )
734     {
735         mpImplLB->SetUpdateMode( IsUpdateMode() );
736     }
737     else if ( nType == StateChangedType::Zoom )
738     {
739         mpImplLB->SetZoom( GetZoom() );
740         if ( mpImplWin )
741         {
742             mpImplWin->SetZoom( GetZoom() );
743             mpImplWin->SetFont( mpImplLB->GetMainWindow()->GetFont() );
744             mpImplWin->Invalidate();
745         }
746         Resize();
747     }
748     else if ( nType == StateChangedType::ControlFont )
749     {
750         mpImplLB->SetControlFont( GetControlFont() );
751         if ( mpImplWin )
752         {
753             mpImplWin->SetControlFont( GetControlFont() );
754             mpImplWin->SetFont( mpImplLB->GetMainWindow()->GetFont() );
755             mpImplWin->Invalidate();
756         }
757         Resize();
758     }
759     else if ( nType == StateChangedType::ControlForeground )
760     {
761         mpImplLB->SetControlForeground( GetControlForeground() );
762         if ( mpImplWin )
763         {
764             mpImplWin->SetControlForeground( GetControlForeground() );
765             mpImplWin->SetTextColor( GetControlForeground() );
766             mpImplWin->SetFont( mpImplLB->GetMainWindow()->GetFont() );
767             mpImplWin->Invalidate();
768         }
769     }
770     else if ( nType == StateChangedType::ControlBackground )
771     {
772         mpImplLB->SetControlBackground( GetControlBackground() );
773         if ( mpImplWin )
774         {
775             if ( mpImplWin->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire) )
776             {
777                 // Transparent background
778                 mpImplWin->SetBackground();
779                 mpImplWin->SetControlBackground();
780             }
781             else
782             {
783                 mpImplWin->SetBackground( mpImplLB->GetMainWindow()->GetControlBackground() );
784                 mpImplWin->SetControlBackground( mpImplLB->GetMainWindow()->GetControlBackground() );
785             }
786             mpImplWin->SetFont( mpImplLB->GetMainWindow()->GetFont() );
787             mpImplWin->Invalidate();
788         }
789     }
790     else if ( nType == StateChangedType::Style )
791     {
792         SetStyle( ImplInitStyle( GetStyle() ) );
793         mpImplLB->GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
794         bool bSimpleMode = ( GetStyle() & WB_SIMPLEMODE ) != 0;
795         mpImplLB->SetMultiSelectionSimpleMode( bSimpleMode );
796     }
797     else if( nType == StateChangedType::Mirroring )
798     {
799         if( mpBtn )
800         {
801             mpBtn->EnableRTL( IsRTLEnabled() );
802             ImplInitDropDownButton( mpBtn );
803         }
804         mpImplLB->EnableRTL( IsRTLEnabled() );
805         if( mpImplWin )
806             mpImplWin->EnableRTL( IsRTLEnabled() );
807         Resize();
808     }
809 
810     Control::StateChanged( nType );
811 }
812 
PreNotify(NotifyEvent & rNEvt)813 bool ListBox::PreNotify( NotifyEvent& rNEvt )
814 {
815     bool bDone = false;
816     if ( mpImplLB )
817     {
818         if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && ( rNEvt.GetWindow() == mpImplWin ) )
819         {
820             KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
821             switch( aKeyEvt.GetKeyCode().GetCode() )
822             {
823                 case KEY_DOWN:
824                 {
825                     if( mpFloatWin && !mpFloatWin->IsInPopupMode() &&
826                         aKeyEvt.GetKeyCode().IsMod2() )
827                     {
828                         CallEventListeners( VclEventId::DropdownPreOpen );
829                         mpBtn->SetPressed( true );
830                         mpFloatWin->StartFloat( false );
831                         CallEventListeners( VclEventId::DropdownOpen );
832                         bDone = true;
833                     }
834                     else
835                     {
836                         bDone = mpImplLB->ProcessKeyInput( aKeyEvt );
837                     }
838                 }
839                 break;
840                 case KEY_UP:
841                 {
842                     if( mpFloatWin && mpFloatWin->IsInPopupMode() &&
843                         aKeyEvt.GetKeyCode().IsMod2() )
844                     {
845                         mpFloatWin->EndPopupMode();
846                         bDone = true;
847                     }
848                     else
849                     {
850                         bDone = mpImplLB->ProcessKeyInput( aKeyEvt );
851                     }
852                 }
853                 break;
854                 case KEY_RETURN:
855                 {
856                     if( IsInDropDown() )
857                     {
858                         mpImplLB->ProcessKeyInput( aKeyEvt );
859                         bDone = true;
860                     }
861                 }
862                 break;
863 
864                 default:
865                 {
866                     bDone = mpImplLB->ProcessKeyInput( aKeyEvt );
867                 }
868             }
869         }
870         else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
871         {
872             if ( IsInDropDown() && !HasChildPathFocus( true ) )
873                 mpFloatWin->EndPopupMode();
874         }
875         else if ( (rNEvt.GetType() == MouseNotifyEvent::COMMAND) &&
876                   (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
877                   (rNEvt.GetWindow() == mpImplWin) )
878         {
879             MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
880             if  (   ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
881                 ||  (   ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
882                     &&  HasChildPathFocus()
883                     )
884                 )
885             {
886                 bDone = mpImplLB->HandleWheelAsCursorTravel(*rNEvt.GetCommandEvent(), *this);
887             }
888             else
889             {
890                 bDone = false;  // Don't consume this event, let the default handling take it (i.e. scroll the context)
891             }
892         }
893     }
894 
895     return bDone || Control::PreNotify( rNEvt );
896 }
897 
Select()898 void ListBox::Select()
899 {
900     ImplCallEventListenersAndHandler( VclEventId::ListboxSelect, [this] () { maSelectHdl.Call(*this); } );
901 }
902 
DoubleClick()903 void ListBox::DoubleClick()
904 {
905     ImplCallEventListenersAndHandler( VclEventId::ListboxDoubleClick, {} );
906 }
907 
Clear()908 void ListBox::Clear()
909 {
910     if (!mpImplLB)
911         return;
912     mpImplLB->Clear();
913     if( IsDropDownBox() )
914     {
915         mpImplWin->SetItemPos( LISTBOX_ENTRY_NOTFOUND );
916         mpImplWin->SetString( OUString() );
917         Image aImage;
918         mpImplWin->SetImage( aImage );
919         mpImplWin->Invalidate();
920     }
921     CallEventListeners( VclEventId::ListboxItemRemoved, reinterpret_cast<void*>(-1) );
922 }
923 
SetNoSelection()924 void ListBox::SetNoSelection()
925 {
926     mpImplLB->SetNoSelection();
927     if( IsDropDownBox() )
928     {
929         mpImplWin->SetItemPos( LISTBOX_ENTRY_NOTFOUND );
930         mpImplWin->SetString( OUString() );
931         Image aImage;
932         mpImplWin->SetImage( aImage );
933         mpImplWin->Invalidate();
934     }
935 }
936 
InsertEntry(const OUString & rStr,sal_Int32 nPos)937 sal_Int32 ListBox::InsertEntry( const OUString& rStr, sal_Int32 nPos )
938 {
939     sal_Int32 nRealPos = mpImplLB->InsertEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount(), rStr );
940     nRealPos = sal::static_int_cast<sal_Int32>(nRealPos - mpImplLB->GetEntryList()->GetMRUCount());
941     CallEventListeners( VclEventId::ListboxItemAdded, reinterpret_cast<void*>(nRealPos) );
942     return nRealPos;
943 }
944 
InsertEntry(const OUString & rStr,const Image & rImage,sal_Int32 nPos)945 sal_Int32 ListBox::InsertEntry( const OUString& rStr, const Image& rImage, sal_Int32 nPos )
946 {
947     sal_Int32 nRealPos = mpImplLB->InsertEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount(), rStr, rImage );
948     nRealPos = sal::static_int_cast<sal_Int32>(nRealPos - mpImplLB->GetEntryList()->GetMRUCount());
949     CallEventListeners( VclEventId::ListboxItemAdded, reinterpret_cast<void*>(nRealPos) );
950     return nRealPos;
951 }
952 
RemoveEntry(sal_Int32 nPos)953 void ListBox::RemoveEntry( sal_Int32 nPos )
954 {
955     mpImplLB->RemoveEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
956     CallEventListeners( VclEventId::ListboxItemRemoved, reinterpret_cast<void*>(nPos) );
957 }
958 
GetEntryImage(sal_Int32 nPos) const959 Image ListBox::GetEntryImage( sal_Int32 nPos ) const
960 {
961     if ( mpImplLB && mpImplLB->GetEntryList()->HasEntryImage( nPos ) )
962         return mpImplLB->GetEntryList()->GetEntryImage( nPos );
963     return Image();
964 }
965 
GetEntryPos(std::u16string_view rStr) const966 sal_Int32 ListBox::GetEntryPos( std::u16string_view rStr ) const
967 {
968     if (!mpImplLB)
969         return LISTBOX_ENTRY_NOTFOUND;
970     sal_Int32 nPos = mpImplLB->GetEntryList()->FindEntry( rStr );
971     if ( nPos != LISTBOX_ENTRY_NOTFOUND )
972         nPos = nPos - mpImplLB->GetEntryList()->GetMRUCount();
973     return nPos;
974 }
975 
GetEntry(sal_Int32 nPos) const976 OUString ListBox::GetEntry( sal_Int32 nPos ) const
977 {
978     if (!mpImplLB)
979         return OUString();
980     return mpImplLB->GetEntryList()->GetEntryText( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
981 }
982 
GetEntryCount() const983 sal_Int32 ListBox::GetEntryCount() const
984 {
985     if (!mpImplLB)
986         return 0;
987     return mpImplLB->GetEntryList()->GetEntryCount() - mpImplLB->GetEntryList()->GetMRUCount();
988 }
989 
GetSelectedEntry(sal_Int32 nIndex) const990 OUString ListBox::GetSelectedEntry(sal_Int32 nIndex) const
991 {
992     return GetEntry( GetSelectedEntryPos( nIndex ) );
993 }
994 
GetSelectedEntryCount() const995 sal_Int32 ListBox::GetSelectedEntryCount() const
996 {
997     if (!mpImplLB)
998         return 0;
999     return mpImplLB->GetEntryList()->GetSelectedEntryCount();
1000 }
1001 
GetSelectedEntryPos(sal_Int32 nIndex) const1002 sal_Int32 ListBox::GetSelectedEntryPos( sal_Int32 nIndex ) const
1003 {
1004     if (!mpImplLB || !mpImplLB->GetEntryList())
1005         return LISTBOX_ENTRY_NOTFOUND;
1006 
1007     sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectedEntryPos( nIndex );
1008     if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1009     {
1010         if ( nPos < mpImplLB->GetEntryList()->GetMRUCount() )
1011             nPos = mpImplLB->GetEntryList()->FindEntry( mpImplLB->GetEntryList()->GetEntryText( nPos ) );
1012         nPos = nPos - mpImplLB->GetEntryList()->GetMRUCount();
1013     }
1014     return nPos;
1015 }
1016 
IsEntryPosSelected(sal_Int32 nPos) const1017 bool ListBox::IsEntryPosSelected( sal_Int32 nPos ) const
1018 {
1019     return mpImplLB->GetEntryList()->IsEntryPosSelected( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
1020 }
1021 
SelectEntry(std::u16string_view rStr,bool bSelect)1022 void ListBox::SelectEntry( std::u16string_view rStr, bool bSelect )
1023 {
1024     SelectEntryPos( GetEntryPos( rStr ), bSelect );
1025 }
1026 
SelectEntryPos(sal_Int32 nPos,bool bSelect)1027 void ListBox::SelectEntryPos( sal_Int32 nPos, bool bSelect )
1028 {
1029     if (!mpImplLB)
1030         return;
1031 
1032     if ( 0 <= nPos && nPos < mpImplLB->GetEntryList()->GetEntryCount() )
1033     {
1034         sal_Int32 nCurrentPos = mpImplLB->GetCurrentPos();
1035         mpImplLB->SelectEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount(), bSelect );
1036         //Only when bSelect == true, send both Selection & Focus events
1037         if (nCurrentPos != nPos && bSelect)
1038         {
1039             CallEventListeners( VclEventId::ListboxSelect, reinterpret_cast<void*>(nPos));
1040             if (HasFocus())
1041                 CallEventListeners( VclEventId::ListboxFocus, reinterpret_cast<void*>(nPos));
1042         }
1043     }
1044 }
1045 
SelectEntriesPos(const std::vector<sal_Int32> & rPositions,bool bSelect)1046 void ListBox::SelectEntriesPos( const std::vector<sal_Int32>& rPositions, bool bSelect )
1047 {
1048     if (!mpImplLB)
1049         return;
1050 
1051     bool bCallListeners = false;
1052 
1053     const sal_Int32 nCurrentPos = mpImplLB->GetCurrentPos();
1054     const auto nEntryCount = mpImplLB->GetEntryList()->GetEntryCount();
1055     const auto nMRUCount = mpImplLB->GetEntryList()->GetMRUCount();
1056 
1057     for (auto nPos : rPositions)
1058     {
1059         if (0 <= nPos && nPos < nEntryCount)
1060         {
1061             mpImplLB->SelectEntry(nPos + nMRUCount, bSelect);
1062             if (nCurrentPos != nPos && bSelect)
1063                 bCallListeners = true;
1064         }
1065     }
1066 
1067     //Only when bSelect == true, send both Selection & Focus events
1068     if (bCallListeners)
1069     {
1070         CallEventListeners(VclEventId::ListboxSelect);
1071         if (HasFocus())
1072             CallEventListeners(VclEventId::ListboxFocus);
1073     }
1074 }
1075 
SetEntryData(sal_Int32 nPos,void * pNewData)1076 void ListBox::SetEntryData( sal_Int32 nPos, void* pNewData )
1077 {
1078     mpImplLB->SetEntryData( nPos + mpImplLB->GetEntryList()->GetMRUCount(), pNewData );
1079 }
1080 
GetEntryData(sal_Int32 nPos) const1081 void* ListBox::GetEntryData( sal_Int32 nPos ) const
1082 {
1083     return mpImplLB->GetEntryList()->GetEntryData( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
1084 }
1085 
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)1086 void ListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
1087 {
1088     mpImplLB->SetEntryFlags( nPos + mpImplLB->GetEntryList()->GetMRUCount(), nFlags );
1089 }
1090 
SetTopEntry(sal_Int32 nPos)1091 void ListBox::SetTopEntry( sal_Int32 nPos )
1092 {
1093     mpImplLB->SetTopEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
1094 }
1095 
GetTopEntry() const1096 sal_Int32 ListBox::GetTopEntry() const
1097 {
1098     sal_Int32 nPos = GetEntryCount() ? mpImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1099     if ( nPos < mpImplLB->GetEntryList()->GetMRUCount() )
1100         nPos = 0;
1101     return nPos;
1102 }
1103 
IsTravelSelect() const1104 bool ListBox::IsTravelSelect() const
1105 {
1106     return mpImplLB->IsTravelSelect();
1107 }
1108 
IsInDropDown() const1109 bool ListBox::IsInDropDown() const
1110 {
1111     // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
1112     // mbPopupMode is set to false
1113     return mpFloatWin && mpFloatWin->IsInPopupMode() && mpFloatWin->ImplIsInPrivatePopupMode();
1114 }
1115 
GetBoundingRectangle(sal_Int32 nItem) const1116 tools::Rectangle ListBox::GetBoundingRectangle( sal_Int32 nItem ) const
1117 {
1118     tools::Rectangle aRect = mpImplLB->GetMainWindow()->GetBoundingRectangle( nItem );
1119     tools::Rectangle aOffset = mpImplLB->GetMainWindow()->GetWindowExtentsRelative( static_cast<vcl::Window*>(const_cast<ListBox *>(this)) );
1120     aRect.Move( aOffset.Left(), aOffset.Top() );
1121     return aRect;
1122 }
1123 
EnableMultiSelection(bool bMulti)1124 void ListBox::EnableMultiSelection( bool bMulti )
1125 {
1126     mpImplLB->EnableMultiSelection( bMulti );
1127 
1128     // WB_SIMPLEMODE:
1129     // The MultiListBox behaves just like a normal ListBox
1130     // MultiSelection is possible via corresponding additional keys
1131     bool bSimpleMode = ( GetStyle() & WB_SIMPLEMODE ) != 0;
1132     mpImplLB->SetMultiSelectionSimpleMode( bSimpleMode );
1133 
1134     // In a MultiSelection, we can't see us travelling without focus
1135     if ( mpFloatWin )
1136         mpImplLB->GetMainWindow()->AllowGrabFocus( bMulti );
1137 }
1138 
IsMultiSelectionEnabled() const1139 bool ListBox::IsMultiSelectionEnabled() const
1140 {
1141     return mpImplLB->IsMultiSelectionEnabled();
1142 }
1143 
CalcMinimumSize() const1144 Size ListBox::CalcMinimumSize() const
1145 {
1146     Size aSz;
1147 
1148     if (!mpImplLB)
1149         return aSz;
1150 
1151     aSz = CalcSubEditSize();
1152 
1153     bool bAddScrollWidth = false;
1154 
1155     if (IsDropDownBox())
1156     {
1157         aSz.AdjustHeight(4 ); // add a space between entry and border
1158         aSz.AdjustWidth(4 );  // add a little breathing space
1159         bAddScrollWidth = true;
1160     }
1161     else
1162         bAddScrollWidth = (GetStyle() & WB_VSCROLL) == WB_VSCROLL;
1163 
1164     if (bAddScrollWidth)
1165     {
1166         // Try native borders; scrollbar size may not be a good indicator
1167         // See how large the edit area inside is to estimate what is needed for the dropdown
1168         ImplControlValue aControlValue;
1169         tools::Rectangle aContent, aBound;
1170         Size aTestSize( 100, 20 );
1171         tools::Rectangle aArea( Point(), aTestSize );
1172         if( GetNativeControlRegion( ControlType::Listbox, ControlPart::SubEdit, aArea, ControlState::NONE,
1173                     aControlValue, aBound, aContent) )
1174         {
1175             // use the themes drop down size
1176             aSz.AdjustWidth(aTestSize.Width() - aContent.GetWidth() );
1177         }
1178         else
1179             aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1180     }
1181 
1182     aSz = CalcWindowSize( aSz );
1183 
1184     if (IsDropDownBox()) // Check minimum height of dropdown box
1185     {
1186         ImplControlValue aControlValue;
1187         tools::Rectangle aRect( Point( 0, 0 ), aSz );
1188         tools::Rectangle aContent, aBound;
1189         if( GetNativeControlRegion( ControlType::Listbox, ControlPart::Entire, aRect, ControlState::NONE,
1190                     aControlValue, aBound, aContent) )
1191         {
1192             if( aBound.GetHeight() > aSz.Height() )
1193                 aSz.setHeight( aBound.GetHeight() );
1194         }
1195     }
1196 
1197     return aSz;
1198 }
1199 
CalcSubEditSize() const1200 Size ListBox::CalcSubEditSize() const
1201 {
1202     Size aSz;
1203 
1204     if (!mpImplLB)
1205         return aSz;
1206 
1207     if ( !IsDropDownBox() )
1208         aSz = mpImplLB->CalcSize (mnLineCount ? mnLineCount : mpImplLB->GetEntryList()->GetEntryCount());
1209     else
1210     {
1211         aSz.setHeight( mpImplLB->GetEntryHeight() );
1212         // Size to maximum entry width
1213         aSz.setWidth( mpImplLB->GetMaxEntryWidth() );
1214 
1215         if (m_nMaxWidthChars != -1)
1216         {
1217             tools::Long nMaxWidth = m_nMaxWidthChars * approximate_char_width();
1218             aSz.setWidth( std::min(aSz.Width(), nMaxWidth) );
1219         }
1220 
1221         // Do not create ultrathin ListBoxes, it doesn't look good
1222         if( aSz.Width() < GetSettings().GetStyleSettings().GetScrollBarSize() )
1223             aSz.setWidth( GetSettings().GetStyleSettings().GetScrollBarSize() );
1224     }
1225 
1226     return aSz;
1227 }
1228 
GetOptimalSize() const1229 Size ListBox::GetOptimalSize() const
1230 {
1231     return CalcMinimumSize();
1232 }
1233 
CalcAdjustedSize(const Size & rPrefSize) const1234 Size ListBox::CalcAdjustedSize( const Size& rPrefSize ) const
1235 {
1236     Size aSz = rPrefSize;
1237     sal_Int32 nLeft, nTop, nRight, nBottom;
1238     static_cast<vcl::Window*>(const_cast<ListBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1239     aSz.AdjustHeight( -(nTop+nBottom) );
1240     if ( !IsDropDownBox() )
1241     {
1242         tools::Long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1243         tools::Long nLines = aSz.Height() / nEntryHeight;
1244         if ( nLines < 1 )
1245             nLines = 1;
1246         aSz.setHeight( nLines * nEntryHeight );
1247     }
1248     else
1249     {
1250         aSz.setHeight( mnDDHeight );
1251     }
1252     aSz.AdjustHeight(nTop+nBottom );
1253 
1254     aSz = CalcWindowSize( aSz );
1255     return aSz;
1256 }
1257 
CalcBlockSize(sal_uInt16 nColumns,sal_uInt16 nLines) const1258 Size ListBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1259 {
1260     // ScrollBars are shown if needed
1261     Size aMinSz = CalcMinimumSize();
1262     // aMinSz = ImplCalcOutSz( aMinSz );
1263 
1264     Size aSz;
1265 
1266     // Height
1267     if ( nLines )
1268     {
1269         if ( !IsDropDownBox() )
1270             aSz.setHeight( mpImplLB->CalcSize( nLines ).Height() );
1271         else
1272             aSz.setHeight( mnDDHeight );
1273     }
1274     else
1275         aSz.setHeight( aMinSz.Height() );
1276 
1277     // Width
1278     if ( nColumns )
1279         aSz.setWidth( nColumns * GetTextWidth( OUString('X') ) );
1280     else
1281         aSz.setWidth( aMinSz.Width() );
1282 
1283     if ( IsDropDownBox() )
1284         aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1285 
1286     if ( !IsDropDownBox() )
1287     {
1288         if ( aSz.Width() < aMinSz.Width() )
1289             aSz.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1290         if ( aSz.Height() < aMinSz.Height() )
1291             aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1292     }
1293 
1294     aSz = CalcWindowSize( aSz );
1295     return aSz;
1296 }
1297 
GetMaxVisColumnsAndLines(sal_uInt16 & rnCols,sal_uInt16 & rnLines) const1298 void ListBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1299 {
1300     float nCharWidth = approximate_char_width();
1301     if ( !IsDropDownBox() )
1302     {
1303         Size aOutSz = mpImplLB->GetMainWindow()->GetOutputSizePixel();
1304         rnCols = static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth);
1305         rnLines = static_cast<sal_uInt16>(aOutSz.Height()/mpImplLB->GetEntryHeightWithMargin());
1306     }
1307     else
1308     {
1309         Size aOutSz = mpImplWin->GetOutputSizePixel();
1310         rnCols = static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth);
1311         rnLines = 1;
1312     }
1313 }
1314 
SetReadOnly(bool bReadOnly)1315 void ListBox::SetReadOnly( bool bReadOnly )
1316 {
1317     if ( mpImplLB->IsReadOnly() != bReadOnly )
1318     {
1319         mpImplLB->SetReadOnly( bReadOnly );
1320         CompatStateChanged( StateChangedType::ReadOnly );
1321     }
1322 }
1323 
IsReadOnly() const1324 bool ListBox::IsReadOnly() const
1325 {
1326     return mpImplLB->IsReadOnly();
1327 }
1328 
SetSeparatorPos(sal_Int32 n)1329 void ListBox::SetSeparatorPos( sal_Int32 n )
1330 {
1331     mpImplLB->SetSeparatorPos( n );
1332 }
1333 
GetSeparatorPos() const1334 sal_Int32 ListBox::GetSeparatorPos() const
1335 {
1336     return mpImplLB->GetSeparatorPos();
1337 }
1338 
AddSeparator(sal_Int32 n)1339 void ListBox::AddSeparator( sal_Int32 n )
1340 {
1341     mpImplLB->AddSeparator( n );
1342 }
1343 
GetDisplayLineCount() const1344 sal_uInt16 ListBox::GetDisplayLineCount() const
1345 {
1346     return mpImplLB->GetDisplayLineCount();
1347 }
1348 
GetDropDownPosSizePixel() const1349 tools::Rectangle ListBox::GetDropDownPosSizePixel() const
1350 {
1351     return mpFloatWin ? mpFloatWin->GetWindowExtentsRelative(this) : tools::Rectangle();
1352 }
1353 
GetDisplayBackground() const1354 const Wallpaper& ListBox::GetDisplayBackground() const
1355 {
1356     // !!! Recursion does not occur because the ImplListBox is initialized by default
1357     // to a non-transparent color in Window::ImplInitData
1358     return mpImplLB->GetDisplayBackground();
1359 }
1360 
setMaxWidthChars(sal_Int32 nWidth)1361 void ListBox::setMaxWidthChars(sal_Int32 nWidth)
1362 {
1363     if (nWidth != m_nMaxWidthChars)
1364     {
1365         m_nMaxWidthChars = nWidth;
1366         queue_resize();
1367     }
1368 }
1369 
set_property(const OString & rKey,const OUString & rValue)1370 bool ListBox::set_property(const OString &rKey, const OUString &rValue)
1371 {
1372     if (rKey == "active")
1373         SelectEntryPos(rValue.toInt32());
1374     else if (rKey == "max-width-chars")
1375         setMaxWidthChars(rValue.toInt32());
1376     else if (rKey == "can-focus")
1377     {
1378         // as far as I can see in Gtk, setting a ComboBox as can.focus means
1379         // the focus gets stuck in it, so try here to behave like gtk does
1380         // with the settings that work, i.e. can.focus of false doesn't
1381         // set the hard WB_NOTABSTOP
1382         WinBits nBits = GetStyle();
1383         nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
1384         if (toBool(rValue))
1385             nBits |= WB_TABSTOP;
1386         SetStyle(nBits);
1387     }
1388     else
1389         return Control::set_property(rKey, rValue);
1390     return true;
1391 }
1392 
GetUITestFactory() const1393 FactoryFunction ListBox::GetUITestFactory() const
1394 {
1395     return ListBoxUIObject::create;
1396 }
1397 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)1398 void ListBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1399 {
1400     Control::DumpAsPropertyTree(rJsonWriter);
1401 
1402     {
1403         auto entriesNode = rJsonWriter.startArray("entries");
1404         for (int i = 0; i < GetEntryCount(); ++i)
1405         {
1406             rJsonWriter.putSimpleValue(GetEntry(i));
1407         }
1408     }
1409 
1410     rJsonWriter.put("selectedCount", GetSelectedEntryCount());
1411 
1412     {
1413         auto entriesNode = rJsonWriter.startArray("selectedEntries");
1414         for (int i = 0; i < GetSelectedEntryCount(); ++i)
1415         {
1416             rJsonWriter.putSimpleValue(OUString::number(GetSelectedEntryPos(i)));
1417         }
1418     }
1419 }
1420 
MultiListBox(vcl::Window * pParent,WinBits nStyle)1421 MultiListBox::MultiListBox( vcl::Window* pParent, WinBits nStyle ) :
1422     ListBox( WindowType::MULTILISTBOX )
1423 {
1424     ImplInit( pParent, nStyle );
1425     EnableMultiSelection( true );
1426 }
1427 
1428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1429