bump product version to 4.1.6.2
[LibreOffice.git] / vcl / source / control / ilstbox.cxx
blob7684ca8bcad707bb776fcb0cb58f0694fdfe0263
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 .
21 #include <tools/debug.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/scrbar.hxx>
27 #include <vcl/help.hxx>
28 #include <vcl/lstbox.h>
29 #include <vcl/unohelp.hxx>
30 #include <vcl/i18nhelp.hxx>
32 #include <ilstbox.hxx>
33 #include <controldata.hxx>
34 #include <svdata.hxx>
36 #include <com/sun/star/i18n/XCollator.hpp>
37 #include <com/sun/star/accessibility/XAccessible.hpp>
38 #include <com/sun/star/accessibility/AccessibleRole.hpp>
40 #include <rtl/instance.hxx>
41 #include <comphelper/string.hxx>
42 #include <comphelper/processfactory.hxx>
44 #define MULTILINE_ENTRY_DRAW_FLAGS ( TEXT_DRAW_WORDBREAK | TEXT_DRAW_MULTILINE | TEXT_DRAW_VCENTER )
46 using namespace ::com::sun::star;
48 // =======================================================================
50 void ImplInitFieldSettings( Window* pWin, sal_Bool bFont, sal_Bool bForeground, sal_Bool bBackground )
52 const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings();
54 if ( bFont )
56 Font aFont = rStyleSettings.GetFieldFont();
57 if ( pWin->IsControlFont() )
58 aFont.Merge( pWin->GetControlFont() );
59 pWin->SetZoomedPointFont( aFont );
62 if ( bFont || bForeground )
64 Color aTextColor = rStyleSettings.GetFieldTextColor();
65 if ( pWin->IsControlForeground() )
66 aTextColor = pWin->GetControlForeground();
67 pWin->SetTextColor( aTextColor );
70 if ( bBackground )
72 if( pWin->IsControlBackground() )
73 pWin->SetBackground( pWin->GetControlBackground() );
74 else
75 pWin->SetBackground( rStyleSettings.GetFieldColor() );
79 // -----------------------------------------------------------------------
81 void ImplInitDropDownButton( PushButton* pButton )
83 if ( pButton->GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_SPINUPDOWN )
84 pButton->SetSymbol( SYMBOL_SPIN_UPDOWN );
85 else
86 pButton->SetSymbol( SYMBOL_SPIN_DOWN );
88 if ( pButton->IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
89 && ! pButton->IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
90 pButton->SetBackground();
93 // =======================================================================
95 ImplEntryList::ImplEntryList( Window* pWindow )
97 mpWindow = pWindow;
98 mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
99 mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
100 mnImages = 0;
101 mbCallSelectionChangedHdl = sal_True;
103 mnMRUCount = 0;
104 mnMaxMRUCount = 0;
107 // -----------------------------------------------------------------------
109 ImplEntryList::~ImplEntryList()
111 Clear();
114 // -----------------------------------------------------------------------
116 void ImplEntryList::Clear()
118 mnImages = 0;
119 maEntries.clear();
122 // -----------------------------------------------------------------------
124 void ImplEntryList::SelectEntry( sal_uInt16 nPos, sal_Bool bSelect )
126 if (nPos < maEntries.size())
128 boost::ptr_vector<ImplEntryType>::iterator iter = maEntries.begin()+nPos;
130 if ( ( iter->mbIsSelected != bSelect ) &&
131 ( (iter->mnFlags & LISTBOX_ENTRY_FLAG_DISABLE_SELECTION) == 0 ) )
133 iter->mbIsSelected = bSelect;
134 if ( mbCallSelectionChangedHdl )
135 maSelectionChangedHdl.Call( (void*)sal_IntPtr(nPos) );
140 namespace
142 struct theSorter
143 : public rtl::StaticWithInit< comphelper::string::NaturalStringSorter, theSorter >
145 comphelper::string::NaturalStringSorter operator () ()
147 return comphelper::string::NaturalStringSorter(
148 ::comphelper::getProcessComponentContext(),
149 Application::GetSettings().GetLanguageTag().getLocale());
154 namespace vcl
156 namespace unohelper
158 const comphelper::string::NaturalStringSorter& getNaturalStringSorterForAppLocale()
160 return theSorter::get();
165 sal_uInt16 ImplEntryList::InsertEntry( sal_uInt16 nPos, ImplEntryType* pNewEntry, sal_Bool bSort )
167 if ( !!pNewEntry->maImage )
168 mnImages++;
170 sal_uInt16 insPos = 0;
172 if ( !bSort || maEntries.empty())
174 if (nPos < maEntries.size())
176 insPos = nPos;
177 maEntries.insert( maEntries.begin() + nPos, pNewEntry );
179 else
181 insPos = maEntries.size();
182 maEntries.push_back(pNewEntry);
185 else
187 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
189 const XubString& rStr = pNewEntry->maStr;
190 sal_uLong nLow, nHigh, nMid;
192 nHigh = maEntries.size();
194 ImplEntryType* pTemp = GetEntry( (sal_uInt16)(nHigh-1) );
198 // XXX even though XCollator::compareString returns a sal_Int32 the only
199 // defined values are {-1, 0, 1} which is compatible with StringCompare
200 StringCompare eComp = (StringCompare)rSorter.compare(rStr, pTemp->maStr);
202 // fast insert for sorted data
203 if ( eComp != COMPARE_LESS )
205 insPos = maEntries.size();
206 maEntries.push_back(pNewEntry);
208 else
210 nLow = mnMRUCount;
211 pTemp = (ImplEntryType*)GetEntry( (sal_uInt16)nLow );
213 eComp = (StringCompare)rSorter.compare(rStr, pTemp->maStr);
214 if ( eComp != COMPARE_GREATER )
216 insPos = 0;
217 maEntries.insert(maEntries.begin(),pNewEntry);
219 else
221 // binary search
222 nHigh--;
225 nMid = (nLow + nHigh) / 2;
226 pTemp = (ImplEntryType*)GetEntry( nMid );
228 eComp = (StringCompare)rSorter.compare(rStr, pTemp->maStr);
230 if ( eComp == COMPARE_LESS )
231 nHigh = nMid-1;
232 else
234 if ( eComp == COMPARE_GREATER )
235 nLow = nMid + 1;
236 else
237 break;
240 while ( nLow <= nHigh );
242 if ( eComp != COMPARE_LESS )
243 nMid++;
245 insPos = nMid;
246 maEntries.insert(maEntries.begin()+nMid,pNewEntry);
250 catch (uno::RuntimeException& )
252 // XXX this is arguable, if the exception occurred because pNewEntry is
253 // garbage you wouldn't insert it. If the exception occurred because the
254 // Collator implementation is garbage then give the user a chance to see
255 // his stuff
256 insPos = 0;
257 maEntries.insert(maEntries.begin(),pNewEntry);
262 return insPos;
265 // -----------------------------------------------------------------------
267 void ImplEntryList::RemoveEntry( sal_uInt16 nPos )
269 if (nPos < maEntries.size())
271 boost::ptr_vector<ImplEntryType>::iterator iter = maEntries.begin()+ nPos;
273 if ( !!iter->maImage )
274 mnImages--;
276 maEntries.erase(iter);
280 // -----------------------------------------------------------------------
282 sal_uInt16 ImplEntryList::FindEntry( const XubString& rString, sal_Bool bSearchMRUArea ) const
284 sal_uInt16 nEntries = maEntries.size();
285 for ( sal_uInt16 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
287 String aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n].maStr ) );
288 if ( aComp == rString )
289 return n;
291 return LISTBOX_ENTRY_NOTFOUND;
294 // -----------------------------------------------------------------------
296 sal_uInt16 ImplEntryList::FindMatchingEntry( const XubString& rStr, sal_uInt16 nStart, sal_Bool bForward, sal_Bool bLazy ) const
298 sal_uInt16 nPos = LISTBOX_ENTRY_NOTFOUND;
299 sal_uInt16 nEntryCount = GetEntryCount();
300 if ( !bForward )
301 nStart++; // decrements right away
303 const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
304 for ( sal_uInt16 n = nStart; bForward ? ( n < nEntryCount ) : n; )
306 if ( !bForward )
307 n--;
309 ImplEntryType* pImplEntry = GetEntry( n );
310 bool bMatch = bLazy ? rI18nHelper.MatchString( rStr, pImplEntry->maStr ) != 0 : ( rStr.Match( pImplEntry->maStr ) == STRING_MATCH );
311 if ( bMatch )
313 nPos = n;
314 break;
317 if ( bForward )
318 n++;
321 return nPos;
324 // -----------------------------------------------------------------------
326 sal_uInt16 ImplEntryList::FindEntry( const void* pData ) const
328 sal_uInt16 nPos = LISTBOX_ENTRY_NOTFOUND;
329 for ( sal_uInt16 n = GetEntryCount(); n; )
331 ImplEntryType* pImplEntry = GetEntry( --n );
332 if ( pImplEntry->mpUserData == pData )
334 nPos = n;
335 break;
338 return nPos;
341 // -----------------------------------------------------------------------
343 long ImplEntryList::GetAddedHeight( sal_uInt16 i_nEndIndex, sal_uInt16 i_nBeginIndex, long i_nBeginHeight ) const
345 long nHeight = i_nBeginHeight;
346 sal_uInt16 nStart = i_nEndIndex > i_nBeginIndex ? i_nBeginIndex : i_nEndIndex;
347 sal_uInt16 nStop = i_nEndIndex > i_nBeginIndex ? i_nEndIndex : i_nBeginIndex;
348 sal_uInt16 nEntryCount = GetEntryCount();
349 if( nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
351 // sanity check
352 if( nStop > nEntryCount-1 )
353 nStop = nEntryCount-1;
354 if( nStart > nEntryCount-1 )
355 nStart = nEntryCount-1;
357 sal_uInt16 nIndex = nStart;
358 while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
360 nHeight += GetEntryPtr( nIndex )-> mnHeight;
361 nIndex++;
364 else
365 nHeight = 0;
366 return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
369 // -----------------------------------------------------------------------
371 long ImplEntryList::GetEntryHeight( sal_uInt16 nPos ) const
373 ImplEntryType* pImplEntry = GetEntry( nPos );
374 return pImplEntry ? pImplEntry->mnHeight : 0;
377 // -----------------------------------------------------------------------
379 OUString ImplEntryList::GetEntryText( sal_uInt16 nPos ) const
381 OUString aEntryText;
382 ImplEntryType* pImplEntry = GetEntry( nPos );
383 if ( pImplEntry )
384 aEntryText = pImplEntry->maStr;
385 return aEntryText;
388 // -----------------------------------------------------------------------
390 sal_Bool ImplEntryList::HasEntryImage( sal_uInt16 nPos ) const
392 sal_Bool bImage = sal_False;
393 ImplEntryType* pImplEntry = GetEntry( nPos );
394 if ( pImplEntry )
395 bImage = !!pImplEntry->maImage;
396 return bImage;
399 // -----------------------------------------------------------------------
401 Image ImplEntryList::GetEntryImage( sal_uInt16 nPos ) const
403 Image aImage;
404 ImplEntryType* pImplEntry = GetEntry( nPos );
405 if ( pImplEntry )
406 aImage = pImplEntry->maImage;
407 return aImage;
410 // -----------------------------------------------------------------------
412 void ImplEntryList::SetEntryData( sal_uInt16 nPos, void* pNewData )
414 ImplEntryType* pImplEntry = GetEntry( nPos );
415 if ( pImplEntry )
416 pImplEntry->mpUserData = pNewData;
419 // -----------------------------------------------------------------------
421 void* ImplEntryList::GetEntryData( sal_uInt16 nPos ) const
423 ImplEntryType* pImplEntry = GetEntry( nPos );
424 return pImplEntry ? pImplEntry->mpUserData : NULL;
427 // -----------------------------------------------------------------------
429 void ImplEntryList::SetEntryFlags( sal_uInt16 nPos, long nFlags )
431 ImplEntryType* pImplEntry = GetEntry( nPos );
432 if ( pImplEntry )
433 pImplEntry->mnFlags = nFlags;
436 // -----------------------------------------------------------------------
438 long ImplEntryList::GetEntryFlags( sal_uInt16 nPos ) const
440 ImplEntryType* pImplEntry = GetEntry( nPos );
441 return pImplEntry ? pImplEntry->mnFlags : 0;
444 // -----------------------------------------------------------------------
446 sal_uInt16 ImplEntryList::GetSelectEntryCount() const
448 sal_uInt16 nSelCount = 0;
449 for ( sal_uInt16 n = GetEntryCount(); n; )
451 ImplEntryType* pImplEntry = GetEntry( --n );
452 if ( pImplEntry->mbIsSelected )
453 nSelCount++;
455 return nSelCount;
458 // -----------------------------------------------------------------------
460 OUString ImplEntryList::GetSelectEntry( sal_uInt16 nIndex ) const
462 return GetEntryText( GetSelectEntryPos( nIndex ) );
465 // -----------------------------------------------------------------------
467 sal_uInt16 ImplEntryList::GetSelectEntryPos( sal_uInt16 nIndex ) const
469 sal_uInt16 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
470 sal_uInt16 nSel = 0;
471 sal_uInt16 nEntryCount = GetEntryCount();
473 for ( sal_uInt16 n = 0; n < nEntryCount; n++ )
475 ImplEntryType* pImplEntry = GetEntry( n );
476 if ( pImplEntry->mbIsSelected )
478 if ( nSel == nIndex )
480 nSelEntryPos = n;
481 break;
483 nSel++;
487 return nSelEntryPos;
490 // -----------------------------------------------------------------------
492 sal_Bool ImplEntryList::IsEntryPosSelected( sal_uInt16 nIndex ) const
494 ImplEntryType* pImplEntry = GetEntry( nIndex );
495 return pImplEntry ? pImplEntry->mbIsSelected : sal_False;
498 // -----------------------------------------------------------------------
500 bool ImplEntryList::IsEntrySelectable( sal_uInt16 nPos ) const
502 ImplEntryType* pImplEntry = GetEntry( nPos );
503 return pImplEntry ? ((pImplEntry->mnFlags & LISTBOX_ENTRY_FLAG_DISABLE_SELECTION) == 0) : true;
506 // -----------------------------------------------------------------------
508 sal_uInt16 ImplEntryList::FindFirstSelectable( sal_uInt16 nPos, bool bForward /* = true */ )
510 if( IsEntrySelectable( nPos ) )
511 return nPos;
513 if( bForward )
515 for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
517 if( IsEntrySelectable( nPos ) )
518 return nPos;
521 else
523 while( nPos )
525 nPos--;
526 if( IsEntrySelectable( nPos ) )
527 return nPos;
531 return LISTBOX_ENTRY_NOTFOUND;
534 // =======================================================================
536 ImplListBoxWindow::ImplListBoxWindow( Window* pParent, WinBits nWinStyle ) :
537 Control( pParent, 0 ),
538 maQuickSelectionEngine( *this )
540 mpEntryList = new ImplEntryList( this );
542 mnTop = 0;
543 mnLeft = 0;
544 mnBorder = 1;
545 mnSelectModifier = 0;
546 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
547 mbTrack = false;
548 mbImgsDiffSz = false;
549 mbTravelSelect = false;
550 mbTrackingSelect = false;
551 mbSelectionChanged = false;
552 mbMouseMoveSelect = false;
553 mbMulti = false;
554 mbStackMode = false;
555 mbGrabFocus = false;
556 mbUserDrawEnabled = false;
557 mbInUserDraw = false;
558 mbReadOnly = false;
559 mbHasFocusRect = false;
560 mbRight = ( nWinStyle & WB_RIGHT );
561 mbCenter = ( nWinStyle & WB_CENTER );
562 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
563 mbSort = ( nWinStyle & WB_SORT );
564 mbEdgeBlending = false;
566 // pb: #106948# explicit mirroring for calc
567 mbMirroring = false;
569 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
570 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
571 mnSeparatorPos = LISTBOX_ENTRY_NOTFOUND;
572 meProminentType = PROMINENT_TOP;
574 SetLineColor();
575 SetTextFillColor();
576 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
578 ImplInitSettings( sal_True, sal_True, sal_True );
579 ImplCalcMetrics();
582 // -----------------------------------------------------------------------
584 ImplListBoxWindow::~ImplListBoxWindow()
586 delete mpEntryList;
589 // -----------------------------------------------------------------------
591 void ImplListBoxWindow::ImplInitSettings( sal_Bool bFont, sal_Bool bForeground, sal_Bool bBackground )
593 ImplInitFieldSettings( this, bFont, bForeground, bBackground );
596 // -----------------------------------------------------------------------
598 void ImplListBoxWindow::ImplCalcMetrics()
600 mnMaxWidth = 0;
601 mnMaxTxtWidth = 0;
602 mnMaxImgWidth = 0;
603 mnMaxImgTxtWidth= 0;
604 mnMaxImgHeight = 0;
606 mnTextHeight = (sal_uInt16)GetTextHeight();
607 mnMaxTxtHeight = mnTextHeight + mnBorder;
608 mnMaxHeight = mnMaxTxtHeight;
610 if ( maUserItemSize.Height() > mnMaxHeight )
611 mnMaxHeight = (sal_uInt16) maUserItemSize.Height();
612 if ( maUserItemSize.Width() > mnMaxWidth )
613 mnMaxWidth= (sal_uInt16) maUserItemSize.Width();
615 for ( sal_uInt16 n = mpEntryList->GetEntryCount(); n; )
617 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
618 ImplUpdateEntryMetrics( *pEntry );
621 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
623 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->mnHeight );
624 maFocusRect.SetSize( aSz );
628 // -----------------------------------------------------------------------
630 void ImplListBoxWindow::Clear()
632 mpEntryList->Clear();
634 mnMaxHeight = mnMaxTxtHeight;
635 mnMaxWidth = 0;
636 mnMaxTxtWidth = 0;
637 mnMaxImgTxtWidth= 0;
638 mnMaxImgWidth = 0;
639 mnMaxImgHeight = 0;
640 mnTop = 0;
641 mnLeft = 0;
642 mbImgsDiffSz = false;
643 ImplClearLayoutData();
645 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
646 maQuickSelectionEngine.Reset();
648 Invalidate();
651 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
653 ImplClearLayoutData();
654 maUserItemSize = rSz;
655 ImplCalcMetrics();
658 // -----------------------------------------------------------------------
660 struct ImplEntryMetrics
662 bool bText;
663 bool bImage;
664 long nEntryWidth;
665 long nEntryHeight;
666 long nTextWidth;
667 long nImgWidth;
668 long nImgHeight;
671 // -----------------------------------------------------------------------
673 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
675 ImplEntryMetrics aMetrics;
676 aMetrics.bText = !rEntry.maStr.isEmpty();
677 aMetrics.bImage = !!rEntry.maImage;
678 aMetrics.nEntryWidth = 0;
679 aMetrics.nEntryHeight = 0;
680 aMetrics.nTextWidth = 0;
681 aMetrics.nImgWidth = 0;
682 aMetrics.nImgHeight = 0;
684 if ( aMetrics.bText )
686 if( (rEntry.mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
688 // multiline case
689 Size aCurSize( PixelToLogic( GetSizePixel() ) );
690 // set the current size to a large number
691 // GetTextRect should shrink it to the actual size
692 aCurSize.Height() = 0x7fffff;
693 Rectangle aTextRect( Point( 0, 0 ), aCurSize );
694 aTextRect = GetTextRect( aTextRect, rEntry.maStr, TEXT_DRAW_WORDBREAK | TEXT_DRAW_MULTILINE );
695 aMetrics.nTextWidth = aTextRect.GetWidth();
696 if( aMetrics.nTextWidth > mnMaxTxtWidth )
697 mnMaxTxtWidth = aMetrics.nTextWidth;
698 aMetrics.nEntryWidth = mnMaxTxtWidth;
699 aMetrics.nEntryHeight = aTextRect.GetHeight() + mnBorder;
701 else
703 // normal single line case
704 aMetrics.nTextWidth = (sal_uInt16)GetTextWidth( rEntry.maStr );
705 if( aMetrics.nTextWidth > mnMaxTxtWidth )
706 mnMaxTxtWidth = aMetrics.nTextWidth;
707 aMetrics.nEntryWidth = mnMaxTxtWidth;
708 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
711 if ( aMetrics.bImage )
713 Size aImgSz = rEntry.maImage.GetSizePixel();
714 aMetrics.nImgWidth = (sal_uInt16) CalcZoom( aImgSz.Width() );
715 aMetrics.nImgHeight = (sal_uInt16) CalcZoom( aImgSz.Height() );
717 if( mnMaxImgWidth && ( aMetrics.nImgWidth != mnMaxImgWidth ) )
718 mbImgsDiffSz = true;
719 else if ( mnMaxImgHeight && ( aMetrics.nImgHeight != mnMaxImgHeight ) )
720 mbImgsDiffSz = true;
722 if( aMetrics.nImgWidth > mnMaxImgWidth )
723 mnMaxImgWidth = aMetrics.nImgWidth;
724 if( aMetrics.nImgHeight > mnMaxImgHeight )
725 mnMaxImgHeight = aMetrics.nImgHeight;
727 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
728 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
731 if ( IsUserDrawEnabled() || aMetrics.bImage )
733 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
734 if ( aMetrics.bText )
735 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
736 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
737 aMetrics.nEntryHeight );
740 if ( !aMetrics.bText && !aMetrics.bImage && !IsUserDrawEnabled() )
742 // entries which have no (aka an empty) text, and no image,
743 // and are not user-drawn, should be shown nonetheless
744 aMetrics.nEntryHeight = mnTextHeight + mnBorder;
747 if ( aMetrics.nEntryWidth > mnMaxWidth )
748 mnMaxWidth = aMetrics.nEntryWidth;
749 if ( aMetrics.nEntryHeight > mnMaxHeight )
750 mnMaxHeight = aMetrics.nEntryHeight;
752 rEntry.mnHeight = aMetrics.nEntryHeight;
755 // -----------------------------------------------------------------------
757 void ImplListBoxWindow::ImplCallSelect()
759 if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
761 // Insert the selected entry as MRU, if not already first MRU
762 sal_uInt16 nSelected = GetEntryList()->GetSelectEntryPos( 0 );
763 sal_uInt16 nMRUCount = GetEntryList()->GetMRUCount();
764 String aSelected = GetEntryList()->GetEntryText( nSelected );
765 sal_uInt16 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, sal_True );
766 if ( nFirstMatchingEntryPos || !nMRUCount )
768 sal_Bool bSelectNewEntry = sal_False;
769 if ( nFirstMatchingEntryPos < nMRUCount )
771 RemoveEntry( nFirstMatchingEntryPos );
772 nMRUCount--;
773 if ( nFirstMatchingEntryPos == nSelected )
774 bSelectNewEntry = sal_True;
776 else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
778 RemoveEntry( nMRUCount - 1 );
779 nMRUCount--;
782 ImplClearLayoutData();
784 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
785 pNewEntry->mbIsSelected = bSelectNewEntry;
786 GetEntryList()->InsertEntry( 0, pNewEntry, sal_False );
787 ImplUpdateEntryMetrics( *pNewEntry );
788 GetEntryList()->SetMRUCount( ++nMRUCount );
789 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
790 maMRUChangedHdl.Call( NULL );
794 maSelectHdl.Call( NULL );
795 mbSelectionChanged = false;
798 // -----------------------------------------------------------------------
800 sal_uInt16 ImplListBoxWindow::InsertEntry( sal_uInt16 nPos, ImplEntryType* pNewEntry )
802 ImplClearLayoutData();
803 sal_uInt16 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, mbSort );
805 if( (GetStyle() & WB_WORDBREAK) )
806 pNewEntry->mnFlags |= LISTBOX_ENTRY_FLAG_MULTILINE;
808 ImplUpdateEntryMetrics( *pNewEntry );
809 return nNewPos;
812 // -----------------------------------------------------------------------
814 void ImplListBoxWindow::RemoveEntry( sal_uInt16 nPos )
816 ImplClearLayoutData();
817 mpEntryList->RemoveEntry( nPos );
818 if( mnCurrentPos >= mpEntryList->GetEntryCount() )
819 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
820 ImplCalcMetrics();
823 // -----------------------------------------------------------------------
825 void ImplListBoxWindow::SetEntryFlags( sal_uInt16 nPos, long nFlags )
827 mpEntryList->SetEntryFlags( nPos, nFlags );
828 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
829 if( pEntry )
830 ImplUpdateEntryMetrics( *pEntry );
833 // -----------------------------------------------------------------------
835 void ImplListBoxWindow::ImplShowFocusRect()
837 if ( mbHasFocusRect )
838 HideFocus();
839 ShowFocus( maFocusRect );
840 mbHasFocusRect = true;
843 // -----------------------------------------------------------------------
845 void ImplListBoxWindow::ImplHideFocusRect()
847 if ( mbHasFocusRect )
849 HideFocus();
850 mbHasFocusRect = false;
855 // -----------------------------------------------------------------------
857 sal_uInt16 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
859 long nY = mnBorder;
861 sal_uInt16 nSelect = mnTop;
862 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
863 while( pEntry && rPoint.Y() > pEntry->mnHeight + nY )
865 nY += pEntry->mnHeight;
866 pEntry = mpEntryList->GetEntryPtr( ++nSelect );
868 if( pEntry == NULL )
869 nSelect = LISTBOX_ENTRY_NOTFOUND;
871 return nSelect;
874 // -----------------------------------------------------------------------
876 sal_Bool ImplListBoxWindow::IsVisible( sal_uInt16 i_nEntry ) const
878 sal_Bool bRet = sal_False;
880 if( i_nEntry >= mnTop )
882 if( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
883 PixelToLogic( GetSizePixel() ).Height() )
885 bRet = sal_True;
889 return bRet;
892 // -----------------------------------------------------------------------
894 sal_uInt16 ImplListBoxWindow::GetLastVisibleEntry() const
896 sal_uInt16 nPos = mnTop;
897 long nWindowHeight = GetSizePixel().Height();
898 sal_uInt16 nCount = mpEntryList->GetEntryCount();
899 long nDiff;
900 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
901 nPos++;
903 if( nDiff > nWindowHeight && nPos > mnTop )
904 nPos--;
906 if( nPos >= nCount )
907 nPos = nCount-1;
909 return nPos;
912 // -----------------------------------------------------------------------
914 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
916 mbMouseMoveSelect = false; // only till the first MouseButtonDown
917 maQuickSelectionEngine.Reset();
919 if ( !IsReadOnly() )
921 if( rMEvt.GetClicks() == 1 )
923 sal_uInt16 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
924 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
926 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
927 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
928 else
929 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
931 mnCurrentPos = nSelect;
932 mbTrackingSelect = true;
933 SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() );
934 mbTrackingSelect = false;
935 if ( mbGrabFocus )
936 GrabFocus();
938 StartTracking( STARTTRACK_SCROLLREPEAT );
941 if( rMEvt.GetClicks() == 2 )
943 maDoubleClickHdl.Call( this );
946 else // if ( mbGrabFocus )
948 GrabFocus();
952 // -----------------------------------------------------------------------
954 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
956 if ( rMEvt.IsLeaveWindow() )
958 if ( mbStackMode && IsMouseMoveSelect() && IsReallyVisible() )
960 if ( rMEvt.GetPosPixel().Y() < 0 )
962 DeselectAll();
963 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
964 SetTopEntry( 0 );
965 if ( mbStackMode ) // #87072#, #92323#
967 mbTravelSelect = true;
968 mnSelectModifier = rMEvt.GetModifier();
969 ImplCallSelect();
970 mbTravelSelect = false;
976 else if ( ( ( !mbMulti && IsMouseMoveSelect() ) || mbStackMode ) && mpEntryList->GetEntryCount() )
978 Point aPoint;
979 Rectangle aRect( aPoint, GetOutputSizePixel() );
980 if( aRect.IsInside( rMEvt.GetPosPixel() ) )
982 if ( IsMouseMoveSelect() )
984 sal_uInt16 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
985 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
986 nSelect = mpEntryList->GetEntryCount() - 1;
987 nSelect = std::min( nSelect, GetLastVisibleEntry() );
988 nSelect = std::min( nSelect, (sal_uInt16) ( mpEntryList->GetEntryCount() - 1 ) );
989 // Select only visible Entries with MouseMove, otherwise Tracking...
990 if ( IsVisible( nSelect ) &&
991 mpEntryList->IsEntrySelectable( nSelect ) &&
992 ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() || ( nSelect != GetEntryList()->GetSelectEntryPos( 0 ) ) ) )
994 mbTrackingSelect = true;
995 if ( SelectEntries( nSelect, LET_TRACKING, sal_False, sal_False ) )
997 if ( mbStackMode ) // #87072#
999 mbTravelSelect = true;
1000 mnSelectModifier = rMEvt.GetModifier();
1001 ImplCallSelect();
1002 mbTravelSelect = false;
1005 mbTrackingSelect = false;
1009 // if the DD button was pressed and someone moved into the ListBox
1010 // with the mouse button pressed...
1011 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
1013 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
1014 mnTrackingSaveSelection = GetEntryList()->GetSelectEntryPos( 0 );
1015 else
1016 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
1018 if ( mbStackMode && ( mpEntryList->GetSelectionAnchor() == LISTBOX_ENTRY_NOTFOUND ) )
1019 mpEntryList->SetSelectionAnchor( 0 );
1021 StartTracking( STARTTRACK_SCROLLREPEAT );
1027 // -----------------------------------------------------------------------
1029 void ImplListBoxWindow::DeselectAll()
1031 while ( GetEntryList()->GetSelectEntryCount() )
1033 sal_uInt16 nS = GetEntryList()->GetSelectEntryPos( 0 );
1034 SelectEntry( nS, sal_False );
1038 // -----------------------------------------------------------------------
1040 void ImplListBoxWindow::SelectEntry( sal_uInt16 nPos, sal_Bool bSelect )
1042 if( (mpEntryList->IsEntryPosSelected( nPos ) != bSelect) && mpEntryList->IsEntrySelectable( nPos ) )
1044 ImplHideFocusRect();
1045 if( bSelect )
1047 if( !mbMulti )
1049 // deselect the selected entry
1050 sal_uInt16 nDeselect = GetEntryList()->GetSelectEntryPos( 0 );
1051 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
1053 //SelectEntryPos( nDeselect, sal_False );
1054 GetEntryList()->SelectEntry( nDeselect, sal_False );
1055 if ( IsUpdateMode() && IsReallyVisible() )
1056 ImplPaint( nDeselect, sal_True );
1059 mpEntryList->SelectEntry( nPos, sal_True );
1060 mnCurrentPos = nPos;
1061 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
1063 ImplPaint( nPos );
1064 if ( !IsVisible( nPos ) )
1066 ImplClearLayoutData();
1067 sal_uInt16 nVisibleEntries = GetLastVisibleEntry()-mnTop;
1068 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
1070 Resize();
1071 ShowProminentEntry( nPos );
1073 else
1075 ShowProminentEntry( nPos );
1080 else
1082 mpEntryList->SelectEntry( nPos, sal_False );
1083 ImplPaint( nPos, sal_True );
1085 mbSelectionChanged = true;
1089 // -----------------------------------------------------------------------
1091 sal_Bool ImplListBoxWindow::SelectEntries( sal_uInt16 nSelect, LB_EVENT_TYPE eLET, sal_Bool bShift, sal_Bool bCtrl )
1093 bool bFocusChanged = false;
1094 sal_Bool bSelectionChanged = sal_False;
1096 if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
1098 // here (Single-ListBox) only one entry can be deselected
1099 if( !mbMulti )
1101 sal_uInt16 nDeselect = mpEntryList->GetSelectEntryPos( 0 );
1102 if( nSelect != nDeselect )
1104 SelectEntry( nSelect, sal_True );
1105 mpEntryList->SetLastSelected( nSelect );
1106 bFocusChanged = true;
1107 bSelectionChanged = sal_True;
1110 // MultiListBox without Modifier
1111 else if( mbSimpleMode && !bCtrl && !bShift )
1113 sal_uInt16 nEntryCount = mpEntryList->GetEntryCount();
1114 for ( sal_uInt16 nPos = 0; nPos < nEntryCount; nPos++ )
1116 sal_Bool bSelect = nPos == nSelect;
1117 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1119 SelectEntry( nPos, bSelect );
1120 bFocusChanged = true;
1121 bSelectionChanged = sal_True;
1124 mpEntryList->SetLastSelected( nSelect );
1125 mpEntryList->SetSelectionAnchor( nSelect );
1127 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1128 else if( ( !mbSimpleMode /* && !bShift */ ) || ( (mbSimpleMode && ( bCtrl || bShift )) || mbStackMode ) )
1130 // Space for selection change
1131 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1133 sal_Bool bSelect = ( mbStackMode && IsMouseMoveSelect() ) ? sal_True : !mpEntryList->IsEntryPosSelected( nSelect );
1134 if ( mbStackMode )
1136 sal_uInt16 n;
1137 if ( bSelect )
1139 // All entries before nSelect must be selected...
1140 for ( n = 0; n < nSelect; n++ )
1141 SelectEntry( n, sal_True );
1143 if ( !bSelect )
1145 for ( n = nSelect+1; n < mpEntryList->GetEntryCount(); n++ )
1146 SelectEntry( n, sal_False );
1149 SelectEntry( nSelect, bSelect );
1150 mpEntryList->SetLastSelected( nSelect );
1151 mpEntryList->SetSelectionAnchor( mbStackMode ? 0 : nSelect );
1152 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1153 mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1154 bFocusChanged = true;
1155 bSelectionChanged = sal_True;
1157 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1158 ( (bShift||mbStackMode) && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1160 mnCurrentPos = nSelect;
1161 bFocusChanged = true;
1163 sal_uInt16 nAnchor = mpEntryList->GetSelectionAnchor();
1164 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && ( mpEntryList->GetSelectEntryCount() || mbStackMode ) )
1166 nAnchor = mbStackMode ? 0 : mpEntryList->GetSelectEntryPos( mpEntryList->GetSelectEntryCount() - 1 );
1168 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1170 // All entries from achor to nSelect have to be selected
1171 sal_uInt16 nStart = std::min( nSelect, nAnchor );
1172 sal_uInt16 nEnd = std::max( nSelect, nAnchor );
1173 for ( sal_uInt16 n = nStart; n <= nEnd; n++ )
1175 if ( !mpEntryList->IsEntryPosSelected( n ) )
1177 SelectEntry( n, sal_True );
1178 bSelectionChanged = sal_True;
1182 // if appropriate some more has to be deselected...
1183 sal_uInt16 nLast = mpEntryList->GetLastSelected();
1184 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1186 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1188 for ( sal_uInt16 n = nSelect+1; n <= nLast; n++ )
1190 if ( mpEntryList->IsEntryPosSelected( n ) )
1192 SelectEntry( n, sal_False );
1193 bSelectionChanged = sal_True;
1197 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1199 for ( sal_uInt16 n = nLast; n < nSelect; n++ )
1201 if ( mpEntryList->IsEntryPosSelected( n ) )
1203 SelectEntry( n, sal_False );
1204 bSelectionChanged = sal_True;
1209 mpEntryList->SetLastSelected( nSelect );
1212 else if( eLET != LET_TRACKING )
1214 ImplHideFocusRect();
1215 ImplPaint( nSelect, sal_True );
1216 bFocusChanged = true;
1219 else if( bShift )
1221 bFocusChanged = true;
1224 if( bSelectionChanged )
1225 mbSelectionChanged = true;
1227 if( bFocusChanged )
1229 long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop, 0 );
1230 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1231 Size aSz( maFocusRect.GetWidth(),
1232 mpEntryList->GetEntryHeight( nSelect ) );
1233 maFocusRect.SetSize( aSz );
1234 if( HasFocus() )
1235 ImplShowFocusRect();
1237 ImplClearLayoutData();
1239 return bSelectionChanged;
1242 // -----------------------------------------------------------------------
1244 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1246 Point aPoint;
1247 Rectangle aRect( aPoint, GetOutputSizePixel() );
1248 sal_Bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1250 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1252 if ( bInside && !rTEvt.IsTrackingCanceled() )
1254 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1255 ImplCallSelect();
1257 else
1259 maCancelHdl.Call( NULL );
1260 if ( !mbMulti )
1262 mbTrackingSelect = true;
1263 SelectEntry( mnTrackingSaveSelection, sal_True );
1264 mbTrackingSelect = false;
1265 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1267 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1268 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1269 Size aSz( maFocusRect.GetWidth(),
1270 mpEntryList->GetEntryHeight( mnCurrentPos ) );
1271 maFocusRect.SetSize( aSz );
1272 ImplShowFocusRect();
1277 mbTrack = false;
1279 else
1281 sal_Bool bTrackOrQuickClick = mbTrack;
1282 if( !mbTrack )
1284 if ( bInside )
1286 mbTrack = true;
1289 // this case only happens, if the mouse button is pressed very briefly
1290 if( rTEvt.IsTrackingEnded() && mbTrack )
1292 bTrackOrQuickClick = sal_True;
1293 mbTrack = false;
1297 if( bTrackOrQuickClick )
1299 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1300 Point aPt( aMEvt.GetPosPixel() );
1301 sal_Bool bShift = aMEvt.IsShift();
1302 sal_Bool bCtrl = aMEvt.IsMod1();
1304 sal_uInt16 nSelect = LISTBOX_ENTRY_NOTFOUND;
1305 if( aPt.Y() < 0 )
1307 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1309 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1310 if( nSelect < mnTop )
1311 SetTopEntry( mnTop-1 );
1314 else if( aPt.Y() > GetOutputSizePixel().Height() )
1316 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1318 nSelect = std::min( (sal_uInt16)(mnCurrentPos+1), (sal_uInt16)(mpEntryList->GetEntryCount()-1) );
1319 if( nSelect >= GetLastVisibleEntry() )
1320 SetTopEntry( mnTop+1 );
1323 else
1325 nSelect = (sal_uInt16) ( ( aPt.Y() + mnBorder ) / mnMaxHeight ) + (sal_uInt16) mnTop;
1326 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1327 nSelect = std::min( nSelect, (sal_uInt16) ( mpEntryList->GetEntryCount() - 1 ) );
1330 if ( bInside )
1332 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectEntryCount() )
1334 mbTrackingSelect = true;
1335 if ( SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl ) )
1337 if ( mbStackMode ) // #87734# (#87072#)
1339 mbTravelSelect = true;
1340 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1341 ImplCallSelect();
1342 mbTravelSelect = false;
1345 mbTrackingSelect = false;
1348 else
1350 if ( !mbMulti && GetEntryList()->GetSelectEntryCount() )
1352 mbTrackingSelect = true;
1353 SelectEntry( GetEntryList()->GetSelectEntryPos( 0 ), sal_False );
1354 mbTrackingSelect = false;
1356 else if ( mbStackMode )
1358 if ( ( rTEvt.GetMouseEvent().GetPosPixel().X() > 0 ) && ( rTEvt.GetMouseEvent().GetPosPixel().X() < aRect.Right() ) )
1360 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 ) || ( rTEvt.GetMouseEvent().GetPosPixel().Y() > GetOutputSizePixel().Height() ) )
1362 sal_Bool bSelectionChanged = sal_False;
1363 if ( ( rTEvt.GetMouseEvent().GetPosPixel().Y() < 0 )
1364 && !mnCurrentPos )
1366 if ( mpEntryList->IsEntryPosSelected( 0 ) )
1368 SelectEntry( 0, sal_False );
1369 bSelectionChanged = sal_True;
1370 nSelect = LISTBOX_ENTRY_NOTFOUND;
1374 else
1376 mbTrackingSelect = true;
1377 bSelectionChanged = SelectEntries( nSelect, LET_TRACKING, bShift, bCtrl );
1378 mbTrackingSelect = false;
1381 if ( bSelectionChanged )
1383 mbSelectionChanged = true;
1384 mbTravelSelect = true;
1385 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1386 ImplCallSelect();
1387 mbTravelSelect = false;
1393 mnCurrentPos = nSelect;
1394 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1396 ImplHideFocusRect();
1398 else
1400 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1401 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1402 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1403 maFocusRect.SetSize( aSz );
1404 ImplShowFocusRect();
1411 // -----------------------------------------------------------------------
1413 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1415 if( !ProcessKeyInput( rKEvt ) )
1416 Control::KeyInput( rKEvt );
1419 // -----------------------------------------------------------------------
1421 sal_Bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1423 // entry to be selected
1424 sal_uInt16 nSelect = LISTBOX_ENTRY_NOTFOUND;
1425 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1427 KeyCode aKeyCode = rKEvt.GetKeyCode();
1429 sal_Bool bShift = aKeyCode.IsShift();
1430 sal_Bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1431 sal_Bool bMod2 = aKeyCode.IsMod2();
1432 sal_Bool bDone = sal_False;
1434 switch( aKeyCode.GetCode() )
1436 case KEY_UP:
1438 if ( IsReadOnly() )
1440 if ( GetTopEntry() )
1441 SetTopEntry( GetTopEntry()-1 );
1443 else if ( !bMod2 )
1445 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1447 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1449 else if ( mnCurrentPos )
1451 // search first selectable above the current position
1452 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1455 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1456 SetTopEntry( mnTop-1 );
1458 bDone = sal_True;
1460 maQuickSelectionEngine.Reset();
1462 break;
1464 case KEY_DOWN:
1466 if ( IsReadOnly() )
1468 SetTopEntry( GetTopEntry()+1 );
1470 else if ( !bMod2 )
1472 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1474 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1476 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1478 // search first selectable below the current position
1479 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1, true );
1482 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1483 SetTopEntry( mnTop+1 );
1485 bDone = sal_True;
1487 maQuickSelectionEngine.Reset();
1489 break;
1491 case KEY_PAGEUP:
1493 if ( IsReadOnly() )
1495 sal_uInt16 nCurVis = GetLastVisibleEntry() - mnTop +1;
1496 SetTopEntry( ( mnTop > nCurVis ) ?
1497 (mnTop-nCurVis) : 0 );
1499 else if ( !bCtrl && !bMod2 )
1501 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1503 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1505 else if ( mnCurrentPos )
1507 if( mnCurrentPos == mnTop )
1509 sal_uInt16 nCurVis = GetLastVisibleEntry() - mnTop +1;
1510 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1513 // find first selectable starting from mnTop looking foreward
1514 nSelect = mpEntryList->FindFirstSelectable( mnTop, true );
1516 bDone = sal_True;
1518 maQuickSelectionEngine.Reset();
1520 break;
1522 case KEY_PAGEDOWN:
1524 if ( IsReadOnly() )
1526 SetTopEntry( GetLastVisibleEntry() );
1528 else if ( !bCtrl && !bMod2 )
1530 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1532 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1534 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1536 sal_uInt16 nCount = mpEntryList->GetEntryCount();
1537 sal_uInt16 nCurVis = GetLastVisibleEntry() - mnTop;
1538 sal_uInt16 nTmp = std::min( nCurVis, nCount );
1539 nTmp += mnTop - 1;
1540 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1542 long nTmp2 = std::min( (long)(nCount-nCurVis), (long)((long)mnTop+(long)nCurVis-1) );
1543 nTmp2 = std::max( (long)0 , nTmp2 );
1544 nTmp = (sal_uInt16)(nTmp2+(nCurVis-1) );
1545 SetTopEntry( (sal_uInt16)nTmp2 );
1547 // find first selectable starting from nTmp looking backwards
1548 nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1550 bDone = sal_True;
1552 maQuickSelectionEngine.Reset();
1554 break;
1556 case KEY_HOME:
1558 if ( IsReadOnly() )
1560 SetTopEntry( 0 );
1562 else if ( !bCtrl && !bMod2 )
1564 if ( mnCurrentPos )
1566 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND, true );
1567 if( mnTop != 0 )
1568 SetTopEntry( 0 );
1570 bDone = sal_True;
1573 maQuickSelectionEngine.Reset();
1575 break;
1577 case KEY_END:
1579 if ( IsReadOnly() )
1581 SetTopEntry( 0xFFFF );
1583 else if ( !bCtrl && !bMod2 )
1585 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1587 nSelect = mpEntryList->FindFirstSelectable( 0, true );
1589 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1591 sal_uInt16 nCount = mpEntryList->GetEntryCount();
1592 nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1593 sal_uInt16 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1594 if( nCount > nCurVis )
1595 SetTopEntry( nCount - nCurVis );
1597 bDone = sal_True;
1599 maQuickSelectionEngine.Reset();
1601 break;
1603 case KEY_LEFT:
1605 if ( !bCtrl && !bMod2 )
1607 ScrollHorz( -HORZ_SCROLL );
1608 bDone = sal_True;
1610 maQuickSelectionEngine.Reset();
1612 break;
1614 case KEY_RIGHT:
1616 if ( !bCtrl && !bMod2 )
1618 ScrollHorz( HORZ_SCROLL );
1619 bDone = sal_True;
1621 maQuickSelectionEngine.Reset();
1623 break;
1625 case KEY_RETURN:
1627 if ( !bMod2 && !IsReadOnly() )
1629 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1630 ImplCallSelect();
1631 bDone = sal_False; // do not catch RETURN
1633 maQuickSelectionEngine.Reset();
1635 break;
1637 case KEY_SPACE:
1639 if ( !bMod2 && !IsReadOnly() )
1641 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) || mbStackMode ) )
1643 nSelect = mnCurrentPos;
1644 eLET = LET_KEYSPACE;
1646 bDone = sal_True;
1648 maQuickSelectionEngine.Reset();
1650 break;
1652 case KEY_A:
1654 if( bCtrl && mbMulti )
1656 // paint only once
1657 sal_Bool bUpdates = IsUpdateMode();
1658 SetUpdateMode( sal_False );
1660 sal_uInt16 nEntryCount = mpEntryList->GetEntryCount();
1661 for( sal_uInt16 i = 0; i < nEntryCount; i++ )
1662 SelectEntry( i, sal_True );
1664 // restore update mode
1665 SetUpdateMode( bUpdates );
1666 Invalidate();
1668 maQuickSelectionEngine.Reset();
1670 bDone = sal_True;
1671 break;
1674 // fall through intentional
1675 default:
1677 if ( !IsReadOnly() )
1679 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1682 break;
1685 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1686 && ( ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1687 || ( eLET == LET_KEYSPACE )
1691 DBG_ASSERT( !mpEntryList->IsEntryPosSelected( nSelect ) || mbMulti, "ImplListBox: Selecting same Entry" );
1692 if( nSelect >= mpEntryList->GetEntryCount() )
1693 nSelect = mpEntryList->GetEntryCount()-1;
1694 mnCurrentPos = nSelect;
1695 if ( SelectEntries( nSelect, eLET, bShift, bCtrl ) )
1697 mbTravelSelect = true;
1698 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1699 ImplCallSelect();
1700 mbTravelSelect = false;
1704 return bDone;
1707 // -----------------------------------------------------------------------
1708 namespace
1710 static ::vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_uInt16 _nPos, String& _out_entryText )
1712 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1713 sal_uInt16 nEntryCount( _rList.GetEntryCount() );
1714 if ( _nPos >= nEntryCount )
1715 _nPos = 0;
1716 _out_entryText = _rList.GetEntryText( _nPos );
1718 // ::vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1719 // => normalize
1720 return reinterpret_cast< ::vcl::StringEntryIdentifier >( _nPos + 1 );
1723 static sal_uInt16 lcl_getEntryPos( ::vcl::StringEntryIdentifier _entry )
1725 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1726 return static_cast< sal_uInt16 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1730 // -----------------------------------------------------------------------
1731 ::vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( String& _out_entryText ) const
1733 return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos + 1, _out_entryText );
1736 // -----------------------------------------------------------------------
1737 ::vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( ::vcl::StringEntryIdentifier _currentEntry, String& _out_entryText ) const
1739 sal_uInt16 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1740 return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1743 // -----------------------------------------------------------------------
1744 void ImplListBoxWindow::SelectEntry( ::vcl::StringEntryIdentifier _entry )
1746 sal_uInt16 nSelect = lcl_getEntryPos( _entry );
1747 if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1749 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1750 // to select the given entry by typing its starting letters. No need to act.
1751 return;
1754 // normalize
1755 OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1756 if( nSelect >= mpEntryList->GetEntryCount() )
1757 nSelect = mpEntryList->GetEntryCount()-1;
1759 // make visible
1760 ShowProminentEntry( nSelect );
1762 // actually select
1763 mnCurrentPos = nSelect;
1764 if ( SelectEntries( nSelect, LET_KEYMOVE, sal_False, sal_False ) )
1766 mbTravelSelect = true;
1767 mnSelectModifier = 0;
1768 ImplCallSelect();
1769 mbTravelSelect = false;
1773 // -----------------------------------------------------------------------
1775 void ImplListBoxWindow::ImplPaint( sal_uInt16 nPos, sal_Bool bErase, bool bLayout )
1777 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1779 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1780 if( ! pEntry )
1781 return;
1783 long nWidth = GetOutputSizePixel().Width();
1784 long nY = mpEntryList->GetAddedHeight( nPos, mnTop );
1785 Rectangle aRect( Point( 0, nY ), Size( nWidth, pEntry->mnHeight ) );
1787 if( ! bLayout )
1789 if( mpEntryList->IsEntryPosSelected( nPos ) )
1791 SetTextColor( !IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor() );
1792 SetFillColor( rStyleSettings.GetHighlightColor() );
1793 SetTextFillColor( rStyleSettings.GetHighlightColor() );
1794 DrawRect( aRect );
1796 else
1798 ImplInitSettings( sal_False, sal_True, sal_False );
1799 if( !IsEnabled() )
1800 SetTextColor( rStyleSettings.GetDisableColor() );
1801 SetTextFillColor();
1802 if( bErase )
1803 Erase( aRect );
1807 if ( IsUserDrawEnabled() )
1809 mbInUserDraw = true;
1810 mnUserDrawEntry = nPos;
1811 aRect.Left() -= mnLeft;
1812 if ( nPos < GetEntryList()->GetMRUCount() )
1813 nPos = GetEntryList()->FindEntry( GetEntryList()->GetEntryText( nPos ) );
1814 nPos = sal::static_int_cast<sal_uInt16>(nPos - GetEntryList()->GetMRUCount());
1815 sal_uInt16 nCurr = mnCurrentPos;
1816 if ( mnCurrentPos < GetEntryList()->GetMRUCount() )
1817 nCurr = GetEntryList()->FindEntry( GetEntryList()->GetEntryText( nCurr ) );
1818 nCurr = sal::static_int_cast<sal_uInt16>( nCurr - GetEntryList()->GetMRUCount());
1820 UserDrawEvent aUDEvt( this, aRect, nPos, nCurr );
1821 maUserDrawHdl.Call( &aUDEvt );
1822 mbInUserDraw = false;
1824 else
1826 DrawEntry( nPos, sal_True, sal_True, sal_False, bLayout );
1830 // -----------------------------------------------------------------------
1832 void ImplListBoxWindow::DrawEntry( sal_uInt16 nPos, sal_Bool bDrawImage, sal_Bool bDrawText, sal_Bool bDrawTextAtImagePos, bool bLayout )
1834 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1835 if( ! pEntry )
1836 return;
1838 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1840 if ( mbInUserDraw )
1841 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1843 long nY = mpEntryList->GetAddedHeight( nPos, mnTop );
1844 Size aImgSz;
1846 if( bDrawImage && mpEntryList->HasImages() && !bLayout )
1848 Image aImage = mpEntryList->GetEntryImage( nPos );
1849 if( !!aImage )
1851 aImgSz = aImage.GetSizePixel();
1852 Point aPtImg( mnBorder - mnLeft, nY + ( ( pEntry->mnHeight - aImgSz.Height() ) / 2 ) );
1854 // pb: #106948# explicit mirroring for calc
1855 if ( mbMirroring )
1856 // right aligned
1857 aPtImg.X() = mnMaxWidth + mnBorder - aImgSz.Width() - mnLeft;
1859 if ( !IsZoom() )
1861 DrawImage( aPtImg, aImage );
1863 else
1865 aImgSz.Width() = CalcZoom( aImgSz.Width() );
1866 aImgSz.Height() = CalcZoom( aImgSz.Height() );
1867 DrawImage( aPtImg, aImgSz, aImage );
1870 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1871 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1873 if(nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1875 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1876 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1877 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1878 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1880 if(!aBlendFrame.IsEmpty())
1882 DrawBitmapEx(aPtImg, aBlendFrame);
1888 if( bDrawText )
1890 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL;
1891 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL;
1892 XubString aStr( mpEntryList->GetEntryText( nPos ) );
1893 if ( aStr.Len() )
1895 long nMaxWidth = std::max( static_cast< long >( mnMaxWidth ),
1896 GetOutputSizePixel().Width() - 2*mnBorder );
1897 // a multiline entry should only be as wide a the window
1898 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
1899 nMaxWidth = GetOutputSizePixel().Width() - 2*mnBorder;
1901 Rectangle aTextRect( Point( mnBorder - mnLeft, nY ),
1902 Size( nMaxWidth, pEntry->mnHeight ) );
1904 if( !bDrawTextAtImagePos && ( mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled() ) )
1906 long nImageWidth = std::max( mnMaxImgWidth, maUserItemSize.Width() );
1907 aTextRect.Left() += nImageWidth + IMG_TXT_DISTANCE;
1910 if( bLayout )
1911 mpControlData->mpLayoutData->m_aLineIndices.push_back( mpControlData->mpLayoutData->m_aDisplayText.getLength() );
1913 // pb: #106948# explicit mirroring for calc
1914 if ( mbMirroring )
1916 // right aligned
1917 aTextRect.Left() = nMaxWidth + mnBorder - GetTextWidth( aStr ) - mnLeft;
1918 if ( aImgSz.Width() > 0 )
1919 aTextRect.Left() -= ( aImgSz.Width() + IMG_TXT_DISTANCE );
1922 sal_uInt16 nDrawStyle = ImplGetTextStyle();
1923 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_MULTILINE) )
1924 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1925 if( (pEntry->mnFlags & LISTBOX_ENTRY_FLAG_DRAW_DISABLED) )
1926 nDrawStyle |= TEXT_DRAW_DISABLE;
1928 DrawText( aTextRect, aStr, nDrawStyle, pVector, pDisplayText );
1932 if( !bLayout )
1934 if ( ( mnSeparatorPos != LISTBOX_ENTRY_NOTFOUND ) &&
1935 ( ( nPos == mnSeparatorPos ) || ( nPos == mnSeparatorPos+1 ) ) )
1937 Color aOldLineColor( GetLineColor() );
1938 SetLineColor( ( GetBackground().GetColor() != COL_LIGHTGRAY ) ? COL_LIGHTGRAY : COL_GRAY );
1939 Point aStartPos( 0, nY );
1940 if ( nPos == mnSeparatorPos )
1941 aStartPos.Y() += pEntry->mnHeight-1;
1942 Point aEndPos( aStartPos );
1943 aEndPos.X() = GetOutputSizePixel().Width();
1944 DrawLine( aStartPos, aEndPos );
1945 SetLineColor( aOldLineColor );
1950 // -----------------------------------------------------------------------
1952 void ImplListBoxWindow::FillLayoutData() const
1954 mpControlData->mpLayoutData = new vcl::ControlLayoutData();
1955 const_cast<ImplListBoxWindow*>(this)->
1956 ImplDoPaint( Rectangle( Point( 0, 0 ), GetOutputSize() ), true );
1959 // -----------------------------------------------------------------------
1961 void ImplListBoxWindow::ImplDoPaint( const Rectangle& rRect, bool bLayout )
1963 sal_uInt16 nCount = mpEntryList->GetEntryCount();
1965 sal_Bool bShowFocusRect = mbHasFocusRect;
1966 if ( mbHasFocusRect && ! bLayout )
1967 ImplHideFocusRect();
1969 long nY = 0; // + mnBorder;
1970 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
1972 for( sal_uInt16 i = (sal_uInt16)mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++ )
1974 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( i );
1975 if( nY + pEntry->mnHeight >= rRect.Top() &&
1976 nY <= rRect.Bottom() + mnMaxHeight )
1978 ImplPaint( i, sal_False, bLayout );
1980 nY += pEntry->mnHeight;
1983 long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop, 0 );
1984 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1985 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1986 maFocusRect.SetSize( aSz );
1987 if( HasFocus() && bShowFocusRect && !bLayout )
1988 ImplShowFocusRect();
1991 // -----------------------------------------------------------------------
1993 void ImplListBoxWindow::Paint( const Rectangle& rRect )
1995 ImplDoPaint( rRect );
1998 // -----------------------------------------------------------------------
2000 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
2002 // FIXME: LISTBOX_ENTRY_FLAG_MULTILINE
2004 sal_uInt16 nCount = mpEntryList->GetEntryCount();
2005 long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + mnBorder;
2006 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
2007 if( nEntries > nCount-mnTop )
2008 nEntries = nCount-mnTop;
2010 return nEntries;
2013 // -----------------------------------------------------------------------
2015 void ImplListBoxWindow::Resize()
2017 Control::Resize();
2019 sal_Bool bShowFocusRect = mbHasFocusRect;
2020 if ( bShowFocusRect )
2021 ImplHideFocusRect();
2023 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
2025 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
2026 maFocusRect.SetSize( aSz );
2029 if ( bShowFocusRect )
2030 ImplShowFocusRect();
2032 ImplClearLayoutData();
2035 // -----------------------------------------------------------------------
2037 void ImplListBoxWindow::GetFocus()
2039 sal_uInt16 nPos = mnCurrentPos;
2040 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
2041 nPos = 0;
2042 long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop, 0 );
2043 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
2044 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
2045 maFocusRect.SetSize( aSz );
2046 ImplShowFocusRect();
2047 Control::GetFocus();
2050 // -----------------------------------------------------------------------
2052 void ImplListBoxWindow::LoseFocus()
2054 ImplHideFocusRect();
2055 Control::LoseFocus();
2058 // -----------------------------------------------------------------------
2060 void ImplListBoxWindow::SetTopEntry( sal_uInt16 nTop )
2062 if( mpEntryList->GetEntryCount() == 0 )
2063 return;
2065 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
2067 sal_uInt16 nLastEntry = mpEntryList->GetEntryCount()-1;
2068 if( nTop > nLastEntry )
2069 nTop = nLastEntry;
2070 const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
2071 while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->mnHeight <= nWHeight )
2072 nTop--;
2074 if ( nTop != mnTop )
2076 ImplClearLayoutData();
2077 long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop, 0 );
2078 Update();
2079 ImplHideFocusRect();
2080 mnTop = nTop;
2081 Scroll( 0, nDiff );
2082 Update();
2083 if( HasFocus() )
2084 ImplShowFocusRect();
2085 maScrollHdl.Call( this );
2089 // -----------------------------------------------------------------------
2091 void ImplListBoxWindow::ShowProminentEntry( sal_uInt16 nEntryPos )
2093 if( meProminentType == PROMINENT_MIDDLE )
2095 sal_uInt16 nPos = nEntryPos;
2096 long nWHeight = PixelToLogic( GetSizePixel() ).Height();
2097 while( nEntryPos > 0 && mpEntryList->GetAddedHeight( nPos+1, nEntryPos ) < nWHeight/2 )
2098 nEntryPos--;
2100 SetTopEntry( nEntryPos );
2103 // -----------------------------------------------------------------------
2105 void ImplListBoxWindow::SetLeftIndent( long n )
2107 ScrollHorz( n - mnLeft );
2110 // -----------------------------------------------------------------------
2112 void ImplListBoxWindow::ScrollHorz( long n )
2114 long nDiff = 0;
2115 if ( n > 0 )
2117 long nWidth = GetOutputSizePixel().Width();
2118 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
2119 nDiff = n;
2121 else if ( n < 0 )
2123 if( mnLeft )
2125 long nAbs = -n;
2126 nDiff = - ( ( mnLeft > nAbs ) ? nAbs : mnLeft );
2130 if ( nDiff )
2132 ImplClearLayoutData();
2133 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
2134 Update();
2135 ImplHideFocusRect();
2136 Scroll( -nDiff, 0 );
2137 Update();
2138 if( HasFocus() )
2139 ImplShowFocusRect();
2140 maScrollHdl.Call( this );
2144 // -----------------------------------------------------------------------
2146 Size ImplListBoxWindow::CalcSize( sal_uInt16 nMaxLines ) const
2148 // FIXME: LISTBOX_ENTRY_FLAG_MULTILINE
2150 Size aSz;
2151 // sal_uInt16 nL = Min( nMaxLines, mpEntryList->GetEntryCount() );
2152 aSz.Height() = nMaxLines * mnMaxHeight;
2153 aSz.Width() = mnMaxWidth + 2*mnBorder;
2154 return aSz;
2157 // -----------------------------------------------------------------------
2159 Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_uInt16 nItem ) const
2161 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
2162 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->mnHeight : GetEntryHeight() );
2163 long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) - mpEntryList->GetAddedHeight( GetTopEntry() );
2164 Rectangle aRect( Point( 0, nY ), aSz );
2165 return aRect;
2169 // -----------------------------------------------------------------------
2171 void ImplListBoxWindow::StateChanged( StateChangedType nType )
2173 Control::StateChanged( nType );
2175 if ( nType == STATE_CHANGE_ZOOM )
2177 ImplInitSettings( sal_True, sal_False, sal_False );
2178 ImplCalcMetrics();
2179 Invalidate();
2181 else if ( nType == STATE_CHANGE_UPDATEMODE )
2183 if ( IsUpdateMode() && IsReallyVisible() )
2184 Invalidate();
2186 else if ( nType == STATE_CHANGE_CONTROLFONT )
2188 ImplInitSettings( sal_True, sal_False, sal_False );
2189 ImplCalcMetrics();
2190 Invalidate();
2192 else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
2194 ImplInitSettings( sal_False, sal_True, sal_False );
2195 Invalidate();
2197 else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
2199 ImplInitSettings( sal_False, sal_False, sal_True );
2200 Invalidate();
2202 ImplClearLayoutData();
2205 // -----------------------------------------------------------------------
2207 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
2209 Control::DataChanged( rDCEvt );
2211 if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
2212 (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
2213 ((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
2214 (rDCEvt.GetFlags() & SETTINGS_STYLE)) )
2216 ImplClearLayoutData();
2217 ImplInitSettings( sal_True, sal_True, sal_True );
2218 ImplCalcMetrics();
2219 Invalidate();
2223 // -----------------------------------------------------------------------
2225 sal_uInt16 ImplListBoxWindow::ImplGetTextStyle() const
2227 sal_uInt16 nTextStyle = TEXT_DRAW_VCENTER;
2229 if ( mpEntryList->HasImages() )
2230 nTextStyle |= TEXT_DRAW_LEFT;
2231 else if ( mbCenter )
2232 nTextStyle |= TEXT_DRAW_CENTER;
2233 else if ( mbRight )
2234 nTextStyle |= TEXT_DRAW_RIGHT;
2235 else
2236 nTextStyle |= TEXT_DRAW_LEFT;
2238 return nTextStyle;
2241 // =======================================================================
2243 ImplListBox::ImplListBox( Window* pParent, WinBits nWinStyle ) :
2244 Control( pParent, nWinStyle ),
2245 maLBWindow( this, nWinStyle&(~WB_BORDER) )
2247 // for native widget rendering we must be able to detect this window type
2248 SetType( WINDOW_LISTBOXWINDOW );
2250 mpVScrollBar = new ScrollBar( this, WB_VSCROLL | WB_DRAG );
2251 mpHScrollBar = new ScrollBar( this, WB_HSCROLL | WB_DRAG );
2252 mpScrollBarBox = new ScrollBarBox( this );
2254 Link aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2255 mpVScrollBar->SetScrollHdl( aLink );
2256 mpHScrollBar->SetScrollHdl( aLink );
2258 mbVScroll = false;
2259 mbHScroll = false;
2260 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2261 mbEdgeBlending = false;
2263 maLBWindow.SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2264 maLBWindow.SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2265 maLBWindow.SetEdgeBlending(GetEdgeBlending());
2266 maLBWindow.Show();
2269 // -----------------------------------------------------------------------
2271 ImplListBox::~ImplListBox()
2273 delete mpHScrollBar;
2274 delete mpVScrollBar;
2275 delete mpScrollBarBox;
2278 // -----------------------------------------------------------------------
2280 void ImplListBox::Clear()
2282 maLBWindow.Clear();
2283 if ( GetEntryList()->GetMRUCount() )
2285 maLBWindow.GetEntryList()->SetMRUCount( 0 );
2286 maLBWindow.SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2288 mpVScrollBar->SetThumbPos( 0 );
2289 mpHScrollBar->SetThumbPos( 0 );
2290 StateChanged( STATE_CHANGE_DATA );
2293 // -----------------------------------------------------------------------
2295 sal_uInt16 ImplListBox::InsertEntry( sal_uInt16 nPos, const OUString& rStr )
2297 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2298 sal_uInt16 nNewPos = maLBWindow.InsertEntry( nPos, pNewEntry );
2299 StateChanged( STATE_CHANGE_DATA );
2300 return nNewPos;
2303 // -----------------------------------------------------------------------
2305 sal_uInt16 ImplListBox::InsertEntry( sal_uInt16 nPos, const Image& rImage )
2307 ImplEntryType* pNewEntry = new ImplEntryType( rImage );
2308 sal_uInt16 nNewPos = maLBWindow.InsertEntry( nPos, pNewEntry );
2309 StateChanged( STATE_CHANGE_DATA );
2310 return nNewPos;
2313 // -----------------------------------------------------------------------
2315 sal_uInt16 ImplListBox::InsertEntry( sal_uInt16 nPos, const OUString& rStr, const Image& rImage )
2317 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2318 sal_uInt16 nNewPos = maLBWindow.InsertEntry( nPos, pNewEntry );
2319 StateChanged( STATE_CHANGE_DATA );
2320 return nNewPos;
2323 // -----------------------------------------------------------------------
2325 void ImplListBox::RemoveEntry( sal_uInt16 nPos )
2327 maLBWindow.RemoveEntry( nPos );
2328 StateChanged( STATE_CHANGE_DATA );
2331 // -----------------------------------------------------------------------
2333 void ImplListBox::SetEntryFlags( sal_uInt16 nPos, long nFlags )
2335 maLBWindow.SetEntryFlags( nPos, nFlags );
2338 // -----------------------------------------------------------------------
2340 void ImplListBox::SelectEntry( sal_uInt16 nPos, sal_Bool bSelect )
2342 maLBWindow.SelectEntry( nPos, bSelect );
2345 // -----------------------------------------------------------------------
2347 void ImplListBox::SetNoSelection()
2349 maLBWindow.DeselectAll();
2352 // -----------------------------------------------------------------------
2354 void ImplListBox::GetFocus()
2356 maLBWindow.GrabFocus();
2359 // -----------------------------------------------------------------------
2361 Window* ImplListBox::GetPreferredKeyInputWindow()
2363 return &maLBWindow;
2366 // -----------------------------------------------------------------------
2368 void ImplListBox::Resize()
2370 Control::Resize();
2371 ImplResizeControls();
2372 ImplCheckScrollBars();
2376 // -----------------------------------------------------------------------
2378 IMPL_LINK_NOARG(ImplListBox, MRUChanged)
2380 StateChanged( STATE_CHANGE_DATA );
2381 return 1;
2384 // -----------------------------------------------------------------------
2386 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled)
2388 long nSet = GetTopEntry();
2389 if( nSet > mpVScrollBar->GetRangeMax() )
2390 mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2391 mpVScrollBar->SetThumbPos( GetTopEntry() );
2393 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2395 maScrollHdl.Call( this );
2397 return 1;
2400 // -----------------------------------------------------------------------
2402 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB )
2404 sal_uInt16 nPos = (sal_uInt16) pSB->GetThumbPos();
2405 if( pSB == mpVScrollBar )
2406 SetTopEntry( nPos );
2407 else if( pSB == mpHScrollBar )
2408 SetLeftIndent( nPos );
2410 return 1;
2413 // -----------------------------------------------------------------------
2415 void ImplListBox::ImplCheckScrollBars()
2417 bool bArrange = false;
2419 Size aOutSz = GetOutputSizePixel();
2420 sal_uInt16 nEntries = GetEntryList()->GetEntryCount();
2421 sal_uInt16 nMaxVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2423 // vertical ScrollBar
2424 if( nEntries > nMaxVisEntries )
2426 if( !mbVScroll )
2427 bArrange = true;
2428 mbVScroll = true;
2430 // check of the scrolled-out region
2431 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2432 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2433 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2434 else
2435 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2437 else
2439 if( mbVScroll )
2440 bArrange = true;
2441 mbVScroll = false;
2442 SetTopEntry( 0 );
2445 // horizontal ScrollBar
2446 if( mbAutoHScroll )
2448 long nWidth = (sal_uInt16) aOutSz.Width();
2449 if ( mbVScroll )
2450 nWidth -= mpVScrollBar->GetSizePixel().Width();
2452 long nMaxWidth = GetMaxEntryWidth();
2453 if( nWidth < nMaxWidth )
2455 if( !mbHScroll )
2456 bArrange = true;
2457 mbHScroll = true;
2459 if ( !mbVScroll ) // maybe we do need one now
2461 nMaxVisEntries = (sal_uInt16) ( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeight() );
2462 if( nEntries > nMaxVisEntries )
2464 bArrange = true;
2465 mbVScroll = true;
2467 // check of the scrolled-out region
2468 if( GetEntryList()->GetSelectEntryCount() == 1 &&
2469 GetEntryList()->GetSelectEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2470 ShowProminentEntry( GetEntryList()->GetSelectEntryPos( 0 ) );
2471 else
2472 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2476 // check of the scrolled-out region
2477 sal_uInt16 nMaxLI = (sal_uInt16) (nMaxWidth - nWidth);
2478 if ( nMaxLI < GetLeftIndent() )
2479 SetLeftIndent( nMaxLI );
2481 else
2483 if( mbHScroll )
2484 bArrange = true;
2485 mbHScroll = false;
2486 SetLeftIndent( 0 );
2490 if( bArrange )
2491 ImplResizeControls();
2493 ImplInitScrollBars();
2496 // -----------------------------------------------------------------------
2498 void ImplListBox::ImplInitScrollBars()
2500 Size aOutSz = maLBWindow.GetOutputSizePixel();
2502 if ( mbVScroll )
2504 sal_uInt16 nEntries = GetEntryList()->GetEntryCount();
2505 sal_uInt16 nVisEntries = (sal_uInt16) (aOutSz.Height() / GetEntryHeight());
2506 mpVScrollBar->SetRangeMax( nEntries );
2507 mpVScrollBar->SetVisibleSize( nVisEntries );
2508 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2511 if ( mbHScroll )
2513 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2514 mpHScrollBar->SetVisibleSize( (sal_uInt16)aOutSz.Width() );
2515 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2516 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2520 // -----------------------------------------------------------------------
2522 void ImplListBox::ImplResizeControls()
2524 // Here we only position the Controls; if the Scrollbars are to be
2525 // visible is already determined in ImplCheckScrollBars
2527 Size aOutSz = GetOutputSizePixel();
2528 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2529 nSBWidth = CalcZoom( nSBWidth );
2531 Size aInnerSz( aOutSz );
2532 if ( mbVScroll )
2533 aInnerSz.Width() -= nSBWidth;
2534 if ( mbHScroll )
2535 aInnerSz.Height() -= nSBWidth;
2537 // pb: #106948# explicit mirroring for calc
2538 // Scrollbar on left or right side?
2539 sal_Bool bMirroring = maLBWindow.IsMirroring();
2540 Point aWinPos( bMirroring && mbVScroll ? nSBWidth : 0, 0 );
2541 maLBWindow.SetPosSizePixel( aWinPos, aInnerSz );
2543 // ScrollBarBox
2544 if( mbVScroll && mbHScroll )
2546 Point aBoxPos( bMirroring ? 0 : aInnerSz.Width(), aInnerSz.Height() );
2547 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2548 mpScrollBarBox->Show();
2550 else
2552 mpScrollBarBox->Hide();
2555 // vertical ScrollBar
2556 if( mbVScroll )
2558 // Scrollbar on left or right side?
2559 Point aVPos( bMirroring ? 0 : aOutSz.Width() - nSBWidth, 0 );
2560 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2561 mpVScrollBar->Show();
2563 else
2565 mpVScrollBar->Hide();
2566 // #107254# Don't reset top entry after resize, but check for max top entry
2567 SetTopEntry( GetTopEntry() );
2570 // horizontal ScrollBar
2571 if( mbHScroll )
2573 Point aHPos( ( bMirroring && mbVScroll ) ? nSBWidth : 0, aOutSz.Height() - nSBWidth );
2574 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2575 mpHScrollBar->Show();
2577 else
2579 mpHScrollBar->Hide();
2580 SetLeftIndent( 0 );
2584 // -----------------------------------------------------------------------
2586 void ImplListBox::StateChanged( StateChangedType nType )
2588 if ( nType == STATE_CHANGE_INITSHOW )
2590 ImplCheckScrollBars();
2592 else if ( ( nType == STATE_CHANGE_UPDATEMODE ) || ( nType == STATE_CHANGE_DATA ) )
2594 sal_Bool bUpdate = IsUpdateMode();
2595 maLBWindow.SetUpdateMode( bUpdate );
2596 if ( bUpdate && IsReallyVisible() )
2597 ImplCheckScrollBars();
2599 else if( nType == STATE_CHANGE_ENABLE )
2601 mpHScrollBar->Enable( IsEnabled() );
2602 mpVScrollBar->Enable( IsEnabled() );
2603 mpScrollBarBox->Enable( IsEnabled() );
2604 Invalidate();
2606 else if ( nType == STATE_CHANGE_ZOOM )
2608 maLBWindow.SetZoom( GetZoom() );
2609 Resize();
2611 else if ( nType == STATE_CHANGE_CONTROLFONT )
2613 maLBWindow.SetControlFont( GetControlFont() );
2615 else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
2617 maLBWindow.SetControlForeground( GetControlForeground() );
2619 else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
2621 maLBWindow.SetControlBackground( GetControlBackground() );
2623 else if( nType == STATE_CHANGE_MIRRORING )
2625 maLBWindow.EnableRTL( IsRTLEnabled() );
2626 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2627 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2628 ImplResizeControls();
2631 Control::StateChanged( nType );
2634 // -----------------------------------------------------------------------
2636 void ImplListBox::DataChanged( const DataChangedEvent& rDCEvt )
2638 Control::DataChanged( rDCEvt );
2641 // -----------------------------------------------------------------------
2643 long ImplListBox::Notify( NotifyEvent& rNEvt )
2645 long nDone = 0;
2646 if ( rNEvt.GetType() == EVENT_COMMAND )
2648 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2649 if ( rCEvt.GetCommand() == COMMAND_WHEEL )
2651 const CommandWheelData* pData = rCEvt.GetWheelData();
2652 if( !pData->GetModifier() && ( pData->GetMode() == COMMAND_WHEEL_SCROLL ) )
2654 nDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2659 return nDone ? nDone : Window::Notify( rNEvt );
2662 // -----------------------------------------------------------------------
2664 const Wallpaper& ImplListBox::GetDisplayBackground() const
2666 return maLBWindow.GetDisplayBackground();
2669 // -----------------------------------------------------------------------
2671 sal_Bool ImplListBox::HandleWheelAsCursorTravel( const CommandEvent& rCEvt )
2673 sal_Bool bDone = sal_False;
2674 if ( rCEvt.GetCommand() == COMMAND_WHEEL )
2676 const CommandWheelData* pData = rCEvt.GetWheelData();
2677 if( !pData->GetModifier() && ( pData->GetMode() == COMMAND_WHEEL_SCROLL ) )
2679 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2680 KeyEvent aKeyEvent( 0, KeyCode( nKey ) );
2681 bDone = ProcessKeyInput( aKeyEvent );
2684 return bDone;
2687 // -----------------------------------------------------------------------
2689 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2691 bool bChanges = GetEntryList()->GetMRUCount() ? true : false;
2693 // Remove old MRU entries
2694 for ( sal_uInt16 n = GetEntryList()->GetMRUCount();n; )
2695 maLBWindow.RemoveEntry( --n );
2697 sal_uInt16 nMRUCount = 0;
2698 sal_Int32 nIndex = 0;
2701 XubString aEntry = rEntries.getToken( 0, cSep, nIndex );
2702 // Accept only existing entries
2703 if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2705 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2706 maLBWindow.GetEntryList()->InsertEntry( nMRUCount++, pNewEntry, sal_False );
2707 bChanges = true;
2710 while ( nIndex >= 0 );
2712 if ( bChanges )
2714 maLBWindow.GetEntryList()->SetMRUCount( nMRUCount );
2715 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2716 StateChanged( STATE_CHANGE_DATA );
2720 // -----------------------------------------------------------------------
2722 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2724 OUStringBuffer aEntries;
2725 for ( sal_uInt16 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2727 aEntries.append(GetEntryList()->GetEntryText( n ));
2728 if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2729 aEntries.append(cSep);
2731 return aEntries.makeStringAndClear();
2734 // -----------------------------------------------------------------------
2736 void ImplListBox::SetEdgeBlending(bool bNew)
2738 if(mbEdgeBlending != bNew)
2740 mbEdgeBlending = bNew;
2741 maLBWindow.SetEdgeBlending(GetEdgeBlending());
2745 // =======================================================================
2747 ImplWin::ImplWin( Window* pParent, WinBits nWinStyle ) :
2748 Control ( pParent, nWinStyle )
2750 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2751 && ! IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
2752 SetBackground();
2753 else
2754 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2756 mbInUserDraw = false;
2757 mbUserDrawEnabled = false;
2758 mbEdgeBlending = false;
2759 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2762 // -----------------------------------------------------------------------
2764 void ImplWin::MBDown()
2766 if( IsEnabled() )
2767 maMBDownHdl.Call( this );
2770 // -----------------------------------------------------------------------
2772 void ImplWin::MouseButtonDown( const MouseEvent& )
2774 if( IsEnabled() )
2776 MBDown();
2780 // -----------------------------------------------------------------------
2782 void ImplWin::FillLayoutData() const
2784 mpControlData->mpLayoutData = new vcl::ControlLayoutData();
2785 const_cast<ImplWin*>(this)->ImplDraw( true );
2788 // -----------------------------------------------------------------------
2790 long ImplWin::PreNotify( NotifyEvent& rNEvt )
2792 long nDone = 0;
2793 const MouseEvent* pMouseEvt = NULL;
2795 if( (rNEvt.GetType() == EVENT_MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != NULL )
2797 if( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() )
2799 // trigger redraw as mouse over state has changed
2800 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2801 && ! IsNativeControlSupported(CTRL_LISTBOX, PART_BUTTON_DOWN) )
2803 GetParent()->GetWindow( WINDOW_BORDER )->Invalidate( INVALIDATE_NOERASE );
2804 GetParent()->GetWindow( WINDOW_BORDER )->Update();
2809 return nDone ? nDone : Control::PreNotify(rNEvt);
2812 // -----------------------------------------------------------------------
2814 void ImplWin::ImplDraw( bool bLayout )
2816 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
2818 sal_Bool bNativeOK = sal_False;
2820 if( ! bLayout )
2822 ControlState nState = CTRL_STATE_ENABLED;
2823 if ( IsNativeControlSupported(CTRL_LISTBOX, PART_ENTIRE_CONTROL)
2824 && IsNativeControlSupported(CTRL_LISTBOX, HAS_BACKGROUND_TEXTURE) )
2826 // Repaint the (focused) area similarly to
2827 // ImplSmallBorderWindowView::DrawWindow() in
2828 // vcl/source/window/brdwin.cxx
2829 Window *pWin = GetParent();
2831 ImplControlValue aControlValue;
2832 if ( !pWin->IsEnabled() )
2833 nState &= ~CTRL_STATE_ENABLED;
2834 if ( pWin->HasFocus() )
2835 nState |= CTRL_STATE_FOCUSED;
2837 // The listbox is painted over the entire control including the
2838 // border, but ImplWin does not contain the border => correction
2839 // needed.
2840 sal_Int32 nLeft, nTop, nRight, nBottom;
2841 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2842 Point aPoint( -nLeft, -nTop );
2843 Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2845 sal_Bool bMouseOver = sal_False;
2846 if( GetParent() )
2848 Window *pChild = GetParent()->GetWindow( WINDOW_FIRSTCHILD );
2849 while( pChild && (bMouseOver = pChild->IsMouseOver()) == sal_False )
2850 pChild = pChild->GetWindow( WINDOW_NEXT );
2853 if( bMouseOver )
2854 nState |= CTRL_STATE_ROLLOVER;
2856 // if parent has no border, then nobody has drawn the background
2857 // since no border window exists. so draw it here.
2858 WinBits nParentStyle = pWin->GetStyle();
2859 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2861 Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2862 pWin->DrawNativeControl( CTRL_LISTBOX, PART_ENTIRE_CONTROL, aParentRect,
2863 nState, aControlValue, OUString() );
2866 bNativeOK = DrawNativeControl( CTRL_LISTBOX, PART_ENTIRE_CONTROL, aCtrlRegion, nState,
2867 aControlValue, OUString() );
2870 if( IsEnabled() )
2872 if( HasFocus() )
2874 SetTextColor( rStyleSettings.GetHighlightTextColor() );
2875 SetFillColor( rStyleSettings.GetHighlightColor() );
2876 DrawRect( maFocusRect );
2878 else
2880 Color aColor;
2881 if( ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea )
2883 if( bNativeOK && (nState & CTRL_STATE_ROLLOVER) )
2884 aColor = rStyleSettings.GetButtonRolloverTextColor();
2885 else
2886 aColor = rStyleSettings.GetButtonTextColor();
2888 else
2890 if( bNativeOK && (nState & CTRL_STATE_ROLLOVER) )
2891 aColor = rStyleSettings.GetFieldRolloverTextColor();
2892 else
2893 aColor = rStyleSettings.GetFieldTextColor();
2895 if( IsControlForeground() )
2896 aColor = GetControlForeground();
2897 SetTextColor( aColor );
2898 if ( !bNativeOK )
2899 Erase( maFocusRect );
2902 else // Disabled
2904 SetTextColor( rStyleSettings.GetDisableColor() );
2905 if ( !bNativeOK )
2906 Erase( maFocusRect );
2910 if ( IsUserDrawEnabled() )
2912 mbInUserDraw = true;
2913 UserDrawEvent aUDEvt( this, maFocusRect, mnItemPos, 0 );
2914 maUserDrawHdl.Call( &aUDEvt );
2915 mbInUserDraw = false;
2917 else
2919 DrawEntry( sal_True, sal_True, sal_False, bLayout );
2923 // -----------------------------------------------------------------------
2925 void ImplWin::Paint( const Rectangle& )
2927 ImplDraw();
2930 // -----------------------------------------------------------------------
2932 void ImplWin::DrawEntry( sal_Bool bDrawImage, sal_Bool bDrawText, sal_Bool bDrawTextAtImagePos, bool bLayout )
2934 long nBorder = 1;
2935 Size aOutSz = GetOutputSizePixel();
2937 bool bImage = !!maImage;
2938 if( bDrawImage && bImage && !bLayout )
2940 sal_uInt16 nStyle = 0;
2941 Size aImgSz = maImage.GetSizePixel();
2942 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2943 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2945 // check for HC mode
2946 Image *pImage = &maImage;
2948 if ( !IsZoom() )
2950 DrawImage( aPtImg, *pImage, nStyle );
2952 else
2954 aImgSz.Width() = CalcZoom( aImgSz.Width() );
2955 aImgSz.Height() = CalcZoom( aImgSz.Height() );
2956 DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2959 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2961 if(nEdgeBlendingPercent)
2963 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2964 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2965 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2966 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2968 if(!aBlendFrame.IsEmpty())
2970 DrawBitmapEx(aPtImg, aBlendFrame);
2975 if( bDrawText && !maString.isEmpty() )
2977 sal_uInt16 nTextStyle = TEXT_DRAW_VCENTER;
2979 if ( bDrawImage && bImage && !bLayout )
2980 nTextStyle |= TEXT_DRAW_LEFT;
2981 else if ( GetStyle() & WB_CENTER )
2982 nTextStyle |= TEXT_DRAW_CENTER;
2983 else if ( GetStyle() & WB_RIGHT )
2984 nTextStyle |= TEXT_DRAW_RIGHT;
2985 else
2986 nTextStyle |= TEXT_DRAW_LEFT;
2988 Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2990 if ( !bDrawTextAtImagePos && ( bImage || IsUserDrawEnabled() ) )
2992 long nMaxWidth = std::max( maImage.GetSizePixel().Width(), maUserItemSize.Width() );
2993 aTextRect.Left() += nMaxWidth + IMG_TXT_DISTANCE;
2996 MetricVector* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : NULL;
2997 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : NULL;
2998 DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
3001 if( HasFocus() && !bLayout )
3002 ShowFocus( maFocusRect );
3005 // -----------------------------------------------------------------------
3007 void ImplWin::Resize()
3009 Control::Resize();
3010 maFocusRect.SetSize( GetOutputSizePixel() );
3011 Invalidate();
3014 // -----------------------------------------------------------------------
3016 void ImplWin::GetFocus()
3018 ShowFocus( maFocusRect );
3019 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3020 IsNativeWidgetEnabled() &&
3021 IsNativeControlSupported( CTRL_LISTBOX, PART_ENTIRE_CONTROL ) )
3023 Window* pWin = GetParent()->GetWindow( WINDOW_BORDER );
3024 if( ! pWin )
3025 pWin = GetParent();
3026 pWin->Invalidate();
3028 else
3029 Invalidate();
3030 Control::GetFocus();
3033 // -----------------------------------------------------------------------
3035 void ImplWin::LoseFocus()
3037 HideFocus();
3038 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3039 IsNativeWidgetEnabled() &&
3040 IsNativeControlSupported( CTRL_LISTBOX, PART_ENTIRE_CONTROL ) )
3042 Window* pWin = GetParent()->GetWindow( WINDOW_BORDER );
3043 if( ! pWin )
3044 pWin = GetParent();
3045 pWin->Invalidate();
3047 else
3048 Invalidate();
3049 Control::LoseFocus();
3052 // =======================================================================
3054 ImplBtn::ImplBtn( Window* pParent, WinBits nWinStyle ) :
3055 PushButton( pParent, nWinStyle ),
3056 mbDown ( sal_False )
3060 // -----------------------------------------------------------------------
3062 void ImplBtn::MBDown()
3064 if( IsEnabled() )
3065 maMBDownHdl.Call( this );
3068 // -----------------------------------------------------------------------
3070 void ImplBtn::MouseButtonDown( const MouseEvent& )
3072 //PushButton::MouseButtonDown( rMEvt );
3073 if( IsEnabled() )
3075 MBDown();
3076 mbDown = sal_True;
3080 // =======================================================================
3082 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( Window* pParent ) :
3083 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
3085 mpImplLB = NULL;
3086 mnDDLineCount = 0;
3087 mbAutoWidth = sal_False;
3089 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
3091 EnableSaveBackground();
3093 Window * pBorderWindow = ImplGetBorderWindow();
3094 if( pBorderWindow )
3096 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
3097 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
3099 else
3101 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
3106 // -----------------------------------------------------------------------
3108 long ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
3110 if( rNEvt.GetType() == EVENT_LOSEFOCUS )
3112 if( !GetParent()->HasChildPathFocus( sal_True ) )
3113 EndPopupMode();
3116 return FloatingWindow::PreNotify( rNEvt );
3119 // -----------------------------------------------------------------------
3121 void ImplListBoxFloatingWindow::setPosSizePixel( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags )
3123 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
3125 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
3126 // after a call to Resize(), we adjust its position if necessary
3127 if ( IsReallyVisible() && ( nFlags & WINDOW_POSSIZE_HEIGHT ) )
3129 Point aPos = GetParent()->GetPosPixel();
3130 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
3132 if ( nFlags & WINDOW_POSSIZE_X )
3133 aPos.X() = nX;
3135 if ( nFlags & WINDOW_POSSIZE_Y )
3136 aPos.Y() = nY;
3138 sal_uInt16 nIndex;
3139 SetPosPixel( ImplCalcPos( this, Rectangle( aPos, GetParent()->GetSizePixel() ), FLOATWIN_POPUPMODE_DOWN, nIndex ) );
3142 // if( !IsReallyVisible() )
3144 // The ImplListBox does not get a Resize() as not visible.
3145 // But the windows must get a Resize(), so that the number of
3146 // visible entries is correct for PgUp/PgDown.
3147 // The number also cannot be calculated by List/Combobox, as for
3148 // this the presence of the vertical Scrollbar has to be known.
3149 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3150 ((Window*)mpImplLB)->Resize();
3151 ((Window*)mpImplLB->GetMainWindow())->Resize();
3155 // -----------------------------------------------------------------------
3157 void ImplListBoxFloatingWindow::Resize()
3159 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3160 FloatingWindow::Resize();
3163 // -----------------------------------------------------------------------
3165 Size ImplListBoxFloatingWindow::CalcFloatSize()
3167 Size aFloatSz( maPrefSz );
3169 sal_Int32 nLeft, nTop, nRight, nBottom;
3170 GetBorder( nLeft, nTop, nRight, nBottom );
3172 sal_uInt16 nLines = mpImplLB->GetEntryList()->GetEntryCount();
3173 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
3174 nLines = mnDDLineCount;
3176 Size aSz = mpImplLB->CalcSize( nLines );
3177 long nMaxHeight = aSz.Height() + nTop + nBottom;
3179 if ( mnDDLineCount )
3180 aFloatSz.Height() = nMaxHeight;
3182 if( mbAutoWidth )
3184 // AutoSize first only for width...
3186 aFloatSz.Width() = aSz.Width() + nLeft + nRight;
3187 aFloatSz.Width() += nRight; // adding some space looks better...
3189 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
3191 // then we also need the vertical Scrollbar
3192 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3193 aFloatSz.Width() += nSBWidth;
3196 long nDesktopWidth = GetDesktopRectPixel().getWidth();
3197 if (aFloatSz.Width() > nDesktopWidth)
3198 // Don't exceed the desktop width.
3199 aFloatSz.Width() = nDesktopWidth;
3202 if ( aFloatSz.Height() > nMaxHeight )
3203 aFloatSz.Height() = nMaxHeight;
3205 // Minimal height, in case height is not set to Float height.
3206 // The parent of FloatWin must be DropDown-Combo/Listbox.
3207 Size aParentSz = GetParent()->GetSizePixel();
3208 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
3209 aFloatSz.Height() = aParentSz.Height();
3211 // do not get narrower than the parent...
3212 if( aFloatSz.Width() < aParentSz.Width() )
3213 aFloatSz.Width() = aParentSz.Width();
3215 // align height to entries...
3216 long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
3217 long nEntryHeight = mpImplLB->GetEntryHeight();
3218 if ( nInnerHeight % nEntryHeight )
3220 nInnerHeight /= nEntryHeight;
3221 nInnerHeight++;
3222 nInnerHeight *= nEntryHeight;
3223 aFloatSz.Height() = nInnerHeight + nTop + nBottom;
3226 if (aFloatSz.Width() < aSz.Width())
3228 // The max width of list box entries exceeds the window width.
3229 // Account for the scroll bar height.
3230 long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
3231 aFloatSz.Height() += nSBWidth;
3234 return aFloatSz;
3237 // -----------------------------------------------------------------------
3239 void ImplListBoxFloatingWindow::StartFloat( sal_Bool bStartTracking )
3241 if( !IsInPopupMode() )
3243 Size aFloatSz = CalcFloatSize();
3245 SetSizePixel( aFloatSz );
3246 mpImplLB->SetSizePixel( GetOutputSizePixel() );
3248 sal_uInt16 nPos = mpImplLB->GetEntryList()->GetSelectEntryPos( 0 );
3249 mnPopupModeStartSaveSelection = nPos;
3251 Size aSz = GetParent()->GetSizePixel();
3252 Point aPos = GetParent()->GetPosPixel();
3253 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
3254 // FIXME: this ugly hack is for Mac/Aqua
3255 // should be replaced by a real mechanism to place the float rectangle
3256 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
3257 GetParent()->IsNativeWidgetEnabled() )
3259 sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
3260 aPos.X() += nLeft;
3261 aPos.Y() += nTop;
3262 aSz.Width() -= nLeft + nRight;
3263 aSz.Height() -= nTop + nBottom;
3265 Rectangle aRect( aPos, aSz );
3267 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
3268 // where the document is unmirrored
3269 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
3270 if( GetParent()->GetParent()->ImplIsAntiparallel() )
3271 GetParent()->GetParent()->ImplReMirror( aRect );
3273 StartPopupMode( aRect, FLOATWIN_POPUPMODE_DOWN );
3275 if( nPos != LISTBOX_ENTRY_NOTFOUND )
3276 mpImplLB->ShowProminentEntry( nPos );
3278 if( bStartTracking )
3279 mpImplLB->GetMainWindow()->EnableMouseMoveSelect( sal_True );
3281 if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
3282 mpImplLB->GetMainWindow()->GrabFocus();
3284 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */