1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/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 // wxStdListboxInputHandler: handles mouse and kbd in a single or multi 
  43 // ---------------------------------------------------------------------------- 
  45 class WXDLLEXPORT wxStdListboxInputHandler 
: public wxStdInputHandler
 
  48     // if pressing the mouse button in a multiselection listbox should toggle 
  49     // the item under mouse immediately, then specify true as the second 
  50     // parameter (this is the standard behaviour, under GTK the item is toggled 
  51     // only when the mouse is released in the multi selection listbox) 
  52     wxStdListboxInputHandler(wxInputHandler 
*inphand
, 
  53                              bool toggleOnPressAlways 
= true); 
  56     virtual bool HandleKey(wxInputConsumer 
*consumer
, 
  57                            const wxKeyEvent
& event
, 
  59     virtual bool HandleMouse(wxInputConsumer 
*consumer
, 
  60                              const wxMouseEvent
& event
); 
  61     virtual bool HandleMouseMove(wxInputConsumer 
*consumer
, 
  62                                  const wxMouseEvent
& event
); 
  65     // return the item under mouse, 0 if the mouse is above the listbox or 
  66     // GetCount() if it is below it 
  67     int HitTest(const wxListBox 
*listbox
, const wxMouseEvent
& event
); 
  69     // parts of HitTest(): first finds the pseudo (because not in range) index 
  70     // of the item and the second one adjusts it if necessary - that is if the 
  71     // third one returns false 
  72     int HitTestUnsafe(const wxListBox 
*listbox
, const wxMouseEvent
& event
); 
  73     int FixItemIndex(const wxListBox 
*listbox
, int item
); 
  74     bool IsValidIndex(const wxListBox 
*listbox
, int item
); 
  76     // init m_btnCapture and m_actionMouse 
  77     wxControlAction 
SetupCapture(wxListBox 
*lbox
, 
  78                                  const wxMouseEvent
& event
, 
  81     wxRenderer 
*m_renderer
; 
  83     // the button which initiated the mouse capture (currently 0 or 1) 
  86     // the action to perform when the mouse moves while we capture it 
  87     wxControlAction m_actionMouse
; 
  89     // the ctor parameter toggleOnPressAlways (see comments near it) 
  90     bool m_toggleOnPressAlways
; 
  92     // do we track the mouse outside the window when it is captured? 
  93     bool m_trackMouseOutside
; 
  96 // ============================================================================ 
  97 // implementation of wxListBox 
  98 // ============================================================================ 
 100 BEGIN_EVENT_TABLE(wxListBox
, wxListBoxBase
) 
 101     EVT_SIZE(wxListBox::OnSize
) 
 104 // ---------------------------------------------------------------------------- 
 106 // ---------------------------------------------------------------------------- 
 108 void wxListBox::Init() 
 110     // will be calculated later when needed 
 116     m_strings
.unsorted 
= NULL
; 
 118     // no items hence no current item 
 121     m_currentChanged 
= false; 
 123     // no need to update anything initially 
 126     // no scrollbars to show nor update 
 130     m_showScrollbarY 
= false; 
 133 wxListBox::wxListBox(wxWindow 
*parent
, 
 137                      const wxArrayString
& choices
, 
 139                      const wxValidator
& validator
, 
 140                      const wxString 
&name
) 
 141           :wxScrollHelper(this) 
 145     Create(parent
, id
, pos
, size
, choices
, style
, validator
, name
); 
 148 bool wxListBox::Create(wxWindow 
*parent
, 
 152                        const wxArrayString
& choices
, 
 154                        const wxValidator
& validator
, 
 155                        const wxString 
&name
) 
 157     wxCArrayString 
chs(choices
); 
 159     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 160                   style
, validator
, name
); 
 163 bool wxListBox::Create(wxWindow 
*parent
, 
 168                        const wxString choices
[], 
 170                        const wxValidator
& validator
, 
 171                        const wxString 
&name
) 
 173     // for compatibility accept both the new and old styles - they mean the 
 175     if ( style 
& wxLB_ALWAYS_SB 
) 
 176         style 
|= wxALWAYS_SHOW_SB
; 
 178     // if we don't have neither multiple nor extended flag, we must have the 
 179     // single selection listbox 
 180     if ( !(style 
& (wxLB_MULTIPLE 
| wxLB_EXTENDED
)) ) 
 181         style 
|= wxLB_SINGLE
; 
 183 #if wxUSE_TWO_WINDOWS 
 184     style 
|=  wxVSCROLL
|wxHSCROLL
; 
 185     if ((style 
& wxBORDER_MASK
) == 0) 
 186         style 
|= wxBORDER_SUNKEN
; 
 189     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, 
 194         m_strings
.sorted 
= new wxSortedArrayString
; 
 196         m_strings
.unsorted 
= new wxArrayString
; 
 200     SetInitialSize(size
); 
 202     CreateInputHandler(wxINP_HANDLER_LISTBOX
); 
 207 wxListBox::~wxListBox() 
 209     // call this just to free the client data -- and avoid leaking memory 
 213         delete m_strings
.sorted
; 
 215         delete m_strings
.unsorted
; 
 217     m_strings
.sorted 
= NULL
; 
 220 // ---------------------------------------------------------------------------- 
 222 // ---------------------------------------------------------------------------- 
 224 unsigned int wxListBox::GetCount() const 
 226     return IsSorted() ? m_strings
.sorted
->size() 
 227                       : m_strings
.unsorted
->size(); 
 230 wxString 
wxListBox::GetString(unsigned int n
) const 
 232     return IsSorted() ? m_strings
.sorted
->Item(n
) 
 233                       : m_strings
.unsorted
->Item(n
); 
 236 int wxListBox::FindString(const wxString
& s
, bool bCase
) const 
 238     return IsSorted() ? m_strings
.sorted
->Index(s
, bCase
) 
 239                       : m_strings
.unsorted
->Index(s
, bCase
); 
 242 // ---------------------------------------------------------------------------- 
 243 // adding/inserting strings 
 244 // ---------------------------------------------------------------------------- 
 246 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
, 
 249                              wxClientDataType type
) 
 251     int idx 
= wxNOT_FOUND
; 
 253     const unsigned int numItems 
= items
.GetCount(); 
 254     for ( unsigned int i 
= 0; i 
< numItems
; ++i 
) 
 256         const wxString
& item 
= items
[i
]; 
 257         idx 
= IsSorted() ? m_strings
.sorted
->Add(item
) 
 258                          : (m_strings
.unsorted
->Insert(item
, pos
), pos
++); 
 260         m_itemsClientData
.Insert(NULL
, idx
); 
 261         AssignNewItemClientData(idx
, clientData
, i
, type
); 
 263         // call the wxCheckListBox hook 
 267     // the number of items has changed so we might have to show the scrollbar 
 268     m_updateScrollbarY 
= true; 
 270     // the max width also might have changed - just recalculate it instead of 
 271     // keeping track of it here, this is probably more efficient for a typical 
 273     RefreshHorzScrollbar(); 
 275     // note that we have to refresh all the items after the ones we inserted, 
 276     // not just these items 
 277     RefreshFromItemToEnd(pos
); 
 282 void wxListBox::SetString(unsigned int n
, const wxString
& s
) 
 284     wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") ); 
 287         (*m_strings
.sorted
)[n
] = s
; 
 289         (*m_strings
.unsorted
)[n
] = s
; 
 291     if ( HasHorzScrollbar() ) 
 293         // we need to update m_maxWidth as changing the string may cause the 
 294         // horz scrollbar [dis]appear 
 297         GetTextExtent(s
, &width
, NULL
); 
 299         // it might have increased if the new string is long 
 300         if ( width 
> m_maxWidth 
) 
 304             m_updateScrollbarX 
= true; 
 306         // or also decreased if the old string was the longest one 
 307         else if ( n 
== (unsigned int)m_maxWidthItem 
) 
 309             RefreshHorzScrollbar(); 
 316 // ---------------------------------------------------------------------------- 
 318 // ---------------------------------------------------------------------------- 
 320 void wxListBox::DoClear() 
 323         m_strings
.sorted
->Clear(); 
 325         m_strings
.unsorted
->Clear(); 
 327     m_itemsClientData
.Clear(); 
 328     m_selections
.Clear(); 
 332     m_updateScrollbarY 
= true; 
 334     RefreshHorzScrollbar(); 
 339 void wxListBox::DoDeleteOneItem(unsigned int n
) 
 341     wxCHECK_RET( IsValid(n
), 
 342                  wxT("invalid index in wxListBox::Delete") ); 
 344     // do it before removing the index as otherwise the last item will not be 
 345     // refreshed (as GetCount() will be decremented) 
 346     RefreshFromItemToEnd(n
); 
 349         m_strings
.sorted
->RemoveAt(n
); 
 351         m_strings
.unsorted
->RemoveAt(n
); 
 353     m_itemsClientData
.RemoveAt(n
); 
 355     // when the item disappears we must not keep using its index 
 356     if ( (int)n 
== m_current 
) 
 360     else if ( (int)n 
< m_current 
) 
 364     //else: current item may stay 
 366     // update the selections array: the indices of all seletected items after 
 367     // the one being deleted must change and the item itselfm ust be removed 
 368     int index 
= wxNOT_FOUND
; 
 369     unsigned int count 
= m_selections
.GetCount(); 
 370     for ( unsigned int item 
= 0; item 
< count
; item
++ ) 
 372         if ( m_selections
[item
] == (int)n 
) 
 374             // remember to delete it later 
 377         else if ( m_selections
[item
] > (int)n 
) 
 379             // to account for the index shift 
 380             m_selections
[item
]--; 
 382         //else: nothing changed for this one 
 385     if ( index 
!= wxNOT_FOUND 
) 
 387         m_selections
.RemoveAt(index
); 
 390     // the number of items has changed, hence the scrollbar may disappear 
 391     m_updateScrollbarY 
= true; 
 393     // finally, if the longest item was deleted the scrollbar may disappear 
 394     if ( (int)n 
== m_maxWidthItem 
) 
 396         RefreshHorzScrollbar(); 
 400 // ---------------------------------------------------------------------------- 
 401 // client data handling 
 402 // ---------------------------------------------------------------------------- 
 404 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 406     m_itemsClientData
[n
] = clientData
; 
 409 void *wxListBox::DoGetItemClientData(unsigned int n
) const 
 411     return m_itemsClientData
[n
]; 
 414 // ---------------------------------------------------------------------------- 
 416 // ---------------------------------------------------------------------------- 
 418 void wxListBox::DoSetSelection(int n
, bool select
) 
 422         if ( n 
== wxNOT_FOUND 
) 
 424             if ( !HasMultipleSelection() ) 
 426                 // selecting wxNOT_FOUND is documented to deselect all items 
 431         else if ( m_selections
.Index(n
) == wxNOT_FOUND 
) 
 433             if ( !HasMultipleSelection() ) 
 435                 // selecting an item in a single selection listbox deselects 
 444         //else: already selected 
 448         int index 
= m_selections
.Index(n
); 
 449         if ( index 
!= wxNOT_FOUND 
) 
 451             m_selections
.RemoveAt(index
); 
 458     // sanity check: a single selection listbox can't have more than one item 
 460     wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2), 
 461                   wxT("multiple selected items in single selection lbox?") ); 
 465         // the newly selected item becomes the current one 
 470 int wxListBox::GetSelection() const 
 472     wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND
, 
 473                  wxT("use wxListBox::GetSelections for ths listbox") ); 
 475     return m_selections
.IsEmpty() ? wxNOT_FOUND 
: m_selections
[0]; 
 478 static int wxCMPFUNC_CONV 
wxCompareInts(int *n
, int *m
) 
 483 int wxListBox::GetSelections(wxArrayInt
& selections
) const 
 485     // always return sorted array to the user 
 486     selections 
= m_selections
; 
 487     unsigned int count 
= m_selections
.GetCount(); 
 489     // don't call sort on an empty array 
 492         selections
.Sort(wxCompareInts
); 
 498 // ---------------------------------------------------------------------------- 
 499 // refresh logic: we use delayed refreshing which allows to avoid multiple 
 500 // refreshes (and hence flicker) in case when several listbox items are 
 501 // added/deleted/changed subsequently 
 502 // ---------------------------------------------------------------------------- 
 504 void wxListBox::RefreshFromItemToEnd(int from
) 
 506     RefreshItems(from
, GetCount() - from
); 
 509 void wxListBox::RefreshItems(int from
, int count
) 
 511     switch ( m_updateCount 
) 
 515             m_updateCount 
= count
; 
 519             // we refresh everything anyhow 
 523             // add these items to the others which we have to refresh 
 524             if ( m_updateFrom 
< from 
) 
 526                 count 
+= from 
- m_updateFrom
; 
 527                 if ( m_updateCount 
< count 
) 
 528                     m_updateCount 
= count
; 
 530             else // m_updateFrom >= from 
 532                 int updateLast 
= wxMax(m_updateFrom 
+ m_updateCount
, 
 535                 m_updateCount 
= updateLast 
- m_updateFrom
; 
 540 void wxListBox::RefreshItem(int n
) 
 542     switch ( m_updateCount 
) 
 545             // refresh this item only 
 551             // we refresh everything anyhow 
 555             // add this item to the others which we have to refresh 
 556             if ( m_updateFrom 
< n 
) 
 558                 if ( m_updateCount 
< n 
- m_updateFrom 
+ 1 ) 
 559                     m_updateCount 
= n 
- m_updateFrom 
+ 1; 
 561             else // n <= m_updateFrom 
 563                 m_updateCount 
+= m_updateFrom 
- n
; 
 569 void wxListBox::RefreshAll() 
 574 void wxListBox::RefreshHorzScrollbar() 
 576     m_maxWidth 
= 0; // recalculate it 
 577     m_updateScrollbarX 
= true; 
 580 void wxListBox::UpdateScrollbars() 
 582     wxSize size 
= GetClientSize(); 
 584     // is our height enough to show all items? 
 585     unsigned int nLines 
= GetCount(); 
 586     wxCoord lineHeight 
= GetLineHeight(); 
 587     bool showScrollbarY 
= (int)nLines
*lineHeight 
> size
.y
; 
 589     // check the width too if required 
 590     wxCoord charWidth
, maxWidth
; 
 592     if ( HasHorzScrollbar() ) 
 594         charWidth 
= GetCharWidth(); 
 595         maxWidth 
= GetMaxWidth(); 
 596         showScrollbarX 
= maxWidth 
> size
.x
; 
 598     else // never show it 
 600         charWidth 
= maxWidth 
= 0; 
 601         showScrollbarX 
= false; 
 604     // what should be the scrollbar range now? 
 605     int scrollRangeX 
= showScrollbarX
 
 606                         ? (maxWidth 
+ charWidth 
- 1) / charWidth 
+ 2 // FIXME 
 608     int scrollRangeY 
= showScrollbarY
 
 610                             (size
.y 
% lineHeight 
+ lineHeight 
- 1) / lineHeight
 
 613     // reset scrollbars if something changed: either the visibility status 
 614     // or the range of a scrollbar which is shown 
 615     if ( (showScrollbarY 
!= m_showScrollbarY
) || 
 616          (showScrollbarX 
!= m_showScrollbarX
) || 
 617          (showScrollbarY 
&& (scrollRangeY 
!= m_scrollRangeY
)) || 
 618          (showScrollbarX 
&& (scrollRangeX 
!= m_scrollRangeX
)) ) 
 621         GetViewStart(&x
, &y
); 
 622         SetScrollbars(charWidth
, lineHeight
, 
 623                       scrollRangeX
, scrollRangeY
, 
 626         m_showScrollbarX 
= showScrollbarX
; 
 627         m_showScrollbarY 
= showScrollbarY
; 
 629         m_scrollRangeX 
= scrollRangeX
; 
 630         m_scrollRangeY 
= scrollRangeY
; 
 634 void wxListBox::UpdateItems() 
 636     // only refresh the items which must be refreshed 
 637     if ( m_updateCount 
== -1 ) 
 640         wxLogTrace(wxT("listbox"), wxT("Refreshing all")); 
 646         wxSize size 
= GetClientSize(); 
 649         rect
.height 
= size
.y
; 
 650         rect
.y 
+= m_updateFrom
*GetLineHeight(); 
 651         rect
.height 
= m_updateCount
*GetLineHeight(); 
 653         // we don't need to calculate x position as we always refresh the 
 655         CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
); 
 657         wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"), 
 658                    m_updateFrom
, m_updateFrom 
+ m_updateCount 
- 1, 
 659                    rect
.GetTop(), rect
.GetBottom()); 
 661         Refresh(true, &rect
); 
 665 void wxListBox::OnInternalIdle() 
 667     if ( m_updateScrollbarY 
|| m_updateScrollbarX 
) 
 672         m_updateScrollbarY 
= false; 
 675     if ( m_currentChanged 
) 
 677         DoEnsureVisible(m_current
); 
 679         m_currentChanged 
= false; 
 688     wxListBoxBase::OnInternalIdle(); 
 691 // ---------------------------------------------------------------------------- 
 693 // ---------------------------------------------------------------------------- 
 695 wxBorder 
wxListBox::GetDefaultBorder() const 
 697     return wxBORDER_SUNKEN
; 
 700 void wxListBox::DoDraw(wxControlRenderer 
*renderer
) 
 702     // adjust the DC to account for scrolling 
 703     wxDC
& dc 
= renderer
->GetDC(); 
 705     dc
.SetFont(GetFont()); 
 707     // get the update rect 
 708     wxRect rectUpdate 
= GetUpdateClientRect(); 
 711     CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
); 
 712     CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
); 
 714     // get the items which must be redrawn 
 715     wxCoord lineHeight 
= GetLineHeight(); 
 716     unsigned int itemFirst 
= yTop 
/ lineHeight
, 
 717                  itemLast 
= (yBottom 
+ lineHeight 
- 1) / lineHeight
, 
 718                  itemMax 
= GetCount(); 
 720     if ( itemFirst 
>= itemMax 
) 
 723     if ( itemLast 
> itemMax 
) 
 727     wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"), 
 728                itemFirst
, itemLast
); 
 730     DoDrawRange(renderer
, itemFirst
, itemLast
); 
 733 void wxListBox::DoDrawRange(wxControlRenderer 
*renderer
, 
 734                             int itemFirst
, int itemLast
) 
 736     renderer
->DrawItems(this, itemFirst
, itemLast
); 
 739 // ---------------------------------------------------------------------------- 
 741 // ---------------------------------------------------------------------------- 
 743 bool wxListBox::SetFont(const wxFont
& font
) 
 745     if ( !wxControl::SetFont(font
) ) 
 755 void wxListBox::CalcItemsPerPage() 
 757     m_lineHeight 
= GetRenderer()->GetListboxItemHeight(GetCharHeight()); 
 758     m_itemsPerPage 
= GetClientSize().y 
/ m_lineHeight
; 
 761 int wxListBox::GetItemsPerPage() const 
 763     if ( !m_itemsPerPage 
) 
 765         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 768     return m_itemsPerPage
; 
 771 wxCoord 
wxListBox::GetLineHeight() const 
 775         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 781 wxCoord 
wxListBox::GetMaxWidth() const 
 783     if ( m_maxWidth 
== 0 ) 
 785         wxListBox 
*self 
= wxConstCast(this, wxListBox
); 
 787         unsigned int count 
= GetCount(); 
 788         for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 790             GetTextExtent(this->GetString(n
), &width
, NULL
); 
 791             if ( width 
> m_maxWidth 
) 
 793                 self
->m_maxWidth 
= width
; 
 794                 self
->m_maxWidthItem 
= n
; 
 802 void wxListBox::OnSize(wxSizeEvent
& event
) 
 804     // recalculate the number of items per page 
 807     // the scrollbars might [dis]appear 
 809     m_updateScrollbarY 
= true; 
 814 void wxListBox::DoSetFirstItem(int n
) 
 819 void wxListBox::DoSetSize(int x
, int y
, 
 820                           int width
, int height
, 
 823     if ( GetWindowStyle() & wxLB_INT_HEIGHT 
) 
 825         // we must round up the height to an entire number of rows 
 827         // the client area must contain an int number of rows, so take borders 
 829         wxRect rectBorders 
= GetRenderer()->GetBorderDimensions(GetBorder()); 
 830         wxCoord hBorders 
= rectBorders
.y 
+ rectBorders
.height
; 
 832         wxCoord hLine 
= GetLineHeight(); 
 833         height 
= ((height 
- hBorders 
+ hLine 
- 1) / hLine
)*hLine 
+ hBorders
; 
 836     wxListBoxBase::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 839 wxSize 
wxListBox::DoGetBestClientSize() const 
 844     unsigned int count 
= GetCount(); 
 845     for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 848         GetTextExtent(this->GetString(n
), &w
, &h
); 
 856     // if the listbox is empty, still give it some non zero (even if 
 857     // arbitrary) size - otherwise, leave small margin around the strings 
 861         width 
+= 3*GetCharWidth(); 
 864         height 
= GetCharHeight(); 
 866     // we need the height of the entire listbox, not just of one line 
 867     height 
*= wxMax(count
, 7); 
 869     return wxSize(width
, height
); 
 872 // ---------------------------------------------------------------------------- 
 874 // ---------------------------------------------------------------------------- 
 876 bool wxListBox::SendEvent(wxEventType type
, int item
) 
 878     wxCommandEvent 
event(type
, m_windowId
); 
 879     event
.SetEventObject(this); 
 881     // use the current item by default 
 887     // client data and string parameters only make sense if we have an item 
 890         if ( HasClientObjectData() ) 
 891             event
.SetClientObject(GetClientObject(item
)); 
 892         else if ( HasClientUntypedData() ) 
 893             event
.SetClientData(GetClientData(item
)); 
 895         event
.SetString(GetString(item
)); 
 900     return GetEventHandler()->ProcessEvent(event
); 
 903 void wxListBox::SetCurrentItem(int n
) 
 905     if ( n 
!= m_current 
) 
 907         if ( m_current 
!= -1 ) 
 908             RefreshItem(m_current
); 
 912         if ( m_current 
!= -1 ) 
 914             m_currentChanged 
= true; 
 916             RefreshItem(m_current
); 
 919     //else: nothing to do 
 922 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 924     unsigned int count 
= GetCount(); 
 927         // empty listbox, we can't find anything in it 
 931     // start either from the current item or from the next one if strictlyAfter 
 936         // the following line will set first correctly to 0 if there is no 
 937         // selection (m_current == -1) 
 938         first 
= m_current 
== (int)(count 
- 1) ? 0 : m_current 
+ 1; 
 940     else // start with the current 
 942         first 
= m_current 
== -1 ? 0 : m_current
; 
 945     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 947     // if this is not true we'd never exit from the loop below! 
 948     wxASSERT_MSG( first 
< (int)count 
&& last 
< (int)count
, wxT("logic error") ); 
 950     // precompute it outside the loop 
 951     size_t len 
= prefix
.length(); 
 953     // loop over all items in the listbox 
 954     for ( int item 
= first
; item 
!= (int)last
; item 
< (int)(count 
- 1) ? item
++ : item 
= 0 ) 
 956         if ( wxStrnicmp(this->GetString(item
).c_str(), prefix
, len
) == 0 ) 
 958             SetCurrentItem(item
); 
 960             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 963                 SelectAndNotify(item
); 
 965                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 966                     AnchorSelection(item
); 
 977 void wxListBox::EnsureVisible(int n
) 
 979     if ( m_updateScrollbarY 
) 
 984         m_updateScrollbarY 
= false; 
 990 void wxListBox::DoEnsureVisible(int n
) 
 992     if ( !m_showScrollbarY 
) 
 994         // nothing to do - everything is shown anyhow 
 999     GetViewStart(0, &first
); 
1002         // we need to scroll upwards, so make the current item appear on top 
1003         // of the shown range 
1008         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
1011             // scroll down: the current item appears at the bottom of the 
1013             Scroll(0, n 
- (last 
- first
)); 
1018 void wxListBox::ChangeCurrent(int diff
) 
1020     int current 
= m_current 
== -1 ? 0 : m_current
; 
1024     int last 
= GetCount() - 1; 
1027     else if ( current 
> last 
) 
1030     SetCurrentItem(current
); 
1033 void wxListBox::ExtendSelection(int itemTo
) 
1035     // if we don't have the explicit values for selection start/end, make them 
1037     if ( m_selAnchor 
== -1 ) 
1038         m_selAnchor 
= m_current
; 
1043     // swap the start/end of selection range if necessary 
1044     int itemFrom 
= m_selAnchor
; 
1045     if ( itemFrom 
> itemTo 
) 
1047         int itemTmp 
= itemFrom
; 
1052     // the selection should now include all items in the range between the 
1053     // anchor and the specified item and only them 
1056     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
1061     for ( ; n 
<= itemTo
; n
++ ) 
1066     unsigned int count 
= GetCount(); 
1067     for ( ; n 
< (int)count
; n
++ ) 
1073 void wxListBox::DoSelect(int item
, bool sel
) 
1077         // go to this item first 
1078         SetCurrentItem(item
); 
1081     // the current item is the one we want to change: either it was just 
1082     // changed above to be the same as item or item == -1 in which we case we 
1083     // are supposed to use the current one anyhow 
1084     if ( m_current 
!= -1 ) 
1087         SetSelection(m_current
, sel
); 
1091 void wxListBox::SelectAndNotify(int item
) 
1095     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1098 void wxListBox::Activate(int item
) 
1101         SetCurrentItem(item
); 
1105     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1114         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1118 // ---------------------------------------------------------------------------- 
1120 // ---------------------------------------------------------------------------- 
1123    The numArg here is the listbox item index while the strArg is used 
1124    differently for the different actions: 
1126    a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string 
1129    b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used 
1130       to decide if the listbox should send the notification event (it is empty) 
1131       or not (it is not): this allows us to reuse the same action for when the 
1132       user is dragging the mouse when it has been released although in the 
1133       first case no notification is sent while in the second it is sent. 
1135 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1137                               const wxString
& strArg
) 
1139     int item 
= (int)numArg
; 
1141     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1143         SetCurrentItem(item
); 
1145     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1149     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1154         if ( IsSelected(item
) ) 
1157             SelectAndNotify(item
); 
1159     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1163         if ( strArg
.empty() ) 
1164             SelectAndNotify(item
); 
1168     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1170     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1172     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1174     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1176     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1177         ChangeCurrent(GetItemsPerPage()); 
1178     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1179         ChangeCurrent(-GetItemsPerPage()); 
1180     else if ( action 
== wxACTION_LISTBOX_START 
) 
1182     else if ( action 
== wxACTION_LISTBOX_END 
) 
1183         SetCurrentItem(GetCount() - 1); 
1184     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1186     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1187         ExtendSelection(item
); 
1188     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1189         FindNextItem(strArg
); 
1190     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1191         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1192     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1193               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1194         wxFAIL_MSG(wxT("unimplemented yet")); 
1196         return wxControl::PerformAction(action
, numArg
, strArg
); 
1202 wxInputHandler 
*wxListBox::GetStdInputHandler(wxInputHandler 
*handlerDef
) 
1204     static wxStdListboxInputHandler 
s_handler(handlerDef
); 
1209 // ============================================================================ 
1210 // implementation of wxStdListboxInputHandler 
1211 // ============================================================================ 
1213 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1214                                                    bool toggleOnPressAlways
) 
1215                         : wxStdInputHandler(handler
) 
1218     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1219     m_actionMouse 
= wxACTION_NONE
; 
1220     m_trackMouseOutside 
= true; 
1223 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1224                                       const wxMouseEvent
& event
) 
1226     int item 
= HitTestUnsafe(lbox
, event
); 
1228     return FixItemIndex(lbox
, item
); 
1231 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1232                                             const wxMouseEvent
& event
) 
1234     wxPoint pt 
= event
.GetPosition(); 
1235     pt 
-= lbox
->GetClientAreaOrigin(); 
1237     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1238     return y 
/ lbox
->GetLineHeight(); 
1241 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1246         // mouse is above the first item 
1249     else if ( (unsigned int)item 
>= lbox
->GetCount() ) 
1251         // mouse is below the last item 
1252         item 
= lbox
->GetCount() - 1; 
1258 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1260     return item 
>= 0 && (unsigned int)item 
< lbox
->GetCount(); 
1264 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1265                                        const wxMouseEvent
& event
, 
1268     // we currently only allow selecting with the left mouse button, if we 
1269     // do need to allow using other buttons too we might use the code 
1272     m_btnCapture 
= event
.LeftDown() 
1281     wxControlAction action
; 
1282     if ( lbox
->HasMultipleSelection() ) 
1284         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1286             if ( m_toggleOnPressAlways 
) 
1288                 // toggle the item right now 
1289                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1293             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1295         else // wxLB_EXTENDED listbox 
1297             // simple click in an extended sel listbox clears the old 
1298             // selection and adds the clicked item to it then, ctrl-click 
1299             // toggles an item to it and shift-click adds a range between 
1300             // the old selection anchor and the clicked item 
1301             if ( event
.ControlDown() ) 
1303                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1305                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1307             else if ( event
.ShiftDown() ) 
1309                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1311             else // simple click 
1313                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1315                 action 
= wxACTION_LISTBOX_SELECT
; 
1318             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1321     else // single selection 
1324         action 
= wxACTION_LISTBOX_SELECT
; 
1327     // by default we always do track it 
1328     m_trackMouseOutside 
= true; 
1333 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1334                                          const wxKeyEvent
& event
, 
1337     // we're only interested in the key press events 
1338     if ( pressed 
&& !event
.AltDown() ) 
1340         bool isMoveCmd 
= true; 
1341         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1343         wxControlAction action
; 
1346         int keycode 
= event
.GetKeyCode(); 
1351                 action 
= wxACTION_LISTBOX_MOVEUP
; 
1355                 action 
= wxACTION_LISTBOX_MOVEDOWN
; 
1359                 action 
= wxACTION_LISTBOX_PAGEUP
; 
1363                 action 
= wxACTION_LISTBOX_PAGEDOWN
; 
1367                 action 
= wxACTION_LISTBOX_START
; 
1371                 action 
= wxACTION_LISTBOX_END
; 
1376                 if ( style 
& wxLB_MULTIPLE 
) 
1378                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1384                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1389                 if ( (keycode 
< 255) && wxIsalnum((wxChar
)keycode
) ) 
1391                     action 
= wxACTION_LISTBOX_FIND
; 
1392                     strArg 
= (wxChar
)keycode
; 
1396         if ( !action
.IsEmpty() ) 
1398             consumer
->PerformAction(action
, -1, strArg
); 
1402                 if ( style 
& wxLB_SINGLE 
) 
1404                     // the current item is always the one selected 
1405                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1407                 else if ( style 
& wxLB_EXTENDED 
) 
1409                     if ( event
.ShiftDown() ) 
1410                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1413                         // select the item and make it the new selection anchor 
1414                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1415                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1418                 //else: nothing to do for multiple selection listboxes 
1425     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1428 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1429                                            const wxMouseEvent
& event
) 
1431     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1432     int item 
= HitTest(lbox
, event
); 
1433     wxControlAction action
; 
1435     // when the left mouse button is pressed, capture the mouse and track the 
1436     // item under mouse (if the mouse leaves the window, we will still be 
1437     // getting the mouse move messages generated by wxScrollWindow) 
1438     if ( event
.LeftDown() ) 
1440         // capture the mouse to track the selected item 
1441         lbox
->CaptureMouse(); 
1443         action 
= SetupCapture(lbox
, event
, item
); 
1445     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1447         // when the left mouse button is released, release the mouse too 
1448         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1451             winCapture
->ReleaseMouse(); 
1454             action 
= m_actionMouse
; 
1456         //else: the mouse wasn't presed over the listbox, only released here 
1458     else if ( event
.LeftDClick() ) 
1460         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1463     if ( !action
.IsEmpty() ) 
1465         lbox
->PerformAction(action
, item
); 
1470     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1473 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1474                                                const wxMouseEvent
& event
) 
1476     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1477     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1479         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1481         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1483             // someone captured the mouse for us (we always set m_btnCapture 
1484             // when we do it ourselves): in this case we only react to 
1485             // the mouse messages when they happen inside the listbox 
1486             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1490         int item 
= HitTest(lbox
, event
); 
1491         if ( !m_btnCapture 
) 
1493             // now that we have the mouse inside the listbox, do capture it 
1494             // normally - but ensure that we will still ignore the outside 
1496             SetupCapture(lbox
, event
, item
); 
1498             m_trackMouseOutside 
= false; 
1501         if ( IsValidIndex(lbox
, item
) ) 
1503             // pass something into strArg to tell the listbox that it shouldn't 
1504             // send the notification message: see PerformAction() above 
1505             lbox
->PerformAction(m_actionMouse
, item
, wxT("no")); 
1507         // else: don't pass invalid index to the listbox 
1509     else // we don't have capture any more 
1513             // if we lost capture unexpectedly (someone else took the capture 
1514             // from us), return to a consistent state 
1519     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1522 #endif // wxUSE_LISTBOX