1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/listbox.cpp
3 // Purpose: wxListBox
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin (owner drawn stuff)
6 // Created:
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_LISTBOX
19
20 #include "wx/listbox.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/dynarray.h"
24 #include "wx/settings.h"
25 #include "wx/brush.h"
26 #include "wx/font.h"
27 #include "wx/dc.h"
28 #include "wx/dcclient.h"
29 #include "wx/utils.h"
30 #include "wx/log.h"
31 #include "wx/window.h"
32 #endif
33
34 #include "wx/msw/private.h"
35 #include "wx/msw/dc.h"
36
37 #include <windowsx.h>
38
39 #if wxUSE_OWNER_DRAWN
40 #include "wx/ownerdrw.h"
41
42 namespace
43 {
44 // space beneath/above each row in pixels
45 const int LISTBOX_EXTRA_SPACE = 1;
46 } // anonymous namespace
47 #endif // wxUSE_OWNER_DRAWN
48
49 // ============================================================================
50 // list box item declaration and implementation
51 // ============================================================================
52
53 #if wxUSE_OWNER_DRAWN
54
55 class wxListBoxItem : public wxOwnerDrawn
56 {
57 public:
wxListBoxItem(wxListBox * parent)58 wxListBoxItem(wxListBox *parent)
59 { m_parent = parent; }
60
GetParent() const61 wxListBox *GetParent() const
62 { return m_parent; }
63
GetIndex() const64 int GetIndex() const
65 { return m_parent->GetItemIndex(const_cast<wxListBoxItem*>(this)); }
66
GetName() const67 wxString GetName() const
68 { return m_parent->GetString(GetIndex()); }
69
70 private:
71 wxListBox *m_parent;
72 };
73
CreateLboxItem(size_t WXUNUSED (n))74 wxOwnerDrawn *wxListBox::CreateLboxItem(size_t WXUNUSED(n))
75 {
76 return new wxListBoxItem(this);
77 }
78
79 #endif //USE_OWNER_DRAWN
80
81 // ============================================================================
82 // list box control implementation
83 // ============================================================================
84
85 // ----------------------------------------------------------------------------
86 // creation
87 // ----------------------------------------------------------------------------
88
Init()89 void wxListBox::Init()
90 {
91 m_noItems = 0;
92 m_updateHorizontalExtent = false;
93 }
94
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)95 bool wxListBox::Create(wxWindow *parent,
96 wxWindowID id,
97 const wxPoint& pos,
98 const wxSize& size,
99 int n, const wxString choices[],
100 long style,
101 const wxValidator& validator,
102 const wxString& name)
103 {
104 // initialize base class fields
105 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
106 return false;
107
108 // create the native control
109 if ( !MSWCreateControl(wxT("LISTBOX"), wxEmptyString, pos, size) )
110 {
111 // control creation failed
112 return false;
113 }
114
115 // initialize the contents
116 for ( int i = 0; i < n; i++ )
117 {
118 Append(choices[i]);
119 }
120
121 // now we can compute our best size correctly, so do it again
122 SetInitialSize(size);
123
124 return true;
125 }
126
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)127 bool wxListBox::Create(wxWindow *parent,
128 wxWindowID id,
129 const wxPoint& pos,
130 const wxSize& size,
131 const wxArrayString& choices,
132 long style,
133 const wxValidator& validator,
134 const wxString& name)
135 {
136 wxCArrayString chs(choices);
137 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
138 style, validator, name);
139 }
140
~wxListBox()141 wxListBox::~wxListBox()
142 {
143 Clear();
144 }
145
MSWGetStyle(long style,WXDWORD * exstyle) const146 WXDWORD wxListBox::MSWGetStyle(long style, WXDWORD *exstyle) const
147 {
148 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
149
150 // we always want to get the notifications
151 msStyle |= LBS_NOTIFY;
152
153 // without this style, you get unexpected heights, so e.g. constraint
154 // layout doesn't work properly
155 msStyle |= LBS_NOINTEGRALHEIGHT;
156
157 wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED),
158 wxT("only one of listbox selection modes can be specified") );
159
160 if ( style & wxLB_MULTIPLE )
161 msStyle |= LBS_MULTIPLESEL;
162 else if ( style & wxLB_EXTENDED )
163 msStyle |= LBS_EXTENDEDSEL;
164
165 wxASSERT_MSG( !(style & wxLB_ALWAYS_SB) || !(style & wxLB_NO_SB),
166 wxT( "Conflicting styles wxLB_ALWAYS_SB and wxLB_NO_SB." ) );
167
168 if ( !(style & wxLB_NO_SB) )
169 {
170 msStyle |= WS_VSCROLL;
171 if ( style & wxLB_ALWAYS_SB )
172 msStyle |= LBS_DISABLENOSCROLL;
173 }
174
175 if ( m_windowStyle & wxLB_HSCROLL )
176 msStyle |= WS_HSCROLL;
177 if ( m_windowStyle & wxLB_SORT )
178 msStyle |= LBS_SORT;
179
180 #if wxUSE_OWNER_DRAWN && !defined(__WXWINCE__)
181 if ( m_windowStyle & wxLB_OWNERDRAW )
182 {
183 // we don't support LBS_OWNERDRAWVARIABLE yet and we also always put
184 // the strings in the listbox for simplicity even though we could have
185 // avoided it in this case
186 msStyle |= LBS_OWNERDRAWFIXED | LBS_HASSTRINGS;
187 }
188 #endif // wxUSE_OWNER_DRAWN
189
190 return msStyle;
191 }
192
OnInternalIdle()193 void wxListBox::OnInternalIdle()
194 {
195 wxWindow::OnInternalIdle();
196
197 if (m_updateHorizontalExtent)
198 {
199 SetHorizontalExtent(wxEmptyString);
200 m_updateHorizontalExtent = false;
201 }
202 }
203
MSWOnItemsChanged()204 void wxListBox::MSWOnItemsChanged()
205 {
206 // we need to do two things when items change: update their max horizontal
207 // extent so that horizontal scrollbar could be shown or hidden as
208 // appropriate and also invlaidate the best size
209 //
210 // updating the max extent is slow (it's an O(N) operation) and so we defer
211 // it until the idle time but the best size should be invalidated
212 // immediately doing it in idle time is too late -- layout using incorrect
213 // old best size will have been already done by then
214
215 m_updateHorizontalExtent = true;
216
217 InvalidateBestSize();
218 }
219
220 // ----------------------------------------------------------------------------
221 // implementation of wxListBoxBase methods
222 // ----------------------------------------------------------------------------
223
DoSetFirstItem(int N)224 void wxListBox::DoSetFirstItem(int N)
225 {
226 wxCHECK_RET( IsValid(N),
227 wxT("invalid index in wxListBox::SetFirstItem") );
228
229 SendMessage(GetHwnd(), LB_SETTOPINDEX, (WPARAM)N, (LPARAM)0);
230 }
231
DoDeleteOneItem(unsigned int n)232 void wxListBox::DoDeleteOneItem(unsigned int n)
233 {
234 wxCHECK_RET( IsValid(n),
235 wxT("invalid index in wxListBox::Delete") );
236
237 #if wxUSE_OWNER_DRAWN
238 if ( HasFlag(wxLB_OWNERDRAW) )
239 {
240 delete m_aItems[n];
241 m_aItems.RemoveAt(n);
242 }
243 #endif // wxUSE_OWNER_DRAWN
244
245 SendMessage(GetHwnd(), LB_DELETESTRING, n, 0);
246 m_noItems--;
247
248 MSWOnItemsChanged();
249
250 UpdateOldSelections();
251 }
252
FindString(const wxString & s,bool bCase) const253 int wxListBox::FindString(const wxString& s, bool bCase) const
254 {
255 // back to base class search for not native search type
256 if (bCase)
257 return wxItemContainerImmutable::FindString( s, bCase );
258
259 int pos = ListBox_FindStringExact(GetHwnd(), -1, s.t_str());
260 if (pos == LB_ERR)
261 return wxNOT_FOUND;
262 else
263 return pos;
264 }
265
DoClear()266 void wxListBox::DoClear()
267 {
268 #if wxUSE_OWNER_DRAWN
269 if ( HasFlag(wxLB_OWNERDRAW) )
270 {
271 WX_CLEAR_ARRAY(m_aItems);
272 }
273 #endif // wxUSE_OWNER_DRAWN
274
275 ListBox_ResetContent(GetHwnd());
276
277 m_noItems = 0;
278 MSWOnItemsChanged();
279
280 UpdateOldSelections();
281 }
282
DoSetSelection(int N,bool select)283 void wxListBox::DoSetSelection(int N, bool select)
284 {
285 wxCHECK_RET( N == wxNOT_FOUND || IsValid(N),
286 wxT("invalid index in wxListBox::SetSelection") );
287
288 if ( HasMultipleSelection() )
289 {
290 // Setting selection to -1 should deselect everything.
291 const bool deselectAll = N == wxNOT_FOUND;
292 SendMessage(GetHwnd(), LB_SETSEL,
293 deselectAll ? FALSE : select,
294 deselectAll ? -1 : N);
295 }
296 else
297 {
298 SendMessage(GetHwnd(), LB_SETCURSEL, select ? N : -1, 0);
299 }
300
301 UpdateOldSelections();
302 }
303
IsSelected(int N) const304 bool wxListBox::IsSelected(int N) const
305 {
306 wxCHECK_MSG( IsValid(N), false,
307 wxT("invalid index in wxListBox::Selected") );
308
309 return SendMessage(GetHwnd(), LB_GETSEL, N, 0) == 0 ? false : true;
310 }
311
DoGetItemClientData(unsigned int n) const312 void *wxListBox::DoGetItemClientData(unsigned int n) const
313 {
314 // This is done here for the same reasons as in wxChoice method with the
315 // same name.
316 SetLastError(ERROR_SUCCESS);
317
318 LPARAM rc = SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0);
319 if ( rc == LB_ERR && GetLastError() != ERROR_SUCCESS )
320 {
321 wxLogLastError(wxT("LB_GETITEMDATA"));
322
323 return NULL;
324 }
325
326 return (void *)rc;
327 }
328
DoSetItemClientData(unsigned int n,void * clientData)329 void wxListBox::DoSetItemClientData(unsigned int n, void *clientData)
330 {
331 if ( ListBox_SetItemData(GetHwnd(), n, clientData) == LB_ERR )
332 {
333 wxLogDebug(wxT("LB_SETITEMDATA failed"));
334 }
335 }
336
337 // Return number of selections and an array of selected integers
GetSelections(wxArrayInt & aSelections) const338 int wxListBox::GetSelections(wxArrayInt& aSelections) const
339 {
340 aSelections.Empty();
341
342 if ( HasMultipleSelection() )
343 {
344 int countSel = ListBox_GetSelCount(GetHwnd());
345 if ( countSel == LB_ERR )
346 {
347 wxLogDebug(wxT("ListBox_GetSelCount failed"));
348 }
349 else if ( countSel != 0 )
350 {
351 int *selections = new int[countSel];
352
353 if ( ListBox_GetSelItems(GetHwnd(),
354 countSel, selections) == LB_ERR )
355 {
356 wxLogDebug(wxT("ListBox_GetSelItems failed"));
357 countSel = -1;
358 }
359 else
360 {
361 aSelections.Alloc(countSel);
362 for ( int n = 0; n < countSel; n++ )
363 aSelections.Add(selections[n]);
364 }
365
366 delete [] selections;
367 }
368
369 return countSel;
370 }
371 else // single-selection listbox
372 {
373 if (ListBox_GetCurSel(GetHwnd()) > -1)
374 aSelections.Add(ListBox_GetCurSel(GetHwnd()));
375
376 return aSelections.Count();
377 }
378 }
379
380 // Get single selection, for single choice list items
GetSelection() const381 int wxListBox::GetSelection() const
382 {
383 wxCHECK_MSG( !HasMultipleSelection(),
384 -1,
385 wxT("GetSelection() can't be used with multiple-selection listboxes, use GetSelections() instead.") );
386
387 return ListBox_GetCurSel(GetHwnd());
388 }
389
390 // Find string for position
GetString(unsigned int n) const391 wxString wxListBox::GetString(unsigned int n) const
392 {
393 wxCHECK_MSG( IsValid(n), wxEmptyString,
394 wxT("invalid index in wxListBox::GetString") );
395
396 int len = ListBox_GetTextLen(GetHwnd(), n);
397
398 // +1 for terminating NUL
399 wxString result;
400 ListBox_GetText(GetHwnd(), n, (wxChar*)wxStringBuffer(result, len + 1));
401
402 return result;
403 }
404
DoInsertItems(const wxArrayStringsAdapter & items,unsigned int pos,void ** clientData,wxClientDataType type)405 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items,
406 unsigned int pos,
407 void **clientData,
408 wxClientDataType type)
409 {
410 MSWAllocStorage(items, LB_INITSTORAGE);
411
412 const bool append = pos == GetCount();
413
414 // we must use CB_ADDSTRING when appending as only it works correctly for
415 // the sorted controls
416 const unsigned msg = append ? LB_ADDSTRING : LB_INSERTSTRING;
417
418 if ( append )
419 pos = 0;
420
421 int n = wxNOT_FOUND;
422
423 const unsigned int numItems = items.GetCount();
424 for ( unsigned int i = 0; i < numItems; i++ )
425 {
426 n = MSWInsertOrAppendItem(pos, items[i], msg);
427 if ( n == wxNOT_FOUND )
428 return n;
429
430 if ( !append )
431 pos++;
432
433 ++m_noItems;
434
435 #if wxUSE_OWNER_DRAWN
436 if ( HasFlag(wxLB_OWNERDRAW) )
437 {
438 wxOwnerDrawn *pNewItem = CreateLboxItem(n);
439 pNewItem->SetFont(GetFont());
440 m_aItems.Insert(pNewItem, n);
441 }
442 #endif // wxUSE_OWNER_DRAWN
443 AssignNewItemClientData(n, clientData, i, type);
444 }
445
446 MSWOnItemsChanged();
447
448 UpdateOldSelections();
449
450 return n;
451 }
452
DoHitTestList(const wxPoint & point) const453 int wxListBox::DoHitTestList(const wxPoint& point) const
454 {
455 LRESULT lRes = ::SendMessage(GetHwnd(), LB_ITEMFROMPOINT,
456 0, MAKELPARAM(point.x, point.y));
457
458 // non zero high-order word means that this item is outside of the client
459 // area, IOW the point is outside of the listbox
460 return HIWORD(lRes) ? wxNOT_FOUND : LOWORD(lRes);
461 }
462
SetString(unsigned int n,const wxString & s)463 void wxListBox::SetString(unsigned int n, const wxString& s)
464 {
465 wxCHECK_RET( IsValid(n),
466 wxT("invalid index in wxListBox::SetString") );
467
468 // remember the state of the item
469 bool wasSelected = IsSelected(n);
470
471 void *oldData = NULL;
472 wxClientData *oldObjData = NULL;
473 if ( HasClientUntypedData() )
474 oldData = GetClientData(n);
475 else if ( HasClientObjectData() )
476 oldObjData = GetClientObject(n);
477
478 // delete and recreate it
479 SendMessage(GetHwnd(), LB_DELETESTRING, n, 0);
480
481 int newN = n;
482 if ( n == (m_noItems - 1) )
483 newN = -1;
484
485 ListBox_InsertString(GetHwnd(), newN, s.t_str());
486
487 // restore the client data
488 if ( oldData )
489 SetClientData(n, oldData);
490 else if ( oldObjData )
491 SetClientObject(n, oldObjData);
492
493 // we may have lost the selection
494 if ( wasSelected )
495 Select(n);
496
497 MSWOnItemsChanged();
498 }
499
GetCount() const500 unsigned int wxListBox::GetCount() const
501 {
502 return m_noItems;
503 }
504
505 // ----------------------------------------------------------------------------
506 // size-related stuff
507 // ----------------------------------------------------------------------------
508
SetHorizontalExtent(const wxString & s)509 void wxListBox::SetHorizontalExtent(const wxString& s)
510 {
511 // the rest is only necessary if we want a horizontal scrollbar
512 if ( !HasFlag(wxHSCROLL) )
513 return;
514
515
516 WindowHDC dc(GetHwnd());
517 SelectInHDC selFont(dc, GetHfontOf(GetFont()));
518
519 TEXTMETRIC lpTextMetric;
520 ::GetTextMetrics(dc, &lpTextMetric);
521
522 int largestExtent = 0;
523 SIZE extentXY;
524
525 if ( s.empty() )
526 {
527 // set extent to the max length of all strings
528 for ( unsigned int i = 0; i < m_noItems; i++ )
529 {
530 const wxString str = GetString(i);
531 ::GetTextExtentPoint32(dc, str.c_str(), str.length(), &extentXY);
532
533 int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
534 if ( extentX > largestExtent )
535 largestExtent = extentX;
536 }
537 }
538 else // just increase the extent to the length of this string
539 {
540 int existingExtent = (int)SendMessage(GetHwnd(),
541 LB_GETHORIZONTALEXTENT, 0, 0L);
542
543 ::GetTextExtentPoint32(dc, s.c_str(), s.length(), &extentXY);
544
545 int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
546 if ( extentX > existingExtent )
547 largestExtent = extentX;
548 }
549
550 if ( largestExtent )
551 SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(largestExtent), 0L);
552 //else: it shouldn't change
553 }
554
DoGetBestClientSize() const555 wxSize wxListBox::DoGetBestClientSize() const
556 {
557 // find the widest string
558 int wLine;
559 int wListbox = 0;
560 for (unsigned int i = 0; i < m_noItems; i++)
561 {
562 wxString str(GetString(i));
563 GetTextExtent(str, &wLine, NULL);
564 if ( wLine > wListbox )
565 wListbox = wLine;
566 }
567
568 // give it some reasonable default value if there are no strings in the
569 // list
570 if ( wListbox == 0 )
571 wListbox = 100;
572
573 // the listbox should be slightly larger than the widest string
574 wListbox += 3*GetCharWidth();
575
576 // add room for the scrollbar
577 wListbox += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
578
579 // don't make the listbox too tall (limit height to 10 items) but don't
580 // make it too small neither
581 int hListbox = SendMessage(GetHwnd(), LB_GETITEMHEIGHT, 0, 0)*
582 wxMin(wxMax(m_noItems, 3), 10);
583
584 return wxSize(wListbox, hListbox);
585 }
586
587 // ----------------------------------------------------------------------------
588 // callbacks
589 // ----------------------------------------------------------------------------
590
MSWCommand(WXUINT param,WXWORD WXUNUSED (id))591 bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
592 {
593 wxEventType evtType;
594 if ( param == LBN_SELCHANGE )
595 {
596 if ( HasMultipleSelection() )
597 return CalcAndSendEvent();
598
599 evtType = wxEVT_LISTBOX;
600 }
601 else if ( param == LBN_DBLCLK )
602 {
603 // Clicking under the last item in the listbox generates double click
604 // event for the currently selected item which is rather surprising.
605 // Avoid the surprise by checking that we do have an item under mouse.
606 const DWORD pos = ::GetMessagePos();
607 const wxPoint pt(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
608 if ( HitTest(ScreenToClient(pt)) == wxNOT_FOUND )
609 return false;
610
611 evtType = wxEVT_LISTBOX_DCLICK;
612 }
613 else
614 {
615 // some event we're not interested in
616 return false;
617 }
618
619 const int n = ListBox_GetCurSel(GetHwnd());
620
621 // We get events even when mouse is clicked outside of any valid item from
622 // Windows, just ignore them.
623 if ( n == wxNOT_FOUND )
624 return false;
625
626 if ( param == LBN_SELCHANGE )
627 {
628 if ( !DoChangeSingleSelection(n) )
629 return false;
630 }
631
632 // Do generate an event otherwise.
633 return SendEvent(evtType, n, true /* selection */);
634 }
635
636 // ----------------------------------------------------------------------------
637 // owner-drawn list boxes support
638 // ----------------------------------------------------------------------------
639
640 #if wxUSE_OWNER_DRAWN
641
642 // misc overloaded methods
643 // -----------------------
644
SetFont(const wxFont & font)645 bool wxListBox::SetFont(const wxFont &font)
646 {
647 if ( HasFlag(wxLB_OWNERDRAW) )
648 {
649 const unsigned count = m_aItems.GetCount();
650 for ( unsigned i = 0; i < count; i++ )
651 m_aItems[i]->SetFont(font);
652
653 // Non owner drawn list boxes update the item height on their own, but
654 // we need to do it manually in the owner drawn case.
655 wxClientDC dc(this);
656 dc.SetFont(font);
657 SendMessage(GetHwnd(), LB_SETITEMHEIGHT, 0,
658 dc.GetCharHeight() + 2 * LISTBOX_EXTRA_SPACE);
659 }
660
661 wxListBoxBase::SetFont(font);
662
663 return true;
664 }
665
GetItemRect(size_t n,wxRect & rect) const666 bool wxListBox::GetItemRect(size_t n, wxRect& rect) const
667 {
668 wxCHECK_MSG( IsValid(n), false,
669 wxT("invalid index in wxListBox::GetItemRect") );
670
671 RECT rc;
672
673 if ( ListBox_GetItemRect(GetHwnd(), n, &rc) != LB_ERR )
674 {
675 rect = wxRectFromRECT(rc);
676 return true;
677 }
678 else
679 {
680 // couldn't retrieve rect: for example, item isn't visible
681 return false;
682 }
683 }
684
RefreshItem(size_t n)685 bool wxListBox::RefreshItem(size_t n)
686 {
687 wxRect rect;
688 if ( !GetItemRect(n, rect) )
689 return false;
690
691 RECT rc;
692 wxCopyRectToRECT(rect, rc);
693
694 return ::InvalidateRect((HWND)GetHWND(), &rc, FALSE) == TRUE;
695 }
696
697
698 // drawing
699 // -------
700
701 // the height is the same for all items
702 // TODO should be changed for LBS_OWNERDRAWVARIABLE style listboxes
703
704 // NB: can't forward this to wxListBoxItem because LB_SETITEMDATA
705 // message is not yet sent when we get here!
MSWOnMeasure(WXMEASUREITEMSTRUCT * item)706 bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item)
707 {
708 // only owner-drawn control should receive this message
709 wxCHECK( HasFlag(wxLB_OWNERDRAW), false );
710
711 MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item;
712
713 #ifdef __WXWINCE__
714 HDC hdc = GetDC(NULL);
715 #else
716 HDC hdc = CreateIC(wxT("DISPLAY"), NULL, NULL, 0);
717 #endif
718
719 {
720 wxDCTemp dc((WXHDC)hdc);
721 dc.SetFont(GetFont());
722
723 pStruct->itemHeight = dc.GetCharHeight() + 2 * LISTBOX_EXTRA_SPACE;
724 pStruct->itemWidth = dc.GetCharWidth();
725 }
726
727 #ifdef __WXWINCE__
728 ReleaseDC(NULL, hdc);
729 #else
730 DeleteDC(hdc);
731 #endif
732
733 return true;
734 }
735
736 // forward the message to the appropriate item
MSWOnDraw(WXDRAWITEMSTRUCT * item)737 bool wxListBox::MSWOnDraw(WXDRAWITEMSTRUCT *item)
738 {
739 // only owner-drawn control should receive this message
740 wxCHECK( HasFlag(wxLB_OWNERDRAW), false );
741
742 DRAWITEMSTRUCT *pStruct = (DRAWITEMSTRUCT *)item;
743
744 // the item may be -1 for an empty listbox
745 if ( pStruct->itemID == (UINT)-1 )
746 return false;
747
748 wxOwnerDrawn *pItem = m_aItems[pStruct->itemID];
749
750 wxDCTemp dc((WXHDC)pStruct->hDC);
751
752 return pItem->OnDrawItem(dc, wxRectFromRECT(pStruct->rcItem),
753 (wxOwnerDrawn::wxODAction)pStruct->itemAction,
754 (wxOwnerDrawn::wxODStatus)(pStruct->itemState | wxOwnerDrawn::wxODHidePrefix));
755 }
756
757 #endif // wxUSE_OWNER_DRAWN
758
759 #endif // wxUSE_LISTBOX
760