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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "univlistbox.h" 
  24 #include "wx/wxprec.h" 
  35     #include "wx/dcclient.h" 
  36     #include "wx/listbox.h" 
  37     #include "wx/validate.h" 
  40 #include "wx/univ/renderer.h" 
  41 #include "wx/univ/inphand.h" 
  42 #include "wx/univ/theme.h" 
  44 // ============================================================================ 
  45 // implementation of wxListBox 
  46 // ============================================================================ 
  48 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
  50 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
) 
  51     EVT_SIZE(wxListBox::OnSize
) 
  54 // ---------------------------------------------------------------------------- 
  56 // ---------------------------------------------------------------------------- 
  58 void wxListBox::Init() 
  60     // will be calculated later when needed 
  67     // no items hence no current item 
  70     m_currentChanged 
= FALSE
; 
  72     // no need to update anything initially 
  75     // no scrollbars to show nor update 
  79     m_showScrollbarY 
= FALSE
; 
  82 bool wxListBox::Create(wxWindow 
*parent
, 
  87                        const wxString choices
[], 
  89                        const wxValidator
& validator
, 
  92     // for compatibility accept both the new and old styles - they mean the 
  94     if ( style 
& wxLB_ALWAYS_SB 
) 
  95         style 
|= wxALWAYS_SHOW_SB
; 
  97     // if we don't have neither multiple nor extended flag, we must have the 
  98     // single selection listbox 
  99     if ( !(style 
& (wxLB_MULTIPLE 
| wxLB_EXTENDED
)) ) 
 100         style 
|= wxLB_SINGLE
; 
 102 #if wxUSE_TWO_WINDOWS 
 103     style 
|=  wxVSCROLL
|wxHSCROLL
; 
 104     if ((style 
& wxBORDER_MASK
) == 0) 
 105         style 
|= wxBORDER_SUNKEN
; 
 108     if ( !wxControl::Create(parent
, id
, pos
, size
, style
,  
 114     if ( style 
& wxLB_SORT 
) 
 115         m_strings 
= wxArrayString(TRUE 
/* auto sort */); 
 121     CreateInputHandler(wxINP_HANDLER_LISTBOX
); 
 126 wxListBox::~wxListBox() 
 128     // call this just to free the client data -- and avoid leaking memory 
 132 // ---------------------------------------------------------------------------- 
 133 // adding/inserting strings 
 134 // ---------------------------------------------------------------------------- 
 136 int wxListBox::DoAppend(const wxString
& item
) 
 138     size_t index 
= m_strings
.Add(item
); 
 139     m_itemsClientData
.Insert(NULL
, index
); 
 141     m_updateScrollbarY 
= TRUE
; 
 143     if ( HasHorzScrollbar() ) 
 145         // has the max width increased? 
 147         GetTextExtent(item
, &width
, NULL
); 
 148         if ( width 
> m_maxWidth 
) 
 151             m_maxWidthItem 
= index
; 
 152             m_updateScrollbarX 
= TRUE
; 
 156     RefreshFromItemToEnd(index
); 
 161 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 163     // the position of the item being added to a sorted listbox can't be 
 165     wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") ); 
 167     size_t count 
= items
.GetCount(); 
 168     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 170         m_strings
.Insert(items
[n
], pos 
+ n
); 
 171         m_itemsClientData
.Insert(NULL
, pos 
+ n
); 
 174     // the number of items has changed so we might have to show the scrollbar 
 175     m_updateScrollbarY 
= TRUE
; 
 177     // the max width also might have changed - just recalculate it instead of 
 178     // keeping track of it here, this is probably more efficient for a typical 
 180     RefreshHorzScrollbar(); 
 182     // note that we have to refresh all the items after the ones we inserted, 
 183     // not just these items 
 184     RefreshFromItemToEnd(pos
); 
 187 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
) 
 191     size_t count 
= items
.GetCount(); 
 195     m_strings
.Alloc(count
); 
 196     m_itemsClientData
.Alloc(count
); 
 197     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 199         size_t index 
= m_strings
.Add(items
[n
]); 
 200         m_itemsClientData
.Insert(clientData 
? clientData
[n
] : NULL
, index
); 
 203     m_updateScrollbarY 
= TRUE
; 
 208 void wxListBox::SetString(int n
, const wxString
& s
) 
 210     if ( HasHorzScrollbar() ) 
 212         // we need to update m_maxWidth as changing the string may cause the 
 213         // horz scrollbar [dis]appear 
 216         GetTextExtent(s
, &width
, NULL
); 
 218         // it might have increased if the new string is long 
 219         if ( width 
> m_maxWidth 
) 
 223             m_updateScrollbarX 
= TRUE
; 
 225         // or also decreased if the old string was the longest one 
 226         else if ( n 
== m_maxWidthItem 
) 
 228             RefreshHorzScrollbar(); 
 231     else // no horz scrollbar 
 239 // ---------------------------------------------------------------------------- 
 241 // ---------------------------------------------------------------------------- 
 243 void wxListBox::DoClear() 
 247     if ( HasClientObjectData() ) 
 249         size_t count 
= m_itemsClientData
.GetCount(); 
 250         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 252             delete (wxClientData 
*) m_itemsClientData
[n
]; 
 256     m_itemsClientData
.Clear(); 
 257     m_selections
.Clear(); 
 262 void wxListBox::Clear() 
 266     m_updateScrollbarY 
= TRUE
; 
 268     RefreshHorzScrollbar(); 
 273 void wxListBox::Delete(int n
) 
 275     wxCHECK_RET( n 
>= 0 && n 
< GetCount(), 
 276                  _T("invalid index in wxListBox::Delete") ); 
 278     // do it before removing the index as otherwise the last item will not be 
 279     // refreshed (as GetCount() will be decremented) 
 280     RefreshFromItemToEnd(n
); 
 282     m_strings
.RemoveAt(n
); 
 284     if ( HasClientObjectData() ) 
 286         delete (wxClientData 
*)m_itemsClientData
[n
]; 
 289     m_itemsClientData
.RemoveAt(n
); 
 291     // when the item disappears we must not keep using its index 
 292     if ( n 
== m_current 
) 
 296     else if ( n 
< m_current 
) 
 300     //else: current item may stay 
 302     // update the selections array: the indices of all seletected items after 
 303     // the one being deleted must change and the item itselfm ust be removed 
 304     int index 
= wxNOT_FOUND
; 
 305     size_t count 
= m_selections
.GetCount(); 
 306     for ( size_t item 
= 0; item 
< count
; item
++ ) 
 308         if ( m_selections
[item
] == n 
) 
 310             // remember to delete it later 
 313         else if ( m_selections
[item
] > n 
) 
 315             // to account for the index shift 
 316             m_selections
[item
]--; 
 318         //else: nothing changed for this one 
 321     if ( index 
!= wxNOT_FOUND 
) 
 323         m_selections
.RemoveAt(index
); 
 326     // the number of items has changed, hence the scrollbar may disappear 
 327     m_updateScrollbarY 
= TRUE
; 
 329     // finally, if the longest item was deleted the scrollbar may disappear 
 330     if ( n 
== m_maxWidthItem 
) 
 332         RefreshHorzScrollbar(); 
 336 // ---------------------------------------------------------------------------- 
 337 // client data handling 
 338 // ---------------------------------------------------------------------------- 
 340 void wxListBox::DoSetItemClientData(int n
, void* clientData
) 
 342     m_itemsClientData
[n
] = clientData
; 
 345 void *wxListBox::DoGetItemClientData(int n
) const 
 347     return m_itemsClientData
[n
]; 
 350 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 352     m_itemsClientData
[n
] = clientData
; 
 355 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const 
 357     return (wxClientData 
*)m_itemsClientData
[n
]; 
 360 // ---------------------------------------------------------------------------- 
 362 // ---------------------------------------------------------------------------- 
 364 void wxListBox::SetSelection(int n
, bool select
) 
 368         if ( m_selections
.Index(n
) == wxNOT_FOUND 
) 
 370             if ( !HasMultipleSelection() ) 
 372                 // selecting an item in a single selection listbox deselects 
 381         //else: already selected 
 385         int index 
= m_selections
.Index(n
); 
 386         if ( index 
!= wxNOT_FOUND 
) 
 388             m_selections
.RemoveAt(index
); 
 395     // sanity check: a single selection listbox can't have more than one item 
 397     wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2), 
 398                   _T("multiple selected items in single selection lbox?") ); 
 402         // the newly selected item becomes the current one 
 407 int wxListBox::GetSelection() const 
 409     wxCHECK_MSG( !HasMultipleSelection(), -1, 
 410                  _T("use wxListBox::GetSelections for ths listbox") ); 
 412     return m_selections
.IsEmpty() ? -1 : m_selections
[0]; 
 415 int wxCMPFUNC_CONV 
wxCompareInts(int *n
, int *m
) 
 420 int wxListBox::GetSelections(wxArrayInt
& selections
) const 
 422     // always return sorted array to the user 
 423     selections 
= m_selections
; 
 424     size_t count 
= m_selections
.GetCount(); 
 426     // don't call sort on an empty array 
 429         selections
.Sort(wxCompareInts
); 
 435 // ---------------------------------------------------------------------------- 
 436 // refresh logic: we use delayed refreshing which allows to avoid multiple 
 437 // refreshes (and hence flicker) in case when several listbox items are 
 438 // added/deleted/changed subsequently 
 439 // ---------------------------------------------------------------------------- 
 441 void wxListBox::RefreshFromItemToEnd(int from
) 
 443     RefreshItems(from
, GetCount() - from
); 
 446 void wxListBox::RefreshItems(int from
, int count
) 
 448     switch ( m_updateCount 
) 
 452             m_updateCount 
= count
; 
 456             // we refresh everything anyhow 
 460             // add these items to the others which we have to refresh 
 461             if ( m_updateFrom 
< from 
) 
 463                 count 
+= from 
- m_updateFrom
; 
 464                 if ( m_updateCount 
< count 
) 
 465                     m_updateCount 
= count
; 
 467             else // m_updateFrom >= from 
 469                 int updateLast 
= wxMax(m_updateFrom 
+ m_updateCount
, 
 472                 m_updateCount 
= updateLast 
- m_updateFrom
; 
 477 void wxListBox::RefreshItem(int n
) 
 479     switch ( m_updateCount 
) 
 482             // refresh this item only 
 488             // we refresh everything anyhow 
 492             // add this item to the others which we have to refresh 
 493             if ( m_updateFrom 
< n 
) 
 495                 if ( m_updateCount 
< n 
- m_updateFrom 
+ 1 ) 
 496                     m_updateCount 
= n 
- m_updateFrom 
+ 1; 
 498             else // n <= m_updateFrom 
 500                 m_updateCount 
+= m_updateFrom 
- n
; 
 506 void wxListBox::RefreshAll() 
 511 void wxListBox::RefreshHorzScrollbar() 
 513     m_maxWidth 
= 0; // recalculate it 
 514     m_updateScrollbarX 
= TRUE
; 
 517 void wxListBox::UpdateScrollbars() 
 519     wxSize size 
= GetClientSize(); 
 521     // is our height enough to show all items? 
 522     int nLines 
= GetCount(); 
 523     wxCoord lineHeight 
= GetLineHeight(); 
 524     bool showScrollbarY 
= nLines
*lineHeight 
> size
.y
; 
 526     // check the width too if required 
 527     wxCoord charWidth
, maxWidth
; 
 529     if ( HasHorzScrollbar() ) 
 531         charWidth 
= GetCharWidth(); 
 532         maxWidth 
= GetMaxWidth(); 
 533         showScrollbarX 
= maxWidth 
> size
.x
; 
 535     else // never show it 
 537         charWidth 
= maxWidth 
= 0; 
 538         showScrollbarX 
= FALSE
; 
 541     // what should be the scrollbar range now? 
 542     int scrollRangeX 
= showScrollbarX
 
 543                         ? (maxWidth 
+ charWidth 
- 1) / charWidth 
+ 2 // FIXME 
 545     int scrollRangeY 
= showScrollbarY
 
 547                             (size
.y 
% lineHeight 
+ lineHeight 
- 1) / lineHeight
 
 550     // reset scrollbars if something changed: either the visibility status 
 551     // or the range of a scrollbar which is shown 
 552     if ( (showScrollbarY 
!= m_showScrollbarY
) || 
 553          (showScrollbarX 
!= m_showScrollbarX
) || 
 554          (showScrollbarY 
&& (scrollRangeY 
!= m_scrollRangeY
)) || 
 555          (showScrollbarX 
&& (scrollRangeX 
!= m_scrollRangeX
)) ) 
 558         GetViewStart(&x
, &y
); 
 559         SetScrollbars(charWidth
, lineHeight
, 
 560                       scrollRangeX
, scrollRangeY
, 
 563         m_showScrollbarX 
= showScrollbarX
; 
 564         m_showScrollbarY 
= showScrollbarY
; 
 566         m_scrollRangeX 
= scrollRangeX
; 
 567         m_scrollRangeY 
= scrollRangeY
; 
 571 void wxListBox::UpdateItems() 
 573     // only refresh the items which must be refreshed 
 574     if ( m_updateCount 
== -1 ) 
 577         wxLogTrace(_T("listbox"), _T("Refreshing all")); 
 583         wxSize size 
= GetClientSize(); 
 586         rect
.height 
= size
.y
; 
 587         rect
.y 
+= m_updateFrom
*GetLineHeight(); 
 588         rect
.height 
= m_updateCount
*GetLineHeight(); 
 590         // we don't need to calculate x position as we always refresh the 
 592         CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
); 
 594         wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"), 
 595                    m_updateFrom
, m_updateFrom 
+ m_updateCount 
- 1, 
 596                    rect
.GetTop(), rect
.GetBottom()); 
 598         Refresh(TRUE
, &rect
); 
 602 void wxListBox::OnInternalIdle() 
 604     if ( m_updateScrollbarY 
|| m_updateScrollbarX 
) 
 609         m_updateScrollbarY 
= FALSE
; 
 612     if ( m_currentChanged 
) 
 614         DoEnsureVisible(m_current
); 
 616         m_currentChanged 
= FALSE
; 
 625     wxListBoxBase::OnInternalIdle(); 
 628 // ---------------------------------------------------------------------------- 
 630 // ---------------------------------------------------------------------------- 
 632 wxBorder 
wxListBox::GetDefaultBorder() const 
 634     return wxBORDER_SUNKEN
; 
 637 void wxListBox::DoDraw(wxControlRenderer 
*renderer
) 
 639     // adjust the DC to account for scrolling 
 640     wxDC
& dc 
= renderer
->GetDC(); 
 642     dc
.SetFont(GetFont()); 
 644     // get the update rect 
 645     wxRect rectUpdate 
= GetUpdateClientRect(); 
 648     CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
); 
 649     CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
); 
 651     // get the items which must be redrawn 
 652     wxCoord lineHeight 
= GetLineHeight(); 
 653     size_t itemFirst 
= yTop 
/ lineHeight
, 
 654            itemLast 
= (yBottom 
+ lineHeight 
- 1) / lineHeight
, 
 655            itemMax 
= m_strings
.GetCount(); 
 657     if ( itemFirst 
>= itemMax 
) 
 660     if ( itemLast 
> itemMax 
) 
 664     wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"), 
 665                itemFirst
, itemLast
); 
 667     DoDrawRange(renderer
, itemFirst
, itemLast
); 
 670 void wxListBox::DoDrawRange(wxControlRenderer 
*renderer
, 
 671                             int itemFirst
, int itemLast
) 
 673     renderer
->DrawItems(this, itemFirst
, itemLast
); 
 676 // ---------------------------------------------------------------------------- 
 678 // ---------------------------------------------------------------------------- 
 680 bool wxListBox::SetFont(const wxFont
& font
) 
 682     if ( !wxControl::SetFont(font
) ) 
 692 void wxListBox::CalcItemsPerPage() 
 694     m_lineHeight 
= GetRenderer()->GetListboxItemHeight(GetCharHeight()); 
 695     m_itemsPerPage 
= GetClientSize().y 
/ m_lineHeight
; 
 698 int wxListBox::GetItemsPerPage() const 
 700     if ( !m_itemsPerPage 
) 
 702         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 705     return m_itemsPerPage
; 
 708 wxCoord 
wxListBox::GetLineHeight() const 
 712         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 718 wxCoord 
wxListBox::GetMaxWidth() const 
 720     if ( m_maxWidth 
== 0 ) 
 722         wxListBox 
*self 
= wxConstCast(this, wxListBox
); 
 724         size_t count 
= m_strings
.GetCount(); 
 725         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 727             GetTextExtent(m_strings
[n
], &width
, NULL
); 
 728             if ( width 
> m_maxWidth 
) 
 730                 self
->m_maxWidth 
= width
; 
 731                 self
->m_maxWidthItem 
= n
; 
 739 void wxListBox::OnSize(wxSizeEvent
& event
) 
 741     // recalculate the number of items per page 
 744     // the scrollbars might [dis]appear 
 746     m_updateScrollbarY 
= TRUE
; 
 751 void wxListBox::DoSetFirstItem(int n
) 
 756 void wxListBox::DoSetSize(int x
, int y
, 
 757                           int width
, int height
, 
 760     if ( GetWindowStyle() & wxLB_INT_HEIGHT 
) 
 762         // we must round up the height to an entire number of rows 
 764         // the client area must contain an int number of rows, so take borders 
 766         wxRect rectBorders 
= GetRenderer()->GetBorderDimensions(GetBorder()); 
 767         wxCoord hBorders 
= rectBorders
.y 
+ rectBorders
.height
; 
 769         wxCoord hLine 
= GetLineHeight(); 
 770         height 
= ((height 
- hBorders 
+ hLine 
- 1) / hLine
)*hLine 
+ hBorders
; 
 773     wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 776 wxSize 
wxListBox::DoGetBestClientSize() const 
 781     size_t count 
= m_strings
.GetCount(); 
 782     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 785         GetTextExtent(m_strings
[n
], &w
, &h
); 
 793     // if the listbox is empty, still give it some non zero (even if 
 794     // arbitrary) size - otherwise, leave small margin around the strings 
 798         width 
+= 3*GetCharWidth(); 
 801         height 
= GetCharHeight(); 
 803     // we need the height of the entire listbox, not just of one line 
 804     height 
*= wxMax(count
, 7); 
 806     return wxSize(width
, height
); 
 809 // ---------------------------------------------------------------------------- 
 811 // ---------------------------------------------------------------------------- 
 813 bool wxListBox::SendEvent(wxEventType type
, int item
) 
 815     wxCommandEvent 
event(type
, m_windowId
); 
 816     event
.SetEventObject(this); 
 818     // use the current item by default 
 824     // client data and string parameters only make sense if we have an item 
 827         if ( HasClientObjectData() ) 
 828             event
.SetClientObject(GetClientObject(item
)); 
 829         else if ( HasClientUntypedData() ) 
 830             event
.SetClientData(GetClientData(item
)); 
 832         event
.SetString(GetString(item
)); 
 835     event
.m_commandInt 
= item
; 
 837     return GetEventHandler()->ProcessEvent(event
); 
 840 void wxListBox::SetCurrentItem(int n
) 
 842     if ( n 
!= m_current 
) 
 844         if ( m_current 
!= -1 ) 
 845             RefreshItem(m_current
); 
 849         if ( m_current 
!= -1 ) 
 851             m_currentChanged 
= TRUE
; 
 853             RefreshItem(m_current
); 
 856     //else: nothing to do 
 859 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 861     int count 
= GetCount(); 
 864         // empty listbox, we can't find anything in it 
 868     // start either from the current item or from the next one if strictlyAfter 
 873         // the following line will set first correctly to 0 if there is no 
 874         // selection (m_current == -1) 
 875         first 
= m_current 
== count 
- 1 ? 0 : m_current 
+ 1; 
 877     else // start with the current 
 879         first 
= m_current 
== -1 ? 0 : m_current
; 
 882     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 884     // if this is not true we'd never exit from the loop below! 
 885     wxASSERT_MSG( first 
< count 
&& last 
< count
, _T("logic error") ); 
 887     // precompute it outside the loop 
 888     size_t len 
= prefix
.length(); 
 890     // loop over all items in the listbox 
 891     for ( int item 
= first
; item 
!= last
; item 
< count 
- 1 ? item
++ : item 
= 0 ) 
 893         if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 ) 
 895             SetCurrentItem(item
); 
 897             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 900                 SelectAndNotify(item
); 
 902                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 903                     AnchorSelection(item
); 
 914 void wxListBox::EnsureVisible(int n
) 
 916     if ( m_updateScrollbarY 
) 
 921         m_updateScrollbarY 
= FALSE
; 
 927 void wxListBox::DoEnsureVisible(int n
) 
 929     if ( !m_showScrollbarY 
) 
 931         // nothing to do - everything is shown anyhow 
 936     GetViewStart(0, &first
); 
 939         // we need to scroll upwards, so make the current item appear on top 
 940         // of the shown range 
 945         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
 948             // scroll down: the current item appears at the bottom of the 
 950             Scroll(0, n 
- (last 
- first
)); 
 955 void wxListBox::ChangeCurrent(int diff
) 
 957     int current 
= m_current 
== -1 ? 0 : m_current
; 
 961     int last 
= GetCount() - 1; 
 964     else if ( current 
> last 
) 
 967     SetCurrentItem(current
); 
 970 void wxListBox::ExtendSelection(int itemTo
) 
 972     // if we don't have the explicit values for selection start/end, make them 
 974     if ( m_selAnchor 
== -1 ) 
 975         m_selAnchor 
= m_current
; 
 980     // swap the start/end of selection range if necessary 
 981     int itemFrom 
= m_selAnchor
; 
 982     if ( itemFrom 
> itemTo 
) 
 984         int itemTmp 
= itemFrom
; 
 989     // the selection should now include all items in the range between the 
 990     // anchor and the specified item and only them 
 993     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
 998     for ( ; n 
<= itemTo
; n
++ ) 
1003     int count 
= GetCount(); 
1004     for ( ; n 
< count
; n
++ ) 
1010 void wxListBox::DoSelect(int item
, bool sel
) 
1014         // go to this item first 
1015         SetCurrentItem(item
); 
1018     // the current item is the one we want to change: either it was just 
1019     // changed above to be the same as item or item == -1 in which we case we 
1020     // are supposed to use the current one anyhow 
1021     if ( m_current 
!= -1 ) 
1024         SetSelection(m_current
, sel
); 
1028 void wxListBox::SelectAndNotify(int item
) 
1032     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1035 void wxListBox::Activate(int item
) 
1038         SetCurrentItem(item
); 
1042     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1051         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1055 // ---------------------------------------------------------------------------- 
1057 // ---------------------------------------------------------------------------- 
1060    The numArg here is the listbox item index while the strArg is used 
1061    differently for the different actions: 
1063    a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string 
1066    b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used 
1067       to decide if the listbox should send the notification event (it is empty) 
1068       or not (it is not): this allows us to reuse the same action for when the 
1069       user is dragging the mouse when it has been released although in the 
1070       first case no notification is sent while in the second it is sent. 
1072 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1074                               const wxString
& strArg
) 
1076     int item 
= (int)numArg
; 
1078     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1080         SetCurrentItem(item
); 
1082     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1086     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1091         if ( IsSelected(item
) ) 
1094             SelectAndNotify(item
); 
1096     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1100         if ( strArg
.empty() ) 
1101             SelectAndNotify(item
); 
1105     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1107     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1109     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1111     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1113     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1114         ChangeCurrent(GetItemsPerPage()); 
1115     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1116         ChangeCurrent(-GetItemsPerPage()); 
1117     else if ( action 
== wxACTION_LISTBOX_START 
) 
1119     else if ( action 
== wxACTION_LISTBOX_END 
) 
1120         SetCurrentItem(GetCount() - 1); 
1121     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1123     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1124         ExtendSelection(item
); 
1125     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1126         FindNextItem(strArg
); 
1127     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1128         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1129     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1130               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1131         wxFAIL_MSG(_T("unimplemented yet")); 
1133         return wxControl::PerformAction(action
, numArg
, strArg
); 
1138 // ============================================================================ 
1139 // implementation of wxStdListboxInputHandler 
1140 // ============================================================================ 
1142 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1143                                                    bool toggleOnPressAlways
) 
1144                         : wxStdInputHandler(handler
) 
1147     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1148     m_actionMouse 
= wxACTION_NONE
; 
1149     m_trackMouseOutside 
= TRUE
; 
1152 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1153                                       const wxMouseEvent
& event
) 
1155     int item 
= HitTestUnsafe(lbox
, event
); 
1157     return FixItemIndex(lbox
, item
); 
1160 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1161                                             const wxMouseEvent
& event
) 
1163     wxPoint pt 
= event
.GetPosition(); 
1164     pt 
-= lbox
->GetClientAreaOrigin(); 
1166     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1167     return y 
/ lbox
->GetLineHeight(); 
1170 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1175         // mouse is above the first item 
1178     else if ( item 
>= lbox
->GetCount() ) 
1180         // mouse is below the last item 
1181         item 
= lbox
->GetCount() - 1; 
1187 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1189     return item 
>= 0 && item 
< lbox
->GetCount(); 
1193 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1194                                        const wxMouseEvent
& event
, 
1197     // we currently only allow selecting with the left mouse button, if we 
1198     // do need to allow using other buttons too we might use the code 
1201     m_btnCapture 
= event
.LeftDown() 
1210     wxControlAction action
; 
1211     if ( lbox
->HasMultipleSelection() ) 
1213         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1215             if ( m_toggleOnPressAlways 
) 
1217                 // toggle the item right now 
1218                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1222             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1224         else // wxLB_EXTENDED listbox 
1226             // simple click in an extended sel listbox clears the old 
1227             // selection and adds the clicked item to it then, ctrl-click 
1228             // toggles an item to it and shift-click adds a range between 
1229             // the old selection anchor and the clicked item 
1230             if ( event
.ControlDown() ) 
1232                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1234                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1236             else if ( event
.ShiftDown() ) 
1238                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1240             else // simple click 
1242                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1244                 action 
= wxACTION_LISTBOX_SELECT
; 
1247             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1250     else // single selection 
1253         action 
= wxACTION_LISTBOX_SELECT
; 
1256     // by default we always do track it 
1257     m_trackMouseOutside 
= TRUE
; 
1262 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1263                                          const wxKeyEvent
& event
, 
1266     // we're only interested in the key press events 
1267     if ( pressed 
&& !event
.AltDown() ) 
1269         bool isMoveCmd 
= TRUE
; 
1270         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1272         wxControlAction action
; 
1275         int keycode 
= event
.GetKeyCode(); 
1280                 action 
= wxACTION_LISTBOX_MOVEUP
; 
1284                 action 
= wxACTION_LISTBOX_MOVEDOWN
; 
1290                 action 
= wxACTION_LISTBOX_PAGEUP
; 
1296                 action 
= wxACTION_LISTBOX_PAGEDOWN
; 
1300                 action 
= wxACTION_LISTBOX_START
; 
1304                 action 
= wxACTION_LISTBOX_END
; 
1309                 if ( style 
& wxLB_MULTIPLE 
) 
1311                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1317                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1322                 if ( (keycode 
< 255) && wxIsalnum(keycode
) ) 
1324                     action 
= wxACTION_LISTBOX_FIND
; 
1325                     strArg 
= (wxChar
)keycode
; 
1331             consumer
->PerformAction(action
, -1, strArg
); 
1335                 if ( style 
& wxLB_SINGLE 
) 
1337                     // the current item is always the one selected 
1338                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1340                 else if ( style 
& wxLB_EXTENDED 
) 
1342                     if ( event
.ShiftDown() ) 
1343                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1346                         // select the item and make it the new selection anchor 
1347                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1348                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1351                 //else: nothing to do for multiple selection listboxes 
1358     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1361 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1362                                            const wxMouseEvent
& event
) 
1364     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1365     int item 
= HitTest(lbox
, event
); 
1366     wxControlAction action
; 
1368     // when the left mouse button is pressed, capture the mouse and track the 
1369     // item under mouse (if the mouse leaves the window, we will still be 
1370     // getting the mouse move messages generated by wxScrollWindow) 
1371     if ( event
.LeftDown() ) 
1373         // capture the mouse to track the selected item 
1374         lbox
->CaptureMouse(); 
1376         action 
= SetupCapture(lbox
, event
, item
); 
1378     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1380         // when the left mouse button is released, release the mouse too 
1381         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1384             winCapture
->ReleaseMouse(); 
1387             action 
= m_actionMouse
; 
1389         //else: the mouse wasn't presed over the listbox, only released here 
1391     else if ( event
.LeftDClick() ) 
1393         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1398         lbox
->PerformAction(action
, item
); 
1403     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1406 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1407                                                const wxMouseEvent
& event
) 
1409     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1410     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1412         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1414         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1416             // someone captured the mouse for us (we always set m_btnCapture 
1417             // when we do it ourselves): in this case we only react to 
1418             // the mouse messages when they happen inside the listbox 
1419             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1423         int item 
= HitTest(lbox
, event
); 
1424         if ( !m_btnCapture 
) 
1426             // now that we have the mouse inside the listbox, do capture it 
1427             // normally - but ensure that we will still ignore the outside 
1429             SetupCapture(lbox
, event
, item
); 
1431             m_trackMouseOutside 
= FALSE
; 
1434         if ( IsValidIndex(lbox
, item
) ) 
1436             // pass something into strArg to tell the listbox that it shouldn't 
1437             // send the notification message: see PerformAction() above 
1438             lbox
->PerformAction(m_actionMouse
, item
, _T("no")); 
1440         // else: don't pass invalid index to the listbox 
1442     else // we don't have capture any more 
1446             // if we lost capture unexpectedly (someone else took the capture 
1447             // from us), return to a consistent state 
1452     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1455 #endif // wxUSE_LISTBOX