Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <set>
21 : #include <comphelper/string.hxx>
22 : #include <tools/debug.hxx>
23 : #include <tools/rc.h>
24 : #include <vcl/decoview.hxx>
25 : #include <vcl/lstbox.h>
26 : #include <vcl/button.hxx>
27 : #include <vcl/event.hxx>
28 : #include <vcl/combobox.hxx>
29 : #include <vcl/settings.hxx>
30 :
31 : #include <svdata.hxx>
32 : #include <ilstbox.hxx>
33 : #include <controldata.hxx>
34 :
35 0 : static void lcl_GetSelectedEntries( ::std::set< sal_Int32 >& rSelectedPos, const OUString& rText, sal_Unicode cTokenSep, const ImplEntryList* pEntryList )
36 : {
37 0 : for (sal_Int32 n = comphelper::string::getTokenCount(rText, cTokenSep); n;)
38 : {
39 0 : OUString aToken = rText.getToken( --n, cTokenSep );
40 0 : aToken = comphelper::string::strip(aToken, ' ');
41 0 : sal_Int32 nPos = pEntryList->FindEntry( aToken );
42 0 : if ( nPos != LISTBOX_ENTRY_NOTFOUND )
43 0 : rSelectedPos.insert( nPos );
44 0 : }
45 0 : }
46 :
47 2492 : ComboBox::ComboBox( vcl::Window* pParent, WinBits nStyle ) :
48 2492 : Edit( WINDOW_COMBOBOX )
49 : {
50 2492 : ImplInitComboBoxData();
51 2492 : ImplInit( pParent, nStyle );
52 2492 : SetWidthInChars(-1);
53 2492 : }
54 :
55 497 : ComboBox::ComboBox( vcl::Window* pParent, const ResId& rResId ) :
56 497 : Edit( WINDOW_COMBOBOX )
57 : {
58 497 : ImplInitComboBoxData();
59 497 : rResId.SetRT( RSC_COMBOBOX );
60 497 : WinBits nStyle = ImplInitRes( rResId );
61 497 : ImplInit( pParent, nStyle );
62 497 : ImplLoadRes( rResId );
63 :
64 497 : SetWidthInChars(-1);
65 497 : if ( !(nStyle & WB_HIDE ) )
66 0 : Show();
67 497 : }
68 :
69 3727 : ComboBox::~ComboBox()
70 : {
71 1849 : disposeOnce();
72 1878 : }
73 :
74 2987 : void ComboBox::dispose()
75 : {
76 2987 : mpSubEdit.disposeAndClear();
77 :
78 2987 : VclPtr< ImplListBox > pImplLB = mpImplLB;
79 2987 : mpImplLB.clear();
80 2987 : pImplLB.disposeAndClear();
81 :
82 2987 : mpFloatWin.disposeAndClear();
83 2987 : mpBtn.disposeAndClear();
84 2987 : Edit::dispose();
85 2987 : }
86 :
87 2989 : void ComboBox::ImplInitComboBoxData()
88 : {
89 2989 : mpSubEdit.disposeAndClear();
90 2989 : mpBtn = NULL;
91 2989 : mpImplLB = NULL;
92 2989 : mpFloatWin = NULL;
93 :
94 2989 : mnDDHeight = 0;
95 2989 : mbDDAutoSize = true;
96 2989 : mbSyntheticModify = false;
97 2989 : mbMatchCase = false;
98 2989 : mcMultiSep = ';';
99 2989 : m_nMaxWidthChars = -1;
100 2989 : }
101 :
102 3154 : void ComboBox::ImplCalcEditHeight()
103 : {
104 : sal_Int32 nLeft, nTop, nRight, nBottom;
105 3154 : GetBorder( nLeft, nTop, nRight, nBottom );
106 3154 : mnDDHeight = (sal_uInt16)(mpSubEdit->GetTextHeight() + nTop + nBottom + 4);
107 3154 : if ( !IsDropDownBox() )
108 58 : mnDDHeight += 4;
109 :
110 3154 : Rectangle aCtrlRegion( Point( 0, 0 ), Size( 10, 10 ) );
111 3154 : Rectangle aBoundRegion, aContentRegion;
112 3154 : ImplControlValue aControlValue;
113 3154 : ControlType aType = IsDropDownBox() ? CTRL_COMBOBOX : CTRL_EDITBOX;
114 6308 : if( GetNativeControlRegion( aType, PART_ENTIRE_CONTROL,
115 : aCtrlRegion,
116 : ControlState::ENABLED,
117 : aControlValue, OUString(),
118 6308 : aBoundRegion, aContentRegion ) )
119 : {
120 0 : const long nNCHeight = aBoundRegion.GetHeight();
121 0 : if( mnDDHeight < nNCHeight )
122 0 : mnDDHeight = sal::static_int_cast<sal_uInt16>( nNCHeight );
123 3154 : }
124 3154 : }
125 :
126 2989 : void ComboBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
127 : {
128 2989 : ImplInitStyle( nStyle );
129 :
130 2989 : bool bNoBorder = ( nStyle & WB_NOBORDER ) != 0;
131 2989 : if ( !(nStyle & WB_DROPDOWN) )
132 : {
133 16 : nStyle &= ~WB_BORDER;
134 16 : nStyle |= WB_NOBORDER;
135 : }
136 : else
137 : {
138 2973 : if ( !bNoBorder )
139 2973 : nStyle |= WB_BORDER;
140 : }
141 :
142 2989 : Edit::ImplInit( pParent, nStyle );
143 2989 : SetBackground();
144 :
145 : // DropDown ?
146 2989 : WinBits nEditStyle = nStyle & ( WB_LEFT | WB_RIGHT | WB_CENTER );
147 2989 : WinBits nListStyle = nStyle;
148 2989 : if( nStyle & WB_DROPDOWN )
149 : {
150 2973 : mpFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
151 2973 : mpFloatWin->SetAutoWidth( true );
152 2973 : mpFloatWin->SetPopupModeEndHdl( LINK( this, ComboBox, ImplPopupModeEndHdl ) );
153 :
154 2973 : mpBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
155 2973 : ImplInitDropDownButton( mpBtn );
156 2973 : mpBtn->buttonDownSignal.connect( boost::bind( &ComboBox::ImplClickButtonHandler, this, _1 ));
157 2973 : mpBtn->Show();
158 :
159 2973 : nEditStyle |= WB_NOBORDER;
160 2973 : nListStyle &= ~WB_BORDER;
161 2973 : nListStyle |= WB_NOBORDER;
162 : }
163 : else
164 : {
165 16 : if ( !bNoBorder )
166 : {
167 16 : nEditStyle |= WB_BORDER;
168 16 : nListStyle &= ~WB_NOBORDER;
169 16 : nListStyle |= WB_BORDER;
170 : }
171 : }
172 :
173 2989 : mpSubEdit.set( VclPtr<Edit>::Create( this, nEditStyle ) );
174 2989 : mpSubEdit->EnableRTL( false );
175 2989 : SetSubEdit( mpSubEdit );
176 2989 : mpSubEdit->SetPosPixel( Point() );
177 2989 : EnableAutocomplete( true );
178 2989 : mpSubEdit->Show();
179 :
180 2989 : vcl::Window* pLBParent = this;
181 2989 : if ( mpFloatWin )
182 2973 : pLBParent = mpFloatWin;
183 2989 : mpImplLB = VclPtr<ImplListBox>::Create( pLBParent, nListStyle|WB_SIMPLEMODE|WB_AUTOHSCROLL );
184 2989 : mpImplLB->SetPosPixel( Point() );
185 2989 : mpImplLB->SetSelectHdl( LINK( this, ComboBox, ImplSelectHdl ) );
186 2989 : mpImplLB->SetCancelHdl( LINK( this, ComboBox, ImplCancelHdl ) );
187 2989 : mpImplLB->SetDoubleClickHdl( LINK( this, ComboBox, ImplDoubleClickHdl ) );
188 2989 : mpImplLB->userDrawSignal.connect( boost::bind( &ComboBox::ImplUserDrawHandler, this, _1 ) );
189 2989 : mpImplLB->SetSelectionChangedHdl( LINK( this, ComboBox, ImplSelectionChangedHdl ) );
190 2989 : mpImplLB->SetListItemSelectHdl( LINK( this, ComboBox, ImplListItemSelectHdl ) );
191 2989 : mpImplLB->Show();
192 :
193 2989 : if ( mpFloatWin )
194 2973 : mpFloatWin->SetImplListBox( mpImplLB );
195 : else
196 16 : mpImplLB->GetMainWindow()->AllowGrabFocus( true );
197 :
198 2989 : ImplCalcEditHeight();
199 :
200 2989 : SetCompoundControl( true );
201 2989 : }
202 :
203 12461 : WinBits ComboBox::ImplInitStyle( WinBits nStyle )
204 : {
205 12461 : if ( !(nStyle & WB_NOTABSTOP) )
206 12461 : nStyle |= WB_TABSTOP;
207 12461 : if ( !(nStyle & WB_NOGROUP) )
208 12461 : nStyle |= WB_GROUP;
209 12461 : return nStyle;
210 : }
211 :
212 497 : void ComboBox::ImplLoadRes( const ResId& rResId )
213 : {
214 497 : Edit::ImplLoadRes( rResId );
215 :
216 497 : sal_Int32 nNumber = ReadLongRes();
217 :
218 497 : if( nNumber )
219 : {
220 0 : for( sal_Int32 i = 0; i < nNumber; i++ )
221 : {
222 0 : InsertEntry( ReadStringRes(), LISTBOX_APPEND );
223 : }
224 : }
225 497 : }
226 :
227 4660 : void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
228 : {
229 4660 : mbMatchCase = bMatchCase;
230 :
231 4660 : if ( bEnable )
232 : {
233 3510 : if( !mAutocompleteConnection.connected())
234 5984 : mAutocompleteConnection = mpSubEdit->autocompleteSignal.connect(
235 5984 : boost::bind( &ComboBox::ImplAutocompleteHandler, this, _1 ) );
236 : }
237 : else
238 1150 : mAutocompleteConnection.disconnect();
239 4660 : }
240 :
241 0 : bool ComboBox::IsAutocompleteEnabled() const
242 : {
243 0 : return mAutocompleteConnection.connected();
244 : }
245 :
246 0 : void ComboBox::ImplClickButtonHandler( ImplBtn* )
247 : {
248 0 : CallEventListeners( VCLEVENT_DROPDOWN_PRE_OPEN );
249 0 : mpSubEdit->GrabFocus();
250 0 : if ( !mpImplLB->GetEntryList()->GetMRUCount() )
251 0 : ImplUpdateFloatSelection();
252 : else
253 0 : mpImplLB->SelectEntry( 0 , true );
254 0 : mpBtn->SetPressed( true );
255 0 : SetSelection( Selection( 0, SELECTION_MAX ) );
256 0 : mpFloatWin->StartFloat( true );
257 0 : CallEventListeners( VCLEVENT_DROPDOWN_OPEN );
258 :
259 0 : ImplClearLayoutData();
260 0 : if( mpImplLB )
261 0 : mpImplLB->GetMainWindow()->ImplClearLayoutData();
262 0 : }
263 :
264 2 : IMPL_LINK_NOARG(ComboBox, ImplPopupModeEndHdl)
265 : {
266 1 : if( mpFloatWin->IsPopupModeCanceled() )
267 : {
268 0 : if ( !mpImplLB->GetEntryList()->IsEntryPosSelected( mpFloatWin->GetPopupModeStartSaveSelection() ) )
269 : {
270 0 : mpImplLB->SelectEntry( mpFloatWin->GetPopupModeStartSaveSelection(), true );
271 0 : bool bTravelSelect = mpImplLB->IsTravelSelect();
272 0 : mpImplLB->SetTravelSelect( true );
273 0 : Select();
274 0 : mpImplLB->SetTravelSelect( bTravelSelect );
275 : }
276 : }
277 :
278 1 : ImplClearLayoutData();
279 1 : if( mpImplLB )
280 1 : mpImplLB->GetMainWindow()->ImplClearLayoutData();
281 :
282 1 : mpBtn->SetPressed( false );
283 1 : CallEventListeners( VCLEVENT_DROPDOWN_CLOSE );
284 1 : return 0;
285 : }
286 :
287 0 : void ComboBox::ImplAutocompleteHandler( Edit* pEdit )
288 : {
289 0 : Selection aSel = pEdit->GetSelection();
290 0 : AutocompleteAction eAction = pEdit->GetAutocompleteAction();
291 :
292 : /* If there is no current selection do not auto complete on
293 : Tab/Shift-Tab since then we would not cycle to the next field.
294 : */
295 0 : if ( aSel.Len() ||
296 0 : ((eAction != AUTOCOMPLETE_TABFORWARD) && (eAction != AUTOCOMPLETE_TABBACKWARD)) )
297 : {
298 0 : OUString aFullText = pEdit->GetText();
299 0 : OUString aStartText = aFullText.copy( 0, (sal_Int32)aSel.Max() );
300 0 : sal_Int32 nStart = mpImplLB->GetCurrentPos();
301 :
302 0 : if ( nStart == LISTBOX_ENTRY_NOTFOUND )
303 0 : nStart = 0;
304 :
305 0 : bool bForward = true;
306 0 : if ( eAction == AUTOCOMPLETE_TABFORWARD )
307 0 : nStart++;
308 0 : else if ( eAction == AUTOCOMPLETE_TABBACKWARD )
309 : {
310 0 : bForward = false;
311 0 : if (nStart)
312 0 : nStart = nStart - 1;
313 0 : else if (mpImplLB->GetEntryList()->GetEntryCount())
314 0 : nStart = mpImplLB->GetEntryList()->GetEntryCount()-1;
315 : }
316 :
317 0 : sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
318 0 : if( ! mbMatchCase )
319 : {
320 : // Try match case insensitive from current position
321 0 : nPos = mpImplLB->GetEntryList()->FindMatchingEntry( aStartText, nStart, bForward, true );
322 0 : if ( nPos == LISTBOX_ENTRY_NOTFOUND )
323 : // Try match case insensitive, but from start
324 0 : nPos = mpImplLB->GetEntryList()->FindMatchingEntry( aStartText, bForward ? 0 : (mpImplLB->GetEntryList()->GetEntryCount()-1), bForward, true );
325 : }
326 :
327 0 : if ( nPos == LISTBOX_ENTRY_NOTFOUND )
328 : // Try match full from current position
329 0 : nPos = mpImplLB->GetEntryList()->FindMatchingEntry( aStartText, nStart, bForward, false );
330 0 : if ( nPos == LISTBOX_ENTRY_NOTFOUND )
331 : // Match full, but from start
332 0 : nPos = mpImplLB->GetEntryList()->FindMatchingEntry( aStartText, bForward ? 0 : (mpImplLB->GetEntryList()->GetEntryCount()-1), bForward, false );
333 :
334 0 : if ( nPos != LISTBOX_ENTRY_NOTFOUND )
335 : {
336 0 : OUString aText = mpImplLB->GetEntryList()->GetEntryText( nPos );
337 0 : Selection aSelection( aText.getLength(), aStartText.getLength() );
338 0 : pEdit->SetText( aText, aSelection );
339 0 : }
340 : }
341 0 : }
342 :
343 0 : IMPL_LINK_NOARG(ComboBox, ImplSelectHdl)
344 : {
345 0 : bool bPopup = IsInDropDown();
346 0 : bool bCallSelect = false;
347 0 : if ( mpImplLB->IsSelectionChanged() || bPopup )
348 : {
349 0 : OUString aText;
350 0 : if ( IsMultiSelectionEnabled() )
351 : {
352 0 : aText = mpSubEdit->GetText();
353 :
354 : // remove all entries to which there is an entry, but which is not selected
355 0 : sal_Int32 nIndex = 0;
356 0 : while ( nIndex >= 0 )
357 : {
358 0 : sal_Int32 nPrevIndex = nIndex;
359 0 : OUString aToken = aText.getToken( 0, mcMultiSep, nIndex );
360 0 : sal_Int32 nTokenLen = aToken.getLength();
361 0 : aToken = comphelper::string::strip(aToken, ' ');
362 0 : sal_Int32 nP = mpImplLB->GetEntryList()->FindEntry( aToken );
363 0 : if ( (nP != LISTBOX_ENTRY_NOTFOUND) && (!mpImplLB->GetEntryList()->IsEntryPosSelected( nP )) )
364 : {
365 0 : aText = aText.replaceAt( nPrevIndex, nTokenLen, "" );
366 0 : nIndex = nIndex - nTokenLen;
367 0 : sal_Int32 nSepCount=0;
368 0 : if ( (nPrevIndex+nSepCount < aText.getLength()) && (aText[nPrevIndex+nSepCount] == mcMultiSep) )
369 : {
370 0 : nIndex--;
371 0 : ++nSepCount;
372 : }
373 0 : aText = aText.replaceAt( nPrevIndex, nSepCount, "" );
374 : }
375 0 : aText = comphelper::string::strip(aText, ' ');
376 0 : }
377 :
378 : // attach missing entries
379 0 : ::std::set< sal_Int32 > aSelInText;
380 0 : lcl_GetSelectedEntries( aSelInText, aText, mcMultiSep, mpImplLB->GetEntryList() );
381 0 : sal_Int32 nSelectedEntries = mpImplLB->GetEntryList()->GetSelectEntryCount();
382 0 : for ( sal_Int32 n = 0; n < nSelectedEntries; n++ )
383 : {
384 0 : sal_Int32 nP = mpImplLB->GetEntryList()->GetSelectEntryPos( n );
385 0 : if ( !aSelInText.count( nP ) )
386 : {
387 0 : if ( !aText.isEmpty() && (aText[ aText.getLength()-1 ] != mcMultiSep) )
388 0 : aText += OUString(mcMultiSep);
389 0 : if ( !aText.isEmpty() )
390 0 : aText += " "; // slightly loosen
391 0 : aText += mpImplLB->GetEntryList()->GetEntryText( nP );
392 0 : aText += OUString(mcMultiSep);
393 : }
394 : }
395 0 : aText = comphelper::string::stripEnd( aText, mcMultiSep );
396 : }
397 : else
398 : {
399 0 : aText = mpImplLB->GetEntryList()->GetSelectEntry( 0 );
400 : }
401 :
402 0 : mpSubEdit->SetText( aText );
403 :
404 0 : Selection aNewSelection( 0, aText.getLength() );
405 0 : if ( IsMultiSelectionEnabled() )
406 0 : aNewSelection.Min() = aText.getLength();
407 0 : mpSubEdit->SetSelection( aNewSelection );
408 :
409 0 : bCallSelect = true;
410 : }
411 :
412 : // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
413 :
414 0 : if ( bPopup && !mpImplLB->IsTravelSelect() &&
415 0 : ( !IsMultiSelectionEnabled() || !mpImplLB->GetSelectModifier() ) )
416 : {
417 0 : mpFloatWin->EndPopupMode();
418 0 : GrabFocus();
419 : }
420 :
421 0 : if ( bCallSelect )
422 : {
423 0 : mpSubEdit->SetModifyFlag();
424 0 : mbSyntheticModify = true;
425 0 : Modify();
426 0 : mbSyntheticModify = false;
427 0 : Select();
428 : }
429 :
430 0 : return 0;
431 : }
432 :
433 0 : IMPL_LINK_NOARG( ComboBox, ImplListItemSelectHdl )
434 : {
435 0 : CallEventListeners( VCLEVENT_DROPDOWN_SELECT );
436 0 : return 1;
437 : }
438 :
439 0 : IMPL_LINK_NOARG(ComboBox, ImplCancelHdl)
440 : {
441 0 : if( IsInDropDown() )
442 0 : mpFloatWin->EndPopupMode();
443 :
444 0 : return 1;
445 : }
446 :
447 0 : IMPL_LINK( ComboBox, ImplSelectionChangedHdl, void*, n )
448 : {
449 0 : if ( !mpImplLB->IsTrackingSelect() )
450 : {
451 0 : sal_Int32 nChanged = (sal_Int32)reinterpret_cast<sal_uLong>(n);
452 0 : if ( !mpSubEdit->IsReadOnly() && mpImplLB->GetEntryList()->IsEntryPosSelected( nChanged ) )
453 0 : mpSubEdit->SetText( mpImplLB->GetEntryList()->GetEntryText( nChanged ) );
454 : }
455 0 : return 1;
456 : }
457 :
458 0 : IMPL_LINK_NOARG(ComboBox, ImplDoubleClickHdl)
459 : {
460 0 : DoubleClick();
461 0 : return 0;
462 : }
463 :
464 3 : void ComboBox::ToggleDropDown()
465 : {
466 3 : if( IsDropDownBox() )
467 : {
468 3 : if( mpFloatWin->IsInPopupMode() )
469 1 : mpFloatWin->EndPopupMode();
470 : else
471 : {
472 2 : mpSubEdit->GrabFocus();
473 2 : if ( !mpImplLB->GetEntryList()->GetMRUCount() )
474 2 : ImplUpdateFloatSelection();
475 : else
476 0 : mpImplLB->SelectEntry( 0 , true );
477 2 : CallEventListeners( VCLEVENT_DROPDOWN_PRE_OPEN );
478 2 : mpBtn->SetPressed( true );
479 2 : SetSelection( Selection( 0, SELECTION_MAX ) );
480 2 : mpFloatWin->StartFloat( true );
481 2 : CallEventListeners( VCLEVENT_DROPDOWN_OPEN );
482 : }
483 : }
484 3 : }
485 :
486 0 : void ComboBox::Select()
487 : {
488 0 : ImplCallEventListenersAndHandler( VCLEVENT_COMBOBOX_SELECT, maSelectHdl, this );
489 0 : }
490 :
491 0 : void ComboBox::DoubleClick()
492 : {
493 0 : ImplCallEventListenersAndHandler( VCLEVENT_COMBOBOX_DOUBLECLICK, maDoubleClickHdl, this );
494 0 : }
495 :
496 180 : void ComboBox::EnableAutoSize( bool bAuto )
497 : {
498 180 : mbDDAutoSize = bAuto;
499 180 : if ( mpFloatWin )
500 : {
501 166 : if ( bAuto && !mpFloatWin->GetDropDownLineCount() )
502 : {
503 : // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
504 152 : AdaptDropDownLineCountToMaximum();
505 : }
506 14 : else if ( !bAuto )
507 : {
508 14 : mpFloatWin->SetDropDownLineCount( 0 );
509 : }
510 : }
511 180 : }
512 :
513 0 : void ComboBox::EnableDDAutoWidth( bool b )
514 : {
515 0 : if ( mpFloatWin )
516 0 : mpFloatWin->SetAutoWidth( b );
517 0 : }
518 :
519 5041 : void ComboBox::SetDropDownLineCount( sal_uInt16 nLines )
520 : {
521 5041 : if ( mpFloatWin )
522 5024 : mpFloatWin->SetDropDownLineCount( nLines );
523 5041 : }
524 :
525 153 : void ComboBox::AdaptDropDownLineCountToMaximum()
526 : {
527 : // adapt to maximum allowed number
528 153 : SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
529 153 : }
530 :
531 0 : sal_uInt16 ComboBox::GetDropDownLineCount() const
532 : {
533 0 : sal_uInt16 nLines = 0;
534 0 : if ( mpFloatWin )
535 0 : nLines = mpFloatWin->GetDropDownLineCount();
536 0 : return nLines;
537 : }
538 :
539 13397 : void ComboBox::setPosSizePixel( long nX, long nY, long nWidth, long nHeight,
540 : PosSizeFlags nFlags )
541 : {
542 13397 : if( IsDropDownBox() && ( nFlags & PosSizeFlags::Size ) )
543 : {
544 9227 : Size aPrefSz = mpFloatWin->GetPrefSize();
545 9227 : if ( ( nFlags & PosSizeFlags::Height ) && ( nHeight >= 2*mnDDHeight ) )
546 2819 : aPrefSz.Height() = nHeight-mnDDHeight;
547 9227 : if ( nFlags & PosSizeFlags::Width )
548 9227 : aPrefSz.Width() = nWidth;
549 9227 : mpFloatWin->SetPrefSize( aPrefSz );
550 :
551 9227 : if ( IsAutoSizeEnabled() && ! (nFlags & PosSizeFlags::Dropdown) )
552 9199 : nHeight = mnDDHeight;
553 : }
554 :
555 13397 : Edit::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
556 13397 : }
557 :
558 5132 : void ComboBox::Resize()
559 : {
560 5132 : Control::Resize();
561 :
562 5132 : if (mpSubEdit)
563 : {
564 5132 : Size aOutSz = GetOutputSizePixel();
565 5132 : if( IsDropDownBox() )
566 : {
567 : ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(aOutSz,
568 4890 : GetWindow(GetWindowType::Border)->GetOutputSizePixel()));
569 4890 : mpSubEdit->SetPosSizePixel(aBounds.aSubEditPos, aBounds.aSubEditSize);
570 4890 : mpBtn->SetPosSizePixel(aBounds.aButtonPos, aBounds.aButtonSize);
571 : }
572 : else
573 : {
574 242 : mpSubEdit->SetSizePixel( Size( aOutSz.Width(), mnDDHeight ) );
575 242 : mpImplLB->setPosSizePixel( 0, mnDDHeight, aOutSz.Width(), aOutSz.Height() - mnDDHeight );
576 242 : if ( !GetText().isEmpty() )
577 0 : ImplUpdateFloatSelection();
578 : }
579 : }
580 :
581 : // adjust the size of the FloatingWindow even when invisible
582 : // as KEY_PGUP/DOWN is being processed...
583 5132 : if ( mpFloatWin )
584 4890 : mpFloatWin->SetSizePixel( mpFloatWin->CalcFloatSize() );
585 5132 : }
586 :
587 0 : void ComboBox::FillLayoutData() const
588 : {
589 0 : mpControlData->mpLayoutData = new vcl::ControlLayoutData();
590 0 : AppendLayoutData( *mpSubEdit );
591 0 : mpSubEdit->SetLayoutDataParent( this );
592 0 : ImplListBoxWindow* rMainWindow = mpImplLB->GetMainWindow();
593 0 : if( mpFloatWin )
594 : {
595 : // dropdown mode
596 0 : if( mpFloatWin->IsReallyVisible() )
597 : {
598 0 : AppendLayoutData( *rMainWindow );
599 0 : rMainWindow->SetLayoutDataParent( this );
600 : }
601 : }
602 : else
603 : {
604 0 : AppendLayoutData( *rMainWindow );
605 0 : rMainWindow->SetLayoutDataParent( this );
606 : }
607 0 : }
608 :
609 33646 : void ComboBox::StateChanged( StateChangedType nType )
610 : {
611 33646 : Edit::StateChanged( nType );
612 :
613 33646 : if ( nType == StateChangedType::ReadOnly )
614 : {
615 18 : mpImplLB->SetReadOnly( IsReadOnly() );
616 18 : if ( mpBtn )
617 6 : mpBtn->Enable( IsEnabled() && !IsReadOnly() );
618 : }
619 33628 : else if ( nType == StateChangedType::Enable )
620 : {
621 126 : mpSubEdit->Enable( IsEnabled() );
622 126 : mpImplLB->Enable( IsEnabled() && !IsReadOnly() );
623 126 : if ( mpBtn )
624 120 : mpBtn->Enable( IsEnabled() && !IsReadOnly() );
625 126 : Invalidate();
626 : }
627 33502 : else if( nType == StateChangedType::UpdateMode )
628 : {
629 17906 : mpImplLB->SetUpdateMode( IsUpdateMode() );
630 : }
631 15596 : else if ( nType == StateChangedType::Zoom )
632 : {
633 2 : mpImplLB->SetZoom( GetZoom() );
634 2 : mpSubEdit->SetZoom( GetZoom() );
635 2 : ImplCalcEditHeight();
636 2 : Resize();
637 : }
638 15594 : else if ( nType == StateChangedType::ControlFont )
639 : {
640 163 : mpImplLB->SetControlFont( GetControlFont() );
641 163 : mpSubEdit->SetControlFont( GetControlFont() );
642 163 : ImplCalcEditHeight();
643 163 : Resize();
644 : }
645 15431 : else if ( nType == StateChangedType::ControlForeground )
646 : {
647 2 : mpImplLB->SetControlForeground( GetControlForeground() );
648 2 : mpSubEdit->SetControlForeground( GetControlForeground() );
649 : }
650 15429 : else if ( nType == StateChangedType::ControlBackground )
651 : {
652 2 : mpImplLB->SetControlBackground( GetControlBackground() );
653 2 : mpSubEdit->SetControlBackground( GetControlBackground() );
654 : }
655 15427 : else if ( nType == StateChangedType::Style )
656 : {
657 9472 : SetStyle( ImplInitStyle( GetStyle() ) );
658 9472 : mpImplLB->GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
659 : }
660 5955 : else if( nType == StateChangedType::Mirroring )
661 : {
662 64 : if( mpBtn )
663 : {
664 32 : mpBtn->EnableRTL( IsRTLEnabled() );
665 32 : ImplInitDropDownButton( mpBtn );
666 : }
667 64 : mpSubEdit->CompatStateChanged( StateChangedType::Mirroring );
668 64 : mpImplLB->EnableRTL( IsRTLEnabled() );
669 64 : Resize();
670 : }
671 33646 : }
672 :
673 395 : void ComboBox::DataChanged( const DataChangedEvent& rDCEvt )
674 : {
675 395 : Control::DataChanged( rDCEvt );
676 :
677 1580 : if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
678 1580 : (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
679 1185 : ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
680 1580 : (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
681 : {
682 395 : if ( mpBtn )
683 : {
684 242 : mpBtn->SetSettings( GetSettings() );
685 242 : ImplInitDropDownButton( mpBtn );
686 : }
687 395 : Resize();
688 395 : mpImplLB->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
689 :
690 395 : SetBackground(); // due to a hack in Window::UpdateSettings the background must be reset
691 : // otherwise it will overpaint NWF drawn comboboxes
692 : }
693 395 : }
694 :
695 3 : bool ComboBox::PreNotify( NotifyEvent& rNEvt )
696 : {
697 :
698 3 : return Edit::PreNotify( rNEvt );
699 : }
700 :
701 6332 : bool ComboBox::Notify( NotifyEvent& rNEvt )
702 : {
703 6332 : bool nDone = false;
704 12664 : if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && ( rNEvt.GetWindow() == mpSubEdit )
705 6332 : && !IsReadOnly() )
706 : {
707 0 : KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
708 0 : sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
709 0 : switch( nKeyCode )
710 : {
711 : case KEY_UP:
712 : case KEY_DOWN:
713 : case KEY_PAGEUP:
714 : case KEY_PAGEDOWN:
715 : {
716 0 : ImplUpdateFloatSelection();
717 0 : if( ( nKeyCode == KEY_DOWN ) && mpFloatWin && !mpFloatWin->IsInPopupMode() && aKeyEvt.GetKeyCode().IsMod2() )
718 : {
719 0 : CallEventListeners( VCLEVENT_DROPDOWN_PRE_OPEN );
720 0 : mpBtn->SetPressed( true );
721 0 : if ( mpImplLB->GetEntryList()->GetMRUCount() )
722 0 : mpImplLB->SelectEntry( 0 , true );
723 0 : SetSelection( Selection( 0, SELECTION_MAX ) );
724 0 : mpFloatWin->StartFloat( false );
725 0 : CallEventListeners( VCLEVENT_DROPDOWN_OPEN );
726 0 : nDone = true;
727 : }
728 0 : else if( ( nKeyCode == KEY_UP ) && mpFloatWin && mpFloatWin->IsInPopupMode() && aKeyEvt.GetKeyCode().IsMod2() )
729 : {
730 0 : mpFloatWin->EndPopupMode();
731 0 : nDone = true;
732 : }
733 : else
734 : {
735 0 : nDone = mpImplLB->ProcessKeyInput( aKeyEvt );
736 : }
737 : }
738 0 : break;
739 :
740 : case KEY_RETURN:
741 : {
742 0 : if( ( rNEvt.GetWindow() == mpSubEdit ) && IsInDropDown() )
743 : {
744 0 : mpImplLB->ProcessKeyInput( aKeyEvt );
745 0 : nDone = true;
746 : }
747 : }
748 0 : break;
749 : }
750 : }
751 6332 : else if ( (rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS) && mpFloatWin )
752 : {
753 1 : if( mpFloatWin->HasChildPathFocus() )
754 0 : mpSubEdit->GrabFocus();
755 1 : else if ( mpFloatWin->IsInPopupMode() && !HasChildPathFocus( true ) )
756 0 : mpFloatWin->EndPopupMode();
757 : }
758 12662 : else if( (rNEvt.GetType() == MouseNotifyEvent::COMMAND) &&
759 6331 : (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
760 0 : (rNEvt.GetWindow() == mpSubEdit) )
761 : {
762 0 : MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
763 0 : if ( ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
764 0 : || ( ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
765 0 : && HasChildPathFocus()
766 : )
767 : )
768 : {
769 0 : nDone = mpImplLB->HandleWheelAsCursorTravel( *rNEvt.GetCommandEvent() );
770 : }
771 : else
772 : {
773 0 : nDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
774 : }
775 : }
776 6331 : else if( ( rNEvt.GetType() == MouseNotifyEvent::MOUSEBUTTONDOWN ) && ( rNEvt.GetWindow() == mpImplLB->GetMainWindow() ) )
777 : {
778 0 : mpSubEdit->GrabFocus();
779 : }
780 :
781 6332 : return nDone || Edit::Notify( rNEvt );
782 : }
783 :
784 6250 : void ComboBox::SetText( const OUString& rStr )
785 : {
786 6250 : CallEventListeners( VCLEVENT_COMBOBOX_SETTEXT );
787 :
788 6250 : Edit::SetText( rStr );
789 6250 : ImplUpdateFloatSelection();
790 6250 : }
791 :
792 10110 : void ComboBox::SetText( const OUString& rStr, const Selection& rNewSelection )
793 : {
794 10110 : CallEventListeners( VCLEVENT_COMBOBOX_SETTEXT );
795 :
796 10110 : Edit::SetText( rStr, rNewSelection );
797 10110 : ImplUpdateFloatSelection();
798 10110 : }
799 :
800 61 : void ComboBox::Modify()
801 : {
802 61 : if ( !mbSyntheticModify )
803 61 : ImplUpdateFloatSelection();
804 :
805 61 : Edit::Modify();
806 61 : }
807 :
808 16423 : void ComboBox::ImplUpdateFloatSelection()
809 : {
810 16423 : if (!mpImplLB || !mpSubEdit)
811 16423 : return;
812 :
813 : // move text in the ListBox into the visible region
814 16423 : mpImplLB->SetCallSelectionChangedHdl( false );
815 16423 : if ( !IsMultiSelectionEnabled() )
816 : {
817 16423 : OUString aSearchStr( mpSubEdit->GetText() );
818 16423 : sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
819 16423 : bool bSelect = true;
820 :
821 16423 : if ( mpImplLB->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND )
822 : {
823 2665 : OUString aCurrent = mpImplLB->GetEntryList()->GetEntryText( mpImplLB->GetCurrentPos() );
824 2665 : if ( aCurrent == aSearchStr )
825 2652 : nSelect = mpImplLB->GetCurrentPos();
826 : }
827 :
828 16423 : if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
829 13771 : nSelect = mpImplLB->GetEntryList()->FindEntry( aSearchStr );
830 16423 : if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
831 : {
832 11375 : nSelect = mpImplLB->GetEntryList()->FindMatchingEntry( aSearchStr );
833 11375 : bSelect = false;
834 : }
835 :
836 16423 : if( nSelect != LISTBOX_ENTRY_NOTFOUND )
837 : {
838 7061 : if ( !mpImplLB->IsVisible( nSelect ) )
839 5268 : mpImplLB->ShowProminentEntry( nSelect );
840 7061 : mpImplLB->SelectEntry( nSelect, bSelect );
841 : }
842 : else
843 : {
844 9362 : nSelect = mpImplLB->GetEntryList()->GetSelectEntryPos( 0 );
845 9362 : if( nSelect != LISTBOX_ENTRY_NOTFOUND )
846 7 : mpImplLB->SelectEntry( nSelect, false );
847 9362 : mpImplLB->ResetCurrentPos();
848 16423 : }
849 : }
850 : else
851 : {
852 0 : ::std::set< sal_Int32 > aSelInText;
853 0 : lcl_GetSelectedEntries( aSelInText, mpSubEdit->GetText(), mcMultiSep, mpImplLB->GetEntryList() );
854 0 : for ( sal_Int32 n = 0; n < mpImplLB->GetEntryList()->GetEntryCount(); n++ )
855 0 : mpImplLB->SelectEntry( n, aSelInText.count( n ) );
856 : }
857 16423 : mpImplLB->SetCallSelectionChangedHdl( true );
858 : }
859 :
860 163067 : sal_Int32 ComboBox::InsertEntry(const OUString& rStr, sal_Int32 const nPos)
861 : {
862 : assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > mpImplLB->GetEntryList()->GetEntryCount());
863 :
864 : sal_Int32 nRealPos;
865 163067 : if (nPos == COMBOBOX_APPEND)
866 95983 : nRealPos = nPos;
867 : else
868 : {
869 67084 : const sal_Int32 nMRUCount = mpImplLB->GetEntryList()->GetMRUCount();
870 : assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
871 67084 : nRealPos = nPos + nMRUCount;
872 : }
873 :
874 163067 : nRealPos = mpImplLB->InsertEntry( nRealPos, rStr );
875 163067 : nRealPos -= mpImplLB->GetEntryList()->GetMRUCount();
876 163067 : CallEventListeners( VCLEVENT_COMBOBOX_ITEMADDED, reinterpret_cast<void*>(nRealPos) );
877 163067 : return nRealPos;
878 : }
879 :
880 93 : sal_Int32 ComboBox::InsertEntryWithImage(
881 : const OUString& rStr, const Image& rImage, sal_Int32 const nPos)
882 : {
883 : assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > mpImplLB->GetEntryList()->GetEntryCount());
884 :
885 : sal_Int32 nRealPos;
886 93 : if (nPos == COMBOBOX_APPEND)
887 93 : nRealPos = nPos;
888 : else
889 : {
890 0 : const sal_Int32 nMRUCount = mpImplLB->GetEntryList()->GetMRUCount();
891 : assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
892 0 : nRealPos = nPos + nMRUCount;
893 : }
894 :
895 93 : nRealPos = mpImplLB->InsertEntry( nRealPos, rStr, rImage );
896 93 : nRealPos -= mpImplLB->GetEntryList()->GetMRUCount();
897 93 : CallEventListeners( VCLEVENT_COMBOBOX_ITEMADDED, reinterpret_cast<void*>(nRealPos) );
898 93 : return nRealPos;
899 : }
900 :
901 0 : void ComboBox::RemoveEntry( const OUString& rStr )
902 : {
903 0 : RemoveEntryAt(GetEntryPos(rStr));
904 0 : }
905 :
906 0 : void ComboBox::RemoveEntryAt(sal_Int32 const nPos)
907 : {
908 0 : const sal_Int32 nMRUCount = mpImplLB->GetEntryList()->GetMRUCount();
909 0 : if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
910 0 : return;
911 :
912 0 : mpImplLB->RemoveEntry( nPos + nMRUCount );
913 0 : CallEventListeners( VCLEVENT_COMBOBOX_ITEMREMOVED, reinterpret_cast<void*>(nPos) );
914 : }
915 :
916 8767 : void ComboBox::Clear()
917 : {
918 8767 : mpImplLB->Clear();
919 8767 : CallEventListeners( VCLEVENT_COMBOBOX_ITEMREMOVED, reinterpret_cast<void*>(-1) );
920 8767 : }
921 :
922 0 : Image ComboBox::GetEntryImage( sal_Int32 nPos ) const
923 : {
924 0 : if ( mpImplLB->GetEntryList()->HasEntryImage( nPos ) )
925 0 : return mpImplLB->GetEntryList()->GetEntryImage( nPos );
926 0 : return Image();
927 : }
928 :
929 0 : sal_Int32 ComboBox::GetEntryPos( const OUString& rStr ) const
930 : {
931 0 : sal_Int32 nPos = mpImplLB->GetEntryList()->FindEntry( rStr );
932 0 : if ( nPos != LISTBOX_ENTRY_NOTFOUND )
933 0 : nPos -= mpImplLB->GetEntryList()->GetMRUCount();
934 0 : return nPos;
935 : }
936 :
937 0 : sal_Int32 ComboBox::GetEntryPos( const void* pData ) const
938 : {
939 0 : sal_Int32 nPos = mpImplLB->GetEntryList()->FindEntry( pData );
940 0 : if ( nPos != LISTBOX_ENTRY_NOTFOUND )
941 0 : nPos = nPos - mpImplLB->GetEntryList()->GetMRUCount();
942 0 : return nPos;
943 : }
944 :
945 84549 : OUString ComboBox::GetEntry( sal_Int32 nPos ) const
946 : {
947 84549 : const sal_Int32 nMRUCount = mpImplLB->GetEntryList()->GetMRUCount();
948 84549 : if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
949 0 : return OUString();
950 :
951 84549 : return mpImplLB->GetEntryList()->GetEntryText( nPos + nMRUCount );
952 : }
953 :
954 57976 : sal_Int32 ComboBox::GetEntryCount() const
955 : {
956 57976 : return mpImplLB->GetEntryList()->GetEntryCount() - mpImplLB->GetEntryList()->GetMRUCount();
957 : }
958 :
959 0 : bool ComboBox::IsTravelSelect() const
960 : {
961 0 : return mpImplLB->IsTravelSelect();
962 : }
963 :
964 11 : bool ComboBox::IsInDropDown() const
965 : {
966 11 : return mpFloatWin && mpFloatWin->IsInPopupMode();
967 : }
968 :
969 0 : void ComboBox::EnableMultiSelection( bool bMulti )
970 : {
971 0 : mpImplLB->EnableMultiSelection( bMulti, false );
972 0 : mpImplLB->SetMultiSelectionSimpleMode( true );
973 0 : }
974 :
975 16432 : bool ComboBox::IsMultiSelectionEnabled() const
976 : {
977 16432 : return mpImplLB->IsMultiSelectionEnabled();
978 : }
979 :
980 338 : long ComboBox::CalcWindowSizePixel( sal_uInt16 nLines ) const
981 : {
982 338 : return mpImplLB->GetEntryHeight() * nLines;
983 : }
984 :
985 1137 : Size ComboBox::GetOptimalSize() const
986 : {
987 1137 : return CalcMinimumSize();
988 : }
989 :
990 1137 : long ComboBox::getMaxWidthScrollBarAndDownButton() const
991 : {
992 1137 : long nButtonDownWidth = 0;
993 :
994 1137 : vcl::Window *pBorder = GetWindow( GetWindowType::Border );
995 1137 : ImplControlValue aControlValue;
996 1137 : Point aPoint;
997 1137 : Rectangle aContent, aBound;
998 :
999 : // use the full extent of the control
1000 1137 : Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
1001 :
1002 2274 : if ( GetNativeControlRegion(CTRL_COMBOBOX, PART_BUTTON_DOWN,
1003 2274 : aArea, ControlState::NONE, aControlValue, OUString(), aBound, aContent) )
1004 : {
1005 0 : nButtonDownWidth = aContent.getWidth();
1006 : }
1007 :
1008 1137 : long nScrollBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1009 :
1010 1137 : return std::max(nScrollBarWidth, nButtonDownWidth);
1011 : }
1012 :
1013 1145 : Size ComboBox::CalcMinimumSize() const
1014 : {
1015 1145 : Size aSz;
1016 :
1017 1145 : if (!mpImplLB)
1018 0 : return aSz;
1019 :
1020 1145 : if (!IsDropDownBox())
1021 : {
1022 8 : aSz = mpImplLB->CalcSize( mpImplLB->GetEntryList()->GetEntryCount() );
1023 8 : aSz.Height() += mnDDHeight;
1024 : }
1025 : else
1026 : {
1027 1137 : aSz.Height() = Edit::CalcMinimumSizeForText(GetText()).Height();
1028 1137 : aSz.Width() = mpImplLB->GetMaxEntryWidth();
1029 : }
1030 :
1031 1145 : if (m_nMaxWidthChars != -1)
1032 : {
1033 0 : long nMaxWidth = m_nMaxWidthChars * approximate_char_width();
1034 0 : aSz.Width() = std::min(aSz.Width(), nMaxWidth);
1035 : }
1036 :
1037 1145 : if (IsDropDownBox())
1038 1137 : aSz.Width() += getMaxWidthScrollBarAndDownButton();
1039 :
1040 : ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(
1041 1145 : Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1042 1145 : aSz.Width() += aBounds.aSubEditPos.X()*2;
1043 :
1044 1145 : aSz.Width() += ImplGetExtraXOffset() * 2;
1045 :
1046 1145 : aSz = CalcWindowSize( aSz );
1047 1145 : return aSz;
1048 : }
1049 :
1050 2 : Size ComboBox::CalcAdjustedSize( const Size& rPrefSize ) const
1051 : {
1052 2 : Size aSz = rPrefSize;
1053 : sal_Int32 nLeft, nTop, nRight, nBottom;
1054 2 : static_cast<vcl::Window*>(const_cast<ComboBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1055 2 : aSz.Height() -= nTop+nBottom;
1056 2 : if ( !IsDropDownBox() )
1057 : {
1058 2 : long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1059 2 : long nLines = aSz.Height() / nEntryHeight;
1060 2 : if ( nLines < 1 )
1061 0 : nLines = 1;
1062 2 : aSz.Height() = nLines * nEntryHeight;
1063 2 : aSz.Height() += mnDDHeight;
1064 : }
1065 : else
1066 : {
1067 0 : aSz.Height() = mnDDHeight;
1068 : }
1069 2 : aSz.Height() += nTop+nBottom;
1070 :
1071 2 : aSz = CalcWindowSize( aSz );
1072 2 : return aSz;
1073 : }
1074 :
1075 4 : Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1076 : {
1077 : // show ScrollBars where appropriate
1078 4 : Size aMinSz = CalcMinimumSize();
1079 4 : Size aSz;
1080 :
1081 : // height
1082 4 : if ( nLines )
1083 : {
1084 2 : if ( !IsDropDownBox() )
1085 2 : aSz.Height() = mpImplLB->CalcSize( nLines ).Height() + mnDDHeight;
1086 : else
1087 0 : aSz.Height() = mnDDHeight;
1088 : }
1089 : else
1090 2 : aSz.Height() = aMinSz.Height();
1091 :
1092 : // width
1093 4 : if ( nColumns )
1094 2 : aSz.Width() = nColumns * approximate_char_width();
1095 : else
1096 2 : aSz.Width() = aMinSz.Width();
1097 :
1098 4 : if ( IsDropDownBox() )
1099 0 : aSz.Width() += getMaxWidthScrollBarAndDownButton();
1100 :
1101 4 : if ( !IsDropDownBox() )
1102 : {
1103 4 : if ( aSz.Width() < aMinSz.Width() )
1104 0 : aSz.Height() += GetSettings().GetStyleSettings().GetScrollBarSize();
1105 4 : if ( aSz.Height() < aMinSz.Height() )
1106 0 : aSz.Width() += GetSettings().GetStyleSettings().GetScrollBarSize();
1107 : }
1108 :
1109 4 : aSz.Width() += ImplGetExtraXOffset() * 2;
1110 :
1111 4 : aSz = CalcWindowSize( aSz );
1112 4 : return aSz;
1113 : }
1114 :
1115 2 : void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1116 : {
1117 2 : long nCharWidth = GetTextWidth(OUString(static_cast<sal_Unicode>('x')));
1118 2 : if ( !IsDropDownBox() )
1119 : {
1120 2 : Size aOutSz = mpImplLB->GetMainWindow()->GetOutputSizePixel();
1121 2 : rnCols = (nCharWidth > 0) ? (sal_uInt16)(aOutSz.Width()/nCharWidth) : 1;
1122 2 : rnLines = (sal_uInt16)(aOutSz.Height()/mpImplLB->GetEntryHeight());
1123 : }
1124 : else
1125 : {
1126 0 : Size aOutSz = mpSubEdit->GetOutputSizePixel();
1127 0 : rnCols = (nCharWidth > 0) ? (sal_uInt16)(aOutSz.Width()/nCharWidth) : 1;
1128 0 : rnLines = 1;
1129 : }
1130 2 : }
1131 :
1132 0 : void ComboBox::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags nFlags )
1133 : {
1134 0 : mpImplLB->GetMainWindow()->ApplySettings(*pDev);
1135 :
1136 0 : Point aPos = pDev->LogicToPixel( rPos );
1137 0 : Size aSize = pDev->LogicToPixel( rSize );
1138 0 : vcl::Font aFont = mpImplLB->GetMainWindow()->GetDrawPixelFont( pDev );
1139 0 : OutDevType eOutDevType = pDev->GetOutDevType();
1140 :
1141 0 : pDev->Push();
1142 0 : pDev->SetMapMode();
1143 0 : pDev->SetFont( aFont );
1144 0 : pDev->SetTextFillColor();
1145 :
1146 : // Border/Background
1147 0 : pDev->SetLineColor();
1148 0 : pDev->SetFillColor();
1149 0 : bool bBorder = !(nFlags & DrawFlags::NoBorder ) && (GetStyle() & WB_BORDER);
1150 0 : bool bBackground = !(nFlags & DrawFlags::NoBackground) && IsControlBackground();
1151 0 : if ( bBorder || bBackground )
1152 : {
1153 0 : Rectangle aRect( aPos, aSize );
1154 : // aRect.Top() += nEditHeight;
1155 0 : if ( bBorder )
1156 : {
1157 0 : ImplDrawFrame( pDev, aRect );
1158 : }
1159 0 : if ( bBackground )
1160 : {
1161 0 : pDev->SetFillColor( GetControlBackground() );
1162 0 : pDev->DrawRect( aRect );
1163 : }
1164 : }
1165 :
1166 : // contents
1167 0 : if ( !IsDropDownBox() )
1168 : {
1169 0 : long nOnePixel = GetDrawPixel( pDev, 1 );
1170 0 : long nTextHeight = pDev->GetTextHeight();
1171 0 : long nEditHeight = nTextHeight + 6*nOnePixel;
1172 0 : DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1173 :
1174 : // First, draw the edit part
1175 0 : mpSubEdit->Draw( pDev, aPos, Size( aSize.Width(), nEditHeight ), nFlags );
1176 :
1177 : // Second, draw the listbox
1178 0 : if ( GetStyle() & WB_CENTER )
1179 0 : nTextStyle |= DrawTextFlags::Center;
1180 0 : else if ( GetStyle() & WB_RIGHT )
1181 0 : nTextStyle |= DrawTextFlags::Right;
1182 : else
1183 0 : nTextStyle |= DrawTextFlags::Left;
1184 :
1185 0 : if ( ( nFlags & DrawFlags::Mono ) || ( eOutDevType == OUTDEV_PRINTER ) )
1186 : {
1187 0 : pDev->SetTextColor( Color( COL_BLACK ) );
1188 : }
1189 : else
1190 : {
1191 0 : if ( !(nFlags & DrawFlags::NoDisable ) && !IsEnabled() )
1192 : {
1193 0 : const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1194 0 : pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1195 : }
1196 : else
1197 : {
1198 0 : pDev->SetTextColor( GetTextColor() );
1199 : }
1200 : }
1201 :
1202 0 : Rectangle aClip( aPos, aSize );
1203 0 : pDev->IntersectClipRegion( aClip );
1204 0 : sal_uInt16 nLines = (sal_uInt16) ( ( nTextHeight > 0 ) ? ( (aSize.Height()-nEditHeight) / nTextHeight ) : 1 );
1205 0 : if ( !nLines )
1206 0 : nLines = 1;
1207 0 : sal_uInt16 nTEntry = IsReallyVisible() ? mpImplLB->GetTopEntry() : 0;
1208 :
1209 0 : Rectangle aTextRect( aPos, aSize );
1210 :
1211 0 : aTextRect.Left() += 3*nOnePixel;
1212 0 : aTextRect.Right() -= 3*nOnePixel;
1213 0 : aTextRect.Top() += nEditHeight + nOnePixel;
1214 0 : aTextRect.Bottom() = aTextRect.Top() + nTextHeight;
1215 :
1216 : // the drawing starts here
1217 0 : for ( sal_uInt16 n = 0; n < nLines; n++ )
1218 : {
1219 0 : pDev->DrawText( aTextRect, mpImplLB->GetEntryList()->GetEntryText( n+nTEntry ), nTextStyle );
1220 0 : aTextRect.Top() += nTextHeight;
1221 0 : aTextRect.Bottom() += nTextHeight;
1222 : }
1223 : }
1224 :
1225 0 : pDev->Pop();
1226 :
1227 : // Call Edit::Draw after restoring the MapMode...
1228 0 : if ( IsDropDownBox() )
1229 : {
1230 0 : mpSubEdit->Draw( pDev, rPos, rSize, nFlags );
1231 : // DD-Button ?
1232 0 : }
1233 :
1234 0 : }
1235 :
1236 26 : void::ComboBox::ImplUserDrawHandler( UserDrawEvent* pEvent )
1237 : {
1238 26 : UserDraw( *pEvent );
1239 26 : }
1240 :
1241 0 : void ComboBox::UserDraw( const UserDrawEvent& )
1242 : {
1243 0 : }
1244 :
1245 7167 : void ComboBox::SetUserItemSize( const Size& rSz )
1246 : {
1247 7167 : mpImplLB->GetMainWindow()->SetUserItemSize( rSz );
1248 7167 : }
1249 :
1250 1483 : void ComboBox::EnableUserDraw( bool bUserDraw )
1251 : {
1252 1483 : mpImplLB->GetMainWindow()->EnableUserDraw( bUserDraw );
1253 1483 : }
1254 :
1255 26 : void ComboBox::DrawEntry(const UserDrawEvent& rEvt, bool bDrawImage, bool bDrawText, bool bDrawTextAtImagePos)
1256 : {
1257 : DBG_ASSERT(rEvt.GetDevice() == mpImplLB->GetMainWindow(), "DrawEntry?!");
1258 26 : mpImplLB->GetMainWindow()->DrawEntry(*rEvt.GetDevice(), rEvt.GetItemId(), bDrawImage, bDrawText, bDrawTextAtImagePos);
1259 26 : }
1260 :
1261 5425 : void ComboBox::SetSeparatorPos( sal_Int32 n )
1262 : {
1263 5425 : mpImplLB->SetSeparatorPos( n );
1264 5425 : }
1265 :
1266 0 : void ComboBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
1267 : {
1268 0 : mpImplLB->SetMRUEntries( rEntries, cSep );
1269 0 : }
1270 :
1271 1962 : OUString ComboBox::GetMRUEntries( sal_Unicode cSep ) const
1272 : {
1273 1962 : return mpImplLB ? mpImplLB->GetMRUEntries( cSep ) : OUString();
1274 : }
1275 :
1276 986 : void ComboBox::SetMaxMRUCount( sal_Int32 n )
1277 : {
1278 986 : mpImplLB->SetMaxMRUCount( n );
1279 986 : }
1280 :
1281 986 : sal_Int32 ComboBox::GetMaxMRUCount() const
1282 : {
1283 986 : return mpImplLB ? mpImplLB->GetMaxMRUCount() : 0;
1284 : }
1285 :
1286 132 : sal_uInt16 ComboBox::GetDisplayLineCount() const
1287 : {
1288 132 : return mpImplLB ? mpImplLB->GetDisplayLineCount() : 0;
1289 : }
1290 :
1291 29580 : void ComboBox::SetEntryData( sal_Int32 nPos, void* pNewData )
1292 : {
1293 29580 : mpImplLB->SetEntryData( nPos + mpImplLB->GetEntryList()->GetMRUCount(), pNewData );
1294 29580 : }
1295 :
1296 0 : void* ComboBox::GetEntryData( sal_Int32 nPos ) const
1297 : {
1298 0 : return mpImplLB->GetEntryList()->GetEntryData( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
1299 : }
1300 :
1301 126 : sal_Int32 ComboBox::GetTopEntry() const
1302 : {
1303 126 : sal_Int32 nPos = GetEntryCount() ? mpImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1304 126 : if ( nPos < mpImplLB->GetEntryList()->GetMRUCount() )
1305 0 : nPos = 0;
1306 126 : return nPos;
1307 : }
1308 :
1309 986 : void ComboBox::SetProminentEntryType( ProminentEntry eType )
1310 : {
1311 986 : mpImplLB->SetProminentEntryType( eType );
1312 986 : }
1313 :
1314 0 : Rectangle ComboBox::GetDropDownPosSizePixel() const
1315 : {
1316 0 : return mpFloatWin ? mpFloatWin->GetWindowExtentsRelative( const_cast<ComboBox*>(this) ) : Rectangle();
1317 : }
1318 :
1319 0 : const Wallpaper& ComboBox::GetDisplayBackground() const
1320 : {
1321 0 : if( ! mpSubEdit->IsBackground() )
1322 0 : return Control::GetDisplayBackground();
1323 :
1324 0 : const Wallpaper& rBack = mpSubEdit->GetBackground();
1325 0 : if( ! rBack.IsBitmap() &&
1326 0 : ! rBack.IsGradient() &&
1327 0 : rBack.GetColor().GetColor() == COL_TRANSPARENT
1328 : )
1329 0 : return Control::GetDisplayBackground();
1330 0 : return rBack;
1331 : }
1332 :
1333 0 : sal_Int32 ComboBox::GetSelectEntryCount() const
1334 : {
1335 0 : return mpImplLB->GetEntryList()->GetSelectEntryCount();
1336 : }
1337 :
1338 33 : sal_Int32 ComboBox::GetSelectEntryPos( sal_Int32 nIndex ) const
1339 : {
1340 33 : sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectEntryPos( nIndex );
1341 33 : if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1342 : {
1343 30 : if ( nPos < mpImplLB->GetEntryList()->GetMRUCount() )
1344 0 : nPos = mpImplLB->GetEntryList()->FindEntry( mpImplLB->GetEntryList()->GetEntryText( nPos ) );
1345 30 : nPos = sal::static_int_cast<sal_Int32>(nPos - mpImplLB->GetEntryList()->GetMRUCount());
1346 : }
1347 33 : return nPos;
1348 : }
1349 :
1350 126 : bool ComboBox::IsEntryPosSelected( sal_Int32 nPos ) const
1351 : {
1352 126 : return mpImplLB->GetEntryList()->IsEntryPosSelected( nPos + mpImplLB->GetEntryList()->GetMRUCount() );
1353 : }
1354 :
1355 86 : void ComboBox::SelectEntryPos( sal_Int32 nPos, bool bSelect)
1356 : {
1357 86 : if ( nPos < mpImplLB->GetEntryList()->GetEntryCount() )
1358 86 : mpImplLB->SelectEntry( nPos + mpImplLB->GetEntryList()->GetMRUCount(), bSelect );
1359 86 : }
1360 :
1361 0 : void ComboBox::SetNoSelection()
1362 : {
1363 0 : mpImplLB->SetNoSelection();
1364 0 : mpSubEdit->SetText( OUString() );
1365 0 : }
1366 :
1367 0 : Rectangle ComboBox::GetBoundingRectangle( sal_Int32 nItem ) const
1368 : {
1369 0 : Rectangle aRect = mpImplLB->GetMainWindow()->GetBoundingRectangle( nItem );
1370 0 : Rectangle aOffset = mpImplLB->GetMainWindow()->GetWindowExtentsRelative( static_cast<vcl::Window*>(const_cast<ComboBox *>(this)) );
1371 0 : aRect.Move( aOffset.TopLeft().X(), aOffset.TopLeft().Y() );
1372 0 : return aRect;
1373 : }
1374 :
1375 28 : void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle )
1376 : {
1377 28 : Window::SetBorderStyle( nBorderStyle );
1378 28 : if ( !IsDropDownBox() )
1379 : {
1380 14 : mpSubEdit->SetBorderStyle( nBorderStyle );
1381 14 : mpImplLB->SetBorderStyle( nBorderStyle );
1382 : }
1383 28 : }
1384 :
1385 0 : long ComboBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
1386 : {
1387 0 : if( !HasLayoutData() )
1388 0 : FillLayoutData();
1389 :
1390 : // check whether rPoint fits at all
1391 0 : long nIndex = Control::GetIndexForPoint( rPoint );
1392 0 : if( nIndex != -1 )
1393 : {
1394 : // point must be either in main list window
1395 : // or in impl window (dropdown case)
1396 0 : ImplListBoxWindow* rMain = mpImplLB->GetMainWindow();
1397 :
1398 : // convert coordinates to ImplListBoxWindow pixel coordinate space
1399 0 : Point aConvPoint = LogicToPixel( rPoint );
1400 0 : aConvPoint = OutputToAbsoluteScreenPixel( aConvPoint );
1401 0 : aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPoint );
1402 0 : aConvPoint = rMain->PixelToLogic( aConvPoint );
1403 :
1404 : // try to find entry
1405 0 : sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
1406 0 : if( nEntry == LISTBOX_ENTRY_NOTFOUND )
1407 0 : nIndex = -1;
1408 : else
1409 0 : rPos = nEntry;
1410 : }
1411 :
1412 : // get line relative index
1413 0 : if( nIndex != -1 )
1414 0 : nIndex = ToRelativeLineIndex( nIndex );
1415 :
1416 0 : return nIndex;
1417 : }
1418 :
1419 6035 : ComboBox::ComboBoxBounds ComboBox::calcComboBoxDropDownComponentBounds(const Size &rOutSz,
1420 : const Size &rBorderOutSz) const
1421 : {
1422 6035 : ComboBoxBounds aBounds;
1423 :
1424 6035 : long nTop = 0;
1425 6035 : long nBottom = rOutSz.Height();
1426 :
1427 6035 : vcl::Window *pBorder = GetWindow( GetWindowType::Border );
1428 6035 : ImplControlValue aControlValue;
1429 6035 : Point aPoint;
1430 6035 : Rectangle aContent, aBound;
1431 :
1432 : // use the full extent of the control
1433 6035 : Rectangle aArea( aPoint, rBorderOutSz );
1434 :
1435 12070 : if ( GetNativeControlRegion(CTRL_COMBOBOX, PART_BUTTON_DOWN,
1436 12070 : aArea, ControlState::NONE, aControlValue, OUString(), aBound, aContent) )
1437 : {
1438 : // convert back from border space to local coordinates
1439 0 : aPoint = pBorder->ScreenToOutputPixel( OutputToScreenPixel( aPoint ) );
1440 0 : aContent.Move(-aPoint.X(), -aPoint.Y());
1441 :
1442 0 : aBounds.aButtonPos = Point(aContent.Left(), nTop);
1443 0 : aBounds.aButtonSize = Size(aContent.getWidth(), (nBottom-nTop));
1444 :
1445 : // adjust the size of the edit field
1446 0 : if ( GetNativeControlRegion(CTRL_COMBOBOX, PART_SUB_EDIT,
1447 0 : aArea, ControlState::NONE, aControlValue, OUString(), aBound, aContent) )
1448 : {
1449 : // convert back from border space to local coordinates
1450 0 : aContent.Move(-aPoint.X(), -aPoint.Y());
1451 :
1452 : // use the themes drop down size
1453 0 : aBounds.aSubEditPos = aContent.TopLeft();
1454 0 : aBounds.aSubEditSize = aContent.GetSize();
1455 : }
1456 : else
1457 : {
1458 : // use the themes drop down size for the button
1459 0 : aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getWidth(), rOutSz.Height());
1460 : }
1461 : }
1462 : else
1463 : {
1464 6035 : long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1465 6035 : nSBWidth = CalcZoom( nSBWidth );
1466 6035 : aBounds.aSubEditSize = Size(rOutSz.Width() - nSBWidth, rOutSz.Height());
1467 6035 : aBounds.aButtonPos = Point(rOutSz.Width() - nSBWidth, nTop);
1468 6035 : aBounds.aButtonSize = Size(nSBWidth, (nBottom-nTop));
1469 : }
1470 6035 : return aBounds;
1471 : }
1472 :
1473 0 : void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1474 : {
1475 0 : if (nWidth != m_nMaxWidthChars)
1476 : {
1477 0 : m_nMaxWidthChars = nWidth;
1478 0 : queue_resize();
1479 : }
1480 0 : }
1481 :
1482 761 : bool ComboBox::set_property(const OString &rKey, const OString &rValue)
1483 : {
1484 761 : if (rKey == "max-width-chars")
1485 0 : setMaxWidthChars(rValue.toInt32());
1486 : else
1487 761 : return Control::set_property(rKey, rValue);
1488 0 : return true;
1489 801 : }
1490 :
1491 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|