1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        univ/listbox.cpp 
   3 // Purpose:     wxListBox implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com) 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  31     #include "wx/dcclient.h" 
  32     #include "wx/listbox.h" 
  33     #include "wx/validate.h" 
  36 #include "wx/univ/renderer.h" 
  37 #include "wx/univ/inphand.h" 
  38 #include "wx/univ/theme.h" 
  40 // ============================================================================ 
  41 // implementation of wxListBox 
  42 // ============================================================================ 
  44 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
  46 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
) 
  47     EVT_SIZE(wxListBox::OnSize
) 
  50 // ---------------------------------------------------------------------------- 
  52 // ---------------------------------------------------------------------------- 
  54 void wxListBox::Init() 
  56     // will be calculated later when needed 
  64     // no items hence no current item 
  67     m_currentChanged 
= false; 
  69     // no need to update anything initially 
  72     // no scrollbars to show nor update 
  76     m_showScrollbarY 
= false; 
  79 wxListBox::wxListBox(wxWindow 
*parent
, 
  83                      const wxArrayString
& choices
, 
  85                      const wxValidator
& validator
, 
  91     Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
); 
  94 bool wxListBox::Create(wxWindow 
*parent
, 
  98                        const wxArrayString
& choices
, 
 100                        const wxValidator
& validator
, 
 101                        const wxString 
&name
) 
 103     wxCArrayString 
chs(choices
); 
 105     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 106                   style
, validator
, name
); 
 109 bool wxListBox::Create(wxWindow 
*parent
, 
 114                        const wxString choices
[], 
 116                        const wxValidator
& validator
, 
 117                        const wxString 
&name
) 
 119     // for compatibility accept both the new and old styles - they mean the 
 121     if ( style 
& wxLB_ALWAYS_SB 
) 
 122         style 
|= wxALWAYS_SHOW_SB
; 
 124     // if we don't have neither multiple nor extended flag, we must have the 
 125     // single selection listbox 
 126     if ( !(style 
& (wxLB_MULTIPLE 
| wxLB_EXTENDED
)) ) 
 127         style 
|= wxLB_SINGLE
; 
 129 #if wxUSE_TWO_WINDOWS 
 130     style 
|=  wxVSCROLL
|wxHSCROLL
; 
 131     if ((style 
& wxBORDER_MASK
) == 0) 
 132         style 
|= wxBORDER_SUNKEN
; 
 135     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, 
 139     m_strings 
= new wxArrayString
; 
 145     CreateInputHandler(wxINP_HANDLER_LISTBOX
); 
 150 wxListBox::~wxListBox() 
 152     // call this just to free the client data -- and avoid leaking memory 
 160 // ---------------------------------------------------------------------------- 
 161 // adding/inserting strings 
 162 // ---------------------------------------------------------------------------- 
 164 int wxCMPFUNC_CONV 
wxListBoxSortNoCase(wxString
* s1
, wxString
* s2
) 
 166     return  s1
->CmpNoCase(*s2
); 
 169 int wxListBox::DoAppendOnly(const wxString
& item
) 
 175         m_strings
->Add(item
); 
 176         m_strings
->Sort(wxListBoxSortNoCase
); 
 177         index 
= m_strings
->Index(item
); 
 181         index 
= m_strings
->GetCount(); 
 182         m_strings
->Add(item
); 
 188 int wxListBox::DoAppend(const wxString
& item
) 
 190     size_t index 
= DoAppendOnly( item 
); 
 192     m_itemsClientData
.Insert(NULL
, index
); 
 194     m_updateScrollbarY 
= true; 
 196     if ( HasHorzScrollbar() ) 
 198         // has the max width increased? 
 200         GetTextExtent(item
, &width
, NULL
); 
 201         if ( width 
> m_maxWidth 
) 
 204             m_maxWidthItem 
= index
; 
 205             m_updateScrollbarX 
= true; 
 209     RefreshFromItemToEnd(index
); 
 214 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 216     // the position of the item being added to a sorted listbox can't be 
 218     wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") ); 
 220     size_t count 
= items
.GetCount(); 
 221     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 223         m_strings
->Insert(items
[n
], pos 
+ n
); 
 224         m_itemsClientData
.Insert(NULL
, pos 
+ n
); 
 227     // the number of items has changed so we might have to show the scrollbar 
 228     m_updateScrollbarY 
= true; 
 230     // the max width also might have changed - just recalculate it instead of 
 231     // keeping track of it here, this is probably more efficient for a typical 
 233     RefreshHorzScrollbar(); 
 235     // note that we have to refresh all the items after the ones we inserted, 
 236     // not just these items 
 237     RefreshFromItemToEnd(pos
); 
 240 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
) 
 244     size_t count 
= items
.GetCount(); 
 248     m_strings
->Alloc(count
); 
 250     m_itemsClientData
.Alloc(count
); 
 251     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 253         size_t index 
= DoAppendOnly(items
[n
]); 
 255         m_itemsClientData
.Insert(clientData 
? clientData
[n
] : NULL
, index
); 
 258     m_updateScrollbarY 
= true; 
 263 void wxListBox::SetString(int n
, const wxString
& s
) 
 265     wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") ); 
 269     if ( HasHorzScrollbar() ) 
 271         // we need to update m_maxWidth as changing the string may cause the 
 272         // horz scrollbar [dis]appear 
 275         GetTextExtent(s
, &width
, NULL
); 
 277         // it might have increased if the new string is long 
 278         if ( width 
> m_maxWidth 
) 
 282             m_updateScrollbarX 
= true; 
 284         // or also decreased if the old string was the longest one 
 285         else if ( n 
== m_maxWidthItem 
) 
 287             RefreshHorzScrollbar(); 
 294 // ---------------------------------------------------------------------------- 
 296 // ---------------------------------------------------------------------------- 
 298 void wxListBox::DoClear() 
 302     if ( HasClientObjectData() ) 
 304         size_t count 
= m_itemsClientData
.GetCount(); 
 305         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 307             delete (wxClientData 
*) m_itemsClientData
[n
]; 
 311     m_itemsClientData
.Clear(); 
 312     m_selections
.Clear(); 
 317 void wxListBox::Clear() 
 321     m_updateScrollbarY 
= true; 
 323     RefreshHorzScrollbar(); 
 328 void wxListBox::Delete(int n
) 
 330     wxCHECK_RET( n 
>= 0 && n 
< GetCount(), 
 331                  _T("invalid index in wxListBox::Delete") ); 
 333     // do it before removing the index as otherwise the last item will not be 
 334     // refreshed (as GetCount() will be decremented) 
 335     RefreshFromItemToEnd(n
); 
 337     m_strings
->RemoveAt(n
); 
 339     if ( HasClientObjectData() ) 
 341         delete (wxClientData 
*)m_itemsClientData
[n
]; 
 344     m_itemsClientData
.RemoveAt(n
); 
 346     // when the item disappears we must not keep using its index 
 347     if ( n 
== m_current 
) 
 351     else if ( n 
< m_current 
) 
 355     //else: current item may stay 
 357     // update the selections array: the indices of all seletected items after 
 358     // the one being deleted must change and the item itselfm ust be removed 
 359     int index 
= wxNOT_FOUND
; 
 360     size_t count 
= m_selections
.GetCount(); 
 361     for ( size_t item 
= 0; item 
< count
; item
++ ) 
 363         if ( m_selections
[item
] == n 
) 
 365             // remember to delete it later 
 368         else if ( m_selections
[item
] > n 
) 
 370             // to account for the index shift 
 371             m_selections
[item
]--; 
 373         //else: nothing changed for this one 
 376     if ( index 
!= wxNOT_FOUND 
) 
 378         m_selections
.RemoveAt(index
); 
 381     // the number of items has changed, hence the scrollbar may disappear 
 382     m_updateScrollbarY 
= true; 
 384     // finally, if the longest item was deleted the scrollbar may disappear 
 385     if ( n 
== m_maxWidthItem 
) 
 387         RefreshHorzScrollbar(); 
 391 // ---------------------------------------------------------------------------- 
 392 // client data handling 
 393 // ---------------------------------------------------------------------------- 
 395 void wxListBox::DoSetItemClientData(int n
, void* clientData
) 
 397     m_itemsClientData
[n
] = clientData
; 
 400 void *wxListBox::DoGetItemClientData(int n
) const 
 402     return m_itemsClientData
[n
]; 
 405 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 407     m_itemsClientData
[n
] = clientData
; 
 410 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const 
 412     return (wxClientData 
*)m_itemsClientData
[n
]; 
 415 // ---------------------------------------------------------------------------- 
 417 // ---------------------------------------------------------------------------- 
 419 void wxListBox::DoSetSelection(int n
, bool select
) 
 423         if ( m_selections
.Index(n
) == wxNOT_FOUND 
) 
 425             if ( !HasMultipleSelection() ) 
 427                 // selecting an item in a single selection listbox deselects 
 436         //else: already selected 
 440         int index 
= m_selections
.Index(n
); 
 441         if ( index 
!= wxNOT_FOUND 
) 
 443             m_selections
.RemoveAt(index
); 
 450     // sanity check: a single selection listbox can't have more than one item 
 452     wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2), 
 453                   _T("multiple selected items in single selection lbox?") ); 
 457         // the newly selected item becomes the current one 
 462 int wxListBox::GetSelection() const 
 464     wxCHECK_MSG( !HasMultipleSelection(), -1, 
 465                  _T("use wxListBox::GetSelections for ths listbox") ); 
 467     return m_selections
.IsEmpty() ? -1 : m_selections
[0]; 
 470 int wxCMPFUNC_CONV 
wxCompareInts(int *n
, int *m
) 
 475 int wxListBox::GetSelections(wxArrayInt
& selections
) const 
 477     // always return sorted array to the user 
 478     selections 
= m_selections
; 
 479     size_t count 
= m_selections
.GetCount(); 
 481     // don't call sort on an empty array 
 484         selections
.Sort(wxCompareInts
); 
 490 // ---------------------------------------------------------------------------- 
 491 // refresh logic: we use delayed refreshing which allows to avoid multiple 
 492 // refreshes (and hence flicker) in case when several listbox items are 
 493 // added/deleted/changed subsequently 
 494 // ---------------------------------------------------------------------------- 
 496 void wxListBox::RefreshFromItemToEnd(int from
) 
 498     RefreshItems(from
, GetCount() - from
); 
 501 void wxListBox::RefreshItems(int from
, int count
) 
 503     switch ( m_updateCount 
) 
 507             m_updateCount 
= count
; 
 511             // we refresh everything anyhow 
 515             // add these items to the others which we have to refresh 
 516             if ( m_updateFrom 
< from 
) 
 518                 count 
+= from 
- m_updateFrom
; 
 519                 if ( m_updateCount 
< count 
) 
 520                     m_updateCount 
= count
; 
 522             else // m_updateFrom >= from 
 524                 int updateLast 
= wxMax(m_updateFrom 
+ m_updateCount
, 
 527                 m_updateCount 
= updateLast 
- m_updateFrom
; 
 532 void wxListBox::RefreshItem(int n
) 
 534     switch ( m_updateCount 
) 
 537             // refresh this item only 
 543             // we refresh everything anyhow 
 547             // add this item to the others which we have to refresh 
 548             if ( m_updateFrom 
< n 
) 
 550                 if ( m_updateCount 
< n 
- m_updateFrom 
+ 1 ) 
 551                     m_updateCount 
= n 
- m_updateFrom 
+ 1; 
 553             else // n <= m_updateFrom 
 555                 m_updateCount 
+= m_updateFrom 
- n
; 
 561 void wxListBox::RefreshAll() 
 566 void wxListBox::RefreshHorzScrollbar() 
 568     m_maxWidth 
= 0; // recalculate it 
 569     m_updateScrollbarX 
= true; 
 572 void wxListBox::UpdateScrollbars() 
 574     wxSize size 
= GetClientSize(); 
 576     // is our height enough to show all items? 
 577     int nLines 
= GetCount(); 
 578     wxCoord lineHeight 
= GetLineHeight(); 
 579     bool showScrollbarY 
= nLines
*lineHeight 
> size
.y
; 
 581     // check the width too if required 
 582     wxCoord charWidth
, maxWidth
; 
 584     if ( HasHorzScrollbar() ) 
 586         charWidth 
= GetCharWidth(); 
 587         maxWidth 
= GetMaxWidth(); 
 588         showScrollbarX 
= maxWidth 
> size
.x
; 
 590     else // never show it 
 592         charWidth 
= maxWidth 
= 0; 
 593         showScrollbarX 
= false; 
 596     // what should be the scrollbar range now? 
 597     int scrollRangeX 
= showScrollbarX
 
 598                         ? (maxWidth 
+ charWidth 
- 1) / charWidth 
+ 2 // FIXME 
 600     int scrollRangeY 
= showScrollbarY
 
 602                             (size
.y 
% lineHeight 
+ lineHeight 
- 1) / lineHeight
 
 605     // reset scrollbars if something changed: either the visibility status 
 606     // or the range of a scrollbar which is shown 
 607     if ( (showScrollbarY 
!= m_showScrollbarY
) || 
 608          (showScrollbarX 
!= m_showScrollbarX
) || 
 609          (showScrollbarY 
&& (scrollRangeY 
!= m_scrollRangeY
)) || 
 610          (showScrollbarX 
&& (scrollRangeX 
!= m_scrollRangeX
)) ) 
 613         GetViewStart(&x
, &y
); 
 614         SetScrollbars(charWidth
, lineHeight
, 
 615                       scrollRangeX
, scrollRangeY
, 
 618         m_showScrollbarX 
= showScrollbarX
; 
 619         m_showScrollbarY 
= showScrollbarY
; 
 621         m_scrollRangeX 
= scrollRangeX
; 
 622         m_scrollRangeY 
= scrollRangeY
; 
 626 void wxListBox::UpdateItems() 
 628     // only refresh the items which must be refreshed 
 629     if ( m_updateCount 
== -1 ) 
 632         wxLogTrace(_T("listbox"), _T("Refreshing all")); 
 638         wxSize size 
= GetClientSize(); 
 641         rect
.height 
= size
.y
; 
 642         rect
.y 
+= m_updateFrom
*GetLineHeight(); 
 643         rect
.height 
= m_updateCount
*GetLineHeight(); 
 645         // we don't need to calculate x position as we always refresh the 
 647         CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
); 
 649         wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"), 
 650                    m_updateFrom
, m_updateFrom 
+ m_updateCount 
- 1, 
 651                    rect
.GetTop(), rect
.GetBottom()); 
 653         Refresh(true, &rect
); 
 657 void wxListBox::OnInternalIdle() 
 659     if ( m_updateScrollbarY 
|| m_updateScrollbarX 
) 
 664         m_updateScrollbarY 
= false; 
 667     if ( m_currentChanged 
) 
 669         DoEnsureVisible(m_current
); 
 671         m_currentChanged 
= false; 
 680     wxListBoxBase::OnInternalIdle(); 
 683 // ---------------------------------------------------------------------------- 
 685 // ---------------------------------------------------------------------------- 
 687 wxBorder 
wxListBox::GetDefaultBorder() const 
 689     return wxBORDER_SUNKEN
; 
 692 void wxListBox::DoDraw(wxControlRenderer 
*renderer
) 
 694     // adjust the DC to account for scrolling 
 695     wxDC
& dc 
= renderer
->GetDC(); 
 697     dc
.SetFont(GetFont()); 
 699     // get the update rect 
 700     wxRect rectUpdate 
= GetUpdateClientRect(); 
 703     CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
); 
 704     CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
); 
 706     // get the items which must be redrawn 
 707     wxCoord lineHeight 
= GetLineHeight(); 
 708     size_t itemFirst 
= yTop 
/ lineHeight
, 
 709            itemLast 
= (yBottom 
+ lineHeight 
- 1) / lineHeight
, 
 710            itemMax 
= m_strings
->GetCount(); 
 712     if ( itemFirst 
>= itemMax 
) 
 715     if ( itemLast 
> itemMax 
) 
 719     wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"), 
 720                itemFirst
, itemLast
); 
 722     DoDrawRange(renderer
, itemFirst
, itemLast
); 
 725 void wxListBox::DoDrawRange(wxControlRenderer 
*renderer
, 
 726                             int itemFirst
, int itemLast
) 
 728     renderer
->DrawItems(this, itemFirst
, itemLast
); 
 731 // ---------------------------------------------------------------------------- 
 733 // ---------------------------------------------------------------------------- 
 735 bool wxListBox::SetFont(const wxFont
& font
) 
 737     if ( !wxControl::SetFont(font
) ) 
 747 void wxListBox::CalcItemsPerPage() 
 749     m_lineHeight 
= GetRenderer()->GetListboxItemHeight(GetCharHeight()); 
 750     m_itemsPerPage 
= GetClientSize().y 
/ m_lineHeight
; 
 753 int wxListBox::GetItemsPerPage() const 
 755     if ( !m_itemsPerPage 
) 
 757         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 760     return m_itemsPerPage
; 
 763 wxCoord 
wxListBox::GetLineHeight() const 
 767         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 773 wxCoord 
wxListBox::GetMaxWidth() const 
 775     if ( m_maxWidth 
== 0 ) 
 777         wxListBox 
*self 
= wxConstCast(this, wxListBox
); 
 779         size_t count 
= m_strings
->GetCount(); 
 780         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 782             GetTextExtent(this->GetString(n
), &width
, NULL
); 
 783             if ( width 
> m_maxWidth 
) 
 785                 self
->m_maxWidth 
= width
; 
 786                 self
->m_maxWidthItem 
= n
; 
 794 void wxListBox::OnSize(wxSizeEvent
& event
) 
 796     // recalculate the number of items per page 
 799     // the scrollbars might [dis]appear 
 801     m_updateScrollbarY 
= true; 
 806 void wxListBox::DoSetFirstItem(int n
) 
 811 void wxListBox::DoSetSize(int x
, int y
, 
 812                           int width
, int height
, 
 815     if ( GetWindowStyle() & wxLB_INT_HEIGHT 
) 
 817         // we must round up the height to an entire number of rows 
 819         // the client area must contain an int number of rows, so take borders 
 821         wxRect rectBorders 
= GetRenderer()->GetBorderDimensions(GetBorder()); 
 822         wxCoord hBorders 
= rectBorders
.y 
+ rectBorders
.height
; 
 824         wxCoord hLine 
= GetLineHeight(); 
 825         height 
= ((height 
- hBorders 
+ hLine 
- 1) / hLine
)*hLine 
+ hBorders
; 
 828     wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 831 wxSize 
wxListBox::DoGetBestClientSize() const 
 836     size_t count 
= m_strings
->GetCount(); 
 837     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 840         GetTextExtent(this->GetString(n
), &w
, &h
); 
 848     // if the listbox is empty, still give it some non zero (even if 
 849     // arbitrary) size - otherwise, leave small margin around the strings 
 853         width 
+= 3*GetCharWidth(); 
 856         height 
= GetCharHeight(); 
 858     // we need the height of the entire listbox, not just of one line 
 859     height 
*= wxMax(count
, 7); 
 861     return wxSize(width
, height
); 
 864 // ---------------------------------------------------------------------------- 
 866 // ---------------------------------------------------------------------------- 
 868 bool wxListBox::SendEvent(wxEventType type
, int item
) 
 870     wxCommandEvent 
event(type
, m_windowId
); 
 871     event
.SetEventObject(this); 
 873     // use the current item by default 
 879     // client data and string parameters only make sense if we have an item 
 882         if ( HasClientObjectData() ) 
 883             event
.SetClientObject(GetClientObject(item
)); 
 884         else if ( HasClientUntypedData() ) 
 885             event
.SetClientData(GetClientData(item
)); 
 887         event
.SetString(GetString(item
)); 
 892     return GetEventHandler()->ProcessEvent(event
); 
 895 void wxListBox::SetCurrentItem(int n
) 
 897     if ( n 
!= m_current 
) 
 899         if ( m_current 
!= -1 ) 
 900             RefreshItem(m_current
); 
 904         if ( m_current 
!= -1 ) 
 906             m_currentChanged 
= true; 
 908             RefreshItem(m_current
); 
 911     //else: nothing to do 
 914 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 916     int count 
= GetCount(); 
 919         // empty listbox, we can't find anything in it 
 923     // start either from the current item or from the next one if strictlyAfter 
 928         // the following line will set first correctly to 0 if there is no 
 929         // selection (m_current == -1) 
 930         first 
= m_current 
== count 
- 1 ? 0 : m_current 
+ 1; 
 932     else // start with the current 
 934         first 
= m_current 
== -1 ? 0 : m_current
; 
 937     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 939     // if this is not true we'd never exit from the loop below! 
 940     wxASSERT_MSG( first 
< count 
&& last 
< count
, _T("logic error") ); 
 942     // precompute it outside the loop 
 943     size_t len 
= prefix
.length(); 
 945     // loop over all items in the listbox 
 946     for ( int item 
= first
; item 
!= last
; item 
< count 
- 1 ? item
++ : item 
= 0 ) 
 948         if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 ) 
 950             SetCurrentItem(item
); 
 952             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 955                 SelectAndNotify(item
); 
 957                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 958                     AnchorSelection(item
); 
 969 void wxListBox::EnsureVisible(int n
) 
 971     if ( m_updateScrollbarY 
) 
 976         m_updateScrollbarY 
= false; 
 982 void wxListBox::DoEnsureVisible(int n
) 
 984     if ( !m_showScrollbarY 
) 
 986         // nothing to do - everything is shown anyhow 
 991     GetViewStart(0, &first
); 
 994         // we need to scroll upwards, so make the current item appear on top 
 995         // of the shown range 
1000         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
1003             // scroll down: the current item appears at the bottom of the 
1005             Scroll(0, n 
- (last 
- first
)); 
1010 void wxListBox::ChangeCurrent(int diff
) 
1012     int current 
= m_current 
== -1 ? 0 : m_current
; 
1016     int last 
= GetCount() - 1; 
1019     else if ( current 
> last 
) 
1022     SetCurrentItem(current
); 
1025 void wxListBox::ExtendSelection(int itemTo
) 
1027     // if we don't have the explicit values for selection start/end, make them 
1029     if ( m_selAnchor 
== -1 ) 
1030         m_selAnchor 
= m_current
; 
1035     // swap the start/end of selection range if necessary 
1036     int itemFrom 
= m_selAnchor
; 
1037     if ( itemFrom 
> itemTo 
) 
1039         int itemTmp 
= itemFrom
; 
1044     // the selection should now include all items in the range between the 
1045     // anchor and the specified item and only them 
1048     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
1053     for ( ; n 
<= itemTo
; n
++ ) 
1058     int count 
= GetCount(); 
1059     for ( ; n 
< count
; n
++ ) 
1065 void wxListBox::DoSelect(int item
, bool sel
) 
1069         // go to this item first 
1070         SetCurrentItem(item
); 
1073     // the current item is the one we want to change: either it was just 
1074     // changed above to be the same as item or item == -1 in which we case we 
1075     // are supposed to use the current one anyhow 
1076     if ( m_current 
!= -1 ) 
1079         SetSelection(m_current
, sel
); 
1083 void wxListBox::SelectAndNotify(int item
) 
1087     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1090 void wxListBox::Activate(int item
) 
1093         SetCurrentItem(item
); 
1097     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1106         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1110 // ---------------------------------------------------------------------------- 
1112 // ---------------------------------------------------------------------------- 
1115    The numArg here is the listbox item index while the strArg is used 
1116    differently for the different actions: 
1118    a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string 
1121    b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used 
1122       to decide if the listbox should send the notification event (it is empty) 
1123       or not (it is not): this allows us to reuse the same action for when the 
1124       user is dragging the mouse when it has been released although in the 
1125       first case no notification is sent while in the second it is sent. 
1127 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1129                               const wxString
& strArg
) 
1131     int item 
= (int)numArg
; 
1133     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1135         SetCurrentItem(item
); 
1137     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1141     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1146         if ( IsSelected(item
) ) 
1149             SelectAndNotify(item
); 
1151     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1155         if ( strArg
.empty() ) 
1156             SelectAndNotify(item
); 
1160     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1162     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1164     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1166     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1168     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1169         ChangeCurrent(GetItemsPerPage()); 
1170     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1171         ChangeCurrent(-GetItemsPerPage()); 
1172     else if ( action 
== wxACTION_LISTBOX_START 
) 
1174     else if ( action 
== wxACTION_LISTBOX_END 
) 
1175         SetCurrentItem(GetCount() - 1); 
1176     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1178     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1179         ExtendSelection(item
); 
1180     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1181         FindNextItem(strArg
); 
1182     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1183         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1184     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1185               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1186         wxFAIL_MSG(_T("unimplemented yet")); 
1188         return wxControl::PerformAction(action
, numArg
, strArg
); 
1193 // ============================================================================ 
1194 // implementation of wxStdListboxInputHandler 
1195 // ============================================================================ 
1197 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1198                                                    bool toggleOnPressAlways
) 
1199                         : wxStdInputHandler(handler
) 
1202     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1203     m_actionMouse 
= wxACTION_NONE
; 
1204     m_trackMouseOutside 
= true; 
1207 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1208                                       const wxMouseEvent
& event
) 
1210     int item 
= HitTestUnsafe(lbox
, event
); 
1212     return FixItemIndex(lbox
, item
); 
1215 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1216                                             const wxMouseEvent
& event
) 
1218     wxPoint pt 
= event
.GetPosition(); 
1219     pt 
-= lbox
->GetClientAreaOrigin(); 
1221     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1222     return y 
/ lbox
->GetLineHeight(); 
1225 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1230         // mouse is above the first item 
1233     else if ( item 
>= lbox
->GetCount() ) 
1235         // mouse is below the last item 
1236         item 
= lbox
->GetCount() - 1; 
1242 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1244     return item 
>= 0 && item 
< lbox
->GetCount(); 
1248 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1249                                        const wxMouseEvent
& event
, 
1252     // we currently only allow selecting with the left mouse button, if we 
1253     // do need to allow using other buttons too we might use the code 
1256     m_btnCapture 
= event
.LeftDown() 
1265     wxControlAction action
; 
1266     if ( lbox
->HasMultipleSelection() ) 
1268         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1270             if ( m_toggleOnPressAlways 
) 
1272                 // toggle the item right now 
1273                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1277             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1279         else // wxLB_EXTENDED listbox 
1281             // simple click in an extended sel listbox clears the old 
1282             // selection and adds the clicked item to it then, ctrl-click 
1283             // toggles an item to it and shift-click adds a range between 
1284             // the old selection anchor and the clicked item 
1285             if ( event
.ControlDown() ) 
1287                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1289                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1291             else if ( event
.ShiftDown() ) 
1293                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1295             else // simple click 
1297                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1299                 action 
= wxACTION_LISTBOX_SELECT
; 
1302             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1305     else // single selection 
1308         action 
= wxACTION_LISTBOX_SELECT
; 
1311     // by default we always do track it 
1312     m_trackMouseOutside 
= true; 
1317 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1318                                          const wxKeyEvent
& event
, 
1321     // we're only interested in the key press events 
1322     if ( pressed 
&& !event
.AltDown() ) 
1324         bool isMoveCmd 
= true; 
1325         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1327         wxControlAction action
; 
1330         int keycode 
= event
.GetKeyCode(); 
1335                 action 
= wxACTION_LISTBOX_MOVEUP
; 
1339                 action 
= wxACTION_LISTBOX_MOVEDOWN
; 
1345                 action 
= wxACTION_LISTBOX_PAGEUP
; 
1351                 action 
= wxACTION_LISTBOX_PAGEDOWN
; 
1355                 action 
= wxACTION_LISTBOX_START
; 
1359                 action 
= wxACTION_LISTBOX_END
; 
1364                 if ( style 
& wxLB_MULTIPLE 
) 
1366                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1372                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1377                 if ( (keycode 
< 255) && wxIsalnum((wxChar
)keycode
) ) 
1379                     action 
= wxACTION_LISTBOX_FIND
; 
1380                     strArg 
= (wxChar
)keycode
; 
1384         if ( !action
.IsEmpty() ) 
1386             consumer
->PerformAction(action
, -1, strArg
); 
1390                 if ( style 
& wxLB_SINGLE 
) 
1392                     // the current item is always the one selected 
1393                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1395                 else if ( style 
& wxLB_EXTENDED 
) 
1397                     if ( event
.ShiftDown() ) 
1398                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1401                         // select the item and make it the new selection anchor 
1402                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1403                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1406                 //else: nothing to do for multiple selection listboxes 
1413     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1416 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1417                                            const wxMouseEvent
& event
) 
1419     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1420     int item 
= HitTest(lbox
, event
); 
1421     wxControlAction action
; 
1423     // when the left mouse button is pressed, capture the mouse and track the 
1424     // item under mouse (if the mouse leaves the window, we will still be 
1425     // getting the mouse move messages generated by wxScrollWindow) 
1426     if ( event
.LeftDown() ) 
1428         // capture the mouse to track the selected item 
1429         lbox
->CaptureMouse(); 
1431         action 
= SetupCapture(lbox
, event
, item
); 
1433     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1435         // when the left mouse button is released, release the mouse too 
1436         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1439             winCapture
->ReleaseMouse(); 
1442             action 
= m_actionMouse
; 
1444         //else: the mouse wasn't presed over the listbox, only released here 
1446     else if ( event
.LeftDClick() ) 
1448         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1451     if ( !action
.IsEmpty() ) 
1453         lbox
->PerformAction(action
, item
); 
1458     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1461 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1462                                                const wxMouseEvent
& event
) 
1464     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1465     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1467         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1469         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1471             // someone captured the mouse for us (we always set m_btnCapture 
1472             // when we do it ourselves): in this case we only react to 
1473             // the mouse messages when they happen inside the listbox 
1474             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1478         int item 
= HitTest(lbox
, event
); 
1479         if ( !m_btnCapture 
) 
1481             // now that we have the mouse inside the listbox, do capture it 
1482             // normally - but ensure that we will still ignore the outside 
1484             SetupCapture(lbox
, event
, item
); 
1486             m_trackMouseOutside 
= false; 
1489         if ( IsValidIndex(lbox
, item
) ) 
1491             // pass something into strArg to tell the listbox that it shouldn't 
1492             // send the notification message: see PerformAction() above 
1493             lbox
->PerformAction(m_actionMouse
, item
, _T("no")); 
1495         // else: don't pass invalid index to the listbox 
1497     else // we don't have capture any more 
1501             // if we lost capture unexpectedly (someone else took the capture 
1502             // from us), return to a consistent state 
1507     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1510 #endif // wxUSE_LISTBOX