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