nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / control / combobox.cxx
blob188df87a4d9cda1830909b85d38fc33a64efa5aa
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/commandevent.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/uitest/uiobject.hxx>
30 #include <sal/log.hxx>
32 #include <listbox.hxx>
33 #include <controldata.hxx>
34 #include <comphelper/lok.hxx>
35 #include <tools/json_writer.hxx>
37 namespace {
39 struct ComboBoxBounds
41 Point aSubEditPos;
42 Size aSubEditSize;
44 Point aButtonPos;
45 Size aButtonSize;
50 struct ComboBox::Impl
52 ComboBox & m_rThis;
53 VclPtr<Edit> m_pSubEdit;
54 VclPtr<ImplListBox> m_pImplLB;
55 VclPtr<ImplBtn> m_pBtn;
56 VclPtr<ImplListBoxFloatingWindow> m_pFloatWin;
57 sal_uInt16 m_nDDHeight;
58 sal_Unicode m_cMultiSep;
59 bool m_isDDAutoSize : 1;
60 bool m_isSyntheticModify : 1;
61 bool m_isKeyBoardModify : 1;
62 bool m_isMatchCase : 1;
63 sal_Int32 m_nMaxWidthChars;
64 sal_Int32 m_nWidthInChars;
65 Link<ComboBox&,void> m_SelectHdl;
67 explicit Impl(ComboBox & rThis)
68 : m_rThis(rThis)
69 , m_nDDHeight(0)
70 , m_cMultiSep(0)
71 , m_isDDAutoSize(false)
72 , m_isSyntheticModify(false)
73 , m_isKeyBoardModify(false)
74 , m_isMatchCase(false)
75 , m_nMaxWidthChars(0)
76 , m_nWidthInChars(-1)
80 void ImplInitComboBoxData();
81 void ImplUpdateFloatSelection();
82 ComboBoxBounds calcComboBoxDropDownComponentBounds(
83 const Size &rOutSize, const Size &rBorderOutSize) const;
85 DECL_LINK( ImplSelectHdl, LinkParamNone*, void );
86 DECL_LINK( ImplCancelHdl, LinkParamNone*, void );
87 DECL_LINK( ImplDoubleClickHdl, ImplListBoxWindow*, void );
88 DECL_LINK( ImplClickBtnHdl, void*, void );
89 DECL_LINK( ImplPopupModeEndHdl, FloatingWindow*, void );
90 DECL_LINK( ImplSelectionChangedHdl, sal_Int32, void );
91 DECL_LINK( ImplAutocompleteHdl, Edit&, void );
92 DECL_LINK( ImplListItemSelectHdl , LinkParamNone*, void );
96 static void lcl_GetSelectedEntries( ::std::set< sal_Int32 >& rSelectedPos, const OUString& rText, sal_Unicode cTokenSep, const ImplEntryList* pEntryList )
98 if (rText.isEmpty())
99 return;
101 sal_Int32 nIdx{0};
102 do {
103 const sal_Int32 nPos = pEntryList->FindEntry(comphelper::string::strip(rText.getToken(0, cTokenSep, nIdx), ' '));
104 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
105 rSelectedPos.insert( nPos );
106 } while (nIdx>=0);
109 ComboBox::ComboBox(vcl::Window *const pParent, WinBits const nStyle)
110 : Edit( WindowType::COMBOBOX )
111 , m_pImpl(new Impl(*this))
113 m_pImpl->ImplInitComboBoxData();
114 ImplInit( pParent, nStyle );
115 SetWidthInChars(-1);
118 ComboBox::~ComboBox()
120 disposeOnce();
123 void ComboBox::dispose()
125 m_pImpl->m_pSubEdit.disposeAndClear();
127 VclPtr< ImplListBox > pImplLB = m_pImpl->m_pImplLB;
128 m_pImpl->m_pImplLB.clear();
129 pImplLB.disposeAndClear();
131 m_pImpl->m_pFloatWin.disposeAndClear();
132 m_pImpl->m_pBtn.disposeAndClear();
133 Edit::dispose();
136 void ComboBox::Impl::ImplInitComboBoxData()
138 m_pSubEdit.disposeAndClear();
139 m_pBtn = nullptr;
140 m_pImplLB = nullptr;
141 m_pFloatWin = nullptr;
143 m_nDDHeight = 0;
144 m_isDDAutoSize = true;
145 m_isSyntheticModify = false;
146 m_isKeyBoardModify = false;
147 m_isMatchCase = false;
148 m_cMultiSep = ';';
149 m_nMaxWidthChars = -1;
150 m_nWidthInChars = -1;
153 void ComboBox::ImplCalcEditHeight()
155 sal_Int32 nLeft, nTop, nRight, nBottom;
156 GetBorder( nLeft, nTop, nRight, nBottom );
157 m_pImpl->m_nDDHeight = static_cast<sal_uInt16>(m_pImpl->m_pSubEdit->GetTextHeight() + nTop + nBottom + 4);
158 if ( !IsDropDownBox() )
159 m_pImpl->m_nDDHeight += 4;
161 tools::Rectangle aCtrlRegion( Point( 0, 0 ), Size( 10, 10 ) );
162 tools::Rectangle aBoundRegion, aContentRegion;
163 ImplControlValue aControlValue;
164 ControlType aType = IsDropDownBox() ? ControlType::Combobox : ControlType::Editbox;
165 if( GetNativeControlRegion( aType, ControlPart::Entire,
166 aCtrlRegion,
167 ControlState::ENABLED,
168 aControlValue,
169 aBoundRegion, aContentRegion ) )
171 const tools::Long nNCHeight = aBoundRegion.GetHeight();
172 if (m_pImpl->m_nDDHeight < nNCHeight)
173 m_pImpl->m_nDDHeight = sal::static_int_cast<sal_uInt16>(nNCHeight);
177 void ComboBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
179 bool bNoBorder = ( nStyle & WB_NOBORDER ) != 0;
180 if ( !(nStyle & WB_DROPDOWN) )
182 nStyle &= ~WB_BORDER;
183 nStyle |= WB_NOBORDER;
185 else
187 if ( !bNoBorder )
188 nStyle |= WB_BORDER;
191 Edit::ImplInit( pParent, nStyle );
192 SetBackground();
194 // DropDown ?
195 WinBits nEditStyle = nStyle & ( WB_LEFT | WB_RIGHT | WB_CENTER );
196 WinBits nListStyle = nStyle;
197 if( nStyle & WB_DROPDOWN )
199 m_pImpl->m_pFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
200 if (!IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
201 m_pImpl->m_pFloatWin->RequestDoubleBuffering(true);
202 m_pImpl->m_pFloatWin->SetAutoWidth( true );
203 m_pImpl->m_pFloatWin->SetPopupModeEndHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplPopupModeEndHdl) );
205 m_pImpl->m_pBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
206 ImplInitDropDownButton( m_pImpl->m_pBtn );
207 m_pImpl->m_pBtn->SetMBDownHdl( LINK( m_pImpl.get(), ComboBox::Impl, ImplClickBtnHdl ) );
208 m_pImpl->m_pBtn->Show();
210 nEditStyle |= WB_NOBORDER;
211 nListStyle &= ~WB_BORDER;
212 nListStyle |= WB_NOBORDER;
214 else
216 if ( !bNoBorder )
218 nEditStyle |= WB_BORDER;
219 nListStyle &= ~WB_NOBORDER;
220 nListStyle |= WB_BORDER;
224 m_pImpl->m_pSubEdit.set( VclPtr<Edit>::Create( this, nEditStyle ) );
225 m_pImpl->m_pSubEdit->EnableRTL( false );
226 SetSubEdit( m_pImpl->m_pSubEdit );
227 m_pImpl->m_pSubEdit->SetPosPixel( Point() );
228 EnableAutocomplete( true );
229 m_pImpl->m_pSubEdit->Show();
231 vcl::Window* pLBParent = this;
232 if (m_pImpl->m_pFloatWin)
233 pLBParent = m_pImpl->m_pFloatWin;
234 m_pImpl->m_pImplLB = VclPtr<ImplListBox>::Create( pLBParent, nListStyle|WB_SIMPLEMODE|WB_AUTOHSCROLL );
235 m_pImpl->m_pImplLB->SetPosPixel( Point() );
236 m_pImpl->m_pImplLB->SetSelectHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplSelectHdl) );
237 m_pImpl->m_pImplLB->SetCancelHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplCancelHdl) );
238 m_pImpl->m_pImplLB->SetDoubleClickHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplDoubleClickHdl) );
239 m_pImpl->m_pImplLB->SetSelectionChangedHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplSelectionChangedHdl) );
240 m_pImpl->m_pImplLB->SetListItemSelectHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplListItemSelectHdl) );
241 m_pImpl->m_pImplLB->Show();
243 if (m_pImpl->m_pFloatWin)
244 m_pImpl->m_pFloatWin->SetImplListBox( m_pImpl->m_pImplLB );
245 else
246 m_pImpl->m_pImplLB->GetMainWindow()->AllowGrabFocus( true );
248 ImplCalcEditHeight();
250 SetCompoundControl( true );
253 WinBits ComboBox::ImplInitStyle( WinBits nStyle )
255 if ( !(nStyle & WB_NOTABSTOP) )
256 nStyle |= WB_TABSTOP;
257 if ( !(nStyle & WB_NOGROUP) )
258 nStyle |= WB_GROUP;
259 return nStyle;
262 void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
264 m_pImpl->m_isMatchCase = bMatchCase;
266 if ( bEnable )
267 m_pImpl->m_pSubEdit->SetAutocompleteHdl( LINK(m_pImpl.get(), ComboBox::Impl, ImplAutocompleteHdl) );
268 else
269 m_pImpl->m_pSubEdit->SetAutocompleteHdl( Link<Edit&,void>() );
272 bool ComboBox::IsAutocompleteEnabled() const
274 return m_pImpl->m_pSubEdit->GetAutocompleteHdl().IsSet();
277 IMPL_LINK_NOARG(ComboBox::Impl, ImplClickBtnHdl, void*, void)
279 m_rThis.CallEventListeners( VclEventId::DropdownPreOpen );
280 m_pSubEdit->GrabFocus();
281 if (!m_pImplLB->GetEntryList()->GetMRUCount())
282 ImplUpdateFloatSelection();
283 else
284 m_pImplLB->SelectEntry( 0 , true );
285 m_pBtn->SetPressed( true );
286 m_rThis.SetSelection( Selection( 0, SELECTION_MAX ) );
287 m_pFloatWin->StartFloat( true );
288 m_rThis.CallEventListeners( VclEventId::DropdownOpen );
290 m_rThis.ImplClearLayoutData();
291 if (m_pImplLB)
292 m_pImplLB->GetMainWindow()->ImplClearLayoutData();
295 IMPL_LINK_NOARG(ComboBox::Impl, ImplPopupModeEndHdl, FloatingWindow*, void)
297 if (m_pFloatWin->IsPopupModeCanceled())
299 if (!m_pImplLB->GetEntryList()->IsEntryPosSelected(
300 m_pFloatWin->GetPopupModeStartSaveSelection()))
302 m_pImplLB->SelectEntry(m_pFloatWin->GetPopupModeStartSaveSelection(), true);
303 bool bTravelSelect = m_pImplLB->IsTravelSelect();
304 m_pImplLB->SetTravelSelect( true );
305 m_rThis.Select();
306 m_pImplLB->SetTravelSelect( bTravelSelect );
310 m_rThis.ImplClearLayoutData();
311 if (m_pImplLB)
312 m_pImplLB->GetMainWindow()->ImplClearLayoutData();
314 m_pBtn->SetPressed( false );
315 m_rThis.CallEventListeners( VclEventId::DropdownClose );
318 IMPL_LINK(ComboBox::Impl, ImplAutocompleteHdl, Edit&, rEdit, void)
320 Selection aSel = rEdit.GetSelection();
323 OUString aFullText = rEdit.GetText();
324 OUString aStartText = aFullText.copy( 0, static_cast<sal_Int32>(aSel.Max()) );
325 sal_Int32 nStart = m_pImplLB->GetCurrentPos();
327 if ( nStart == LISTBOX_ENTRY_NOTFOUND )
328 nStart = 0;
330 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
331 if (!m_isMatchCase)
333 // Try match case insensitive from current position
334 nPos = m_pImplLB->GetEntryList()->FindMatchingEntry(aStartText, nStart, true);
335 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
336 // Try match case insensitive, but from start
337 nPos = m_pImplLB->GetEntryList()->FindMatchingEntry(aStartText, 0, true);
340 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
341 // Try match full from current position
342 nPos = m_pImplLB->GetEntryList()->FindMatchingEntry(aStartText, nStart, false);
343 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
344 // Match full, but from start
345 nPos = m_pImplLB->GetEntryList()->FindMatchingEntry(aStartText, 0, false);
347 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
349 OUString aText = m_pImplLB->GetEntryList()->GetEntryText( nPos );
350 Selection aSelection( aText.getLength(), aStartText.getLength() );
351 rEdit.SetText( aText, aSelection );
356 IMPL_LINK_NOARG(ComboBox::Impl, ImplSelectHdl, LinkParamNone*, void)
358 bool bPopup = m_rThis.IsInDropDown();
359 bool bCallSelect = false;
360 if (m_pImplLB->IsSelectionChanged() || bPopup)
362 OUString aText;
363 if (m_rThis.IsMultiSelectionEnabled())
365 aText = m_pSubEdit->GetText();
367 // remove all entries to which there is an entry, but which is not selected
368 sal_Int32 nIndex = 0;
369 while ( nIndex >= 0 )
371 sal_Int32 nPrevIndex = nIndex;
372 OUString aToken = aText.getToken( 0, m_cMultiSep, nIndex );
373 sal_Int32 nTokenLen = aToken.getLength();
374 aToken = comphelper::string::strip(aToken, ' ');
375 sal_Int32 nP = m_pImplLB->GetEntryList()->FindEntry( aToken );
376 if ((nP != LISTBOX_ENTRY_NOTFOUND) && (!m_pImplLB->GetEntryList()->IsEntryPosSelected(nP)))
378 aText = aText.replaceAt( nPrevIndex, nTokenLen, "" );
379 nIndex = nIndex - nTokenLen;
380 sal_Int32 nSepCount=0;
381 if ((nPrevIndex+nSepCount < aText.getLength()) && (aText[nPrevIndex+nSepCount] == m_cMultiSep))
383 nIndex--;
384 ++nSepCount;
386 aText = aText.replaceAt( nPrevIndex, nSepCount, "" );
388 aText = comphelper::string::strip(aText, ' ');
391 // attach missing entries
392 ::std::set< sal_Int32 > aSelInText;
393 lcl_GetSelectedEntries( aSelInText, aText, m_cMultiSep, m_pImplLB->GetEntryList() );
394 sal_Int32 nSelectedEntries = m_pImplLB->GetEntryList()->GetSelectedEntryCount();
395 for ( sal_Int32 n = 0; n < nSelectedEntries; n++ )
397 sal_Int32 nP = m_pImplLB->GetEntryList()->GetSelectedEntryPos( n );
398 if ( !aSelInText.count( nP ) )
400 if (!aText.isEmpty() && (aText[aText.getLength()-1] != m_cMultiSep))
401 aText += OUStringChar(m_cMultiSep);
402 if ( !aText.isEmpty() )
403 aText += " "; // slightly loosen
404 aText += m_pImplLB->GetEntryList()->GetEntryText( nP ) +
405 OUStringChar(m_cMultiSep);
408 aText = comphelper::string::stripEnd( aText, m_cMultiSep );
410 else
412 aText = m_pImplLB->GetEntryList()->GetSelectedEntry( 0 );
415 m_pSubEdit->SetText( aText );
417 Selection aNewSelection( 0, aText.getLength() );
418 if (m_rThis.IsMultiSelectionEnabled())
419 aNewSelection.Min() = aText.getLength();
420 m_pSubEdit->SetSelection( aNewSelection );
422 bCallSelect = true;
425 // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
426 bool bMenuSelect = bPopup && !m_pImplLB->IsTravelSelect() && (!m_rThis.IsMultiSelectionEnabled() || !m_pImplLB->GetSelectModifier());
427 if (bMenuSelect)
429 m_pFloatWin->EndPopupMode();
430 m_rThis.GrabFocus();
433 if ( bCallSelect )
435 m_isKeyBoardModify = !bMenuSelect;
436 m_pSubEdit->SetModifyFlag();
437 m_isSyntheticModify = true;
438 m_rThis.Modify();
439 m_isSyntheticModify = false;
440 m_rThis.Select();
441 m_isKeyBoardModify = false;
445 bool ComboBox::IsSyntheticModify() const
447 return m_pImpl->m_isSyntheticModify;
450 bool ComboBox::IsModifyByKeyboard() const
452 return m_pImpl->m_isKeyBoardModify;
455 IMPL_LINK_NOARG( ComboBox::Impl, ImplListItemSelectHdl, LinkParamNone*, void )
457 m_rThis.CallEventListeners( VclEventId::DropdownSelect );
460 IMPL_LINK_NOARG(ComboBox::Impl, ImplCancelHdl, LinkParamNone*, void)
462 if (m_rThis.IsInDropDown())
463 m_pFloatWin->EndPopupMode();
466 IMPL_LINK( ComboBox::Impl, ImplSelectionChangedHdl, sal_Int32, nChanged, void )
468 if (!m_pImplLB->IsTrackingSelect())
470 if (!m_pSubEdit->IsReadOnly() && m_pImplLB->GetEntryList()->IsEntryPosSelected(nChanged))
471 m_pSubEdit->SetText(m_pImplLB->GetEntryList()->GetEntryText(nChanged));
475 IMPL_LINK_NOARG(ComboBox::Impl, ImplDoubleClickHdl, ImplListBoxWindow*, void)
477 m_rThis.DoubleClick();
480 void ComboBox::ToggleDropDown()
482 if( !IsDropDownBox() )
483 return;
485 if (m_pImpl->m_pFloatWin->IsInPopupMode())
486 m_pImpl->m_pFloatWin->EndPopupMode();
487 else
489 m_pImpl->m_pSubEdit->GrabFocus();
490 if (!m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount())
491 m_pImpl->ImplUpdateFloatSelection();
492 else
493 m_pImpl->m_pImplLB->SelectEntry( 0 , true );
494 CallEventListeners( VclEventId::DropdownPreOpen );
495 m_pImpl->m_pBtn->SetPressed( true );
496 SetSelection( Selection( 0, SELECTION_MAX ) );
497 m_pImpl->m_pFloatWin->StartFloat( true );
498 CallEventListeners( VclEventId::DropdownOpen );
502 void ComboBox::Select()
504 ImplCallEventListenersAndHandler( VclEventId::ComboboxSelect, [this] () { m_pImpl->m_SelectHdl.Call(*this); } );
507 void ComboBox::DoubleClick()
509 ImplCallEventListenersAndHandler( VclEventId::ComboboxDoubleClick, [] () {} );
512 bool ComboBox::IsAutoSizeEnabled() const { return m_pImpl->m_isDDAutoSize; }
514 void ComboBox::EnableAutoSize( bool bAuto )
516 m_pImpl->m_isDDAutoSize = bAuto;
517 if (m_pImpl->m_pFloatWin)
519 if (bAuto && !m_pImpl->m_pFloatWin->GetDropDownLineCount())
521 // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
522 AdaptDropDownLineCountToMaximum();
524 else if ( !bAuto )
526 m_pImpl->m_pFloatWin->SetDropDownLineCount( 0 );
531 void ComboBox::SetDropDownLineCount( sal_uInt16 nLines )
533 if (m_pImpl->m_pFloatWin)
534 m_pImpl->m_pFloatWin->SetDropDownLineCount( nLines );
537 void ComboBox::AdaptDropDownLineCountToMaximum()
539 // Adapt to maximum allowed number.
540 // Limit for LOK as we can't render outside of the dialog canvas.
541 if (comphelper::LibreOfficeKit::isActive())
542 SetDropDownLineCount(11);
543 else
544 SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
547 sal_uInt16 ComboBox::GetDropDownLineCount() const
549 sal_uInt16 nLines = 0;
550 if (m_pImpl->m_pFloatWin)
551 nLines = m_pImpl->m_pFloatWin->GetDropDownLineCount();
552 return nLines;
555 void ComboBox::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
556 PosSizeFlags nFlags )
558 if( IsDropDownBox() && ( nFlags & PosSizeFlags::Size ) )
560 Size aPrefSz = m_pImpl->m_pFloatWin->GetPrefSize();
561 if ((nFlags & PosSizeFlags::Height) && (nHeight >= 2*m_pImpl->m_nDDHeight))
562 aPrefSz.setHeight( nHeight-m_pImpl->m_nDDHeight );
563 if ( nFlags & PosSizeFlags::Width )
564 aPrefSz.setWidth( nWidth );
565 m_pImpl->m_pFloatWin->SetPrefSize( aPrefSz );
567 if (IsAutoSizeEnabled())
568 nHeight = m_pImpl->m_nDDHeight;
571 Edit::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
574 void ComboBox::Resize()
576 Control::Resize();
578 if (m_pImpl->m_pSubEdit)
580 Size aOutSz = GetOutputSizePixel();
581 if( IsDropDownBox() )
583 ComboBoxBounds aBounds(m_pImpl->calcComboBoxDropDownComponentBounds(aOutSz,
584 GetWindow(GetWindowType::Border)->GetOutputSizePixel()));
585 m_pImpl->m_pSubEdit->SetPosSizePixel(aBounds.aSubEditPos, aBounds.aSubEditSize);
586 m_pImpl->m_pBtn->SetPosSizePixel(aBounds.aButtonPos, aBounds.aButtonSize);
588 else
590 m_pImpl->m_pSubEdit->SetSizePixel(Size(aOutSz.Width(), m_pImpl->m_nDDHeight));
591 m_pImpl->m_pImplLB->setPosSizePixel(0, m_pImpl->m_nDDHeight,
592 aOutSz.Width(), aOutSz.Height() - m_pImpl->m_nDDHeight);
593 if ( !GetText().isEmpty() )
594 m_pImpl->ImplUpdateFloatSelection();
598 // adjust the size of the FloatingWindow even when invisible
599 // as KEY_PGUP/DOWN is being processed...
600 if (m_pImpl->m_pFloatWin)
601 m_pImpl->m_pFloatWin->SetSizePixel(m_pImpl->m_pFloatWin->CalcFloatSize());
604 bool ComboBox::IsDropDownBox() const { return m_pImpl->m_pFloatWin != nullptr; }
606 void ComboBox::FillLayoutData() const
608 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
609 AppendLayoutData( *m_pImpl->m_pSubEdit );
610 m_pImpl->m_pSubEdit->SetLayoutDataParent( this );
611 ImplListBoxWindow* rMainWindow = m_pImpl->m_pImplLB->GetMainWindow();
612 if (m_pImpl->m_pFloatWin)
614 // dropdown mode
615 if (m_pImpl->m_pFloatWin->IsReallyVisible())
617 AppendLayoutData( *rMainWindow );
618 rMainWindow->SetLayoutDataParent( this );
621 else
623 AppendLayoutData( *rMainWindow );
624 rMainWindow->SetLayoutDataParent( this );
628 void ComboBox::StateChanged( StateChangedType nType )
630 Edit::StateChanged( nType );
632 if ( nType == StateChangedType::ReadOnly )
634 m_pImpl->m_pImplLB->SetReadOnly( IsReadOnly() );
635 if (m_pImpl->m_pBtn)
636 m_pImpl->m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
638 else if ( nType == StateChangedType::Enable )
640 m_pImpl->m_pSubEdit->Enable( IsEnabled() );
641 m_pImpl->m_pImplLB->Enable( IsEnabled() && !IsReadOnly() );
642 if (m_pImpl->m_pBtn)
643 m_pImpl->m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
644 Invalidate();
646 else if( nType == StateChangedType::UpdateMode )
648 m_pImpl->m_pImplLB->SetUpdateMode( IsUpdateMode() );
650 else if ( nType == StateChangedType::Zoom )
652 m_pImpl->m_pImplLB->SetZoom( GetZoom() );
653 m_pImpl->m_pSubEdit->SetZoom( GetZoom() );
654 ImplCalcEditHeight();
655 Resize();
657 else if ( nType == StateChangedType::ControlFont )
659 m_pImpl->m_pImplLB->SetControlFont( GetControlFont() );
660 m_pImpl->m_pSubEdit->SetControlFont( GetControlFont() );
661 ImplCalcEditHeight();
662 Resize();
664 else if ( nType == StateChangedType::ControlForeground )
666 m_pImpl->m_pImplLB->SetControlForeground( GetControlForeground() );
667 m_pImpl->m_pSubEdit->SetControlForeground( GetControlForeground() );
669 else if ( nType == StateChangedType::ControlBackground )
671 m_pImpl->m_pImplLB->SetControlBackground( GetControlBackground() );
672 m_pImpl->m_pSubEdit->SetControlBackground( GetControlBackground() );
674 else if ( nType == StateChangedType::Style )
676 SetStyle( ImplInitStyle( GetStyle() ) );
677 m_pImpl->m_pImplLB->GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
679 else if( nType == StateChangedType::Mirroring )
681 if (m_pImpl->m_pBtn)
683 m_pImpl->m_pBtn->EnableRTL( IsRTLEnabled() );
684 ImplInitDropDownButton( m_pImpl->m_pBtn );
686 m_pImpl->m_pSubEdit->CompatStateChanged( StateChangedType::Mirroring );
687 m_pImpl->m_pImplLB->EnableRTL( IsRTLEnabled() );
688 Resize();
692 void ComboBox::DataChanged( const DataChangedEvent& rDCEvt )
694 Control::DataChanged( rDCEvt );
696 if ( !((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
697 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
698 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
699 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
700 return;
702 if (m_pImpl->m_pBtn)
704 m_pImpl->m_pBtn->SetSettings( GetSettings() );
705 ImplInitDropDownButton( m_pImpl->m_pBtn );
707 Resize();
708 m_pImpl->m_pImplLB->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
710 SetBackground(); // due to a hack in Window::UpdateSettings the background must be reset
711 // otherwise it will overpaint NWF drawn comboboxes
714 bool ComboBox::EventNotify( NotifyEvent& rNEvt )
716 bool bDone = false;
717 if ((rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
718 && (rNEvt.GetWindow() == m_pImpl->m_pSubEdit)
719 && !IsReadOnly())
721 KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
722 sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
723 switch( nKeyCode )
725 case KEY_UP:
726 case KEY_DOWN:
727 case KEY_PAGEUP:
728 case KEY_PAGEDOWN:
730 m_pImpl->ImplUpdateFloatSelection();
731 if ((nKeyCode == KEY_DOWN) && m_pImpl->m_pFloatWin
732 && !m_pImpl->m_pFloatWin->IsInPopupMode()
733 && aKeyEvt.GetKeyCode().IsMod2())
735 CallEventListeners( VclEventId::DropdownPreOpen );
736 m_pImpl->m_pBtn->SetPressed( true );
737 if (m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount())
738 m_pImpl->m_pImplLB->SelectEntry( 0 , true );
739 SetSelection( Selection( 0, SELECTION_MAX ) );
740 m_pImpl->m_pFloatWin->StartFloat( false );
741 CallEventListeners( VclEventId::DropdownOpen );
742 bDone = true;
744 else if ((nKeyCode == KEY_UP) && m_pImpl->m_pFloatWin
745 && m_pImpl->m_pFloatWin->IsInPopupMode()
746 && aKeyEvt.GetKeyCode().IsMod2())
748 m_pImpl->m_pFloatWin->EndPopupMode();
749 bDone = true;
751 else
753 bDone = m_pImpl->m_pImplLB->ProcessKeyInput( aKeyEvt );
756 break;
758 case KEY_RETURN:
760 if ((rNEvt.GetWindow() == m_pImpl->m_pSubEdit) && IsInDropDown())
762 m_pImpl->m_pImplLB->ProcessKeyInput( aKeyEvt );
763 bDone = true;
766 break;
769 else if ((rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS) && m_pImpl->m_pFloatWin)
771 if (m_pImpl->m_pFloatWin->HasChildPathFocus())
772 m_pImpl->m_pSubEdit->GrabFocus();
773 else if (m_pImpl->m_pFloatWin->IsInPopupMode() && !HasChildPathFocus(true))
774 m_pImpl->m_pFloatWin->EndPopupMode();
776 else if( (rNEvt.GetType() == MouseNotifyEvent::COMMAND) &&
777 (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
778 (rNEvt.GetWindow() == m_pImpl->m_pSubEdit) )
780 MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
781 if ( ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
782 || ( ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
783 && HasChildPathFocus()
787 bDone = m_pImpl->m_pImplLB->HandleWheelAsCursorTravel(*rNEvt.GetCommandEvent(), *this);
789 else
791 bDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
794 else if ((rNEvt.GetType() == MouseNotifyEvent::MOUSEBUTTONDOWN)
795 && (rNEvt.GetWindow() == m_pImpl->m_pImplLB->GetMainWindow()))
797 m_pImpl->m_pSubEdit->GrabFocus();
800 return bDone || Edit::EventNotify( rNEvt );
803 void ComboBox::SetText( const OUString& rStr )
805 CallEventListeners( VclEventId::ComboboxSetText );
807 Edit::SetText( rStr );
808 m_pImpl->ImplUpdateFloatSelection();
811 void ComboBox::SetText( const OUString& rStr, const Selection& rNewSelection )
813 CallEventListeners( VclEventId::ComboboxSetText );
815 Edit::SetText( rStr, rNewSelection );
816 m_pImpl->ImplUpdateFloatSelection();
819 void ComboBox::Modify()
821 if (!m_pImpl->m_isSyntheticModify)
822 m_pImpl->ImplUpdateFloatSelection();
824 Edit::Modify();
827 void ComboBox::Impl::ImplUpdateFloatSelection()
829 if (!m_pImplLB || !m_pSubEdit)
830 return;
832 // move text in the ListBox into the visible region
833 m_pImplLB->SetCallSelectionChangedHdl( false );
834 if (!m_rThis.IsMultiSelectionEnabled())
836 OUString aSearchStr( m_pSubEdit->GetText() );
837 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
838 bool bSelect = true;
840 if (m_pImplLB->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND)
842 OUString aCurrent = m_pImplLB->GetEntryList()->GetEntryText(
843 m_pImplLB->GetCurrentPos());
844 if ( aCurrent == aSearchStr )
845 nSelect = m_pImplLB->GetCurrentPos();
848 if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
849 nSelect = m_pImplLB->GetEntryList()->FindEntry( aSearchStr );
850 if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
852 nSelect = m_pImplLB->GetEntryList()->FindMatchingEntry( aSearchStr, 0, true );
853 bSelect = false;
856 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
858 if (!m_pImplLB->IsVisible(nSelect))
859 m_pImplLB->ShowProminentEntry( nSelect );
860 m_pImplLB->SelectEntry( nSelect, bSelect );
862 else
864 nSelect = m_pImplLB->GetEntryList()->GetSelectedEntryPos( 0 );
865 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
866 m_pImplLB->SelectEntry( nSelect, false );
867 m_pImplLB->ResetCurrentPos();
870 else
872 ::std::set< sal_Int32 > aSelInText;
873 lcl_GetSelectedEntries(aSelInText, m_pSubEdit->GetText(), m_cMultiSep, m_pImplLB->GetEntryList());
874 for (sal_Int32 n = 0; n < m_pImplLB->GetEntryList()->GetEntryCount(); n++)
875 m_pImplLB->SelectEntry( n, aSelInText.count( n ) != 0 );
877 m_pImplLB->SetCallSelectionChangedHdl( true );
880 sal_Int32 ComboBox::InsertEntry(const OUString& rStr, sal_Int32 const nPos)
882 assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImpl->m_pImplLB->GetEntryList()->GetEntryCount());
884 sal_Int32 nRealPos;
885 if (nPos == COMBOBOX_APPEND)
886 nRealPos = nPos;
887 else
889 const sal_Int32 nMRUCount = m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
890 assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
891 nRealPos = nPos + nMRUCount;
894 nRealPos = m_pImpl->m_pImplLB->InsertEntry( nRealPos, rStr );
895 nRealPos -= m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
896 CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(nRealPos) );
897 return nRealPos;
900 sal_Int32 ComboBox::InsertEntryWithImage(
901 const OUString& rStr, const Image& rImage, sal_Int32 const nPos)
903 assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImpl->m_pImplLB->GetEntryList()->GetEntryCount());
905 sal_Int32 nRealPos;
906 if (nPos == COMBOBOX_APPEND)
907 nRealPos = nPos;
908 else
910 const sal_Int32 nMRUCount = m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
911 assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
912 nRealPos = nPos + nMRUCount;
915 nRealPos = m_pImpl->m_pImplLB->InsertEntry( nRealPos, rStr, rImage );
916 nRealPos -= m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
917 CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(nRealPos) );
918 return nRealPos;
921 void ComboBox::RemoveEntryAt(sal_Int32 const nPos)
923 const sal_Int32 nMRUCount = m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
924 assert(nPos >= 0 && nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
925 m_pImpl->m_pImplLB->RemoveEntry( nPos + nMRUCount );
926 CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(nPos) );
929 void ComboBox::Clear()
931 if (!m_pImpl->m_pImplLB)
932 return;
933 m_pImpl->m_pImplLB->Clear();
934 CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(-1) );
937 Image ComboBox::GetEntryImage( sal_Int32 nPos ) const
939 if (m_pImpl->m_pImplLB->GetEntryList()->HasEntryImage(nPos))
940 return m_pImpl->m_pImplLB->GetEntryList()->GetEntryImage( nPos );
941 return Image();
944 sal_Int32 ComboBox::GetEntryPos( const OUString& rStr ) const
946 sal_Int32 nPos = m_pImpl->m_pImplLB->GetEntryList()->FindEntry( rStr );
947 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
948 nPos -= m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
949 return nPos;
952 OUString ComboBox::GetEntry( sal_Int32 nPos ) const
954 const sal_Int32 nMRUCount = m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
955 if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
956 return OUString();
958 return m_pImpl->m_pImplLB->GetEntryList()->GetEntryText( nPos + nMRUCount );
961 sal_Int32 ComboBox::GetEntryCount() const
963 if (!m_pImpl->m_pImplLB)
964 return 0;
965 return m_pImpl->m_pImplLB->GetEntryList()->GetEntryCount() - m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount();
968 bool ComboBox::IsTravelSelect() const
970 return m_pImpl->m_pImplLB->IsTravelSelect();
973 bool ComboBox::IsInDropDown() const
975 // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
976 // mbPopupMode is set to false
977 return m_pImpl->m_pFloatWin && m_pImpl->m_pFloatWin->IsInPopupMode() && m_pImpl->m_pFloatWin->ImplIsInPrivatePopupMode();
980 bool ComboBox::IsMultiSelectionEnabled() const
982 return m_pImpl->m_pImplLB->IsMultiSelectionEnabled();
985 void ComboBox::SetSelectHdl(const Link<ComboBox&,void>& rLink) { m_pImpl->m_SelectHdl = rLink; }
987 void ComboBox::SetEntryActivateHdl(const Link<Edit&,bool>& rLink)
989 if (!m_pImpl->m_pSubEdit)
990 return;
991 m_pImpl->m_pSubEdit->SetActivateHdl(rLink);
994 Size ComboBox::GetOptimalSize() const
996 return CalcMinimumSize();
999 tools::Long ComboBox::getMaxWidthScrollBarAndDownButton() const
1001 tools::Long nButtonDownWidth = 0;
1003 vcl::Window *pBorder = GetWindow( GetWindowType::Border );
1004 ImplControlValue aControlValue;
1005 tools::Rectangle aContent, aBound;
1007 // use the full extent of the control
1008 tools::Rectangle aArea( Point(), pBorder->GetOutputSizePixel() );
1010 if ( GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1011 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1013 nButtonDownWidth = aContent.getWidth();
1016 tools::Long nScrollBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1018 return std::max(nScrollBarWidth, nButtonDownWidth);
1021 Size ComboBox::CalcMinimumSize() const
1023 Size aSz;
1025 if (!m_pImpl->m_pImplLB)
1026 return aSz;
1028 if (!IsDropDownBox())
1030 aSz = m_pImpl->m_pImplLB->CalcSize( m_pImpl->m_pImplLB->GetEntryList()->GetEntryCount() );
1031 aSz.AdjustHeight(m_pImpl->m_nDDHeight );
1033 else
1035 aSz.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1037 if (m_pImpl->m_nWidthInChars!= -1)
1038 aSz.setWidth(m_pImpl->m_nWidthInChars * approximate_digit_width());
1039 else
1040 aSz.setWidth(m_pImpl->m_pImplLB->GetMaxEntryWidth());
1043 if (m_pImpl->m_nMaxWidthChars != -1)
1045 tools::Long nMaxWidth = m_pImpl->m_nMaxWidthChars * approximate_char_width();
1046 aSz.setWidth( std::min(aSz.Width(), nMaxWidth) );
1049 if (IsDropDownBox())
1050 aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1052 ComboBoxBounds aBounds(m_pImpl->calcComboBoxDropDownComponentBounds(
1053 Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1054 aSz.AdjustWidth(aBounds.aSubEditPos.X()*2 );
1056 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1058 aSz = CalcWindowSize( aSz );
1059 return aSz;
1062 Size ComboBox::CalcAdjustedSize( const Size& rPrefSize ) const
1064 Size aSz = rPrefSize;
1065 sal_Int32 nLeft, nTop, nRight, nBottom;
1066 static_cast<vcl::Window*>(const_cast<ComboBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1067 aSz.AdjustHeight( -(nTop+nBottom) );
1068 if ( !IsDropDownBox() )
1070 tools::Long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1071 tools::Long nLines = aSz.Height() / nEntryHeight;
1072 if ( nLines < 1 )
1073 nLines = 1;
1074 aSz.setHeight( nLines * nEntryHeight );
1075 aSz.AdjustHeight(m_pImpl->m_nDDHeight );
1077 else
1079 aSz.setHeight( m_pImpl->m_nDDHeight );
1081 aSz.AdjustHeight(nTop+nBottom );
1083 aSz = CalcWindowSize( aSz );
1084 return aSz;
1087 Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1089 // show ScrollBars where appropriate
1090 Size aMinSz = CalcMinimumSize();
1091 Size aSz;
1093 // height
1094 if ( nLines )
1096 if ( !IsDropDownBox() )
1097 aSz.setHeight( m_pImpl->m_pImplLB->CalcSize( nLines ).Height() + m_pImpl->m_nDDHeight );
1098 else
1099 aSz.setHeight( m_pImpl->m_nDDHeight );
1101 else
1102 aSz.setHeight( aMinSz.Height() );
1104 // width
1105 if ( nColumns )
1106 aSz.setWidth( nColumns * approximate_char_width() );
1107 else
1108 aSz.setWidth( aMinSz.Width() );
1110 if ( IsDropDownBox() )
1111 aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1113 if ( !IsDropDownBox() )
1115 if ( aSz.Width() < aMinSz.Width() )
1116 aSz.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1117 if ( aSz.Height() < aMinSz.Height() )
1118 aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1121 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1123 aSz = CalcWindowSize( aSz );
1124 return aSz;
1127 tools::Long ComboBox::GetDropDownEntryHeight() const
1129 return m_pImpl->m_pImplLB->GetEntryHeight();
1132 void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1134 tools::Long nCharWidth = GetTextWidth(OUString(u'x'));
1135 if ( !IsDropDownBox() )
1137 Size aOutSz = m_pImpl->m_pImplLB->GetMainWindow()->GetOutputSizePixel();
1138 rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1139 rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight());
1141 else
1143 Size aOutSz = m_pImpl->m_pSubEdit->GetOutputSizePixel();
1144 rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1145 rnLines = 1;
1149 void ComboBox::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags )
1151 m_pImpl->m_pImplLB->GetMainWindow()->ApplySettings(*pDev);
1153 Point aPos = pDev->LogicToPixel( rPos );
1154 Size aSize = GetSizePixel();
1155 vcl::Font aFont = m_pImpl->m_pImplLB->GetMainWindow()->GetDrawPixelFont( pDev );
1157 pDev->Push();
1158 pDev->SetMapMode();
1159 pDev->SetFont( aFont );
1160 pDev->SetTextFillColor();
1162 // Border/Background
1163 pDev->SetLineColor();
1164 pDev->SetFillColor();
1165 bool bBorder = (GetStyle() & WB_BORDER);
1166 bool bBackground = IsControlBackground();
1167 if ( bBorder || bBackground )
1169 tools::Rectangle aRect( aPos, aSize );
1170 // aRect.Top() += nEditHeight;
1171 if ( bBorder )
1173 ImplDrawFrame( pDev, aRect );
1175 if ( bBackground )
1177 pDev->SetFillColor( GetControlBackground() );
1178 pDev->DrawRect( aRect );
1182 // contents
1183 if ( !IsDropDownBox() )
1185 tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
1186 tools::Long nTextHeight = pDev->GetTextHeight();
1187 tools::Long nEditHeight = nTextHeight + 6*nOnePixel;
1188 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1190 // First, draw the edit part
1191 Size aOrigSize(m_pImpl->m_pSubEdit->GetSizePixel());
1192 m_pImpl->m_pSubEdit->SetSizePixel(Size(aSize.Width(), nEditHeight));
1193 m_pImpl->m_pSubEdit->Draw( pDev, aPos, nFlags );
1194 m_pImpl->m_pSubEdit->SetSizePixel(aOrigSize);
1196 // Second, draw the listbox
1197 if ( GetStyle() & WB_CENTER )
1198 nTextStyle |= DrawTextFlags::Center;
1199 else if ( GetStyle() & WB_RIGHT )
1200 nTextStyle |= DrawTextFlags::Right;
1201 else
1202 nTextStyle |= DrawTextFlags::Left;
1204 if ( nFlags & DrawFlags::Mono )
1206 pDev->SetTextColor( COL_BLACK );
1208 else
1210 if ( !IsEnabled() )
1212 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1213 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1215 else
1217 pDev->SetTextColor( GetTextColor() );
1221 tools::Rectangle aClip( aPos, aSize );
1222 pDev->IntersectClipRegion( aClip );
1223 sal_Int32 nLines = static_cast<sal_Int32>( nTextHeight > 0 ? (aSize.Height()-nEditHeight)/nTextHeight : 1 );
1224 if ( !nLines )
1225 nLines = 1;
1226 const sal_Int32 nTEntry = IsReallyVisible() ? m_pImpl->m_pImplLB->GetTopEntry() : 0;
1228 tools::Rectangle aTextRect( aPos, aSize );
1230 aTextRect.AdjustLeft(3*nOnePixel );
1231 aTextRect.AdjustRight( -(3*nOnePixel) );
1232 aTextRect.AdjustTop(nEditHeight + nOnePixel );
1233 aTextRect.SetBottom( aTextRect.Top() + nTextHeight );
1235 // the drawing starts here
1236 for ( sal_Int32 n = 0; n < nLines; ++n )
1238 pDev->DrawText( aTextRect, m_pImpl->m_pImplLB->GetEntryList()->GetEntryText( n+nTEntry ), nTextStyle );
1239 aTextRect.AdjustTop(nTextHeight );
1240 aTextRect.AdjustBottom(nTextHeight );
1244 pDev->Pop();
1246 // Call Edit::Draw after restoring the MapMode...
1247 if ( IsDropDownBox() )
1249 Size aOrigSize(m_pImpl->m_pSubEdit->GetSizePixel());
1250 m_pImpl->m_pSubEdit->SetSizePixel(GetSizePixel());
1251 m_pImpl->m_pSubEdit->Draw( pDev, rPos, nFlags );
1252 m_pImpl->m_pSubEdit->SetSizePixel(aOrigSize);
1253 // DD-Button ?
1257 void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink)
1259 m_pImpl->m_pImplLB->SetUserDrawHdl(rLink);
1262 void ComboBox::SetUserItemSize( const Size& rSz )
1264 m_pImpl->m_pImplLB->GetMainWindow()->SetUserItemSize( rSz );
1267 void ComboBox::EnableUserDraw( bool bUserDraw )
1269 m_pImpl->m_pImplLB->GetMainWindow()->EnableUserDraw( bUserDraw );
1272 bool ComboBox::IsUserDrawEnabled() const
1274 return m_pImpl->m_pImplLB->GetMainWindow()->IsUserDrawEnabled();
1277 void ComboBox::DrawEntry(const UserDrawEvent& rEvt)
1279 SAL_WARN_IF(rEvt.GetWindow() != m_pImpl->m_pImplLB->GetMainWindow(), "vcl", "DrawEntry?!");
1280 m_pImpl->m_pImplLB->GetMainWindow()->DrawEntry(*rEvt.GetRenderContext(), rEvt.GetItemId(), /*bDrawImage*/false, /*bDrawText*/false);
1283 void ComboBox::AddSeparator( sal_Int32 n )
1285 m_pImpl->m_pImplLB->AddSeparator( n );
1288 void ComboBox::SetMRUEntries( const OUString& rEntries )
1290 m_pImpl->m_pImplLB->SetMRUEntries( rEntries, ';' );
1293 OUString ComboBox::GetMRUEntries() const
1295 return m_pImpl->m_pImplLB ? m_pImpl->m_pImplLB->GetMRUEntries( ';' ) : OUString();
1298 void ComboBox::SetMaxMRUCount( sal_Int32 n )
1300 m_pImpl->m_pImplLB->SetMaxMRUCount( n );
1303 sal_Int32 ComboBox::GetMaxMRUCount() const
1305 return m_pImpl->m_pImplLB ? m_pImpl->m_pImplLB->GetMaxMRUCount() : 0;
1308 sal_uInt16 ComboBox::GetDisplayLineCount() const
1310 return m_pImpl->m_pImplLB ? m_pImpl->m_pImplLB->GetDisplayLineCount() : 0;
1313 void ComboBox::SetEntryData( sal_Int32 nPos, void* pNewData )
1315 m_pImpl->m_pImplLB->SetEntryData( nPos + m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount(), pNewData );
1318 void* ComboBox::GetEntryData( sal_Int32 nPos ) const
1320 return m_pImpl->m_pImplLB->GetEntryList()->GetEntryData(
1321 nPos + m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount() );
1324 sal_Int32 ComboBox::GetTopEntry() const
1326 sal_Int32 nPos = GetEntryCount() ? m_pImpl->m_pImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1327 if (nPos < m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount())
1328 nPos = 0;
1329 return nPos;
1332 tools::Rectangle ComboBox::GetDropDownPosSizePixel() const
1334 return m_pImpl->m_pFloatWin
1335 ? m_pImpl->m_pFloatWin->GetWindowExtentsRelative(this)
1336 : tools::Rectangle();
1339 const Wallpaper& ComboBox::GetDisplayBackground() const
1341 if (!m_pImpl->m_pSubEdit->IsBackground())
1342 return Control::GetDisplayBackground();
1344 const Wallpaper& rBack = m_pImpl->m_pSubEdit->GetBackground();
1345 if( ! rBack.IsBitmap() &&
1346 ! rBack.IsGradient() &&
1347 rBack == COL_TRANSPARENT
1349 return Control::GetDisplayBackground();
1350 return rBack;
1353 sal_Int32 ComboBox::GetSelectedEntryCount() const
1355 return m_pImpl->m_pImplLB->GetEntryList()->GetSelectedEntryCount();
1358 sal_Int32 ComboBox::GetSelectedEntryPos( sal_Int32 nIndex ) const
1360 sal_Int32 nPos = m_pImpl->m_pImplLB->GetEntryList()->GetSelectedEntryPos( nIndex );
1361 if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1363 if (nPos < m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount())
1364 nPos = m_pImpl->m_pImplLB->GetEntryList()->FindEntry(m_pImpl->m_pImplLB->GetEntryList()->GetEntryText(nPos));
1365 nPos = sal::static_int_cast<sal_Int32>(nPos - m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount());
1367 return nPos;
1370 bool ComboBox::IsEntryPosSelected( sal_Int32 nPos ) const
1372 return m_pImpl->m_pImplLB->GetEntryList()->IsEntryPosSelected(
1373 nPos + m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount() );
1376 void ComboBox::SelectEntryPos( sal_Int32 nPos, bool bSelect)
1378 if (nPos < m_pImpl->m_pImplLB->GetEntryList()->GetEntryCount())
1379 m_pImpl->m_pImplLB->SelectEntry(
1380 nPos + m_pImpl->m_pImplLB->GetEntryList()->GetMRUCount(), bSelect);
1383 void ComboBox::SetNoSelection()
1385 m_pImpl->m_pImplLB->SetNoSelection();
1386 m_pImpl->m_pSubEdit->SetText( OUString() );
1389 tools::Rectangle ComboBox::GetBoundingRectangle( sal_Int32 nItem ) const
1391 tools::Rectangle aRect = m_pImpl->m_pImplLB->GetMainWindow()->GetBoundingRectangle( nItem );
1392 tools::Rectangle aOffset = m_pImpl->m_pImplLB->GetMainWindow()->GetWindowExtentsRelative( static_cast<vcl::Window*>(const_cast<ComboBox *>(this)) );
1393 aRect.Move( aOffset.TopLeft().X(), aOffset.TopLeft().Y() );
1394 return aRect;
1397 void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle )
1399 Window::SetBorderStyle( nBorderStyle );
1400 if ( !IsDropDownBox() )
1402 m_pImpl->m_pSubEdit->SetBorderStyle( nBorderStyle );
1403 m_pImpl->m_pImplLB->SetBorderStyle( nBorderStyle );
1407 tools::Long ComboBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
1409 if( !HasLayoutData() )
1410 FillLayoutData();
1412 // check whether rPoint fits at all
1413 tools::Long nIndex = Control::GetIndexForPoint( rPoint );
1414 if( nIndex != -1 )
1416 // point must be either in main list window
1417 // or in impl window (dropdown case)
1418 ImplListBoxWindow* rMain = m_pImpl->m_pImplLB->GetMainWindow();
1420 // convert coordinates to ImplListBoxWindow pixel coordinate space
1421 Point aConvPoint = LogicToPixel( rPoint );
1422 aConvPoint = OutputToAbsoluteScreenPixel( aConvPoint );
1423 aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPoint );
1424 aConvPoint = rMain->PixelToLogic( aConvPoint );
1426 // try to find entry
1427 sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
1428 if( nEntry == LISTBOX_ENTRY_NOTFOUND )
1429 nIndex = -1;
1430 else
1431 rPos = nEntry;
1434 // get line relative index
1435 if( nIndex != -1 )
1436 nIndex = ToRelativeLineIndex( nIndex );
1438 return nIndex;
1441 ComboBoxBounds ComboBox::Impl::calcComboBoxDropDownComponentBounds(
1442 const Size &rOutSz, const Size &rBorderOutSz) const
1444 ComboBoxBounds aBounds;
1446 tools::Long nTop = 0;
1447 tools::Long nBottom = rOutSz.Height();
1449 vcl::Window *pBorder = m_rThis.GetWindow( GetWindowType::Border );
1450 ImplControlValue aControlValue;
1451 Point aPoint;
1452 tools::Rectangle aContent, aBound;
1454 // use the full extent of the control
1455 tools::Rectangle aArea( aPoint, rBorderOutSz );
1457 if (m_rThis.GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1458 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1460 // convert back from border space to local coordinates
1461 aPoint = pBorder->ScreenToOutputPixel(m_rThis.OutputToScreenPixel(aPoint));
1462 aContent.Move(-aPoint.X(), -aPoint.Y());
1464 aBounds.aButtonPos = Point(aContent.Left(), nTop);
1465 aBounds.aButtonSize = Size(aContent.getWidth(), (nBottom-nTop));
1467 // adjust the size of the edit field
1468 if (m_rThis.GetNativeControlRegion(ControlType::Combobox, ControlPart::SubEdit,
1469 aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1471 // convert back from border space to local coordinates
1472 aContent.Move(-aPoint.X(), -aPoint.Y());
1474 // use the themes drop down size
1475 aBounds.aSubEditPos = aContent.TopLeft();
1476 aBounds.aSubEditSize = aContent.GetSize();
1478 else
1480 // use the themes drop down size for the button
1481 aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getWidth(), rOutSz.Height());
1484 else
1486 tools::Long nSBWidth = m_rThis.GetSettings().GetStyleSettings().GetScrollBarSize();
1487 nSBWidth = m_rThis.CalcZoom( nSBWidth );
1488 aBounds.aSubEditSize = Size(rOutSz.Width() - nSBWidth, rOutSz.Height());
1489 aBounds.aButtonPos = Point(rOutSz.Width() - nSBWidth, nTop);
1490 aBounds.aButtonSize = Size(nSBWidth, (nBottom-nTop));
1492 return aBounds;
1495 void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars)
1497 if (nWidthInChars != m_pImpl->m_nWidthInChars)
1499 m_pImpl->m_nWidthInChars = nWidthInChars;
1500 queue_resize();
1504 void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1506 if (nWidth != m_pImpl->m_nMaxWidthChars)
1508 m_pImpl->m_nMaxWidthChars = nWidth;
1509 queue_resize();
1513 bool ComboBox::set_property(const OString &rKey, const OUString &rValue)
1515 if (rKey == "width-chars")
1516 SetWidthInChars(rValue.toInt32());
1517 else if (rKey == "max-width-chars")
1518 setMaxWidthChars(rValue.toInt32());
1519 else if (rKey == "can-focus")
1521 // as far as I can see in Gtk, setting a ComboBox as can.focus means
1522 // the focus gets stuck in it, so try here to behave like gtk does
1523 // with the settings that work, i.e. can.focus of false doesn't
1524 // set the hard WB_NOTABSTOP
1525 WinBits nBits = GetStyle();
1526 nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
1527 if (toBool(rValue))
1528 nBits |= WB_TABSTOP;
1529 SetStyle(nBits);
1531 else if (rKey == "placeholder-text")
1532 SetPlaceholderText(rValue);
1533 else
1534 return Control::set_property(rKey, rValue);
1535 return true;
1538 FactoryFunction ComboBox::GetUITestFactory() const
1540 return ComboBoxUIObject::create;
1543 void ComboBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1545 Control::DumpAsPropertyTree(rJsonWriter);
1547 auto entriesNode = rJsonWriter.startNode("entries");
1548 for (int i = 0; i < GetEntryCount(); ++i)
1550 auto entryNode = rJsonWriter.startNode("");
1551 rJsonWriter.put("", GetEntry(i));
1554 auto selectedNode = rJsonWriter.startNode("selectedEntries");
1555 for (int i = 0; i < GetSelectedEntryCount(); ++i)
1557 auto entryNode = rJsonWriter.startNode("");
1558 rJsonWriter.put("", GetSelectedEntryPos(i));
1561 rJsonWriter.put("selectedCount", GetSelectedEntryCount());
1564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */