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     // don't generate select events while the mouse is captured, we will only 
 809     // send them once it is released 
 810     if ( (type 
== wxEVT_COMMAND_LISTBOX_SELECTED
) && (GetCapture() == this) ) 
 813     wxCommandEvent 
event(type
, m_windowId
); 
 814     event
.SetEventObject(this); 
 816     // use the current item by default 
 822     // client data and string parameters only make sense if we have an item 
 825         if ( HasClientObjectData() ) 
 826             event
.SetClientObject(GetClientObject(item
)); 
 827         else if ( HasClientUntypedData() ) 
 828             event
.SetClientData(GetClientData(item
)); 
 830         event
.SetString(GetString(item
)); 
 833     event
.m_commandInt 
= item
; 
 835     return GetEventHandler()->ProcessEvent(event
); 
 838 void wxListBox::SetCurrentItem(int n
) 
 840     if ( n 
!= m_current 
) 
 842         if ( m_current 
!= -1 ) 
 843             RefreshItem(m_current
); 
 847         if ( m_current 
!= -1 ) 
 849             m_currentChanged 
= TRUE
; 
 851             RefreshItem(m_current
); 
 854     //else: nothing to do 
 857 bool wxListBox::FindItem(const wxString
& prefix
, bool strictlyAfter
) 
 859     int count 
= GetCount(); 
 862         // empty listbox, we can't find anything in it 
 866     // start either from the current item or from the next one if strictlyAfter 
 871         // the following line will set first correctly to 0 if there is no 
 872         // selection (m_current == -1) 
 873         first 
= m_current 
== count 
- 1 ? 0 : m_current 
+ 1; 
 875     else // start with the current 
 877         first 
= m_current 
== -1 ? 0 : m_current
; 
 880     int last 
= first 
== 0 ? count 
- 1 : first 
- 1; 
 882     // if this is not true we'd never exit from the loop below! 
 883     wxASSERT_MSG( first 
< count 
&& last 
< count
, _T("logic error") ); 
 885     // precompute it outside the loop 
 886     size_t len 
= prefix
.length(); 
 888     // loop over all items in the listbox 
 889     for ( int item 
= first
; item 
!= last
; item 
< count 
- 1 ? item
++ : item 
= 0 ) 
 891         if ( wxStrnicmp(m_strings
[item
], prefix
, len
) == 0 ) 
 893             SetCurrentItem(item
); 
 895             if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
 898                 SelectAndNotify(item
); 
 900                 if ( GetWindowStyle() & wxLB_EXTENDED 
) 
 901                     AnchorSelection(item
); 
 912 void wxListBox::EnsureVisible(int n
) 
 914     if ( m_updateScrollbarY 
) 
 919         m_updateScrollbarY 
= FALSE
; 
 925 void wxListBox::DoEnsureVisible(int n
) 
 927     if ( !m_showScrollbarY 
) 
 929         // nothing to do - everything is shown anyhow 
 934     GetViewStart(0, &first
); 
 937         // we need to scroll upwards, so make the current item appear on top 
 938         // of the shown range 
 943         int last 
= first 
+ GetClientSize().y 
/ GetLineHeight() - 1; 
 946             // scroll down: the current item appears at the bottom of the 
 948             Scroll(0, n 
- (last 
- first
)); 
 953 void wxListBox::ChangeCurrent(int diff
) 
 955     int current 
= m_current 
== -1 ? 0 : m_current
; 
 959     int last 
= GetCount() - 1; 
 962     else if ( current 
> last 
) 
 965     SetCurrentItem(current
); 
 968 void wxListBox::ExtendSelection(int itemTo
) 
 970     // if we don't have the explicit values for selection start/end, make them 
 972     if ( m_selAnchor 
== -1 ) 
 973         m_selAnchor 
= m_current
; 
 978     // swap the start/end of selection range if necessary 
 979     int itemFrom 
= m_selAnchor
; 
 980     if ( itemFrom 
> itemTo 
) 
 982         int itemTmp 
= itemFrom
; 
 987     // the selection should now include all items in the range between the 
 988     // anchor and the specified item and only them 
 991     for ( n 
= 0; n 
< itemFrom
; n
++ ) 
 996     for ( ; n 
<= itemTo
; n
++ ) 
1001     int count 
= GetCount(); 
1002     for ( ; n 
< count
; n
++ ) 
1008 void wxListBox::Select(bool sel
, int item
) 
1012         // go to this item first 
1013         SetCurrentItem(item
); 
1016     // the current item is the one we want to change: either it was just 
1017     // changed above to be the same as item or item == -1 in which we case we 
1018     // are supposed to use the current one anyhow 
1019     if ( m_current 
!= -1 ) 
1022         SetSelection(m_current
, sel
); 
1026 void wxListBox::SelectAndNotify(int item
) 
1030     SendEvent(wxEVT_COMMAND_LISTBOX_SELECTED
); 
1033 void wxListBox::Activate(int item
) 
1036         SetCurrentItem(item
); 
1040     if ( !(GetWindowStyle() & wxLB_MULTIPLE
) ) 
1049         SendEvent(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
); 
1053 // ---------------------------------------------------------------------------- 
1055 // ---------------------------------------------------------------------------- 
1057 bool wxListBox::PerformAction(const wxControlAction
& action
, 
1059                               const wxString
& strArg
) 
1061     int item 
= (int)numArg
; 
1063     if ( action 
== wxACTION_LISTBOX_SETFOCUS 
) 
1064         SetCurrentItem(item
); 
1065     else if ( action 
== wxACTION_LISTBOX_ACTIVATE 
) 
1067     else if ( action 
== wxACTION_LISTBOX_TOGGLE 
) 
1072         if ( IsSelected(item
) ) 
1075             SelectAndNotify(item
); 
1077     else if ( action 
== wxACTION_LISTBOX_SELECT 
) 
1080         SelectAndNotify(item
); 
1082     else if ( action 
== wxACTION_LISTBOX_SELECTADD 
) 
1084     else if ( action 
== wxACTION_LISTBOX_UNSELECT 
) 
1085         Select(FALSE
, item
); 
1086     else if ( action 
== wxACTION_LISTBOX_MOVEDOWN 
) 
1088     else if ( action 
== wxACTION_LISTBOX_MOVEUP 
) 
1090     else if ( action 
== wxACTION_LISTBOX_PAGEDOWN 
) 
1091         ChangeCurrent(GetItemsPerPage()); 
1092     else if ( action 
== wxACTION_LISTBOX_PAGEUP 
) 
1093         ChangeCurrent(-GetItemsPerPage()); 
1094     else if ( action 
== wxACTION_LISTBOX_START 
) 
1096     else if ( action 
== wxACTION_LISTBOX_END 
) 
1097         SetCurrentItem(GetCount() - 1); 
1098     else if ( action 
== wxACTION_LISTBOX_UNSELECTALL 
) 
1100     else if ( action 
== wxACTION_LISTBOX_EXTENDSEL 
) 
1101         ExtendSelection(item
); 
1102     else if ( action 
== wxACTION_LISTBOX_FIND 
) 
1103         FindNextItem(strArg
); 
1104     else if ( action 
== wxACTION_LISTBOX_ANCHOR 
) 
1105         AnchorSelection(item 
== -1 ? m_current 
: item
); 
1106     else if ( action 
== wxACTION_LISTBOX_SELECTALL 
|| 
1107               action 
== wxACTION_LISTBOX_SELTOGGLE 
) 
1108         wxFAIL_MSG(_T("unimplemented yet")); 
1110         return wxControl::PerformAction(action
, numArg
, strArg
); 
1115 // ============================================================================ 
1116 // implementation of wxStdListboxInputHandler 
1117 // ============================================================================ 
1119 wxStdListboxInputHandler::wxStdListboxInputHandler(wxInputHandler 
*handler
, 
1120                                                    bool toggleOnPressAlways
) 
1121                         : wxStdInputHandler(handler
) 
1124     m_toggleOnPressAlways 
= toggleOnPressAlways
; 
1125     m_actionMouse 
= wxACTION_NONE
; 
1126     m_trackMouseOutside 
= TRUE
; 
1129 int wxStdListboxInputHandler::HitTest(const wxListBox 
*lbox
, 
1130                                       const wxMouseEvent
& event
) 
1132     int item 
= HitTestUnsafe(lbox
, event
); 
1134     return FixItemIndex(lbox
, item
); 
1137 int wxStdListboxInputHandler::HitTestUnsafe(const wxListBox 
*lbox
, 
1138                                             const wxMouseEvent
& event
) 
1140     wxPoint pt 
= event
.GetPosition(); 
1141     pt 
-= lbox
->GetClientAreaOrigin(); 
1143     lbox
->CalcUnscrolledPosition(0, pt
.y
, NULL
, &y
); 
1144     return y 
/ lbox
->GetLineHeight(); 
1147 int wxStdListboxInputHandler::FixItemIndex(const wxListBox 
*lbox
, 
1152         // mouse is above the first item 
1155     else if ( item 
>= lbox
->GetCount() ) 
1157         // mouse is below the last item 
1158         item 
= lbox
->GetCount() - 1; 
1164 bool wxStdListboxInputHandler::IsValidIndex(const wxListBox 
*lbox
, int item
) 
1166     return item 
>= 0 && item 
< lbox
->GetCount(); 
1170 wxStdListboxInputHandler::SetupCapture(wxListBox 
*lbox
, 
1171                                        const wxMouseEvent
& event
, 
1174     // we currently only allow selecting with the left mouse button, if we 
1175     // do need to allow using other buttons too we might use the code 
1178     m_btnCapture 
= event
.LeftDown() 
1187     wxControlAction action
; 
1188     if ( lbox
->HasMultipleSelection() ) 
1190         if ( lbox
->GetWindowStyle() & wxLB_MULTIPLE 
) 
1192             if ( m_toggleOnPressAlways 
) 
1194                 // toggle the item right now 
1195                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1199             m_actionMouse 
= wxACTION_LISTBOX_SETFOCUS
; 
1201         else // wxLB_EXTENDED listbox 
1203             // simple click in an extended sel listbox clears the old 
1204             // selection and adds the clicked item to it then, ctrl-click 
1205             // toggles an item to it and shift-click adds a range between 
1206             // the old selection anchor and the clicked item 
1207             if ( event
.ControlDown() ) 
1209                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1211                 action 
= wxACTION_LISTBOX_TOGGLE
; 
1213             else if ( event
.ShiftDown() ) 
1215                 action 
= wxACTION_LISTBOX_EXTENDSEL
; 
1217             else // simple click 
1219                 lbox
->PerformAction(wxACTION_LISTBOX_ANCHOR
, item
); 
1221                 action 
= wxACTION_LISTBOX_SELECT
; 
1224             m_actionMouse 
= wxACTION_LISTBOX_EXTENDSEL
; 
1227     else // single selection 
1230         action 
= wxACTION_LISTBOX_SELECT
; 
1233     // by default we always do track it 
1234     m_trackMouseOutside 
= TRUE
; 
1239 bool wxStdListboxInputHandler::HandleKey(wxInputConsumer 
*consumer
, 
1240                                          const wxKeyEvent
& event
, 
1243     // we're only interested in the key press events 
1244     if ( pressed 
&& !event
.AltDown() ) 
1246         bool isMoveCmd 
= TRUE
; 
1247         int style 
= consumer
->GetInputWindow()->GetWindowStyle(); 
1249         wxControlAction action
; 
1252         int keycode 
= event
.GetKeyCode(); 
1256             case WXK_UP
:    action 
= wxACTION_LISTBOX_MOVEUP
; break; 
1257             case WXK_DOWN
:  action 
= wxACTION_LISTBOX_MOVEDOWN
; break; 
1258             case WXK_PRIOR
: action 
= wxACTION_LISTBOX_PAGEUP
; break; 
1259             case WXK_NEXT
:  action 
= wxACTION_LISTBOX_PAGEDOWN
; break; 
1260             case WXK_HOME
:  action 
= wxACTION_LISTBOX_START
; break; 
1261             case WXK_END
:   action 
= wxACTION_LISTBOX_END
; break; 
1265                 if ( style 
& wxLB_MULTIPLE 
) 
1267                     action 
= wxACTION_LISTBOX_TOGGLE
; 
1273                 action 
= wxACTION_LISTBOX_ACTIVATE
; 
1278                 if ( (keycode 
< 255) && wxIsalnum(keycode
) ) 
1280                     action 
= wxACTION_LISTBOX_FIND
; 
1281                     strArg 
= (wxChar
)keycode
; 
1287             consumer
->PerformAction(action
, -1, strArg
); 
1291                 if ( style 
& wxLB_SINGLE 
) 
1293                     // the current item is always the one selected 
1294                     consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1296                 else if ( style 
& wxLB_EXTENDED 
) 
1298                     if ( event
.ShiftDown() ) 
1299                         consumer
->PerformAction(wxACTION_LISTBOX_EXTENDSEL
); 
1302                         // select the item and make it the new selection anchor 
1303                         consumer
->PerformAction(wxACTION_LISTBOX_SELECT
); 
1304                         consumer
->PerformAction(wxACTION_LISTBOX_ANCHOR
); 
1307                 //else: nothing to do for multiple selection listboxes 
1314     return wxStdInputHandler::HandleKey(consumer
, event
, pressed
); 
1317 bool wxStdListboxInputHandler::HandleMouse(wxInputConsumer 
*consumer
, 
1318                                            const wxMouseEvent
& event
) 
1320     wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1321     int item 
= HitTest(lbox
, event
); 
1322     wxControlAction action
; 
1324     // when the left mouse button is pressed, capture the mouse and track the 
1325     // item under mouse (if the mouse leaves the window, we will still be 
1326     // getting the mouse move messages generated by wxScrollWindow) 
1327     if ( event
.LeftDown() ) 
1329         // capture the mouse to track the selected item 
1330         lbox
->CaptureMouse(); 
1332         action 
= SetupCapture(lbox
, event
, item
); 
1334     else if ( m_btnCapture 
&& event
.ButtonUp(m_btnCapture
) ) 
1336         // when the left mouse button is released, release the mouse too 
1337         wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1340             winCapture
->ReleaseMouse(); 
1343             // generate the last event to triiger sending the selection event 
1344             action 
= m_actionMouse
; 
1346         //else: the mouse wasn't presed over the listbox, only released here 
1348     else if ( event
.LeftDClick() ) 
1350         action 
= wxACTION_LISTBOX_ACTIVATE
; 
1355         lbox
->PerformAction(action
, item
); 
1360     return wxStdInputHandler::HandleMouse(consumer
, event
); 
1363 bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer 
*consumer
, 
1364                                                const wxMouseEvent
& event
) 
1366     wxWindow 
*winCapture 
= wxWindow::GetCapture(); 
1367     if ( winCapture 
&& (event
.GetEventObject() == winCapture
) ) 
1369         wxListBox 
*lbox 
= wxStaticCast(consumer
->GetInputWindow(), wxListBox
); 
1371         if ( !m_btnCapture 
|| !m_trackMouseOutside 
) 
1373             // someone captured the mouse for us (we always set m_btnCapture 
1374             // when we do it ourselves): in this case we only react to 
1375             // the mouse messages when they happen inside the listbox 
1376             if ( lbox
->HitTest(event
.GetPosition()) != wxHT_WINDOW_INSIDE 
) 
1380         int item 
= HitTest(lbox
, event
); 
1381         if ( !m_btnCapture 
) 
1383             // now that we have the mouse inside the listbox, do capture it 
1384             // normally - but ensure that we will still ignore the outside 
1386             SetupCapture(lbox
, event
, item
); 
1388             m_trackMouseOutside 
= FALSE
; 
1391         if ( IsValidIndex(lbox
, item
) ) 
1393             lbox
->PerformAction(m_actionMouse
, item
); 
1395         // else: don't pass invalid index to the listbox 
1397     else // we don't have capture any more 
1401             // if we lost capture unexpectedly (someone else took the capture 
1402             // from us), return to a consistent state 
1407     return wxStdInputHandler::HandleMouseMove(consumer
, event
); 
1410 #endif // wxUSE_LISTBOX