tdf#67990: Management of case in combobox
[LibreOffice.git] / vcl / source / control / ilstbox.cxx
blob74969b84898c7bbb4360099d4b6b0b53421965a0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/settings.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/scrbar.hxx>
26 #include <vcl/help.hxx>
27 #include <vcl/lstbox.h>
28 #include <vcl/unohelp.hxx>
29 #include <vcl/i18nhelp.hxx>
31 #include <ilstbox.hxx>
32 #include <controldata.hxx>
33 #include <svdata.hxx>
35 #include <com/sun/star/i18n/XCollator.hpp>
36 #include <com/sun/star/accessibility/XAccessible.hpp>
37 #include <com/sun/star/accessibility/AccessibleRole.hpp>
39 #include <rtl/instance.hxx>
40 #include <comphelper/string.hxx>
41 #include <comphelper/processfactory.hxx>
43 #include <limits>
45 #define MULTILINE_ENTRY_DRAW_FLAGS ( TEXT_DRAW_WORDBREAK | TEXT_DRAW_MULTILINE | TEXT_DRAW_VCENTER )
47 using namespace ::com::sun::star;
49 void ImplInitFieldSettings( vcl::Window* pWin, bool bFont, bool bForeground, bool bBackground )
51 const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings();
53 if ( bFont )
55 vcl::Font aFont = rStyleSettings.GetFieldFont();
56 if ( pWin->IsControlFont() )
57 aFont.Merge( pWin->GetControlFont() );
58 pWin->SetZoomedPointFont( aFont );
61 if ( bFont || bForeground )
63 Color aTextColor = rStyleSettings.GetFieldTextColor();
64 if ( pWin->IsControlForeground() )
65 aTextColor = pWin->GetControlForeground();
66 pWin->SetTextColor( aTextColor );
69 if ( bBackground )
71 if( pWin->IsControlBackground() )
72 pWin->SetBackground( pWin->GetControlBackground() );
73 else
74 pWin->SetBackground( rStyleSettings.GetFieldColor() );
78 void ImplInitDropDownButton( PushButton* pButton )
80 if ( pButton->GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_SPINUPDOWN )
81 pButton->SetSymbol( SymbolType::SPIN_UPDOWN );
82 else
83 pButton->SetSymbol( SymbolType::SPIN_DOWN );
85 if ( pButton->IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
86 && ! pButton->IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
87 pButton->SetBackground();
90 ImplEntryList::ImplEntryList( vcl::Window* pWindow )
92 mpWindow = pWindow;
93 mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
94 mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
95 mnImages = 0;
96 mbCallSelectionChangedHdl = true;
98 mnMRUCount = 0;
99 mnMaxMRUCount = 0;
102 ImplEntryList::~ImplEntryList()
104 Clear();
107 void ImplEntryList::Clear()
109 mnImages = 0;
110 maEntries.clear();
113 void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
115 if (0 <= nPos && static_cast<size_t>(nPos) < maEntries.size())
117 boost::ptr_vector<ImplEntryType>::iterator iter = maEntries.begin()+nPos;
119 if ( ( iter->mbIsSelected != bSelect ) &&
120 ( (iter->mnFlags & LISTBOX_ENTRY_FLAG_DISABLE_SELECTION) == 0 ) )
122 iter->mbIsSelected = bSelect;
123 if ( mbCallSelectionChangedHdl )
124 maSelectionChangedHdl.Call( reinterpret_cast<void*>(nPos) );
129 namespace
131 struct theSorter
132 : public rtl::StaticWithInit< comphelper::string::NaturalStringSorter, theSorter >
134 comphelper::string::NaturalStringSorter operator () ()
136 return comphelper::string::NaturalStringSorter(
137 ::comphelper::getProcessComponentContext(),
138 Application::GetSettings().GetLanguageTag().getLocale());
143 sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
145 if (nPos < 0 || LISTBOX_MAX_ENTRIES <= maEntries.size())
146 return LISTBOX_ERROR;
148 if ( !!pNewEntry->maImage )
149 mnImages++;
151 sal_Int32 insPos = 0;
153 if ( !bSort || maEntries.empty())
155 if (0 <= nPos && static_cast<size_t>(nPos) < maEntries.size())
157 insPos = nPos;
158 maEntries.insert( maEntries.begin() + nPos, pNewEntry );
160 else
162 insPos = maEntries.size();
163 maEntries.push_back(pNewEntry);
166 else
168 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
170 const OUString& rStr = pNewEntry->maStr;
171 sal_uLong nLow, nHigh, nMid;
173 nHigh = maEntries.size();
175 ImplEntryType* pTemp = GetEntry( (sal_Int32)(nHigh-1) );
179 sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);
181 // fast insert for sorted data
182 if ( nComp >= 0 )
184 insPos = maEntries.size();
185 maEntries.push_back(pNewEntry);
187 else
189 nLow = mnMRUCount;
190 pTemp = (ImplEntryType*)GetEntry( (sal_Int32)nLow );
192 nComp = rSorter.compare(rStr, pTemp->maStr);
193 if ( nComp <= 0 )
195 insPos = 0;
196 maEntries.insert(maEntries.begin(),pNewEntry);
198 else
200 // binary search
201 nHigh--;
204 nMid = (nLow + nHigh) / 2;
205 pTemp = (ImplEntryType*)GetEntry( nMid );
207 nComp = rSorter.compare(rStr, pTemp->maStr);
209 if ( nComp < 0 )
210 nHigh = nMid-1;
211 else
213 if ( nComp > 0 )
214 nLow = nMid + 1;
215 else
216 break;
219 while ( nLow <= nHigh );
221 if ( nComp >= 0 )
222 nMid++;
224 insPos = nMid;
225 maEntries.insert(maEntries.begin()+nMid,pNewEntry);
229 catch (uno::RuntimeException& )
231 // XXX this is arguable, if the exception occurred because pNewEntry is
232 // garbage you wouldn't insert it. If the exception occurred because the
233 // Collator implementation is garbage then give the user a chance to see
234 // his stuff
235 insPos = 0;
236 maEntries.insert(maEntries.begin(),pNewEntry);
241 return insPos;
244 void ImplEntryList::RemoveEntry( sal_Int32 nPos )
246 if (0 <= nPos && static_cast<size_t>(nPos) < maEntries.size())
248 boost::ptr_vector<ImplEntryType>::iterator iter = maEntries.begin()+ nPos;
250 if ( !!iter->maImage )
251 mnImages--;
253 maEntries.erase(iter);
257 sal_Int32 ImplEntryList::FindEntry( const OUString& rString, bool bSearchMRUArea ) const
259 sal_Int32 nEntries = maEntries.size();
260 for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
262 OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n].maStr ) );
263 if ( aComp == rString )
264 return n;
266 return LISTBOX_ENTRY_NOTFOUND;
269 sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStart, bool bForward, bool bLazy ) const
271 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
272 sal_Int32 nEntryCount = GetEntryCount();
273 if ( !bForward )
274 nStart++; // decrements right away
276 const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
277 for ( sal_Int32 n = nStart; bForward ? n < nEntryCount : n != 0; )
279 if ( !bForward )
280 n--;
282 ImplEntryType* pImplEntry = GetEntry( n );
283 bool bMatch;
284 if ( bLazy )
286 bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
288 else
290 bMatch = rStr.isEmpty() || (pImplEntry->maStr.startsWith(rStr));
292 if ( bMatch )
294 nPos = n;
295 break;
298 if ( bForward )
299 n++;
302 return nPos;
305 sal_Int32 ImplEntryList::FindEntry( const void* pData ) const
307 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
308 for ( sal_Int32 n = GetEntryCount(); n; )
310 ImplEntryType* pImplEntry = GetEntry( --n );
311 if ( pImplEntry->mpUserData == pData )
313 nPos = n;
314 break;
317 return nPos;
320 long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex, long i_nBeginHeight ) const
322 long nHeight = i_nBeginHeight;
323 sal_Int32 nStart = i_nEndIndex > i_nBeginIndex ? i_nBeginIndex : i_nEndIndex;
324 sal_Int32 nStop = i_nEndIndex > i_nBeginIndex ? i_nEndIndex : i_nBeginIndex;
325 sal_Int32 nEntryCount = GetEntryCount();
326 if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
328 // sanity check
329 if( nStop > nEntryCount-1 )
330 nStop = nEntryCount-1;
331 if (nStart < 0)
332 nStart = 0;
333 else if( nStart > nEntryCount-1 )
334 nStart = nEntryCount-1;
336 sal_Int32 nIndex = nStart;
337 while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
339 long nPosHeight = GetEntryPtr( nIndex )->mnHeight;
340 if (nHeight > ::std::numeric_limits<long>::max() - nPosHeight)
342 SAL_WARN( "vcl", "ImplEntryList::GetAddedHeight: truncated");
343 break;
345 nHeight += nPosHeight;
346 nIndex++;
349 else
350 nHeight = 0;
351 return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
354 long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
356 ImplEntryType* pImplEntry = GetEntry( nPos );
357 return pImplEntry ? pImplEntry->mnHeight : 0;
360 OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
362 OUString aEntryText;
363 ImplEntryType* pImplEntry = GetEntry( nPos );
364 if ( pImplEntry )
365 aEntryText = pImplEntry->maStr;
366 return aEntryText;
369 bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
371 bool bImage = false;
372 ImplEntryType* pImplEntry = GetEntry( nPos );
373 if ( pImplEntry )
374 bImage = !!pImplEntry->maImage;
375 return bImage;
378 Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
380 Image aImage;
381 ImplEntryType* pImplEntry = GetEntry( nPos );
382 if ( pImplEntry )
383 aImage = pImplEntry->maImage;
384 return aImage;
387 void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
389 ImplEntryType* pImplEntry = GetEntry( nPos );
390 if ( pImplEntry )
391 pImplEntry->mpUserData = pNewData;
394 void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
396 ImplEntryType* pImplEntry = GetEntry( nPos );
397 return pImplEntry ? pImplEntry->mpUserData : NULL;
400 void ImplEntryList::SetEntryFlags( sal_Int32 nPos, long nFlags )
402 ImplEntryType* pImplEntry = GetEntry( nPos );
403 if ( pImplEntry )
404 pImplEntry->mnFlags = nFlags;
407 long ImplEntryList::GetEntryFlags( sal_Int32 nPos ) const
409 ImplEntryType* pImplEntry = GetEntry( nPos );
410 return pImplEntry ? pImplEntry->mnFlags : 0;
413 sal_Int32 ImplEntryList::GetSelectEntryCount() const
415 sal_Int32 nSelCount = 0;
416 for ( sal_Int32 n = GetEntryCount(); n; )
418 ImplEntryType* pImplEntry = GetEntry( --n );
419 if ( pImplEntry->mbIsSelected )
420 nSelCount++;
422 return nSelCount;
425 OUString ImplEntryList::GetSelectEntry( sal_Int32 nIndex ) const
427 return GetEntryText( GetSelectEntryPos( nIndex ) );
430 sal_Int32 ImplEntryList::GetSelectEntryPos( sal_Int32 nIndex ) const
432 sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
433 sal_Int32 nSel = 0;
434 sal_Int32 nEntryCount = GetEntryCount();
436 for ( sal_Int32 n = 0; n < nEntryCount; n++ )
438 ImplEntryType* pImplEntry = GetEntry( n );
439 if ( pImplEntry->mbIsSelected )
441 if ( nSel == nIndex )
443 nSelEntryPos = n;
444 break;
446 nSel++;
450 return nSelEntryPos;
453 bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
455 ImplEntryType* pImplEntry = GetEntry( nIndex );
456 return pImplEntry && pImplEntry->mbIsSelected;
459 bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
461 ImplEntryType* pImplEntry = GetEntry( nPos );
462 return pImplEntry ? ((pImplEntry->mnFlags & LISTBOX_ENTRY_FLAG_DISABLE_SELECTION) == 0) : true;
465 sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ )
467 if( IsEntrySelectable( nPos ) )
468 return nPos;
470 if( bForward )
472 for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
474 if( IsEntrySelectable( nPos ) )
475 return nPos;
478 else
480 while( nPos )
482 nPos--;
483 if( IsEntrySelectable( nPos ) )
484 return nPos;
488 return LISTBOX_ENTRY_NOTFOUND;
491 ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
492 Control( pParent, 0 ),
493 maQuickSelectionEngine( *this )
495 mpEntryList = new ImplEntryList( this );
497 mnTop = 0;
498 mnLeft = 0;
499 mnBorder = 1;
500 mnSelectModifier = 0;
501 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
502 mbTrack = false;
503 mbImgsDiffSz = false;
504 mbTravelSelect = false;
505 mbTrackingSelect = false;
506 mbSelectionChanged = false;
507 mbMouseMoveSelect = false;
508 mbMulti = false;
509 mbStackMode = false;
510 mbGrabFocus = false;
511 mbUserDrawEnabled = false;
512 mbInUserDraw = false;
513 mbReadOnly = false;
514 mbHasFocusRect = false;
515 mbRight = ( nWinStyle & WB_RIGHT );
516 mbCenter = ( nWinStyle & WB_CENTER );
517 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
518 mbSort = ( nWinStyle & WB_SORT );
519 mbEdgeBlending = false;
521 // pb: #106948# explicit mirroring for calc
522 mbMirroring = false;
524 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
525 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
526 mnSeparatorPos = LISTBOX_ENTRY_NOTFOUND;
527 meProminentType = PROMINENT_TOP;
529 SetLineColor();
530 SetTextFillColor();
531 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
533 ImplInitSettings( true, true, true );
534 ImplCalcMetrics();
537 ImplListBoxWindow::~ImplListBoxWindow()
539 delete mpEntryList;
542 void ImplListBoxWindow::ImplInitSettings( bool bFont, bool bForeground, bool bBackground )
544 ImplInitFieldSettings( this, bFont, bForeground, bBackground );
547 void ImplListBoxWindow::ImplCalcMetrics()
549 mnMaxWidth = 0;
550 mnMaxTxtWidth = 0;
551 mnMaxImgWidth = 0;
552 mnMaxImgTxtWidth= 0;
553 mnMaxImgHeight = 0;
555 mnTextHeight = (sal_uInt16)GetTextHeight();
556 mnMaxTxtHeight = mnTextHeight + mnBorder;
557 mnMaxHeight = mnMaxTxtHeight;
559 if ( maUserItemSize.Height() > mnMaxHeight )
560 mnMaxHeight = (sal_uInt16) maUserItemSize.Height();
561 if ( maUserItemSize.Width() > mnMaxWidth )
562 mnMaxWidth= (sal_uInt16) maUserItemSize.Width();
564 for ( sal_Int32 n = mpEntryList->GetEntryCount(); n; )
566 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
567 ImplUpdateEntryMetrics( *pEntry );
570 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
572 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->mnHeight );
573 maFocusRect.SetSize( aSz );
577 void ImplListBoxWindow::Clear()
579 mpEntryList->Clear();
581 mnMaxHeight = mnMaxTxtHeight;
582 mnMaxWidth = 0;
583 mnMaxTxtWidth = 0;
584 mnMaxImgTxtWidth= 0;
585 mnMaxImgWidth = 0;
586 mnMaxImgHeight = 0;
587 mnTop = 0;
588 mnLeft = 0;
589 mbImgsDiffSz = false;
590 ImplClearLayoutData();
592 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
593 maQuickSelectionEngine.Reset();
595 Invalidate();
598 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
600 ImplClearLayoutData();
601 maUserItemSize = rSz;
602 ImplCalcMetrics();
605 struct ImplEntryMetrics
607 bool bText;
608 bool bImage;
609 long nEntryWidth;
610 long nEntryHeight;
611 long nTextWidth;
612 long nImgWidth;
613 long nImgHeight;
616 void ImplListBoxWindow::EnableQuickSelection( const bool& b )
618 maQuickSelectionEngine.SetEnabled( b );
621 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
623 ImplEntryMetrics aMetrics;
624 aMetrics.bText = !rEntry.maStr.isEmpty();
625 aMetrics.bImage = !!rEntry.maImage;
626 aMetrics.nEntryWidth = 0;
627 aMetrics.nEntryHeight = 0;
628 aMetrics.nTextWidth = 0;
629 aMetrics.nImgWidth = 0;
630 aMetrics.nImgHeight = 0;
632 if ( aMetrics.bText )
634 if( (rEntry.mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
636 // multiline case
637 Size aCurSize( PixelToLogic( GetSizePixel() ) );
638 // set the current size to a large number
639 // GetTextRect should shrink it to the actual size
640 aCurSize.Height() = 0x7fffff;
641 Rectangle aTextRect( Point( 0, 0 ), aCurSize );
642 aTextRect = GetTextRect( aTextRect, rEntry.maStr, TEXT_DRAW_WORDBREAK | TEXT_DRAW_MULTILINE );
643 aMetrics.nTextWidth = aTextRect.GetWidth();
644 if( aMetrics.nTextWidth > mnMaxTxtWidth )
645 mnMaxTxtWidth = aMetrics.nTextWidth;
646 aMetrics.nEntryWidth = mnMaxTxtWidth;
647 aMetrics.nEntryHeight = aTextRect.GetHeight() + mnBorder;
649 else
651 // normal single line case
652 aMetrics.nTextWidth = (sal_uInt16)GetTextWidth( rEntry.maStr );
653 if( aMetrics.nTextWidth > mnMaxTxtWidth )
654 mnMaxTxtWidth = aMetrics.nTextWidth;
655 aMetrics.nEntryWidth = mnMaxTxtWidth;
656 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
659 if ( aMetrics.bImage )
661 Size aImgSz = rEntry.maImage.GetSizePixel();
662 aMetrics.nImgWidth = (sal_uInt16) CalcZoom( aImgSz.Width() );
663 aMetrics.nImgHeight = (sal_uInt16) CalcZoom( aImgSz.Height() );
665 if( mnMaxImgWidth && ( aMetrics.nImgWidth != mnMaxImgWidth ) )
666 mbImgsDiffSz = true;
667 else if ( mnMaxImgHeight && ( aMetrics.nImgHeight != mnMaxImgHeight ) )
668 mbImgsDiffSz = true;
670 if( aMetrics.nImgWidth > mnMaxImgWidth )
671 mnMaxImgWidth = aMetrics.nImgWidth;
672 if( aMetrics.nImgHeight > mnMaxImgHeight )
673 mnMaxImgHeight = aMetrics.nImgHeight;
675 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
676 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
679 if ( IsUserDrawEnabled() || aMetrics.bImage )
681 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
682 if ( aMetrics.bText )
683 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
684 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
685 aMetrics.nEntryHeight );
688 if ( !aMetrics.bText && !aMetrics.bImage && !IsUserDrawEnabled() )
690 // entries which have no (aka an empty) text, and no image,
691 // and are not user-drawn, should be shown nonetheless
692 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
695 if ( aMetrics.nEntryWidth > mnMaxWidth )
696 mnMaxWidth = aMetrics.nEntryWidth;
697 if ( aMetrics.nEntryHeight > mnMaxHeight )
698 mnMaxHeight = aMetrics.nEntryHeight;
700 rEntry.mnHeight = aMetrics.nEntryHeight;
703 void ImplListBoxWindow::ImplCallSelect()
705 if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
707 // Insert the selected entry as MRU, if not already first MRU
708 sal_Int32 nSelected = GetEntryList()->GetSelectEntryPos( 0 );
709 sal_Int32 nMRUCount = GetEntryList()->GetMRUCount();
710 OUString aSelected = GetEntryList()->GetEntryText( nSelected );
711 sal_Int32 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, true );
712 if ( nFirstMatchingEntryPos || !nMRUCount )
714 bool bSelectNewEntry = false;
715 if ( nFirstMatchingEntryPos < nMRUCount )
717 RemoveEntry( nFirstMatchingEntryPos );
718 nMRUCount--;
719 if ( nFirstMatchingEntryPos == nSelected )
720 bSelectNewEntry = true;
722 else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
724 RemoveEntry( nMRUCount - 1 );
725 nMRUCount--;
728 ImplClearLayoutData();
730 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
731 pNewEntry->mbIsSelected = bSelectNewEntry;
732 GetEntryList()->InsertEntry( 0, pNewEntry, false );
733 ImplUpdateEntryMetrics( *pNewEntry );
734 GetEntryList()->SetMRUCount( ++nMRUCount );
735 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
736 maMRUChangedHdl.Call( NULL );
740 maSelectHdl.Call( NULL );
741 mbSelectionChanged = false;
744 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
746 if (nPos < 0 || LISTBOX_MAX_ENTRIES <= mpEntryList->GetEntryCount())
747 return LISTBOX_ERROR;
749 ImplClearLayoutData();
750 sal_Int32 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, mbSort );
752 if( (GetStyle() & WB_WORDBREAK) )
753 pNewEntry->mnFlags |= LISTBOX_ENTRY_FLAG_MULTILINE;
755 ImplUpdateEntryMetrics( *pNewEntry );
756 return nNewPos;
759 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
761 ImplClearLayoutData();
762 mpEntryList->RemoveEntry( nPos );
763 if( mnCurrentPos >= mpEntryList->GetEntryCount() )
764 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
765 ImplCalcMetrics();
768 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, long nFlags )
770 mpEntryList->SetEntryFlags( nPos, nFlags );
771 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
772 if( pEntry )
773 ImplUpdateEntryMetrics( *pEntry );
776 void ImplListBoxWindow::ImplShowFocusRect()
778 if ( mbHasFocusRect )
779 HideFocus();
780 ShowFocus( maFocusRect );
781 mbHasFocusRect = true;
784 void ImplListBoxWindow::ImplHideFocusRect()
786 if ( mbHasFocusRect )
788 HideFocus();
789 mbHasFocusRect = false;
793 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
795 long nY = mnBorder;
797 sal_Int32 nSelect = mnTop;
798 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
799 while( pEntry && rPoint.Y() > pEntry->mnHeight + nY )
801 nY += pEntry->mnHeight;
802 pEntry = mpEntryList->GetEntryPtr( ++nSelect );
804 if( pEntry == NULL )
805 nSelect = LISTBOX_ENTRY_NOTFOUND;
807 return nSelect;
810 bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
812 bool bRet = false;
814 if( i_nEntry >= mnTop )
816 if( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
817 PixelToLogic( GetSizePixel() ).Height() )
819 bRet = true;
823 return bRet;
826 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
828 sal_Int32 nPos = mnTop;
829 long nWindowHeight = GetSizePixel().Height();
830 sal_Int32 nCount = mpEntryList->GetEntryCount();
831 long nDiff;
832 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
833 nPos++;
835 if( nDiff > nWindowHeight && nPos > mnTop )
836 nPos--;
838 if( nPos >= nCount )
839 nPos = nCount-1;
841 return nPos;
844 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
846 mbMouseMoveSelect = false; // only till the first MouseButtonDown
847 maQuickSelectionEngine.Reset();
849 if ( !IsReadOnly() )
851 if( rMEvt.GetClicks() == 1 )
853 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
854 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
856 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
857 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
858 else
859 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
861 mnCurrentPos = nSelect;
862 mbTrackingSelect = true;
863 bool bCurPosChange = (mnCurrentPos != nSelect);
864 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
865 mbTrackingSelect = false;
866 if ( mbGrabFocus )
867 GrabFocus();
869 StartTracking( STARTTRACK_SCROLLREPEAT );
872 if( rMEvt.GetClicks() == 2 )
874 maDoubleClickHdl.Call( this );
877 else // if ( mbGrabFocus )
879 GrabFocus();
883 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
885 if ( rMEvt.IsLeaveWindow() )
887 if ( mbStackMode && IsMouseMoveSelect() && IsReallyVisible() )
889 if ( rMEvt.GetPosPixel().Y() < 0 )
891 DeselectAll();
892 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
893 SetTopEntry( 0 );
894 if ( mbStackMode ) // #87072#, #92323#
896 mbTravelSelect = true;
897 mnSelectModifier = rMEvt.GetModifier();
898 ImplCallSelect();
899 mbTravelSelect = false;
905 else if ( ( ( !mbMulti && IsMouseMoveSelect() ) || mbStackMode ) && mpEntryList->GetEntryCount() )
907 Point aPoint;
908 Rectangle aRect( aPoint, GetOutputSizePixel() );
909 if( aRect.IsInside( rMEvt.GetPosPixel() ) )
911 if ( IsMouseMoveSelect() )
913 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
914 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
915 nSelect = mpEntryList->GetEntryCount() - 1;
916 nSelect = std::min( nSelect, GetLastVisibleEntry() );
917 nSelect = std::min( nSelect, (sal_Int32) ( mpEntryList->GetEntryCount() - 1 ) );
918 // Select only visible Entries with MouseMove, otherwise Tracking...
919 if ( IsVisible( nSelect ) &&
920 mpEntryList->IsEntrySelectable( nSelect ) &&
921 ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() || ( nSelect != GetEntryList()->GetSelectEntryPos( 0 ) ) ) )
923 mbTrackingSelect = true;
924 if ( SelectEntries( nSelect, LET_TRACKING, false, false ) )
926 if ( mbStackMode ) // #87072#
928 mbTravelSelect = true;
929 mnSelectModifier = rMEvt.GetModifier();
930 ImplCallSelect();
931 mbTravelSelect = false;
933 // When list box selection change by mouse move, notity
934 // VCLEVENT_LISTBOX_SELECT vcl event.
935 else
937 maListItemSelectHdl.Call(NULL);
940 mbTrackingSelect = false;
944 // if the DD button was pressed and someone moved into the ListBox
945 // with the mouse button pressed...
946 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
948 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
949 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
950 else
951 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
953 if ( mbStackMode && ( mpEntryList->GetSelectionAnchor() == LISTBOX_ENTRY_NOTFOUND ) )
954 mpEntryList->SetSelectionAnchor( 0 );
956 StartTracking( STARTTRACK_SCROLLREPEAT );
962 void ImplListBoxWindow::DeselectAll()
964 while ( GetEntryList()->GetSelectEntryCount() )
966 sal_Int32 nS = GetEntryList()->GetSelectEntryPos( 0 );
967 SelectEntry( nS, false );
971 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
973 if( (mpEntryList->IsEntryPosSelected( nPos ) != bSelect) && mpEntryList->IsEntrySelectable( nPos ) )
975 ImplHideFocusRect();
976 if( bSelect )
978 if( !mbMulti )
980 // deselect the selected entry
981 sal_Int32 nDeselect = GetEntryList()->GetSelectEntryPos( 0 );
982 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
984 //SelectEntryPos( nDeselect, false );
985 GetEntryList()->SelectEntry( nDeselect, false );
986 if ( IsUpdateMode() && IsReallyVisible() )
987 ImplPaint( nDeselect, true );
990 mpEntryList->SelectEntry( nPos, true );
991 mnCurrentPos = nPos;
992 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
994 ImplPaint( nPos );
995 if ( !IsVisible( nPos ) )
997 ImplClearLayoutData();
998 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
999 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
1001 Resize();
1002 ShowProminentEntry( nPos );
1004 else
1006 ShowProminentEntry( nPos );
1011 else
1013 mpEntryList->SelectEntry( nPos, false );
1014 ImplPaint( nPos, true );
1016 mbSelectionChanged = true;
1020 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
1022 bool bSelectionChanged = false;
1024 if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
1026 bool bFocusChanged = false;
1028 // here (Single-ListBox) only one entry can be deselected
1029 if( !mbMulti )
1031 sal_Int32 nDeselect = mpEntryList->GetSelectEntryPos( 0 );
1032 if( nSelect != nDeselect )
1034 SelectEntry( nSelect, true );
1035 mpEntryList->SetLastSelected( nSelect );
1036 bFocusChanged = true;
1037 bSelectionChanged = true;
1040 // MultiListBox without Modifier
1041 else if( mbSimpleMode && !bCtrl && !bShift )
1043 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1044 for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1046 bool bSelect = nPos == nSelect;
1047 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1049 SelectEntry( nPos, bSelect );
1050 bFocusChanged = true;
1051 bSelectionChanged = true;
1054 mpEntryList->SetLastSelected( nSelect );
1055 mpEntryList->SetSelectionAnchor( nSelect );
1057 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1058 else if( ( !mbSimpleMode /* && !bShift */ ) || ( (mbSimpleMode && ( bCtrl || bShift )) || mbStackMode ) )
1060 // Space for selection change
1061 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1063 bool bSelect = ( mbStackMode && IsMouseMoveSelect() ) || !mpEntryList->IsEntryPosSelected( nSelect );
1064 if ( mbStackMode )
1066 sal_Int32 n;
1067 if ( bSelect )
1069 // All entries before nSelect must be selected...
1070 for ( n = 0; n < nSelect; n++ )
1071 SelectEntry( n, true );
1073 if ( !bSelect )
1075 for ( n = nSelect+1; n < mpEntryList->GetEntryCount(); n++ )
1076 SelectEntry( n, false );
1079 SelectEntry( nSelect, bSelect );
1080 mpEntryList->SetLastSelected( nSelect );
1081 mpEntryList->SetSelectionAnchor( mbStackMode ? 0 : nSelect );
1082 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1083 mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1084 bFocusChanged = true;
1085 bSelectionChanged = true;
1087 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1088 ( (bShift||mbStackMode) && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1090 mnCurrentPos = nSelect;
1091 bFocusChanged = true;
1093 sal_Int32 nAnchor = mpEntryList->GetSelectionAnchor();
1094 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && ( mpEntryList->GetSelectEntryCount() || mbStackMode ) )
1096 nAnchor = mbStackMode ? 0 : mpEntryList->GetSelectEntryPos( mpEntryList->GetSelectEntryCount() - 1 );
1098 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1100 // All entries from achor to nSelect have to be selected
1101 sal_Int32 nStart = std::min( nSelect, nAnchor );
1102 sal_Int32 nEnd = std::max( nSelect, nAnchor );
1103 for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1105 if ( !mpEntryList->IsEntryPosSelected( n ) )
1107 SelectEntry( n, true );
1108 bSelectionChanged = true;
1112 // if appropriate some more has to be deselected...
1113 sal_Int32 nLast = mpEntryList->GetLastSelected();
1114 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1116 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1118 for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1120 if ( mpEntryList->IsEntryPosSelected( n ) )
1122 SelectEntry( n, false );
1123 bSelectionChanged = true;
1127 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1129 for ( sal_Int32 n = nLast; n < nSelect; n++ )
1131 if ( mpEntryList->IsEntryPosSelected( n ) )
1133 SelectEntry( n, false );
1134 bSelectionChanged = true;
1139 mpEntryList->SetLastSelected( nSelect );
1142 else if( eLET != LET_TRACKING )
1144 ImplHideFocusRect();
1145 ImplPaint( nSelect, true );
1146 bFocusChanged = true;
1149 else if( bShift )
1151 bFocusChanged = true;
1154 if( bSelectionChanged )
1155 mbSelectionChanged = true;
1157 if( bFocusChanged )
1159 long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop, 0 );
1160 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1161 Size aSz( maFocusRect.GetWidth(),
1162 mpEntryList->GetEntryHeight( nSelect ) );
1163 maFocusRect.SetSize( aSz );
1164 if( HasFocus() )
1165 ImplShowFocusRect();
1166 if (bSelectPosChange)
1168 maFocusHdl.Call(reinterpret_cast<void*>(nSelect));
1171 ImplClearLayoutData();
1173 return bSelectionChanged;
1176 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1178 Point aPoint;
1179 Rectangle aRect( aPoint, GetOutputSizePixel() );
1180 bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1182 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1184 if ( bInside && !rTEvt.IsTrackingCanceled() )
1186 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1187 ImplCallSelect();
1189 else
1191 maCancelHdl.Call( NULL );
1192 if ( !mbMulti )
1194 mbTrackingSelect = true;
1195 SelectEntry( mnTrackingSaveSelection, true );
1196 mbTrackingSelect = false;
1197 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1199 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1200 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1201 Size aSz( maFocusRect.GetWidth(),
1202 mpEntryList->GetEntryHeight( mnCurrentPos ) );
1203 maFocusRect.SetSize( aSz );
1204 ImplShowFocusRect();
1209 mbTrack = false;
1211 else
1213 bool bTrackOrQuickClick = mbTrack;
1214 if( !mbTrack )
1216 if ( bInside )
1218 mbTrack = true;
1221 // this case only happens, if the mouse button is pressed very briefly
1222 if( rTEvt.IsTrackingEnded() && mbTrack )
1224 bTrackOrQuickClick = true;
1225 mbTrack = false;
1229 if( bTrackOrQuickClick )
1231 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1232 Point aPt( aMEvt.GetPosPixel() );
1233 bool bShift = aMEvt.IsShift();
1234 bool bCtrl = aMEvt.IsMod1();
1236 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1237 if( aPt.Y() < 0 )
1239 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1241 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1242 if( nSelect < mnTop )
1243 SetTopEntry( mnTop-1 );
1246 else if( aPt.Y() > GetOutputSizePixel().Height() )
1248 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1250 nSelect = std::min( (sal_Int32)(mnCurrentPos+1), (sal_Int32)(mpEntryList->GetEntryCount()-1) );
1251 if( nSelect >= GetLastVisibleEntry() )
1252 SetTopEntry( mnTop+1 );
1255 else
1257 nSelect = (sal_Int32) ( ( aPt.Y() + mnBorder ) / mnMaxHeight ) + (sal_Int32) mnTop;
1258 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1259 nSelect = std::min( nSelect, (sal_Int32) ( mpEntryList->GetEntryCount() - 1 ) );
1262 if ( bInside )
1264 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() )
1266 mbTrackingSelect = true;
1267 if ( SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl ) )
1269 if ( mbStackMode ) // #87734# (#87072#)
1271 mbTravelSelect = true;
1272 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1273 ImplCallSelect();
1274 mbTravelSelect = false;
1277 mbTrackingSelect = false;
1280 else
1282 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
1284 mbTrackingSelect = true;
1285 SelectEntry( GetEntryList()->GetSelectEntryPos( 0 ), false );
1286 mbTrackingSelect = false;
1288 else if ( mbStackMode )
1290 if ( ( rTEvt.GetMouseEvent().GetPosPixel().X() > 0 ) && ( rTEvt.GetMouseEvent().GetPosPixel().X() < aRect.Right() ) )
1292 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 ) || ( rTEvt.GetMouseEvent().GetPosPixel().Y() > GetOutputSizePixel().Height() ) )
1294 bool bSelectionChanged = false;
1295 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 )
1296 && !mnCurrentPos )
1298 if ( mpEntryList->IsEntryPosSelected( 0 ) )
1300 SelectEntry( 0, false );
1301 bSelectionChanged = true;
1302 nSelect = LISTBOX_ENTRY_NOTFOUND;
1306 else
1308 mbTrackingSelect = true;
1309 bSelectionChanged = SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl );
1310 mbTrackingSelect = false;
1313 if ( bSelectionChanged )
1315 mbSelectionChanged = true;
1316 mbTravelSelect = true;
1317 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1318 ImplCallSelect();
1319 mbTravelSelect = false;
1325 mnCurrentPos = nSelect;
1326 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1328 ImplHideFocusRect();
1330 else
1332 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1333 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1334 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1335 maFocusRect.SetSize( aSz );
1336 ImplShowFocusRect();
1342 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1344 if( !ProcessKeyInput( rKEvt ) )
1345 Control::KeyInput( rKEvt );
1348 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1350 // entry to be selected
1351 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1352 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1354 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1356 bool bShift = aKeyCode.IsShift();
1357 bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1358 bool bMod2 = aKeyCode.IsMod2();
1359 bool bDone = false;
1361 switch( aKeyCode.GetCode() )
1363 case KEY_UP:
1365 if ( IsReadOnly() )
1367 if ( GetTopEntry() )
1368 SetTopEntry( GetTopEntry()-1 );
1370 else if ( !bMod2 )
1372 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1374 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1376 else if ( mnCurrentPos )
1378 // search first selectable above the current position
1379 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1382 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1383 SetTopEntry( mnTop-1 );
1385 bDone = true;
1387 maQuickSelectionEngine.Reset();
1389 break;
1391 case KEY_DOWN:
1393 if ( IsReadOnly() )
1395 SetTopEntry( GetTopEntry()+1 );
1397 else if ( !bMod2 )
1399 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1401 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1403 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1405 // search first selectable below the current position
1406 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1, true );
1409 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1410 SetTopEntry( mnTop+1 );
1412 bDone = true;
1414 maQuickSelectionEngine.Reset();
1416 break;
1418 case KEY_PAGEUP:
1420 if ( IsReadOnly() )
1422 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1423 SetTopEntry( ( mnTop > nCurVis ) ?
1424 (mnTop-nCurVis) : 0 );
1426 else if ( !bCtrl && !bMod2 )
1428 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1430 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1432 else if ( mnCurrentPos )
1434 if( mnCurrentPos == mnTop )
1436 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1437 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1440 // find first selectable starting from mnTop looking forward
1441 nSelect = mpEntryList->FindFirstSelectable( mnTop, true );
1443 bDone = true;
1445 maQuickSelectionEngine.Reset();
1447 break;
1449 case KEY_PAGEDOWN:
1451 if ( IsReadOnly() )
1453 SetTopEntry( GetLastVisibleEntry() );
1455 else if ( !bCtrl && !bMod2 )
1457 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1459 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1461 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1463 sal_Int32 nCount = mpEntryList->GetEntryCount();
1464 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1465 sal_Int32 nTmp = std::min( nCurVis, nCount );
1466 nTmp += mnTop - 1;
1467 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1469 long nTmp2 = std::min( (long)(nCount-nCurVis), (long)((long)mnTop+(long)nCurVis-1) );
1470 nTmp2 = std::max( (long)0 , nTmp2 );
1471 nTmp = (sal_Int32)(nTmp2+(nCurVis-1) );
1472 SetTopEntry( (sal_Int32)nTmp2 );
1474 // find first selectable starting from nTmp looking backwards
1475 nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1477 bDone = true;
1479 maQuickSelectionEngine.Reset();
1481 break;
1483 case KEY_HOME:
1485 if ( IsReadOnly() )
1487 SetTopEntry( 0 );
1489 else if ( !bCtrl && !bMod2 )
1491 if ( mnCurrentPos )
1493 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND, true );
1494 if( mnTop != 0 )
1495 SetTopEntry( 0 );
1497 bDone = true;
1500 maQuickSelectionEngine.Reset();
1502 break;
1504 case KEY_END:
1506 if ( IsReadOnly() )
1508 SetTopEntry( 0xFFFF );
1510 else if ( !bCtrl && !bMod2 )
1512 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1514 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1516 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1518 sal_Int32 nCount = mpEntryList->GetEntryCount();
1519 nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1520 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1521 if( nCount > nCurVis )
1522 SetTopEntry( nCount - nCurVis );
1524 bDone = true;
1526 maQuickSelectionEngine.Reset();
1528 break;
1530 case KEY_LEFT:
1532 if ( !bCtrl && !bMod2 )
1534 ScrollHorz( -HORZ_SCROLL );
1535 bDone = true;
1537 maQuickSelectionEngine.Reset();
1539 break;
1541 case KEY_RIGHT:
1543 if ( !bCtrl && !bMod2 )
1545 ScrollHorz( HORZ_SCROLL );
1546 bDone = true;
1548 maQuickSelectionEngine.Reset();
1550 break;
1552 case KEY_RETURN:
1554 if ( !bMod2 && !IsReadOnly() )
1556 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1557 ImplCallSelect();
1558 bDone = false; // do not catch RETURN
1560 maQuickSelectionEngine.Reset();
1562 break;
1564 case KEY_SPACE:
1566 if ( !bMod2 && !IsReadOnly() )
1568 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) || mbStackMode ) )
1570 nSelect = mnCurrentPos;
1571 eLET = LET_KEYSPACE;
1573 bDone = true;
1575 maQuickSelectionEngine.Reset();
1577 break;
1579 case KEY_A:
1581 if( bCtrl && mbMulti )
1583 // paint only once
1584 bool bUpdates = IsUpdateMode();
1585 SetUpdateMode( false );
1587 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1588 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1589 SelectEntry( i, true );
1591 // restore update mode
1592 SetUpdateMode( bUpdates );
1593 Invalidate();
1595 maQuickSelectionEngine.Reset();
1597 bDone = true;
1598 break;
1601 // fall through intentional
1602 default:
1604 if ( !IsReadOnly() )
1606 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1609 break;
1612 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1613 && ( ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1614 || ( eLET == LET_KEYSPACE )
1618 DBG_ASSERT( !mpEntryList->IsEntryPosSelected( nSelect ) || mbMulti, "ImplListBox: Selecting same Entry" );
1619 if( nSelect >= mpEntryList->GetEntryCount() )
1620 nSelect = mpEntryList->GetEntryCount()-1;
1621 bool bCurPosChange = (mnCurrentPos != nSelect);
1622 mnCurrentPos = nSelect;
1623 if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1625 mbTravelSelect = true;
1626 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1627 ImplCallSelect();
1628 mbTravelSelect = false;
1632 return bDone;
1635 namespace
1637 static ::vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1639 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1640 sal_Int32 nEntryCount( _rList.GetEntryCount() );
1641 if ( _nPos >= nEntryCount )
1642 _nPos = 0;
1643 _out_entryText = _rList.GetEntryText( _nPos );
1645 // ::vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1646 // => normalize
1647 return reinterpret_cast< ::vcl::StringEntryIdentifier >( _nPos + 1 );
1650 static sal_Int32 lcl_getEntryPos( ::vcl::StringEntryIdentifier _entry )
1652 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1653 return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1657 ::vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1659 return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1662 ::vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( ::vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1664 sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1665 return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1668 void ImplListBoxWindow::SelectEntry( ::vcl::StringEntryIdentifier _entry )
1670 sal_Int32 nSelect = lcl_getEntryPos( _entry );
1671 if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1673 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1674 // to select the given entry by typing its starting letters. No need to act.
1675 return;
1678 // normalize
1679 OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1680 if( nSelect >= mpEntryList->GetEntryCount() )
1681 nSelect = mpEntryList->GetEntryCount()-1;
1683 // make visible
1684 ShowProminentEntry( nSelect );
1686 // actually select
1687 mnCurrentPos = nSelect;
1688 if ( SelectEntries( nSelect, LET_KEYMOVE, false, false ) )
1690 mbTravelSelect = true;
1691 mnSelectModifier = 0;
1692 ImplCallSelect();
1693 mbTravelSelect = false;
1697 void ImplListBoxWindow::ImplPaint( sal_Int32 nPos, bool bErase, bool bLayout )
1699 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1701 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1702 if( ! pEntry )
1703 return;
1705 long nWidth = GetOutputSizePixel().Width();
1706 long nY = mpEntryList->GetAddedHeight( nPos, mnTop );
1707 Rectangle aRect( Point( 0, nY ), Size( nWidth, pEntry->mnHeight ) );
1709 if( ! bLayout )
1711 if( mpEntryList->IsEntryPosSelected( nPos ) )
1713 SetTextColor( !IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor() );
1714 SetFillColor( rStyleSettings.GetHighlightColor() );
1715 SetTextFillColor( rStyleSettings.GetHighlightColor() );
1716 DrawRect( aRect );
1718 else
1720 ImplInitSettings( false, true, false );
1721 if( !IsEnabled() )
1722 SetTextColor( rStyleSettings.GetDisableColor() );
1723 SetTextFillColor();
1724 if( bErase )
1725 Erase( aRect );
1729 if ( IsUserDrawEnabled() )
1731 mbInUserDraw = true;
1732 mnUserDrawEntry = nPos;
1733 aRect.Left() -= mnLeft;
1734 if ( nPos < GetEntryList()->GetMRUCount() )
1735 nPos = GetEntryList()->FindEntry( GetEntryList()->GetEntryText( nPos ) );
1736 nPos = nPos - GetEntryList()->GetMRUCount();
1737 sal_Int32 nCurr = mnCurrentPos;
1738 if ( mnCurrentPos < GetEntryList()->GetMRUCount() )
1739 nCurr = GetEntryList()->FindEntry( GetEntryList()->GetEntryText( nCurr ) );
1740 nCurr = sal::static_int_cast<sal_Int32>( nCurr - GetEntryList()->GetMRUCount());
1742 UserDrawEvent aUDEvt( this, aRect, nPos, nCurr );
1743 userDrawSignal( &aUDEvt );
1744 mbInUserDraw = false;
1746 else
1748 DrawEntry( nPos, true, true, false, bLayout );
1752 void ImplListBoxWindow::DrawEntry( sal_Int32 nPos, bool bDrawImage, bool bDrawText, bool bDrawTextAtImagePos, bool bLayout )
1754 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1755 if( ! pEntry )
1756 return;
1758 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1760 if ( mbInUserDraw )
1761 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1763 long nY = mpEntryList->GetAddedHeight( nPos, mnTop );
1764 Size aImgSz;
1766 if( bDrawImage && mpEntryList->HasImages() && !bLayout )
1768 Image aImage = mpEntryList->GetEntryImage( nPos );
1769 if( !!aImage )
1771 aImgSz = aImage.GetSizePixel();
1772 Point aPtImg( mnBorder - mnLeft, nY + ( ( pEntry->mnHeight - aImgSz.Height() ) / 2 ) );
1774 // pb: #106948# explicit mirroring for calc
1775 if ( mbMirroring )
1776 // right aligned
1777 aPtImg.X() = mnMaxWidth + mnBorder - aImgSz.Width() - mnLeft;
1779 if ( !IsZoom() )
1781 DrawImage( aPtImg, aImage );
1783 else
1785 aImgSz.Width() = CalcZoom( aImgSz.Width() );
1786 aImgSz.Height() = CalcZoom( aImgSz.Height() );
1787 DrawImage( aPtImg, aImgSz, aImage );
1790 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1791 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1793 if(nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1795 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1796 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1797 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1798 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1800 if(!aBlendFrame.IsEmpty())
1802 DrawBitmapEx(aPtImg, aBlendFrame);
1808 if( bDrawText )
1810 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL;
1811 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL;
1812 OUString aStr( mpEntryList->GetEntryText( nPos ) );
1813 if ( !aStr.isEmpty() )
1815 long nMaxWidth = std::max( static_cast< long >( mnMaxWidth ),
1816 GetOutputSizePixel().Width() - 2*mnBorder );
1817 // a multiline entry should only be as wide a the window
1818 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
1819 nMaxWidth = GetOutputSizePixel().Width() - 2*mnBorder;
1821 Rectangle aTextRect( Point( mnBorder - mnLeft, nY ),
1822 Size( nMaxWidth, pEntry->mnHeight ) );
1824 if( !bDrawTextAtImagePos && ( mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled() ) )
1826 long nImageWidth = std::max( mnMaxImgWidth, maUserItemSize.Width() );
1827 aTextRect.Left() += nImageWidth + IMG_TXT_DISTANCE;
1830 if( bLayout )
1831 mpControlData->mpLayoutData->m_aLineIndices.push_back( mpControlData->mpLayoutData->m_aDisplayText.getLength() );
1833 // pb: #106948# explicit mirroring for calc
1834 if ( mbMirroring )
1836 // right aligned
1837 aTextRect.Left() = nMaxWidth + mnBorder - GetTextWidth( aStr ) - mnLeft;
1838 if ( aImgSz.Width() > 0 )
1839 aTextRect.Left() -= ( aImgSz.Width() + IMG_TXT_DISTANCE );
1842 sal_uInt16 nDrawStyle = ImplGetTextStyle();
1843 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
1844 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1845 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_DRAW_DISABLED) )
1846 nDrawStyle |= TEXT_DRAW_DISABLE;
1848 DrawText( aTextRect, aStr, nDrawStyle, pVector, pDisplayText );
1852 if( !bLayout )
1854 if ( ( mnSeparatorPos != LISTBOX_ENTRY_NOTFOUND ) &&
1855 ( ( nPos == mnSeparatorPos ) || ( nPos == mnSeparatorPos+1 ) ) )
1857 Color aOldLineColor( GetLineColor() );
1858 SetLineColor( ( GetBackground().GetColor() != COL_LIGHTGRAY ) ? COL_LIGHTGRAY : COL_GRAY );
1859 Point aStartPos( 0, nY );
1860 if ( nPos == mnSeparatorPos )
1861 aStartPos.Y() += pEntry->mnHeight-1;
1862 Point aEndPos( aStartPos );
1863 aEndPos.X() = GetOutputSizePixel().Width();
1864 DrawLine( aStartPos, aEndPos );
1865 SetLineColor( aOldLineColor );
1870 void ImplListBoxWindow::FillLayoutData() const
1872 mpControlData->mpLayoutData = new vcl::ControlLayoutData();
1873 const_cast<ImplListBoxWindow*>(this)->
1874 ImplDoPaint( Rectangle( Point( 0, 0 ), GetOutputSize() ), true );
1877 void ImplListBoxWindow::ImplDoPaint( const Rectangle& rRect, bool bLayout )
1879 sal_Int32 nCount = mpEntryList->GetEntryCount();
1881 bool bShowFocusRect = mbHasFocusRect;
1882 if ( mbHasFocusRect && ! bLayout )
1883 ImplHideFocusRect();
1885 long nY = 0; // + mnBorder;
1886 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
1888 for( sal_Int32 i = (sal_Int32)mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++ )
1890 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( i );
1891 if( nY + pEntry->mnHeight >= rRect.Top() &&
1892 nY <= rRect.Bottom() + mnMaxHeight )
1894 ImplPaint( i, false, bLayout );
1896 nY += pEntry->mnHeight;
1899 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1900 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1901 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1902 maFocusRect.SetSize( aSz );
1903 if( HasFocus() && bShowFocusRect && !bLayout )
1904 ImplShowFocusRect();
1907 void ImplListBoxWindow::Paint( const Rectangle& rRect )
1909 ImplDoPaint( rRect );
1912 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1914 // FIXME: LISTBOX_ENTRY_FLAG_MULTILINE
1916 sal_Int32 nCount = mpEntryList->GetEntryCount();
1917 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
1918 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1919 if( nEntries > nCount-mnTop )
1920 nEntries = nCount-mnTop;
1922 return nEntries;
1925 void ImplListBoxWindow::Resize()
1927 Control::Resize();
1929 bool bShowFocusRect = mbHasFocusRect;
1930 if ( bShowFocusRect )
1931 ImplHideFocusRect();
1933 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1935 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1936 maFocusRect.SetSize( aSz );
1939 if ( bShowFocusRect )
1940 ImplShowFocusRect();
1942 ImplClearLayoutData();
1945 void ImplListBoxWindow::GetFocus()
1947 sal_Int32 nPos = mnCurrentPos;
1948 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1949 nPos = 0;
1950 long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop, 0 );
1951 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1952 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
1953 maFocusRect.SetSize( aSz );
1954 ImplShowFocusRect();
1955 Control::GetFocus();
1958 void ImplListBoxWindow::LoseFocus()
1960 ImplHideFocusRect();
1961 Control::LoseFocus();
1964 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1966 if( mpEntryList->GetEntryCount() == 0 )
1967 return;
1969 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1971 sal_Int32 nLastEntry = mpEntryList->GetEntryCount()-1;
1972 if( nTop > nLastEntry )
1973 nTop = nLastEntry;
1974 const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
1975 while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->mnHeight <= nWHeight )
1976 nTop--;
1978 if ( nTop != mnTop )
1980 ImplClearLayoutData();
1981 long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop, 0 );
1982 Update();
1983 ImplHideFocusRect();
1984 mnTop = nTop;
1985 Scroll( 0, nDiff );
1986 Update();
1987 if( HasFocus() )
1988 ImplShowFocusRect();
1989 maScrollHdl.Call( this );
1993 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
1995 if( meProminentType == PROMINENT_MIDDLE )
1997 sal_Int32 nPos = nEntryPos;
1998 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1999 while( nEntryPos > 0 && mpEntryList->GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
2000 nEntryPos--;
2002 SetTopEntry( nEntryPos );
2005 void ImplListBoxWindow::SetLeftIndent( long n )
2007 ScrollHorz( n - mnLeft );
2010 void ImplListBoxWindow::ScrollHorz( long n )
2012 long nDiff = 0;
2013 if ( n > 0 )
2015 long nWidth = GetOutputSizePixel().Width();
2016 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
2017 nDiff = n;
2019 else if ( n < 0 )
2021 if( mnLeft )
2023 long nAbs = -n;
2024 nDiff = - ( ( mnLeft > nAbs ) ? nAbs : mnLeft );
2028 if ( nDiff )
2030 ImplClearLayoutData();
2031 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
2032 Update();
2033 ImplHideFocusRect();
2034 Scroll( -nDiff, 0 );
2035 Update();
2036 if( HasFocus() )
2037 ImplShowFocusRect();
2038 maScrollHdl.Call( this );
2042 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
2044 // FIXME: LISTBOX_ENTRY_FLAG_MULTILINE
2046 Size aSz;
2047 aSz.Height() = nMaxLines * mnMaxHeight;
2048 aSz.Width() = mnMaxWidth + 2*mnBorder;
2049 return aSz;
2052 Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
2054 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
2055 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->mnHeight : GetEntryHeight() );
2056 long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList()->GetMRUCount()*GetEntryHeight();
2057 Rectangle aRect( Point( 0, nY ), aSz );
2058 return aRect;
2061 void ImplListBoxWindow::StateChanged( StateChangedType nType )
2063 Control::StateChanged( nType );
2065 if ( nType == StateChangedType::ZOOM )
2067 ImplInitSettings( true, false, false );
2068 ImplCalcMetrics();
2069 Invalidate();
2071 else if ( nType == StateChangedType::UPDATEMODE )
2073 if ( IsUpdateMode() && IsReallyVisible() )
2074 Invalidate();
2076 else if ( nType == StateChangedType::CONTROLFONT )
2078 ImplInitSettings( true, false, false );
2079 ImplCalcMetrics();
2080 Invalidate();
2082 else if ( nType == StateChangedType::CONTROLFOREGROUND )
2084 ImplInitSettings( false, true, false );
2085 Invalidate();
2087 else if ( nType == StateChangedType::CONTROLBACKGROUND )
2089 ImplInitSettings( false, false, true );
2090 Invalidate();
2092 else if( nType == StateChangedType::ENABLE )
2094 Invalidate();
2097 ImplClearLayoutData();
2100 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
2102 Control::DataChanged( rDCEvt );
2104 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2105 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2106 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2107 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2109 ImplClearLayoutData();
2110 ImplInitSettings( true, true, true );
2111 ImplCalcMetrics();
2112 Invalidate();
2116 sal_uInt16 ImplListBoxWindow::ImplGetTextStyle() const
2118 sal_uInt16 nTextStyle = TEXT_DRAW_VCENTER;
2120 if ( mpEntryList->HasImages() )
2121 nTextStyle |= TEXT_DRAW_LEFT;
2122 else if ( mbCenter )
2123 nTextStyle |= TEXT_DRAW_CENTER;
2124 else if ( mbRight )
2125 nTextStyle |= TEXT_DRAW_RIGHT;
2126 else
2127 nTextStyle |= TEXT_DRAW_LEFT;
2129 return nTextStyle;
2132 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2133 Control( pParent, nWinStyle ),
2134 maLBWindow( this, nWinStyle&(~WB_BORDER) )
2136 maLBWindow.userDrawSignal.connect( userDrawSignal );
2138 // for native widget rendering we must be able to detect this window type
2139 SetType( WINDOW_LISTBOXWINDOW );
2141 mpVScrollBar = new ScrollBar( this, WB_VSCROLL | WB_DRAG );
2142 mpHScrollBar = new ScrollBar( this, WB_HSCROLL | WB_DRAG );
2143 mpScrollBarBox = new ScrollBarBox( this );
2145 Link aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2146 mpVScrollBar->SetScrollHdl( aLink );
2147 mpHScrollBar->SetScrollHdl( aLink );
2149 mbVScroll = false;
2150 mbHScroll = false;
2151 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2152 mbEdgeBlending = false;
2154 maLBWindow.SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2155 maLBWindow.SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2156 maLBWindow.SetEdgeBlending(GetEdgeBlending());
2157 maLBWindow.Show();
2160 ImplListBox::~ImplListBox()
2162 delete mpHScrollBar;
2163 delete mpVScrollBar;
2164 delete mpScrollBarBox;
2167 void ImplListBox::Clear()
2169 maLBWindow.Clear();
2170 if ( GetEntryList()->GetMRUCount() )
2172 maLBWindow.GetEntryList()->SetMRUCount( 0 );
2173 maLBWindow.SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2175 mpVScrollBar->SetThumbPos( 0 );
2176 mpHScrollBar->SetThumbPos( 0 );
2177 StateChanged( StateChangedType::DATA );
2180 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2182 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2183 sal_Int32 nNewPos = maLBWindow.InsertEntry( nPos, pNewEntry );
2184 if (nNewPos == LISTBOX_ERROR)
2186 delete pNewEntry;
2187 return nNewPos;
2189 StateChanged( StateChangedType::DATA );
2190 return nNewPos;
2193 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2195 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2196 sal_Int32 nNewPos = maLBWindow.InsertEntry( nPos, pNewEntry );
2197 if (nNewPos == LISTBOX_ERROR)
2199 delete pNewEntry;
2200 return nNewPos;
2202 StateChanged( StateChangedType::DATA );
2203 return nNewPos;
2206 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2208 maLBWindow.RemoveEntry( nPos );
2209 StateChanged( StateChangedType::DATA );
2212 void ImplListBox::SetEntryFlags( sal_Int32 nPos, long nFlags )
2214 maLBWindow.SetEntryFlags( nPos, nFlags );
2217 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2219 maLBWindow.SelectEntry( nPos, bSelect );
2222 void ImplListBox::SetNoSelection()
2224 maLBWindow.DeselectAll();
2227 void ImplListBox::GetFocus()
2229 maLBWindow.GrabFocus();
2232 vcl::Window* ImplListBox::GetPreferredKeyInputWindow()
2234 return &maLBWindow;
2237 void ImplListBox::Resize()
2239 Control::Resize();
2240 ImplResizeControls();
2241 ImplCheckScrollBars();
2244 IMPL_LINK_NOARG(ImplListBox, MRUChanged)
2246 StateChanged( StateChangedType::DATA );
2247 return 1;
2250 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled)
2252 long nSet = GetTopEntry();
2253 if( nSet > mpVScrollBar->GetRangeMax() )
2254 mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2255 mpVScrollBar->SetThumbPos( GetTopEntry() );
2257 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2259 maScrollHdl.Call( this );
2261 return 1;
2264 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB )
2266 sal_uInt16 nPos = (sal_uInt16) pSB->GetThumbPos();
2267 if( pSB == mpVScrollBar )
2268 SetTopEntry( nPos );
2269 else if( pSB == mpHScrollBar )
2270 SetLeftIndent( nPos );
2272 return 1;
2275 void ImplListBox::ImplCheckScrollBars()
2277 bool bArrange = false;
2279 Size aOutSz = GetOutputSizePixel();
2280 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2281 sal_uInt16 nMaxVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2283 // vertical ScrollBar
2284 if( nEntries > nMaxVisEntries )
2286 if( !mbVScroll )
2287 bArrange = true;
2288 mbVScroll = true;
2290 // check of the scrolled-out region
2291 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2292 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2293 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2294 else
2295 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2297 else
2299 if( mbVScroll )
2300 bArrange = true;
2301 mbVScroll = false;
2302 SetTopEntry( 0 );
2305 // horizontal ScrollBar
2306 if( mbAutoHScroll )
2308 long nWidth = (sal_uInt16) aOutSz.Width();
2309 if ( mbVScroll )
2310 nWidth -= mpVScrollBar->GetSizePixel().Width();
2312 long nMaxWidth = GetMaxEntryWidth();
2313 if( nWidth < nMaxWidth )
2315 if( !mbHScroll )
2316 bArrange = true;
2317 mbHScroll = true;
2319 if ( !mbVScroll ) // maybe we do need one now
2321 nMaxVisEntries = (sal_uInt16) ( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeight() );
2322 if( nEntries > nMaxVisEntries )
2324 bArrange = true;
2325 mbVScroll = true;
2327 // check of the scrolled-out region
2328 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2329 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2330 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2331 else
2332 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2336 // check of the scrolled-out region
2337 sal_uInt16 nMaxLI = (sal_uInt16) (nMaxWidth - nWidth);
2338 if ( nMaxLI < GetLeftIndent() )
2339 SetLeftIndent( nMaxLI );
2341 else
2343 if( mbHScroll )
2344 bArrange = true;
2345 mbHScroll = false;
2346 SetLeftIndent( 0 );
2350 if( bArrange )
2351 ImplResizeControls();
2353 ImplInitScrollBars();
2356 void ImplListBox::ImplInitScrollBars()
2358 Size aOutSz = maLBWindow.GetOutputSizePixel();
2360 if ( mbVScroll )
2362 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2363 sal_uInt16 nVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2364 mpVScrollBar->SetRangeMax( nEntries );
2365 mpVScrollBar->SetVisibleSize( nVisEntries );
2366 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2369 if ( mbHScroll )
2371 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2372 mpHScrollBar->SetVisibleSize( (sal_uInt16)aOutSz.Width() );
2373 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2374 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2378 void ImplListBox::ImplResizeControls()
2380 // Here we only position the Controls; if the Scrollbars are to be
2381 // visible is already determined in ImplCheckScrollBars
2383 Size aOutSz = GetOutputSizePixel();
2384 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2385 nSBWidth = CalcZoom( nSBWidth );
2387 Size aInnerSz( aOutSz );
2388 if ( mbVScroll )
2389 aInnerSz.Width() -= nSBWidth;
2390 if ( mbHScroll )
2391 aInnerSz.Height() -= nSBWidth;
2393 // pb: #106948# explicit mirroring for calc
2394 // Scrollbar on left or right side?
2395 bool bMirroring = maLBWindow.IsMirroring();
2396 Point aWinPos( bMirroring && mbVScroll ? nSBWidth : 0, 0 );
2397 maLBWindow.SetPosSizePixel( aWinPos, aInnerSz );
2399 // ScrollBarBox
2400 if( mbVScroll && mbHScroll )
2402 Point aBoxPos( bMirroring ? 0 : aInnerSz.Width(), aInnerSz.Height() );
2403 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2404 mpScrollBarBox->Show();
2406 else
2408 mpScrollBarBox->Hide();
2411 // vertical ScrollBar
2412 if( mbVScroll )
2414 // Scrollbar on left or right side?
2415 Point aVPos( bMirroring ? 0 : aOutSz.Width() - nSBWidth, 0 );
2416 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2417 mpVScrollBar->Show();
2419 else
2421 mpVScrollBar->Hide();
2422 // #107254# Don't reset top entry after resize, but check for max top entry
2423 SetTopEntry( GetTopEntry() );
2426 // horizontal ScrollBar
2427 if( mbHScroll )
2429 Point aHPos( ( bMirroring && mbVScroll ) ? nSBWidth : 0, aOutSz.Height() - nSBWidth );
2430 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2431 mpHScrollBar->Show();
2433 else
2435 mpHScrollBar->Hide();
2436 SetLeftIndent( 0 );
2440 void ImplListBox::StateChanged( StateChangedType nType )
2442 if ( nType == StateChangedType::INITSHOW )
2444 ImplCheckScrollBars();
2446 else if ( ( nType == StateChangedType::UPDATEMODE ) || ( nType == StateChangedType::DATA ) )
2448 bool bUpdate = IsUpdateMode();
2449 maLBWindow.SetUpdateMode( bUpdate );
2450 if ( bUpdate && IsReallyVisible() )
2451 ImplCheckScrollBars();
2453 else if( nType == StateChangedType::ENABLE )
2455 mpHScrollBar->Enable( IsEnabled() );
2456 mpVScrollBar->Enable( IsEnabled() );
2457 mpScrollBarBox->Enable( IsEnabled() );
2458 maLBWindow.Enable( IsEnabled() );
2460 Invalidate();
2462 else if ( nType == StateChangedType::ZOOM )
2464 maLBWindow.SetZoom( GetZoom() );
2465 Resize();
2467 else if ( nType == StateChangedType::CONTROLFONT )
2469 maLBWindow.SetControlFont( GetControlFont() );
2471 else if ( nType == StateChangedType::CONTROLFOREGROUND )
2473 maLBWindow.SetControlForeground( GetControlForeground() );
2475 else if ( nType == StateChangedType::CONTROLBACKGROUND )
2477 maLBWindow.SetControlBackground( GetControlBackground() );
2479 else if( nType == StateChangedType::MIRRORING )
2481 maLBWindow.EnableRTL( IsRTLEnabled() );
2482 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2483 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2484 ImplResizeControls();
2487 Control::StateChanged( nType );
2490 void ImplListBox::DataChanged( const DataChangedEvent& rDCEvt )
2492 Control::DataChanged( rDCEvt );
2495 bool ImplListBox::Notify( NotifyEvent& rNEvt )
2497 bool nDone = false;
2498 if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
2500 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2501 if ( rCEvt.GetCommand() == COMMAND_WHEEL )
2503 const CommandWheelData* pData = rCEvt.GetWheelData();
2504 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2506 nDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2511 return nDone || Window::Notify( rNEvt );
2514 const Wallpaper& ImplListBox::GetDisplayBackground() const
2516 return maLBWindow.GetDisplayBackground();
2519 bool ImplListBox::HandleWheelAsCursorTravel( const CommandEvent& rCEvt )
2521 bool bDone = false;
2522 if ( rCEvt.GetCommand() == COMMAND_WHEEL )
2524 const CommandWheelData* pData = rCEvt.GetWheelData();
2525 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2527 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2528 KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2529 bDone = ProcessKeyInput( aKeyEvent );
2532 return bDone;
2535 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2537 bool bChanges = GetEntryList()->GetMRUCount() ? true : false;
2539 // Remove old MRU entries
2540 for ( sal_Int32 n = GetEntryList()->GetMRUCount();n; )
2541 maLBWindow.RemoveEntry( --n );
2543 sal_Int32 nMRUCount = 0;
2544 sal_Int32 nIndex = 0;
2547 OUString aEntry = rEntries.getToken( 0, cSep, nIndex );
2548 // Accept only existing entries
2549 if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2551 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2552 maLBWindow.GetEntryList()->InsertEntry( nMRUCount++, pNewEntry, false );
2553 bChanges = true;
2556 while ( nIndex >= 0 );
2558 if ( bChanges )
2560 maLBWindow.GetEntryList()->SetMRUCount( nMRUCount );
2561 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2562 StateChanged( StateChangedType::DATA );
2566 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2568 OUStringBuffer aEntries;
2569 for ( sal_Int32 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2571 aEntries.append(GetEntryList()->GetEntryText( n ));
2572 if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2573 aEntries.append(cSep);
2575 return aEntries.makeStringAndClear();
2578 void ImplListBox::SetEdgeBlending(bool bNew)
2580 if(mbEdgeBlending != bNew)
2582 mbEdgeBlending = bNew;
2583 maLBWindow.SetEdgeBlending(GetEdgeBlending());
2587 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2588 Control ( pParent, nWinStyle )
2590 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2591 && ! IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
2592 SetBackground();
2593 else
2594 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2596 mbInUserDraw = false;
2597 mbUserDrawEnabled = false;
2598 mbEdgeBlending = false;
2599 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2602 void ImplWin::MBDown()
2604 if( IsEnabled() )
2605 buttonDownSignal( this );
2608 void ImplWin::MouseButtonDown( const MouseEvent& )
2610 if( IsEnabled() )
2612 MBDown();
2616 void ImplWin::FillLayoutData() const
2618 mpControlData->mpLayoutData = new vcl::ControlLayoutData();
2619 const_cast<ImplWin*>(this)->ImplDraw( true );
2622 bool ImplWin::PreNotify( NotifyEvent& rNEvt )
2624 const MouseEvent* pMouseEvt = NULL;
2626 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != NULL )
2628 if( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() )
2630 // trigger redraw as mouse over state has changed
2631 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2632 && ! IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
2634 GetParent()->GetWindow( WINDOW_BORDER )->Invalidate( INVALIDATE_NOERASE );
2635 GetParent()->GetWindow( WINDOW_BORDER )->Update();
2640 return Control::PreNotify(rNEvt);
2643 void ImplWin::ImplDraw( bool bLayout )
2645 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
2647 if( ! bLayout )
2649 bool bNativeOK = false;
2651 ControlState nState = ControlState::ENABLED;
2652 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2653 && IsNativeControlSupported(CTRL_LISTBOX, HAS_BACKGROUND_TEXTURE) )
2655 // Repaint the (focused) area similarly to
2656 // ImplSmallBorderWindowView::DrawWindow() in
2657 // vcl/source/window/brdwin.cxx
2658 vcl::Window *pWin = GetParent();
2660 ImplControlValue aControlValue;
2661 if ( !pWin->IsEnabled() )
2662 nState &= ~ControlState::ENABLED;
2663 if ( pWin->HasFocus() )
2664 nState |= ControlState::FOCUSED;
2666 // The listbox is painted over the entire control including the
2667 // border, but ImplWin does not contain the border => correction
2668 // needed.
2669 sal_Int32 nLeft, nTop, nRight, nBottom;
2670 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2671 Point aPoint( -nLeft, -nTop );
2672 Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2674 bool bMouseOver = false;
2675 if( GetParent() )
2677 vcl::Window *pChild = GetParent()->GetWindow( WINDOW_FIRSTCHILD );
2678 while( pChild && !(bMouseOver = pChild->IsMouseOver()) )
2679 pChild = pChild->GetWindow( WINDOW_NEXT );
2682 if( bMouseOver )
2683 nState |= ControlState::ROLLOVER;
2685 // if parent has no border, then nobody has drawn the background
2686 // since no border window exists. so draw it here.
2687 WinBits nParentStyle = pWin->GetStyle();
2688 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2690 Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2691 pWin->DrawNativeControl( CTRL_LISTBOX, PART_ENTIRE_CONTROL, aParentRect,
2692 nState, aControlValue, OUString() );
2695 bNativeOK = DrawNativeControl( CTRL_LISTBOX, PART_ENTIRE_CONTROL, aCtrlRegion, nState,
2696 aControlValue, OUString() );
2699 if( IsEnabled() )
2701 if( HasFocus() )
2703 SetTextColor( rStyleSettings.GetHighlightTextColor() );
2704 SetFillColor( rStyleSettings.GetHighlightColor() );
2705 DrawRect( maFocusRect );
2707 else
2709 Color aColor;
2710 if( ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea )
2712 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2713 aColor = rStyleSettings.GetButtonRolloverTextColor();
2714 else
2715 aColor = rStyleSettings.GetButtonTextColor();
2717 else
2719 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2720 aColor = rStyleSettings.GetFieldRolloverTextColor();
2721 else
2722 aColor = rStyleSettings.GetFieldTextColor();
2724 if( IsControlForeground() )
2725 aColor = GetControlForeground();
2726 SetTextColor( aColor );
2727 if ( !bNativeOK )
2728 Erase( maFocusRect );
2731 else // Disabled
2733 SetTextColor( rStyleSettings.GetDisableColor() );
2734 if ( !bNativeOK )
2735 Erase( maFocusRect );
2739 if ( IsUserDrawEnabled() )
2741 mbInUserDraw = true;
2742 UserDrawEvent aUDEvt( this, maFocusRect, mnItemPos, 0 );
2743 userDrawSignal( &aUDEvt );
2744 mbInUserDraw = false;
2746 else
2748 DrawEntry( true, true, false, bLayout );
2752 void ImplWin::Paint( const Rectangle& )
2754 ImplDraw();
2757 void ImplWin::DrawEntry( bool bDrawImage, bool bDrawText, bool bDrawTextAtImagePos, bool bLayout )
2759 long nBorder = 1;
2760 Size aOutSz = GetOutputSizePixel();
2762 bool bImage = !!maImage;
2763 if( bDrawImage && bImage && !bLayout )
2765 sal_uInt16 nStyle = 0;
2766 Size aImgSz = maImage.GetSizePixel();
2767 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2768 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2770 // check for HC mode
2771 Image *pImage = &maImage;
2773 if ( !IsZoom() )
2775 DrawImage( aPtImg, *pImage, nStyle );
2777 else
2779 aImgSz.Width() = CalcZoom( aImgSz.Width() );
2780 aImgSz.Height() = CalcZoom( aImgSz.Height() );
2781 DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2784 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2786 if(nEdgeBlendingPercent)
2788 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2789 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2790 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2791 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2793 if(!aBlendFrame.IsEmpty())
2795 DrawBitmapEx(aPtImg, aBlendFrame);
2800 if( bDrawText && !maString.isEmpty() )
2802 sal_uInt16 nTextStyle = TEXT_DRAW_VCENTER;
2804 if ( bDrawImage && bImage && !bLayout )
2805 nTextStyle |= TEXT_DRAW_LEFT;
2806 else if ( GetStyle() & WB_CENTER )
2807 nTextStyle |= TEXT_DRAW_CENTER;
2808 else if ( GetStyle() & WB_RIGHT )
2809 nTextStyle |= TEXT_DRAW_RIGHT;
2810 else
2811 nTextStyle |= TEXT_DRAW_LEFT;
2813 Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2815 if ( !bDrawTextAtImagePos && ( bImage || IsUserDrawEnabled() ) )
2817 long nMaxWidth = std::max( maImage.GetSizePixel().Width(), maUserItemSize.Width() );
2818 aTextRect.Left() += nMaxWidth + IMG_TXT_DISTANCE;
2821 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL;
2822 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL;
2823 DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2826 if( HasFocus() && !bLayout )
2827 ShowFocus( maFocusRect );
2830 void ImplWin::Resize()
2832 Control::Resize();
2833 maFocusRect.SetSize( GetOutputSizePixel() );
2834 Invalidate();
2837 void ImplWin::GetFocus()
2839 ShowFocus( maFocusRect );
2840 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2841 IsNativeWidgetEnabled() &&
2842 IsNativeControlSupported( CTRL_LISTBOX, PART_ENTIRE_CONTROL ) )
2844 vcl::Window* pWin = GetParent()->GetWindow( WINDOW_BORDER );
2845 if( ! pWin )
2846 pWin = GetParent();
2847 pWin->Invalidate();
2849 else
2850 Invalidate();
2851 Control::GetFocus();
2854 void ImplWin::LoseFocus()
2856 HideFocus();
2857 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2858 IsNativeWidgetEnabled() &&
2859 IsNativeControlSupported( CTRL_LISTBOX, PART_ENTIRE_CONTROL ) )
2861 vcl::Window* pWin = GetParent()->GetWindow( WINDOW_BORDER );
2862 if( ! pWin )
2863 pWin = GetParent();
2864 pWin->Invalidate();
2866 else
2867 Invalidate();
2868 Control::LoseFocus();
2871 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2872 PushButton( pParent, nWinStyle ),
2873 mbDown ( false )
2877 void ImplBtn::MBDown()
2879 if( IsEnabled() )
2880 buttonDownSignal( this );
2883 void ImplBtn::MouseButtonDown( const MouseEvent& )
2885 //PushButton::MouseButtonDown( rMEvt );
2886 if( IsEnabled() )
2888 MBDown();
2889 mbDown = true;
2893 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2894 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
2896 mpImplLB = NULL;
2897 mnDDLineCount = 0;
2898 mbAutoWidth = false;
2900 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2902 EnableSaveBackground();
2904 vcl::Window * pBorderWindow = ImplGetBorderWindow();
2905 if( pBorderWindow )
2907 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2908 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2910 else
2912 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2917 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2919 if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2921 if( !GetParent()->HasChildPathFocus( true ) )
2922 EndPopupMode();
2925 return FloatingWindow::PreNotify( rNEvt );
2928 void ImplListBoxFloatingWindow::setPosSizePixel( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags )
2930 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2932 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2933 // after a call to Resize(), we adjust its position if necessary
2934 if ( IsReallyVisible() && ( nFlags & WINDOW_POSSIZE_HEIGHT ) )
2936 Point aPos = GetParent()->GetPosPixel();
2937 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2939 if ( nFlags & WINDOW_POSSIZE_X )
2940 aPos.X() = nX;
2942 if ( nFlags & WINDOW_POSSIZE_Y )
2943 aPos.Y() = nY;
2945 sal_uInt16 nIndex;
2946 SetPosPixel( ImplCalcPos( this, Rectangle( aPos, GetParent()->GetSizePixel() ), FLOATWIN_POPUPMODE_DOWN, nIndex ) );
2949 // if( !IsReallyVisible() )
2951 // The ImplListBox does not get a Resize() as not visible.
2952 // But the windows must get a Resize(), so that the number of
2953 // visible entries is correct for PgUp/PgDown.
2954 // The number also cannot be calculated by List/Combobox, as for
2955 // this the presence of the vertical Scrollbar has to be known.
2956 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2957 ((vcl::Window*)mpImplLB)->Resize();
2958 ((vcl::Window&)mpImplLB->GetMainWindow()).Resize();
2962 void ImplListBoxFloatingWindow::Resize()
2964 mpImplLB->GetMainWindow().ImplClearLayoutData();
2965 FloatingWindow::Resize();
2968 Size ImplListBoxFloatingWindow::CalcFloatSize()
2970 Size aFloatSz( maPrefSz );
2972 sal_Int32 nLeft, nTop, nRight, nBottom;
2973 GetBorder( nLeft, nTop, nRight, nBottom );
2975 sal_Int32 nLines = mpImplLB->GetEntryList()->GetEntryCount();
2976 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
2977 nLines = mnDDLineCount;
2979 Size aSz = mpImplLB->CalcSize( nLines );
2980 long nMaxHeight = aSz.Height() + nTop + nBottom;
2982 if ( mnDDLineCount )
2983 aFloatSz.Height() = nMaxHeight;
2985 if( mbAutoWidth )
2987 // AutoSize first only for width...
2989 aFloatSz.Width() = aSz.Width() + nLeft + nRight;
2990 aFloatSz.Width() += nRight; // adding some space looks better...
2992 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
2994 // then we also need the vertical Scrollbar
2995 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2996 aFloatSz.Width() += nSBWidth;
2999 long nDesktopWidth = GetDesktopRectPixel().getWidth();
3000 if (aFloatSz.Width() > nDesktopWidth)
3001 // Don't exceed the desktop width.
3002 aFloatSz.Width() = nDesktopWidth;
3005 if ( aFloatSz.Height() > nMaxHeight )
3006 aFloatSz.Height() = nMaxHeight;
3008 // Minimal height, in case height is not set to Float height.
3009 // The parent of FloatWin must be DropDown-Combo/Listbox.
3010 Size aParentSz = GetParent()->GetSizePixel();
3011 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
3012 aFloatSz.Height() = aParentSz.Height();
3014 // do not get narrower than the parent...
3015 if( aFloatSz.Width() < aParentSz.Width() )
3016 aFloatSz.Width() = aParentSz.Width();
3018 // align height to entries...
3019 long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
3020 long nEntryHeight = mpImplLB->GetEntryHeight();
3021 if ( nInnerHeight % nEntryHeight )
3023 nInnerHeight /= nEntryHeight;
3024 nInnerHeight++;
3025 nInnerHeight *= nEntryHeight;
3026 aFloatSz.Height() = nInnerHeight + nTop + nBottom;
3029 if (aFloatSz.Width() < aSz.Width())
3031 // The max width of list box entries exceeds the window width.
3032 // Account for the scroll bar height.
3033 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3034 aFloatSz.Height() += nSBWidth;
3037 return aFloatSz;
3040 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
3042 if( !IsInPopupMode() )
3044 Size aFloatSz = CalcFloatSize();
3046 SetSizePixel( aFloatSz );
3047 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3049 sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectEntryPos( 0 );
3050 mnPopupModeStartSaveSelection = nPos;
3052 Size aSz = GetParent()->GetSizePixel();
3053 Point aPos = GetParent()->GetPosPixel();
3054 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
3055 // FIXME: this ugly hack is for Mac/Aqua
3056 // should be replaced by a real mechanism to place the float rectangle
3057 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3058 GetParent()->IsNativeWidgetEnabled() )
3060 sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
3061 aPos.X() += nLeft;
3062 aPos.Y() += nTop;
3063 aSz.Width() -= nLeft + nRight;
3064 aSz.Height() -= nTop + nBottom;
3066 Rectangle aRect( aPos, aSz );
3068 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
3069 // where the document is unmirrored
3070 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
3071 vcl::Window *pGrandparent = GetParent()->GetParent();
3072 const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
3074 if( pGrandparent->ImplIsAntiparallel() )
3075 pGrandparentOutDev->ReMirror( aRect );
3077 // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
3078 const sal_uLong nFlags = FLOATWIN_POPUPMODE_PATHMOUSECANCELCLICK | FLOATWIN_POPUPMODE_ALLMOUSEBUTTONCLOSE;
3080 StartPopupMode( aRect, FLOATWIN_POPUPMODE_DOWN | nFlags );
3082 if( nPos != LISTBOX_ENTRY_NOTFOUND )
3083 mpImplLB->ShowProminentEntry( nPos );
3085 if( bStartTracking )
3086 mpImplLB->GetMainWindow().EnableMouseMoveSelect( true );
3088 if ( mpImplLB->GetMainWindow().IsGrabFocusAllowed() )
3089 mpImplLB->GetMainWindow().GrabFocus();
3091 mpImplLB->GetMainWindow().ImplClearLayoutData();
3095 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */