1 ///////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin (owner drawn stuff)
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "listbox.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
24 #include "wx/listbox.h"
25 #include "wx/settings.h"
28 #include "wx/msw/private.h"
34 #include <wx/msw/gnuwin32/extra.h>
42 #include "wx/ownerdrw.h"
45 #if !USE_SHARED_LIBRARY
46 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
49 // ============================================================================
50 // list box item declaration and implementation
51 // ============================================================================
55 class wxListBoxItem
: public wxOwnerDrawn
58 wxListBoxItem(const wxString
& str
= "");
61 wxListBoxItem::wxListBoxItem(const wxString
& str
) : wxOwnerDrawn(str
, FALSE
)
63 // no bitmaps/checkmarks
67 wxOwnerDrawn
*wxListBox::CreateItem(uint n
)
69 return new wxListBoxItem();
72 #endif //USE_OWNER_DRAWN
74 // ============================================================================
75 // list box control implementation
76 // ============================================================================
78 // this macro is dangerous but still better than tons of (HWND)GetHWND()
79 #define hwnd (HWND)GetHWND()
81 bool wxListBox::MSWCommand(const WXUINT param
, const WXWORD
WXUNUSED(id
))
84 if (param == LBN_SELCANCEL)
86 event.extraLong = FALSE;
89 if (param
== LBN_SELCHANGE
)
91 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_SELECTED
, m_windowId
);
93 int count
= GetSelections(&liste
) ;
96 event
.m_commandInt
= liste
[0] ;
97 event
.m_clientData
= GetClientData(event
.m_commandInt
);
98 wxString
str(GetString(event
.m_commandInt
));
100 event
.m_commandString
= copystring((char *)(const char *)str
);
104 event
.m_commandInt
= -1 ;
105 event
.m_commandString
= copystring("") ;
108 event
.SetEventObject( this );
109 ProcessCommand(event
);
110 if (event
.m_commandString
)
111 delete[] event
.m_commandString
;
114 else if (param
== LBN_DBLCLK
)
116 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, m_windowId
);
117 event
.SetEventObject( this );
118 if ( !GetEventHandler()->ProcessEvent(event
) )
120 #if WXWIN_COMPATIBILITY
121 wxWindow
*parent
= (wxWindow
*)GetParent();
123 parent
->GetEventHandler()->OnDefaultAction(this);
132 wxListBox::wxListBox(void)
139 bool wxListBox::Create(wxWindow
*parent
, const wxWindowID id
,
142 const int n
, const wxString choices
[],
144 const wxValidator
& validator
,
145 const wxString
& name
)
153 SetValidator(validator
);
155 if (parent
) parent
->AddChild(this);
157 wxSystemSettings settings
;
158 SetBackgroundColour(settings
.GetSystemColour(wxSYS_COLOUR_WINDOW
));
159 SetForegroundColour(parent
->GetDefaultForegroundColour());
161 m_windowId
= ( id
== -1 ) ? (int)NewControlId() : id
;
167 m_windowStyle
= style
;
169 DWORD wstyle
= WS_VSCROLL
| WS_TABSTOP
| LBS_NOTIFY
| LBS_HASSTRINGS
;
170 if (m_windowStyle
& wxLB_MULTIPLE
)
171 wstyle
|= LBS_MULTIPLESEL
;
172 else if (m_windowStyle
& wxLB_EXTENDED
)
173 wstyle
|= LBS_EXTENDEDSEL
;
175 if (m_windowStyle
& wxLB_ALWAYS_SB
)
176 wstyle
|= LBS_DISABLENOSCROLL
;
177 if (m_windowStyle
& wxLB_HSCROLL
)
178 wstyle
|= WS_HSCROLL
;
179 if (m_windowStyle
& wxLB_SORT
)
183 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
184 // we don't support LBS_OWNERDRAWVARIABLE yet
185 wstyle
|= LBS_OWNERDRAWFIXED
;
188 // Change from previous versions of wxWin: JACS Nov. 1995
189 // Not sure whether to have integral, or no integral
190 // style. With the latter we may get partial items showing.
191 // VZ: also it makes life more difficult for owner-drawn controls
192 wstyle
|= LBS_NOINTEGRALHEIGHT
;
196 WXDWORD exStyle
= Determine3DEffects(WS_EX_CLIENTEDGE
, &want3D
) ;
198 // Even with extended styles, need to combine with WS_BORDER
199 // for them to look right.
200 if ( want3D
|| (m_windowStyle
& wxSIMPLE_BORDER
)
201 || (m_windowStyle
& wxRAISED_BORDER
)
202 || (m_windowStyle
& wxSUNKEN_BORDER
)
203 || (m_windowStyle
& wxDOUBLE_BORDER
) ) {
207 HWND wx_list
= CreateWindowEx(exStyle
, "LISTBOX", NULL
,
210 (HWND
)parent
->GetHWND(), (HMENU
)m_windowId
,
211 wxGetInstance(), NULL
);
215 Ctl3dSubclassCtl(wx_list
);
221 for (ui
= 0; ui
< (uint
)n
; ui
++) {
222 SendMessage(wx_list
, LB_ADDSTRING
, 0, (LPARAM
)(const char *)choices
[ui
]);
226 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
227 for (ui
= 0; ui
< (uint
)n
; ui
++) {
228 // create new item which will process WM_{DRAW|MEASURE}ITEM messages
229 wxOwnerDrawn
*pNewItem
= CreateItem(ui
);
230 pNewItem
->SetName(choices
[ui
]);
231 m_aItems
.Add(pNewItem
);
232 ListBox_SetItemData(wx_list
, ui
, pNewItem
);
237 if ((m_windowStyle
& wxLB_MULTIPLE
) == 0)
238 SendMessage(wx_list
, LB_SETCURSEL
, 0, 0);
240 ShowWindow(wx_list
, SW_SHOW
);
242 m_hWnd
= (WXHWND
)wx_list
;
244 // Subclass again for purposes of dialog editing mode
245 SubclassWin((WXHWND
)wx_list
);
247 SetFont(* parent
->GetFont());
249 SetSize(x
, y
, width
, height
);
254 wxListBox::~wxListBox(void)
257 uint uiCount
= m_aItems
.Count();
258 while ( uiCount
-- != 0 ) {
259 delete m_aItems
[uiCount
];
263 DELETEA(m_selections
);
266 void wxListBox::SetupColours(void)
268 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
));
269 SetForegroundColour(GetParent()->GetDefaultForegroundColour());
272 void wxListBox::SetFirstItem(const int N
)
274 SendMessage(hwnd
,LB_SETTOPINDEX
,(WPARAM
)N
,(LPARAM
)0) ;
277 void wxListBox::SetFirstItem(const wxString
& s
)
279 int N
= FindString(s
) ;
285 void wxListBox::Delete(const int N
)
287 SendMessage(hwnd
, LB_DELETESTRING
, N
, 0);
289 SetHorizontalExtent("");
292 void wxListBox::Append(const wxString
& item
)
294 int index
= ListBox_AddString(hwnd
, item
);
298 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
299 wxOwnerDrawn
*pNewItem
= CreateItem(-1); // dummy argument
300 pNewItem
->SetName(item
);
301 m_aItems
.Add(pNewItem
);
302 ListBox_SetItemData(hwnd
, index
, pNewItem
);
306 SetHorizontalExtent(item
);
309 void wxListBox::Append(const wxString
& item
, char *Client_data
)
311 int index
= ListBox_AddString(hwnd
, item
);
315 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
316 // client data must be pointer to wxOwnerDrawn, otherwise we would crash
317 // in OnMeasure/OnDraw.
318 wxFAIL_MSG("Can't use client data with owner-drawn listboxes");
322 ListBox_SetItemData(hwnd
, index
, Client_data
);
324 SetHorizontalExtent(item
);
327 void wxListBox::Set(const int n
, const wxString
*choices
, char** clientData
)
329 ShowWindow(hwnd
, SW_HIDE
);
330 ListBox_ResetContent(hwnd
);
332 for (i
= 0; i
< n
; i
++)
334 ListBox_AddString(hwnd
, choices
[i
]);
336 ListBox_SetItemData(hwnd
, i
, clientData
[i
]);
341 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
342 // first delete old items
343 uint ui
= m_aItems
.Count();
344 while ( ui
-- != 0 ) {
349 // then create new ones
350 for (ui
= 0; ui
< (uint
)n
; ui
++) {
351 wxOwnerDrawn
*pNewItem
= CreateItem(ui
);
352 pNewItem
->SetName(choices
[ui
]);
353 m_aItems
.Add(pNewItem
);
354 ListBox_SetItemData(hwnd
, ui
, pNewItem
);
356 wxASSERT_MSG(clientData
[ui
] == NULL
,
357 "Can't use client data with owner-drawn listboxes");
362 SetHorizontalExtent("");
363 ShowWindow(hwnd
, SW_SHOW
);
366 int wxListBox::FindString(const wxString
& s
) const
368 int pos
= ListBox_FindStringExact(hwnd
, (WPARAM
)-1, s
);
375 void wxListBox::Clear(void)
377 ListBox_ResetContent(hwnd
);
380 ListBox_GetHorizontalExtent(hwnd
);
383 void wxListBox::SetSelection(const int N
, const bool select
)
385 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
386 SendMessage(hwnd
, LB_SETSEL
, select
, N
);
392 SendMessage(hwnd
, LB_SETCURSEL
, N1
, 0);
396 bool wxListBox::Selected(const int N
) const
398 return SendMessage(hwnd
, LB_GETSEL
, N
, 0) == 0 ? FALSE
: TRUE
;
401 void wxListBox::Deselect(const int N
)
403 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
404 SendMessage(hwnd
, LB_SETSEL
, FALSE
, N
);
407 char *wxListBox::GetClientData(const int N
) const
409 return (char *)SendMessage(hwnd
, LB_GETITEMDATA
, N
, 0);
412 void wxListBox::SetClientData(const int N
, char *Client_data
)
414 (void)SendMessage(hwnd
, LB_SETITEMDATA
, N
, (LONG
)Client_data
);
416 if (result == LB_ERR)
423 // Return number of selections and an array of selected integers
424 // Use selections field to store data, which will be cleaned up
425 // by destructor if necessary.
426 int wxListBox::GetSelections(int **list_selections
) const
428 wxListBox
*nonConst
= (wxListBox
*)this; // const is a white lie!
429 if (nonConst
->m_selections
)
430 { delete[] nonConst
->m_selections
; nonConst
->m_selections
= NULL
; };
431 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
433 int no_sel
= (int)SendMessage(hwnd
, LB_GETSELCOUNT
, 0, 0);
436 nonConst
->m_selections
= new int[no_sel
];
437 SendMessage(hwnd
, LB_GETSELITEMS
, no_sel
, (LONG
)m_selections
);
438 *list_selections
= m_selections
;
443 int sel
= (int)SendMessage(hwnd
, LB_GETCURSEL
, 0, 0);
446 nonConst
->m_selections
= new int[1];
447 nonConst
->m_selections
[0] = sel
;
448 *list_selections
= m_selections
;
453 // Get single selection, for single choice list items
454 int wxListBox::GetSelection(void) const
456 wxListBox
*nonConst
= (wxListBox
*)this; // const is a white lie!
457 if (nonConst
->m_selections
)
458 { delete[] nonConst
->m_selections
; nonConst
->m_selections
= NULL
; };
459 if ((m_windowStyle
& wxLB_MULTIPLE
) || (m_windowStyle
& wxLB_EXTENDED
))
463 int sel
= (int)SendMessage(hwnd
, LB_GETCURSEL
, 0, 0);
473 // Find string for position
474 wxString
wxListBox::GetString(const int N
) const
476 if (N
< 0 || N
> m_noItems
)
479 int len
= (int)SendMessage(hwnd
, LB_GETTEXT
, N
, (LONG
)wxBuffer
);
481 return wxString(wxBuffer
);
484 void wxListBox::SetSize(const int x
, const int y
, const int width
, const int height
, const int sizeFlags
)
486 int currentX
, currentY
;
487 GetPosition(¤tX
, ¤tY
);
494 if (x
== -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
496 if (y
== -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
499 // If we're prepared to use the existing size, then...
500 if (width
== -1 && height
== -1 && ((sizeFlags
& wxSIZE_AUTO
) != wxSIZE_AUTO
))
505 int cx
; // button font dimensions
508 wxGetCharSize(GetHWND(), &cx
, &cy
,GetFont());
510 float control_width
, control_height
, control_x
, control_y
;
512 // Deal with default size (using -1 values)
514 w1
= DEFAULT_ITEM_WIDTH
;
517 h1
= DEFAULT_ITEM_HEIGHT
;
519 control_x
= (float)x1
;
520 control_y
= (float)y1
;
521 control_width
= (float)w1
;
522 control_height
= (float)h1
;
524 // Calculations may have made size too small
525 if (control_height
<= 0)
526 control_height
= (float)DEFAULT_ITEM_HEIGHT
;
528 if (control_width
<= 0)
529 control_width
= (float)DEFAULT_ITEM_WIDTH
;
531 // wxDebugMsg("About to set the listbox height to %d", (int)control_height);
532 MoveWindow(hwnd
, (int)control_x
, (int)control_y
,
533 (int)control_width
, (int)control_height
, TRUE
);
536 #if WXWIN_COMPATIBILITY
537 GetEventHandler()->OldOnSize(width, height);
539 wxSizeEvent event(wxSize(width, height), m_windowId);
540 event.eventObject = this;
541 GetEventHandler()->ProcessEvent(event);
547 // Windows-specific code to set the horizontal extent of
548 // the listbox, if necessary. If s is non-NULL, it's
549 // used to calculate the horizontal extent.
550 // Otherwise, all strings are used.
551 void wxListBox::SetHorizontalExtent(const wxString
& s
)
553 // Only necessary if we want a horizontal scrollbar
554 if (!(m_windowStyle
& wxHSCROLL
))
556 TEXTMETRIC lpTextMetric
;
560 int existingExtent
= (int)SendMessage(hwnd
, LB_GETHORIZONTALEXTENT
, 0, 0L);
561 HDC dc
= GetWindowDC(hwnd
);
563 if (GetFont() && GetFont()->GetResourceHandle())
564 oldFont
= ::SelectObject(dc
, (HFONT
) GetFont()->GetResourceHandle());
566 GetTextMetrics(dc
, &lpTextMetric
);
568 ::GetTextExtentPoint(dc
, (LPSTR
) (const char *)s
, s
.Length(), &extentXY
);
569 int extentX
= (int)(extentXY
.cx
+ lpTextMetric
.tmAveCharWidth
);
572 ::SelectObject(dc
, oldFont
);
575 if (extentX
> existingExtent
)
576 SendMessage(hwnd
, LB_SETHORIZONTALEXTENT
, LOWORD(extentX
), 0L);
581 int largestExtent
= 0;
582 HDC dc
= GetWindowDC(hwnd
);
584 if (GetFont() && GetFont()->GetResourceHandle())
585 oldFont
= ::SelectObject(dc
, (HFONT
) GetFont()->GetResourceHandle());
587 GetTextMetrics(dc
, &lpTextMetric
);
589 for (i
= 0; i
< m_noItems
; i
++)
591 int len
= (int)SendMessage(hwnd
, LB_GETTEXT
, i
, (LONG
)wxBuffer
);
594 ::GetTextExtentPoint(dc
, (LPSTR
)wxBuffer
, len
, &extentXY
);
595 int extentX
= (int)(extentXY
.cx
+ lpTextMetric
.tmAveCharWidth
);
596 if (extentX
> largestExtent
)
597 largestExtent
= extentX
;
600 ::SelectObject(dc
, oldFont
);
603 SendMessage(hwnd
, LB_SETHORIZONTALEXTENT
, LOWORD(largestExtent
), 0L);
608 wxListBox::InsertItems(const int nItems
, const wxString items
[], const int pos
)
611 for (i
= 0; i
< nItems
; i
++)
612 ListBox_InsertString(hwnd
, i
+ pos
, items
[i
]);
616 if ( m_windowStyle
& wxLB_OWNERDRAW
) {
617 for ( i
= 0; i
< nItems
; i
++ ) {
618 wxOwnerDrawn
*pNewItem
= CreateItem((uint
)(pos
+ i
));
619 pNewItem
->SetName(items
[i
]);
620 m_aItems
.Insert(pNewItem
, (uint
)(pos
+ i
));
621 ListBox_SetItemData(hwnd
, i
, pNewItem
);
626 SetHorizontalExtent("");
629 void wxListBox::SetString(const int N
, const wxString
& s
)
631 int sel
= GetSelection();
633 char *oldData
= (char *)wxListBox::GetClientData(N
);
635 SendMessage(hwnd
, LB_DELETESTRING
, N
, 0);
638 if (N
== (m_noItems
- 1))
641 SendMessage(hwnd
, LB_INSERTSTRING
, newN
, (LPARAM
) (const char *)s
);
643 wxListBox::SetClientData(N
, oldData
);
645 // Selection may have changed
650 // update item's text
651 m_aItems
[N
]->SetName(s
);
652 #endif //USE_OWNER_DRAWN
655 int wxListBox::Number (void) const
660 // For single selection items only
661 wxString
wxListBox::GetStringSelection (void) const
663 int sel
= GetSelection ();
665 return this->GetString (sel
);
670 bool wxListBox::SetStringSelection (const wxString
& s
, const bool flag
)
672 int sel
= FindString (s
);
675 SetSelection (sel
, flag
);
682 // Is this the right thing? Won't setselection generate a command
683 // event too? No! It'll just generate a setselection event.
684 // But we still can't have this being called whenever a real command
685 // is generated, because it sets the selection, which will already
686 // have been done! (Unless we have an optional argument for calling
687 // by the actual window system, or a separate function, ProcessCommand)
688 void wxListBox::Command (wxCommandEvent
& event
)
690 if (event
.m_extraLong
)
691 SetSelection (event
.m_commandInt
);
694 Deselect (event
.m_commandInt
);
697 ProcessCommand (event
);
700 WXHBRUSH
wxListBox::OnCtlColor(const WXHDC pDC
, const WXHWND pWnd
, const WXUINT nCtlColor
,
701 WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
)
706 HBRUSH hbrush
= Ctl3dCtlColorEx(message
, wParam
, lParam
);
707 return (WXHBRUSH
) hbrush
;
711 if (GetParent()->GetTransparentBackground())
712 SetBkMode((HDC
) pDC
, TRANSPARENT
);
714 SetBkMode((HDC
) pDC
, OPAQUE
);
716 ::SetBkColor((HDC
) pDC
, RGB(GetBackgroundColour().Red(), GetBackgroundColour().Green(), GetBackgroundColour().Blue()));
717 ::SetTextColor((HDC
) pDC
, RGB(GetForegroundColour().Red(), GetForegroundColour().Green(), GetForegroundColour().Blue()));
719 wxBrush
*backgroundBrush
= wxTheBrushList
->FindOrCreateBrush(GetBackgroundColour(), wxSOLID
);
721 // Note that this will be cleaned up in wxApp::OnIdle, if backgroundBrush
722 // has a zero usage count.
723 backgroundBrush
->RealizeResource();
724 return (WXHBRUSH
) backgroundBrush
->GetResourceHandle();
727 long wxListBox::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
738 case WM_QUERYDRAGICON:
742 case WM_RBUTTONDBLCLK:
745 case WM_MBUTTONDBLCLK:
748 // case WM_LBUTTONDBLCLK:
754 case WM_INITMENUPOPUP:
759 case WM_CHAR: // Always an ASCII character
764 case WM_CTLCOLORLISTBOX:
765 case WM_CTLCOLORMSGBOX:
766 case WM_CTLCOLORSCROLLBAR:
767 case WM_CTLCOLORSTATIC:
768 case WM_CTLCOLOREDIT:
769 case WM_SYSCOLORCHANGE:
773 case WM_QUERYENDSESSION:
775 case WM_GETMINMAXINFO:
777 return MSWDefWindowProc(nMsg, wParam, lParam );
780 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
788 // space beneath/above each row in pixels
789 // "standard" checklistbox use 1 here, some might prefer 2. 0 is ugly.
790 #define OWNER_DRAWN_LISTBOX_EXTRA_SPACE (1)
792 // the height is the same for all items
793 // ## should be changed for LBS_OWNERDRAWVARIABLE style listboxes
794 // NB: can't forward this to wxListBoxItem because LB_SETITEMDATA
795 // message is not yet sent when we get here!
796 bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT
*item
)
798 // only owner-drawn control should receive this message
799 wxCHECK_RET( ((m_windowStyle
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), FALSE
);
801 MEASUREITEMSTRUCT
*pStruct
= (MEASUREITEMSTRUCT
*)item
;
804 dc
.SetHDC((WXHDC
)CreateIC("DISPLAY", NULL
, NULL
, 0));
805 dc
.SetFont(wxSystemSettings::GetSystemFont(wxSYS_ANSI_VAR_FONT
));
807 pStruct
->itemHeight
= dc
.GetCharHeight() + 2*OWNER_DRAWN_LISTBOX_EXTRA_SPACE
;
808 pStruct
->itemWidth
= dc
.GetCharWidth();
813 // forward the message to the appropriate item
814 bool wxListBox::MSWOnDraw(WXDRAWITEMSTRUCT
*item
)
816 // only owner-drawn control should receive this message
817 wxCHECK_RET( ((m_windowStyle
& wxLB_OWNERDRAW
) == wxLB_OWNERDRAW
), FALSE
);
819 DRAWITEMSTRUCT
*pStruct
= (DRAWITEMSTRUCT
*)item
;
820 wxListBoxItem
*pItem
= (wxListBoxItem
*)SendMessage(hwnd
, LB_GETITEMDATA
,
823 wxCHECK_RET( (int)pItem
!= LB_ERR
, FALSE
);
826 dc
.SetHDC((WXHDC
)pStruct
->hDC
, FALSE
);
827 wxRect
rect(pStruct
->rcItem
.left
, pStruct
->rcItem
.top
,
828 pStruct
->rcItem
.right
- pStruct
->rcItem
.left
,
829 pStruct
->rcItem
.bottom
- pStruct
->rcItem
.top
);
831 return pItem
->OnDrawItem(dc
, rect
,
832 (wxOwnerDrawn::wxODAction
)pStruct
->itemAction
,
833 (wxOwnerDrawn::wxODStatus
)pStruct
->itemState
);