bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / combobox.cxx
blob2fa7913c5148f7fb3df55933dbec79c7db657587
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/toolkit/combobox.hxx>
22 #include <set>
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>
39 namespace {
41 struct ComboBoxBounds
43 Point aSubEditPos;
44 Size aSubEditSize;
46 Point aButtonPos;
47 Size aButtonSize;
52 struct ComboBox::Impl
54 ComboBox & m_rThis;
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)
70 : m_rThis(rThis)
71 , m_nDDHeight(0)
72 , m_cMultiSep(0)
73 , m_isDDAutoSize(false)
74 , m_isSyntheticModify(false)
75 , m_isKeyBoardModify(false)
76 , m_isMatchCase(false)
77 , m_nMaxWidthChars(0)
78 , m_nWidthInChars(-1)
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 )
100 if (rText.empty())
101 return;
103 sal_Int32 nIdx{0};
104 do {
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 );
108 } while (nIdx>=0);
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 );
117 SetWidthInChars(-1);
120 ComboBox::~ComboBox()
122 disposeOnce();
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();
135 Edit::dispose();
138 void ComboBox::Impl::ImplInitComboBoxData()
140 m_pSubEdit.disposeAndClear();
141 m_pBtn = nullptr;
142 m_pImplLB = nullptr;
143 m_pFloatWin = nullptr;
145 m_nDDHeight = 0;
146 m_isDDAutoSize = true;
147 m_isSyntheticModify = false;
148 m_isKeyBoardModify = false;
149 m_isMatchCase = false;
150 m_cMultiSep = ';';
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,
168 aCtrlRegion,
169 ControlState::ENABLED,
170 aControlValue,
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;
187 else
189 if ( !bNoBorder )
190 nStyle |= WB_BORDER;
193 Edit::ImplInit( pParent, nStyle );
194 SetBackground();
196 // DropDown ?
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;
216 else
218 if ( !bNoBorder )
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 );
247 else
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) )
260 nStyle |= WB_GROUP;
261 return nStyle;
264 void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
266 m_pImpl->m_isMatchCase = bMatchCase;
268 if ( bEnable )
269 m_pImpl->m_pSubEdit->SetAutocompleteHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplAutocompleteHdl) );
270 else
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();
285 else
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();
293 if (m_pImplLB)
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 );
307 m_rThis.Select();
308 m_pImplLB->SetTravelSelect( bTravelSelect );
312 m_rThis.ImplClearLayoutData();
313 if (m_pImplLB)
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 )
330 nStart = 0;
332 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
333 if (!m_isMatchCase)
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)
364 OUString aText;
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))
385 nIndex--;
386 ++nSepCount;
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 );
412 else
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 );
424 bCallSelect = true;
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());
429 if (bMenuSelect)
431 m_pFloatWin->EndPopupMode();
432 m_rThis.GrabFocus();
435 if ( bCallSelect )
437 m_isKeyBoardModify = !bMenuSelect;
438 m_pSubEdit->SetModifyFlag();
439 m_isSyntheticModify = true;
440 m_rThis.Modify();
441 m_isSyntheticModify = false;
442 m_rThis.Select();
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() )
485 return;
487 if (m_pImpl->m_pFloatWin->IsInPopupMode())
488 m_pImpl->m_pFloatWin->EndPopupMode();
489 else
491 m_pImpl->m_pSubEdit->GrabFocus();
492 if (!m_pImpl->m_pImplLB->GetEntryList().GetMRUCount())
493 m_pImpl->ImplUpdateFloatSelection();
494 else
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();
526 else if ( !bAuto )
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);
545 else
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();
554 return nLines;
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()
578 Control::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);
590 else
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)
616 // dropdown mode
617 if (m_pImpl->m_pFloatWin->IsReallyVisible())
619 AppendLayoutData( *rMainWindow );
620 rMainWindow->SetLayoutDataParent( this );
623 else
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() );
637 if (m_pImpl->m_pBtn)
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() );
644 if (m_pImpl->m_pBtn)
645 m_pImpl->m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
646 Invalidate();
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();
657 Resize();
659 else if ( nType == StateChangedType::ControlFont )
661 m_pImpl->m_pImplLB->SetControlFont( GetControlFont() );
662 m_pImpl->m_pSubEdit->SetControlFont( GetControlFont() );
663 ImplCalcEditHeight();
664 Resize();
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 )
683 if (m_pImpl->m_pBtn)
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() );
690 Resize();
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))) )
702 return;
704 if (m_pImpl->m_pBtn)
706 m_pImpl->m_pBtn->GetOutDev()->SetSettings( GetSettings() );
707 ImplInitDropDownButton( m_pImpl->m_pBtn );
709 Resize();
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 )
718 bool bDone = false;
719 if ((rNEvt.GetType() == NotifyEventType::KEYINPUT)
720 && (rNEvt.GetWindow() == m_pImpl->m_pSubEdit)
721 && !IsReadOnly())
723 KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
724 sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
725 switch( nKeyCode )
727 case KEY_UP:
728 case KEY_DOWN:
729 case KEY_PAGEUP:
730 case KEY_PAGEDOWN:
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 );
744 bDone = true;
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();
751 bDone = true;
753 else
755 bDone = m_pImpl->m_pImplLB->ProcessKeyInput( aKeyEvt );
758 break;
760 case KEY_RETURN:
762 if ((rNEvt.GetWindow() == m_pImpl->m_pSubEdit) && IsInDropDown())
764 m_pImpl->m_pImplLB->ProcessKeyInput( aKeyEvt );
765 bDone = true;
768 break;
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);
791 else
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();
826 Edit::Modify();
829 void ComboBox::Impl::ImplUpdateFloatSelection()
831 if (!m_pImplLB || !m_pSubEdit)
832 return;
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;
840 bool bSelect = true;
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 );
855 bSelect = false;
858 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
860 if (!m_pImplLB->IsVisible(nSelect))
861 m_pImplLB->ShowProminentEntry( nSelect );
862 m_pImplLB->SelectEntry( nSelect, bSelect );
864 else
866 nSelect = m_pImplLB->GetEntryList().GetSelectedEntryPos( 0 );
867 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
868 m_pImplLB->SelectEntry( nSelect, false );
869 m_pImplLB->ResetCurrentPos();
872 else
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());
886 sal_Int32 nRealPos;
887 if (nPos == COMBOBOX_APPEND)
888 nRealPos = nPos;
889 else
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) );
899 return 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());
907 sal_Int32 nRealPos;
908 if (nPos == COMBOBOX_APPEND)
909 nRealPos = nPos;
910 else
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) );
920 return 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)
934 return;
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 );
943 return Image();
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();
951 return nPos;
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)
958 return OUString();
960 return m_pImpl->m_pImplLB->GetEntryList().GetEntryText( nPos + nMRUCount );
963 sal_Int32 ComboBox::GetEntryCount() const
965 if (!m_pImpl->m_pImplLB)
966 return 0;
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)
992 return;
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
1025 Size aSz;
1027 if (!m_pImpl->m_pImplLB)
1028 return aSz;
1030 if (!IsDropDownBox())
1032 aSz = m_pImpl->m_pImplLB->CalcSize( m_pImpl->m_pImplLB->GetEntryList().GetEntryCount() );
1033 aSz.AdjustHeight(m_pImpl->m_nDDHeight );
1035 else
1037 aSz.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1039 if (m_pImpl->m_nWidthInChars!= -1)
1040 aSz.setWidth(m_pImpl->m_nWidthInChars * approximate_digit_width());
1041 else
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 );
1061 return 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;
1074 if ( nLines < 1 )
1075 nLines = 1;
1076 aSz.setHeight( nLines * nEntryHeight );
1077 aSz.AdjustHeight(m_pImpl->m_nDDHeight );
1079 else
1081 aSz.setHeight( m_pImpl->m_nDDHeight );
1083 aSz.AdjustHeight(nTop+nBottom );
1085 aSz = CalcWindowSize( aSz );
1086 return aSz;
1089 Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1091 // show ScrollBars where appropriate
1092 Size aMinSz = CalcMinimumSize();
1093 Size aSz;
1095 // height
1096 if ( nLines )
1098 if ( !IsDropDownBox() )
1099 aSz.setHeight( m_pImpl->m_pImplLB->CalcSize( nLines ).Height() + m_pImpl->m_nDDHeight );
1100 else
1101 aSz.setHeight( m_pImpl->m_nDDHeight );
1103 else
1104 aSz.setHeight( aMinSz.Height() );
1106 // width
1107 if ( nColumns )
1108 aSz.setWidth( nColumns * approximate_char_width() );
1109 else
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 );
1126 return 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());
1143 else
1145 Size aOutSz = m_pImpl->m_pSubEdit->GetOutputSizePixel();
1146 rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1147 rnLines = 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 );
1159 pDev->Push();
1160 pDev->SetMapMode();
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;
1173 if ( bBorder )
1175 ImplDrawFrame( pDev, aRect );
1177 if ( bBackground )
1179 pDev->SetFillColor( GetControlBackground() );
1180 pDev->DrawRect( aRect );
1184 // contents
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;
1203 else
1204 nTextStyle |= DrawTextFlags::Left;
1206 if ( nFlags & SystemTextColorFlags::Mono )
1208 pDev->SetTextColor( COL_BLACK );
1210 else
1212 if ( !IsEnabled() )
1214 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1215 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1217 else
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 );
1226 if ( !nLines )
1227 nLines = 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 );
1246 pDev->Pop();
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);
1255 // DD-Button ?
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())
1329 nPos = 0;
1330 return nPos;
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();
1351 return rBack;
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());
1368 return nPos;
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() );
1395 return aRect;
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() )
1450 FillLayoutData();
1452 // check whether rPoint fits at all
1453 tools::Long nIndex = Control::GetIndexForPoint( rPoint );
1454 if( nIndex != -1 )
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 )
1469 nIndex = -1;
1470 else
1471 rPos = nEntry;
1474 // get line relative index
1475 if( nIndex != -1 )
1476 nIndex = ToRelativeLineIndex( nIndex );
1478 return 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;
1491 Point aPoint;
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();
1518 else
1520 // use the themes drop down size for the button
1521 aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getOpenWidth(), rOutSz.Height());
1524 else
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));
1532 return aBounds;
1535 void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars)
1537 if (nWidthInChars != m_pImpl->m_nWidthInChars)
1539 m_pImpl->m_nWidthInChars = nWidthInChars;
1540 queue_resize();
1544 void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1546 if (nWidth != m_pImpl->m_nMaxWidthChars)
1548 m_pImpl->m_nMaxWidthChars = nWidth;
1549 queue_resize();
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);
1567 if (toBool(rValue))
1568 nBits |= WB_TABSTOP;
1569 SetStyle(nBits);
1571 else if (rKey == "placeholder-text")
1572 SetPlaceholderText(rValue);
1573 else
1574 return Control::set_property(rKey, rValue);
1575 return true;
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: */