1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/listbox.cpp 
   4 // Author:      Julian Smart 
   5 // Modified by: Vadim Zeitlin (owner drawn stuff) 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  21 #include "wx/listbox.h" 
  24     #include "wx/dynarray.h" 
  25     #include "wx/settings.h" 
  31     #include "wx/window.h" 
  34 #include "wx/msw/private.h" 
  35 #include "wx/msw/dc.h" 
  40     #include  "wx/ownerdrw.h" 
  43 #if wxUSE_EXTENDED_RTTI 
  44 WX_DEFINE_FLAGS( wxListBoxStyle 
) 
  46 wxBEGIN_FLAGS( wxListBoxStyle 
) 
  47     // new style border flags, we put them first to 
  48     // use them for streaming out 
  49     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  50     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  51     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  52     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  53     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  54     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  56     // old style border flags 
  57     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  58     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  59     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  60     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  61     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  62     wxFLAGS_MEMBER(wxBORDER
) 
  64     // standard window styles 
  65     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  66     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  67     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  68     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  69     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  70     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  71     wxFLAGS_MEMBER(wxVSCROLL
) 
  72     wxFLAGS_MEMBER(wxHSCROLL
) 
  74     wxFLAGS_MEMBER(wxLB_SINGLE
) 
  75     wxFLAGS_MEMBER(wxLB_MULTIPLE
) 
  76     wxFLAGS_MEMBER(wxLB_EXTENDED
) 
  77     wxFLAGS_MEMBER(wxLB_HSCROLL
) 
  78     wxFLAGS_MEMBER(wxLB_ALWAYS_SB
) 
  79     wxFLAGS_MEMBER(wxLB_NEEDED_SB
) 
  80     wxFLAGS_MEMBER(wxLB_SORT
) 
  82 wxEND_FLAGS( wxListBoxStyle 
) 
  84 IMPLEMENT_DYNAMIC_CLASS_XTI(wxListBox
, wxControlWithItems
,"wx/listbox.h") 
  86 wxBEGIN_PROPERTIES_TABLE(wxListBox
) 
  87     wxEVENT_PROPERTY( Select 
, wxEVT_COMMAND_LISTBOX_SELECTED 
, wxCommandEvent 
) 
  88     wxEVENT_PROPERTY( DoubleClick 
, wxEVT_COMMAND_LISTBOX_DOUBLECLICKED 
, wxCommandEvent 
) 
  90     wxPROPERTY( Font 
, wxFont 
, SetFont 
, GetFont  
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
  91     wxPROPERTY_COLLECTION( Choices 
, wxArrayString 
, wxString 
, AppendString 
, GetStrings
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
  92     wxPROPERTY( Selection 
,int, SetSelection
, GetSelection
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
  93     wxPROPERTY_FLAGS( WindowStyle 
, wxListBoxStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
  94 wxEND_PROPERTIES_TABLE() 
  96 wxBEGIN_HANDLERS_TABLE(wxListBox
) 
  97 wxEND_HANDLERS_TABLE() 
  99 wxCONSTRUCTOR_4( wxListBox 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
) 
 101 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControlWithItems
) 
 111 // ============================================================================ 
 112 // list box item declaration and implementation 
 113 // ============================================================================ 
 115 #if wxUSE_OWNER_DRAWN 
 117 class wxListBoxItem 
: public wxOwnerDrawn
 
 120     wxListBoxItem(const wxString
& str 
= wxEmptyString
); 
 123 wxListBoxItem::wxListBoxItem(const wxString
& str
) : wxOwnerDrawn(str
, false) 
 125     // no bitmaps/checkmarks 
 129 wxOwnerDrawn 
*wxListBox::CreateLboxItem(size_t WXUNUSED(n
)) 
 131     return new wxListBoxItem(); 
 134 #endif  //USE_OWNER_DRAWN 
 136 // ============================================================================ 
 137 // list box control implementation 
 138 // ============================================================================ 
 140 // ---------------------------------------------------------------------------- 
 142 // ---------------------------------------------------------------------------- 
 145 wxListBox::wxListBox() 
 148     m_updateHorizontalExtent 
= false; 
 151 bool wxListBox::Create(wxWindow 
*parent
, 
 155                        int n
, const wxString choices
[], 
 157                        const wxValidator
& validator
, 
 158                        const wxString
& name
) 
 161     m_updateHorizontalExtent 
= false; 
 163     // initialize base class fields 
 164     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 167     // create the native control 
 168     if ( !MSWCreateControl(_T("LISTBOX"), wxEmptyString
, pos
, size
) ) 
 170         // control creation failed 
 174     // initialize the contents 
 175     for ( int i 
= 0; i 
< n
; i
++ ) 
 180     // now we can compute our best size correctly, so do it again 
 181     InvalidateBestSize(); 
 182     SetInitialSize(size
); 
 187 bool wxListBox::Create(wxWindow 
*parent
, 
 191                        const wxArrayString
& choices
, 
 193                        const wxValidator
& validator
, 
 194                        const wxString
& name
) 
 196     wxCArrayString 
chs(choices
); 
 197     return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 198                   style
, validator
, name
); 
 201 wxListBox::~wxListBox() 
 206 WXDWORD 
wxListBox::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 208     WXDWORD msStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 210     // always show the vertical scrollbar if necessary -- otherwise it is 
 211     // impossible to use the control with the mouse 
 212     msStyle 
|= WS_VSCROLL
; 
 214     // we always want to get the notifications 
 215     msStyle 
|= LBS_NOTIFY
; 
 217     // without this style, you get unexpected heights, so e.g. constraint 
 218     // layout doesn't work properly 
 219     msStyle 
|= LBS_NOINTEGRALHEIGHT
; 
 221     wxASSERT_MSG( !(style 
& wxLB_MULTIPLE
) || !(style 
& wxLB_EXTENDED
), 
 222                   _T("only one of listbox selection modes can be specified") ); 
 224     if ( style 
& wxLB_MULTIPLE 
) 
 225         msStyle 
|= LBS_MULTIPLESEL
; 
 226     else if ( style 
& wxLB_EXTENDED 
) 
 227         msStyle 
|= LBS_EXTENDEDSEL
; 
 229     if ( m_windowStyle 
& wxLB_ALWAYS_SB 
) 
 230         msStyle 
|= LBS_DISABLENOSCROLL
; 
 231     if ( m_windowStyle 
& wxLB_HSCROLL 
) 
 232         msStyle 
|= WS_HSCROLL
; 
 233     if ( m_windowStyle 
& wxLB_SORT 
) 
 236 #if wxUSE_OWNER_DRAWN && !defined(__WXWINCE__) 
 237     if ( m_windowStyle 
& wxLB_OWNERDRAW 
) 
 239         // we don't support LBS_OWNERDRAWVARIABLE yet and we also always put 
 240         // the strings in the listbox for simplicity even though we could have 
 241         // avoided it in this case 
 242         msStyle 
|= LBS_OWNERDRAWFIXED 
| LBS_HASSTRINGS
; 
 244 #endif // wxUSE_OWNER_DRAWN 
 249 void wxListBox::OnInternalIdle() 
 251     wxWindow::OnInternalIdle(); 
 253     if (m_updateHorizontalExtent
) 
 255         SetHorizontalExtent(wxEmptyString
); 
 256         m_updateHorizontalExtent 
= false; 
 260 // ---------------------------------------------------------------------------- 
 261 // implementation of wxListBoxBase methods 
 262 // ---------------------------------------------------------------------------- 
 264 void wxListBox::DoSetFirstItem(int N
) 
 266     wxCHECK_RET( IsValid(N
), 
 267                  wxT("invalid index in wxListBox::SetFirstItem") ); 
 269     SendMessage(GetHwnd(), LB_SETTOPINDEX
, (WPARAM
)N
, (LPARAM
)0); 
 272 void wxListBox::DoDeleteOneItem(unsigned int n
) 
 274     wxCHECK_RET( IsValid(n
), 
 275                  wxT("invalid index in wxListBox::Delete") ); 
 277     SendMessage(GetHwnd(), LB_DELETESTRING
, n
, 0); 
 280     // SetHorizontalExtent(wxEmptyString); can be slow 
 281     m_updateHorizontalExtent 
= true; 
 283     UpdateOldSelections(); 
 286 int wxListBox::FindString(const wxString
& s
, bool bCase
) const 
 288     // back to base class search for not native search type 
 290        return wxItemContainerImmutable::FindString( s
, bCase 
); 
 292     int pos 
= ListBox_FindStringExact(GetHwnd(), -1, s
.wx_str()); 
 299 void wxListBox::DoClear() 
 303     ListBox_ResetContent(GetHwnd()); 
 306     m_updateHorizontalExtent 
= true; 
 308     UpdateOldSelections(); 
 311 void wxListBox::Free() 
 313 #if wxUSE_OWNER_DRAWN 
 314     if ( m_windowStyle 
& wxLB_OWNERDRAW 
) 
 316         WX_CLEAR_ARRAY(m_aItems
); 
 318 #endif // wxUSE_OWNER_DRAWN 
 321 void wxListBox::DoSetSelection(int N
, bool select
) 
 323     wxCHECK_RET( N 
== wxNOT_FOUND 
|| IsValid(N
), 
 324                  wxT("invalid index in wxListBox::SetSelection") ); 
 326     if ( HasMultipleSelection() ) 
 328         SendMessage(GetHwnd(), LB_SETSEL
, select
, N
); 
 332         SendMessage(GetHwnd(), LB_SETCURSEL
, select 
? N 
: -1, 0); 
 335     UpdateOldSelections(); 
 338 bool wxListBox::IsSelected(int N
) const 
 340     wxCHECK_MSG( IsValid(N
), false, 
 341                  wxT("invalid index in wxListBox::Selected") ); 
 343     return SendMessage(GetHwnd(), LB_GETSEL
, N
, 0) == 0 ? false : true; 
 346 void *wxListBox::DoGetItemClientData(unsigned int n
) const 
 348     wxCHECK_MSG( IsValid(n
), NULL
, 
 349                  wxT("invalid index in wxListBox::GetClientData") ); 
 351     return (void *)SendMessage(GetHwnd(), LB_GETITEMDATA
, n
, 0); 
 354 void wxListBox::DoSetItemClientData(unsigned int n
, void *clientData
) 
 356     wxCHECK_RET( IsValid(n
), 
 357                  wxT("invalid index in wxListBox::SetClientData") ); 
 359 #if wxUSE_OWNER_DRAWN 
 360     if ( m_windowStyle 
& wxLB_OWNERDRAW 
) 
 362         // client data must be pointer to wxOwnerDrawn, otherwise we would crash 
 363         // in OnMeasure/OnDraw. 
 364         wxFAIL_MSG(wxT("Can't use client data with owner-drawn listboxes")); 
 366 #endif // wxUSE_OWNER_DRAWN 
 368     if ( ListBox_SetItemData(GetHwnd(), n
, clientData
) == LB_ERR 
) 
 369         wxLogDebug(wxT("LB_SETITEMDATA failed")); 
 372 // Return number of selections and an array of selected integers 
 373 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const 
 377     if ( HasMultipleSelection() ) 
 379         int countSel 
= ListBox_GetSelCount(GetHwnd()); 
 380         if ( countSel 
== LB_ERR 
) 
 382             wxLogDebug(_T("ListBox_GetSelCount failed")); 
 384         else if ( countSel 
!= 0 ) 
 386             int *selections 
= new int[countSel
]; 
 388             if ( ListBox_GetSelItems(GetHwnd(), 
 389                                      countSel
, selections
) == LB_ERR 
) 
 391                 wxLogDebug(wxT("ListBox_GetSelItems failed")); 
 396                 aSelections
.Alloc(countSel
); 
 397                 for ( int n 
= 0; n 
< countSel
; n
++ ) 
 398                     aSelections
.Add(selections
[n
]); 
 401             delete [] selections
; 
 406     else  // single-selection listbox 
 408         if (ListBox_GetCurSel(GetHwnd()) > -1) 
 409             aSelections
.Add(ListBox_GetCurSel(GetHwnd())); 
 411         return aSelections
.Count(); 
 415 // Get single selection, for single choice list items 
 416 int wxListBox::GetSelection() const 
 418     wxCHECK_MSG( !HasMultipleSelection(), 
 420                  wxT("GetSelection() can't be used with multiple-selection listboxes, use GetSelections() instead.") ); 
 422     return ListBox_GetCurSel(GetHwnd()); 
 425 // Find string for position 
 426 wxString 
wxListBox::GetString(unsigned int n
) const 
 428     wxCHECK_MSG( IsValid(n
), wxEmptyString
, 
 429                  wxT("invalid index in wxListBox::GetString") ); 
 431     int len 
= ListBox_GetTextLen(GetHwnd(), n
); 
 433     // +1 for terminating NUL 
 435     ListBox_GetText(GetHwnd(), n
, (wxChar
*)wxStringBuffer(result
, len 
+ 1)); 
 440 int wxListBox::DoInsertItems(const wxArrayStringsAdapter 
& items
, 
 443                              wxClientDataType type
) 
 445     MSWAllocStorage(items
, LB_INITSTORAGE
); 
 447     const bool append 
= pos 
== GetCount(); 
 449     // we must use CB_ADDSTRING when appending as only it works correctly for 
 450     // the sorted controls 
 451     const unsigned msg 
= append 
? LB_ADDSTRING 
: LB_INSERTSTRING
; 
 458     const unsigned int numItems 
= items
.GetCount(); 
 459     for ( unsigned int i 
= 0; i 
< numItems
; i
++ ) 
 461         n 
= MSWInsertOrAppendItem(pos
, items
[i
], msg
); 
 462         if ( n 
== wxNOT_FOUND 
) 
 470 #if wxUSE_OWNER_DRAWN 
 471         if ( HasFlag(wxLB_OWNERDRAW
) ) 
 473             wxOwnerDrawn 
*pNewItem 
= CreateLboxItem(n
); 
 474             pNewItem
->SetName(items
[i
]); 
 475             pNewItem
->SetFont(GetFont()); 
 476             m_aItems
.Insert(pNewItem
, n
); 
 478             ListBox_SetItemData(GetHwnd(), n
, pNewItem
); 
 480 #endif // wxUSE_OWNER_DRAWN 
 481         AssignNewItemClientData(n
, clientData
, i
, type
); 
 484     m_updateHorizontalExtent 
= true; 
 486     UpdateOldSelections(); 
 491 int wxListBox::DoListHitTest(const wxPoint
& point
) const 
 493     LRESULT lRes 
=  ::SendMessage(GetHwnd(), LB_ITEMFROMPOINT
, 
 494                                   0L, MAKELONG(point
.x
, point
.y
)); 
 496     // non zero high-order word means that this item is outside of the client 
 497     // area, IOW the point is outside of the listbox 
 498     return HIWORD(lRes
) ? wxNOT_FOUND 
: lRes
; 
 501 void wxListBox::SetString(unsigned int n
, const wxString
& s
) 
 503     wxCHECK_RET( IsValid(n
), 
 504                  wxT("invalid index in wxListBox::SetString") ); 
 506     // remember the state of the item 
 507     bool wasSelected 
= IsSelected(n
); 
 509     void *oldData 
= NULL
; 
 510     wxClientData 
*oldObjData 
= NULL
; 
 511     if ( HasClientUntypedData() ) 
 512         oldData 
= GetClientData(n
); 
 513     else if ( HasClientObjectData() ) 
 514         oldObjData 
= GetClientObject(n
); 
 516     // delete and recreate it 
 517     SendMessage(GetHwnd(), LB_DELETESTRING
, n
, 0); 
 520     if ( n 
== (m_noItems 
- 1) ) 
 523     ListBox_InsertString(GetHwnd(), newN
, s
.wx_str()); 
 525     // restore the client data 
 527         SetClientData(n
, oldData
); 
 528     else if ( oldObjData 
) 
 529         SetClientObject(n
, oldObjData
); 
 531 #if wxUSE_OWNER_DRAWN 
 532     if ( m_windowStyle 
& wxLB_OWNERDRAW 
) 
 534         // update item's text 
 535         m_aItems
[n
]->SetName(s
); 
 537         // reassign the item's data 
 538         ListBox_SetItemData(GetHwnd(), n
, m_aItems
[n
]); 
 540 #endif  //USE_OWNER_DRAWN 
 542     // we may have lost the selection 
 546     m_updateHorizontalExtent 
= true; 
 549 unsigned int wxListBox::GetCount() const 
 554 // ---------------------------------------------------------------------------- 
 555 // size-related stuff 
 556 // ---------------------------------------------------------------------------- 
 558 void wxListBox::SetHorizontalExtent(const wxString
& s
) 
 560     // in any case, our best size could have changed 
 561     InvalidateBestSize(); 
 563     // the rest is only necessary if we want a horizontal scrollbar 
 564     if ( !HasFlag(wxHSCROLL
) ) 
 568     WindowHDC 
dc(GetHwnd()); 
 569     SelectInHDC 
selFont(dc
, GetHfontOf(GetFont())); 
 571     TEXTMETRIC lpTextMetric
; 
 572     ::GetTextMetrics(dc
, &lpTextMetric
); 
 574     int largestExtent 
= 0; 
 579         // set extent to the max length of all strings 
 580         for ( unsigned int i 
= 0; i 
< m_noItems
; i
++ ) 
 582             const wxString str 
= GetString(i
); 
 583             ::GetTextExtentPoint32(dc
, str
.c_str(), str
.length(), &extentXY
); 
 585             int extentX 
= (int)(extentXY
.cx 
+ lpTextMetric
.tmAveCharWidth
); 
 586             if ( extentX 
> largestExtent 
) 
 587                 largestExtent 
= extentX
; 
 590     else // just increase the extent to the length of this string 
 592         int existingExtent 
= (int)SendMessage(GetHwnd(), 
 593                                               LB_GETHORIZONTALEXTENT
, 0, 0L); 
 595         ::GetTextExtentPoint32(dc
, s
.c_str(), s
.length(), &extentXY
); 
 597         int extentX 
= (int)(extentXY
.cx 
+ lpTextMetric
.tmAveCharWidth
); 
 598         if ( extentX 
> existingExtent 
) 
 599             largestExtent 
= extentX
; 
 603         SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT
, LOWORD(largestExtent
), 0L); 
 604     //else: it shouldn't change 
 607 wxSize 
wxListBox::DoGetBestSize() const 
 609     // find the widest string 
 612     for (unsigned int i 
= 0; i 
< m_noItems
; i
++) 
 614         wxString 
str(GetString(i
)); 
 615         GetTextExtent(str
, &wLine
, NULL
); 
 616         if ( wLine 
> wListbox 
) 
 620     // give it some reasonable default value if there are no strings in the 
 625     // the listbox should be slightly larger than the widest string 
 627     wxGetCharSize(GetHWND(), &cx
, &cy
, GetFont()); 
 631     // Add room for the scrollbar 
 632     wListbox 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
 634     // don't make the listbox too tall (limit height to 10 items) but don't 
 635     // make it too small neither 
 636     int hListbox 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
)* 
 637                     wxMin(wxMax(m_noItems
, 3), 10); 
 639     wxSize 
best(wListbox
, hListbox
); 
 644 // ---------------------------------------------------------------------------- 
 646 // ---------------------------------------------------------------------------- 
 648 bool wxListBox::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 650     if ((param 
== LBN_SELCHANGE
) && HasMultipleSelection()) 
 657     if ( param 
== LBN_SELCHANGE 
) 
 659         evtType 
= wxEVT_COMMAND_LISTBOX_SELECTED
; 
 661     else if ( param 
== LBN_DBLCLK 
) 
 663         evtType 
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
; 
 667         // some event we're not interested in 
 671     wxCommandEvent 
event(evtType
, m_windowId
); 
 672     event
.SetEventObject( this ); 
 674     // retrieve the affected item 
 675     int n 
= SendMessage(GetHwnd(), LB_GETCARETINDEX
, 0, 0); 
 678         if ( HasClientObjectData() ) 
 679             event
.SetClientObject( GetClientObject(n
) ); 
 680         else if ( HasClientUntypedData() ) 
 681             event
.SetClientData( GetClientData(n
) ); 
 683         event
.SetString(GetString(n
)); 
 692     return HandleWindowEvent(event
); 
 695 // ---------------------------------------------------------------------------- 
 696 // wxCheckListBox support 
 697 // ---------------------------------------------------------------------------- 
 699 #if wxUSE_OWNER_DRAWN 
 704 // space beneath/above each row in pixels 
 705 // "standard" checklistbox use 1 here, some might prefer 2. 0 is ugly. 
 706 #define OWNER_DRAWN_LISTBOX_EXTRA_SPACE    (1) 
 708 // the height is the same for all items 
 709 // TODO should be changed for LBS_OWNERDRAWVARIABLE style listboxes 
 711 // NB: can't forward this to wxListBoxItem because LB_SETITEMDATA 
 712 //     message is not yet sent when we get here! 
 713 bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 715     // only owner-drawn control should receive this message 
 716     wxCHECK( ((m_windowStyle 
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), false ); 
 718     MEASUREITEMSTRUCT 
*pStruct 
= (MEASUREITEMSTRUCT 
*)item
; 
 721     HDC hdc 
= GetDC(NULL
); 
 723     HDC hdc 
= CreateIC(wxT("DISPLAY"), NULL
, NULL
, 0); 
 727         wxDCTemp 
dc((WXHDC
)hdc
); 
 728         dc
.SetFont(GetFont()); 
 730         pStruct
->itemHeight 
= dc
.GetCharHeight() + 2*OWNER_DRAWN_LISTBOX_EXTRA_SPACE
; 
 731         pStruct
->itemWidth  
= dc
.GetCharWidth(); 
 735     ReleaseDC(NULL
, hdc
); 
 743 // forward the message to the appropriate item 
 744 bool wxListBox::MSWOnDraw(WXDRAWITEMSTRUCT 
*item
) 
 746     // only owner-drawn control should receive this message 
 747     wxCHECK( ((m_windowStyle 
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), false ); 
 749     DRAWITEMSTRUCT 
*pStruct 
= (DRAWITEMSTRUCT 
*)item
; 
 750     UINT itemID 
= pStruct
->itemID
; 
 752     // the item may be -1 for an empty listbox 
 753     if ( itemID 
== (UINT
)-1 ) 
 756     LRESULT data 
= ListBox_GetItemData(GetHwnd(), pStruct
->itemID
); 
 758     wxCHECK( data 
&& (data 
!= LB_ERR
), false ); 
 760     wxListBoxItem 
*pItem 
= (wxListBoxItem 
*)data
; 
 762     wxDCTemp 
dc((WXHDC
)pStruct
->hDC
); 
 763     wxPoint 
pt1(pStruct
->rcItem
.left
, pStruct
->rcItem
.top
); 
 764     wxPoint 
pt2(pStruct
->rcItem
.right
, pStruct
->rcItem
.bottom
); 
 765     wxRect 
rect(pt1
, pt2
); 
 767     return pItem
->OnDrawItem(dc
, rect
, 
 768                              (wxOwnerDrawn::wxODAction
)pStruct
->itemAction
, 
 769                              (wxOwnerDrawn::wxODStatus
)pStruct
->itemState
); 
 772 #endif // wxUSE_OWNER_DRAWN 
 774 #endif // wxUSE_LISTBOX