1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/toolkit/combobox.hxx>
24 #include <comphelper/string.hxx>
25 #include <vcl/toolkit/lstbox.hxx>
26 #include <vcl/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>
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
)
71 , m_isDDAutoSize(false)
72 , m_isSyntheticModify(false)
73 , m_isKeyBoardModify(false)
74 , m_isMatchCase(false)
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
)
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
);
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
);
118 ComboBox::~ComboBox()
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();
136 void ComboBox::Impl::ImplInitComboBoxData()
138 m_pSubEdit
.disposeAndClear();
141 m_pFloatWin
= nullptr;
144 m_isDDAutoSize
= true;
145 m_isSyntheticModify
= false;
146 m_isKeyBoardModify
= false;
147 m_isMatchCase
= false;
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
,
167 ControlState::ENABLED
,
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
;
191 Edit::ImplInit( pParent
, nStyle
);
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
;
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
);
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
) )
262 void ComboBox::EnableAutocomplete( bool bEnable
, bool bMatchCase
)
264 m_pImpl
->m_isMatchCase
= bMatchCase
;
267 m_pImpl
->m_pSubEdit
->SetAutocompleteHdl( LINK(m_pImpl
.get(), ComboBox::Impl
, ImplAutocompleteHdl
) );
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();
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();
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 );
306 m_pImplLB
->SetTravelSelect( bTravelSelect
);
310 m_rThis
.ImplClearLayoutData();
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
)
330 sal_Int32 nPos
= LISTBOX_ENTRY_NOTFOUND
;
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
)
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
))
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
);
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
);
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());
429 m_pFloatWin
->EndPopupMode();
435 m_isKeyBoardModify
= !bMenuSelect
;
436 m_pSubEdit
->SetModifyFlag();
437 m_isSyntheticModify
= true;
439 m_isSyntheticModify
= false;
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() )
485 if (m_pImpl
->m_pFloatWin
->IsInPopupMode())
486 m_pImpl
->m_pFloatWin
->EndPopupMode();
489 m_pImpl
->m_pSubEdit
->GrabFocus();
490 if (!m_pImpl
->m_pImplLB
->GetEntryList()->GetMRUCount())
491 m_pImpl
->ImplUpdateFloatSelection();
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();
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);
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();
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()
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
);
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
)
615 if (m_pImpl
->m_pFloatWin
->IsReallyVisible())
617 AppendLayoutData( *rMainWindow
);
618 rMainWindow
->SetLayoutDataParent( this );
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() );
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() );
643 m_pImpl
->m_pBtn
->Enable( IsEnabled() && !IsReadOnly() );
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();
657 else if ( nType
== StateChangedType::ControlFont
)
659 m_pImpl
->m_pImplLB
->SetControlFont( GetControlFont() );
660 m_pImpl
->m_pSubEdit
->SetControlFont( GetControlFont() );
661 ImplCalcEditHeight();
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
)
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() );
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
))) )
704 m_pImpl
->m_pBtn
->SetSettings( GetSettings() );
705 ImplInitDropDownButton( m_pImpl
->m_pBtn
);
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
)
717 if ((rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
718 && (rNEvt
.GetWindow() == m_pImpl
->m_pSubEdit
)
721 KeyEvent aKeyEvt
= *rNEvt
.GetKeyEvent();
722 sal_uInt16 nKeyCode
= aKeyEvt
.GetKeyCode().GetCode();
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
);
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();
753 bDone
= m_pImpl
->m_pImplLB
->ProcessKeyInput( aKeyEvt
);
760 if ((rNEvt
.GetWindow() == m_pImpl
->m_pSubEdit
) && IsInDropDown())
762 m_pImpl
->m_pImplLB
->ProcessKeyInput( aKeyEvt
);
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);
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();
827 void ComboBox::Impl::ImplUpdateFloatSelection()
829 if (!m_pImplLB
|| !m_pSubEdit
)
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
;
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 );
856 if( nSelect
!= LISTBOX_ENTRY_NOTFOUND
)
858 if (!m_pImplLB
->IsVisible(nSelect
))
859 m_pImplLB
->ShowProminentEntry( nSelect
);
860 m_pImplLB
->SelectEntry( nSelect
, bSelect
);
864 nSelect
= m_pImplLB
->GetEntryList()->GetSelectedEntryPos( 0 );
865 if( nSelect
!= LISTBOX_ENTRY_NOTFOUND
)
866 m_pImplLB
->SelectEntry( nSelect
, false );
867 m_pImplLB
->ResetCurrentPos();
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());
885 if (nPos
== COMBOBOX_APPEND
)
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
) );
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());
906 if (nPos
== COMBOBOX_APPEND
)
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
) );
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
)
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
);
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();
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
)
958 return m_pImpl
->m_pImplLB
->GetEntryList()->GetEntryText( nPos
+ nMRUCount
);
961 sal_Int32
ComboBox::GetEntryCount() const
963 if (!m_pImpl
->m_pImplLB
)
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
)
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
1025 if (!m_pImpl
->m_pImplLB
)
1028 if (!IsDropDownBox())
1030 aSz
= m_pImpl
->m_pImplLB
->CalcSize( m_pImpl
->m_pImplLB
->GetEntryList()->GetEntryCount() );
1031 aSz
.AdjustHeight(m_pImpl
->m_nDDHeight
);
1035 aSz
.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1037 if (m_pImpl
->m_nWidthInChars
!= -1)
1038 aSz
.setWidth(m_pImpl
->m_nWidthInChars
* approximate_digit_width());
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
);
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
;
1074 aSz
.setHeight( nLines
* nEntryHeight
);
1075 aSz
.AdjustHeight(m_pImpl
->m_nDDHeight
);
1079 aSz
.setHeight( m_pImpl
->m_nDDHeight
);
1081 aSz
.AdjustHeight(nTop
+nBottom
);
1083 aSz
= CalcWindowSize( aSz
);
1087 Size
ComboBox::CalcBlockSize( sal_uInt16 nColumns
, sal_uInt16 nLines
) const
1089 // show ScrollBars where appropriate
1090 Size aMinSz
= CalcMinimumSize();
1096 if ( !IsDropDownBox() )
1097 aSz
.setHeight( m_pImpl
->m_pImplLB
->CalcSize( nLines
).Height() + m_pImpl
->m_nDDHeight
);
1099 aSz
.setHeight( m_pImpl
->m_nDDHeight
);
1102 aSz
.setHeight( aMinSz
.Height() );
1106 aSz
.setWidth( nColumns
* approximate_char_width() );
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
);
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());
1143 Size aOutSz
= m_pImpl
->m_pSubEdit
->GetOutputSizePixel();
1144 rnCols
= (nCharWidth
> 0) ? static_cast<sal_uInt16
>(aOutSz
.Width()/nCharWidth
) : 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
);
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;
1173 ImplDrawFrame( pDev
, aRect
);
1177 pDev
->SetFillColor( GetControlBackground() );
1178 pDev
->DrawRect( aRect
);
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
;
1202 nTextStyle
|= DrawTextFlags::Left
;
1204 if ( nFlags
& DrawFlags::Mono
)
1206 pDev
->SetTextColor( COL_BLACK
);
1212 const StyleSettings
& rStyleSettings
= GetSettings().GetStyleSettings();
1213 pDev
->SetTextColor( rStyleSettings
.GetDisableColor() );
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 );
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
);
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
);
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())
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();
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());
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() );
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() )
1412 // check whether rPoint fits at all
1413 tools::Long nIndex
= Control::GetIndexForPoint( rPoint
);
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
)
1434 // get line relative index
1436 nIndex
= ToRelativeLineIndex( 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
;
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();
1480 // use the themes drop down size for the button
1481 aBounds
.aSubEditSize
= Size(rOutSz
.Width() - aContent
.getWidth(), rOutSz
.Height());
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
));
1495 void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars
)
1497 if (nWidthInChars
!= m_pImpl
->m_nWidthInChars
)
1499 m_pImpl
->m_nWidthInChars
= nWidthInChars
;
1504 void ComboBox::setMaxWidthChars(sal_Int32 nWidth
)
1506 if (nWidth
!= m_pImpl
->m_nMaxWidthChars
)
1508 m_pImpl
->m_nMaxWidthChars
= nWidth
;
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
);
1528 nBits
|= WB_TABSTOP
;
1531 else if (rKey
== "placeholder-text")
1532 SetPlaceholderText(rValue
);
1534 return Control::set_property(rKey
, rValue
);
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: */