1 /////////////////////////////////////////////////////////////////////////////// 
   4 // Author:      David Webster 
   8 // Copyright:   (c) David Webster 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  15 #include "wx/window.h" 
  16 #include "wx/os2/private.h" 
  19 #include "wx/listbox.h" 
  20 #include "wx/settings.h" 
  30 #include "wx/dynarray.h" 
  36     #include  "wx/ownerdrw.h" 
  39   IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
  41 // ============================================================================ 
  42 // list box item declaration and implementation 
  43 // ============================================================================ 
  47 class wxListBoxItem 
: public wxOwnerDrawn
 
  50     wxListBoxItem(const wxString
& str 
= ""); 
  53 wxListBoxItem::wxListBoxItem(const wxString
& str
) : wxOwnerDrawn(str
, FALSE
) 
  55     // no bitmaps/checkmarks 
  59 wxOwnerDrawn 
*wxListBox::CreateItem(size_t n
) 
  61     return new wxListBoxItem(); 
  64 #endif  //USE_OWNER_DRAWN 
  66 // ============================================================================ 
  67 // list box control implementation 
  68 // ============================================================================ 
  71 wxListBox::wxListBox() 
  77 bool wxListBox::Create(wxWindow 
*parent
, 
  81                        int n
, const wxString choices
[], 
  84                        const wxValidator
& validator
, 
  94     SetValidator(validator
); 
  98         parent
->AddChild(this); 
 100     wxSystemSettings settings
; 
 101     SetBackgroundColour(settings
.GetSystemColour(wxSYS_COLOUR_WINDOW
)); 
 102     SetForegroundColour(parent
->GetForegroundColour()); 
 104     m_windowId 
= ( id 
== -1 ) ? (int)NewControlId() : id
; 
 110     m_windowStyle 
= style
; 
 114     DWORD wstyle = WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | 
 115                    LBS_NOTIFY | LBS_HASSTRINGS; 
 116     if (m_windowStyle & wxLB_MULTIPLE) 
 117         wstyle |= LBS_MULTIPLESEL; 
 118     else if (m_windowStyle & wxLB_EXTENDED) 
 119         wstyle |= LBS_EXTENDEDSEL; 
 121     if (m_windowStyle & wxLB_ALWAYS_SB) 
 122         wstyle |= LBS_DISABLENOSCROLL; 
 123     if (m_windowStyle & wxLB_HSCROLL) 
 124         wstyle |= WS_HSCROLL; 
 125     if (m_windowStyle & wxLB_SORT) 
 128 #if wxUSE_OWNER_DRAWN 
 129     if ( m_windowStyle & wxLB_OWNERDRAW ) { 
 130         // we don't support LBS_OWNERDRAWVARIABLE yet 
 131         wstyle |= LBS_OWNERDRAWFIXED; 
 135     // Without this style, you get unexpected heights, so e.g. constraint layout 
 136     // doesn't work properly 
 137     wstyle |= LBS_NOINTEGRALHEIGHT; 
 140     WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); 
 142     // Even with extended styles, need to combine with WS_BORDER for them to 
 144     if ( want3D || wxStyleHasBorder(m_windowStyle) ) 
 149     m_hWnd = (WXHWND)::CreateWindowEx(exStyle, wxT("LISTBOX"), NULL, 
 152             (HWND)parent->GetHWND(), (HMENU)m_windowId, 
 153             wxGetInstance(), NULL); 
 155     wxCHECK_MSG( m_hWnd, FALSE, wxT("Failed to create listbox") ); 
 157     // Subclass again to catch messages 
 161     for (ui = 0; ui < (size_t)n; ui++) { 
 165     if ( (m_windowStyle & wxLB_MULTIPLE) == 0 ) 
 166         SendMessage(GetHwnd(), LB_SETCURSEL, 0, 0); 
 168     SetFont(parent
->GetFont()); 
 170     SetSize(x
, y
, width
, height
); 
 177 wxListBox::~wxListBox() 
 179 #if wxUSE_OWNER_DRAWN 
 180     size_t uiCount 
= m_aItems
.Count(); 
 181     while ( uiCount
-- != 0 ) { 
 182         delete m_aItems
[uiCount
]; 
 184 #endif // wxUSE_OWNER_DRAWN 
 187 void wxListBox::SetupColours() 
 189     SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
)); 
 190     SetForegroundColour(GetParent()->GetForegroundColour()); 
 193 // ---------------------------------------------------------------------------- 
 194 // implementation of wxListBoxBase methods 
 195 // ---------------------------------------------------------------------------- 
 197 void wxListBox::DoSetFirstItem(int N
) 
 199     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 200                  wxT("invalid index in wxListBox::SetFirstItem") ); 
 202 //    SendMessage(GetHwnd(), LB_SETTOPINDEX, (WPARAM)N, (LPARAM)0); 
 205 void wxListBox::Delete(int N
) 
 207     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 208                  wxT("invalid index in wxListBox::Delete") ); 
 210 #if wxUSE_OWNER_DRAWN 
 213 #else // !wxUSE_OWNER_DRAWN 
 214     if ( HasClientObjectData() ) 
 216         delete GetClientObject(N
); 
 218 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN 
 220 //    SendMessage(GetHwnd(), LB_DELETESTRING, N, 0); 
 223     SetHorizontalExtent(""); 
 226 int wxListBox::DoAppend(const wxString
& item
) 
 230     int index = ListBox_AddString(GetHwnd(), item); 
 233 #if wxUSE_OWNER_DRAWN 
 234     if ( m_windowStyle & wxLB_OWNERDRAW ) { 
 235         wxOwnerDrawn *pNewItem = CreateItem(index); // dummy argument 
 236         pNewItem->SetName(item); 
 237         m_aItems.Add(pNewItem); 
 238         ListBox_SetItemData(GetHwnd(), index, pNewItem); 
 242     SetHorizontalExtent(item); 
 249 void wxListBox::DoSetItems(const wxArrayString
& choices
, void** clientData
) 
 253     ShowWindow(GetHwnd(), SW_HIDE); 
 255     ListBox_ResetContent(GetHwnd()); 
 257     m_noItems = choices.GetCount(); 
 259     for (i = 0; i < m_noItems; i++) 
 261         ListBox_AddString(GetHwnd(), choices[i]); 
 264 #if wxUSE_OWNER_DRAWN 
 265             wxASSERT_MSG(clientData[ui] == NULL, 
 266                          wxT("Can't use client data with owner-drawn listboxes")); 
 267 #else // !wxUSE_OWNER_DRAWN 
 268             ListBox_SetItemData(GetHwnd(), i, clientData[i]); 
 269 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN 
 273 #if wxUSE_OWNER_DRAWN 
 274     if ( m_windowStyle & wxLB_OWNERDRAW ) { 
 275         // first delete old items 
 276         size_t ui = m_aItems.Count(); 
 277         while ( ui-- != 0 ) { 
 282         // then create new ones 
 283         for ( ui = 0; ui < (size_t)m_noItems; ui++ ) { 
 284             wxOwnerDrawn *pNewItem = CreateItem(ui); 
 285             pNewItem->SetName(choices[ui]); 
 286             m_aItems.Add(pNewItem); 
 287             ListBox_SetItemData(GetHwnd(), ui, pNewItem); 
 290 #endif // wxUSE_OWNER_DRAWN 
 292     SetHorizontalExtent(); 
 294     ShowWindow(GetHwnd(), SW_SHOW); 
 298 int wxListBox::FindString(const wxString
& s
) const 
 302     int pos = ListBox_FindStringExact(GetHwnd(), (WPARAM)-1, s); 
 311 void wxListBox::Clear() 
 313 #if wxUSE_OWNER_DRAWN 
 314     size_t uiCount 
= m_aItems
.Count(); 
 315     while ( uiCount
-- != 0 ) { 
 316         delete m_aItems
[uiCount
]; 
 320 #else // !wxUSE_OWNER_DRAWN 
 321     if ( HasClientObjectData() ) 
 323         for ( size_t n 
= 0; n 
< (size_t)m_noItems
; n
++ ) 
 325             delete GetClientObject(n
); 
 328 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN 
 332     ListBox_ResetContent(GetHwnd()); 
 335     SetHorizontalExtent(); 
 339 void wxListBox::SetSelection(int N
, bool select
) 
 341     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 342                  wxT("invalid index in wxListBox::SetSelection") ); 
 346     if ( HasMultipleSelection() ) 
 348         SendMessage(GetHwnd(), LB_SETSEL, select, N); 
 352         SendMessage(GetHwnd(), LB_SETCURSEL, select ? N : -1, 0); 
 357 bool wxListBox::IsSelected(int N
) const 
 359     wxCHECK_MSG( N 
>= 0 && N 
< m_noItems
, FALSE
, 
 360                  wxT("invalid index in wxListBox::Selected") ); 
 362 //    return SendMessage(GetHwnd(), LB_GETSEL, N, 0) == 0 ? FALSE : TRUE; 
 366 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const 
 368     return (wxClientData 
*)DoGetItemClientData(n
); 
 371 void *wxListBox::DoGetItemClientData(int n
) const 
 373     wxCHECK_MSG( n 
>= 0 && n 
< m_noItems
, NULL
, 
 374                  wxT("invalid index in wxListBox::GetClientData") ); 
 376 //    return (void *)SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0); 
 380 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
) 
 382     DoSetItemClientData(n
, clientData
); 
 385 void wxListBox::DoSetItemClientData(int n
, void *clientData
) 
 387     wxCHECK_RET( n 
>= 0 && n 
< m_noItems
, 
 388                  wxT("invalid index in wxListBox::SetClientData") ); 
 390 #if wxUSE_OWNER_DRAWN 
 391     if ( m_windowStyle 
& wxLB_OWNERDRAW 
) 
 393         // client data must be pointer to wxOwnerDrawn, otherwise we would crash 
 394         // in OnMeasure/OnDraw. 
 395         wxFAIL_MSG(wxT("Can't use client data with owner-drawn listboxes")); 
 397 #endif // wxUSE_OWNER_DRAWN 
 401     if ( ListBox_SetItemData(GetHwnd(), n, clientData) == LB_ERR ) 
 402         wxLogDebug(wxT("LB_SETITEMDATA failed")); 
 406 bool wxListBox::HasMultipleSelection() const 
 408     return (m_windowStyle 
& wxLB_MULTIPLE
) || (m_windowStyle 
& wxLB_EXTENDED
); 
 411 // Return number of selections and an array of selected integers 
 412 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const 
 418     if ( HasMultipleSelection() ) 
 420         int no_sel = ListBox_GetSelCount(GetHwnd()); 
 422             int *selections = new int[no_sel]; 
 423             int rc = ListBox_GetSelItems(GetHwnd(), no_sel, selections); 
 425             wxCHECK_MSG(rc != LB_ERR, -1, wxT("ListBox_GetSelItems failed")); 
 427             aSelections.Alloc(no_sel); 
 428             for ( int n = 0; n < no_sel; n++ ) 
 429                 aSelections.Add(selections[n]); 
 431             delete [] selections; 
 436     else  // single-selection listbox 
 438         aSelections.Add(ListBox_GetCurSel(GetHwnd())); 
 446 // Get single selection, for single choice list items 
 447 int wxListBox::GetSelection() const 
 449     wxCHECK_MSG( !HasMultipleSelection(), 
 451                  wxT("GetSelection() can't be used with multiple-selection " 
 452                     "listboxes, use GetSelections() instead.") ); 
 454 //    return ListBox_GetCurSel(GetHwnd()); 
 458 // Find string for position 
 459 wxString 
wxListBox::GetString(int N
) const 
 461     wxCHECK_MSG( N 
>= 0 && N 
< m_noItems
, "", 
 462                  wxT("invalid index in wxListBox::GetClientData") ); 
 466     int len = ListBox_GetTextLen(GetHwnd(), N); 
 468     // +1 for terminating NUL 
 470     ListBox_GetText(GetHwnd(), N, result.GetWriteBuf(len + 1)); 
 471     result.UngetWriteBuf(); 
 475     return((wxString
)""); 
 479 wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 481     wxCHECK_RET( pos 
>= 0 && pos 
<= m_noItems
, 
 482                  wxT("invalid index in wxListBox::InsertItems") ); 
 486     int nItems = items.GetCount(); 
 487     for ( int i = 0; i < nItems; i++ ) 
 488         ListBox_InsertString(GetHwnd(), i + pos, items[i]); 
 491     SetHorizontalExtent(); 
 495 void wxListBox::SetString(int N
, const wxString
& s
) 
 497     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 498                  wxT("invalid index in wxListBox::SetString") ); 
 500     // remember the state of the item 
 501     bool wasSelected 
= IsSelected(N
); 
 503     void *oldData 
= NULL
; 
 504     wxClientData 
*oldObjData 
= NULL
; 
 505     if ( m_clientDataItemsType 
== ClientData_Void 
) 
 506         oldData 
= GetClientData(N
); 
 507     else if ( m_clientDataItemsType 
== ClientData_Object 
) 
 508         oldObjData 
= GetClientObject(N
); 
 512     // delete and recreate it 
 513     SendMessage(GetHwnd(), LB_DELETESTRING, N, 0); 
 516     if ( N == m_noItems - 1 ) 
 519     ListBox_InsertString(GetHwnd(), newN, s); 
 521     // restore the client data 
 523         SetClientData(N, oldData); 
 524     else if ( oldObjData ) 
 525         SetClientObject(N, oldObjData); 
 527     // we may have lost the selection 
 531 #if wxUSE_OWNER_DRAWN 
 532     if ( m_windowStyle & wxLB_OWNERDRAW ) 
 533         // update item's text 
 534         m_aItems[N]->SetName(s); 
 535 #endif  //USE_OWNER_DRAWN 
 539 int wxListBox::GetCount() const 
 544 // ---------------------------------------------------------------------------- 
 546 // ---------------------------------------------------------------------------- 
 548 // Windows-specific code to set the horizontal extent of the listbox, if 
 549 // necessary. If s is non-NULL, it's used to calculate the horizontal extent. 
 550 // Otherwise, all strings are used. 
 551 void wxListBox::SetHorizontalExtent(const wxString
& s
) 
 555     // Only necessary if we want a horizontal scrollbar 
 556     if (!(m_windowStyle & wxHSCROLL)) 
 558     TEXTMETRIC lpTextMetric; 
 562         int existingExtent = (int)SendMessage(GetHwnd(), LB_GETHORIZONTALEXTENT, 0, 0L); 
 563         HDC dc = GetWindowDC(GetHwnd()); 
 565         if (GetFont().Ok() && GetFont().GetResourceHandle()) 
 566             oldFont = (HFONT) ::SelectObject(dc, (HFONT) GetFont().GetResourceHandle()); 
 568         GetTextMetrics(dc, &lpTextMetric); 
 570         ::GetTextExtentPoint(dc, (LPTSTR) (const wxChar *)s, s.Length(), &extentXY); 
 571         int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth); 
 574             ::SelectObject(dc, oldFont); 
 576         ReleaseDC(GetHwnd(), dc); 
 577         if (extentX > existingExtent) 
 578             SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(extentX), 0L); 
 582         int largestExtent = 0; 
 583         HDC dc = GetWindowDC(GetHwnd()); 
 585         if (GetFont().Ok() && GetFont().GetResourceHandle()) 
 586             oldFont = (HFONT) ::SelectObject(dc, (HFONT) GetFont().GetResourceHandle()); 
 588         GetTextMetrics(dc, &lpTextMetric); 
 590         for (i = 0; i < m_noItems; i++) 
 592             int len = (int)SendMessage(GetHwnd(), LB_GETTEXT, i, (LONG)wxBuffer); 
 595             ::GetTextExtentPoint(dc, (LPTSTR)wxBuffer, len, &extentXY); 
 596             int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth); 
 597             if (extentX > largestExtent) 
 598                 largestExtent = extentX; 
 601             ::SelectObject(dc, oldFont); 
 603         ReleaseDC(GetHwnd(), dc); 
 604         SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(largestExtent), 0L); 
 609 wxSize 
wxListBox::DoGetBestSize() const 
 611     // find the widest string 
 614     for ( int i 
= 0; i 
< m_noItems
; i
++ ) 
 616         wxString 
str(GetString(i
)); 
 617         GetTextExtent(str
, &wLine
, NULL
); 
 618         if ( wLine 
> wListbox 
) 
 622     // give it some reasonable default value if there are no strings in the 
 627     // the listbox should be slightly larger than the widest string 
 629     wxGetCharSize(GetHWND(), &cx
, &cy
, (wxFont
*)&GetFont()); 
 633     int hListbox 
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
)*(wxMax(m_noItems
, 7)); 
 635     return wxSize(wListbox
, hListbox
); 
 638 // ---------------------------------------------------------------------------- 
 640 // ---------------------------------------------------------------------------- 
 642 bool wxListBox::OS2Command(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 645        if (param == LBN_SELCANCEL) 
 647        event.extraLong = FALSE; 
 652     if (param == LBN_SELCHANGE) 
 654         wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, m_windowId); 
 655         wxArrayInt aSelections; 
 656         int count = GetSelections(aSelections); 
 659             event.m_commandInt = aSelections[0]; 
 660             event.m_clientData = GetClientData(event.m_commandInt); 
 661             wxString str(GetString(event.m_commandInt)); 
 664                 event.m_commandString = str; 
 669             event.m_commandInt = -1; 
 670             event.m_commandString.Empty(); 
 673         event.SetEventObject( this ); 
 674         ProcessCommand(event); 
 677     else if (param == LBN_DBLCLK) 
 679         wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, m_windowId); 
 680         event.SetEventObject( this ); 
 681         GetEventHandler()->ProcessEvent(event); 
 688 WXHBRUSH 
wxListBox::OnCtlColor(WXHDC pDC
, WXHWND pWnd
, WXUINT nCtlColor
, 
 689                                WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
 693     if (GetParent()->GetTransparentBackground()) 
 694         SetBkMode((HDC) pDC, TRANSPARENT); 
 696         SetBkMode((HDC) pDC, OPAQUE); 
 698     ::SetBkColor((HDC) pDC, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue())); 
 699     ::SetTextColor((HDC) pDC, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue())); 
 701     wxBrush *backgroundBrush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID); 
 703     // Note that this will be cleaned up in wxApp::OnIdle, if backgroundBrush 
 704     // has a zero usage count. 
 705     backgroundBrush->RealizeResource(); 
 706     return (WXHBRUSH) backgroundBrush->GetResourceHandle(); 
 711 // ---------------------------------------------------------------------------- 
 712 // wxCheckListBox support 
 713 // ---------------------------------------------------------------------------- 
 715 #if wxUSE_OWNER_DRAWN 
 720 // space beneath/above each row in pixels 
 721 // "standard" checklistbox use 1 here, some might prefer 2. 0 is ugly. 
 722 #define OWNER_DRAWN_LISTBOX_EXTRA_SPACE    (1) 
 724 // the height is the same for all items 
 725 // TODO should be changed for LBS_OWNERDRAWVARIABLE style listboxes 
 727 // NB: can't forward this to wxListBoxItem because LB_SETITEMDATA 
 728 //     message is not yet sent when we get here! 
 729 bool wxListBox::OS2OnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 733     // only owner-drawn control should receive this message 
 734     wxCHECK( ((m_windowStyle & wxLB_OWNERDRAW) == wxLB_OWNERDRAW), FALSE ); 
 736     MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item; 
 739     dc.SetHDC((WXHDC)CreateIC(wxT("DISPLAY"), NULL, NULL, 0)); 
 740     dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_ANSI_VAR_FONT)); 
 742     pStruct->itemHeight = dc.GetCharHeight() + 2*OWNER_DRAWN_LISTBOX_EXTRA_SPACE; 
 743     pStruct->itemWidth  = dc.GetCharWidth(); 
 750 // forward the message to the appropriate item 
 751 bool wxListBox::OS2OnDraw(WXDRAWITEMSTRUCT 
*item
) 
 755     // only owner-drawn control should receive this message 
 756     wxCHECK( ((m_windowStyle & wxLB_OWNERDRAW) == wxLB_OWNERDRAW), FALSE ); 
 758     DRAWITEMSTRUCT *pStruct = (DRAWITEMSTRUCT *)item; 
 760     long data = ListBox_GetItemData(GetHwnd(), pStruct->itemID); 
 762     wxCHECK( data && (data != LB_ERR), FALSE ); 
 764     wxListBoxItem *pItem = (wxListBoxItem *)data; 
 767     dc.SetHDC((WXHDC)pStruct->hDC, FALSE); 
 768     wxRect rect(wxPoint(pStruct->rcItem.left, pStruct->rcItem.top), 
 769             wxPoint(pStruct->rcItem.right, pStruct->rcItem.bottom)); 
 771     return pItem->OnDrawItem(dc, rect, 
 772             (wxOwnerDrawn::wxODAction)pStruct->itemAction, 
 773             (wxOwnerDrawn::wxODStatus)pStruct->itemState); 
 780 #endif //wxUSE_LISTBOX