Simplify using designated initializers
[LibreOffice.git] / vcl / source / control / combobox.cxx
blobe8d93f1ea52c57cddad02fa0964f350a71732e53
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>
40 struct ComboBoxBounds
42 Point aSubEditPos;
43 Size aSubEditSize;
45 Point aButtonPos;
46 Size aButtonSize;
49 static void lcl_GetSelectedEntries( ::std::set< sal_Int32 >& rSelectedPos, std::u16string_view rText, sal_Unicode cTokenSep, const ImplEntryList& rEntryList )
51 if (rText.empty())
52 return;
54 sal_Int32 nIdx{0};
55 do {
56 const sal_Int32 nPos = rEntryList.FindEntry(comphelper::string::strip(o3tl::getToken(rText, 0, cTokenSep, nIdx), ' '));
57 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
58 rSelectedPos.insert( nPos );
59 } while (nIdx>=0);
62 ComboBox::ComboBox(vcl::Window *const pParent, WinBits const nStyle)
63 : Edit( WindowType::COMBOBOX )
64 , m_nDDHeight(0)
65 , m_cMultiSep(0)
66 , m_isDDAutoSize(false)
67 , m_isSyntheticModify(false)
68 , m_isKeyBoardModify(false)
69 , m_isMatchCase(false)
70 , m_nMaxWidthChars(0)
71 , m_nWidthInChars(-1)
73 ImplInitComboBoxData();
74 ImplInit( pParent, nStyle );
75 SetWidthInChars(-1);
78 ComboBox::~ComboBox()
80 disposeOnce();
83 void ComboBox::dispose()
85 m_pSubEdit.disposeAndClear();
87 VclPtr< ImplListBox > pImplLB = m_pImplLB;
88 m_pImplLB.clear();
89 pImplLB.disposeAndClear();
91 m_pFloatWin.disposeAndClear();
92 m_pBtn.disposeAndClear();
93 Edit::dispose();
96 void ComboBox::ImplInitComboBoxData()
98 m_pSubEdit.disposeAndClear();
99 m_pBtn = nullptr;
100 m_pImplLB = nullptr;
101 m_pFloatWin = nullptr;
103 m_nDDHeight = 0;
104 m_isDDAutoSize = true;
105 m_isSyntheticModify = false;
106 m_isKeyBoardModify = false;
107 m_isMatchCase = false;
108 m_cMultiSep = ';';
109 m_nMaxWidthChars = -1;
110 m_nWidthInChars = -1;
113 void ComboBox::ImplCalcEditHeight()
115 sal_Int32 nLeft, nTop, nRight, nBottom;
116 GetBorder( nLeft, nTop, nRight, nBottom );
117 m_nDDHeight = static_cast<sal_uInt16>(m_pSubEdit->GetTextHeight() + nTop + nBottom + 4);
118 if ( !IsDropDownBox() )
119 m_nDDHeight += 4;
121 tools::Rectangle aCtrlRegion( Point( 0, 0 ), Size( 10, 10 ) );
122 tools::Rectangle aBoundRegion, aContentRegion;
123 ImplControlValue aControlValue;
124 ControlType aType = IsDropDownBox() ? ControlType::Combobox : ControlType::Editbox;
125 if( GetNativeControlRegion( aType, ControlPart::Entire,
126 aCtrlRegion,
127 ControlState::ENABLED,
128 aControlValue,
129 aBoundRegion, aContentRegion ) )
131 const tools::Long nNCHeight = aBoundRegion.GetHeight();
132 if (m_nDDHeight < nNCHeight)
133 m_nDDHeight = sal::static_int_cast<sal_uInt16>(nNCHeight);
137 void ComboBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
139 bool bNoBorder = ( nStyle & WB_NOBORDER ) != 0;
140 if ( !(nStyle & WB_DROPDOWN) )
142 nStyle &= ~WB_BORDER;
143 nStyle |= WB_NOBORDER;
145 else
147 if ( !bNoBorder )
148 nStyle |= WB_BORDER;
151 Edit::ImplInit( pParent, nStyle );
152 SetBackground();
154 // DropDown ?
155 WinBits nEditStyle = nStyle & ( WB_LEFT | WB_RIGHT | WB_CENTER );
156 WinBits nListStyle = nStyle;
157 if( nStyle & WB_DROPDOWN )
159 m_pFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
160 if (!IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
161 m_pFloatWin->RequestDoubleBuffering(true);
162 m_pFloatWin->SetAutoWidth( true );
163 m_pFloatWin->SetPopupModeEndHdl(LINK(this, ComboBox, ImplPopupModeEndHdl));
165 m_pBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
166 ImplInitDropDownButton( m_pBtn );
167 m_pBtn->SetMBDownHdl(LINK(this, ComboBox, ImplClickBtnHdl));
168 m_pBtn->Show();
170 nEditStyle |= WB_NOBORDER;
171 nListStyle &= ~WB_BORDER;
172 nListStyle |= WB_NOBORDER;
174 else
176 if ( !bNoBorder )
178 nEditStyle |= WB_BORDER;
179 nListStyle &= ~WB_NOBORDER;
180 nListStyle |= WB_BORDER;
184 m_pSubEdit.set( VclPtr<Edit>::Create( this, nEditStyle ) );
185 m_pSubEdit->EnableRTL( false );
186 SetSubEdit( m_pSubEdit );
187 m_pSubEdit->SetPosPixel( Point() );
188 EnableAutocomplete( true );
189 m_pSubEdit->Show();
191 vcl::Window* pLBParent = this;
192 if (m_pFloatWin)
193 pLBParent = m_pFloatWin;
194 m_pImplLB = VclPtr<ImplListBox>::Create( pLBParent, nListStyle|WB_SIMPLEMODE|WB_AUTOHSCROLL );
195 m_pImplLB->SetPosPixel( Point() );
196 m_pImplLB->SetSelectHdl(LINK(this, ComboBox, ImplSelectHdl));
197 m_pImplLB->SetCancelHdl( LINK(this, ComboBox, ImplCancelHdl));
198 m_pImplLB->SetDoubleClickHdl(LINK(this, ComboBox, ImplDoubleClickHdl));
199 m_pImplLB->SetSelectionChangedHdl(LINK(this, ComboBox, ImplSelectionChangedHdl));
200 m_pImplLB->SetListItemSelectHdl(LINK(this, ComboBox, ImplListItemSelectHdl));
201 m_pImplLB->Show();
203 if (m_pFloatWin)
204 m_pFloatWin->SetImplListBox(m_pImplLB);
205 else
206 GetMainWindow()->AllowGrabFocus( true );
208 ImplCalcEditHeight();
210 SetCompoundControl( true );
213 WinBits ComboBox::ImplInitStyle( WinBits nStyle )
215 if ( !(nStyle & WB_NOTABSTOP) )
216 nStyle |= WB_TABSTOP;
217 if ( !(nStyle & WB_NOGROUP) )
218 nStyle |= WB_GROUP;
219 return nStyle;
222 void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
224 m_isMatchCase = bMatchCase;
226 if ( bEnable )
227 m_pSubEdit->SetAutocompleteHdl(LINK(this, ComboBox, ImplAutocompleteHdl));
228 else
229 m_pSubEdit->SetAutocompleteHdl( Link<Edit&,void>() );
232 bool ComboBox::IsAutocompleteEnabled() const
234 return m_pSubEdit->GetAutocompleteHdl().IsSet();
237 IMPL_LINK_NOARG(ComboBox, ImplClickBtnHdl, void*, void)
239 CallEventListeners( VclEventId::DropdownPreOpen );
240 m_pSubEdit->GrabFocus();
241 if (!m_pImplLB->GetEntryList().GetMRUCount())
242 ImplUpdateFloatSelection();
243 else
244 m_pImplLB->SelectEntry( 0 , true );
245 m_pBtn->SetPressed( true );
246 SetSelection( Selection( 0, SELECTION_MAX ) );
247 m_pFloatWin->StartFloat( true );
248 CallEventListeners( VclEventId::DropdownOpen );
250 ImplClearLayoutData();
251 if (m_pImplLB)
252 m_pImplLB->GetMainWindow()->ImplClearLayoutData();
255 IMPL_LINK_NOARG(ComboBox, ImplPopupModeEndHdl, FloatingWindow*, void)
257 if (m_pFloatWin->IsPopupModeCanceled())
259 if (!m_pImplLB->GetEntryList().IsEntryPosSelected(
260 m_pFloatWin->GetPopupModeStartSaveSelection()))
262 m_pImplLB->SelectEntry(m_pFloatWin->GetPopupModeStartSaveSelection(), true);
263 bool bTravelSelect = m_pImplLB->IsTravelSelect();
264 m_pImplLB->SetTravelSelect( true );
265 Select();
266 m_pImplLB->SetTravelSelect( bTravelSelect );
270 ImplClearLayoutData();
271 if (m_pImplLB)
272 m_pImplLB->GetMainWindow()->ImplClearLayoutData();
274 m_pBtn->SetPressed( false );
275 CallEventListeners( VclEventId::DropdownClose );
278 IMPL_LINK(ComboBox, ImplAutocompleteHdl, Edit&, rEdit, void)
280 Selection aSel = rEdit.GetSelection();
283 OUString aFullText = rEdit.GetText();
284 OUString aStartText = aFullText.copy( 0, static_cast<sal_Int32>(aSel.Max()) );
285 sal_Int32 nStart = m_pImplLB->GetCurrentPos();
287 if ( nStart == LISTBOX_ENTRY_NOTFOUND )
288 nStart = 0;
290 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
291 if (!m_isMatchCase)
293 // Try match case insensitive from current position
294 nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, true);
295 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
296 // Try match case insensitive, but from start
297 nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, true);
300 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
301 // Try match full from current position
302 nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, false);
303 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
304 // Match full, but from start
305 nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, false);
307 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
309 OUString aText = m_pImplLB->GetEntryList().GetEntryText( nPos );
310 Selection aSelection( aText.getLength(), aStartText.getLength() );
311 rEdit.SetText( aText, aSelection );
316 IMPL_LINK_NOARG(ComboBox, ImplSelectHdl, LinkParamNone*, void)
318 bool bPopup = IsInDropDown();
319 bool bCallSelect = false;
320 if (m_pImplLB->IsSelectionChanged() || bPopup)
322 OUString aText;
323 if (IsMultiSelectionEnabled())
325 aText = m_pSubEdit->GetText();
327 // remove all entries to which there is an entry, but which is not selected
328 sal_Int32 nIndex = 0;
329 while ( nIndex >= 0 )
331 sal_Int32 nPrevIndex = nIndex;
332 std::u16string_view aToken = o3tl::getToken(aText, 0, m_cMultiSep, nIndex );
333 sal_Int32 nTokenLen = aToken.size();
334 aToken = comphelper::string::strip(aToken, ' ');
335 sal_Int32 nP = m_pImplLB->GetEntryList().FindEntry( aToken );
336 if ((nP != LISTBOX_ENTRY_NOTFOUND) && (!m_pImplLB->GetEntryList().IsEntryPosSelected(nP)))
338 aText = aText.replaceAt( nPrevIndex, nTokenLen, u"" );
339 nIndex = nIndex - nTokenLen;
340 sal_Int32 nSepCount=0;
341 if ((nPrevIndex+nSepCount < aText.getLength()) && (aText[nPrevIndex+nSepCount] == m_cMultiSep))
343 nIndex--;
344 ++nSepCount;
346 aText = aText.replaceAt( nPrevIndex, nSepCount, u"" );
348 aText = comphelper::string::strip(aText, ' ');
351 // attach missing entries
352 ::std::set< sal_Int32 > aSelInText;
353 lcl_GetSelectedEntries( aSelInText, aText, m_cMultiSep, m_pImplLB->GetEntryList() );
354 sal_Int32 nSelectedEntries = m_pImplLB->GetEntryList().GetSelectedEntryCount();
355 for ( sal_Int32 n = 0; n < nSelectedEntries; n++ )
357 sal_Int32 nP = m_pImplLB->GetEntryList().GetSelectedEntryPos( n );
358 if ( !aSelInText.count( nP ) )
360 if (!aText.isEmpty() && (aText[aText.getLength()-1] != m_cMultiSep))
361 aText += OUStringChar(m_cMultiSep);
362 if ( !aText.isEmpty() )
363 aText += " "; // slightly loosen
364 aText += m_pImplLB->GetEntryList().GetEntryText( nP ) +
365 OUStringChar(m_cMultiSep);
368 aText = comphelper::string::stripEnd( aText, m_cMultiSep );
370 else
372 aText = m_pImplLB->GetEntryList().GetSelectedEntry( 0 );
375 m_pSubEdit->SetText( aText );
377 switch (GetSettings().GetStyleSettings().GetComboBoxTextSelectionMode())
379 case ComboBoxTextSelectionMode::SelectText:
381 Selection aNewSelection(0, aText.getLength());
382 if (IsMultiSelectionEnabled())
383 aNewSelection.Min() = aText.getLength();
384 m_pSubEdit->SetSelection(aNewSelection);
385 break;
387 case ComboBoxTextSelectionMode::CursorToStart:
389 Selection aNewSelection(0, 0);
390 m_pSubEdit->SetSelection(aNewSelection);
391 break;
393 case ComboBoxTextSelectionMode::CursorToEnd:
395 m_pSubEdit->SetCursorAtLast();
396 break;
398 default:
399 assert(false && "Unhandled ComboBoxTextSelectionMode case");
400 break;
403 bCallSelect = true;
406 // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
407 bool bMenuSelect = bPopup && !m_pImplLB->IsTravelSelect() && (!IsMultiSelectionEnabled() || !m_pImplLB->GetSelectModifier());
408 if (bMenuSelect)
410 m_pFloatWin->EndPopupMode();
411 GrabFocus();
414 if ( bCallSelect )
416 m_isKeyBoardModify = !bMenuSelect;
417 m_pSubEdit->SetModifyFlag();
418 m_isSyntheticModify = true;
419 Modify();
420 m_isSyntheticModify = false;
421 Select();
422 m_isKeyBoardModify = false;
426 bool ComboBox::IsSyntheticModify() const
428 return m_isSyntheticModify;
431 bool ComboBox::IsModifyByKeyboard() const
433 return m_isKeyBoardModify;
436 IMPL_LINK_NOARG(ComboBox, ImplListItemSelectHdl, LinkParamNone*, void)
438 CallEventListeners(VclEventId::DropdownSelect);
441 IMPL_LINK_NOARG(ComboBox, ImplCancelHdl, LinkParamNone*, void)
443 if (IsInDropDown())
444 m_pFloatWin->EndPopupMode();
447 IMPL_LINK( ComboBox, ImplSelectionChangedHdl, sal_Int32, nChanged, void)
449 if (!m_pImplLB->IsTrackingSelect())
451 if (!m_pSubEdit->IsReadOnly() && m_pImplLB->GetEntryList().IsEntryPosSelected(nChanged))
452 m_pSubEdit->SetText(m_pImplLB->GetEntryList().GetEntryText(nChanged));
456 IMPL_LINK_NOARG(ComboBox, ImplDoubleClickHdl, ImplListBoxWindow*, void)
458 DoubleClick();
461 void ComboBox::ToggleDropDown()
463 if( !IsDropDownBox() )
464 return;
466 if (m_pFloatWin->IsInPopupMode())
467 m_pFloatWin->EndPopupMode();
468 else
470 m_pSubEdit->GrabFocus();
471 if (!m_pImplLB->GetEntryList().GetMRUCount())
472 ImplUpdateFloatSelection();
473 else
474 m_pImplLB->SelectEntry( 0 , true );
475 CallEventListeners( VclEventId::DropdownPreOpen );
476 m_pBtn->SetPressed( true );
477 SetSelection( Selection( 0, SELECTION_MAX ) );
478 m_pFloatWin->StartFloat(true);
479 CallEventListeners( VclEventId::DropdownOpen );
483 void ComboBox::Select()
485 ImplCallEventListenersAndHandler( VclEventId::ComboboxSelect, [this] () { m_SelectHdl.Call(*this); } );
488 void ComboBox::DoubleClick()
490 ImplCallEventListenersAndHandler( VclEventId::ComboboxDoubleClick, [] () {} );
493 bool ComboBox::IsAutoSizeEnabled() const { return m_isDDAutoSize; }
495 void ComboBox::EnableAutoSize( bool bAuto )
497 m_isDDAutoSize = bAuto;
498 if (m_pFloatWin)
500 if (bAuto && !m_pFloatWin->GetDropDownLineCount())
502 // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
503 AdaptDropDownLineCountToMaximum();
505 else if ( !bAuto )
507 m_pFloatWin->SetDropDownLineCount(0);
512 void ComboBox::SetDropDownLineCount( sal_uInt16 nLines )
514 if (m_pFloatWin)
515 m_pFloatWin->SetDropDownLineCount(nLines);
518 void ComboBox::AdaptDropDownLineCountToMaximum()
520 // Adapt to maximum allowed number.
521 // Limit for LOK as we can't render outside of the dialog canvas.
522 if (comphelper::LibreOfficeKit::isActive())
523 SetDropDownLineCount(11);
524 else
525 SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
528 sal_uInt16 ComboBox::GetDropDownLineCount() const
530 sal_uInt16 nLines = 0;
531 if (m_pFloatWin)
532 nLines = m_pFloatWin->GetDropDownLineCount();
533 return nLines;
536 void ComboBox::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
537 PosSizeFlags nFlags )
539 if( IsDropDownBox() && ( nFlags & PosSizeFlags::Size ) )
541 Size aPrefSz = m_pFloatWin->GetPrefSize();
542 if ((nFlags & PosSizeFlags::Height) && (nHeight >= 2*m_nDDHeight))
543 aPrefSz.setHeight( nHeight-m_nDDHeight );
544 if ( nFlags & PosSizeFlags::Width )
545 aPrefSz.setWidth( nWidth );
546 m_pFloatWin->SetPrefSize(aPrefSz);
548 if (IsAutoSizeEnabled())
549 nHeight = m_nDDHeight;
552 Edit::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
555 void ComboBox::Resize()
557 Control::Resize();
559 if (m_pSubEdit)
561 Size aOutSz = GetOutputSizePixel();
562 if( IsDropDownBox() )
564 ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(aOutSz,
565 GetWindow(GetWindowType::Border)->GetOutputSizePixel()));
566 m_pSubEdit->SetPosSizePixel(aBounds.aSubEditPos, aBounds.aSubEditSize);
567 m_pBtn->SetPosSizePixel(aBounds.aButtonPos, aBounds.aButtonSize);
569 else
571 m_pSubEdit->SetSizePixel(Size(aOutSz.Width(), m_nDDHeight));
572 m_pImplLB->setPosSizePixel(0, m_nDDHeight,
573 aOutSz.Width(), aOutSz.Height() - m_nDDHeight);
574 if ( !GetText().isEmpty() )
575 ImplUpdateFloatSelection();
579 // adjust the size of the FloatingWindow even when invisible
580 // as KEY_PGUP/DOWN is being processed...
581 if (m_pFloatWin)
582 m_pFloatWin->SetSizePixel(m_pFloatWin->CalcFloatSize());
585 bool ComboBox::IsDropDownBox() const { return m_pFloatWin != nullptr; }
587 void ComboBox::FillLayoutData() const
589 mxLayoutData.emplace();
590 AppendLayoutData( *m_pSubEdit );
591 m_pSubEdit->SetLayoutDataParent( this );
592 ImplListBoxWindow* rMainWindow = GetMainWindow();
593 if (m_pFloatWin)
595 // dropdown mode
596 if (m_pFloatWin->IsReallyVisible())
598 AppendLayoutData( *rMainWindow );
599 rMainWindow->SetLayoutDataParent( this );
602 else
604 AppendLayoutData( *rMainWindow );
605 rMainWindow->SetLayoutDataParent( this );
609 void ComboBox::StateChanged( StateChangedType nType )
611 Edit::StateChanged( nType );
613 if ( nType == StateChangedType::ReadOnly )
615 m_pImplLB->SetReadOnly( IsReadOnly() );
616 if (m_pBtn)
617 m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
619 else if ( nType == StateChangedType::Enable )
621 m_pSubEdit->Enable( IsEnabled() );
622 m_pImplLB->Enable( IsEnabled() && !IsReadOnly() );
623 if (m_pBtn)
624 m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
625 Invalidate();
627 else if( nType == StateChangedType::UpdateMode )
629 m_pImplLB->SetUpdateMode( IsUpdateMode() );
631 else if ( nType == StateChangedType::Zoom )
633 m_pImplLB->SetZoom( GetZoom() );
634 m_pSubEdit->SetZoom( GetZoom() );
635 ImplCalcEditHeight();
636 Resize();
638 else if ( nType == StateChangedType::ControlFont )
640 m_pImplLB->SetControlFont( GetControlFont() );
641 m_pSubEdit->SetControlFont( GetControlFont() );
642 ImplCalcEditHeight();
643 Resize();
645 else if ( nType == StateChangedType::ControlForeground )
647 m_pImplLB->SetControlForeground( GetControlForeground() );
648 m_pSubEdit->SetControlForeground( GetControlForeground() );
650 else if ( nType == StateChangedType::ControlBackground )
652 m_pImplLB->SetControlBackground( GetControlBackground() );
653 m_pSubEdit->SetControlBackground( GetControlBackground() );
655 else if ( nType == StateChangedType::Style )
657 SetStyle( ImplInitStyle( GetStyle() ) );
658 GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
660 else if( nType == StateChangedType::Mirroring )
662 if (m_pBtn)
664 m_pBtn->EnableRTL( IsRTLEnabled() );
665 ImplInitDropDownButton( m_pBtn );
667 m_pSubEdit->CompatStateChanged( StateChangedType::Mirroring );
668 m_pImplLB->EnableRTL( IsRTLEnabled() );
669 Resize();
673 void ComboBox::DataChanged( const DataChangedEvent& rDCEvt )
675 Control::DataChanged( rDCEvt );
677 if ( !((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
678 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
679 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
680 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
681 return;
683 if (m_pBtn)
685 m_pBtn->GetOutDev()->SetSettings( GetSettings() );
686 ImplInitDropDownButton( m_pBtn );
688 Resize();
689 m_pImplLB->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
691 SetBackground(); // due to a hack in Window::UpdateSettings the background must be reset
692 // otherwise it will overpaint NWF drawn comboboxes
695 bool ComboBox::EventNotify( NotifyEvent& rNEvt )
697 bool bDone = false;
698 if ((rNEvt.GetType() == NotifyEventType::KEYINPUT)
699 && (rNEvt.GetWindow() == m_pSubEdit)
700 && !IsReadOnly())
702 KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
703 sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
704 switch( nKeyCode )
706 case KEY_UP:
707 case KEY_DOWN:
708 case KEY_PAGEUP:
709 case KEY_PAGEDOWN:
711 ImplUpdateFloatSelection();
712 if ((nKeyCode == KEY_DOWN) && m_pFloatWin
713 && !m_pFloatWin->IsInPopupMode()
714 && aKeyEvt.GetKeyCode().IsMod2())
716 CallEventListeners( VclEventId::DropdownPreOpen );
717 m_pBtn->SetPressed( true );
718 if (m_pImplLB->GetEntryList().GetMRUCount())
719 m_pImplLB->SelectEntry( 0 , true );
720 SetSelection( Selection( 0, SELECTION_MAX ) );
721 m_pFloatWin->StartFloat(false);
722 CallEventListeners( VclEventId::DropdownOpen );
723 bDone = true;
725 else if ((nKeyCode == KEY_UP) && m_pFloatWin
726 && m_pFloatWin->IsInPopupMode()
727 && aKeyEvt.GetKeyCode().IsMod2())
729 m_pFloatWin->EndPopupMode();
730 bDone = true;
732 else
734 bDone = m_pImplLB->ProcessKeyInput( aKeyEvt );
737 break;
739 case KEY_RETURN:
741 if ((rNEvt.GetWindow() == m_pSubEdit) && IsInDropDown())
743 m_pImplLB->ProcessKeyInput( aKeyEvt );
744 bDone = true;
747 break;
750 else if ((rNEvt.GetType() == NotifyEventType::LOSEFOCUS) && m_pFloatWin)
752 if (m_pFloatWin->HasChildPathFocus())
753 m_pSubEdit->GrabFocus();
754 else if (m_pFloatWin->IsInPopupMode() && !HasChildPathFocus(true))
755 m_pFloatWin->EndPopupMode();
757 else if( (rNEvt.GetType() == NotifyEventType::COMMAND) &&
758 (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
759 (rNEvt.GetWindow() == m_pSubEdit) )
761 MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
762 if ( ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
763 || ( ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
764 && HasChildPathFocus()
768 bDone = m_pImplLB->HandleWheelAsCursorTravel(*rNEvt.GetCommandEvent(), *this);
770 else
772 bDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
775 else if ((rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN)
776 && (rNEvt.GetWindow() == GetMainWindow()))
778 m_pSubEdit->GrabFocus();
781 return bDone || Edit::EventNotify( rNEvt );
784 void ComboBox::SetText( const OUString& rStr )
786 CallEventListeners( VclEventId::ComboboxSetText );
788 Edit::SetText( rStr );
789 ImplUpdateFloatSelection();
792 void ComboBox::SetText( const OUString& rStr, const Selection& rNewSelection )
794 CallEventListeners( VclEventId::ComboboxSetText );
796 Edit::SetText( rStr, rNewSelection );
797 ImplUpdateFloatSelection();
800 void ComboBox::Modify()
802 if (!m_isSyntheticModify)
803 ImplUpdateFloatSelection();
805 Edit::Modify();
808 void ComboBox::ImplUpdateFloatSelection()
810 if (!m_pImplLB || !m_pSubEdit)
811 return;
813 // move text in the ListBox into the visible region
814 m_pImplLB->SetCallSelectionChangedHdl( false );
815 if (!IsMultiSelectionEnabled())
817 OUString aSearchStr( m_pSubEdit->GetText() );
818 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
819 bool bSelect = true;
821 if (m_pImplLB->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND)
823 OUString aCurrent = m_pImplLB->GetEntryList().GetEntryText(
824 m_pImplLB->GetCurrentPos());
825 if ( aCurrent == aSearchStr )
826 nSelect = m_pImplLB->GetCurrentPos();
829 if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
830 nSelect = m_pImplLB->GetEntryList().FindEntry( aSearchStr );
831 if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
833 nSelect = m_pImplLB->GetEntryList().FindMatchingEntry( aSearchStr, 0, true );
834 bSelect = false;
837 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
839 if (!m_pImplLB->IsVisible(nSelect))
840 m_pImplLB->ShowProminentEntry( nSelect );
841 m_pImplLB->SelectEntry( nSelect, bSelect );
843 else
845 nSelect = m_pImplLB->GetEntryList().GetSelectedEntryPos( 0 );
846 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
847 m_pImplLB->SelectEntry( nSelect, false );
848 m_pImplLB->ResetCurrentPos();
851 else
853 ::std::set< sal_Int32 > aSelInText;
854 lcl_GetSelectedEntries(aSelInText, m_pSubEdit->GetText(), m_cMultiSep, m_pImplLB->GetEntryList());
855 for (sal_Int32 n = 0; n < m_pImplLB->GetEntryList().GetEntryCount(); n++)
856 m_pImplLB->SelectEntry( n, aSelInText.count( n ) != 0 );
858 m_pImplLB->SetCallSelectionChangedHdl( true );
861 sal_Int32 ComboBox::InsertEntry(const OUString& rStr, sal_Int32 const nPos)
863 assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
865 sal_Int32 nRealPos;
866 if (nPos == COMBOBOX_APPEND)
867 nRealPos = nPos;
868 else
870 const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
871 assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
872 nRealPos = nPos + nMRUCount;
875 nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr );
876 nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
877 CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
878 return nRealPos;
881 sal_Int32 ComboBox::InsertEntryWithImage(
882 const OUString& rStr, const Image& rImage, sal_Int32 const nPos)
884 assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
886 sal_Int32 nRealPos;
887 if (nPos == COMBOBOX_APPEND)
888 nRealPos = nPos;
889 else
891 const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
892 assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
893 nRealPos = nPos + nMRUCount;
896 nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr, rImage );
897 nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
898 CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
899 return nRealPos;
902 void ComboBox::RemoveEntryAt(sal_Int32 const nPos)
904 const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
905 assert(nPos >= 0 && nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
906 m_pImplLB->RemoveEntry( nPos + nMRUCount );
907 CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nPos)) );
910 void ComboBox::Clear()
912 if (!m_pImplLB)
913 return;
914 m_pImplLB->Clear();
915 CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(sal_IntPtr(-1)) );
918 Image ComboBox::GetEntryImage( sal_Int32 nPos ) const
920 if (m_pImplLB->GetEntryList().HasEntryImage(nPos))
921 return m_pImplLB->GetEntryList().GetEntryImage( nPos );
922 return Image();
925 sal_Int32 ComboBox::GetEntryPos( std::u16string_view rStr ) const
927 sal_Int32 nPos = m_pImplLB->GetEntryList().FindEntry( rStr );
928 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
929 nPos -= m_pImplLB->GetEntryList().GetMRUCount();
930 return nPos;
933 OUString ComboBox::GetEntry( sal_Int32 nPos ) const
935 const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
936 if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
937 return OUString();
939 return m_pImplLB->GetEntryList().GetEntryText( nPos + nMRUCount );
942 sal_Int32 ComboBox::GetEntryCount() const
944 if (!m_pImplLB)
945 return 0;
946 return m_pImplLB->GetEntryList().GetEntryCount() - m_pImplLB->GetEntryList().GetMRUCount();
949 bool ComboBox::IsTravelSelect() const
951 return m_pImplLB->IsTravelSelect();
954 bool ComboBox::IsInDropDown() const
956 // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
957 // mbPopupMode is set to false
958 return m_pFloatWin && m_pFloatWin->IsInPopupMode() && m_pFloatWin->ImplIsInPrivatePopupMode();
961 bool ComboBox::IsMultiSelectionEnabled() const
963 return m_pImplLB->IsMultiSelectionEnabled();
966 void ComboBox::SetSelectHdl(const Link<ComboBox&,void>& rLink) { m_SelectHdl = rLink; }
968 void ComboBox::SetEntryActivateHdl(const Link<Edit&,bool>& rLink)
970 if (!m_pSubEdit)
971 return;
972 m_pSubEdit->SetActivateHdl(rLink);
975 Size ComboBox::GetOptimalSize() const
977 return CalcMinimumSize();
980 tools::Long ComboBox::getMaxWidthScrollBarAndDownButton() const
982 tools::Long nButtonDownWidth = 0;
984 vcl::Window *pBorder = GetWindow( GetWindowType::Border );
985 ImplControlValue aControlValue;
986 tools::Rectangle aContent, aBound;
988 // use the full extent of the control
989 tools::Rectangle aArea( Point(), pBorder->GetOutputSizePixel() );
991 if ( GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
992 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
994 nButtonDownWidth = aContent.getOpenWidth();
997 tools::Long nScrollBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
999 return std::max(nScrollBarWidth, nButtonDownWidth);
1002 Size ComboBox::CalcMinimumSize() const
1004 Size aSz;
1006 if (!m_pImplLB)
1007 return aSz;
1009 if (!IsDropDownBox())
1011 aSz = m_pImplLB->CalcSize( m_pImplLB->GetEntryList().GetEntryCount() );
1012 aSz.AdjustHeight(m_nDDHeight );
1014 else
1016 aSz.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1018 if (m_nWidthInChars!= -1)
1019 aSz.setWidth(m_nWidthInChars * approximate_digit_width());
1020 else
1021 aSz.setWidth(m_pImplLB->GetMaxEntryWidth());
1024 if (m_nMaxWidthChars != -1)
1026 tools::Long nMaxWidth = m_nMaxWidthChars * approximate_char_width();
1027 aSz.setWidth( std::min(aSz.Width(), nMaxWidth) );
1030 if (IsDropDownBox())
1031 aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1033 ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(
1034 Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1035 aSz.AdjustWidth(aBounds.aSubEditPos.X()*2 );
1037 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1039 aSz = CalcWindowSize( aSz );
1040 return aSz;
1043 Size ComboBox::CalcAdjustedSize( const Size& rPrefSize ) const
1045 Size aSz = rPrefSize;
1046 sal_Int32 nLeft, nTop, nRight, nBottom;
1047 static_cast<vcl::Window*>(const_cast<ComboBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1048 aSz.AdjustHeight( -(nTop+nBottom) );
1049 if ( !IsDropDownBox() )
1051 tools::Long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1052 tools::Long nLines = aSz.Height() / nEntryHeight;
1053 if ( nLines < 1 )
1054 nLines = 1;
1055 aSz.setHeight( nLines * nEntryHeight );
1056 aSz.AdjustHeight(m_nDDHeight );
1058 else
1060 aSz.setHeight( m_nDDHeight );
1062 aSz.AdjustHeight(nTop+nBottom );
1064 aSz = CalcWindowSize( aSz );
1065 return aSz;
1068 Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1070 // show ScrollBars where appropriate
1071 Size aMinSz = CalcMinimumSize();
1072 Size aSz;
1074 // height
1075 if ( nLines )
1077 if ( !IsDropDownBox() )
1078 aSz.setHeight( m_pImplLB->CalcSize( nLines ).Height() + m_nDDHeight );
1079 else
1080 aSz.setHeight( m_nDDHeight );
1082 else
1083 aSz.setHeight( aMinSz.Height() );
1085 // width
1086 if ( nColumns )
1087 aSz.setWidth( nColumns * approximate_char_width() );
1088 else
1089 aSz.setWidth( aMinSz.Width() );
1091 if ( IsDropDownBox() )
1092 aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1094 if ( !IsDropDownBox() )
1096 if ( aSz.Width() < aMinSz.Width() )
1097 aSz.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1098 if ( aSz.Height() < aMinSz.Height() )
1099 aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1102 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1104 aSz = CalcWindowSize( aSz );
1105 return aSz;
1108 tools::Long ComboBox::GetDropDownEntryHeight() const
1110 return m_pImplLB->GetEntryHeight();
1113 void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1115 tools::Long nCharWidth = GetTextWidth(OUString(u'x'));
1116 if ( !IsDropDownBox() )
1118 Size aOutSz = GetMainWindow()->GetOutputSizePixel();
1119 rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1120 rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight());
1122 else
1124 Size aOutSz = m_pSubEdit->GetOutputSizePixel();
1125 rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1126 rnLines = 1;
1130 void ComboBox::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags )
1132 GetMainWindow()->ApplySettings(*pDev);
1134 Point aPos = pDev->LogicToPixel( rPos );
1135 Size aSize = GetSizePixel();
1136 vcl::Font aFont = GetMainWindow()->GetDrawPixelFont( pDev );
1138 pDev->Push();
1139 pDev->SetMapMode();
1140 pDev->SetFont( aFont );
1141 pDev->SetTextFillColor();
1143 // Border/Background
1144 pDev->SetLineColor();
1145 pDev->SetFillColor();
1146 bool bBorder = (GetStyle() & WB_BORDER);
1147 bool bBackground = IsControlBackground();
1148 if ( bBorder || bBackground )
1150 tools::Rectangle aRect( aPos, aSize );
1151 // aRect.Top() += nEditHeight;
1152 if ( bBorder )
1154 ImplDrawFrame( pDev, aRect );
1156 if ( bBackground )
1158 pDev->SetFillColor( GetControlBackground() );
1159 pDev->DrawRect( aRect );
1163 // contents
1164 if ( !IsDropDownBox() )
1166 tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
1167 tools::Long nTextHeight = pDev->GetTextHeight();
1168 tools::Long nEditHeight = nTextHeight + 6*nOnePixel;
1169 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1171 // First, draw the edit part
1172 Size aOrigSize(m_pSubEdit->GetSizePixel());
1173 m_pSubEdit->SetSizePixel(Size(aSize.Width(), nEditHeight));
1174 m_pSubEdit->Draw( pDev, aPos, nFlags );
1175 m_pSubEdit->SetSizePixel(aOrigSize);
1177 // Second, draw the listbox
1178 if ( GetStyle() & WB_CENTER )
1179 nTextStyle |= DrawTextFlags::Center;
1180 else if ( GetStyle() & WB_RIGHT )
1181 nTextStyle |= DrawTextFlags::Right;
1182 else
1183 nTextStyle |= DrawTextFlags::Left;
1185 if ( nFlags & SystemTextColorFlags::Mono )
1187 pDev->SetTextColor( COL_BLACK );
1189 else
1191 if ( !IsEnabled() )
1193 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1194 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1196 else
1198 pDev->SetTextColor( GetTextColor() );
1202 tools::Rectangle aClip( aPos, aSize );
1203 pDev->IntersectClipRegion( aClip );
1204 sal_Int32 nLines = static_cast<sal_Int32>( nTextHeight > 0 ? (aSize.Height()-nEditHeight)/nTextHeight : 1 );
1205 if ( !nLines )
1206 nLines = 1;
1207 const sal_Int32 nTEntry = IsReallyVisible() ? m_pImplLB->GetTopEntry() : 0;
1209 tools::Rectangle aTextRect( aPos, aSize );
1211 aTextRect.AdjustLeft(3*nOnePixel );
1212 aTextRect.AdjustRight( -(3*nOnePixel) );
1213 aTextRect.AdjustTop(nEditHeight + nOnePixel );
1214 aTextRect.SetBottom( aTextRect.Top() + nTextHeight );
1216 // the drawing starts here
1217 for ( sal_Int32 n = 0; n < nLines; ++n )
1219 pDev->DrawText( aTextRect, m_pImplLB->GetEntryList().GetEntryText( n+nTEntry ), nTextStyle );
1220 aTextRect.AdjustTop(nTextHeight );
1221 aTextRect.AdjustBottom(nTextHeight );
1225 pDev->Pop();
1227 // Call Edit::Draw after restoring the MapMode...
1228 if ( IsDropDownBox() )
1230 Size aOrigSize(m_pSubEdit->GetSizePixel());
1231 m_pSubEdit->SetSizePixel(GetSizePixel());
1232 m_pSubEdit->Draw( pDev, rPos, nFlags );
1233 m_pSubEdit->SetSizePixel(aOrigSize);
1234 // DD-Button ?
1238 void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink)
1240 m_pImplLB->SetUserDrawHdl(rLink);
1243 void ComboBox::SetUserItemSize( const Size& rSz )
1245 GetMainWindow()->SetUserItemSize( rSz );
1248 void ComboBox::EnableUserDraw( bool bUserDraw )
1250 GetMainWindow()->EnableUserDraw( bUserDraw );
1253 bool ComboBox::IsUserDrawEnabled() const
1255 return GetMainWindow()->IsUserDrawEnabled();
1258 void ComboBox::DrawEntry(const UserDrawEvent& rEvt)
1260 GetMainWindow()->DrawEntry(*rEvt.GetRenderContext(), rEvt.GetItemId(), /*bDrawImage*/false, /*bDrawText*/false);
1263 void ComboBox::AddSeparator( sal_Int32 n )
1265 m_pImplLB->AddSeparator( n );
1268 void ComboBox::SetMRUEntries( std::u16string_view rEntries )
1270 m_pImplLB->SetMRUEntries( rEntries, ';' );
1273 OUString ComboBox::GetMRUEntries() const
1275 return m_pImplLB ? m_pImplLB->GetMRUEntries( ';' ) : OUString();
1278 void ComboBox::SetMaxMRUCount( sal_Int32 n )
1280 m_pImplLB->SetMaxMRUCount( n );
1283 sal_Int32 ComboBox::GetMaxMRUCount() const
1285 return m_pImplLB ? m_pImplLB->GetMaxMRUCount() : 0;
1288 sal_uInt16 ComboBox::GetDisplayLineCount() const
1290 return m_pImplLB ? m_pImplLB->GetDisplayLineCount() : 0;
1293 void ComboBox::SetEntryData( sal_Int32 nPos, void* pNewData )
1295 m_pImplLB->SetEntryData( nPos + m_pImplLB->GetEntryList().GetMRUCount(), pNewData );
1298 void* ComboBox::GetEntryData( sal_Int32 nPos ) const
1300 return m_pImplLB->GetEntryList().GetEntryData(
1301 nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1304 sal_Int32 ComboBox::GetTopEntry() const
1306 sal_Int32 nPos = GetEntryCount() ? m_pImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1307 if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1308 nPos = 0;
1309 return nPos;
1312 tools::Rectangle ComboBox::GetDropDownPosSizePixel() const
1314 return m_pFloatWin
1315 ? m_pFloatWin->GetWindowExtentsRelative(*this)
1316 : tools::Rectangle();
1319 const Wallpaper& ComboBox::GetDisplayBackground() const
1321 if (!m_pSubEdit->IsBackground())
1322 return Control::GetDisplayBackground();
1324 const Wallpaper& rBack = m_pSubEdit->GetBackground();
1325 if( ! rBack.IsBitmap() &&
1326 ! rBack.IsGradient() &&
1327 rBack == Wallpaper(COL_TRANSPARENT)
1329 return Control::GetDisplayBackground();
1330 return rBack;
1333 sal_Int32 ComboBox::GetSelectedEntryCount() const
1335 return m_pImplLB->GetEntryList().GetSelectedEntryCount();
1338 sal_Int32 ComboBox::GetSelectedEntryPos( sal_Int32 nIndex ) const
1340 sal_Int32 nPos = m_pImplLB->GetEntryList().GetSelectedEntryPos( nIndex );
1341 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1343 if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1344 nPos = m_pImplLB->GetEntryList().FindEntry(m_pImplLB->GetEntryList().GetEntryText(nPos));
1345 nPos = sal::static_int_cast<sal_Int32>(nPos - m_pImplLB->GetEntryList().GetMRUCount());
1347 return nPos;
1350 bool ComboBox::IsEntryPosSelected( sal_Int32 nPos ) const
1352 return m_pImplLB->GetEntryList().IsEntryPosSelected(
1353 nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1356 void ComboBox::SelectEntryPos( sal_Int32 nPos, bool bSelect)
1358 if (nPos < m_pImplLB->GetEntryList().GetEntryCount())
1359 m_pImplLB->SelectEntry(
1360 nPos + m_pImplLB->GetEntryList().GetMRUCount(), bSelect);
1363 void ComboBox::SetNoSelection()
1365 m_pImplLB->SetNoSelection();
1366 m_pSubEdit->SetText(OUString());
1369 tools::Rectangle ComboBox::GetBoundingRectangle( sal_Int32 nItem ) const
1371 tools::Rectangle aRect = GetMainWindow()->GetBoundingRectangle( nItem );
1372 tools::Rectangle aOffset = GetMainWindow()->GetWindowExtentsRelative( *static_cast<vcl::Window*>(const_cast<ComboBox *>(this)) );
1373 aRect.Move( aOffset.Left(), aOffset.Top() );
1374 return aRect;
1377 void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle )
1379 Window::SetBorderStyle( nBorderStyle );
1380 if ( !IsDropDownBox() )
1382 m_pSubEdit->SetBorderStyle(nBorderStyle);
1383 m_pImplLB->SetBorderStyle( nBorderStyle );
1387 void ComboBox::SetHighlightColor( const Color& rColor )
1389 AllSettings aSettings(GetSettings());
1390 StyleSettings aStyle(aSettings.GetStyleSettings());
1391 aStyle.SetHighlightColor(rColor);
1392 aSettings.SetStyleSettings(aStyle);
1393 SetSettings(aSettings);
1395 AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1396 StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1397 aStyleSubEdit.SetHighlightColor(rColor);
1398 aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1399 m_pSubEdit->SetSettings(aSettings);
1401 m_pImplLB->SetHighlightColor(rColor);
1404 void ComboBox::SetHighlightTextColor( const Color& rColor )
1406 AllSettings aSettings(GetSettings());
1407 StyleSettings aStyle(aSettings.GetStyleSettings());
1408 aStyle.SetHighlightTextColor(rColor);
1409 aSettings.SetStyleSettings(aStyle);
1410 SetSettings(aSettings);
1412 AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1413 StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1414 aStyleSubEdit.SetHighlightTextColor(rColor);
1415 aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1416 m_pSubEdit->SetSettings(aSettings);
1418 m_pImplLB->SetHighlightTextColor(rColor);
1421 ImplListBoxWindow* ComboBox::GetMainWindow() const
1423 return m_pImplLB->GetMainWindow();
1426 tools::Long ComboBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
1428 if( !HasLayoutData() )
1429 FillLayoutData();
1431 // check whether rPoint fits at all
1432 tools::Long nIndex = Control::GetIndexForPoint( rPoint );
1433 if( nIndex != -1 )
1435 // point must be either in main list window
1436 // or in impl window (dropdown case)
1437 ImplListBoxWindow* rMain = GetMainWindow();
1439 // convert coordinates to ImplListBoxWindow pixel coordinate space
1440 Point aConvPoint = LogicToPixel( rPoint );
1441 AbsoluteScreenPixelPoint aConvPointAbs = OutputToAbsoluteScreenPixel( aConvPoint );
1442 aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPointAbs );
1443 aConvPoint = rMain->PixelToLogic( aConvPoint );
1445 // try to find entry
1446 sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
1447 if( nEntry == LISTBOX_ENTRY_NOTFOUND )
1448 nIndex = -1;
1449 else
1450 rPos = nEntry;
1453 // get line relative index
1454 if( nIndex != -1 )
1455 nIndex = ToRelativeLineIndex( nIndex );
1457 return nIndex;
1460 ComboBoxBounds ComboBox::calcComboBoxDropDownComponentBounds(
1461 const Size &rOutSz, const Size &rBorderOutSz) const
1463 ComboBoxBounds aBounds;
1465 tools::Long nTop = 0;
1466 tools::Long nBottom = rOutSz.Height();
1468 vcl::Window *pBorder = GetWindow(GetWindowType::Border);
1469 ImplControlValue aControlValue;
1470 Point aPoint;
1471 tools::Rectangle aContent, aBound;
1473 // use the full extent of the control
1474 tools::Rectangle aArea( aPoint, rBorderOutSz );
1476 if (GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1477 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1479 // convert back from border space to local coordinates
1480 aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
1481 aContent.Move(-aPoint.X(), -aPoint.Y());
1483 aBounds.aButtonPos = Point(aContent.Left(), nTop);
1484 aBounds.aButtonSize = Size(aContent.getOpenWidth(), (nBottom-nTop));
1486 // adjust the size of the edit field
1487 if (GetNativeControlRegion(ControlType::Combobox, ControlPart::SubEdit,
1488 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1490 // convert back from border space to local coordinates
1491 aContent.Move(-aPoint.X(), -aPoint.Y());
1493 // use the themes drop down size
1494 aBounds.aSubEditPos = aContent.TopLeft();
1495 aBounds.aSubEditSize = aContent.GetSize();
1497 else
1499 // use the themes drop down size for the button
1500 aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getOpenWidth(), rOutSz.Height());
1503 else
1505 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1506 nSBWidth = CalcZoom( nSBWidth );
1507 aBounds.aSubEditSize = Size(rOutSz.Width() - nSBWidth, rOutSz.Height());
1508 aBounds.aButtonPos = Point(rOutSz.Width() - nSBWidth, nTop);
1509 aBounds.aButtonSize = Size(nSBWidth, (nBottom-nTop));
1511 return aBounds;
1514 void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars)
1516 if (nWidthInChars != m_nWidthInChars)
1518 m_nWidthInChars = nWidthInChars;
1519 queue_resize();
1523 void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1525 if (nWidth != m_nMaxWidthChars)
1527 m_nMaxWidthChars = nWidth;
1528 queue_resize();
1532 bool ComboBox::set_property(const OUString &rKey, const OUString &rValue)
1534 if (rKey == "width-chars")
1535 SetWidthInChars(rValue.toInt32());
1536 else if (rKey == "max-width-chars")
1537 setMaxWidthChars(rValue.toInt32());
1538 else if (rKey == "can-focus")
1540 // as far as I can see in Gtk, setting a ComboBox as can.focus means
1541 // the focus gets stuck in it, so try here to behave like gtk does
1542 // with the settings that work, i.e. can.focus of false doesn't
1543 // set the hard WB_NOTABSTOP
1544 WinBits nBits = GetStyle();
1545 nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
1546 if (toBool(rValue))
1547 nBits |= WB_TABSTOP;
1548 SetStyle(nBits);
1550 else if (rKey == "placeholder-text")
1551 SetPlaceholderText(rValue);
1552 else
1553 return Control::set_property(rKey, rValue);
1554 return true;
1557 FactoryFunction ComboBox::GetUITestFactory() const
1559 return ComboBoxUIObject::create;
1562 void ComboBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1564 Control::DumpAsPropertyTree(rJsonWriter);
1567 auto entriesNode = rJsonWriter.startArray("entries");
1568 for (int i = 0; i < GetEntryCount(); ++i)
1570 rJsonWriter.putSimpleValue(GetEntry(i));
1575 auto selectedNode = rJsonWriter.startArray("selectedEntries");
1576 for (int i = 0; i < GetSelectedEntryCount(); ++i)
1578 rJsonWriter.putSimpleValue(OUString::number(GetSelectedEntryPos(i)));
1582 rJsonWriter.put("selectedCount", GetSelectedEntryCount());
1584 if (IsUserDrawEnabled())
1585 rJsonWriter.put("customEntryRenderer", true);
1588 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */