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 
  68     // no items hence no current item 
  71     m_currentChanged 
= false; 
  73     // no need to update anything initially 
  76     // no scrollbars to show nor update 
  80     m_showScrollbarY 
= false; 
  83 wxListBox::wxListBox(wxWindow 
*parent
, 
  87                      const wxArrayString
& choices
, 
  89                      const wxValidator
& validator
, 
  94     Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
); 
  97 bool wxListBox::Create(wxWindow 
*parent
, 
 101                        const wxArrayString
& choices
, 
 103                        const wxValidator
& validator
, 
 104                        const wxString 
&name
) 
 106     wxCArrayString 
chs(choices
); 
 108     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 109                   style
, validator
, name
); 
 112 bool wxListBox::Create(wxWindow 
*parent
, 
 117                        const wxString choices
[], 
 119                        const wxValidator
& validator
, 
 120                        const wxString 
&name
) 
 122     // for compatibility accept both the new and old styles - they mean the 
 124     if ( style 
& wxLB_ALWAYS_SB 
) 
 125         style 
|= wxALWAYS_SHOW_SB
; 
 127     // if we don't have neither multiple nor extended flag, we must have the 
 128     // single selection listbox 
 129     if ( !(style 
& (wxLB_MULTIPLE 
| wxLB_EXTENDED
)) ) 
 130         style 
|= wxLB_SINGLE
; 
 132 #if wxUSE_TWO_WINDOWS 
 133     style 
|=  wxVSCROLL
|wxHSCROLL
; 
 134     if ((style 
& wxBORDER_MASK
) == 0) 
 135         style 
|= wxBORDER_SUNKEN
; 
 138     if ( !wxControl::Create(parent
, id
, pos
, size
, style
,  
 144     if ( style 
& wxLB_SORT 
) 
 145         m_stringsSorted 
= new wxSortedArrayString
; 
 147         m_strings 
= new wxArrayString
; 
 153     CreateInputHandler(wxINP_HANDLER_LISTBOX
); 
 158 wxListBox::~wxListBox() 
 160     // call this just to free the client data -- and avoid leaking memory 
 164         delete m_stringsSorted
; 
 171 // ---------------------------------------------------------------------------- 
 172 // adding/inserting strings 
 173 // ---------------------------------------------------------------------------- 
 175 int wxListBox::DoAppend(const wxString
& item
) 
 181         index 
= m_stringsSorted
->Add(item
); 
 185         index 
= m_strings
->GetCount(); 
 186         m_strings
->Add(item
); 
 189     m_itemsClientData
.Insert(NULL
, index
); 
 191     m_updateScrollbarY 
= true; 
 193     if ( HasHorzScrollbar() ) 
 195         // has the max width increased? 
 197         GetTextExtent(item
, &width
, NULL
); 
 198         if ( width 
> m_maxWidth 
) 
 201             m_maxWidthItem 
= index
; 
 202             m_updateScrollbarX 
= true; 
 206     RefreshFromItemToEnd(index
); 
 211 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 213     // the position of the item being added to a sorted listbox can't be 
 215     wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") ); 
 217     size_t count 
= items
.GetCount(); 
 218     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 220         m_strings
->Insert(items
[n
], pos 
+ n
); 
 221         m_itemsClientData
.Insert(NULL
, pos 
+ n
); 
 224     // the number of items has changed so we might have to show the scrollbar 
 225     m_updateScrollbarY 
= true; 
 227     // the max width also might have changed - just recalculate it instead of 
 228     // keeping track of it here, this is probably more efficient for a typical 
 230     RefreshHorzScrollbar(); 
 232     // note that we have to refresh all the items after the ones we inserted, 
 233     // not just these items 
 234     RefreshFromItemToEnd(pos
); 
 237 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
) 
 241     size_t count 
= items
.GetCount(); 
 245     m_strings
->Alloc(count
); 
 246     m_itemsClientData
.Alloc(count
); 
 247     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 253             index 
= m_stringsSorted
->Add(items
[n
]); 
 257             index 
= m_strings
->GetCount(); 
 258             m_strings
->Add(items
[n
]); 
 261         m_itemsClientData
.Insert(clientData 
? clientData
[n
] : NULL
, index
); 
 264     m_updateScrollbarY 
= true; 
 269 void wxListBox::SetString(int n
, const wxString
& s
) 
 271     if ( HasHorzScrollbar() ) 
 273         // we need to update m_maxWidth as changing the string may cause the 
 274         // horz scrollbar [dis]appear 
 277         GetTextExtent(s
, &width
, NULL
); 
 279         // it might have increased if the new string is long 
 280         if ( width 
> m_maxWidth 
) 
 284             m_updateScrollbarX 
= true; 
 286         // or also decreased if the old string was the longest one 
 287         else if ( n 
== m_maxWidthItem 
) 
 289             RefreshHorzScrollbar(); 
 292     else // no horz scrollbar 
 300 // ---------------------------------------------------------------------------- 
 302 // ---------------------------------------------------------------------------- 
 304 void wxListBox::DoClear() 
 308     if ( HasClientObjectData() ) 
 310         size_t count 
= m_itemsClientData
.GetCount(); 
 311         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 313             delete (wxClientData 
*) m_itemsClientData
[n
]; 
 317     m_itemsClientData
.Clear(); 
 318     m_selections
.Clear(); 
 323 void wxListBox::Clear() 
 327     m_updateScrollbarY 
= true; 
 329     RefreshHorzScrollbar(); 
 334 void wxListBox::Delete(int n
) 
 336     wxCHECK_RET( n 
>= 0 && n 
< GetCount(), 
 337                  _T("invalid index in wxListBox::Delete") ); 
 339     // do it before removing the index as otherwise the last item will not be 
 340     // refreshed (as GetCount() will be decremented) 
 341     RefreshFromItemToEnd(n
); 
 343     m_strings
->RemoveAt(n
); 
 345     if ( HasClientObjectData() ) 
 347         delete (wxClientData 
*)m_itemsClientData
[n
]; 
 350     m_itemsClientData
.RemoveAt(n
); 
 352     // when the item disappears we must not keep using its index 
 353     if ( n 
== m_current 
) 
 357     else if ( n 
< m_current 
) 
 361     //else: current item may stay 
 363     // update the selections array: the indices of all seletected items after 
 364     // the one being deleted must change and the item itselfm ust be removed 
 365     int index 
= wxNOT_FOUND
; 
 366     size_t count 
= m_selections
.GetCount(); 
 367     for ( size_t item 
= 0; item 
< count
; item
++ ) 
 369         if ( m_selections
[item
] == n 
) 
 371             // remember to delete it later 
 374         else if ( m_selections
[item
] > n 
) 
 376             // to account for the index shift 
 377             m_selections
[item
]--; 
 379         //else: nothing changed for this one 
 382     if ( index 
!= wxNOT_FOUND 
) 
 384         m_selections
.RemoveAt(index
); 
 387     // the number of items has changed, hence the scrollbar may disappear 
 388     m_updateScrollbarY 
= true; 
 390     // finally, if the longest item was deleted the scrollbar may disappear 
 391     if ( n 
== m_maxWidthItem 
) 
 393         RefreshHorzScrollbar(); 
 397 // ---------------------------------------------------------------------------- 
 398 // client data handling 
 399 // ---------------------------------------------------------------------------- 
 401 void wxListBox::DoSetItemClientData(int n
, void* clientData
) 
 403     m_itemsClientData
[n
] = clientData
; 
 406 void *wxListBox::DoGetItemClientData(int n
) const 
 408     return m_itemsClientData
[n
]; 
 411 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 413     m_itemsClientData
[n
] = clientData
; 
 416 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const 
 418     return (wxClientData 
*)m_itemsClientData
[n
]; 
 421 // ---------------------------------------------------------------------------- 
 423 // ---------------------------------------------------------------------------- 
 425 void wxListBox::SetSelection(int n
, bool select
) 
 429         if ( m_selections
.Index(n
) == wxNOT_FOUND 
) 
 431             if ( !HasMultipleSelection() ) 
 433                 // selecting an item in a single selection listbox deselects 
 442         //else: already selected 
 446         int index 
= m_selections
.Index(n
); 
 447         if ( index 
!= wxNOT_FOUND 
) 
 449             m_selections
.RemoveAt(index
); 
 456     // sanity check: a single selection listbox can't have more than one item 
 458     wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2), 
 459                   _T("multiple selected items in single selection lbox?") ); 
 463         // the newly selected item becomes the current one 
 468 int wxListBox::GetSelection() const 
 470     wxCHECK_MSG( !HasMultipleSelection(), -1, 
 471                  _T("use wxListBox::GetSelections for ths listbox") ); 
 473     return m_selections
.IsEmpty() ? -1 : m_selections
[0]; 
 476 int wxCMPFUNC_CONV 
wxCompareInts(int *n
, int *m
) 
 481 int wxListBox::GetSelections(wxArrayInt
& selections
) const 
 483     // always return sorted array to the user 
 484     selections 
= m_selections
; 
 485     size_t count 
= m_selections
.GetCount(); 
 487     // don't call sort on an empty array 
 490         selections
.Sort(wxCompareInts
); 
 496 // ---------------------------------------------------------------------------- 
 497 // refresh logic: we use delayed refreshing which allows to avoid multiple 
 498 // refreshes (and hence flicker) in case when several listbox items are 
 499 // added/deleted/changed subsequently 
 500 // ---------------------------------------------------------------------------- 
 502 void wxListBox::RefreshFromItemToEnd(int from
) 
 504     RefreshItems(from
, GetCount() - from
); 
 507 void wxListBox::RefreshItems(int from
, int count
) 
 509     switch ( m_updateCount 
) 
 513             m_updateCount 
= count
; 
 517             // we refresh everything anyhow 
 521             // add these items to the others which we have to refresh 
 522             if ( m_updateFrom 
< from 
) 
 524                 count 
+= from 
- m_updateFrom
; 
 525                 if ( m_updateCount 
< count 
) 
 526                     m_updateCount 
= count
; 
 528             else // m_updateFrom >= from 
 530                 int updateLast 
= wxMax(m_updateFrom 
+ m_updateCount
, 
 533                 m_updateCount 
= updateLast 
- m_updateFrom
; 
 538 void wxListBox::RefreshItem(int n
) 
 540     switch ( m_updateCount 
) 
 543             // refresh this item only 
 549             // we refresh everything anyhow 
 553             // add this item to the others which we have to refresh 
 554             if ( m_updateFrom 
< n 
) 
 556                 if ( m_updateCount 
< n 
- m_updateFrom 
+ 1 ) 
 557                     m_updateCount 
= n 
- m_updateFrom 
+ 1; 
 559             else // n <= m_updateFrom 
 561                 m_updateCount 
+= m_updateFrom 
- n
; 
 567 void wxListBox::RefreshAll() 
 572 void wxListBox::RefreshHorzScrollbar() 
 574     m_maxWidth 
= 0; // recalculate it 
 575     m_updateScrollbarX 
= true; 
 578 void wxListBox::UpdateScrollbars() 
 580     wxSize size 
= GetClientSize(); 
 582     // is our height enough to show all items? 
 583     int nLines 
= GetCount(); 
 584     wxCoord lineHeight 
= GetLineHeight(); 
 585     bool showScrollbarY 
= nLines
*lineHeight 
> size
.y
; 
 587     // check the width too if required 
 588     wxCoord charWidth
, maxWidth
; 
 590     if ( HasHorzScrollbar() ) 
 592         charWidth 
= GetCharWidth(); 
 593         maxWidth 
= GetMaxWidth(); 
 594         showScrollbarX 
= maxWidth 
> size
.x
; 
 596     else // never show it 
 598         charWidth 
= maxWidth 
= 0; 
 599         showScrollbarX 
= false; 
 602     // what should be the scrollbar range now? 
 603     int scrollRangeX 
= showScrollbarX
 
 604                         ? (maxWidth 
+ charWidth 
- 1) / charWidth 
+ 2 // FIXME 
 606     int scrollRangeY 
= showScrollbarY
 
 608                             (size
.y 
% lineHeight 
+ lineHeight 
- 1) / lineHeight
 
 611     // reset scrollbars if something changed: either the visibility status 
 612     // or the range of a scrollbar which is shown 
 613     if ( (showScrollbarY 
!= m_showScrollbarY
) || 
 614          (showScrollbarX 
!= m_showScrollbarX
) || 
 615          (showScrollbarY 
&& (scrollRangeY 
!= m_scrollRangeY
)) || 
 616          (showScrollbarX 
&& (scrollRangeX 
!= m_scrollRangeX
)) ) 
 619         GetViewStart(&x
, &y
); 
 620         SetScrollbars(charWidth
, lineHeight
, 
 621                       scrollRangeX
, scrollRangeY
, 
 624         m_showScrollbarX 
= showScrollbarX
; 
 625         m_showScrollbarY 
= showScrollbarY
; 
 627         m_scrollRangeX 
= scrollRangeX
; 
 628         m_scrollRangeY 
= scrollRangeY
; 
 632 void wxListBox::UpdateItems() 
 634     // only refresh the items which must be refreshed 
 635     if ( m_updateCount 
== -1 ) 
 638         wxLogTrace(_T("listbox"), _T("Refreshing all")); 
 644         wxSize size 
= GetClientSize(); 
 647         rect
.height 
= size
.y
; 
 648         rect
.y 
+= m_updateFrom
*GetLineHeight(); 
 649         rect
.height 
= m_updateCount
*GetLineHeight(); 
 651         // we don't need to calculate x position as we always refresh the 
 653         CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
); 
 655         wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"), 
 656                    m_updateFrom
, m_updateFrom 
+ m_updateCount 
- 1, 
 657                    rect
.GetTop(), rect
.GetBottom()); 
 659         Refresh(true, &rect
); 
 663 void wxListBox::OnInternalIdle() 
 665     if ( m_updateScrollbarY 
|| m_updateScrollbarX 
) 
 670         m_updateScrollbarY 
= false; 
 673     if ( m_currentChanged 
) 
 675         DoEnsureVisible(m_current
); 
 677         m_currentChanged 
= false; 
 686     wxListBoxBase::OnInternalIdle(); 
 689 // ---------------------------------------------------------------------------- 
 691 // ---------------------------------------------------------------------------- 
 693 wxBorder 
wxListBox::GetDefaultBorder() const 
 695     return wxBORDER_SUNKEN
; 
 698 void wxListBox::DoDraw(wxControlRenderer 
*renderer
) 
 700     // adjust the DC to account for scrolling 
 701     wxDC
& dc 
= renderer
->GetDC(); 
 703     dc
.SetFont(GetFont()); 
 705     // get the update rect 
 706     wxRect rectUpdate 
= GetUpdateClientRect(); 
 709     CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
); 
 710     CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
); 
 712     // get the items which must be redrawn 
 713     wxCoord lineHeight 
= GetLineHeight(); 
 714     size_t itemFirst 
= yTop 
/ lineHeight
, 
 715            itemLast 
= (yBottom 
+ lineHeight 
- 1) / lineHeight
, 
 716            itemMax 
= m_strings
->GetCount(); 
 718     if ( itemFirst 
>= itemMax 
) 
 721     if ( itemLast 
> itemMax 
) 
 725     wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"), 
 726                itemFirst
, itemLast
); 
 728     DoDrawRange(renderer
, itemFirst
, itemLast
); 
 731 void wxListBox::DoDrawRange(wxControlRenderer 
*renderer
, 
 732                             int itemFirst
, int itemLast
) 
 734     renderer
->DrawItems(this, itemFirst
, itemLast
); 
 737 // ---------------------------------------------------------------------------- 
 739 // ---------------------------------------------------------------------------- 
 741 bool wxListBox::SetFont(const wxFont
& font
) 
 743     if ( !wxControl::SetFont(font
) ) 
 753 void wxListBox::CalcItemsPerPage() 
 755     m_lineHeight 
= GetRenderer()->GetListboxItemHeight(GetCharHeight()); 
 756     m_itemsPerPage 
= GetClientSize().y 
/ m_lineHeight
; 
 759 int wxListBox::GetItemsPerPage() const 
 761     if ( !m_itemsPerPage 
) 
 763         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 766     return m_itemsPerPage
; 
 769 wxCoord 
wxListBox::GetLineHeight() const 
 773         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 779 wxCoord 
wxListBox::GetMaxWidth() const 
 781     if ( m_maxWidth 
== 0 ) 
 783         wxListBox 
*self 
= wxConstCast(this, wxListBox
); 
 785         size_t count 
= m_strings
->GetCount(); 
 786         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 788             GetTextExtent((*m_strings
)[n
], &width
, NULL
); 
 789             if ( width 
> m_maxWidth 
) 
 791                 self
->m_maxWidth 
= width
; 
 792                 self
->m_maxWidthItem 
= n
; 
 800 void wxListBox::OnSize(wxSizeEvent
& event
) 
 802     // recalculate the number of items per page 
 805     // the scrollbars might [dis]appear 
 807     m_updateScrollbarY 
= true; 
 812 void wxListBox::DoSetFirstItem(int n
) 
 817 void wxListBox::DoSetSize(int x
, int y
, 
 818                           int width
, int height
, 
 821     if ( GetWindowStyle() & wxLB_INT_HEIGHT 
) 
 823         // we must round up the height to an entire number of rows 
 825         // the client area must contain an int number of rows, so take borders 
 827         wxRect rectBorders 
= GetRenderer()->GetBorderDimensions(GetBorder()); 
 828         wxCoord hBorders 
= rectBorders
.y 
+ rectBorders
.height
; 
 830         wxCoord hLine 
= GetLineHeight(); 
 831         height 
= ((height 
- hBorders 
+ hLine 
- 1) / hLine
)*hLine 
+ hBorders
; 
 834     wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 837 wxSize 
wxListBox::DoGetBestClientSize() const 
 842     size_t count 
= m_strings
->GetCount(); 
 843     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 846         GetTextExtent((*m_strings
)[n
], &w
, &h
); 
 854     // if the listbox is empty, still give it some non zero (even if 
 855     // arbitrary) size - otherwise, leave small margin around the strings 
 859         width 
+= 3*GetCharWidth(); 
 862         height 
= GetCharHeight(); 
 864     // we need the height of the entire listbox, not just of one line 
 865     height 
*= wxMax(count
, 7); 
 867     return wxSize(width
, height
); 
 870 // ---------------------------------------------------------------------------- 
 872 // ---------------------------------------------------------------------------- 
 874 bool wxListBox::SendEvent(wxEventType type
, int item
) 
 876     wxCommandEvent 
event(type
, m_windowId
); 
 877     event
.SetEventObject(this); 
 879     // use the current item by default 
 885     // client data and string parameters only make sense if we have an item 
 888         if ( HasClientObjectData() ) 
 889             event
.SetClientObject(GetClientObject(item
)); 
 890         else if ( HasClientUntypedData() ) 
 891             event
.SetClientData(GetClientData(item
)); 
 893         event
.SetString(GetString(item
)); 
 896     event
.m_commandInt 
= item
; 
 898     return GetEventHandler()->ProcessEvent(event
); 
 901 void wxListBox::SetCurrentItem(int n
) 
 903     if ( n 
!= m_current 
) 
 905         if ( m_current 
!= -1 ) 
 906             RefreshItem(m_current
); 
 910         if ( m_current 
!= -1 ) 
 912             m_currentChanged 
= true; 
 914             RefreshItem(m_current
); 
 917     //else: nothing to do 
 920 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 922     int count 
= GetCount(); 
 925         // empty listbox, we can't find anything in it 
 929     // start either from the current item or from the next one if strictlyAfter 
 934         // the following line will set first correctly to 0 if there is no 
 935         // selection (m_current == -1) 
 936         first 
= m_current 
== count 
- 1 ? 0 : m_current 
+ 1; 
 938     else // start with the current 
 940         first 
= m_current 
== -1 ? 0 : m_current
; 
 943     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 945     // if this is not true we'd never exit from the loop below! 
 946     wxASSERT_MSG( first 
< count 
&& last 
< count
, _T("logic error") ); 
 948     // precompute it outside the loop 
 949     size_t len 
= prefix
.length(); 
 951     // loop over all items in the listbox 
 952     for ( int item 
= first
; item 
!= last
; item 
< count 
- 1 ? item
++ : item 
= 0 ) 
 954         if ( wxStrnicmp((*m_strings
)[item
], prefix
, len
) == 0 ) 
 956             SetCurrentItem(item
); 
 958             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 961                 SelectAndNotify(item
); 
 963                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 964                     AnchorSelection(item
); 
 975 void wxListBox::EnsureVisible(int n
) 
 977     if ( m_updateScrollbarY 
) 
 982         m_updateScrollbarY 
= false; 
 988 void wxListBox::DoEnsureVisible(int n
) 
 990     if ( !m_showScrollbarY 
) 
 992         // nothing to do - everything is shown anyhow 
 997     GetViewStart(0, &first
); 
1000         // we need to scroll upwards, so make the current item appear on top 
1001         // of the shown range 
1006         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
1009             // scroll down: the current item appears at the bottom of the 
1011             Scroll(0, n 
- (last 
- first
)); 
1016 void wxListBox::ChangeCurrent(int diff
) 
1018     int current 
= m_current 
== -1 ? 0 : m_current
; 
1022     int last 
= GetCount() - 1; 
1025     else if ( current 
> last 
) 
1028     SetCurrentItem(current
); 
1031 void wxListBox::ExtendSelection(int itemTo
) 
1033     // if we don't have the explicit values for selection start/end, make them 
1035     if ( m_selAnchor 
== -1 ) 
1036         m_selAnchor 
= m_current
; 
1041     // swap the start/end of selection range if necessary 
1042     int itemFrom 
= m_selAnchor
; 
1043     if ( itemFrom 
> itemTo 
) 
1045         int itemTmp 
= itemFrom
; 
1050     // the selection should now include all items in the range between the 
1051     // anchor and the specified item and only them 
1054     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
1059     for ( ; n 
<= itemTo
; n
++ ) 
1064     int count 
= GetCount(); 
1065     for ( ; n 
< count
; n
++ ) 
1071 void wxListBox::DoSelect(int item
, bool sel
) 
1075         // go to this item first 
1076         SetCurrentItem(item
); 
1079     // the current item is the one we want to change: either it was just 
1080     // changed above to be the same as item or item == -1 in which we case we 
1081     // are supposed to use the current one anyhow 
1082     if ( m_current 
!= -1 ) 
1085         SetSelection(m_current
, sel
); 
1089 void wxListBox::SelectAndNotify(int item
) 
1093     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1096 void wxListBox::Activate(int item
) 
1099         SetCurrentItem(item
); 
1103     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1112         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1116 // ---------------------------------------------------------------------------- 
1118 // ---------------------------------------------------------------------------- 
1121    The numArg here is the listbox item index while the strArg is used 
1122    differently for the different actions: 
1124    a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string 
1127    b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used 
1128       to decide if the listbox should send the notification event (it is empty) 
1129       or not (it is not): this allows us to reuse the same action for when the 
1130       user is dragging the mouse when it has been released although in the 
1131       first case no notification is sent while in the second it is sent. 
1133 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1135                               const wxString
& strArg
) 
1137     int item 
= (int)numArg
; 
1139     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1141         SetCurrentItem(item
); 
1143     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1147     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1152         if ( IsSelected(item
) ) 
1155             SelectAndNotify(item
); 
1157     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1161         if ( strArg
.empty() ) 
1162             SelectAndNotify(item
); 
1166     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1168     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1170     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1172     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1174     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1175         ChangeCurrent(GetItemsPerPage()); 
1176     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1177         ChangeCurrent(-GetItemsPerPage()); 
1178     else if ( action 
== wxACTION_LISTBOX_START 
) 
1180     else if ( action 
== wxACTION_LISTBOX_END 
) 
1181         SetCurrentItem(GetCount() - 1); 
1182     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1184     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1185         ExtendSelection(item
); 
1186     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1187         FindNextItem(strArg
); 
1188     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1189         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1190     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1191               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1192         wxFAIL_MSG(_T("unimplemented yet")); 
1194         return wxControl::PerformAction(action
, numArg
, strArg
); 
1199 // ============================================================================ 
1200 // implementation of wxStdListboxInputHandler 
1201 // ============================================================================ 
1203 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1204                                                    bool toggleOnPressAlways
) 
1205                         : wxStdInputHandler(handler
) 
1208     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1209     m_actionMouse 
= wxACTION_NONE
; 
1210     m_trackMouseOutside 
= true; 
1213 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1214                                       const wxMouseEvent
& event
) 
1216     int item 
= HitTestUnsafe(lbox
, event
); 
1218     return FixItemIndex(lbox
, item
); 
1221 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1222                                             const wxMouseEvent
& event
) 
1224     wxPoint pt 
= event
.GetPosition(); 
1225     pt 
-= lbox
->GetClientAreaOrigin(); 
1227     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1228     return y 
/ lbox
->GetLineHeight(); 
1231 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1236         // mouse is above the first item 
1239     else if ( item 
>= lbox
->GetCount() ) 
1241         // mouse is below the last item 
1242         item 
= lbox
->GetCount() - 1; 
1248 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1250     return item 
>= 0 && item 
< lbox
->GetCount(); 
1254 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1255                                        const wxMouseEvent
& event
, 
1258     // we currently only allow selecting with the left mouse button, if we 
1259     // do need to allow using other buttons too we might use the code 
1262     m_btnCapture 
= event
.LeftDown() 
1271     wxControlAction action
; 
1272     if ( lbox
->HasMultipleSelection() ) 
1274         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1276             if ( m_toggleOnPressAlways 
) 
1278                 // toggle the item right now 
1279                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1283             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1285         else // wxLB_EXTENDED listbox 
1287             // simple click in an extended sel listbox clears the old 
1288             // selection and adds the clicked item to it then, ctrl-click 
1289             // toggles an item to it and shift-click adds a range between 
1290             // the old selection anchor and the clicked item 
1291             if ( event
.ControlDown() ) 
1293                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1295                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1297             else if ( event
.ShiftDown() ) 
1299                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1301             else // simple click 
1303                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1305                 action 
= wxACTION_LISTBOX_SELECT
; 
1308             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1311     else // single selection 
1314         action 
= wxACTION_LISTBOX_SELECT
; 
1317     // by default we always do track it 
1318     m_trackMouseOutside 
= true; 
1323 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1324                                          const wxKeyEvent
& event
, 
1327     // we're only interested in the key press events 
1328     if ( pressed 
&& !event
.AltDown() ) 
1330         bool isMoveCmd 
= true; 
1331         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1333         wxControlAction action
; 
1336         int keycode 
= event
.GetKeyCode(); 
1341                 action 
= wxACTION_LISTBOX_MOVEUP
; 
1345                 action 
= wxACTION_LISTBOX_MOVEDOWN
; 
1351                 action 
= wxACTION_LISTBOX_PAGEUP
; 
1357                 action 
= wxACTION_LISTBOX_PAGEDOWN
; 
1361                 action 
= wxACTION_LISTBOX_START
; 
1365                 action 
= wxACTION_LISTBOX_END
; 
1370                 if ( style 
& wxLB_MULTIPLE 
) 
1372                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1378                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1383                 if ( (keycode 
< 255) && wxIsalnum(keycode
) ) 
1385                     action 
= wxACTION_LISTBOX_FIND
; 
1386                     strArg 
= (wxChar
)keycode
; 
1390         if ( !action
.IsEmpty() ) 
1392             consumer
->PerformAction(action
, -1, strArg
); 
1396                 if ( style 
& wxLB_SINGLE 
) 
1398                     // the current item is always the one selected 
1399                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1401                 else if ( style 
& wxLB_EXTENDED 
) 
1403                     if ( event
.ShiftDown() ) 
1404                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1407                         // select the item and make it the new selection anchor 
1408                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1409                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1412                 //else: nothing to do for multiple selection listboxes 
1419     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1422 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1423                                            const wxMouseEvent
& event
) 
1425     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1426     int item 
= HitTest(lbox
, event
); 
1427     wxControlAction action
; 
1429     // when the left mouse button is pressed, capture the mouse and track the 
1430     // item under mouse (if the mouse leaves the window, we will still be 
1431     // getting the mouse move messages generated by wxScrollWindow) 
1432     if ( event
.LeftDown() ) 
1434         // capture the mouse to track the selected item 
1435         lbox
->CaptureMouse(); 
1437         action 
= SetupCapture(lbox
, event
, item
); 
1439     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1441         // when the left mouse button is released, release the mouse too 
1442         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1445             winCapture
->ReleaseMouse(); 
1448             action 
= m_actionMouse
; 
1450         //else: the mouse wasn't presed over the listbox, only released here 
1452     else if ( event
.LeftDClick() ) 
1454         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1457     if ( !action
.IsEmpty() ) 
1459         lbox
->PerformAction(action
, item
); 
1464     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1467 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1468                                                const wxMouseEvent
& event
) 
1470     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1471     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1473         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1475         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1477             // someone captured the mouse for us (we always set m_btnCapture 
1478             // when we do it ourselves): in this case we only react to 
1479             // the mouse messages when they happen inside the listbox 
1480             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1484         int item 
= HitTest(lbox
, event
); 
1485         if ( !m_btnCapture 
) 
1487             // now that we have the mouse inside the listbox, do capture it 
1488             // normally - but ensure that we will still ignore the outside 
1490             SetupCapture(lbox
, event
, item
); 
1492             m_trackMouseOutside 
= false; 
1495         if ( IsValidIndex(lbox
, item
) ) 
1497             // pass something into strArg to tell the listbox that it shouldn't 
1498             // send the notification message: see PerformAction() above 
1499             lbox
->PerformAction(m_actionMouse
, item
, _T("no")); 
1501         // else: don't pass invalid index to the listbox 
1503     else // we don't have capture any more 
1507             // if we lost capture unexpectedly (someone else took the capture 
1508             // from us), return to a consistent state 
1513     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1516 #endif // wxUSE_LISTBOX