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