1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <memory>
21
22 #include <vcl/svapp.hxx>
23 #include <vcl/settings.hxx>
24 #include <vcl/event.hxx>
25 #include <vcl/scrbar.hxx>
26 #include <vcl/toolkit/lstbox.hxx>
27 #include <vcl/i18nhelp.hxx>
28 #include <vcl/naturalsort.hxx>
29
30 #include <listbox.hxx>
31 #include <controldata.hxx>
32 #include <svdata.hxx>
33 #include <window.h>
34
35 #include <com/sun/star/accessibility/AccessibleRole.hpp>
36
37 #include <rtl/instance.hxx>
38 #include <sal/log.hxx>
39 #include <o3tl/safeint.hxx>
40 #include <osl/diagnose.h>
41 #include <comphelper/string.hxx>
42 #include <comphelper/processfactory.hxx>
43
44 #include <limits>
45
46 #define MULTILINE_ENTRY_DRAW_FLAGS ( DrawTextFlags::WordBreak | DrawTextFlags::MultiLine | DrawTextFlags::VCenter )
47
48 using namespace ::com::sun::star;
49
50 constexpr tools::Long gnBorder = 1;
51
ImplInitDropDownButton(PushButton * pButton)52 void ImplInitDropDownButton( PushButton* pButton )
53 {
54 pButton->SetSymbol( SymbolType::SPIN_DOWN );
55
56 if ( pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
57 && ! pButton->IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
58 pButton->SetBackground();
59 }
60
ImplEntryList(vcl::Window * pWindow)61 ImplEntryList::ImplEntryList( vcl::Window* pWindow )
62 {
63 mpWindow = pWindow;
64 mnLastSelected = LISTBOX_ENTRY_NOTFOUND;
65 mnSelectionAnchor = LISTBOX_ENTRY_NOTFOUND;
66 mnImages = 0;
67 mbCallSelectionChangedHdl = true;
68
69 mnMRUCount = 0;
70 mnMaxMRUCount = 0;
71 }
72
~ImplEntryList()73 ImplEntryList::~ImplEntryList()
74 {
75 Clear();
76 }
77
Clear()78 void ImplEntryList::Clear()
79 {
80 mnImages = 0;
81 maEntries.clear();
82 }
83
SelectEntry(sal_Int32 nPos,bool bSelect)84 void ImplEntryList::SelectEntry( sal_Int32 nPos, bool bSelect )
85 {
86 if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
87 {
88 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+nPos;
89
90 if ( ( (*iter)->mbIsSelected != bSelect ) &&
91 ( ( (*iter)->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE ) )
92 {
93 (*iter)->mbIsSelected = bSelect;
94 if ( mbCallSelectionChangedHdl )
95 maSelectionChangedHdl.Call( nPos );
96 }
97 }
98 }
99
100 namespace
101 {
102 struct theSorter
103 : public rtl::StaticWithInit< comphelper::string::NaturalStringSorter, theSorter >
104 {
operator ()__anon2206311c0111::theSorter105 comphelper::string::NaturalStringSorter operator () ()
106 {
107 return comphelper::string::NaturalStringSorter(
108 ::comphelper::getProcessComponentContext(),
109 Application::GetSettings().GetLanguageTag().getLocale());
110 }
111 };
112 }
113
114 namespace vcl
115 {
NaturalSortCompare(const OUString & rA,const OUString & rB)116 sal_Int32 NaturalSortCompare(const OUString &rA, const OUString &rB)
117 {
118 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
119 return rSorter.compare(rA, rB);
120 }
121 }
122
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry,bool bSort)123 sal_Int32 ImplEntryList::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort )
124 {
125 assert(nPos >= 0);
126 assert(maEntries.size() < LISTBOX_MAX_ENTRIES);
127
128 if ( !!pNewEntry->maImage )
129 mnImages++;
130
131 sal_Int32 insPos = 0;
132 const sal_Int32 nEntriesSize = static_cast<sal_Int32>(maEntries.size());
133
134 if ( !bSort || maEntries.empty())
135 {
136 if (0 <= nPos && nPos < nEntriesSize)
137 {
138 insPos = nPos;
139 maEntries.insert( maEntries.begin() + nPos, std::unique_ptr<ImplEntryType>(pNewEntry) );
140 }
141 else
142 {
143 insPos = nEntriesSize;
144 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
145 }
146 }
147 else
148 {
149 const comphelper::string::NaturalStringSorter &rSorter = theSorter::get();
150
151 const OUString& rStr = pNewEntry->maStr;
152
153 ImplEntryType* pTemp = GetEntry( nEntriesSize-1 );
154
155 try
156 {
157 sal_Int32 nComp = rSorter.compare(rStr, pTemp->maStr);
158
159 // fast insert for sorted data
160 if ( nComp >= 0 )
161 {
162 insPos = nEntriesSize;
163 maEntries.push_back(std::unique_ptr<ImplEntryType>(pNewEntry));
164 }
165 else
166 {
167 pTemp = GetEntry( mnMRUCount );
168
169 nComp = rSorter.compare(rStr, pTemp->maStr);
170 if ( nComp <= 0 )
171 {
172 insPos = 0;
173 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
174 }
175 else
176 {
177 sal_uLong nLow = mnMRUCount;
178 sal_uLong nHigh = maEntries.size()-1;
179 sal_Int32 nMid;
180
181 // binary search
182 do
183 {
184 nMid = static_cast<sal_Int32>((nLow + nHigh) / 2);
185 pTemp = GetEntry( nMid );
186
187 nComp = rSorter.compare(rStr, pTemp->maStr);
188
189 if ( nComp < 0 )
190 nHigh = nMid-1;
191 else
192 {
193 if ( nComp > 0 )
194 nLow = nMid + 1;
195 else
196 break;
197 }
198 }
199 while ( nLow <= nHigh );
200
201 if ( nComp >= 0 )
202 nMid++;
203
204 insPos = nMid;
205 maEntries.insert(maEntries.begin()+nMid, std::unique_ptr<ImplEntryType>(pNewEntry));
206 }
207 }
208 }
209 catch (uno::RuntimeException& )
210 {
211 // XXX this is arguable, if the exception occurred because pNewEntry is
212 // garbage you wouldn't insert it. If the exception occurred because the
213 // Collator implementation is garbage then give the user a chance to see
214 // his stuff
215 insPos = 0;
216 maEntries.insert(maEntries.begin(), std::unique_ptr<ImplEntryType>(pNewEntry));
217 }
218
219 }
220
221 return insPos;
222 }
223
RemoveEntry(sal_Int32 nPos)224 void ImplEntryList::RemoveEntry( sal_Int32 nPos )
225 {
226 if (0 <= nPos && o3tl::make_unsigned(nPos) < maEntries.size())
227 {
228 std::vector<std::unique_ptr<ImplEntryType> >::iterator iter = maEntries.begin()+ nPos;
229
230 if ( !!(*iter)->maImage )
231 mnImages--;
232
233 maEntries.erase(iter);
234 }
235 }
236
FindEntry(std::u16string_view rString,bool bSearchMRUArea) const237 sal_Int32 ImplEntryList::FindEntry( std::u16string_view rString, bool bSearchMRUArea ) const
238 {
239 const sal_Int32 nEntries = static_cast<sal_Int32>(maEntries.size());
240 for ( sal_Int32 n = bSearchMRUArea ? 0 : GetMRUCount(); n < nEntries; n++ )
241 {
242 OUString aComp( vcl::I18nHelper::filterFormattingChars( maEntries[n]->maStr ) );
243 if ( aComp == rString )
244 return n;
245 }
246 return LISTBOX_ENTRY_NOTFOUND;
247 }
248
FindMatchingEntry(const OUString & rStr,sal_Int32 nStart,bool bLazy) const249 sal_Int32 ImplEntryList::FindMatchingEntry( const OUString& rStr, sal_Int32 nStart, bool bLazy ) const
250 {
251 sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
252 sal_Int32 nEntryCount = GetEntryCount();
253
254 const vcl::I18nHelper& rI18nHelper = mpWindow->GetSettings().GetLocaleI18nHelper();
255 for ( sal_Int32 n = nStart; n < nEntryCount; )
256 {
257 ImplEntryType* pImplEntry = GetEntry( n );
258 bool bMatch;
259 if ( bLazy )
260 {
261 bMatch = rI18nHelper.MatchString( rStr, pImplEntry->maStr );
262 }
263 else
264 {
265 bMatch = pImplEntry->maStr.startsWith(rStr);
266 }
267 if ( bMatch )
268 {
269 nPos = n;
270 break;
271 }
272
273 n++;
274 }
275
276 return nPos;
277 }
278
GetAddedHeight(sal_Int32 i_nEndIndex,sal_Int32 i_nBeginIndex) const279 tools::Long ImplEntryList::GetAddedHeight( sal_Int32 i_nEndIndex, sal_Int32 i_nBeginIndex ) const
280 {
281 tools::Long nHeight = 0;
282 sal_Int32 nStart = std::min(i_nEndIndex, i_nBeginIndex);
283 sal_Int32 nStop = std::max(i_nEndIndex, i_nBeginIndex);
284 sal_Int32 nEntryCount = GetEntryCount();
285 if( 0 <= nStop && nStop != LISTBOX_ENTRY_NOTFOUND && nEntryCount != 0 )
286 {
287 // sanity check
288 if( nStop > nEntryCount-1 )
289 nStop = nEntryCount-1;
290 if (nStart < 0)
291 nStart = 0;
292 else if( nStart > nEntryCount-1 )
293 nStart = nEntryCount-1;
294
295 sal_Int32 nIndex = nStart;
296 while( nIndex != LISTBOX_ENTRY_NOTFOUND && nIndex < nStop )
297 {
298 tools::Long nPosHeight = GetEntryPtr( nIndex )->getHeightWithMargin();
299 if (nHeight > ::std::numeric_limits<tools::Long>::max() - nPosHeight)
300 {
301 SAL_WARN( "vcl", "ImplEntryList::GetAddedHeight: truncated");
302 break;
303 }
304 nHeight += nPosHeight;
305 nIndex++;
306 }
307 }
308 else
309 nHeight = 0;
310 return i_nEndIndex > i_nBeginIndex ? nHeight : -nHeight;
311 }
312
GetEntryHeight(sal_Int32 nPos) const313 tools::Long ImplEntryList::GetEntryHeight( sal_Int32 nPos ) const
314 {
315 ImplEntryType* pImplEntry = GetEntry( nPos );
316 return pImplEntry ? pImplEntry->getHeightWithMargin() : 0;
317 }
318
GetEntryText(sal_Int32 nPos) const319 OUString ImplEntryList::GetEntryText( sal_Int32 nPos ) const
320 {
321 OUString aEntryText;
322 ImplEntryType* pImplEntry = GetEntry( nPos );
323 if ( pImplEntry )
324 aEntryText = pImplEntry->maStr;
325 return aEntryText;
326 }
327
HasEntryImage(sal_Int32 nPos) const328 bool ImplEntryList::HasEntryImage( sal_Int32 nPos ) const
329 {
330 bool bImage = false;
331 ImplEntryType* pImplEntry = GetEntry( nPos );
332 if ( pImplEntry )
333 bImage = !!pImplEntry->maImage;
334 return bImage;
335 }
336
GetEntryImage(sal_Int32 nPos) const337 Image ImplEntryList::GetEntryImage( sal_Int32 nPos ) const
338 {
339 Image aImage;
340 ImplEntryType* pImplEntry = GetEntry( nPos );
341 if ( pImplEntry )
342 aImage = pImplEntry->maImage;
343 return aImage;
344 }
345
SetEntryData(sal_Int32 nPos,void * pNewData)346 void ImplEntryList::SetEntryData( sal_Int32 nPos, void* pNewData )
347 {
348 ImplEntryType* pImplEntry = GetEntry( nPos );
349 if ( pImplEntry )
350 pImplEntry->mpUserData = pNewData;
351 }
352
GetEntryData(sal_Int32 nPos) const353 void* ImplEntryList::GetEntryData( sal_Int32 nPos ) const
354 {
355 ImplEntryType* pImplEntry = GetEntry( nPos );
356 return pImplEntry ? pImplEntry->mpUserData : nullptr;
357 }
358
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)359 void ImplEntryList::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
360 {
361 ImplEntryType* pImplEntry = GetEntry( nPos );
362 if ( pImplEntry )
363 pImplEntry->mnFlags = nFlags;
364 }
365
GetSelectedEntryCount() const366 sal_Int32 ImplEntryList::GetSelectedEntryCount() const
367 {
368 sal_Int32 nSelCount = 0;
369 for ( sal_Int32 n = GetEntryCount(); n; )
370 {
371 ImplEntryType* pImplEntry = GetEntry( --n );
372 if ( pImplEntry->mbIsSelected )
373 nSelCount++;
374 }
375 return nSelCount;
376 }
377
GetSelectedEntry(sal_Int32 nIndex) const378 OUString ImplEntryList::GetSelectedEntry( sal_Int32 nIndex ) const
379 {
380 return GetEntryText( GetSelectedEntryPos( nIndex ) );
381 }
382
GetSelectedEntryPos(sal_Int32 nIndex) const383 sal_Int32 ImplEntryList::GetSelectedEntryPos( sal_Int32 nIndex ) const
384 {
385 sal_Int32 nSelEntryPos = LISTBOX_ENTRY_NOTFOUND;
386 sal_Int32 nSel = 0;
387 sal_Int32 nEntryCount = GetEntryCount();
388
389 for ( sal_Int32 n = 0; n < nEntryCount; n++ )
390 {
391 ImplEntryType* pImplEntry = GetEntry( n );
392 if ( pImplEntry->mbIsSelected )
393 {
394 if ( nSel == nIndex )
395 {
396 nSelEntryPos = n;
397 break;
398 }
399 nSel++;
400 }
401 }
402
403 return nSelEntryPos;
404 }
405
IsEntryPosSelected(sal_Int32 nIndex) const406 bool ImplEntryList::IsEntryPosSelected( sal_Int32 nIndex ) const
407 {
408 ImplEntryType* pImplEntry = GetEntry( nIndex );
409 return pImplEntry && pImplEntry->mbIsSelected;
410 }
411
IsEntrySelectable(sal_Int32 nPos) const412 bool ImplEntryList::IsEntrySelectable( sal_Int32 nPos ) const
413 {
414 ImplEntryType* pImplEntry = GetEntry( nPos );
415 return pImplEntry == nullptr || ((pImplEntry->mnFlags & ListBoxEntryFlags::DisableSelection) == ListBoxEntryFlags::NONE);
416 }
417
FindFirstSelectable(sal_Int32 nPos,bool bForward)418 sal_Int32 ImplEntryList::FindFirstSelectable( sal_Int32 nPos, bool bForward /* = true */ )
419 {
420 if( IsEntrySelectable( nPos ) )
421 return nPos;
422
423 if( bForward )
424 {
425 for( nPos = nPos + 1; nPos < GetEntryCount(); nPos++ )
426 {
427 if( IsEntrySelectable( nPos ) )
428 return nPos;
429 }
430 }
431 else
432 {
433 while( nPos )
434 {
435 nPos--;
436 if( IsEntrySelectable( nPos ) )
437 return nPos;
438 }
439 }
440
441 return LISTBOX_ENTRY_NOTFOUND;
442 }
443
ImplListBoxWindow(vcl::Window * pParent,WinBits nWinStyle)444 ImplListBoxWindow::ImplListBoxWindow( vcl::Window* pParent, WinBits nWinStyle ) :
445 Control( pParent, 0 ),
446 maQuickSelectionEngine( *this )
447 {
448 mpEntryList.reset(new ImplEntryList( this ));
449
450 mnTop = 0;
451 mnLeft = 0;
452 mnSelectModifier = 0;
453 mnUserDrawEntry = LISTBOX_ENTRY_NOTFOUND;
454 mbTrack = false;
455 mbTravelSelect = false;
456 mbTrackingSelect = false;
457 mbSelectionChanged = false;
458 mbMouseMoveSelect = false;
459 mbMulti = false;
460 mbGrabFocus = false;
461 mbUserDrawEnabled = false;
462 mbInUserDraw = false;
463 mbReadOnly = false;
464 mbHasFocusRect = false;
465 mbRight = ( nWinStyle & WB_RIGHT );
466 mbCenter = ( nWinStyle & WB_CENTER );
467 mbSimpleMode = ( nWinStyle & WB_SIMPLEMODE );
468 mbSort = ( nWinStyle & WB_SORT );
469 mbIsDropdown = ( nWinStyle & WB_DROPDOWN );
470 mbEdgeBlending = false;
471
472 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
473 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
474
475 GetOutDev()->SetLineColor();
476 SetTextFillColor();
477 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
478
479 ApplySettings(*GetOutDev());
480 ImplCalcMetrics();
481 }
482
~ImplListBoxWindow()483 ImplListBoxWindow::~ImplListBoxWindow()
484 {
485 disposeOnce();
486 }
487
dispose()488 void ImplListBoxWindow::dispose()
489 {
490 mpEntryList.reset();
491 Control::dispose();
492 }
493
ApplySettings(vcl::RenderContext & rRenderContext)494 void ImplListBoxWindow::ApplySettings(vcl::RenderContext& rRenderContext)
495 {
496 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
497
498 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
499 ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
500
501 if (IsControlBackground())
502 rRenderContext.SetBackground(GetControlBackground());
503 else
504 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
505 }
506
ImplCalcMetrics()507 void ImplListBoxWindow::ImplCalcMetrics()
508 {
509 mnMaxWidth = 0;
510 mnMaxTxtWidth = 0;
511 mnMaxImgWidth = 0;
512 mnMaxImgTxtWidth= 0;
513 mnMaxImgHeight = 0;
514
515 mnTextHeight = static_cast<sal_uInt16>(GetTextHeight());
516 mnMaxTxtHeight = mnTextHeight + gnBorder;
517 mnMaxHeight = mnMaxTxtHeight;
518
519 if ( maUserItemSize.Height() > mnMaxHeight )
520 mnMaxHeight = static_cast<sal_uInt16>(maUserItemSize.Height());
521 if ( maUserItemSize.Width() > mnMaxWidth )
522 mnMaxWidth= static_cast<sal_uInt16>(maUserItemSize.Width());
523
524 for ( sal_Int32 n = mpEntryList->GetEntryCount(); n; )
525 {
526 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( --n );
527 ImplUpdateEntryMetrics( *pEntry );
528 }
529
530 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
531 {
532 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryPtr( mnCurrentPos )->getHeightWithMargin() );
533 maFocusRect.SetSize( aSz );
534 }
535 }
536
Clear()537 void ImplListBoxWindow::Clear()
538 {
539 mpEntryList->Clear();
540
541 mnMaxHeight = mnMaxTxtHeight;
542 mnMaxWidth = 0;
543 mnMaxTxtWidth = 0;
544 mnMaxImgTxtWidth= 0;
545 mnMaxImgWidth = 0;
546 mnMaxImgHeight = 0;
547 mnTop = 0;
548 mnLeft = 0;
549 ImplClearLayoutData();
550
551 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
552 maQuickSelectionEngine.Reset();
553
554 Invalidate();
555 }
556
SetUserItemSize(const Size & rSz)557 void ImplListBoxWindow::SetUserItemSize( const Size& rSz )
558 {
559 ImplClearLayoutData();
560 maUserItemSize = rSz;
561 ImplCalcMetrics();
562 }
563
564 namespace {
565
566 struct ImplEntryMetrics
567 {
568 bool bText;
569 bool bImage;
570 tools::Long nEntryWidth;
571 tools::Long nEntryHeight;
572 tools::Long nTextWidth;
573 tools::Long nImgWidth;
574 tools::Long nImgHeight;
575 };
576
577 }
578
getHeightWithMargin() const579 tools::Long ImplEntryType::getHeightWithMargin() const
580 {
581 return mnHeight + ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
582 }
583
GetTextGlyphs(const OutputDevice * pOutputDevice)584 SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const OutputDevice* pOutputDevice)
585 {
586 if (maStrGlyphs.IsValid())
587 // Use pre-calculated result.
588 return &maStrGlyphs;
589
590 std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
591 maStr, 0, maStr.getLength(), Point(0, 0), 0, nullptr, SalLayoutFlags::GlyphItemsOnly);
592 if (!pLayout)
593 return nullptr;
594
595 // Remember the calculation result.
596 maStrGlyphs = pLayout->GetGlyphs();
597
598 return &maStrGlyphs;
599 }
600
ImplUpdateEntryMetrics(ImplEntryType & rEntry)601 void ImplListBoxWindow::ImplUpdateEntryMetrics( ImplEntryType& rEntry )
602 {
603 ImplEntryMetrics aMetrics;
604 aMetrics.bText = !rEntry.maStr.isEmpty();
605 aMetrics.bImage = !!rEntry.maImage;
606 aMetrics.nEntryWidth = 0;
607 aMetrics.nEntryHeight = 0;
608 aMetrics.nTextWidth = 0;
609 aMetrics.nImgWidth = 0;
610 aMetrics.nImgHeight = 0;
611
612 if ( aMetrics.bText )
613 {
614 if( rEntry.mnFlags & ListBoxEntryFlags::MultiLine )
615 {
616 // multiline case
617 Size aCurSize( PixelToLogic( GetSizePixel() ) );
618 // set the current size to a large number
619 // GetTextRect should shrink it to the actual size
620 aCurSize.setHeight( 0x7fffff );
621 tools::Rectangle aTextRect( Point( 0, 0 ), aCurSize );
622 aTextRect = GetTextRect( aTextRect, rEntry.maStr, DrawTextFlags::WordBreak | DrawTextFlags::MultiLine );
623 aMetrics.nTextWidth = aTextRect.GetWidth();
624 if( aMetrics.nTextWidth > mnMaxTxtWidth )
625 mnMaxTxtWidth = aMetrics.nTextWidth;
626 aMetrics.nEntryWidth = mnMaxTxtWidth;
627 aMetrics.nEntryHeight = aTextRect.GetHeight() + gnBorder;
628 }
629 else
630 {
631 // normal single line case
632 const SalLayoutGlyphs* pGlyphs = rEntry.GetTextGlyphs(GetOutDev());
633 aMetrics.nTextWidth
634 = static_cast<sal_uInt16>(GetTextWidth(rEntry.maStr, 0, -1, nullptr, pGlyphs));
635 if( aMetrics.nTextWidth > mnMaxTxtWidth )
636 mnMaxTxtWidth = aMetrics.nTextWidth;
637 aMetrics.nEntryWidth = mnMaxTxtWidth;
638 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
639 }
640 }
641 if ( aMetrics.bImage )
642 {
643 Size aImgSz = rEntry.maImage.GetSizePixel();
644 aMetrics.nImgWidth = static_cast<sal_uInt16>(CalcZoom( aImgSz.Width() ));
645 aMetrics.nImgHeight = static_cast<sal_uInt16>(CalcZoom( aImgSz.Height() ));
646
647 if( aMetrics.nImgWidth > mnMaxImgWidth )
648 mnMaxImgWidth = aMetrics.nImgWidth;
649 if( aMetrics.nImgHeight > mnMaxImgHeight )
650 mnMaxImgHeight = aMetrics.nImgHeight;
651
652 mnMaxImgTxtWidth = std::max( mnMaxImgTxtWidth, aMetrics.nTextWidth );
653 aMetrics.nEntryHeight = std::max( aMetrics.nImgHeight, aMetrics.nEntryHeight );
654
655 }
656
657 bool bIsUserDrawEnabled = IsUserDrawEnabled();
658 if (bIsUserDrawEnabled || aMetrics.bImage)
659 {
660 aMetrics.nEntryWidth = std::max( aMetrics.nImgWidth, maUserItemSize.Width() );
661 if (!bIsUserDrawEnabled && aMetrics.bText)
662 aMetrics.nEntryWidth += aMetrics.nTextWidth + IMG_TXT_DISTANCE;
663 aMetrics.nEntryHeight = std::max( std::max( mnMaxImgHeight, maUserItemSize.Height() ) + 2,
664 aMetrics.nEntryHeight );
665 }
666
667 if (!aMetrics.bText && !aMetrics.bImage && !bIsUserDrawEnabled)
668 {
669 // entries which have no (aka an empty) text, and no image,
670 // and are not user-drawn, should be shown nonetheless
671 aMetrics.nEntryHeight = mnTextHeight + gnBorder;
672 }
673
674 if ( aMetrics.nEntryWidth > mnMaxWidth )
675 mnMaxWidth = aMetrics.nEntryWidth;
676 if ( aMetrics.nEntryHeight > mnMaxHeight )
677 mnMaxHeight = aMetrics.nEntryHeight;
678
679 rEntry.mnHeight = aMetrics.nEntryHeight;
680 }
681
ImplCallSelect()682 void ImplListBoxWindow::ImplCallSelect()
683 {
684 if ( !IsTravelSelect() && GetEntryList()->GetMaxMRUCount() )
685 {
686 // Insert the selected entry as MRU, if not already first MRU
687 sal_Int32 nSelected = GetEntryList()->GetSelectedEntryPos( 0 );
688 sal_Int32 nMRUCount = GetEntryList()->GetMRUCount();
689 OUString aSelected = GetEntryList()->GetEntryText( nSelected );
690 sal_Int32 nFirstMatchingEntryPos = GetEntryList()->FindEntry( aSelected, true );
691 if ( nFirstMatchingEntryPos || !nMRUCount )
692 {
693 bool bSelectNewEntry = false;
694 if ( nFirstMatchingEntryPos < nMRUCount )
695 {
696 RemoveEntry( nFirstMatchingEntryPos );
697 nMRUCount--;
698 if ( nFirstMatchingEntryPos == nSelected )
699 bSelectNewEntry = true;
700 }
701 else if ( nMRUCount == GetEntryList()->GetMaxMRUCount() )
702 {
703 RemoveEntry( nMRUCount - 1 );
704 nMRUCount--;
705 }
706
707 ImplClearLayoutData();
708
709 ImplEntryType* pNewEntry = new ImplEntryType( aSelected );
710 pNewEntry->mbIsSelected = bSelectNewEntry;
711 GetEntryList()->InsertEntry( 0, pNewEntry, false );
712 ImplUpdateEntryMetrics( *pNewEntry );
713 GetEntryList()->SetMRUCount( ++nMRUCount );
714 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
715 maMRUChangedHdl.Call( nullptr );
716 }
717 }
718
719 maSelectHdl.Call( nullptr );
720 mbSelectionChanged = false;
721 }
722
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry,bool bSort)723 sal_Int32 ImplListBoxWindow::InsertEntry(sal_Int32 nPos, ImplEntryType* pNewEntry, bool bSort)
724 {
725 assert(nPos >= 0);
726 assert(mpEntryList->GetEntryCount() < LISTBOX_MAX_ENTRIES);
727
728 ImplClearLayoutData();
729 sal_Int32 nNewPos = mpEntryList->InsertEntry( nPos, pNewEntry, bSort );
730
731 if( GetStyle() & WB_WORDBREAK )
732 pNewEntry->mnFlags |= ListBoxEntryFlags::MultiLine;
733
734 ImplUpdateEntryMetrics( *pNewEntry );
735 return nNewPos;
736 }
737
InsertEntry(sal_Int32 nPos,ImplEntryType * pNewEntry)738 sal_Int32 ImplListBoxWindow::InsertEntry( sal_Int32 nPos, ImplEntryType* pNewEntry )
739 {
740 return InsertEntry(nPos, pNewEntry, mbSort);
741 }
742
RemoveEntry(sal_Int32 nPos)743 void ImplListBoxWindow::RemoveEntry( sal_Int32 nPos )
744 {
745 ImplClearLayoutData();
746 mpEntryList->RemoveEntry( nPos );
747 if( mnCurrentPos >= mpEntryList->GetEntryCount() )
748 mnCurrentPos = LISTBOX_ENTRY_NOTFOUND;
749 ImplCalcMetrics();
750 }
751
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)752 void ImplListBoxWindow::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
753 {
754 mpEntryList->SetEntryFlags( nPos, nFlags );
755 ImplEntryType* pEntry = mpEntryList->GetMutableEntryPtr( nPos );
756 if( pEntry )
757 ImplUpdateEntryMetrics( *pEntry );
758 }
759
ImplShowFocusRect()760 void ImplListBoxWindow::ImplShowFocusRect()
761 {
762 if ( mbHasFocusRect )
763 HideFocus();
764 ShowFocus( maFocusRect );
765 mbHasFocusRect = true;
766 }
767
ImplHideFocusRect()768 void ImplListBoxWindow::ImplHideFocusRect()
769 {
770 if ( mbHasFocusRect )
771 {
772 HideFocus();
773 mbHasFocusRect = false;
774 }
775 }
776
GetEntryPosForPoint(const Point & rPoint) const777 sal_Int32 ImplListBoxWindow::GetEntryPosForPoint( const Point& rPoint ) const
778 {
779 tools::Long nY = gnBorder;
780
781 sal_Int32 nSelect = mnTop;
782 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nSelect );
783 while (pEntry)
784 {
785 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
786 if (rPoint.Y() <= nEntryHeight + nY)
787 break;
788 nY += nEntryHeight;
789 pEntry = mpEntryList->GetEntryPtr( ++nSelect );
790 }
791 if( pEntry == nullptr )
792 nSelect = LISTBOX_ENTRY_NOTFOUND;
793
794 return nSelect;
795 }
796
IsVisible(sal_Int32 i_nEntry) const797 bool ImplListBoxWindow::IsVisible( sal_Int32 i_nEntry ) const
798 {
799 bool bRet = false;
800
801 if( i_nEntry >= mnTop )
802 {
803 if( mpEntryList->GetAddedHeight( i_nEntry, mnTop ) <
804 PixelToLogic( GetSizePixel() ).Height() )
805 {
806 bRet = true;
807 }
808 }
809
810 return bRet;
811 }
812
GetEntryHeightWithMargin() const813 tools::Long ImplListBoxWindow::GetEntryHeightWithMargin() const
814 {
815 tools::Long nMargin = ImplGetSVData()->maNWFData.mnListBoxEntryMargin;
816 return mnMaxHeight + nMargin;
817 }
818
GetLastVisibleEntry() const819 sal_Int32 ImplListBoxWindow::GetLastVisibleEntry() const
820 {
821 sal_Int32 nPos = mnTop;
822 tools::Long nWindowHeight = GetSizePixel().Height();
823 sal_Int32 nCount = mpEntryList->GetEntryCount();
824 tools::Long nDiff;
825 for( nDiff = 0; nDiff < nWindowHeight && nPos < nCount; nDiff = mpEntryList->GetAddedHeight( nPos, mnTop ) )
826 nPos++;
827
828 if( nDiff > nWindowHeight && nPos > mnTop )
829 nPos--;
830
831 if( nPos >= nCount )
832 nPos = nCount-1;
833
834 return nPos;
835 }
836
MouseButtonDown(const MouseEvent & rMEvt)837 void ImplListBoxWindow::MouseButtonDown( const MouseEvent& rMEvt )
838 {
839 mbMouseMoveSelect = false; // only till the first MouseButtonDown
840 maQuickSelectionEngine.Reset();
841
842 if ( !IsReadOnly() )
843 {
844 if( rMEvt.GetClicks() == 1 )
845 {
846 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
847 if( nSelect != LISTBOX_ENTRY_NOTFOUND )
848 {
849 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
850 mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
851 else
852 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
853
854 mnCurrentPos = nSelect;
855 mbTrackingSelect = true;
856 bool bCurPosChange = (mnCurrentPos != nSelect);
857 (void)SelectEntries( nSelect, LET_MBDOWN, rMEvt.IsShift(), rMEvt.IsMod1() ,bCurPosChange);
858 mbTrackingSelect = false;
859 if ( mbGrabFocus )
860 GrabFocus();
861
862 StartTracking( StartTrackingFlags::ScrollRepeat );
863 }
864 }
865 if( rMEvt.GetClicks() == 2 )
866 {
867 maDoubleClickHdl.Call( this );
868 }
869 }
870 else // if ( mbGrabFocus )
871 {
872 GrabFocus();
873 }
874 }
875
MouseMove(const MouseEvent & rMEvt)876 void ImplListBoxWindow::MouseMove( const MouseEvent& rMEvt )
877 {
878 if (rMEvt.IsLeaveWindow() || mbMulti || !IsMouseMoveSelect() || !mpEntryList->GetEntryCount())
879 return;
880
881 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
882 if( !aRect.IsInside( rMEvt.GetPosPixel() ) )
883 return;
884
885 if ( IsMouseMoveSelect() )
886 {
887 sal_Int32 nSelect = GetEntryPosForPoint( rMEvt.GetPosPixel() );
888 if( nSelect == LISTBOX_ENTRY_NOTFOUND )
889 nSelect = mpEntryList->GetEntryCount() - 1;
890 nSelect = std::min( nSelect, GetLastVisibleEntry() );
891 nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
892 // Select only visible Entries with MouseMove, otherwise Tracking...
893 if ( IsVisible( nSelect ) &&
894 mpEntryList->IsEntrySelectable( nSelect ) &&
895 ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() || ( nSelect != GetEntryList()->GetSelectedEntryPos( 0 ) ) ) )
896 {
897 mbTrackingSelect = true;
898 if ( SelectEntries( nSelect, LET_TRACKING ) )
899 {
900 // When list box selection change by mouse move, notify
901 // VclEventId::ListboxSelect vcl event.
902 maListItemSelectHdl.Call(nullptr);
903 }
904 mbTrackingSelect = false;
905 }
906 }
907
908 // if the DD button was pressed and someone moved into the ListBox
909 // with the mouse button pressed...
910 if ( rMEvt.IsLeft() && !rMEvt.IsSynthetic() )
911 {
912 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
913 mnTrackingSaveSelection = GetEntryList()->GetSelectedEntryPos( 0 );
914 else
915 mnTrackingSaveSelection = LISTBOX_ENTRY_NOTFOUND;
916
917 StartTracking( StartTrackingFlags::ScrollRepeat );
918 }
919 }
920
DeselectAll()921 void ImplListBoxWindow::DeselectAll()
922 {
923 while ( GetEntryList()->GetSelectedEntryCount() )
924 {
925 sal_Int32 nS = GetEntryList()->GetSelectedEntryPos( 0 );
926 SelectEntry( nS, false );
927 }
928 }
929
SelectEntry(sal_Int32 nPos,bool bSelect)930 void ImplListBoxWindow::SelectEntry( sal_Int32 nPos, bool bSelect )
931 {
932 if( (mpEntryList->IsEntryPosSelected( nPos ) == bSelect) || !mpEntryList->IsEntrySelectable( nPos ) )
933 return;
934
935 ImplHideFocusRect();
936 if( bSelect )
937 {
938 if( !mbMulti )
939 {
940 // deselect the selected entry
941 sal_Int32 nDeselect = GetEntryList()->GetSelectedEntryPos( 0 );
942 if( nDeselect != LISTBOX_ENTRY_NOTFOUND )
943 {
944 //SelectEntryPos( nDeselect, false );
945 GetEntryList()->SelectEntry( nDeselect, false );
946 if (IsUpdateMode() && IsReallyVisible())
947 Invalidate();
948 }
949 }
950 mpEntryList->SelectEntry( nPos, true );
951 mnCurrentPos = nPos;
952 if ( ( nPos != LISTBOX_ENTRY_NOTFOUND ) && IsUpdateMode() )
953 {
954 Invalidate();
955 if ( !IsVisible( nPos ) )
956 {
957 ImplClearLayoutData();
958 sal_Int32 nVisibleEntries = GetLastVisibleEntry()-mnTop;
959 if ( !nVisibleEntries || !IsReallyVisible() || ( nPos < GetTopEntry() ) )
960 {
961 Resize();
962 ShowProminentEntry( nPos );
963 }
964 else
965 {
966 ShowProminentEntry( nPos );
967 }
968 }
969 }
970 }
971 else
972 {
973 mpEntryList->SelectEntry( nPos, false );
974 Invalidate();
975 }
976 mbSelectionChanged = true;
977 }
978
SelectEntries(sal_Int32 nSelect,LB_EVENT_TYPE eLET,bool bShift,bool bCtrl,bool bSelectPosChange)979 bool ImplListBoxWindow::SelectEntries( sal_Int32 nSelect, LB_EVENT_TYPE eLET, bool bShift, bool bCtrl, bool bSelectPosChange /*=FALSE*/ )
980 {
981 bool bSelectionChanged = false;
982
983 if( IsEnabled() && mpEntryList->IsEntrySelectable( nSelect ) )
984 {
985 bool bFocusChanged = false;
986
987 // here (Single-ListBox) only one entry can be deselected
988 if( !mbMulti )
989 {
990 sal_Int32 nDeselect = mpEntryList->GetSelectedEntryPos( 0 );
991 if( nSelect != nDeselect )
992 {
993 SelectEntry( nSelect, true );
994 mpEntryList->SetLastSelected( nSelect );
995 bFocusChanged = true;
996 bSelectionChanged = true;
997 }
998 }
999 // MultiListBox without Modifier
1000 else if( mbSimpleMode && !bCtrl && !bShift )
1001 {
1002 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1003 for ( sal_Int32 nPos = 0; nPos < nEntryCount; nPos++ )
1004 {
1005 bool bSelect = nPos == nSelect;
1006 if ( mpEntryList->IsEntryPosSelected( nPos ) != bSelect )
1007 {
1008 SelectEntry( nPos, bSelect );
1009 bFocusChanged = true;
1010 bSelectionChanged = true;
1011 }
1012 }
1013 mpEntryList->SetLastSelected( nSelect );
1014 mpEntryList->SetSelectionAnchor( nSelect );
1015 }
1016 // MultiListBox only with CTRL/SHIFT or not in SimpleMode
1017 else if( ( !mbSimpleMode /* && !bShift */ ) || ( mbSimpleMode && ( bCtrl || bShift ) ) )
1018 {
1019 // Space for selection change
1020 if( !bShift && ( ( eLET == LET_KEYSPACE ) || ( eLET == LET_MBDOWN ) ) )
1021 {
1022 bool bSelect = !mpEntryList->IsEntryPosSelected( nSelect );
1023 SelectEntry( nSelect, bSelect );
1024 mpEntryList->SetLastSelected( nSelect );
1025 mpEntryList->SetSelectionAnchor( nSelect );
1026 if ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1027 mpEntryList->SetSelectionAnchor( LISTBOX_ENTRY_NOTFOUND );
1028 bFocusChanged = true;
1029 bSelectionChanged = true;
1030 }
1031 else if( ( ( eLET == LET_TRACKING ) && ( nSelect != mnCurrentPos ) ) ||
1032 ( bShift && ( ( eLET == LET_KEYMOVE ) || ( eLET == LET_MBDOWN ) ) ) )
1033 {
1034 mnCurrentPos = nSelect;
1035 bFocusChanged = true;
1036
1037 sal_Int32 nAnchor = mpEntryList->GetSelectionAnchor();
1038 if( ( nAnchor == LISTBOX_ENTRY_NOTFOUND ) && mpEntryList->GetSelectedEntryCount() )
1039 {
1040 nAnchor = mpEntryList->GetSelectedEntryPos( mpEntryList->GetSelectedEntryCount() - 1 );
1041 }
1042 if( nAnchor != LISTBOX_ENTRY_NOTFOUND )
1043 {
1044 // All entries from Anchor to nSelect have to be selected
1045 sal_Int32 nStart = std::min( nSelect, nAnchor );
1046 sal_Int32 nEnd = std::max( nSelect, nAnchor );
1047 for ( sal_Int32 n = nStart; n <= nEnd; n++ )
1048 {
1049 if ( !mpEntryList->IsEntryPosSelected( n ) )
1050 {
1051 SelectEntry( n, true );
1052 bSelectionChanged = true;
1053 }
1054 }
1055
1056 // if appropriate some more has to be deselected...
1057 sal_Int32 nLast = mpEntryList->GetLastSelected();
1058 if ( nLast != LISTBOX_ENTRY_NOTFOUND )
1059 {
1060 if ( ( nLast > nSelect ) && ( nLast > nAnchor ) )
1061 {
1062 for ( sal_Int32 n = nSelect+1; n <= nLast; n++ )
1063 {
1064 if ( mpEntryList->IsEntryPosSelected( n ) )
1065 {
1066 SelectEntry( n, false );
1067 bSelectionChanged = true;
1068 }
1069 }
1070 }
1071 else if ( ( nLast < nSelect ) && ( nLast < nAnchor ) )
1072 {
1073 for ( sal_Int32 n = nLast; n < nSelect; n++ )
1074 {
1075 if ( mpEntryList->IsEntryPosSelected( n ) )
1076 {
1077 SelectEntry( n, false );
1078 bSelectionChanged = true;
1079 }
1080 }
1081 }
1082 }
1083 mpEntryList->SetLastSelected( nSelect );
1084 }
1085 }
1086 else if( eLET != LET_TRACKING )
1087 {
1088 ImplHideFocusRect();
1089 Invalidate();
1090 bFocusChanged = true;
1091 }
1092 }
1093 else if( bShift )
1094 {
1095 bFocusChanged = true;
1096 }
1097
1098 if( bSelectionChanged )
1099 mbSelectionChanged = true;
1100
1101 if( bFocusChanged )
1102 {
1103 tools::Long nHeightDiff = mpEntryList->GetAddedHeight( nSelect, mnTop );
1104 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1105 Size aSz( maFocusRect.GetWidth(),
1106 mpEntryList->GetEntryHeight( nSelect ) );
1107 maFocusRect.SetSize( aSz );
1108 if( HasFocus() )
1109 ImplShowFocusRect();
1110 if (bSelectPosChange)
1111 {
1112 maFocusHdl.Call(nSelect);
1113 }
1114 }
1115 ImplClearLayoutData();
1116 }
1117 return bSelectionChanged;
1118 }
1119
Tracking(const TrackingEvent & rTEvt)1120 void ImplListBoxWindow::Tracking( const TrackingEvent& rTEvt )
1121 {
1122 tools::Rectangle aRect( Point(), GetOutputSizePixel() );
1123 bool bInside = aRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() );
1124
1125 if( rTEvt.IsTrackingCanceled() || rTEvt.IsTrackingEnded() ) // MouseButtonUp
1126 {
1127 if ( bInside && !rTEvt.IsTrackingCanceled() )
1128 {
1129 mnSelectModifier = rTEvt.GetMouseEvent().GetModifier();
1130 ImplCallSelect();
1131 }
1132 else
1133 {
1134 maCancelHdl.Call( nullptr );
1135 if ( !mbMulti )
1136 {
1137 mbTrackingSelect = true;
1138 SelectEntry( mnTrackingSaveSelection, true );
1139 mbTrackingSelect = false;
1140 if ( mnTrackingSaveSelection != LISTBOX_ENTRY_NOTFOUND )
1141 {
1142 tools::Long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1143 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1144 Size aSz( maFocusRect.GetWidth(),
1145 mpEntryList->GetEntryHeight( mnCurrentPos ) );
1146 maFocusRect.SetSize( aSz );
1147 ImplShowFocusRect();
1148 }
1149 }
1150 }
1151
1152 mbTrack = false;
1153 }
1154 else
1155 {
1156 bool bTrackOrQuickClick = mbTrack;
1157 if( !mbTrack )
1158 {
1159 if ( bInside )
1160 {
1161 mbTrack = true;
1162 }
1163
1164 // this case only happens, if the mouse button is pressed very briefly
1165 if( rTEvt.IsTrackingEnded() && mbTrack )
1166 {
1167 bTrackOrQuickClick = true;
1168 mbTrack = false;
1169 }
1170 }
1171
1172 if( bTrackOrQuickClick )
1173 {
1174 MouseEvent aMEvt = rTEvt.GetMouseEvent();
1175 Point aPt( aMEvt.GetPosPixel() );
1176 bool bShift = aMEvt.IsShift();
1177 bool bCtrl = aMEvt.IsMod1();
1178
1179 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1180 if( aPt.Y() < 0 )
1181 {
1182 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1183 {
1184 nSelect = mnCurrentPos ? ( mnCurrentPos - 1 ) : 0;
1185 if( nSelect < mnTop )
1186 SetTopEntry( mnTop-1 );
1187 }
1188 }
1189 else if( aPt.Y() > GetOutputSizePixel().Height() )
1190 {
1191 if ( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1192 {
1193 nSelect = std::min( static_cast<sal_Int32>(mnCurrentPos+1), static_cast<sal_Int32>(mpEntryList->GetEntryCount()-1) );
1194 if( nSelect >= GetLastVisibleEntry() )
1195 SetTopEntry( mnTop+1 );
1196 }
1197 }
1198 else
1199 {
1200 nSelect = static_cast<sal_Int32>( ( aPt.Y() + gnBorder ) / mnMaxHeight ) + mnTop;
1201 nSelect = std::min( nSelect, GetLastVisibleEntry() );
1202 nSelect = std::min( nSelect, static_cast<sal_Int32>( mpEntryList->GetEntryCount() - 1 ) );
1203 }
1204
1205 if ( bInside )
1206 {
1207 if ( ( nSelect != mnCurrentPos ) || !GetEntryList()->GetSelectedEntryCount() )
1208 {
1209 mbTrackingSelect = true;
1210 SelectEntries(nSelect, LET_TRACKING, bShift, bCtrl);
1211 mbTrackingSelect = false;
1212 }
1213 }
1214 else
1215 {
1216 if ( !mbMulti && GetEntryList()->GetSelectedEntryCount() )
1217 {
1218 mbTrackingSelect = true;
1219 SelectEntry( GetEntryList()->GetSelectedEntryPos( 0 ), false );
1220 mbTrackingSelect = false;
1221 }
1222 }
1223 mnCurrentPos = nSelect;
1224 if ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1225 {
1226 ImplHideFocusRect();
1227 }
1228 else
1229 {
1230 tools::Long nHeightDiff = mpEntryList->GetAddedHeight( mnCurrentPos, mnTop );
1231 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1232 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1233 maFocusRect.SetSize( aSz );
1234 ImplShowFocusRect();
1235 }
1236 }
1237 }
1238 }
1239
KeyInput(const KeyEvent & rKEvt)1240 void ImplListBoxWindow::KeyInput( const KeyEvent& rKEvt )
1241 {
1242 if( !ProcessKeyInput( rKEvt ) )
1243 Control::KeyInput( rKEvt );
1244 }
1245
ProcessKeyInput(const KeyEvent & rKEvt)1246 bool ImplListBoxWindow::ProcessKeyInput( const KeyEvent& rKEvt )
1247 {
1248 // entry to be selected
1249 sal_Int32 nSelect = LISTBOX_ENTRY_NOTFOUND;
1250 LB_EVENT_TYPE eLET = LET_KEYMOVE;
1251
1252 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1253
1254 bool bShift = aKeyCode.IsShift();
1255 bool bCtrl = aKeyCode.IsMod1() || aKeyCode.IsMod3();
1256 bool bMod2 = aKeyCode.IsMod2();
1257 bool bDone = false;
1258 bool bHandleKey = false;
1259
1260 switch( aKeyCode.GetCode() )
1261 {
1262 case KEY_UP:
1263 {
1264 if ( IsReadOnly() )
1265 {
1266 if ( GetTopEntry() )
1267 SetTopEntry( GetTopEntry()-1 );
1268 }
1269 else if ( !bMod2 )
1270 {
1271 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1272 {
1273 nSelect = mpEntryList->FindFirstSelectable( 0 );
1274 }
1275 else if ( mnCurrentPos )
1276 {
1277 // search first selectable above the current position
1278 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos - 1, false );
1279 }
1280
1281 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect < mnTop ) )
1282 SetTopEntry( mnTop-1 );
1283
1284 bDone = true;
1285 }
1286 maQuickSelectionEngine.Reset();
1287 }
1288 break;
1289
1290 case KEY_DOWN:
1291 {
1292 if ( IsReadOnly() )
1293 {
1294 SetTopEntry( GetTopEntry()+1 );
1295 }
1296 else if ( !bMod2 )
1297 {
1298 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1299 {
1300 nSelect = mpEntryList->FindFirstSelectable( 0 );
1301 }
1302 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1303 {
1304 // search first selectable below the current position
1305 nSelect = mpEntryList->FindFirstSelectable( mnCurrentPos + 1 );
1306 }
1307
1308 if( ( nSelect != LISTBOX_ENTRY_NOTFOUND ) && ( nSelect >= GetLastVisibleEntry() ) )
1309 SetTopEntry( mnTop+1 );
1310
1311 bDone = true;
1312 }
1313 maQuickSelectionEngine.Reset();
1314 }
1315 break;
1316
1317 case KEY_PAGEUP:
1318 {
1319 if ( IsReadOnly() )
1320 {
1321 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1322 SetTopEntry( ( mnTop > nCurVis ) ?
1323 (mnTop-nCurVis) : 0 );
1324 }
1325 else if ( !bCtrl && !bMod2 )
1326 {
1327 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1328 {
1329 nSelect = mpEntryList->FindFirstSelectable( 0 );
1330 }
1331 else if ( mnCurrentPos )
1332 {
1333 if( mnCurrentPos == mnTop )
1334 {
1335 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop +1;
1336 SetTopEntry( ( mnTop > nCurVis ) ? ( mnTop-nCurVis+1 ) : 0 );
1337 }
1338
1339 // find first selectable starting from mnTop looking forward
1340 nSelect = mpEntryList->FindFirstSelectable( mnTop );
1341 }
1342 bDone = true;
1343 }
1344 maQuickSelectionEngine.Reset();
1345 }
1346 break;
1347
1348 case KEY_PAGEDOWN:
1349 {
1350 if ( IsReadOnly() )
1351 {
1352 SetTopEntry( GetLastVisibleEntry() );
1353 }
1354 else if ( !bCtrl && !bMod2 )
1355 {
1356 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1357 {
1358 nSelect = mpEntryList->FindFirstSelectable( 0 );
1359 }
1360 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1361 {
1362 sal_Int32 nCount = mpEntryList->GetEntryCount();
1363 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop;
1364 sal_Int32 nTmp = std::min( nCurVis, nCount );
1365 nTmp += mnTop - 1;
1366 if( mnCurrentPos == nTmp && mnCurrentPos != nCount - 1 )
1367 {
1368 tools::Long nTmp2 = std::min( static_cast<tools::Long>(nCount-nCurVis), static_cast<tools::Long>(static_cast<tools::Long>(mnTop)+static_cast<tools::Long>(nCurVis)-1) );
1369 nTmp2 = std::max( tools::Long(0) , nTmp2 );
1370 nTmp = static_cast<sal_Int32>(nTmp2+(nCurVis-1) );
1371 SetTopEntry( static_cast<sal_Int32>(nTmp2) );
1372 }
1373 // find first selectable starting from nTmp looking backwards
1374 nSelect = mpEntryList->FindFirstSelectable( nTmp, false );
1375 }
1376 bDone = true;
1377 }
1378 maQuickSelectionEngine.Reset();
1379 }
1380 break;
1381
1382 case KEY_HOME:
1383 {
1384 if ( IsReadOnly() )
1385 {
1386 SetTopEntry( 0 );
1387 }
1388 else if ( !bCtrl && !bMod2 && mnCurrentPos )
1389 {
1390 nSelect = mpEntryList->FindFirstSelectable( mpEntryList->GetEntryCount() ? 0 : LISTBOX_ENTRY_NOTFOUND );
1391 if( mnTop != 0 )
1392 SetTopEntry( 0 );
1393
1394 bDone = true;
1395 }
1396 maQuickSelectionEngine.Reset();
1397 }
1398 break;
1399
1400 case KEY_END:
1401 {
1402 if ( IsReadOnly() )
1403 {
1404 SetTopEntry( 0xFFFF );
1405 }
1406 else if ( !bCtrl && !bMod2 )
1407 {
1408 if( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND )
1409 {
1410 nSelect = mpEntryList->FindFirstSelectable( 0 );
1411 }
1412 else if ( (mnCurrentPos+1) < mpEntryList->GetEntryCount() )
1413 {
1414 sal_Int32 nCount = mpEntryList->GetEntryCount();
1415 nSelect = mpEntryList->FindFirstSelectable( nCount - 1, false );
1416 sal_Int32 nCurVis = GetLastVisibleEntry() - mnTop + 1;
1417 if( nCount > nCurVis )
1418 SetTopEntry( nCount - nCurVis );
1419 }
1420 bDone = true;
1421 }
1422 maQuickSelectionEngine.Reset();
1423 }
1424 break;
1425
1426 case KEY_LEFT:
1427 {
1428 if ( !bCtrl && !bMod2 )
1429 {
1430 ScrollHorz( -HORZ_SCROLL );
1431 bDone = true;
1432 }
1433 maQuickSelectionEngine.Reset();
1434 }
1435 break;
1436
1437 case KEY_RIGHT:
1438 {
1439 if ( !bCtrl && !bMod2 )
1440 {
1441 ScrollHorz( HORZ_SCROLL );
1442 bDone = true;
1443 }
1444 maQuickSelectionEngine.Reset();
1445 }
1446 break;
1447
1448 case KEY_RETURN:
1449 {
1450 if ( !bMod2 && !IsReadOnly() )
1451 {
1452 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1453 ImplCallSelect();
1454 bDone = false; // do not catch RETURN
1455 }
1456 maQuickSelectionEngine.Reset();
1457 }
1458 break;
1459
1460 case KEY_SPACE:
1461 {
1462 if ( !bMod2 && !IsReadOnly() )
1463 {
1464 if( mbMulti && ( !mbSimpleMode || ( mbSimpleMode && bCtrl && !bShift ) ) )
1465 {
1466 nSelect = mnCurrentPos;
1467 eLET = LET_KEYSPACE;
1468 }
1469 bDone = true;
1470 }
1471 bHandleKey = true;
1472 }
1473 break;
1474
1475 case KEY_A:
1476 {
1477 if( bCtrl && mbMulti )
1478 {
1479 // paint only once
1480 bool bUpdates = IsUpdateMode();
1481 SetUpdateMode( false );
1482
1483 sal_Int32 nEntryCount = mpEntryList->GetEntryCount();
1484 for( sal_Int32 i = 0; i < nEntryCount; i++ )
1485 SelectEntry( i, true );
1486
1487 // tdf#97066 - Update selected items
1488 ImplCallSelect();
1489
1490 // restore update mode
1491 SetUpdateMode( bUpdates );
1492 Invalidate();
1493
1494 maQuickSelectionEngine.Reset();
1495
1496 bDone = true;
1497 }
1498 else
1499 {
1500 bHandleKey = true;
1501 }
1502 }
1503 break;
1504
1505 default:
1506 bHandleKey = true;
1507 break;
1508 }
1509 if (bHandleKey && !IsReadOnly())
1510 {
1511 bDone = maQuickSelectionEngine.HandleKeyEvent( rKEvt );
1512 }
1513
1514 if ( ( nSelect != LISTBOX_ENTRY_NOTFOUND )
1515 && ( ( !mpEntryList->IsEntryPosSelected( nSelect ) )
1516 || ( eLET == LET_KEYSPACE )
1517 )
1518 )
1519 {
1520 SAL_WARN_IF( mpEntryList->IsEntryPosSelected( nSelect ) && !mbMulti, "vcl", "ImplListBox: Selecting same Entry" );
1521 sal_Int32 nCount = mpEntryList->GetEntryCount();
1522 if (nSelect >= nCount)
1523 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1524 bool bCurPosChange = (mnCurrentPos != nSelect);
1525 mnCurrentPos = nSelect;
1526 if(SelectEntries( nSelect, eLET, bShift, bCtrl, bCurPosChange))
1527 {
1528 // tdf#129043 Correctly deliver events when changing values with arrow keys in combobox
1529 if (mbIsDropdown && IsReallyVisible())
1530 mbTravelSelect = true;
1531 mnSelectModifier = rKEvt.GetKeyCode().GetModifier();
1532 ImplCallSelect();
1533 mbTravelSelect = false;
1534 }
1535 }
1536
1537 return bDone;
1538 }
1539
1540 namespace
1541 {
lcl_getEntry(const ImplEntryList & _rList,sal_Int32 _nPos,OUString & _out_entryText)1542 vcl::StringEntryIdentifier lcl_getEntry( const ImplEntryList& _rList, sal_Int32 _nPos, OUString& _out_entryText )
1543 {
1544 OSL_PRECOND( ( _nPos != LISTBOX_ENTRY_NOTFOUND ), "lcl_getEntry: invalid position!" );
1545 sal_Int32 nEntryCount( _rList.GetEntryCount() );
1546 if ( _nPos >= nEntryCount )
1547 _nPos = 0;
1548 _out_entryText = _rList.GetEntryText( _nPos );
1549
1550 // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
1551 // => normalize
1552 return reinterpret_cast< vcl::StringEntryIdentifier >( _nPos + 1 );
1553 }
1554
lcl_getEntryPos(vcl::StringEntryIdentifier _entry)1555 sal_Int32 lcl_getEntryPos( vcl::StringEntryIdentifier _entry )
1556 {
1557 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
1558 return static_cast< sal_Int32 >( reinterpret_cast< sal_Int64 >( _entry ) ) - 1;
1559 }
1560 }
1561
CurrentEntry(OUString & _out_entryText) const1562 vcl::StringEntryIdentifier ImplListBoxWindow::CurrentEntry( OUString& _out_entryText ) const
1563 {
1564 return lcl_getEntry( *GetEntryList(), ( mnCurrentPos == LISTBOX_ENTRY_NOTFOUND ) ? 0 : mnCurrentPos, _out_entryText );
1565 }
1566
NextEntry(vcl::StringEntryIdentifier _currentEntry,OUString & _out_entryText) const1567 vcl::StringEntryIdentifier ImplListBoxWindow::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1568 {
1569 sal_Int32 nNextPos = lcl_getEntryPos( _currentEntry ) + 1;
1570 return lcl_getEntry( *GetEntryList(), nNextPos, _out_entryText );
1571 }
1572
SelectEntry(vcl::StringEntryIdentifier _entry)1573 void ImplListBoxWindow::SelectEntry( vcl::StringEntryIdentifier _entry )
1574 {
1575 sal_Int32 nSelect = lcl_getEntryPos( _entry );
1576 if ( mpEntryList->IsEntryPosSelected( nSelect ) )
1577 {
1578 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
1579 // to select the given entry by typing its starting letters. No need to act.
1580 return;
1581 }
1582
1583 // normalize
1584 OSL_ENSURE( nSelect < mpEntryList->GetEntryCount(), "ImplListBoxWindow::SelectEntry: how that?" );
1585 sal_Int32 nCount = mpEntryList->GetEntryCount();
1586 if (nSelect >= nCount)
1587 nSelect = nCount ? nCount-1 : LISTBOX_ENTRY_NOTFOUND;
1588
1589 // make visible
1590 ShowProminentEntry( nSelect );
1591
1592 // actually select
1593 mnCurrentPos = nSelect;
1594 if ( SelectEntries( nSelect, LET_KEYMOVE ) )
1595 {
1596 mbTravelSelect = true;
1597 mnSelectModifier = 0;
1598 ImplCallSelect();
1599 mbTravelSelect = false;
1600 }
1601 }
1602
ImplPaint(vcl::RenderContext & rRenderContext,sal_Int32 nPos)1603 void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos)
1604 {
1605 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1606
1607 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nPos );
1608 if (!pEntry)
1609 return;
1610
1611 tools::Long nWidth = GetOutputSizePixel().Width();
1612 tools::Long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1613 tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin()));
1614
1615 bool bSelected = mpEntryList->IsEntryPosSelected(nPos);
1616 if (bSelected)
1617 {
1618 rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor());
1619 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
1620 rRenderContext.SetLineColor();
1621 rRenderContext.DrawRect(aRect);
1622 }
1623 else
1624 {
1625 ApplySettings(rRenderContext);
1626 if (!IsEnabled())
1627 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
1628 }
1629 rRenderContext.SetTextFillColor();
1630
1631 if (IsUserDrawEnabled())
1632 {
1633 mbInUserDraw = true;
1634 mnUserDrawEntry = nPos;
1635 aRect.AdjustLeft( -mnLeft );
1636 if (nPos < GetEntryList()->GetMRUCount())
1637 nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos));
1638 nPos = nPos - GetEntryList()->GetMRUCount();
1639
1640 UserDrawEvent aUDEvt(&rRenderContext, aRect, nPos, bSelected);
1641 maUserDrawHdl.Call( &aUDEvt );
1642 mbInUserDraw = false;
1643 }
1644 else
1645 {
1646 DrawEntry(rRenderContext, nPos, true, true);
1647 }
1648 }
1649
DrawEntry(vcl::RenderContext & rRenderContext,sal_Int32 nPos,bool bDrawImage,bool bDrawText)1650 void ImplListBoxWindow::DrawEntry(vcl::RenderContext& rRenderContext, sal_Int32 nPos, bool bDrawImage, bool bDrawText)
1651 {
1652 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(nPos);
1653 if (!pEntry)
1654 return;
1655
1656 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1657
1658 // when changing this function don't forget to adjust ImplWin::DrawEntry()
1659
1660 if (mbInUserDraw)
1661 nPos = mnUserDrawEntry; // real entry, not the matching entry from MRU
1662
1663 tools::Long nY = mpEntryList->GetAddedHeight(nPos, mnTop);
1664
1665 if (bDrawImage && mpEntryList->HasImages())
1666 {
1667 Image aImage = mpEntryList->GetEntryImage(nPos);
1668 if (!!aImage)
1669 {
1670 Size aImgSz = aImage.GetSizePixel();
1671 Point aPtImg(gnBorder - mnLeft, nY + ((nEntryHeight - aImgSz.Height()) / 2));
1672
1673 if (!IsZoom())
1674 {
1675 rRenderContext.DrawImage(aPtImg, aImage);
1676 }
1677 else
1678 {
1679 aImgSz.setWidth( CalcZoom(aImgSz.Width()) );
1680 aImgSz.setHeight( CalcZoom(aImgSz.Height()) );
1681 rRenderContext.DrawImage(aPtImg, aImgSz, aImage);
1682 }
1683
1684 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1685 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
1686
1687 if (nEdgeBlendingPercent && aImgSz.Width() && aImgSz.Height())
1688 {
1689 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
1690 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
1691 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
1692 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
1693
1694 if (!aBlendFrame.IsEmpty())
1695 {
1696 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
1697 }
1698 }
1699 }
1700 }
1701
1702 if (bDrawText)
1703 {
1704 OUString aStr(mpEntryList->GetEntryText(nPos));
1705 if (!aStr.isEmpty())
1706 {
1707 tools::Long nMaxWidth = std::max(mnMaxWidth, GetOutputSizePixel().Width() - 2 * gnBorder);
1708 // a multiline entry should only be as wide as the window
1709 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1710 nMaxWidth = GetOutputSizePixel().Width() - 2 * gnBorder;
1711
1712 tools::Rectangle aTextRect(Point(gnBorder - mnLeft, nY),
1713 Size(nMaxWidth, nEntryHeight));
1714
1715 if (mpEntryList->HasEntryImage(nPos) || IsUserDrawEnabled())
1716 {
1717 tools::Long nImageWidth = std::max(mnMaxImgWidth, maUserItemSize.Width());
1718 aTextRect.AdjustLeft(nImageWidth + IMG_TXT_DISTANCE );
1719 }
1720
1721 DrawTextFlags nDrawStyle = ImplGetTextStyle();
1722 if (pEntry->mnFlags & ListBoxEntryFlags::MultiLine)
1723 nDrawStyle |= MULTILINE_ENTRY_DRAW_FLAGS;
1724 if (pEntry->mnFlags & ListBoxEntryFlags::DrawDisabled)
1725 nDrawStyle |= DrawTextFlags::Disable;
1726
1727 rRenderContext.DrawText(aTextRect, aStr, nDrawStyle);
1728 }
1729 }
1730
1731 if ( !maSeparators.empty() && ( isSeparator(nPos) || isSeparator(nPos-1) ) )
1732 {
1733 Color aOldLineColor(rRenderContext.GetLineColor());
1734 rRenderContext.SetLineColor((GetBackground() != COL_LIGHTGRAY) ? COL_LIGHTGRAY : COL_GRAY);
1735 Point aStartPos(0, nY);
1736 if (isSeparator(nPos))
1737 aStartPos.AdjustY(pEntry->getHeightWithMargin() - 1 );
1738 Point aEndPos(aStartPos);
1739 aEndPos.setX( GetOutputSizePixel().Width() );
1740 rRenderContext.DrawLine(aStartPos, aEndPos);
1741 rRenderContext.SetLineColor(aOldLineColor);
1742 }
1743 }
1744
FillLayoutData() const1745 void ImplListBoxWindow::FillLayoutData() const
1746 {
1747 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1748 const_cast<ImplListBoxWindow*>(this)->Invalidate(tools::Rectangle(Point(0, 0), GetOutDev()->GetOutputSize()));
1749 }
1750
ImplDoPaint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1751 void ImplListBoxWindow::ImplDoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1752 {
1753 sal_Int32 nCount = mpEntryList->GetEntryCount();
1754
1755 bool bShowFocusRect = mbHasFocusRect;
1756 if (mbHasFocusRect)
1757 ImplHideFocusRect();
1758
1759 tools::Long nY = 0; // + gnBorder;
1760 tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1761
1762 for (sal_Int32 i = mnTop; i < nCount && nY < nHeight + mnMaxHeight; i++)
1763 {
1764 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr(i);
1765 tools::Long nEntryHeight = pEntry->getHeightWithMargin();
1766 if (nY + nEntryHeight >= rRect.Top() &&
1767 nY <= rRect.Bottom() + mnMaxHeight)
1768 {
1769 ImplPaint(rRenderContext, i);
1770 }
1771 nY += nEntryHeight;
1772 }
1773
1774 tools::Long nHeightDiff = mpEntryList->GetAddedHeight(mnCurrentPos, mnTop);
1775 maFocusRect.SetPos(Point(0, nHeightDiff));
1776 Size aSz(maFocusRect.GetWidth(), mpEntryList->GetEntryHeight(mnCurrentPos));
1777 maFocusRect.SetSize(aSz);
1778 if (HasFocus() && bShowFocusRect)
1779 ImplShowFocusRect();
1780 }
1781
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1782 void ImplListBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1783 {
1784 if (SupportsDoubleBuffering())
1785 {
1786 // This widget is explicitly double-buffered, so avoid partial paints.
1787 tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
1788 ImplDoPaint(rRenderContext, aRect);
1789 }
1790 else
1791 ImplDoPaint(rRenderContext, rRect);
1792 }
1793
GetDisplayLineCount() const1794 sal_uInt16 ImplListBoxWindow::GetDisplayLineCount() const
1795 {
1796 // FIXME: ListBoxEntryFlags::MultiLine
1797
1798 const sal_Int32 nCount = mpEntryList->GetEntryCount()-mnTop;
1799 tools::Long nHeight = GetOutputSizePixel().Height();// - mnMaxHeight + gnBorder;
1800 sal_uInt16 nEntries = static_cast< sal_uInt16 >( ( nHeight + mnMaxHeight - 1 ) / mnMaxHeight );
1801 if( nEntries > nCount )
1802 nEntries = static_cast<sal_uInt16>(nCount);
1803
1804 return nEntries;
1805 }
1806
Resize()1807 void ImplListBoxWindow::Resize()
1808 {
1809 Control::Resize();
1810
1811 bool bShowFocusRect = mbHasFocusRect;
1812 if ( bShowFocusRect )
1813 ImplHideFocusRect();
1814
1815 if( mnCurrentPos != LISTBOX_ENTRY_NOTFOUND )
1816 {
1817 Size aSz( GetOutputSizePixel().Width(), mpEntryList->GetEntryHeight( mnCurrentPos ) );
1818 maFocusRect.SetSize( aSz );
1819 }
1820
1821 if ( bShowFocusRect )
1822 ImplShowFocusRect();
1823
1824 ImplClearLayoutData();
1825 }
1826
GetFocus()1827 void ImplListBoxWindow::GetFocus()
1828 {
1829 sal_Int32 nPos = mnCurrentPos;
1830 if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1831 nPos = 0;
1832 tools::Long nHeightDiff = mpEntryList->GetAddedHeight( nPos, mnTop );
1833 maFocusRect.SetPos( Point( 0, nHeightDiff ) );
1834 Size aSz( maFocusRect.GetWidth(), mpEntryList->GetEntryHeight( nPos ) );
1835 maFocusRect.SetSize( aSz );
1836 ImplShowFocusRect();
1837 Control::GetFocus();
1838 }
1839
LoseFocus()1840 void ImplListBoxWindow::LoseFocus()
1841 {
1842 ImplHideFocusRect();
1843 Control::LoseFocus();
1844 }
1845
SetTopEntry(sal_Int32 nTop)1846 void ImplListBoxWindow::SetTopEntry( sal_Int32 nTop )
1847 {
1848 if( mpEntryList->GetEntryCount() == 0 )
1849 return;
1850
1851 tools::Long nWHeight = PixelToLogic( GetSizePixel() ).Height();
1852
1853 sal_Int32 nLastEntry = mpEntryList->GetEntryCount()-1;
1854 if( nTop > nLastEntry )
1855 nTop = nLastEntry;
1856 const ImplEntryType* pLast = mpEntryList->GetEntryPtr( nLastEntry );
1857 while( nTop > 0 && mpEntryList->GetAddedHeight( nLastEntry, nTop-1 ) + pLast->getHeightWithMargin() <= nWHeight )
1858 nTop--;
1859
1860 if ( nTop == mnTop )
1861 return;
1862
1863 ImplClearLayoutData();
1864 tools::Long nDiff = mpEntryList->GetAddedHeight( mnTop, nTop );
1865 PaintImmediately();
1866 ImplHideFocusRect();
1867 mnTop = nTop;
1868 Scroll( 0, nDiff );
1869 PaintImmediately();
1870 if( HasFocus() )
1871 ImplShowFocusRect();
1872 maScrollHdl.Call( this );
1873 }
1874
ShowProminentEntry(sal_Int32 nEntryPos)1875 void ImplListBoxWindow::ShowProminentEntry( sal_Int32 nEntryPos )
1876 {
1877 SetTopEntry( nEntryPos );
1878 }
1879
SetLeftIndent(tools::Long n)1880 void ImplListBoxWindow::SetLeftIndent( tools::Long n )
1881 {
1882 ScrollHorz( n - mnLeft );
1883 }
1884
ScrollHorz(tools::Long n)1885 void ImplListBoxWindow::ScrollHorz( tools::Long n )
1886 {
1887 tools::Long nDiff = 0;
1888 if ( n > 0 )
1889 {
1890 tools::Long nWidth = GetOutputSizePixel().Width();
1891 if( ( mnMaxWidth - mnLeft + n ) > nWidth )
1892 nDiff = n;
1893 }
1894 else if ( n < 0 )
1895 {
1896 if( mnLeft )
1897 {
1898 tools::Long nAbs = -n;
1899 nDiff = - std::min( mnLeft, nAbs );
1900 }
1901 }
1902
1903 if ( nDiff )
1904 {
1905 ImplClearLayoutData();
1906 mnLeft = sal::static_int_cast<sal_uInt16>(mnLeft + nDiff);
1907 PaintImmediately();
1908 ImplHideFocusRect();
1909 Scroll( -nDiff, 0 );
1910 PaintImmediately();
1911 if( HasFocus() )
1912 ImplShowFocusRect();
1913 maScrollHdl.Call( this );
1914 }
1915 }
1916
SetSeparatorPos(sal_Int32 n)1917 void ImplListBoxWindow::SetSeparatorPos( sal_Int32 n )
1918 {
1919 maSeparators.clear();
1920
1921 if ( n != LISTBOX_ENTRY_NOTFOUND )
1922 {
1923 maSeparators.insert( n );
1924 }
1925 }
1926
GetSeparatorPos() const1927 sal_Int32 ImplListBoxWindow::GetSeparatorPos() const
1928 {
1929 if (!maSeparators.empty())
1930 return *(maSeparators.begin());
1931 else
1932 return LISTBOX_ENTRY_NOTFOUND;
1933 }
1934
isSeparator(const sal_Int32 & n) const1935 bool ImplListBoxWindow::isSeparator( const sal_Int32 &n) const
1936 {
1937 return maSeparators.find(n) != maSeparators.end();
1938 }
1939
CalcSize(sal_Int32 nMaxLines) const1940 Size ImplListBoxWindow::CalcSize(sal_Int32 nMaxLines) const
1941 {
1942 // FIXME: ListBoxEntryFlags::MultiLine
1943
1944 Size aSz;
1945 aSz.setHeight(nMaxLines * GetEntryHeightWithMargin());
1946 aSz.setWidth( mnMaxWidth + 2*gnBorder );
1947 return aSz;
1948 }
1949
GetBoundingRectangle(sal_Int32 nItem) const1950 tools::Rectangle ImplListBoxWindow::GetBoundingRectangle( sal_Int32 nItem ) const
1951 {
1952 const ImplEntryType* pEntry = mpEntryList->GetEntryPtr( nItem );
1953 Size aSz( GetSizePixel().Width(), pEntry ? pEntry->getHeightWithMargin() : GetEntryHeightWithMargin() );
1954 tools::Long nY = mpEntryList->GetAddedHeight( nItem, GetTopEntry() ) + GetEntryList()->GetMRUCount()*GetEntryHeightWithMargin();
1955 tools::Rectangle aRect( Point( 0, nY ), aSz );
1956 return aRect;
1957 }
1958
StateChanged(StateChangedType nType)1959 void ImplListBoxWindow::StateChanged( StateChangedType nType )
1960 {
1961 Control::StateChanged( nType );
1962
1963 if ( nType == StateChangedType::Zoom )
1964 {
1965 ApplySettings(*GetOutDev());
1966 ImplCalcMetrics();
1967 Invalidate();
1968 }
1969 else if ( nType == StateChangedType::UpdateMode )
1970 {
1971 if ( IsUpdateMode() && IsReallyVisible() )
1972 Invalidate();
1973 }
1974 else if ( nType == StateChangedType::ControlFont )
1975 {
1976 ApplySettings(*GetOutDev());
1977 ImplCalcMetrics();
1978 Invalidate();
1979 }
1980 else if ( nType == StateChangedType::ControlForeground )
1981 {
1982 ApplySettings(*GetOutDev());
1983 Invalidate();
1984 }
1985 else if ( nType == StateChangedType::ControlBackground )
1986 {
1987 ApplySettings(*GetOutDev());
1988 Invalidate();
1989 }
1990 else if( nType == StateChangedType::Enable )
1991 {
1992 Invalidate();
1993 }
1994
1995 ImplClearLayoutData();
1996 }
1997
DataChanged(const DataChangedEvent & rDCEvt)1998 void ImplListBoxWindow::DataChanged( const DataChangedEvent& rDCEvt )
1999 {
2000 Control::DataChanged( rDCEvt );
2001
2002 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2003 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2004 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2005 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2006 {
2007 ImplClearLayoutData();
2008 ApplySettings(*GetOutDev());
2009 ImplCalcMetrics();
2010 Invalidate();
2011 }
2012 }
2013
ImplGetTextStyle() const2014 DrawTextFlags ImplListBoxWindow::ImplGetTextStyle() const
2015 {
2016 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2017
2018 if (mpEntryList->HasImages())
2019 nTextStyle |= DrawTextFlags::Left;
2020 else if (mbCenter)
2021 nTextStyle |= DrawTextFlags::Center;
2022 else if (mbRight)
2023 nTextStyle |= DrawTextFlags::Right;
2024 else
2025 nTextStyle |= DrawTextFlags::Left;
2026
2027 return nTextStyle;
2028 }
2029
ImplListBox(vcl::Window * pParent,WinBits nWinStyle)2030 ImplListBox::ImplListBox( vcl::Window* pParent, WinBits nWinStyle ) :
2031 Control( pParent, nWinStyle ),
2032 maLBWindow(VclPtr<ImplListBoxWindow>::Create( this, nWinStyle&(~WB_BORDER) ))
2033 {
2034 // for native widget rendering we must be able to detect this window type
2035 SetType( WindowType::LISTBOXWINDOW );
2036
2037 mpVScrollBar = VclPtr<ScrollBar>::Create( this, WB_VSCROLL | WB_DRAG );
2038 mpHScrollBar = VclPtr<ScrollBar>::Create( this, WB_HSCROLL | WB_DRAG );
2039 mpScrollBarBox = VclPtr<ScrollBarBox>::Create( this );
2040
2041 Link<ScrollBar*,void> aLink( LINK( this, ImplListBox, ScrollBarHdl ) );
2042 mpVScrollBar->SetScrollHdl( aLink );
2043 mpHScrollBar->SetScrollHdl( aLink );
2044
2045 mbVScroll = false;
2046 mbHScroll = false;
2047 mbAutoHScroll = ( nWinStyle & WB_AUTOHSCROLL );
2048 mbEdgeBlending = false;
2049
2050 maLBWindow->SetScrollHdl( LINK( this, ImplListBox, LBWindowScrolled ) );
2051 maLBWindow->SetMRUChangedHdl( LINK( this, ImplListBox, MRUChanged ) );
2052 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2053 maLBWindow->Show();
2054 }
2055
~ImplListBox()2056 ImplListBox::~ImplListBox()
2057 {
2058 disposeOnce();
2059 }
2060
dispose()2061 void ImplListBox::dispose()
2062 {
2063 mpHScrollBar.disposeAndClear();
2064 mpVScrollBar.disposeAndClear();
2065 mpScrollBarBox.disposeAndClear();
2066 maLBWindow.disposeAndClear();
2067 Control::dispose();
2068 }
2069
Clear()2070 void ImplListBox::Clear()
2071 {
2072 maLBWindow->Clear();
2073 if ( GetEntryList()->GetMRUCount() )
2074 {
2075 maLBWindow->GetEntryList()->SetMRUCount( 0 );
2076 maLBWindow->SetSeparatorPos( LISTBOX_ENTRY_NOTFOUND );
2077 }
2078 mpVScrollBar->SetThumbPos( 0 );
2079 mpHScrollBar->SetThumbPos( 0 );
2080 CompatStateChanged( StateChangedType::Data );
2081 }
2082
InsertEntry(sal_Int32 nPos,const OUString & rStr)2083 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr )
2084 {
2085 ImplEntryType* pNewEntry = new ImplEntryType( rStr );
2086 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2087 CompatStateChanged( StateChangedType::Data );
2088 return nNewPos;
2089 }
2090
InsertEntry(sal_Int32 nPos,const OUString & rStr,const Image & rImage)2091 sal_Int32 ImplListBox::InsertEntry( sal_Int32 nPos, const OUString& rStr, const Image& rImage )
2092 {
2093 ImplEntryType* pNewEntry = new ImplEntryType( rStr, rImage );
2094 sal_Int32 nNewPos = maLBWindow->InsertEntry( nPos, pNewEntry );
2095 CompatStateChanged( StateChangedType::Data );
2096 return nNewPos;
2097 }
2098
RemoveEntry(sal_Int32 nPos)2099 void ImplListBox::RemoveEntry( sal_Int32 nPos )
2100 {
2101 maLBWindow->RemoveEntry( nPos );
2102 CompatStateChanged( StateChangedType::Data );
2103 }
2104
SetEntryFlags(sal_Int32 nPos,ListBoxEntryFlags nFlags)2105 void ImplListBox::SetEntryFlags( sal_Int32 nPos, ListBoxEntryFlags nFlags )
2106 {
2107 maLBWindow->SetEntryFlags( nPos, nFlags );
2108 }
2109
SelectEntry(sal_Int32 nPos,bool bSelect)2110 void ImplListBox::SelectEntry( sal_Int32 nPos, bool bSelect )
2111 {
2112 maLBWindow->SelectEntry( nPos, bSelect );
2113 }
2114
SetNoSelection()2115 void ImplListBox::SetNoSelection()
2116 {
2117 maLBWindow->DeselectAll();
2118 }
2119
GetFocus()2120 void ImplListBox::GetFocus()
2121 {
2122 if (maLBWindow)
2123 maLBWindow->GrabFocus();
2124 else
2125 Control::GetFocus();
2126 }
2127
Resize()2128 void ImplListBox::Resize()
2129 {
2130 Control::Resize();
2131 ImplResizeControls();
2132 ImplCheckScrollBars();
2133 }
2134
IMPL_LINK_NOARG(ImplListBox,MRUChanged,LinkParamNone *,void)2135 IMPL_LINK_NOARG(ImplListBox, MRUChanged, LinkParamNone*, void)
2136 {
2137 CompatStateChanged( StateChangedType::Data );
2138 }
2139
IMPL_LINK_NOARG(ImplListBox,LBWindowScrolled,ImplListBoxWindow *,void)2140 IMPL_LINK_NOARG(ImplListBox, LBWindowScrolled, ImplListBoxWindow*, void)
2141 {
2142 tools::Long nSet = GetTopEntry();
2143 if( nSet > mpVScrollBar->GetRangeMax() )
2144 mpVScrollBar->SetRangeMax( GetEntryList()->GetEntryCount() );
2145 mpVScrollBar->SetThumbPos( GetTopEntry() );
2146
2147 mpHScrollBar->SetThumbPos( GetLeftIndent() );
2148
2149 maScrollHdl.Call( this );
2150 }
2151
IMPL_LINK(ImplListBox,ScrollBarHdl,ScrollBar *,pSB,void)2152 IMPL_LINK( ImplListBox, ScrollBarHdl, ScrollBar*, pSB, void )
2153 {
2154 sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
2155 if( pSB == mpVScrollBar )
2156 SetTopEntry( nPos );
2157 else if( pSB == mpHScrollBar )
2158 SetLeftIndent( nPos );
2159 if( GetParent() )
2160 GetParent()->Invalidate( InvalidateFlags::Update );
2161 }
2162
ImplCheckScrollBars()2163 void ImplListBox::ImplCheckScrollBars()
2164 {
2165 bool bArrange = false;
2166
2167 Size aOutSz = GetOutputSizePixel();
2168 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2169 sal_uInt16 nMaxVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2170
2171 // vertical ScrollBar
2172 if( nEntries > nMaxVisEntries )
2173 {
2174 if( !mbVScroll )
2175 bArrange = true;
2176 mbVScroll = true;
2177
2178 // check of the scrolled-out region
2179 if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2180 GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2181 ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2182 else
2183 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2184 }
2185 else
2186 {
2187 if( mbVScroll )
2188 bArrange = true;
2189 mbVScroll = false;
2190 SetTopEntry( 0 );
2191 }
2192
2193 // horizontal ScrollBar
2194 if( mbAutoHScroll )
2195 {
2196 tools::Long nWidth = static_cast<sal_uInt16>(aOutSz.Width());
2197 if ( mbVScroll )
2198 nWidth -= mpVScrollBar->GetSizePixel().Width();
2199
2200 tools::Long nMaxWidth = GetMaxEntryWidth();
2201 if( nWidth < nMaxWidth )
2202 {
2203 if( !mbHScroll )
2204 bArrange = true;
2205 mbHScroll = true;
2206
2207 if ( !mbVScroll ) // maybe we do need one now
2208 {
2209 nMaxVisEntries = static_cast<sal_uInt16>( ( aOutSz.Height() - mpHScrollBar->GetSizePixel().Height() ) / GetEntryHeightWithMargin() );
2210 if( nEntries > nMaxVisEntries )
2211 {
2212 bArrange = true;
2213 mbVScroll = true;
2214
2215 // check of the scrolled-out region
2216 if( GetEntryList()->GetSelectedEntryCount() == 1 &&
2217 GetEntryList()->GetSelectedEntryPos( 0 ) != LISTBOX_ENTRY_NOTFOUND )
2218 ShowProminentEntry( GetEntryList()->GetSelectedEntryPos( 0 ) );
2219 else
2220 SetTopEntry( GetTopEntry() ); // MaxTop is being checked...
2221 }
2222 }
2223
2224 // check of the scrolled-out region
2225 sal_uInt16 nMaxLI = static_cast<sal_uInt16>(nMaxWidth - nWidth);
2226 if ( nMaxLI < GetLeftIndent() )
2227 SetLeftIndent( nMaxLI );
2228 }
2229 else
2230 {
2231 if( mbHScroll )
2232 bArrange = true;
2233 mbHScroll = false;
2234 SetLeftIndent( 0 );
2235 }
2236 }
2237
2238 if( bArrange )
2239 ImplResizeControls();
2240
2241 ImplInitScrollBars();
2242 }
2243
ImplInitScrollBars()2244 void ImplListBox::ImplInitScrollBars()
2245 {
2246 Size aOutSz = maLBWindow->GetOutputSizePixel();
2247
2248 if ( mbVScroll )
2249 {
2250 sal_Int32 nEntries = GetEntryList()->GetEntryCount();
2251 sal_uInt16 nVisEntries = static_cast<sal_uInt16>(aOutSz.Height() / GetEntryHeightWithMargin());
2252 mpVScrollBar->SetRangeMax( nEntries );
2253 mpVScrollBar->SetVisibleSize( nVisEntries );
2254 mpVScrollBar->SetPageSize( nVisEntries - 1 );
2255 }
2256
2257 if ( mbHScroll )
2258 {
2259 mpHScrollBar->SetRangeMax( GetMaxEntryWidth() + HORZ_SCROLL );
2260 mpHScrollBar->SetVisibleSize( static_cast<sal_uInt16>(aOutSz.Width()) );
2261 mpHScrollBar->SetLineSize( HORZ_SCROLL );
2262 mpHScrollBar->SetPageSize( aOutSz.Width() - HORZ_SCROLL );
2263 }
2264 }
2265
ImplResizeControls()2266 void ImplListBox::ImplResizeControls()
2267 {
2268 // Here we only position the Controls; if the Scrollbars are to be
2269 // visible is already determined in ImplCheckScrollBars
2270
2271 Size aOutSz = GetOutputSizePixel();
2272 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2273 nSBWidth = CalcZoom( nSBWidth );
2274
2275 Size aInnerSz( aOutSz );
2276 if ( mbVScroll )
2277 aInnerSz.AdjustWidth( -nSBWidth );
2278 if ( mbHScroll )
2279 aInnerSz.AdjustHeight( -nSBWidth );
2280
2281 Point aWinPos( 0, 0 );
2282 maLBWindow->SetPosSizePixel( aWinPos, aInnerSz );
2283
2284 // ScrollBarBox
2285 if( mbVScroll && mbHScroll )
2286 {
2287 Point aBoxPos( aInnerSz.Width(), aInnerSz.Height() );
2288 mpScrollBarBox->SetPosSizePixel( aBoxPos, Size( nSBWidth, nSBWidth ) );
2289 mpScrollBarBox->Show();
2290 }
2291 else
2292 {
2293 mpScrollBarBox->Hide();
2294 }
2295
2296 // vertical ScrollBar
2297 if( mbVScroll )
2298 {
2299 // Scrollbar on left or right side?
2300 Point aVPos( aOutSz.Width() - nSBWidth, 0 );
2301 mpVScrollBar->SetPosSizePixel( aVPos, Size( nSBWidth, aInnerSz.Height() ) );
2302 mpVScrollBar->Show();
2303 }
2304 else
2305 {
2306 mpVScrollBar->Hide();
2307 // #107254# Don't reset top entry after resize, but check for max top entry
2308 SetTopEntry( GetTopEntry() );
2309 }
2310
2311 // horizontal ScrollBar
2312 if( mbHScroll )
2313 {
2314 Point aHPos( 0, aOutSz.Height() - nSBWidth );
2315 mpHScrollBar->SetPosSizePixel( aHPos, Size( aInnerSz.Width(), nSBWidth ) );
2316 mpHScrollBar->Show();
2317 }
2318 else
2319 {
2320 mpHScrollBar->Hide();
2321 SetLeftIndent( 0 );
2322 }
2323 }
2324
StateChanged(StateChangedType nType)2325 void ImplListBox::StateChanged( StateChangedType nType )
2326 {
2327 if ( nType == StateChangedType::InitShow )
2328 {
2329 ImplCheckScrollBars();
2330 }
2331 else if ( ( nType == StateChangedType::UpdateMode ) || ( nType == StateChangedType::Data ) )
2332 {
2333 bool bUpdate = IsUpdateMode();
2334 maLBWindow->SetUpdateMode( bUpdate );
2335 if ( bUpdate && IsReallyVisible() )
2336 ImplCheckScrollBars();
2337 }
2338 else if( nType == StateChangedType::Enable )
2339 {
2340 mpHScrollBar->Enable( IsEnabled() );
2341 mpVScrollBar->Enable( IsEnabled() );
2342 mpScrollBarBox->Enable( IsEnabled() );
2343 maLBWindow->Enable( IsEnabled() );
2344
2345 Invalidate();
2346 }
2347 else if ( nType == StateChangedType::Zoom )
2348 {
2349 maLBWindow->SetZoom( GetZoom() );
2350 Resize();
2351 }
2352 else if ( nType == StateChangedType::ControlFont )
2353 {
2354 maLBWindow->SetControlFont( GetControlFont() );
2355 }
2356 else if ( nType == StateChangedType::ControlForeground )
2357 {
2358 maLBWindow->SetControlForeground( GetControlForeground() );
2359 }
2360 else if ( nType == StateChangedType::ControlBackground )
2361 {
2362 maLBWindow->SetControlBackground( GetControlBackground() );
2363 }
2364 else if( nType == StateChangedType::Mirroring )
2365 {
2366 maLBWindow->EnableRTL( IsRTLEnabled() );
2367 mpHScrollBar->EnableRTL( IsRTLEnabled() );
2368 mpVScrollBar->EnableRTL( IsRTLEnabled() );
2369 ImplResizeControls();
2370 }
2371
2372 Control::StateChanged( nType );
2373 }
2374
EventNotify(NotifyEvent & rNEvt)2375 bool ImplListBox::EventNotify( NotifyEvent& rNEvt )
2376 {
2377 bool bDone = false;
2378 if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
2379 {
2380 const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2381 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2382 {
2383 const CommandWheelData* pData = rCEvt.GetWheelData();
2384 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2385 {
2386 bDone = HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar );
2387 }
2388 }
2389 else if (rCEvt.GetCommand() == CommandEventId::Gesture)
2390 {
2391 bDone = HandleScrollCommand(rCEvt, mpHScrollBar, mpVScrollBar);
2392 }
2393 }
2394
2395 return bDone || Window::EventNotify( rNEvt );
2396 }
2397
GetDisplayBackground() const2398 const Wallpaper& ImplListBox::GetDisplayBackground() const
2399 {
2400 return maLBWindow->GetDisplayBackground();
2401 }
2402
HandleWheelAsCursorTravel(const CommandEvent & rCEvt,Control & rControl)2403 bool ImplListBox::HandleWheelAsCursorTravel(const CommandEvent& rCEvt, Control& rControl)
2404 {
2405 bool bDone = false;
2406 if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2407 {
2408 const CommandWheelData* pData = rCEvt.GetWheelData();
2409 if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2410 {
2411 if (!rControl.HasChildPathFocus())
2412 rControl.GrabFocus();
2413 sal_uInt16 nKey = ( pData->GetDelta() < 0 ) ? KEY_DOWN : KEY_UP;
2414 KeyEvent aKeyEvent( 0, vcl::KeyCode( nKey ) );
2415 bDone = ProcessKeyInput( aKeyEvent );
2416 }
2417 }
2418 return bDone;
2419 }
2420
SetMRUEntries(const OUString & rEntries,sal_Unicode cSep)2421 void ImplListBox::SetMRUEntries( const OUString& rEntries, sal_Unicode cSep )
2422 {
2423 bool bChanges = GetEntryList()->GetMRUCount() != 0;
2424
2425 // Remove old MRU entries
2426 for ( sal_Int32 n = GetEntryList()->GetMRUCount();n; )
2427 maLBWindow->RemoveEntry( --n );
2428
2429 sal_Int32 nMRUCount = 0;
2430 sal_Int32 nIndex = 0;
2431 do
2432 {
2433 OUString aEntry = rEntries.getToken( 0, cSep, nIndex );
2434 // Accept only existing entries
2435 if ( GetEntryList()->FindEntry( aEntry ) != LISTBOX_ENTRY_NOTFOUND )
2436 {
2437 ImplEntryType* pNewEntry = new ImplEntryType( aEntry );
2438 maLBWindow->InsertEntry(nMRUCount++, pNewEntry, false);
2439 bChanges = true;
2440 }
2441 }
2442 while ( nIndex >= 0 );
2443
2444 if ( bChanges )
2445 {
2446 maLBWindow->GetEntryList()->SetMRUCount( nMRUCount );
2447 SetSeparatorPos( nMRUCount ? nMRUCount-1 : 0 );
2448 CompatStateChanged( StateChangedType::Data );
2449 }
2450 }
2451
GetMRUEntries(sal_Unicode cSep) const2452 OUString ImplListBox::GetMRUEntries( sal_Unicode cSep ) const
2453 {
2454 OUStringBuffer aEntries;
2455 for ( sal_Int32 n = 0; n < GetEntryList()->GetMRUCount(); n++ )
2456 {
2457 aEntries.append(GetEntryList()->GetEntryText( n ));
2458 if( n < ( GetEntryList()->GetMRUCount() - 1 ) )
2459 aEntries.append(cSep);
2460 }
2461 return aEntries.makeStringAndClear();
2462 }
2463
SetEdgeBlending(bool bNew)2464 void ImplListBox::SetEdgeBlending(bool bNew)
2465 {
2466 if(mbEdgeBlending != bNew)
2467 {
2468 mbEdgeBlending = bNew;
2469 maLBWindow->SetEdgeBlending(GetEdgeBlending());
2470 }
2471 }
2472
ImplWin(vcl::Window * pParent,WinBits nWinStyle)2473 ImplWin::ImplWin( vcl::Window* pParent, WinBits nWinStyle ) :
2474 Control ( pParent, nWinStyle )
2475 {
2476 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2477 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2478 SetBackground();
2479 else
2480 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) );
2481
2482 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2483
2484 mbEdgeBlending = false;
2485 mnItemPos = LISTBOX_ENTRY_NOTFOUND;
2486 }
2487
MouseButtonDown(const MouseEvent &)2488 void ImplWin::MouseButtonDown( const MouseEvent& )
2489 {
2490 if( IsEnabled() )
2491 {
2492 maMBDownHdl.Call(this);
2493 }
2494 }
2495
FillLayoutData() const2496 void ImplWin::FillLayoutData() const
2497 {
2498 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2499 ImplWin* pThis = const_cast<ImplWin*>(this);
2500 pThis->ImplDraw(*pThis->GetOutDev(), true);
2501 }
2502
PreNotify(NotifyEvent & rNEvt)2503 bool ImplWin::PreNotify( NotifyEvent& rNEvt )
2504 {
2505 if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
2506 {
2507 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
2508 if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
2509 {
2510 // trigger redraw as mouse over state has changed
2511 if ( IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2512 && ! IsNativeControlSupported(ControlType::Listbox, ControlPart::ButtonDown) )
2513 {
2514 GetParent()->GetWindow( GetWindowType::Border )->Invalidate( InvalidateFlags::NoErase );
2515 }
2516 }
2517 }
2518
2519 return Control::PreNotify(rNEvt);
2520 }
2521
ImplDraw(vcl::RenderContext & rRenderContext,bool bLayout)2522 void ImplWin::ImplDraw(vcl::RenderContext& rRenderContext, bool bLayout)
2523 {
2524 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2525
2526 if (!bLayout)
2527 {
2528 bool bNativeOK = false;
2529 bool bHasFocus = HasFocus();
2530 bool bIsEnabled = IsEnabled();
2531
2532 ControlState nState = ControlState::ENABLED;
2533 if (rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::Entire)
2534 && rRenderContext.IsNativeControlSupported(ControlType::Listbox, ControlPart::HasBackgroundTexture) )
2535 {
2536 // Repaint the (focused) area similarly to
2537 // ImplSmallBorderWindowView::DrawWindow() in
2538 // vcl/source/window/brdwin.cxx
2539 vcl::Window *pWin = GetParent();
2540
2541 ImplControlValue aControlValue;
2542 bIsEnabled &= pWin->IsEnabled();
2543 if ( !bIsEnabled )
2544 nState &= ~ControlState::ENABLED;
2545 bHasFocus |= pWin->HasFocus();
2546 if ( bHasFocus )
2547 nState |= ControlState::FOCUSED;
2548
2549 // The listbox is painted over the entire control including the
2550 // border, but ImplWin does not contain the border => correction
2551 // needed.
2552 sal_Int32 nLeft, nTop, nRight, nBottom;
2553 pWin->GetBorder( nLeft, nTop, nRight, nBottom );
2554 Point aPoint( -nLeft, -nTop );
2555 tools::Rectangle aCtrlRegion( aPoint - GetPosPixel(), pWin->GetSizePixel() );
2556
2557 bool bMouseOver = false;
2558 vcl::Window *pChild = pWin->GetWindow( GetWindowType::FirstChild );
2559 while( pChild )
2560 {
2561 bMouseOver = pChild->IsMouseOver();
2562 if (bMouseOver)
2563 break;
2564 pChild = pChild->GetWindow( GetWindowType::Next );
2565 }
2566 if( bMouseOver )
2567 nState |= ControlState::ROLLOVER;
2568
2569 // if parent has no border, then nobody has drawn the background
2570 // since no border window exists. so draw it here.
2571 WinBits nParentStyle = pWin->GetStyle();
2572 if( ! (nParentStyle & WB_BORDER) || (nParentStyle & WB_NOBORDER) )
2573 {
2574 tools::Rectangle aParentRect( Point( 0, 0 ), pWin->GetSizePixel() );
2575 pWin->GetOutDev()->DrawNativeControl( ControlType::Listbox, ControlPart::Entire, aParentRect,
2576 nState, aControlValue, OUString() );
2577 }
2578
2579 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Listbox, ControlPart::Entire, aCtrlRegion,
2580 nState, aControlValue, OUString());
2581 }
2582
2583 if (bIsEnabled)
2584 {
2585 if (bHasFocus && !ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2586 {
2587 if ( !ImplGetSVData()->maNWFData.mbNoFocusRects )
2588 {
2589 rRenderContext.SetFillColor( rStyleSettings.GetHighlightColor() );
2590 rRenderContext.SetTextColor( rStyleSettings.GetHighlightTextColor() );
2591 }
2592 else
2593 {
2594 rRenderContext.SetLineColor();
2595 rRenderContext.SetFillColor();
2596 rRenderContext.SetTextColor( rStyleSettings.GetFieldTextColor() );
2597 }
2598 rRenderContext.DrawRect( maFocusRect );
2599 }
2600 else
2601 {
2602 Color aColor;
2603 if (IsControlForeground())
2604 aColor = GetControlForeground();
2605 else if (ImplGetSVData()->maNWFData.mbDDListBoxNoTextArea)
2606 {
2607 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2608 aColor = rStyleSettings.GetButtonRolloverTextColor();
2609 else
2610 aColor = rStyleSettings.GetButtonTextColor();
2611 }
2612 else
2613 {
2614 if( bNativeOK && (nState & ControlState::ROLLOVER) )
2615 aColor = rStyleSettings.GetFieldRolloverTextColor();
2616 else
2617 aColor = rStyleSettings.GetFieldTextColor();
2618 }
2619 rRenderContext.SetTextColor(aColor);
2620 if (!bNativeOK)
2621 rRenderContext.Erase(maFocusRect);
2622 }
2623 }
2624 else // Disabled
2625 {
2626 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
2627 if (!bNativeOK)
2628 rRenderContext.Erase(maFocusRect);
2629 }
2630 }
2631
2632 DrawEntry(rRenderContext, bLayout);
2633 }
2634
ApplySettings(vcl::RenderContext & rRenderContext)2635 void ImplWin::ApplySettings(vcl::RenderContext& rRenderContext)
2636 {
2637 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2638
2639 ApplyControlFont(rRenderContext, rStyleSettings.GetFieldFont());
2640 ApplyControlForeground(rRenderContext, rStyleSettings.GetFieldTextColor());
2641
2642 if (IsControlBackground())
2643 rRenderContext.SetBackground(GetControlBackground());
2644 else
2645 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
2646 }
2647
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)2648 void ImplWin::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2649 {
2650 ImplDraw(rRenderContext);
2651 }
2652
DrawEntry(vcl::RenderContext & rRenderContext,bool bLayout)2653 void ImplWin::DrawEntry(vcl::RenderContext& rRenderContext, bool bLayout)
2654 {
2655 tools::Long nBorder = 1;
2656 Size aOutSz(GetOutputSizePixel());
2657
2658 bool bImage = !!maImage;
2659 if (bImage && !bLayout)
2660 {
2661 DrawImageFlags nStyle = DrawImageFlags::NONE;
2662 Size aImgSz = maImage.GetSizePixel();
2663 Point aPtImg( nBorder, ( ( aOutSz.Height() - aImgSz.Height() ) / 2 ) );
2664 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2665
2666 // check for HC mode
2667 Image *pImage = &maImage;
2668
2669 if ( !IsZoom() )
2670 {
2671 rRenderContext.DrawImage( aPtImg, *pImage, nStyle );
2672 }
2673 else
2674 {
2675 aImgSz.setWidth( CalcZoom( aImgSz.Width() ) );
2676 aImgSz.setHeight( CalcZoom( aImgSz.Height() ) );
2677 rRenderContext.DrawImage( aPtImg, aImgSz, *pImage, nStyle );
2678 }
2679
2680 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0);
2681
2682 if(nEdgeBlendingPercent)
2683 {
2684 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor());
2685 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor());
2686 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100);
2687 const BitmapEx aBlendFrame(createBlendFrame(aImgSz, nAlpha, rTopLeft, rBottomRight));
2688
2689 if(!aBlendFrame.IsEmpty())
2690 {
2691 rRenderContext.DrawBitmapEx(aPtImg, aBlendFrame);
2692 }
2693 }
2694 }
2695
2696 if( !maString.isEmpty() )
2697 {
2698 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
2699
2700 if ( bImage && !bLayout )
2701 nTextStyle |= DrawTextFlags::Left;
2702 else if ( GetStyle() & WB_CENTER )
2703 nTextStyle |= DrawTextFlags::Center;
2704 else if ( GetStyle() & WB_RIGHT )
2705 nTextStyle |= DrawTextFlags::Right;
2706 else
2707 nTextStyle |= DrawTextFlags::Left;
2708
2709 tools::Rectangle aTextRect( Point( nBorder, 0 ), Size( aOutSz.Width()-2*nBorder, aOutSz.Height() ) );
2710
2711 if ( bImage )
2712 {
2713 aTextRect.AdjustLeft(maImage.GetSizePixel().Width() + IMG_TXT_DISTANCE );
2714 }
2715
2716 std::vector< tools::Rectangle >* pVector = bLayout ? &mpControlData->mpLayoutData->m_aUnicodeBoundRects : nullptr;
2717 OUString* pDisplayText = bLayout ? &mpControlData->mpLayoutData->m_aDisplayText : nullptr;
2718 rRenderContext.DrawText( aTextRect, maString, nTextStyle, pVector, pDisplayText );
2719 }
2720
2721 if( HasFocus() && !bLayout )
2722 ShowFocus( maFocusRect );
2723 }
2724
Resize()2725 void ImplWin::Resize()
2726 {
2727 Control::Resize();
2728 maFocusRect.SetSize( GetOutputSizePixel() );
2729 Invalidate();
2730 }
2731
GetFocus()2732 void ImplWin::GetFocus()
2733 {
2734 ShowFocus( maFocusRect );
2735 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2736 IsNativeWidgetEnabled() &&
2737 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2738 {
2739 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2740 if( ! pWin )
2741 pWin = GetParent();
2742 pWin->Invalidate();
2743 }
2744 else
2745 Invalidate();
2746 Control::GetFocus();
2747 }
2748
LoseFocus()2749 void ImplWin::LoseFocus()
2750 {
2751 HideFocus();
2752 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2753 IsNativeWidgetEnabled() &&
2754 IsNativeControlSupported( ControlType::Listbox, ControlPart::Entire ) )
2755 {
2756 vcl::Window* pWin = GetParent()->GetWindow( GetWindowType::Border );
2757 if( ! pWin )
2758 pWin = GetParent();
2759 pWin->Invalidate();
2760 }
2761 else
2762 Invalidate();
2763 Control::LoseFocus();
2764 }
2765
ShowFocus(const tools::Rectangle & rRect)2766 void ImplWin::ShowFocus(const tools::Rectangle& rRect)
2767 {
2768 if (IsNativeControlSupported(ControlType::Listbox, ControlPart::Focus))
2769 {
2770 ImplControlValue aControlValue;
2771
2772 vcl::Window *pWin = GetParent();
2773 tools::Rectangle aParentRect(Point(0, 0), pWin->GetSizePixel());
2774 pWin->GetOutDev()->DrawNativeControl(ControlType::Listbox, ControlPart::Focus, aParentRect,
2775 ControlState::FOCUSED, aControlValue, OUString());
2776 }
2777 Control::ShowFocus(rRect);
2778 }
2779
ImplBtn(vcl::Window * pParent,WinBits nWinStyle)2780 ImplBtn::ImplBtn( vcl::Window* pParent, WinBits nWinStyle ) :
2781 PushButton( pParent, nWinStyle )
2782 {
2783 }
2784
MouseButtonDown(const MouseEvent &)2785 void ImplBtn::MouseButtonDown( const MouseEvent& )
2786 {
2787 if( IsEnabled() )
2788 maMBDownHdl.Call(this);
2789 }
2790
ImplListBoxFloatingWindow(vcl::Window * pParent)2791 ImplListBoxFloatingWindow::ImplListBoxFloatingWindow( vcl::Window* pParent ) :
2792 FloatingWindow( pParent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW ) // no drop shadow for list boxes
2793 {
2794 // for native widget rendering we must be able to detect this window type
2795 SetType( WindowType::LISTBOXWINDOW );
2796
2797 mpImplLB = nullptr;
2798 mnDDLineCount = 0;
2799 mbAutoWidth = false;
2800
2801 mnPopupModeStartSaveSelection = LISTBOX_ENTRY_NOTFOUND;
2802
2803 vcl::Window * pBorderWindow = ImplGetBorderWindow();
2804 if( pBorderWindow )
2805 {
2806 SetAccessibleRole(accessibility::AccessibleRole::PANEL);
2807 pBorderWindow->SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2808 }
2809 else
2810 {
2811 SetAccessibleRole(accessibility::AccessibleRole::WINDOW);
2812 }
2813
2814 }
2815
~ImplListBoxFloatingWindow()2816 ImplListBoxFloatingWindow::~ImplListBoxFloatingWindow()
2817 {
2818 disposeOnce();
2819 }
2820
dispose()2821 void ImplListBoxFloatingWindow::dispose()
2822 {
2823 mpImplLB.clear();
2824 FloatingWindow::dispose();
2825 }
2826
2827
PreNotify(NotifyEvent & rNEvt)2828 bool ImplListBoxFloatingWindow::PreNotify( NotifyEvent& rNEvt )
2829 {
2830 if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2831 {
2832 if( !GetParent()->HasChildPathFocus( true ) )
2833 EndPopupMode();
2834 }
2835
2836 return FloatingWindow::PreNotify( rNEvt );
2837 }
2838
setPosSizePixel(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,PosSizeFlags nFlags)2839 void ImplListBoxFloatingWindow::setPosSizePixel( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
2840 {
2841 FloatingWindow::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
2842
2843 // Fix #60890# ( MBA ): to be able to resize the Listbox even in its open state
2844 // after a call to Resize(), we adjust its position if necessary
2845 if ( IsReallyVisible() && ( nFlags & PosSizeFlags::Height ) )
2846 {
2847 Point aPos = GetParent()->GetPosPixel();
2848 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2849
2850 if ( nFlags & PosSizeFlags::X )
2851 aPos.setX( nX );
2852
2853 if ( nFlags & PosSizeFlags::Y )
2854 aPos.setY( nY );
2855
2856 sal_uInt16 nIndex;
2857 SetPosPixel( ImplCalcPos( this, tools::Rectangle( aPos, GetParent()->GetSizePixel() ), FloatWinPopupFlags::Down, nIndex ) );
2858 }
2859
2860 // if( !IsReallyVisible() )
2861 {
2862 // The ImplListBox does not get a Resize() as not visible.
2863 // But the windows must get a Resize(), so that the number of
2864 // visible entries is correct for PgUp/PgDown.
2865 // The number also cannot be calculated by List/Combobox, as for
2866 // this the presence of the vertical Scrollbar has to be known.
2867 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2868 static_cast<vcl::Window*>(mpImplLB)->Resize();
2869 static_cast<vcl::Window*>(mpImplLB->GetMainWindow())->Resize();
2870 }
2871 }
2872
Resize()2873 void ImplListBoxFloatingWindow::Resize()
2874 {
2875 mpImplLB->GetMainWindow()->ImplClearLayoutData();
2876 FloatingWindow::Resize();
2877 }
2878
CalcFloatSize()2879 Size ImplListBoxFloatingWindow::CalcFloatSize()
2880 {
2881 Size aFloatSz( maPrefSz );
2882
2883 sal_Int32 nLeft, nTop, nRight, nBottom;
2884 GetBorder( nLeft, nTop, nRight, nBottom );
2885
2886 sal_Int32 nLines = mpImplLB->GetEntryList()->GetEntryCount();
2887 if ( mnDDLineCount && ( nLines > mnDDLineCount ) )
2888 nLines = mnDDLineCount;
2889
2890 Size aSz = mpImplLB->CalcSize( nLines );
2891 tools::Long nMaxHeight = aSz.Height() + nTop + nBottom;
2892
2893 if ( mnDDLineCount )
2894 aFloatSz.setHeight( nMaxHeight );
2895
2896 if( mbAutoWidth )
2897 {
2898 // AutoSize first only for width...
2899
2900 aFloatSz.setWidth( aSz.Width() + nLeft + nRight );
2901 aFloatSz.AdjustWidth(nRight ); // adding some space looks better...
2902
2903 if ( ( aFloatSz.Height() < nMaxHeight ) || ( mnDDLineCount && ( mnDDLineCount < mpImplLB->GetEntryList()->GetEntryCount() ) ) )
2904 {
2905 // then we also need the vertical Scrollbar
2906 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2907 aFloatSz.AdjustWidth(nSBWidth );
2908 }
2909
2910 tools::Long nDesktopWidth = GetDesktopRectPixel().getWidth();
2911 if (aFloatSz.Width() > nDesktopWidth)
2912 // Don't exceed the desktop width.
2913 aFloatSz.setWidth( nDesktopWidth );
2914 }
2915
2916 if ( aFloatSz.Height() > nMaxHeight )
2917 aFloatSz.setHeight( nMaxHeight );
2918
2919 // Minimal height, in case height is not set to Float height.
2920 // The parent of FloatWin must be DropDown-Combo/Listbox.
2921 Size aParentSz = GetParent()->GetSizePixel();
2922 if( (!mnDDLineCount || !nLines) && ( aFloatSz.Height() < aParentSz.Height() ) )
2923 aFloatSz.setHeight( aParentSz.Height() );
2924
2925 // do not get narrower than the parent...
2926 if( aFloatSz.Width() < aParentSz.Width() )
2927 aFloatSz.setWidth( aParentSz.Width() );
2928
2929 // align height to entries...
2930 tools::Long nInnerHeight = aFloatSz.Height() - nTop - nBottom;
2931 tools::Long nEntryHeight = mpImplLB->GetEntryHeightWithMargin();
2932 if ( nInnerHeight % nEntryHeight )
2933 {
2934 nInnerHeight /= nEntryHeight;
2935 nInnerHeight++;
2936 nInnerHeight *= nEntryHeight;
2937 aFloatSz.setHeight( nInnerHeight + nTop + nBottom );
2938 }
2939
2940 if (aFloatSz.Width() < aSz.Width())
2941 {
2942 // The max width of list box entries exceeds the window width.
2943 // Account for the scroll bar height.
2944 tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
2945 aFloatSz.AdjustHeight(nSBWidth );
2946 }
2947
2948 return aFloatSz;
2949 }
2950
StartFloat(bool bStartTracking)2951 void ImplListBoxFloatingWindow::StartFloat( bool bStartTracking )
2952 {
2953 if( IsInPopupMode() )
2954 return;
2955
2956 Size aFloatSz = CalcFloatSize();
2957
2958 SetSizePixel( aFloatSz );
2959 mpImplLB->SetSizePixel( GetOutputSizePixel() );
2960
2961 sal_Int32 nPos = mpImplLB->GetEntryList()->GetSelectedEntryPos( 0 );
2962 mnPopupModeStartSaveSelection = nPos;
2963
2964 Size aSz = GetParent()->GetSizePixel();
2965 Point aPos = GetParent()->GetPosPixel();
2966 aPos = GetParent()->GetParent()->OutputToScreenPixel( aPos );
2967 // FIXME: this ugly hack is for Mac/Aqua
2968 // should be replaced by a real mechanism to place the float rectangle
2969 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2970 GetParent()->IsNativeWidgetEnabled() )
2971 {
2972 const sal_Int32 nLeft = 4, nTop = 4, nRight = 4, nBottom = 4;
2973 aPos.AdjustX(nLeft );
2974 aPos.AdjustY(nTop );
2975 aSz.AdjustWidth( -(nLeft + nRight) );
2976 aSz.AdjustHeight( -(nTop + nBottom) );
2977 }
2978 tools::Rectangle aRect( aPos, aSz );
2979
2980 // check if the control's parent is un-mirrored which is the case for form controls in a mirrored UI
2981 // where the document is unmirrored
2982 // because StartPopupMode() expects a rectangle in mirrored coordinates we have to re-mirror
2983 vcl::Window *pGrandparent = GetParent()->GetParent();
2984 const OutputDevice *pGrandparentOutDev = pGrandparent->GetOutDev();
2985
2986 if( pGrandparent->GetOutDev()->ImplIsAntiparallel() )
2987 pGrandparentOutDev->ReMirror( aRect );
2988
2989 // mouse-button right: close the List-Box-Float-win and don't stop the handling fdo#84795
2990 StartPopupMode( aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::NoHorzPlacement | FloatWinPopupFlags::AllMouseButtonClose );
2991
2992 if( nPos != LISTBOX_ENTRY_NOTFOUND )
2993 mpImplLB->ShowProminentEntry( nPos );
2994
2995 if( bStartTracking )
2996 mpImplLB->GetMainWindow()->EnableMouseMoveSelect( true );
2997
2998 if ( mpImplLB->GetMainWindow()->IsGrabFocusAllowed() )
2999 mpImplLB->GetMainWindow()->GrabFocus();
3000
3001 mpImplLB->GetMainWindow()->ImplClearLayoutData();
3002
3003 }
3004
3005 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3006