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"
34 #include "wx/ownerdrw.h"
37 #if !USE_SHARED_LIBRARY
38 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 // ============================================================================
70 bool wxListBox::OS2Command(WXUINT param
, WXWORD
WXUNUSED(id
))
73 if (param == LBN_SELCHANGE)
75 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, m_windowId);
76 wxArrayInt aSelections;
77 int count = GetSelections(aSelections);
80 event.m_commandInt = aSelections[0] ;
81 event.m_clientData = GetClientData(event.m_commandInt);
82 wxString str(GetString(event.m_commandInt));
85 event.m_commandString = str;
90 event.m_commandInt = -1 ;
91 event.m_commandString.Empty();
94 event.SetEventObject( this );
95 ProcessCommand(event);
98 else if (param == LBN_DBLCLK)
100 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, m_windowId);
101 event.SetEventObject( this );
102 GetEventHandler()->ProcessEvent(event) ;
109 wxListBox::wxListBox()
115 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
118 int n
, const wxString choices
[],
120 const wxValidator
& validator
,
121 const wxString
& name
)
128 SetValidator(validator
);
130 if (parent
) parent
->AddChild(this);
132 wxSystemSettings settings
;
133 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
));
134 SetForegroundColour(parent
->GetForegroundColour());
136 m_windowId
= ( id
== -1 ) ? (int)NewControlId() : id
;
142 m_windowStyle
= style
;
144 // TODO create listbox
146 DWORD wstyle = WS_VISIBLE | WS_VSCROLL | WS_TABSTOP |
147 LBS_NOTIFY | LBS_HASSTRINGS;
148 if (m_windowStyle & wxLB_MULTIPLE)
149 wstyle |= LBS_MULTIPLESEL;
150 else if (m_windowStyle & wxLB_EXTENDED)
151 wstyle |= LBS_EXTENDEDSEL;
153 if (m_windowStyle & wxLB_ALWAYS_SB)
154 wstyle |= LBS_DISABLENOSCROLL ;
155 if (m_windowStyle & wxLB_HSCROLL)
156 wstyle |= WS_HSCROLL;
157 if (m_windowStyle & wxLB_SORT)
160 #if wxUSE_OWNER_DRAWN
161 if ( m_windowStyle & wxLB_OWNERDRAW ) {
162 // we don't support LBS_OWNERDRAWVARIABLE yet
163 wstyle |= LBS_OWNERDRAWFIXED;
167 // Without this style, you get unexpected heights, so e.g. constraint layout
168 // doesn't work properly
169 wstyle |= LBS_NOINTEGRALHEIGHT;
172 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ;
174 // Even with extended styles, need to combine with WS_BORDER
175 // for them to look right.
176 if ( want3D || wxStyleHasBorder(m_windowStyle) )
181 m_hWnd = (WXHWND)::CreateWindowEx(exStyle, wxT("LISTBOX"), NULL,
184 (HWND)parent->GetHWND(), (HMENU)m_windowId,
185 wxGetInstance(), NULL);
190 Ctl3dSubclassCtl(GetHwnd());
195 // Subclass again to catch messages
199 for (ui = 0; ui < (size_t)n; ui++) {
203 if ( (m_windowStyle & wxLB_MULTIPLE) == 0 )
204 SendMessage(GetHwnd(), LB_SETCURSEL, 0, 0);
206 SetFont(parent->GetFont());
208 SetSize(x, y, width, height);
216 wxListBox::~wxListBox()
218 #if wxUSE_OWNER_DRAWN
219 size_t uiCount
= m_aItems
.Count();
220 while ( uiCount
-- != 0 ) {
221 delete m_aItems
[uiCount
];
223 #endif // wxUSE_OWNER_DRAWN
226 void wxListBox::SetupColours()
228 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
));
229 SetForegroundColour(GetParent()->GetForegroundColour());
232 void wxListBox::SetFirstItem(int N
)
234 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
235 wxT("invalid index in wxListBox::SetFirstItem") );
237 // TODO SendMessage(GetHwnd(),LB_SETTOPINDEX,(WPARAM)N,(LPARAM)0) ;
240 void wxListBox::SetFirstItem(const wxString
& s
)
242 int N
= FindString(s
) ;
248 void wxListBox::Delete(int N
)
250 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
251 wxT("invalid index in wxListBox::Delete") );
253 // TODO SendMessage(GetHwnd(), LB_DELETESTRING, N, 0);
255 SetHorizontalExtent("");
258 void wxListBox::Append(const wxString
& item
)
260 int index
=0; // TODO = ListBox_AddString(GetHwnd(), item);
263 #if wxUSE_OWNER_DRAWN
264 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
265 wxOwnerDrawn
*pNewItem
= CreateItem(index
); // dummy argument
266 pNewItem
->SetName(item
);
267 m_aItems
.Add(pNewItem
);
268 // ListBox_SetItemData(GetHwnd(), index, pNewItem);
272 SetHorizontalExtent(item
);
275 void wxListBox::Append(const wxString
& item
, void *Client_data
)
277 int index
; // TODO = ListBox_AddString(GetHwnd(), item);
280 #if wxUSE_OWNER_DRAWN
281 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
282 // client data must be pointer to wxOwnerDrawn, otherwise we would crash
283 // in OnMeasure/OnDraw.
284 wxFAIL_MSG(wxT("Can't use client data with owner-drawn listboxes"));
289 // TODO ListBox_SetItemData(GetHwnd(), index, Client_data);
291 SetHorizontalExtent(item
);
294 void wxListBox::Set(int n
, const wxString
*choices
, char** clientData
)
296 // ShowWindow(GetHwnd(), SW_HIDE);
297 // ListBox_ResetContent(GetHwnd());
299 for (i
= 0; i
< n
; i
++)
301 // ListBox_AddString(GetHwnd(), choices[i]);
303 // ListBox_SetItemData(GetHwnd(), i, clientData[i]);
307 #if wxUSE_OWNER_DRAWN
308 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
309 // first delete old items
310 size_t ui
= m_aItems
.Count();
311 while ( ui
-- != 0 ) {
316 // then create new ones
317 for (ui
= 0; ui
< (size_t)n
; ui
++) {
318 wxOwnerDrawn
*pNewItem
= CreateItem(ui
);
319 pNewItem
->SetName(choices
[ui
]);
320 m_aItems
.Add(pNewItem
);
321 // ListBox_SetItemData(GetHwnd(), ui, pNewItem);
323 wxASSERT_MSG(clientData
[ui
] == NULL
,
324 wxT("Can't use client data with owner-drawn listboxes"));
329 SetHorizontalExtent("");
330 // ShowWindow(GetHwnd(), SW_SHOW);
333 int wxListBox::FindString(const wxString
& s
) const
335 int pos
; // = ListBox_FindStringExact(GetHwnd(), (WPARAM)-1, s);
336 // if (pos == LB_ERR)
342 void wxListBox::Clear()
344 // ListBox_ResetContent(GetHwnd());
346 #if wxUSE_OWNER_DRAWN
347 size_t uiCount
= m_aItems
.Count();
348 while ( uiCount
-- != 0 ) {
349 delete m_aItems
[uiCount
];
353 #endif // wxUSE_OWNER_DRAWN
356 // ListBox_GetHorizontalExtent(GetHwnd());
359 void wxListBox::SetSelection(int N
, bool select
)
361 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
362 wxT("invalid index in wxListBox::SetSelection") );
364 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
366 // SendMessage(GetHwnd(), LB_SETSEL, select, N);
373 // SendMessage(GetHwnd(), LB_SETCURSEL, N1, 0);
377 bool wxListBox::Selected(int N
) const
379 wxCHECK_MSG( N
>= 0 && N
< m_noItems
, FALSE
,
380 wxT("invalid index in wxListBox::Selected") );
382 // return SendMessage(GetHwnd(), LB_GETSEL, N, 0) == 0 ? FALSE : TRUE;
386 void wxListBox::Deselect(int N
)
388 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
389 wxT("invalid index in wxListBox::Deselect") );
391 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
393 // SendMessage(GetHwnd(), LB_SETSEL, FALSE, N);
397 void *wxListBox::GetClientData(int N
) const
399 wxCHECK_MSG( N
>= 0 && N
< m_noItems
, NULL
,
400 wxT("invalid index in wxListBox::GetClientData") );
402 // return (void *)SendMessage(GetHwnd(), LB_GETITEMDATA, N, 0);
406 void wxListBox::SetClientData(int N
, void *Client_data
)
408 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
409 wxT("invalid index in wxListBox::SetClientData") );
411 // if ( ListBox_SetItemData(GetHwnd(), N, Client_data) == LB_ERR )
412 // wxLogDebug(wxT("LB_SETITEMDATA failed"));
415 // Return number of selections and an array of selected integers
416 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
420 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
422 int no_sel
; // = ListBox_GetSelCount(GetHwnd());
424 int *selections
= new int[no_sel
];
425 // if ( ListBox_GetSelItems(GetHwnd(), no_sel, selections) == LB_ERR ) {
426 // wxFAIL_MSG(wxT("This listbox can't have single-selection style!"));
429 aSelections
.Alloc(no_sel
);
430 for ( int n
= 0; n
< no_sel
; n
++ )
431 aSelections
.Add(selections
[n
]);
433 delete [] selections
;
438 else // single-selection listbox
440 // aSelections.Add(ListBox_GetCurSel(GetHwnd()));
446 // Get single selection, for single choice list items
447 int wxListBox::GetSelection() const
449 wxCHECK_MSG( !(m_windowStyle
& wxLB_MULTIPLE
) &&
450 !(m_windowStyle
& wxLB_EXTENDED
),
452 wxT("GetSelection() can't be used with multiple-selection "
453 "listboxes, use GetSelections() instead.") );
455 // return ListBox_GetCurSel(GetHwnd());
459 // Find string for position
460 wxString
wxListBox::GetString(int N
) const
462 wxCHECK_MSG( N
>= 0 && N
< m_noItems
, "",
463 wxT("invalid index in wxListBox::GetClientData") );
465 // int len = ListBox_GetTextLen(GetHwnd(), N);
467 // +1 for terminating NUL
469 // ListBox_GetText(GetHwnd(), N, result.GetWriteBuf(len + 1));
470 result
.UngetWriteBuf();
475 // OS/2-PM-specific code to set the horizontal extent of the listbox, if
476 // necessary. If s is non-NULL, it's used to calculate the horizontal extent.
477 // Otherwise, all strings are used.
478 void wxListBox::SetHorizontalExtent(const wxString
& s
)
481 // Only necessary if we want a horizontal scrollbar
482 if (!(m_windowStyle & wxHSCROLL))
484 TEXTMETRIC lpTextMetric;
488 int existingExtent = (int)SendMessage(GetHwnd(), LB_GETHORIZONTALEXTENT, 0, 0L);
489 HDC dc = GetWindowDC(GetHwnd());
491 if (GetFont().Ok() && GetFont().GetResourceHandle())
492 oldFont = (HFONT) ::SelectObject(dc, (HFONT) GetFont().GetResourceHandle());
494 GetTextMetrics(dc, &lpTextMetric);
496 ::GetTextExtentPoint(dc, (LPTSTR) (const wxChar *)s, s.Length(), &extentXY);
497 int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
500 ::SelectObject(dc, oldFont);
502 ReleaseDC(GetHwnd(), dc);
503 if (extentX > existingExtent)
504 SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(extentX), 0L);
509 int largestExtent = 0;
510 HDC dc = GetWindowDC(GetHwnd());
512 if (GetFont().Ok() && GetFont().GetResourceHandle())
513 oldFont = (HFONT) ::SelectObject(dc, (HFONT) GetFont().GetResourceHandle());
515 GetTextMetrics(dc, &lpTextMetric);
517 for (i = 0; i < m_noItems; i++)
519 int len = (int)SendMessage(GetHwnd(), LB_GETTEXT, i, (LONG)wxBuffer);
522 ::GetTextExtentPoint(dc, (LPTSTR)wxBuffer, len, &extentXY);
523 int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
524 if (extentX > largestExtent)
525 largestExtent = extentX;
528 ::SelectObject(dc, oldFont);
530 ReleaseDC(GetHwnd(), dc);
531 SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(largestExtent), 0L);
535 void wxListBox::InsertItems(int nItems
, const wxString items
[], int pos
)
537 wxCHECK_RET( pos
>= 0 && pos
<= m_noItems
,
538 wxT("invalid index in wxListBox::InsertItems") );
541 // for (i = 0; i < nItems; i++)
542 // ListBox_InsertString(GetHwnd(), i + pos, items[i]);
545 SetHorizontalExtent(wxT(""));
548 void wxListBox::SetString(int N
, const wxString
& s
)
550 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
551 wxT("invalid index in wxListBox::SetString") );
554 if (!(m_windowStyle
& wxLB_MULTIPLE
) && !(m_windowStyle
& wxLB_EXTENDED
))
555 sel
= GetSelection();
557 void *oldData
= wxListBox::GetClientData(N
);
559 // SendMessage(GetHwnd(), LB_DELETESTRING, N, 0);
562 if (N
== (m_noItems
- 1))
565 // SendMessage(GetHwnd(), LB_INSERTSTRING, newN, (LPARAM) (const wxChar *)s);
567 wxListBox::SetClientData(N
, oldData
);
569 // Selection may have changed
573 #if wxUSE_OWNER_DRAWN
574 if ( m_windowStyle
& wxLB_OWNERDRAW
)
575 // update item's text
576 m_aItems
[N
]->SetName(s
);
577 #endif //USE_OWNER_DRAWN
580 int wxListBox::Number () const
585 // For single selection items only
586 wxString
wxListBox::GetStringSelection () const
588 int sel
= GetSelection ();
590 return this->GetString (sel
);
595 bool wxListBox::SetStringSelection (const wxString
& s
, bool flag
)
597 int sel
= FindString (s
);
600 SetSelection (sel
, flag
);
607 wxSize
wxListBox::DoGetBestSize()
609 // find the widest string
612 for ( 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 int hListbox
= EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy
)*(wxMax(m_noItems
, 7));
633 return wxSize(wListbox
, hListbox
);
636 void wxListBox::Command (wxCommandEvent
& event
)
638 if (event
.m_extraLong
)
639 SetSelection (event
.m_commandInt
);
642 Deselect (event
.m_commandInt
);
645 ProcessCommand (event
);
648 WXHBRUSH
wxListBox::OnCtlColor(WXHDC pDC
, WXHWND pWnd
, WXUINT nCtlColor
,
649 WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
655 HBRUSH hbrush = Ctl3dCtlColorEx(message, wParam, lParam);
656 return (WXHBRUSH) hbrush;
660 if (GetParent()->GetTransparentBackground())
661 SetBkMode((HDC) pDC, TRANSPARENT);
663 SetBkMode((HDC) pDC, OPAQUE);
665 ::SetBkColor((HDC) pDC, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue()));
666 ::SetTextColor((HDC) pDC, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue()));
668 wxBrush *backgroundBrush = wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID);
670 // Note that this will be cleaned up in wxApp::OnIdle, if backgroundBrush
671 // has a zero usage count.
672 backgroundBrush->RealizeResource();
674 wxBrush
*backgroundBrush
= wxTheBrushList
->FindOrCreateBrush(GetBackgroundColour(), wxSOLID
);
675 return (WXHBRUSH
) backgroundBrush
->GetResourceHandle();
678 MRESULT
wxListBox::OS2WindowProc(HWND hwnd
, WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
680 return wxControl::OS2WindowProc(hwnd
, nMsg
, wParam
, lParam
);
683 #if wxUSE_OWNER_DRAWN
688 // space beneath/above each row in pixels
689 // "standard" checklistbox use 1 here, some might prefer 2. 0 is ugly.
690 #define OWNER_DRAWN_LISTBOX_EXTRA_SPACE (1)
692 // the height is the same for all items
693 // TODO should be changed for LBS_OWNERDRAWVARIABLE style listboxes
695 // NB: can't forward this to wxListBoxItem because LB_SETITEMDATA
696 // message is not yet sent when we get here!
697 bool wxListBox::OS2OnMeasure(WXMEASUREITEMSTRUCT
*item
)
699 // only owner-drawn control should receive this message
700 wxCHECK( ((m_windowStyle
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), FALSE
);
702 // MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item;
705 // TODO: dc.SetHDC((WXHDC)CreateIC(wxT("DISPLAY"), NULL, NULL, 0));
706 dc
.SetFont(wxSystemSettings::GetSystemFont(wxSYS_ANSI_VAR_FONT
));
708 // pStruct->itemHeight = dc.GetCharHeight() + 2*OWNER_DRAWN_LISTBOX_EXTRA_SPACE;
709 // pStruct->itemWidth = dc.GetCharWidth();
714 // forward the message to the appropriate item
715 bool wxListBox::OS2OnDraw(WXDRAWITEMSTRUCT
*item
)
717 // only owner-drawn control should receive this message
718 wxCHECK( ((m_windowStyle
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), FALSE
);
720 DRAWITEMSTRUCT *pStruct = (DRAWITEMSTRUCT *)item;
722 long data = ListBox_GetItemData(GetHwnd(), pStruct->itemID);
724 wxCHECK( data && (data != LB_ERR), FALSE );
726 wxListBoxItem *pItem = (wxListBoxItem *)data;
729 dc.SetHDC((WXHDC)pStruct->hDC, FALSE);
730 wxRect rect(wxPoint(pStruct->rcItem.left, pStruct->rcItem.top),
731 wxPoint(pStruct->rcItem.right, pStruct->rcItem.bottom));
733 return pItem->OnDrawItem(dc, rect,
734 (wxOwnerDrawn::wxODAction)pStruct->itemAction,
735 (wxOwnerDrawn::wxODStatus)pStruct->itemState);