1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/toolkit/combobox.hxx>
24 #include <comphelper/string.hxx>
25 #include <vcl/toolkit/lstbox.hxx>
26 #include <vcl/builder.hxx>
27 #include <vcl/commandevent.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/vclevent.hxx>
31 #include <vcl/uitest/uiobject.hxx>
32 #include <sal/log.hxx>
34 #include <listbox.hxx>
35 #include <comphelper/lok.hxx>
36 #include <tools/json_writer.hxx>
37 #include <o3tl/string_view.hxx>
55 VclPtr
<Edit
> m_pSubEdit
;
56 VclPtr
<ImplListBox
> m_pImplLB
;
57 VclPtr
<ImplBtn
> m_pBtn
;
58 VclPtr
<ImplListBoxFloatingWindow
> m_pFloatWin
;
59 sal_uInt16 m_nDDHeight
;
60 sal_Unicode m_cMultiSep
;
61 bool m_isDDAutoSize
: 1;
62 bool m_isSyntheticModify
: 1;
63 bool m_isKeyBoardModify
: 1;
64 bool m_isMatchCase
: 1;
65 sal_Int32 m_nMaxWidthChars
;
66 sal_Int32 m_nWidthInChars
;
67 Link
<ComboBox
&,void> m_SelectHdl
;
69 explicit Impl(ComboBox
& rThis
)
73 , m_isDDAutoSize(false)
74 , m_isSyntheticModify(false)
75 , m_isKeyBoardModify(false)
76 , m_isMatchCase(false)
82 void ImplInitComboBoxData();
83 void ImplUpdateFloatSelection();
84 ComboBoxBounds
calcComboBoxDropDownComponentBounds(
85 const Size
&rOutSize
, const Size
&rBorderOutSize
) const;
87 DECL_LINK( ImplSelectHdl
, LinkParamNone
*, void );
88 DECL_LINK( ImplCancelHdl
, LinkParamNone
*, void );
89 DECL_LINK( ImplDoubleClickHdl
, ImplListBoxWindow
*, void );
90 DECL_LINK( ImplClickBtnHdl
, void*, void );
91 DECL_LINK( ImplPopupModeEndHdl
, FloatingWindow
*, void );
92 DECL_LINK( ImplSelectionChangedHdl
, sal_Int32
, void );
93 DECL_LINK( ImplAutocompleteHdl
, Edit
&, void );
94 DECL_LINK( ImplListItemSelectHdl
, LinkParamNone
*, void );
98 static void lcl_GetSelectedEntries( ::std::set
< sal_Int32
>& rSelectedPos
, std::u16string_view rText
, sal_Unicode cTokenSep
, const ImplEntryList
& rEntryList
)
105 const sal_Int32 nPos
= rEntryList
.FindEntry(comphelper::string::strip(o3tl::getToken(rText
, 0, cTokenSep
, nIdx
), ' '));
106 if ( nPos
!= LISTBOX_ENTRY_NOTFOUND
)
107 rSelectedPos
.insert( nPos
);
111 ComboBox::ComboBox(vcl::Window
*const pParent
, WinBits
const nStyle
)
112 : Edit( WindowType::COMBOBOX
)
113 , m_pImpl(new Impl(*this))
115 m_pImpl
->ImplInitComboBoxData();
116 ImplInit( pParent
, nStyle
);
120 ComboBox::~ComboBox()
125 void ComboBox::dispose()
127 m_pImpl
->m_pSubEdit
.disposeAndClear();
129 VclPtr
< ImplListBox
> pImplLB
= m_pImpl
->m_pImplLB
;
130 m_pImpl
->m_pImplLB
.clear();
131 pImplLB
.disposeAndClear();
133 m_pImpl
->m_pFloatWin
.disposeAndClear();
134 m_pImpl
->m_pBtn
.disposeAndClear();
138 void ComboBox::Impl::ImplInitComboBoxData()
140 m_pSubEdit
.disposeAndClear();
143 m_pFloatWin
= nullptr;
146 m_isDDAutoSize
= true;
147 m_isSyntheticModify
= false;
148 m_isKeyBoardModify
= false;
149 m_isMatchCase
= false;
151 m_nMaxWidthChars
= -1;
152 m_nWidthInChars
= -1;
155 void ComboBox::ImplCalcEditHeight()
157 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
158 GetBorder( nLeft
, nTop
, nRight
, nBottom
);
159 m_pImpl
->m_nDDHeight
= static_cast<sal_uInt16
>(m_pImpl
->m_pSubEdit
->GetTextHeight() + nTop
+ nBottom
+ 4);
160 if ( !IsDropDownBox() )
161 m_pImpl
->m_nDDHeight
+= 4;
163 tools::Rectangle
aCtrlRegion( Point( 0, 0 ), Size( 10, 10 ) );
164 tools::Rectangle aBoundRegion
, aContentRegion
;
165 ImplControlValue aControlValue
;
166 ControlType aType
= IsDropDownBox() ? ControlType::Combobox
: ControlType::Editbox
;
167 if( GetNativeControlRegion( aType
, ControlPart::Entire
,
169 ControlState::ENABLED
,
171 aBoundRegion
, aContentRegion
) )
173 const tools::Long nNCHeight
= aBoundRegion
.GetHeight();
174 if (m_pImpl
->m_nDDHeight
< nNCHeight
)
175 m_pImpl
->m_nDDHeight
= sal::static_int_cast
<sal_uInt16
>(nNCHeight
);
179 void ComboBox::ImplInit( vcl::Window
* pParent
, WinBits nStyle
)
181 bool bNoBorder
= ( nStyle
& WB_NOBORDER
) != 0;
182 if ( !(nStyle
& WB_DROPDOWN
) )
184 nStyle
&= ~WB_BORDER
;
185 nStyle
|= WB_NOBORDER
;
193 Edit::ImplInit( pParent
, nStyle
);
197 WinBits nEditStyle
= nStyle
& ( WB_LEFT
| WB_RIGHT
| WB_CENTER
);
198 WinBits nListStyle
= nStyle
;
199 if( nStyle
& WB_DROPDOWN
)
201 m_pImpl
->m_pFloatWin
= VclPtr
<ImplListBoxFloatingWindow
>::Create( this );
202 if (!IsNativeControlSupported(ControlType::Pushbutton
, ControlPart::Focus
))
203 m_pImpl
->m_pFloatWin
->RequestDoubleBuffering(true);
204 m_pImpl
->m_pFloatWin
->SetAutoWidth( true );
205 m_pImpl
->m_pFloatWin
->SetPopupModeEndHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplPopupModeEndHdl
) );
207 m_pImpl
->m_pBtn
= VclPtr
<ImplBtn
>::Create( this, WB_NOLIGHTBORDER
| WB_RECTSTYLE
);
208 ImplInitDropDownButton( m_pImpl
->m_pBtn
);
209 m_pImpl
->m_pBtn
->SetMBDownHdl( LINK( m_pImpl
.get(), ComboBox::Impl
, ImplClickBtnHdl
) );
210 m_pImpl
->m_pBtn
->Show();
212 nEditStyle
|= WB_NOBORDER
;
213 nListStyle
&= ~WB_BORDER
;
214 nListStyle
|= WB_NOBORDER
;
220 nEditStyle
|= WB_BORDER
;
221 nListStyle
&= ~WB_NOBORDER
;
222 nListStyle
|= WB_BORDER
;
226 m_pImpl
->m_pSubEdit
.set( VclPtr
<Edit
>::Create( this, nEditStyle
) );
227 m_pImpl
->m_pSubEdit
->EnableRTL( false );
228 SetSubEdit( m_pImpl
->m_pSubEdit
);
229 m_pImpl
->m_pSubEdit
->SetPosPixel( Point() );
230 EnableAutocomplete( true );
231 m_pImpl
->m_pSubEdit
->Show();
233 vcl::Window
* pLBParent
= this;
234 if (m_pImpl
->m_pFloatWin
)
235 pLBParent
= m_pImpl
->m_pFloatWin
;
236 m_pImpl
->m_pImplLB
= VclPtr
<ImplListBox
>::Create( pLBParent
, nListStyle
|WB_SIMPLEMODE
|WB_AUTOHSCROLL
);
237 m_pImpl
->m_pImplLB
->SetPosPixel( Point() );
238 m_pImpl
->m_pImplLB
->SetSelectHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplSelectHdl
) );
239 m_pImpl
->m_pImplLB
->SetCancelHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplCancelHdl
) );
240 m_pImpl
->m_pImplLB
->SetDoubleClickHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplDoubleClickHdl
) );
241 m_pImpl
->m_pImplLB
->SetSelectionChangedHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplSelectionChangedHdl
) );
242 m_pImpl
->m_pImplLB
->SetListItemSelectHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplListItemSelectHdl
) );
243 m_pImpl
->m_pImplLB
->Show();
245 if (m_pImpl
->m_pFloatWin
)
246 m_pImpl
->m_pFloatWin
->SetImplListBox( m_pImpl
->m_pImplLB
);
248 GetMainWindow()->AllowGrabFocus( true );
250 ImplCalcEditHeight();
252 SetCompoundControl( true );
255 WinBits
ComboBox::ImplInitStyle( WinBits nStyle
)
257 if ( !(nStyle
& WB_NOTABSTOP
) )
258 nStyle
|= WB_TABSTOP
;
259 if ( !(nStyle
& WB_NOGROUP
) )
264 void ComboBox::EnableAutocomplete( bool bEnable
, bool bMatchCase
)
266 m_pImpl
->m_isMatchCase
= bMatchCase
;
269 m_pImpl
->m_pSubEdit
->SetAutocompleteHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplAutocompleteHdl
) );
271 m_pImpl
->m_pSubEdit
->SetAutocompleteHdl( Link
<Edit
&,void>() );
274 bool ComboBox::IsAutocompleteEnabled() const
276 return m_pImpl
->m_pSubEdit
->GetAutocompleteHdl().IsSet();
279 IMPL_LINK_NOARG(ComboBox::Impl
, ImplClickBtnHdl
, void*, void)
281 m_rThis
.CallEventListeners( VclEventId::DropdownPreOpen
);
282 m_pSubEdit
->GrabFocus();
283 if (!m_pImplLB
->GetEntryList().GetMRUCount())
284 ImplUpdateFloatSelection();
286 m_pImplLB
->SelectEntry( 0 , true );
287 m_pBtn
->SetPressed( true );
288 m_rThis
.SetSelection( Selection( 0, SELECTION_MAX
) );
289 m_pFloatWin
->StartFloat( true );
290 m_rThis
.CallEventListeners( VclEventId::DropdownOpen
);
292 m_rThis
.ImplClearLayoutData();
294 m_pImplLB
->GetMainWindow()->ImplClearLayoutData();
297 IMPL_LINK_NOARG(ComboBox::Impl
, ImplPopupModeEndHdl
, FloatingWindow
*, void)
299 if (m_pFloatWin
->IsPopupModeCanceled())
301 if (!m_pImplLB
->GetEntryList().IsEntryPosSelected(
302 m_pFloatWin
->GetPopupModeStartSaveSelection()))
304 m_pImplLB
->SelectEntry(m_pFloatWin
->GetPopupModeStartSaveSelection(), true);
305 bool bTravelSelect
= m_pImplLB
->IsTravelSelect();
306 m_pImplLB
->SetTravelSelect( true );
308 m_pImplLB
->SetTravelSelect( bTravelSelect
);
312 m_rThis
.ImplClearLayoutData();
314 m_pImplLB
->GetMainWindow()->ImplClearLayoutData();
316 m_pBtn
->SetPressed( false );
317 m_rThis
.CallEventListeners( VclEventId::DropdownClose
);
320 IMPL_LINK(ComboBox::Impl
, ImplAutocompleteHdl
, Edit
&, rEdit
, void)
322 Selection aSel
= rEdit
.GetSelection();
325 OUString aFullText
= rEdit
.GetText();
326 OUString aStartText
= aFullText
.copy( 0, static_cast<sal_Int32
>(aSel
.Max()) );
327 sal_Int32 nStart
= m_pImplLB
->GetCurrentPos();
329 if ( nStart
== LISTBOX_ENTRY_NOTFOUND
)
332 sal_Int32 nPos
= LISTBOX_ENTRY_NOTFOUND
;
335 // Try match case insensitive from current position
336 nPos
= m_pImplLB
->GetEntryList().FindMatchingEntry(aStartText
, nStart
, true);
337 if ( nPos
== LISTBOX_ENTRY_NOTFOUND
)
338 // Try match case insensitive, but from start
339 nPos
= m_pImplLB
->GetEntryList().FindMatchingEntry(aStartText
, 0, true);
342 if ( nPos
== LISTBOX_ENTRY_NOTFOUND
)
343 // Try match full from current position
344 nPos
= m_pImplLB
->GetEntryList().FindMatchingEntry(aStartText
, nStart
, false);
345 if ( nPos
== LISTBOX_ENTRY_NOTFOUND
)
346 // Match full, but from start
347 nPos
= m_pImplLB
->GetEntryList().FindMatchingEntry(aStartText
, 0, false);
349 if ( nPos
!= LISTBOX_ENTRY_NOTFOUND
)
351 OUString aText
= m_pImplLB
->GetEntryList().GetEntryText( nPos
);
352 Selection
aSelection( aText
.getLength(), aStartText
.getLength() );
353 rEdit
.SetText( aText
, aSelection
);
358 IMPL_LINK_NOARG(ComboBox::Impl
, ImplSelectHdl
, LinkParamNone
*, void)
360 bool bPopup
= m_rThis
.IsInDropDown();
361 bool bCallSelect
= false;
362 if (m_pImplLB
->IsSelectionChanged() || bPopup
)
365 if (m_rThis
.IsMultiSelectionEnabled())
367 aText
= m_pSubEdit
->GetText();
369 // remove all entries to which there is an entry, but which is not selected
370 sal_Int32 nIndex
= 0;
371 while ( nIndex
>= 0 )
373 sal_Int32 nPrevIndex
= nIndex
;
374 std::u16string_view aToken
= o3tl::getToken(aText
, 0, m_cMultiSep
, nIndex
);
375 sal_Int32 nTokenLen
= aToken
.size();
376 aToken
= comphelper::string::strip(aToken
, ' ');
377 sal_Int32 nP
= m_pImplLB
->GetEntryList().FindEntry( aToken
);
378 if ((nP
!= LISTBOX_ENTRY_NOTFOUND
) && (!m_pImplLB
->GetEntryList().IsEntryPosSelected(nP
)))
380 aText
= aText
.replaceAt( nPrevIndex
, nTokenLen
, u
"" );
381 nIndex
= nIndex
- nTokenLen
;
382 sal_Int32 nSepCount
=0;
383 if ((nPrevIndex
+nSepCount
< aText
.getLength()) && (aText
[nPrevIndex
+nSepCount
] == m_cMultiSep
))
388 aText
= aText
.replaceAt( nPrevIndex
, nSepCount
, u
"" );
390 aText
= comphelper::string::strip(aText
, ' ');
393 // attach missing entries
394 ::std::set
< sal_Int32
> aSelInText
;
395 lcl_GetSelectedEntries( aSelInText
, aText
, m_cMultiSep
, m_pImplLB
->GetEntryList() );
396 sal_Int32 nSelectedEntries
= m_pImplLB
->GetEntryList().GetSelectedEntryCount();
397 for ( sal_Int32 n
= 0; n
< nSelectedEntries
; n
++ )
399 sal_Int32 nP
= m_pImplLB
->GetEntryList().GetSelectedEntryPos( n
);
400 if ( !aSelInText
.count( nP
) )
402 if (!aText
.isEmpty() && (aText
[aText
.getLength()-1] != m_cMultiSep
))
403 aText
+= OUStringChar(m_cMultiSep
);
404 if ( !aText
.isEmpty() )
405 aText
+= " "; // slightly loosen
406 aText
+= m_pImplLB
->GetEntryList().GetEntryText( nP
) +
407 OUStringChar(m_cMultiSep
);
410 aText
= comphelper::string::stripEnd( aText
, m_cMultiSep
);
414 aText
= m_pImplLB
->GetEntryList().GetSelectedEntry( 0 );
417 m_pSubEdit
->SetText( aText
);
419 Selection
aNewSelection( 0, aText
.getLength() );
420 if (m_rThis
.IsMultiSelectionEnabled())
421 aNewSelection
.Min() = aText
.getLength();
422 m_pSubEdit
->SetSelection( aNewSelection
);
427 // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
428 bool bMenuSelect
= bPopup
&& !m_pImplLB
->IsTravelSelect() && (!m_rThis
.IsMultiSelectionEnabled() || !m_pImplLB
->GetSelectModifier());
431 m_pFloatWin
->EndPopupMode();
437 m_isKeyBoardModify
= !bMenuSelect
;
438 m_pSubEdit
->SetModifyFlag();
439 m_isSyntheticModify
= true;
441 m_isSyntheticModify
= false;
443 m_isKeyBoardModify
= false;
447 bool ComboBox::IsSyntheticModify() const
449 return m_pImpl
->m_isSyntheticModify
;
452 bool ComboBox::IsModifyByKeyboard() const
454 return m_pImpl
->m_isKeyBoardModify
;
457 IMPL_LINK_NOARG( ComboBox::Impl
, ImplListItemSelectHdl
, LinkParamNone
*, void )
459 m_rThis
.CallEventListeners( VclEventId::DropdownSelect
);
462 IMPL_LINK_NOARG(ComboBox::Impl
, ImplCancelHdl
, LinkParamNone
*, void)
464 if (m_rThis
.IsInDropDown())
465 m_pFloatWin
->EndPopupMode();
468 IMPL_LINK( ComboBox::Impl
, ImplSelectionChangedHdl
, sal_Int32
, nChanged
, void )
470 if (!m_pImplLB
->IsTrackingSelect())
472 if (!m_pSubEdit
->IsReadOnly() && m_pImplLB
->GetEntryList().IsEntryPosSelected(nChanged
))
473 m_pSubEdit
->SetText(m_pImplLB
->GetEntryList().GetEntryText(nChanged
));
477 IMPL_LINK_NOARG(ComboBox::Impl
, ImplDoubleClickHdl
, ImplListBoxWindow
*, void)
479 m_rThis
.DoubleClick();
482 void ComboBox::ToggleDropDown()
484 if( !IsDropDownBox() )
487 if (m_pImpl
->m_pFloatWin
->IsInPopupMode())
488 m_pImpl
->m_pFloatWin
->EndPopupMode();
491 m_pImpl
->m_pSubEdit
->GrabFocus();
492 if (!m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount())
493 m_pImpl
->ImplUpdateFloatSelection();
495 m_pImpl
->m_pImplLB
->SelectEntry( 0 , true );
496 CallEventListeners( VclEventId::DropdownPreOpen
);
497 m_pImpl
->m_pBtn
->SetPressed( true );
498 SetSelection( Selection( 0, SELECTION_MAX
) );
499 m_pImpl
->m_pFloatWin
->StartFloat( true );
500 CallEventListeners( VclEventId::DropdownOpen
);
504 void ComboBox::Select()
506 ImplCallEventListenersAndHandler( VclEventId::ComboboxSelect
, [this] () { m_pImpl
->m_SelectHdl
.Call(*this); } );
509 void ComboBox::DoubleClick()
511 ImplCallEventListenersAndHandler( VclEventId::ComboboxDoubleClick
, [] () {} );
514 bool ComboBox::IsAutoSizeEnabled() const { return m_pImpl
->m_isDDAutoSize
; }
516 void ComboBox::EnableAutoSize( bool bAuto
)
518 m_pImpl
->m_isDDAutoSize
= bAuto
;
519 if (m_pImpl
->m_pFloatWin
)
521 if (bAuto
&& !m_pImpl
->m_pFloatWin
->GetDropDownLineCount())
523 // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
524 AdaptDropDownLineCountToMaximum();
528 m_pImpl
->m_pFloatWin
->SetDropDownLineCount( 0 );
533 void ComboBox::SetDropDownLineCount( sal_uInt16 nLines
)
535 if (m_pImpl
->m_pFloatWin
)
536 m_pImpl
->m_pFloatWin
->SetDropDownLineCount( nLines
);
539 void ComboBox::AdaptDropDownLineCountToMaximum()
541 // Adapt to maximum allowed number.
542 // Limit for LOK as we can't render outside of the dialog canvas.
543 if (comphelper::LibreOfficeKit::isActive())
544 SetDropDownLineCount(11);
546 SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
549 sal_uInt16
ComboBox::GetDropDownLineCount() const
551 sal_uInt16 nLines
= 0;
552 if (m_pImpl
->m_pFloatWin
)
553 nLines
= m_pImpl
->m_pFloatWin
->GetDropDownLineCount();
557 void ComboBox::setPosSizePixel( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
,
558 PosSizeFlags nFlags
)
560 if( IsDropDownBox() && ( nFlags
& PosSizeFlags::Size
) )
562 Size aPrefSz
= m_pImpl
->m_pFloatWin
->GetPrefSize();
563 if ((nFlags
& PosSizeFlags::Height
) && (nHeight
>= 2*m_pImpl
->m_nDDHeight
))
564 aPrefSz
.setHeight( nHeight
-m_pImpl
->m_nDDHeight
);
565 if ( nFlags
& PosSizeFlags::Width
)
566 aPrefSz
.setWidth( nWidth
);
567 m_pImpl
->m_pFloatWin
->SetPrefSize( aPrefSz
);
569 if (IsAutoSizeEnabled())
570 nHeight
= m_pImpl
->m_nDDHeight
;
573 Edit::setPosSizePixel( nX
, nY
, nWidth
, nHeight
, nFlags
);
576 void ComboBox::Resize()
580 if (m_pImpl
->m_pSubEdit
)
582 Size aOutSz
= GetOutputSizePixel();
583 if( IsDropDownBox() )
585 ComboBoxBounds
aBounds(m_pImpl
->calcComboBoxDropDownComponentBounds(aOutSz
,
586 GetWindow(GetWindowType::Border
)->GetOutputSizePixel()));
587 m_pImpl
->m_pSubEdit
->SetPosSizePixel(aBounds
.aSubEditPos
, aBounds
.aSubEditSize
);
588 m_pImpl
->m_pBtn
->SetPosSizePixel(aBounds
.aButtonPos
, aBounds
.aButtonSize
);
592 m_pImpl
->m_pSubEdit
->SetSizePixel(Size(aOutSz
.Width(), m_pImpl
->m_nDDHeight
));
593 m_pImpl
->m_pImplLB
->setPosSizePixel(0, m_pImpl
->m_nDDHeight
,
594 aOutSz
.Width(), aOutSz
.Height() - m_pImpl
->m_nDDHeight
);
595 if ( !GetText().isEmpty() )
596 m_pImpl
->ImplUpdateFloatSelection();
600 // adjust the size of the FloatingWindow even when invisible
601 // as KEY_PGUP/DOWN is being processed...
602 if (m_pImpl
->m_pFloatWin
)
603 m_pImpl
->m_pFloatWin
->SetSizePixel(m_pImpl
->m_pFloatWin
->CalcFloatSize());
606 bool ComboBox::IsDropDownBox() const { return m_pImpl
->m_pFloatWin
!= nullptr; }
608 void ComboBox::FillLayoutData() const
610 mxLayoutData
.emplace();
611 AppendLayoutData( *m_pImpl
->m_pSubEdit
);
612 m_pImpl
->m_pSubEdit
->SetLayoutDataParent( this );
613 ImplListBoxWindow
* rMainWindow
= GetMainWindow();
614 if (m_pImpl
->m_pFloatWin
)
617 if (m_pImpl
->m_pFloatWin
->IsReallyVisible())
619 AppendLayoutData( *rMainWindow
);
620 rMainWindow
->SetLayoutDataParent( this );
625 AppendLayoutData( *rMainWindow
);
626 rMainWindow
->SetLayoutDataParent( this );
630 void ComboBox::StateChanged( StateChangedType nType
)
632 Edit::StateChanged( nType
);
634 if ( nType
== StateChangedType::ReadOnly
)
636 m_pImpl
->m_pImplLB
->SetReadOnly( IsReadOnly() );
638 m_pImpl
->m_pBtn
->Enable( IsEnabled() && !IsReadOnly() );
640 else if ( nType
== StateChangedType::Enable
)
642 m_pImpl
->m_pSubEdit
->Enable( IsEnabled() );
643 m_pImpl
->m_pImplLB
->Enable( IsEnabled() && !IsReadOnly() );
645 m_pImpl
->m_pBtn
->Enable( IsEnabled() && !IsReadOnly() );
648 else if( nType
== StateChangedType::UpdateMode
)
650 m_pImpl
->m_pImplLB
->SetUpdateMode( IsUpdateMode() );
652 else if ( nType
== StateChangedType::Zoom
)
654 m_pImpl
->m_pImplLB
->SetZoom( GetZoom() );
655 m_pImpl
->m_pSubEdit
->SetZoom( GetZoom() );
656 ImplCalcEditHeight();
659 else if ( nType
== StateChangedType::ControlFont
)
661 m_pImpl
->m_pImplLB
->SetControlFont( GetControlFont() );
662 m_pImpl
->m_pSubEdit
->SetControlFont( GetControlFont() );
663 ImplCalcEditHeight();
666 else if ( nType
== StateChangedType::ControlForeground
)
668 m_pImpl
->m_pImplLB
->SetControlForeground( GetControlForeground() );
669 m_pImpl
->m_pSubEdit
->SetControlForeground( GetControlForeground() );
671 else if ( nType
== StateChangedType::ControlBackground
)
673 m_pImpl
->m_pImplLB
->SetControlBackground( GetControlBackground() );
674 m_pImpl
->m_pSubEdit
->SetControlBackground( GetControlBackground() );
676 else if ( nType
== StateChangedType::Style
)
678 SetStyle( ImplInitStyle( GetStyle() ) );
679 GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT
) != 0 );
681 else if( nType
== StateChangedType::Mirroring
)
685 m_pImpl
->m_pBtn
->EnableRTL( IsRTLEnabled() );
686 ImplInitDropDownButton( m_pImpl
->m_pBtn
);
688 m_pImpl
->m_pSubEdit
->CompatStateChanged( StateChangedType::Mirroring
);
689 m_pImpl
->m_pImplLB
->EnableRTL( IsRTLEnabled() );
694 void ComboBox::DataChanged( const DataChangedEvent
& rDCEvt
)
696 Control::DataChanged( rDCEvt
);
698 if ( !((rDCEvt
.GetType() == DataChangedEventType::FONTS
) ||
699 (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
700 ((rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
701 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
))) )
706 m_pImpl
->m_pBtn
->GetOutDev()->SetSettings( GetSettings() );
707 ImplInitDropDownButton( m_pImpl
->m_pBtn
);
710 m_pImpl
->m_pImplLB
->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
712 SetBackground(); // due to a hack in Window::UpdateSettings the background must be reset
713 // otherwise it will overpaint NWF drawn comboboxes
716 bool ComboBox::EventNotify( NotifyEvent
& rNEvt
)
719 if ((rNEvt
.GetType() == NotifyEventType::KEYINPUT
)
720 && (rNEvt
.GetWindow() == m_pImpl
->m_pSubEdit
)
723 KeyEvent aKeyEvt
= *rNEvt
.GetKeyEvent();
724 sal_uInt16 nKeyCode
= aKeyEvt
.GetKeyCode().GetCode();
732 m_pImpl
->ImplUpdateFloatSelection();
733 if ((nKeyCode
== KEY_DOWN
) && m_pImpl
->m_pFloatWin
734 && !m_pImpl
->m_pFloatWin
->IsInPopupMode()
735 && aKeyEvt
.GetKeyCode().IsMod2())
737 CallEventListeners( VclEventId::DropdownPreOpen
);
738 m_pImpl
->m_pBtn
->SetPressed( true );
739 if (m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount())
740 m_pImpl
->m_pImplLB
->SelectEntry( 0 , true );
741 SetSelection( Selection( 0, SELECTION_MAX
) );
742 m_pImpl
->m_pFloatWin
->StartFloat( false );
743 CallEventListeners( VclEventId::DropdownOpen
);
746 else if ((nKeyCode
== KEY_UP
) && m_pImpl
->m_pFloatWin
747 && m_pImpl
->m_pFloatWin
->IsInPopupMode()
748 && aKeyEvt
.GetKeyCode().IsMod2())
750 m_pImpl
->m_pFloatWin
->EndPopupMode();
755 bDone
= m_pImpl
->m_pImplLB
->ProcessKeyInput( aKeyEvt
);
762 if ((rNEvt
.GetWindow() == m_pImpl
->m_pSubEdit
) && IsInDropDown())
764 m_pImpl
->m_pImplLB
->ProcessKeyInput( aKeyEvt
);
771 else if ((rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
) && m_pImpl
->m_pFloatWin
)
773 if (m_pImpl
->m_pFloatWin
->HasChildPathFocus())
774 m_pImpl
->m_pSubEdit
->GrabFocus();
775 else if (m_pImpl
->m_pFloatWin
->IsInPopupMode() && !HasChildPathFocus(true))
776 m_pImpl
->m_pFloatWin
->EndPopupMode();
778 else if( (rNEvt
.GetType() == NotifyEventType::COMMAND
) &&
779 (rNEvt
.GetCommandEvent()->GetCommand() == CommandEventId::Wheel
) &&
780 (rNEvt
.GetWindow() == m_pImpl
->m_pSubEdit
) )
782 MouseWheelBehaviour
nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
783 if ( ( nWheelBehavior
== MouseWheelBehaviour::ALWAYS
)
784 || ( ( nWheelBehavior
== MouseWheelBehaviour::FocusOnly
)
785 && HasChildPathFocus()
789 bDone
= m_pImpl
->m_pImplLB
->HandleWheelAsCursorTravel(*rNEvt
.GetCommandEvent(), *this);
793 bDone
= false; // don't eat this event, let the default handling happen (i.e. scroll the context)
796 else if ((rNEvt
.GetType() == NotifyEventType::MOUSEBUTTONDOWN
)
797 && (rNEvt
.GetWindow() == GetMainWindow()))
799 m_pImpl
->m_pSubEdit
->GrabFocus();
802 return bDone
|| Edit::EventNotify( rNEvt
);
805 void ComboBox::SetText( const OUString
& rStr
)
807 CallEventListeners( VclEventId::ComboboxSetText
);
809 Edit::SetText( rStr
);
810 m_pImpl
->ImplUpdateFloatSelection();
813 void ComboBox::SetText( const OUString
& rStr
, const Selection
& rNewSelection
)
815 CallEventListeners( VclEventId::ComboboxSetText
);
817 Edit::SetText( rStr
, rNewSelection
);
818 m_pImpl
->ImplUpdateFloatSelection();
821 void ComboBox::Modify()
823 if (!m_pImpl
->m_isSyntheticModify
)
824 m_pImpl
->ImplUpdateFloatSelection();
829 void ComboBox::Impl::ImplUpdateFloatSelection()
831 if (!m_pImplLB
|| !m_pSubEdit
)
834 // move text in the ListBox into the visible region
835 m_pImplLB
->SetCallSelectionChangedHdl( false );
836 if (!m_rThis
.IsMultiSelectionEnabled())
838 OUString
aSearchStr( m_pSubEdit
->GetText() );
839 sal_Int32 nSelect
= LISTBOX_ENTRY_NOTFOUND
;
842 if (m_pImplLB
->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND
)
844 OUString aCurrent
= m_pImplLB
->GetEntryList().GetEntryText(
845 m_pImplLB
->GetCurrentPos());
846 if ( aCurrent
== aSearchStr
)
847 nSelect
= m_pImplLB
->GetCurrentPos();
850 if ( nSelect
== LISTBOX_ENTRY_NOTFOUND
)
851 nSelect
= m_pImplLB
->GetEntryList().FindEntry( aSearchStr
);
852 if ( nSelect
== LISTBOX_ENTRY_NOTFOUND
)
854 nSelect
= m_pImplLB
->GetEntryList().FindMatchingEntry( aSearchStr
, 0, true );
858 if( nSelect
!= LISTBOX_ENTRY_NOTFOUND
)
860 if (!m_pImplLB
->IsVisible(nSelect
))
861 m_pImplLB
->ShowProminentEntry( nSelect
);
862 m_pImplLB
->SelectEntry( nSelect
, bSelect
);
866 nSelect
= m_pImplLB
->GetEntryList().GetSelectedEntryPos( 0 );
867 if( nSelect
!= LISTBOX_ENTRY_NOTFOUND
)
868 m_pImplLB
->SelectEntry( nSelect
, false );
869 m_pImplLB
->ResetCurrentPos();
874 ::std::set
< sal_Int32
> aSelInText
;
875 lcl_GetSelectedEntries(aSelInText
, m_pSubEdit
->GetText(), m_cMultiSep
, m_pImplLB
->GetEntryList());
876 for (sal_Int32 n
= 0; n
< m_pImplLB
->GetEntryList().GetEntryCount(); n
++)
877 m_pImplLB
->SelectEntry( n
, aSelInText
.count( n
) != 0 );
879 m_pImplLB
->SetCallSelectionChangedHdl( true );
882 sal_Int32
ComboBox::InsertEntry(const OUString
& rStr
, sal_Int32
const nPos
)
884 assert(nPos
>= 0 && COMBOBOX_MAX_ENTRIES
> m_pImpl
->m_pImplLB
->GetEntryList().GetEntryCount());
887 if (nPos
== COMBOBOX_APPEND
)
891 const sal_Int32 nMRUCount
= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
892 assert(nPos
<= COMBOBOX_MAX_ENTRIES
- nMRUCount
);
893 nRealPos
= nPos
+ nMRUCount
;
896 nRealPos
= m_pImpl
->m_pImplLB
->InsertEntry( nRealPos
, rStr
);
897 nRealPos
-= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
898 CallEventListeners( VclEventId::ComboboxItemAdded
, reinterpret_cast<void*>(nRealPos
) );
902 sal_Int32
ComboBox::InsertEntryWithImage(
903 const OUString
& rStr
, const Image
& rImage
, sal_Int32
const nPos
)
905 assert(nPos
>= 0 && COMBOBOX_MAX_ENTRIES
> m_pImpl
->m_pImplLB
->GetEntryList().GetEntryCount());
908 if (nPos
== COMBOBOX_APPEND
)
912 const sal_Int32 nMRUCount
= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
913 assert(nPos
<= COMBOBOX_MAX_ENTRIES
- nMRUCount
);
914 nRealPos
= nPos
+ nMRUCount
;
917 nRealPos
= m_pImpl
->m_pImplLB
->InsertEntry( nRealPos
, rStr
, rImage
);
918 nRealPos
-= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
919 CallEventListeners( VclEventId::ComboboxItemAdded
, reinterpret_cast<void*>(nRealPos
) );
923 void ComboBox::RemoveEntryAt(sal_Int32
const nPos
)
925 const sal_Int32 nMRUCount
= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
926 assert(nPos
>= 0 && nPos
<= COMBOBOX_MAX_ENTRIES
- nMRUCount
);
927 m_pImpl
->m_pImplLB
->RemoveEntry( nPos
+ nMRUCount
);
928 CallEventListeners( VclEventId::ComboboxItemRemoved
, reinterpret_cast<void*>(nPos
) );
931 void ComboBox::Clear()
933 if (!m_pImpl
->m_pImplLB
)
935 m_pImpl
->m_pImplLB
->Clear();
936 CallEventListeners( VclEventId::ComboboxItemRemoved
, reinterpret_cast<void*>(-1) );
939 Image
ComboBox::GetEntryImage( sal_Int32 nPos
) const
941 if (m_pImpl
->m_pImplLB
->GetEntryList().HasEntryImage(nPos
))
942 return m_pImpl
->m_pImplLB
->GetEntryList().GetEntryImage( nPos
);
946 sal_Int32
ComboBox::GetEntryPos( std::u16string_view rStr
) const
948 sal_Int32 nPos
= m_pImpl
->m_pImplLB
->GetEntryList().FindEntry( rStr
);
949 if ( nPos
!= LISTBOX_ENTRY_NOTFOUND
)
950 nPos
-= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
954 OUString
ComboBox::GetEntry( sal_Int32 nPos
) const
956 const sal_Int32 nMRUCount
= m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
957 if (nPos
< 0 || nPos
> COMBOBOX_MAX_ENTRIES
- nMRUCount
)
960 return m_pImpl
->m_pImplLB
->GetEntryList().GetEntryText( nPos
+ nMRUCount
);
963 sal_Int32
ComboBox::GetEntryCount() const
965 if (!m_pImpl
->m_pImplLB
)
967 return m_pImpl
->m_pImplLB
->GetEntryList().GetEntryCount() - m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount();
970 bool ComboBox::IsTravelSelect() const
972 return m_pImpl
->m_pImplLB
->IsTravelSelect();
975 bool ComboBox::IsInDropDown() const
977 // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
978 // mbPopupMode is set to false
979 return m_pImpl
->m_pFloatWin
&& m_pImpl
->m_pFloatWin
->IsInPopupMode() && m_pImpl
->m_pFloatWin
->ImplIsInPrivatePopupMode();
982 bool ComboBox::IsMultiSelectionEnabled() const
984 return m_pImpl
->m_pImplLB
->IsMultiSelectionEnabled();
987 void ComboBox::SetSelectHdl(const Link
<ComboBox
&,void>& rLink
) { m_pImpl
->m_SelectHdl
= rLink
; }
989 void ComboBox::SetEntryActivateHdl(const Link
<Edit
&,bool>& rLink
)
991 if (!m_pImpl
->m_pSubEdit
)
993 m_pImpl
->m_pSubEdit
->SetActivateHdl(rLink
);
996 Size
ComboBox::GetOptimalSize() const
998 return CalcMinimumSize();
1001 tools::Long
ComboBox::getMaxWidthScrollBarAndDownButton() const
1003 tools::Long nButtonDownWidth
= 0;
1005 vcl::Window
*pBorder
= GetWindow( GetWindowType::Border
);
1006 ImplControlValue aControlValue
;
1007 tools::Rectangle aContent
, aBound
;
1009 // use the full extent of the control
1010 tools::Rectangle
aArea( Point(), pBorder
->GetOutputSizePixel() );
1012 if ( GetNativeControlRegion(ControlType::Combobox
, ControlPart::ButtonDown
,
1013 aArea
, ControlState::NONE
, aControlValue
, aBound
, aContent
) )
1015 nButtonDownWidth
= aContent
.getOpenWidth();
1018 tools::Long nScrollBarWidth
= GetSettings().GetStyleSettings().GetScrollBarSize();
1020 return std::max(nScrollBarWidth
, nButtonDownWidth
);
1023 Size
ComboBox::CalcMinimumSize() const
1027 if (!m_pImpl
->m_pImplLB
)
1030 if (!IsDropDownBox())
1032 aSz
= m_pImpl
->m_pImplLB
->CalcSize( m_pImpl
->m_pImplLB
->GetEntryList().GetEntryCount() );
1033 aSz
.AdjustHeight(m_pImpl
->m_nDDHeight
);
1037 aSz
.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1039 if (m_pImpl
->m_nWidthInChars
!= -1)
1040 aSz
.setWidth(m_pImpl
->m_nWidthInChars
* approximate_digit_width());
1042 aSz
.setWidth(m_pImpl
->m_pImplLB
->GetMaxEntryWidth());
1045 if (m_pImpl
->m_nMaxWidthChars
!= -1)
1047 tools::Long nMaxWidth
= m_pImpl
->m_nMaxWidthChars
* approximate_char_width();
1048 aSz
.setWidth( std::min(aSz
.Width(), nMaxWidth
) );
1051 if (IsDropDownBox())
1052 aSz
.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1054 ComboBoxBounds
aBounds(m_pImpl
->calcComboBoxDropDownComponentBounds(
1055 Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1056 aSz
.AdjustWidth(aBounds
.aSubEditPos
.X()*2 );
1058 aSz
.AdjustWidth(ImplGetExtraXOffset() * 2 );
1060 aSz
= CalcWindowSize( aSz
);
1064 Size
ComboBox::CalcAdjustedSize( const Size
& rPrefSize
) const
1066 Size aSz
= rPrefSize
;
1067 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
1068 static_cast<vcl::Window
*>(const_cast<ComboBox
*>(this))->GetBorder( nLeft
, nTop
, nRight
, nBottom
);
1069 aSz
.AdjustHeight( -(nTop
+nBottom
) );
1070 if ( !IsDropDownBox() )
1072 tools::Long nEntryHeight
= CalcBlockSize( 1, 1 ).Height();
1073 tools::Long nLines
= aSz
.Height() / nEntryHeight
;
1076 aSz
.setHeight( nLines
* nEntryHeight
);
1077 aSz
.AdjustHeight(m_pImpl
->m_nDDHeight
);
1081 aSz
.setHeight( m_pImpl
->m_nDDHeight
);
1083 aSz
.AdjustHeight(nTop
+nBottom
);
1085 aSz
= CalcWindowSize( aSz
);
1089 Size
ComboBox::CalcBlockSize( sal_uInt16 nColumns
, sal_uInt16 nLines
) const
1091 // show ScrollBars where appropriate
1092 Size aMinSz
= CalcMinimumSize();
1098 if ( !IsDropDownBox() )
1099 aSz
.setHeight( m_pImpl
->m_pImplLB
->CalcSize( nLines
).Height() + m_pImpl
->m_nDDHeight
);
1101 aSz
.setHeight( m_pImpl
->m_nDDHeight
);
1104 aSz
.setHeight( aMinSz
.Height() );
1108 aSz
.setWidth( nColumns
* approximate_char_width() );
1110 aSz
.setWidth( aMinSz
.Width() );
1112 if ( IsDropDownBox() )
1113 aSz
.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1115 if ( !IsDropDownBox() )
1117 if ( aSz
.Width() < aMinSz
.Width() )
1118 aSz
.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1119 if ( aSz
.Height() < aMinSz
.Height() )
1120 aSz
.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1123 aSz
.AdjustWidth(ImplGetExtraXOffset() * 2 );
1125 aSz
= CalcWindowSize( aSz
);
1129 tools::Long
ComboBox::GetDropDownEntryHeight() const
1131 return m_pImpl
->m_pImplLB
->GetEntryHeight();
1134 void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16
& rnCols
, sal_uInt16
& rnLines
) const
1136 tools::Long nCharWidth
= GetTextWidth(OUString(u
'x'));
1137 if ( !IsDropDownBox() )
1139 Size aOutSz
= GetMainWindow()->GetOutputSizePixel();
1140 rnCols
= (nCharWidth
> 0) ? static_cast<sal_uInt16
>(aOutSz
.Width()/nCharWidth
) : 1;
1141 rnLines
= static_cast<sal_uInt16
>(aOutSz
.Height()/GetDropDownEntryHeight());
1145 Size aOutSz
= m_pImpl
->m_pSubEdit
->GetOutputSizePixel();
1146 rnCols
= (nCharWidth
> 0) ? static_cast<sal_uInt16
>(aOutSz
.Width()/nCharWidth
) : 1;
1151 void ComboBox::Draw( OutputDevice
* pDev
, const Point
& rPos
, SystemTextColorFlags nFlags
)
1153 GetMainWindow()->ApplySettings(*pDev
);
1155 Point aPos
= pDev
->LogicToPixel( rPos
);
1156 Size aSize
= GetSizePixel();
1157 vcl::Font aFont
= GetMainWindow()->GetDrawPixelFont( pDev
);
1161 pDev
->SetFont( aFont
);
1162 pDev
->SetTextFillColor();
1164 // Border/Background
1165 pDev
->SetLineColor();
1166 pDev
->SetFillColor();
1167 bool bBorder
= (GetStyle() & WB_BORDER
);
1168 bool bBackground
= IsControlBackground();
1169 if ( bBorder
|| bBackground
)
1171 tools::Rectangle
aRect( aPos
, aSize
);
1172 // aRect.Top() += nEditHeight;
1175 ImplDrawFrame( pDev
, aRect
);
1179 pDev
->SetFillColor( GetControlBackground() );
1180 pDev
->DrawRect( aRect
);
1185 if ( !IsDropDownBox() )
1187 tools::Long nOnePixel
= GetDrawPixel( pDev
, 1 );
1188 tools::Long nTextHeight
= pDev
->GetTextHeight();
1189 tools::Long nEditHeight
= nTextHeight
+ 6*nOnePixel
;
1190 DrawTextFlags nTextStyle
= DrawTextFlags::VCenter
;
1192 // First, draw the edit part
1193 Size
aOrigSize(m_pImpl
->m_pSubEdit
->GetSizePixel());
1194 m_pImpl
->m_pSubEdit
->SetSizePixel(Size(aSize
.Width(), nEditHeight
));
1195 m_pImpl
->m_pSubEdit
->Draw( pDev
, aPos
, nFlags
);
1196 m_pImpl
->m_pSubEdit
->SetSizePixel(aOrigSize
);
1198 // Second, draw the listbox
1199 if ( GetStyle() & WB_CENTER
)
1200 nTextStyle
|= DrawTextFlags::Center
;
1201 else if ( GetStyle() & WB_RIGHT
)
1202 nTextStyle
|= DrawTextFlags::Right
;
1204 nTextStyle
|= DrawTextFlags::Left
;
1206 if ( nFlags
& SystemTextColorFlags::Mono
)
1208 pDev
->SetTextColor( COL_BLACK
);
1214 const StyleSettings
& rStyleSettings
= GetSettings().GetStyleSettings();
1215 pDev
->SetTextColor( rStyleSettings
.GetDisableColor() );
1219 pDev
->SetTextColor( GetTextColor() );
1223 tools::Rectangle
aClip( aPos
, aSize
);
1224 pDev
->IntersectClipRegion( aClip
);
1225 sal_Int32 nLines
= static_cast<sal_Int32
>( nTextHeight
> 0 ? (aSize
.Height()-nEditHeight
)/nTextHeight
: 1 );
1228 const sal_Int32 nTEntry
= IsReallyVisible() ? m_pImpl
->m_pImplLB
->GetTopEntry() : 0;
1230 tools::Rectangle
aTextRect( aPos
, aSize
);
1232 aTextRect
.AdjustLeft(3*nOnePixel
);
1233 aTextRect
.AdjustRight( -(3*nOnePixel
) );
1234 aTextRect
.AdjustTop(nEditHeight
+ nOnePixel
);
1235 aTextRect
.SetBottom( aTextRect
.Top() + nTextHeight
);
1237 // the drawing starts here
1238 for ( sal_Int32 n
= 0; n
< nLines
; ++n
)
1240 pDev
->DrawText( aTextRect
, m_pImpl
->m_pImplLB
->GetEntryList().GetEntryText( n
+nTEntry
), nTextStyle
);
1241 aTextRect
.AdjustTop(nTextHeight
);
1242 aTextRect
.AdjustBottom(nTextHeight
);
1248 // Call Edit::Draw after restoring the MapMode...
1249 if ( IsDropDownBox() )
1251 Size
aOrigSize(m_pImpl
->m_pSubEdit
->GetSizePixel());
1252 m_pImpl
->m_pSubEdit
->SetSizePixel(GetSizePixel());
1253 m_pImpl
->m_pSubEdit
->Draw( pDev
, rPos
, nFlags
);
1254 m_pImpl
->m_pSubEdit
->SetSizePixel(aOrigSize
);
1259 void ComboBox::SetUserDrawHdl(const Link
<UserDrawEvent
*, void>& rLink
)
1261 m_pImpl
->m_pImplLB
->SetUserDrawHdl(rLink
);
1264 void ComboBox::SetUserItemSize( const Size
& rSz
)
1266 GetMainWindow()->SetUserItemSize( rSz
);
1269 void ComboBox::EnableUserDraw( bool bUserDraw
)
1271 GetMainWindow()->EnableUserDraw( bUserDraw
);
1274 bool ComboBox::IsUserDrawEnabled() const
1276 return GetMainWindow()->IsUserDrawEnabled();
1279 void ComboBox::DrawEntry(const UserDrawEvent
& rEvt
)
1281 GetMainWindow()->DrawEntry(*rEvt
.GetRenderContext(), rEvt
.GetItemId(), /*bDrawImage*/false, /*bDrawText*/false);
1284 void ComboBox::AddSeparator( sal_Int32 n
)
1286 m_pImpl
->m_pImplLB
->AddSeparator( n
);
1289 void ComboBox::SetMRUEntries( std::u16string_view rEntries
)
1291 m_pImpl
->m_pImplLB
->SetMRUEntries( rEntries
, ';' );
1294 OUString
ComboBox::GetMRUEntries() const
1296 return m_pImpl
->m_pImplLB
? m_pImpl
->m_pImplLB
->GetMRUEntries( ';' ) : OUString();
1299 void ComboBox::SetMaxMRUCount( sal_Int32 n
)
1301 m_pImpl
->m_pImplLB
->SetMaxMRUCount( n
);
1304 sal_Int32
ComboBox::GetMaxMRUCount() const
1306 return m_pImpl
->m_pImplLB
? m_pImpl
->m_pImplLB
->GetMaxMRUCount() : 0;
1309 sal_uInt16
ComboBox::GetDisplayLineCount() const
1311 return m_pImpl
->m_pImplLB
? m_pImpl
->m_pImplLB
->GetDisplayLineCount() : 0;
1314 void ComboBox::SetEntryData( sal_Int32 nPos
, void* pNewData
)
1316 m_pImpl
->m_pImplLB
->SetEntryData( nPos
+ m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount(), pNewData
);
1319 void* ComboBox::GetEntryData( sal_Int32 nPos
) const
1321 return m_pImpl
->m_pImplLB
->GetEntryList().GetEntryData(
1322 nPos
+ m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount() );
1325 sal_Int32
ComboBox::GetTopEntry() const
1327 sal_Int32 nPos
= GetEntryCount() ? m_pImpl
->m_pImplLB
->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND
;
1328 if (nPos
< m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount())
1333 tools::Rectangle
ComboBox::GetDropDownPosSizePixel() const
1335 return m_pImpl
->m_pFloatWin
1336 ? m_pImpl
->m_pFloatWin
->GetWindowExtentsRelative(this)
1337 : tools::Rectangle();
1340 const Wallpaper
& ComboBox::GetDisplayBackground() const
1342 if (!m_pImpl
->m_pSubEdit
->IsBackground())
1343 return Control::GetDisplayBackground();
1345 const Wallpaper
& rBack
= m_pImpl
->m_pSubEdit
->GetBackground();
1346 if( ! rBack
.IsBitmap() &&
1347 ! rBack
.IsGradient() &&
1348 rBack
== Wallpaper(COL_TRANSPARENT
)
1350 return Control::GetDisplayBackground();
1354 sal_Int32
ComboBox::GetSelectedEntryCount() const
1356 return m_pImpl
->m_pImplLB
->GetEntryList().GetSelectedEntryCount();
1359 sal_Int32
ComboBox::GetSelectedEntryPos( sal_Int32 nIndex
) const
1361 sal_Int32 nPos
= m_pImpl
->m_pImplLB
->GetEntryList().GetSelectedEntryPos( nIndex
);
1362 if ( nPos
!= LISTBOX_ENTRY_NOTFOUND
)
1364 if (nPos
< m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount())
1365 nPos
= m_pImpl
->m_pImplLB
->GetEntryList().FindEntry(m_pImpl
->m_pImplLB
->GetEntryList().GetEntryText(nPos
));
1366 nPos
= sal::static_int_cast
<sal_Int32
>(nPos
- m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount());
1371 bool ComboBox::IsEntryPosSelected( sal_Int32 nPos
) const
1373 return m_pImpl
->m_pImplLB
->GetEntryList().IsEntryPosSelected(
1374 nPos
+ m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount() );
1377 void ComboBox::SelectEntryPos( sal_Int32 nPos
, bool bSelect
)
1379 if (nPos
< m_pImpl
->m_pImplLB
->GetEntryList().GetEntryCount())
1380 m_pImpl
->m_pImplLB
->SelectEntry(
1381 nPos
+ m_pImpl
->m_pImplLB
->GetEntryList().GetMRUCount(), bSelect
);
1384 void ComboBox::SetNoSelection()
1386 m_pImpl
->m_pImplLB
->SetNoSelection();
1387 m_pImpl
->m_pSubEdit
->SetText( OUString() );
1390 tools::Rectangle
ComboBox::GetBoundingRectangle( sal_Int32 nItem
) const
1392 tools::Rectangle aRect
= GetMainWindow()->GetBoundingRectangle( nItem
);
1393 tools::Rectangle aOffset
= GetMainWindow()->GetWindowExtentsRelative( static_cast<vcl::Window
*>(const_cast<ComboBox
*>(this)) );
1394 aRect
.Move( aOffset
.Left(), aOffset
.Top() );
1398 void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle
)
1400 Window::SetBorderStyle( nBorderStyle
);
1401 if ( !IsDropDownBox() )
1403 m_pImpl
->m_pSubEdit
->SetBorderStyle( nBorderStyle
);
1404 m_pImpl
->m_pImplLB
->SetBorderStyle( nBorderStyle
);
1408 void ComboBox::SetHighlightColor( const Color
& rColor
)
1410 AllSettings
aSettings(GetSettings());
1411 StyleSettings
aStyle(aSettings
.GetStyleSettings());
1412 aStyle
.SetHighlightColor(rColor
);
1413 aSettings
.SetStyleSettings(aStyle
);
1414 SetSettings(aSettings
);
1416 AllSettings
aSettingsSubEdit(m_pImpl
->m_pSubEdit
->GetSettings());
1417 StyleSettings
aStyleSubEdit(aSettingsSubEdit
.GetStyleSettings());
1418 aStyleSubEdit
.SetHighlightColor(rColor
);
1419 aSettingsSubEdit
.SetStyleSettings(aStyleSubEdit
);
1420 m_pImpl
->m_pSubEdit
->SetSettings(aSettings
);
1422 m_pImpl
->m_pImplLB
->SetHighlightColor(rColor
);
1425 void ComboBox::SetHighlightTextColor( const Color
& rColor
)
1427 AllSettings
aSettings(GetSettings());
1428 StyleSettings
aStyle(aSettings
.GetStyleSettings());
1429 aStyle
.SetHighlightTextColor(rColor
);
1430 aSettings
.SetStyleSettings(aStyle
);
1431 SetSettings(aSettings
);
1433 AllSettings
aSettingsSubEdit(m_pImpl
->m_pSubEdit
->GetSettings());
1434 StyleSettings
aStyleSubEdit(aSettingsSubEdit
.GetStyleSettings());
1435 aStyleSubEdit
.SetHighlightTextColor(rColor
);
1436 aSettingsSubEdit
.SetStyleSettings(aStyleSubEdit
);
1437 m_pImpl
->m_pSubEdit
->SetSettings(aSettings
);
1439 m_pImpl
->m_pImplLB
->SetHighlightTextColor(rColor
);
1442 ImplListBoxWindow
* ComboBox::GetMainWindow() const
1444 return m_pImpl
->m_pImplLB
->GetMainWindow();
1447 tools::Long
ComboBox::GetIndexForPoint( const Point
& rPoint
, sal_Int32
& rPos
) const
1449 if( !HasLayoutData() )
1452 // check whether rPoint fits at all
1453 tools::Long nIndex
= Control::GetIndexForPoint( rPoint
);
1456 // point must be either in main list window
1457 // or in impl window (dropdown case)
1458 ImplListBoxWindow
* rMain
= GetMainWindow();
1460 // convert coordinates to ImplListBoxWindow pixel coordinate space
1461 Point aConvPoint
= LogicToPixel( rPoint
);
1462 aConvPoint
= OutputToAbsoluteScreenPixel( aConvPoint
);
1463 aConvPoint
= rMain
->AbsoluteScreenToOutputPixel( aConvPoint
);
1464 aConvPoint
= rMain
->PixelToLogic( aConvPoint
);
1466 // try to find entry
1467 sal_Int32 nEntry
= rMain
->GetEntryPosForPoint( aConvPoint
);
1468 if( nEntry
== LISTBOX_ENTRY_NOTFOUND
)
1474 // get line relative index
1476 nIndex
= ToRelativeLineIndex( nIndex
);
1481 ComboBoxBounds
ComboBox::Impl::calcComboBoxDropDownComponentBounds(
1482 const Size
&rOutSz
, const Size
&rBorderOutSz
) const
1484 ComboBoxBounds aBounds
;
1486 tools::Long nTop
= 0;
1487 tools::Long nBottom
= rOutSz
.Height();
1489 vcl::Window
*pBorder
= m_rThis
.GetWindow( GetWindowType::Border
);
1490 ImplControlValue aControlValue
;
1492 tools::Rectangle aContent
, aBound
;
1494 // use the full extent of the control
1495 tools::Rectangle
aArea( aPoint
, rBorderOutSz
);
1497 if (m_rThis
.GetNativeControlRegion(ControlType::Combobox
, ControlPart::ButtonDown
,
1498 aArea
, ControlState::NONE
, aControlValue
, aBound
, aContent
) )
1500 // convert back from border space to local coordinates
1501 aPoint
= pBorder
->ScreenToOutputPixel(m_rThis
.OutputToScreenPixel(aPoint
));
1502 aContent
.Move(-aPoint
.X(), -aPoint
.Y());
1504 aBounds
.aButtonPos
= Point(aContent
.Left(), nTop
);
1505 aBounds
.aButtonSize
= Size(aContent
.getOpenWidth(), (nBottom
-nTop
));
1507 // adjust the size of the edit field
1508 if (m_rThis
.GetNativeControlRegion(ControlType::Combobox
, ControlPart::SubEdit
,
1509 aArea
, ControlState::NONE
, aControlValue
, aBound
, aContent
) )
1511 // convert back from border space to local coordinates
1512 aContent
.Move(-aPoint
.X(), -aPoint
.Y());
1514 // use the themes drop down size
1515 aBounds
.aSubEditPos
= aContent
.TopLeft();
1516 aBounds
.aSubEditSize
= aContent
.GetSize();
1520 // use the themes drop down size for the button
1521 aBounds
.aSubEditSize
= Size(rOutSz
.Width() - aContent
.getOpenWidth(), rOutSz
.Height());
1526 tools::Long nSBWidth
= m_rThis
.GetSettings().GetStyleSettings().GetScrollBarSize();
1527 nSBWidth
= m_rThis
.CalcZoom( nSBWidth
);
1528 aBounds
.aSubEditSize
= Size(rOutSz
.Width() - nSBWidth
, rOutSz
.Height());
1529 aBounds
.aButtonPos
= Point(rOutSz
.Width() - nSBWidth
, nTop
);
1530 aBounds
.aButtonSize
= Size(nSBWidth
, (nBottom
-nTop
));
1535 void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars
)
1537 if (nWidthInChars
!= m_pImpl
->m_nWidthInChars
)
1539 m_pImpl
->m_nWidthInChars
= nWidthInChars
;
1544 void ComboBox::setMaxWidthChars(sal_Int32 nWidth
)
1546 if (nWidth
!= m_pImpl
->m_nMaxWidthChars
)
1548 m_pImpl
->m_nMaxWidthChars
= nWidth
;
1553 bool ComboBox::set_property(const OUString
&rKey
, const OUString
&rValue
)
1555 if (rKey
== "width-chars")
1556 SetWidthInChars(rValue
.toInt32());
1557 else if (rKey
== "max-width-chars")
1558 setMaxWidthChars(rValue
.toInt32());
1559 else if (rKey
== "can-focus")
1561 // as far as I can see in Gtk, setting a ComboBox as can.focus means
1562 // the focus gets stuck in it, so try here to behave like gtk does
1563 // with the settings that work, i.e. can.focus of false doesn't
1564 // set the hard WB_NOTABSTOP
1565 WinBits nBits
= GetStyle();
1566 nBits
&= ~(WB_TABSTOP
|WB_NOTABSTOP
);
1568 nBits
|= WB_TABSTOP
;
1571 else if (rKey
== "placeholder-text")
1572 SetPlaceholderText(rValue
);
1574 return Control::set_property(rKey
, rValue
);
1578 FactoryFunction
ComboBox::GetUITestFactory() const
1580 return ComboBoxUIObject::create
;
1583 void ComboBox::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
1585 Control::DumpAsPropertyTree(rJsonWriter
);
1588 auto entriesNode
= rJsonWriter
.startArray("entries");
1589 for (int i
= 0; i
< GetEntryCount(); ++i
)
1591 rJsonWriter
.putSimpleValue(GetEntry(i
));
1596 auto selectedNode
= rJsonWriter
.startArray("selectedEntries");
1597 for (int i
= 0; i
< GetSelectedEntryCount(); ++i
)
1599 rJsonWriter
.putSimpleValue(OUString::number(GetSelectedEntryPos(i
)));
1603 rJsonWriter
.put("selectedCount", GetSelectedEntryCount());
1606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */