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 // ---------------------------------------------------------------------------- 
  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
) 
  53     EVT_IDLE(wxListBox::OnIdle
) 
  56 // ---------------------------------------------------------------------------- 
  58 // ---------------------------------------------------------------------------- 
  60 void wxListBox::Init() 
  62     // will be calculated later when needed 
  69     // no items hence no current item 
  72     m_currentChanged 
= FALSE
; 
  74     // no need to update anything initially 
  77     // no scrollbars to show nor update 
  81     m_showScrollbarY 
= FALSE
; 
  84 bool wxListBox::Create(wxWindow 
*parent
, 
  89                        const wxString choices
[], 
  91                        const wxValidator
& validator
, 
  94     // for compatibility accept both the new and old styles - they mean the 
  96     if ( style 
& wxLB_ALWAYS_SB 
) 
  97         style 
|= wxALWAYS_SHOW_SB
; 
  99     // if we don't have neither multiple nor extended flag, we must have the 
 100     // single selection listbox 
 101     if ( !(style 
& (wxLB_MULTIPLE 
| wxLB_EXTENDED
)) ) 
 102         style 
|= wxLB_SINGLE
; 
 104     if ( !wxControl::Create(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) ) 
 109     if ( style 
& wxLB_SORT 
) 
 110         m_strings 
= wxArrayString(TRUE 
/* auto sort */); 
 116     CreateInputHandler(wxINP_HANDLER_LISTBOX
); 
 121 wxListBox::~wxListBox() 
 125 // ---------------------------------------------------------------------------- 
 126 // adding/inserting strings 
 127 // ---------------------------------------------------------------------------- 
 129 int wxListBox::DoAppend(const wxString
& item
) 
 131     size_t index 
= m_strings
.Add(item
); 
 132     m_itemsClientData
.Insert(NULL
, index
); 
 134     m_updateScrollbarY 
= TRUE
; 
 136     if ( HasHorzScrollbar() ) 
 138         // has the max width increased? 
 140         GetTextExtent(item
, &width
, NULL
); 
 141         if ( width 
> m_maxWidth 
) 
 144             m_maxWidthItem 
= index
; 
 145             m_updateScrollbarX 
= TRUE
; 
 149     RefreshFromItemToEnd(index
); 
 154 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 156     // the position of the item being added to a sorted listbox can't be 
 158     wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") ); 
 160     size_t count 
= items
.GetCount(); 
 161     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 163         m_strings
.Insert(items
[n
], pos 
+ n
); 
 164         m_itemsClientData
.Insert(NULL
, pos 
+ n
); 
 167     // the number of items has changed so we might have to show the scrollbar 
 168     m_updateScrollbarY 
= TRUE
; 
 170     // the max width also might have changed - just recalculate it instead of 
 171     // keeping track of it here, this is probably more efficient for a typical 
 173     RefreshHorzScrollbar(); 
 175     // note that we have to refresh all the items after the ones we inserted, 
 176     // not just these items 
 177     RefreshFromItemToEnd(pos
); 
 180 void wxListBox::DoSetItems(const wxArrayString
& items
, void **clientData
) 
 184     size_t count 
= items
.GetCount(); 
 188     m_strings
.Alloc(count
); 
 189     m_itemsClientData
.Alloc(count
); 
 190     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 192         size_t index 
= m_strings
.Add(items
[n
]); 
 193         m_itemsClientData
.Insert(clientData 
? clientData
[n
] : NULL
, index
); 
 196     m_updateScrollbarY 
= TRUE
; 
 201 void wxListBox::SetString(int n
, const wxString
& s
) 
 203     if ( HasHorzScrollbar() ) 
 205         // we need to update m_maxWidth as changing the string may cause the 
 206         // horz scrollbar [dis]appear 
 209         GetTextExtent(s
, &width
, NULL
); 
 211         // it might have increased if the new string is long 
 212         if ( width 
> m_maxWidth 
) 
 216             m_updateScrollbarX 
= TRUE
; 
 218         // or also decreased if the old string was the longest one 
 219         else if ( n 
== m_maxWidthItem 
) 
 221             RefreshHorzScrollbar(); 
 224     else // no horz scrollbar 
 232 // ---------------------------------------------------------------------------- 
 234 // ---------------------------------------------------------------------------- 
 236 void wxListBox::DoClear() 
 240     if ( HasClientObjectData() ) 
 242         size_t count 
= m_itemsClientData
.GetCount(); 
 243         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 245             delete (wxClientData 
*) m_itemsClientData
[n
]; 
 249     m_itemsClientData
.Clear(); 
 250     m_selections
.Clear(); 
 255 void wxListBox::Clear() 
 259     m_updateScrollbarY 
= TRUE
; 
 261     RefreshHorzScrollbar(); 
 266 void wxListBox::Delete(int n
) 
 268     wxCHECK_RET( n 
< GetCount(), _T("invalid index in wxListBox::Delete") ); 
 270     // do it before removing the index as otherwise the last item will not be 
 271     // refreshed (as GetCount() will be decremented) 
 272     RefreshFromItemToEnd(n
); 
 274     m_strings
.RemoveAt(n
); 
 276     if ( HasClientObjectData() ) 
 278         delete (wxClientData 
*)m_itemsClientData
[n
]; 
 281     m_itemsClientData
.RemoveAt(n
); 
 283     // when the item disappears we must not keep using its index 
 284     if ( n 
== m_current 
) 
 288     else if ( n 
< m_current 
) 
 292     //else: current item may stay 
 294     // update the selections array: the indices of all seletected items after 
 295     // the one being deleted must change and the item itselfm ust be removed 
 296     int index 
= wxNOT_FOUND
; 
 297     size_t count 
= m_selections
.GetCount(); 
 298     for ( size_t item 
= 0; item 
< count
; item
++ ) 
 300         if ( m_selections
[item
] == n 
) 
 302             // remember to delete it later 
 305         else if ( m_selections
[item
] > n 
) 
 307             // to account for the index shift 
 308             m_selections
[item
]--; 
 310         //else: nothing changed for this one 
 313     if ( index 
!= wxNOT_FOUND 
) 
 315         m_selections
.RemoveAt(index
); 
 318     // the number of items has changed, hence the scrollbar may disappear 
 319     m_updateScrollbarY 
= TRUE
; 
 321     // finally, if the longest item was deleted the scrollbar may disappear 
 322     if ( n 
== m_maxWidthItem 
) 
 324         RefreshHorzScrollbar(); 
 328 // ---------------------------------------------------------------------------- 
 329 // client data handling 
 330 // ---------------------------------------------------------------------------- 
 332 void wxListBox::DoSetItemClientData(int n
, void* clientData
) 
 334     m_itemsClientData
[n
] = clientData
; 
 337 void *wxListBox::DoGetItemClientData(int n
) const 
 339     return m_itemsClientData
[n
]; 
 342 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 344     m_itemsClientData
[n
] = clientData
; 
 347 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const 
 349     return (wxClientData 
*)m_itemsClientData
[n
]; 
 352 // ---------------------------------------------------------------------------- 
 354 // ---------------------------------------------------------------------------- 
 356 void wxListBox::SetSelection(int n
, bool select
) 
 360         if ( m_selections
.Index(n
) == wxNOT_FOUND 
) 
 362             if ( !HasMultipleSelection() ) 
 364                 // selecting an item in a single selection listbox deselects 
 373         //else: already selected 
 377         int index 
= m_selections
.Index(n
); 
 378         if ( index 
!= wxNOT_FOUND 
) 
 380             m_selections
.RemoveAt(index
); 
 387     // sanity check: a single selection listbox can't have more than one item 
 389     wxASSERT_MSG( HasMultipleSelection() || (m_selections
.GetCount() < 2), 
 390                   _T("multiple selected items in single selection lbox?") ); 
 394         // the newly selected item becomes the current one 
 399 int wxListBox::GetSelection() const 
 401     wxCHECK_MSG( !HasMultipleSelection(), -1, 
 402                  _T("use wxListBox::GetSelections for ths listbox") ); 
 404     return m_selections
.IsEmpty() ? -1 : m_selections
[0]; 
 407 int wxCMPFUNC_CONV 
wxCompareInts(int *n
, int *m
) 
 412 int wxListBox::GetSelections(wxArrayInt
& selections
) const 
 414     // always return sorted array to the user 
 415     selections 
= m_selections
; 
 416     size_t count 
= m_selections
.GetCount(); 
 418     // don't call sort on an empty array 
 421         selections
.Sort(wxCompareInts
); 
 427 // ---------------------------------------------------------------------------- 
 428 // refresh logic: we use delayed refreshing which allows to avoid multiple 
 429 // refreshes (and hence flicker) in case when several listbox items are 
 430 // added/deleted/changed subsequently 
 431 // ---------------------------------------------------------------------------- 
 433 void wxListBox::RefreshFromItemToEnd(int from
) 
 435     RefreshItems(from
, GetCount() - from
); 
 438 void wxListBox::RefreshItems(int from
, int count
) 
 440     switch ( m_updateCount 
) 
 444             m_updateCount 
= count
; 
 448             // we refresh everything anyhow 
 452             // add these items to the others which we have to refresh 
 453             if ( m_updateFrom 
< from 
) 
 455                 count 
+= from 
- m_updateFrom
; 
 456                 if ( m_updateCount 
< count 
) 
 457                     m_updateCount 
= count
; 
 459             else // m_updateFrom >= from 
 461                 int updateLast 
= wxMax(m_updateFrom 
+ m_updateCount
, 
 464                 m_updateCount 
= updateLast 
- m_updateFrom
; 
 469 void wxListBox::RefreshItem(int n
) 
 471     switch ( m_updateCount 
) 
 474             // refresh this item only 
 480             // we refresh everything anyhow 
 484             // add this item to the others which we have to refresh 
 485             if ( m_updateFrom 
< n 
) 
 487                 if ( m_updateCount 
< n 
- m_updateFrom 
+ 1 ) 
 488                     m_updateCount 
= n 
- m_updateFrom 
+ 1; 
 490             else // n <= m_updateFrom 
 492                 m_updateCount 
+= m_updateFrom 
- n
; 
 498 void wxListBox::RefreshAll() 
 503 void wxListBox::RefreshHorzScrollbar() 
 505     m_maxWidth 
= 0; // recalculate it 
 506     m_updateScrollbarX 
= TRUE
; 
 509 void wxListBox::UpdateScrollbars() 
 511     wxSize size 
= GetClientSize(); 
 513     // is our height enough to show all items? 
 514     int nLines 
= GetCount(); 
 515     wxCoord lineHeight 
= GetLineHeight(); 
 516     bool showScrollbarY 
= nLines
*lineHeight 
> size
.y
; 
 518     // check the width too if required 
 519     wxCoord charWidth
, maxWidth
; 
 521     if ( HasHorzScrollbar() ) 
 523         charWidth 
= GetCharWidth(); 
 524         maxWidth 
= GetMaxWidth(); 
 525         showScrollbarX 
= maxWidth 
> size
.x
; 
 527     else // never show it 
 529         charWidth 
= maxWidth 
= 0; 
 530         showScrollbarX 
= FALSE
; 
 533     // what should be the scrollbar range now? 
 534     int scrollRangeX 
= showScrollbarX
 
 535                         ? (maxWidth 
+ charWidth 
- 1) / charWidth 
+ 2 // FIXME 
 537     int scrollRangeY 
= showScrollbarY
 
 539                             (size
.y 
% lineHeight 
+ lineHeight 
- 1) / lineHeight
 
 542     // reset scrollbars if something changed: either the visibility status 
 543     // or the range of a scrollbar which is shown 
 544     if ( (showScrollbarY 
!= m_showScrollbarY
) || 
 545          (showScrollbarX 
!= m_showScrollbarX
) || 
 546          (showScrollbarY 
&& (scrollRangeY 
!= m_scrollRangeY
)) || 
 547          (showScrollbarX 
&& (scrollRangeX 
!= m_scrollRangeX
)) ) 
 550         GetViewStart(&x
, &y
); 
 551         SetScrollbars(charWidth
, lineHeight
, 
 552                       scrollRangeX
, scrollRangeY
, 
 555         m_showScrollbarX 
= showScrollbarX
; 
 556         m_showScrollbarY 
= showScrollbarY
; 
 558         m_scrollRangeX 
= scrollRangeX
; 
 559         m_scrollRangeY 
= scrollRangeY
; 
 563 void wxListBox::UpdateItems() 
 565     // only refresh the items which must be refreshed 
 566     if ( m_updateCount 
== -1 ) 
 569         wxLogTrace(_T("listbox"), _T("Refreshing all")); 
 575         wxSize size 
= GetClientSize(); 
 578         rect
.height 
= size
.y
; 
 579         rect
.y 
+= m_updateFrom
*GetLineHeight(); 
 580         rect
.height 
= m_updateCount
*GetLineHeight(); 
 582         // we don't need to calculate x position as we always refresh the 
 584         CalcScrolledPosition(0, rect
.y
, NULL
, &rect
.y
); 
 586         wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"), 
 587                    m_updateFrom
, m_updateFrom 
+ m_updateCount 
- 1, 
 588                    rect
.GetTop(), rect
.GetBottom()); 
 590         Refresh(TRUE
, &rect
); 
 594 void wxListBox::OnIdle(wxIdleEvent
& event
) 
 596     if ( m_updateScrollbarY 
|| m_updateScrollbarX 
) 
 601         m_updateScrollbarY 
= FALSE
; 
 604     if ( m_currentChanged 
) 
 606         DoEnsureVisible(m_current
); 
 608         m_currentChanged 
= FALSE
; 
 621 // ---------------------------------------------------------------------------- 
 623 // ---------------------------------------------------------------------------- 
 625 wxBorder 
wxListBox::GetDefaultBorder() const 
 627     return wxBORDER_SUNKEN
; 
 630 void wxListBox::DoDraw(wxControlRenderer 
*renderer
) 
 632     // adjust the DC to account for scrolling 
 633     wxDC
& dc 
= renderer
->GetDC(); 
 635     dc
.SetFont(GetFont()); 
 637     // get the update rect 
 638     wxRect rectUpdate 
= GetUpdateClientRect(); 
 641     CalcUnscrolledPosition(0, rectUpdate
.GetTop(), NULL
, &yTop
); 
 642     CalcUnscrolledPosition(0, rectUpdate
.GetBottom(), NULL
, &yBottom
); 
 644     // get the items which must be redrawn 
 645     wxCoord lineHeight 
= GetLineHeight(); 
 646     size_t itemFirst 
= yTop 
/ lineHeight
, 
 647            itemLast 
= (yBottom 
+ lineHeight 
- 1) / lineHeight
, 
 648            itemMax 
= m_strings
.GetCount(); 
 650     if ( itemFirst 
>= itemMax 
) 
 653     if ( itemLast 
> itemMax 
) 
 657     wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"), 
 658                itemFirst
, itemLast
); 
 660     DoDrawRange(renderer
, itemFirst
, itemLast
); 
 663 void wxListBox::DoDrawRange(wxControlRenderer 
*renderer
, 
 664                             int itemFirst
, int itemLast
) 
 666     renderer
->DrawItems(this, itemFirst
, itemLast
); 
 669 // ---------------------------------------------------------------------------- 
 671 // ---------------------------------------------------------------------------- 
 673 bool wxListBox::SetFont(const wxFont
& font
) 
 675     if ( !wxControl::SetFont(font
) ) 
 685 void wxListBox::CalcItemsPerPage() 
 687     m_lineHeight 
= GetRenderer()->GetListboxItemHeight(GetCharHeight()); 
 688     m_itemsPerPage 
= GetClientSize().y 
/ m_lineHeight
; 
 691 int wxListBox::GetItemsPerPage() const 
 693     if ( !m_itemsPerPage 
) 
 695         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 698     return m_itemsPerPage
; 
 701 wxCoord 
wxListBox::GetLineHeight() const 
 705         wxConstCast(this, wxListBox
)->CalcItemsPerPage(); 
 711 wxCoord 
wxListBox::GetMaxWidth() const 
 713     if ( m_maxWidth 
== 0 ) 
 715         wxListBox 
*self 
= wxConstCast(this, wxListBox
); 
 717         size_t count 
= m_strings
.GetCount(); 
 718         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 720             GetTextExtent(m_strings
[n
], &width
, NULL
); 
 721             if ( width 
> m_maxWidth 
) 
 723                 self
->m_maxWidth 
= width
; 
 724                 self
->m_maxWidthItem 
= n
; 
 732 void wxListBox::OnSize(wxSizeEvent
& event
) 
 734     // recalculate the number of items per page 
 737     // the scrollbars might [dis]appear 
 739     m_updateScrollbarY 
= TRUE
; 
 744 void wxListBox::DoSetFirstItem(int n
) 
 749 void wxListBox::DoSetSize(int x
, int y
, 
 750                           int width
, int height
, 
 753     if ( GetWindowStyle() & wxLB_INT_HEIGHT 
) 
 755         // we must round up the height to an entire number of rows 
 757         // the client area must contain an int number of rows, so take borders 
 759         wxRect rectBorders 
= GetRenderer()->GetBorderDimensions(GetBorder()); 
 760         wxCoord hBorders 
= rectBorders
.y 
+ rectBorders
.height
; 
 762         wxCoord hLine 
= GetLineHeight(); 
 763         height 
= ((height 
- hBorders 
+ hLine 
- 1) / hLine
)*hLine 
+ hBorders
; 
 766     wxListBoxBase::DoSetSize(x
, y
, width
, height
); 
 769 wxSize 
wxListBox::DoGetBestClientSize() const 
 774     size_t count 
= m_strings
.GetCount(); 
 775     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 778         GetTextExtent(m_strings
[n
], &w
, &h
); 
 786     // if the listbox is empty, still give it some non zero (even if 
 787     // arbitrary) size - otherwise, leave small margin around the strings 
 791         width 
+= 3*GetCharWidth(); 
 794         height 
= GetCharHeight(); 
 796     // we need the height of the entire listbox, not just of one line 
 797     height 
*= wxMax(count
, 7); 
 799     return wxSize(width
, height
); 
 802 // ---------------------------------------------------------------------------- 
 804 // ---------------------------------------------------------------------------- 
 806 bool wxListBox::SendEvent(wxEventType type
, int item
) 
 808     wxCommandEvent 
event(type
, m_windowId
); 
 809     event
.SetEventObject(this); 
 811     // use the current item by default 
 817     // client data and string parameters only make sense if we have an item 
 820         if ( HasClientObjectData() ) 
 821             event
.SetClientObject(GetClientObject(item
)); 
 822         else if ( HasClientUntypedData() ) 
 823             event
.SetClientData(GetClientData(item
)); 
 825         event
.SetString(GetString(item
)); 
 828     event
.m_commandInt 
= item
; 
 830     return GetEventHandler()->ProcessEvent(event
); 
 833 void wxListBox::SetCurrentItem(int n
) 
 835     if ( n 
!= m_current 
) 
 837         if ( m_current 
!= -1 ) 
 838             RefreshItem(m_current
); 
 842         if ( m_current 
!= -1 ) 
 844             m_currentChanged 
= TRUE
; 
 846             RefreshItem(m_current
); 
 849     //else: nothing to do 
 852 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 854     int count 
= GetCount(); 
 857         // empty listbox, we can't find anything in it 
 861     // start either from the current item or from the next one if strictlyAfter 
 866         // the following line will set first correctly to 0 if there is no 
 867         // selection (m_current == -1) 
 868         first 
= m_current 
== count 
- 1 ? 0 : m_current 
+ 1; 
 870     else // start with the current 
 872         first 
= m_current 
== -1 ? 0 : m_current
; 
 875     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 877     // if this is not true we'd never exit from the loop below! 
 878     wxASSERT_MSG( first 
< count 
&& last 
< count
, _T("logic error") ); 
 880     // precompute it outside the loop 
 881     size_t len 
= prefix
.length(); 
 883     // loop over all items in the listbox 
 884     for ( int item 
= first
; item 
!= last
; item 
< count 
- 1 ? item
++ : item 
= 0 ) 
 886         if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 ) 
 888             SetCurrentItem(item
); 
 890             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 893                 SelectAndNotify(item
); 
 895                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 896                     AnchorSelection(item
); 
 907 void wxListBox::EnsureVisible(int n
) 
 909     if ( m_updateScrollbarY 
) 
 914         m_updateScrollbarY 
= FALSE
; 
 920 void wxListBox::DoEnsureVisible(int n
) 
 922     if ( !m_showScrollbarY 
) 
 924         // nothing to do - everything is shown anyhow 
 929     GetViewStart(0, &first
); 
 932         // we need to scroll upwards, so make the current item appear on top 
 933         // of the shown range 
 938         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
 941             // scroll down: the current item appears at the bottom of the 
 943             Scroll(0, n 
- (last 
- first
)); 
 948 void wxListBox::ChangeCurrent(int diff
) 
 950     int current 
= m_current 
== -1 ? 0 : m_current
; 
 954     int last 
= GetCount() - 1; 
 957     else if ( current 
> last 
) 
 960     SetCurrentItem(current
); 
 963 void wxListBox::ExtendSelection(int itemTo
) 
 965     // if we don't have the explicit values for selection start/end, make them 
 967     if ( m_selAnchor 
== -1 ) 
 968         m_selAnchor 
= m_current
; 
 973     // swap the start/end of selection range if necessary 
 974     int itemFrom 
= m_selAnchor
; 
 975     if ( itemFrom 
> itemTo 
) 
 977         int itemTmp 
= itemFrom
; 
 982     // the selection should now include all items in the range between the 
 983     // anchor and the specified item and only them 
 986     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
 991     for ( ; n 
<= itemTo
; n
++ ) 
 996     int count 
= GetCount(); 
 997     for ( ; n 
< count
; n
++ ) 
1003 void wxListBox::Select(bool sel
, int item
) 
1007         // go to this item first 
1008         SetCurrentItem(item
); 
1011     // the current item is the one we want to change: either it was just 
1012     // changed above to be the same as item or item == -1 in which we case we 
1013     // are supposed to use the current one anyhow 
1014     if ( m_current 
!= -1 ) 
1017         SetSelection(m_current
, sel
); 
1021 void wxListBox::SelectAndNotify(int item
) 
1025     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1028 void wxListBox::Activate(int item
) 
1031         SetCurrentItem(item
); 
1035     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1044         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1048 // ---------------------------------------------------------------------------- 
1050 // ---------------------------------------------------------------------------- 
1053    The numArg here is the listbox item index while the strArg is used 
1054    differently for the different actions: 
1056    a) for wxACTION_LISTBOX_FIND it has the natural meaning: this is the string 
1059    b) for wxACTION_LISTBOX_SELECT and wxACTION_LISTBOX_EXTENDSEL it is used 
1060       to decide if the listbox should send the notification event (it is empty) 
1061       or not (it is not): this allows us to reuse the same action for when the 
1062       user is dragging the mouse when it has been released although in the 
1063       first case no notification is sent while in the second it is sent. 
1065 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1067                               const wxString
& strArg
) 
1069     int item 
= (int)numArg
; 
1071     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1073         SetCurrentItem(item
); 
1075     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1079     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1084         if ( IsSelected(item
) ) 
1087             SelectAndNotify(item
); 
1089     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1093         if ( strArg
.empty() ) 
1094             SelectAndNotify(item
); 
1098     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1100     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1101         Select(FALSE
, item
); 
1102     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1104     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1106     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1107         ChangeCurrent(GetItemsPerPage()); 
1108     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1109         ChangeCurrent(-GetItemsPerPage()); 
1110     else if ( action 
== wxACTION_LISTBOX_START 
) 
1112     else if ( action 
== wxACTION_LISTBOX_END 
) 
1113         SetCurrentItem(GetCount() - 1); 
1114     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1116     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1117         ExtendSelection(item
); 
1118     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1119         FindNextItem(strArg
); 
1120     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1121         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1122     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1123               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1124         wxFAIL_MSG(_T("unimplemented yet")); 
1126         return wxControl::PerformAction(action
, numArg
, strArg
); 
1131 // ============================================================================ 
1132 // implementation of wxStdListboxInputHandler 
1133 // ============================================================================ 
1135 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1136                                                    bool toggleOnPressAlways
) 
1137                         : wxStdInputHandler(handler
) 
1140     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1141     m_actionMouse 
= wxACTION_NONE
; 
1142     m_trackMouseOutside 
= TRUE
; 
1145 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1146                                       const wxMouseEvent
& event
) 
1148     int item 
= HitTestUnsafe(lbox
, event
); 
1150     return FixItemIndex(lbox
, item
); 
1153 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1154                                             const wxMouseEvent
& event
) 
1156     wxPoint pt 
= event
.GetPosition(); 
1157     pt 
-= lbox
->GetClientAreaOrigin(); 
1159     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1160     return y 
/ lbox
->GetLineHeight(); 
1163 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1168         // mouse is above the first item 
1171     else if ( item 
>= lbox
->GetCount() ) 
1173         // mouse is below the last item 
1174         item 
= lbox
->GetCount() - 1; 
1180 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1182     return item 
>= 0 && item 
< lbox
->GetCount(); 
1186 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1187                                        const wxMouseEvent
& event
, 
1190     // we currently only allow selecting with the left mouse button, if we 
1191     // do need to allow using other buttons too we might use the code 
1194     m_btnCapture 
= event
.LeftDown() 
1203     wxControlAction action
; 
1204     if ( lbox
->HasMultipleSelection() ) 
1206         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1208             if ( m_toggleOnPressAlways 
) 
1210                 // toggle the item right now 
1211                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1215             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1217         else // wxLB_EXTENDED listbox 
1219             // simple click in an extended sel listbox clears the old 
1220             // selection and adds the clicked item to it then, ctrl-click 
1221             // toggles an item to it and shift-click adds a range between 
1222             // the old selection anchor and the clicked item 
1223             if ( event
.ControlDown() ) 
1225                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1227                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1229             else if ( event
.ShiftDown() ) 
1231                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1233             else // simple click 
1235                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1237                 action 
= wxACTION_LISTBOX_SELECT
; 
1240             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1243     else // single selection 
1246         action 
= wxACTION_LISTBOX_SELECT
; 
1249     // by default we always do track it 
1250     m_trackMouseOutside 
= TRUE
; 
1255 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1256                                          const wxKeyEvent
& event
, 
1259     // we're only interested in the key press events 
1260     if ( pressed 
&& !event
.AltDown() ) 
1262         bool isMoveCmd 
= TRUE
; 
1263         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1265         wxControlAction action
; 
1268         int keycode 
= event
.GetKeyCode(); 
1273                 action 
= wxACTION_LISTBOX_MOVEUP
; 
1277                 action 
= wxACTION_LISTBOX_MOVEDOWN
; 
1283                 action 
= wxACTION_LISTBOX_PAGEUP
; 
1289                 action 
= wxACTION_LISTBOX_PAGEDOWN
; 
1293                 action 
= wxACTION_LISTBOX_START
; 
1297                 action 
= wxACTION_LISTBOX_END
; 
1302                 if ( style 
& wxLB_MULTIPLE 
) 
1304                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1310                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1315                 if ( (keycode 
< 255) && wxIsalnum(keycode
) ) 
1317                     action 
= wxACTION_LISTBOX_FIND
; 
1318                     strArg 
= (wxChar
)keycode
; 
1324             consumer
->PerformAction(action
, -1, strArg
); 
1328                 if ( style 
& wxLB_SINGLE 
) 
1330                     // the current item is always the one selected 
1331                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1333                 else if ( style 
& wxLB_EXTENDED 
) 
1335                     if ( event
.ShiftDown() ) 
1336                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1339                         // select the item and make it the new selection anchor 
1340                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1341                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1344                 //else: nothing to do for multiple selection listboxes 
1351     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1354 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1355                                            const wxMouseEvent
& event
) 
1357     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1358     int item 
= HitTest(lbox
, event
); 
1359     wxControlAction action
; 
1361     // when the left mouse button is pressed, capture the mouse and track the 
1362     // item under mouse (if the mouse leaves the window, we will still be 
1363     // getting the mouse move messages generated by wxScrollWindow) 
1364     if ( event
.LeftDown() ) 
1366         // capture the mouse to track the selected item 
1367         lbox
->CaptureMouse(); 
1369         action 
= SetupCapture(lbox
, event
, item
); 
1371     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1373         // when the left mouse button is released, release the mouse too 
1374         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1377             winCapture
->ReleaseMouse(); 
1380             action 
= m_actionMouse
; 
1382         //else: the mouse wasn't presed over the listbox, only released here 
1384     else if ( event
.LeftDClick() ) 
1386         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1391         lbox
->PerformAction(action
, item
); 
1396     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1399 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1400                                                const wxMouseEvent
& event
) 
1402     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1403     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1405         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1407         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1409             // someone captured the mouse for us (we always set m_btnCapture 
1410             // when we do it ourselves): in this case we only react to 
1411             // the mouse messages when they happen inside the listbox 
1412             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1416         int item 
= HitTest(lbox
, event
); 
1417         if ( !m_btnCapture 
) 
1419             // now that we have the mouse inside the listbox, do capture it 
1420             // normally - but ensure that we will still ignore the outside 
1422             SetupCapture(lbox
, event
, item
); 
1424             m_trackMouseOutside 
= FALSE
; 
1427         if ( IsValidIndex(lbox
, item
) ) 
1429             // pass something into strArg to tell the listbox that it shouldn't 
1430             // send the notification message: see PerformAction() above 
1431             lbox
->PerformAction(m_actionMouse
, item
, _T("no")); 
1433         // else: don't pass invalid index to the listbox 
1435     else // we don't have capture any more 
1439             // if we lost capture unexpectedly (someone else took the capture 
1440             // from us), return to a consistent state 
1445     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1448 #endif // wxUSE_LISTBOX