1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: msw/checklst.cpp
3 // Purpose: implementation of wxCheckListBox class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #if wxUSE_CHECKLISTBOX && wxUSE_OWNER_DRAWN
30 #include "wx/object.h"
31 #include "wx/colour.h"
33 #include "wx/bitmap.h"
34 #include "wx/window.h"
35 #include "wx/listbox.h"
36 #include "wx/dcmemory.h"
38 #include "wx/settings.h"
43 #include "wx/ownerdrw.h"
44 #include "wx/checklst.h"
46 #include "wx/msw/wrapwin.h"
49 #include "wx/msw/private.h"
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 // get item (converted to right type)
56 #define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n)))
58 // ============================================================================
60 // ============================================================================
63 #if wxUSE_EXTENDED_RTTI
64 WX_DEFINE_FLAGS( wxCheckListBoxStyle
)
66 wxBEGIN_FLAGS( wxCheckListBoxStyle
)
67 // new style border flags, we put them first to
68 // use them for streaming out
69 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
70 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
71 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
72 wxFLAGS_MEMBER(wxBORDER_RAISED
)
73 wxFLAGS_MEMBER(wxBORDER_STATIC
)
74 wxFLAGS_MEMBER(wxBORDER_NONE
)
76 // old style border flags
77 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
78 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
79 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
80 wxFLAGS_MEMBER(wxRAISED_BORDER
)
81 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
82 wxFLAGS_MEMBER(wxBORDER
)
84 // standard window styles
85 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
86 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
87 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
88 wxFLAGS_MEMBER(wxWANTS_CHARS
)
89 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
90 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
91 wxFLAGS_MEMBER(wxVSCROLL
)
92 wxFLAGS_MEMBER(wxHSCROLL
)
94 wxFLAGS_MEMBER(wxLB_SINGLE
)
95 wxFLAGS_MEMBER(wxLB_MULTIPLE
)
96 wxFLAGS_MEMBER(wxLB_EXTENDED
)
97 wxFLAGS_MEMBER(wxLB_HSCROLL
)
98 wxFLAGS_MEMBER(wxLB_ALWAYS_SB
)
99 wxFLAGS_MEMBER(wxLB_NEEDED_SB
)
100 wxFLAGS_MEMBER(wxLB_SORT
)
101 wxFLAGS_MEMBER(wxLB_OWNERDRAW
)
103 wxEND_FLAGS( wxCheckListBoxStyle
)
105 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckListBox
, wxListBox
,"wx/checklst.h")
107 wxBEGIN_PROPERTIES_TABLE(wxCheckListBox
)
108 wxEVENT_PROPERTY( Toggle
, wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, wxCommandEvent
)
109 wxPROPERTY_FLAGS( WindowStyle
, wxCheckListBoxStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, wxLB_OWNERDRAW
/*flags*/ , wxT("Helpstring") , wxT("group")) // style
110 wxEND_PROPERTIES_TABLE()
112 wxBEGIN_HANDLERS_TABLE(wxCheckListBox
)
113 wxEND_HANDLERS_TABLE()
115 wxCONSTRUCTOR_4( wxCheckListBox
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
)
118 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
)
121 // ----------------------------------------------------------------------------
122 // declaration and implementation of wxCheckListBoxItem class
123 // ----------------------------------------------------------------------------
125 class wxCheckListBoxItem
: public wxOwnerDrawn
127 friend class WXDLLEXPORT wxCheckListBox
;
130 wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
);
133 virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
);
135 // simple accessors and operations
136 bool IsChecked() const { return m_bChecked
; }
138 void Check(bool bCheck
);
139 void Toggle() { Check(!IsChecked()); }
145 wxCheckListBox
*m_pParent
;
148 DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
)
151 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
)
152 : wxOwnerDrawn(wxEmptyString
, true) // checkable
158 // we don't initialize m_nCheckHeight/Width vars because it's
159 // done in OnMeasure while they are used only in OnDraw and we
160 // know that there will always be OnMeasure before OnDraw
162 // fix appearance for check list boxes: they don't look quite the same as
164 SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK
) -
165 2*wxSystemSettings::GetMetric(wxSYS_EDGE_X
) + 1);
166 SetBackgroundColour(pParent
->GetBackgroundColour());
169 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
,
170 wxODAction act
, wxODStatus stat
)
172 // first draw the label
174 stat
= (wxOwnerDrawn::wxODStatus
)(stat
| wxOwnerDrawn::wxODChecked
);
176 if ( !wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) )
180 // now draw the check mark part
181 size_t nCheckWidth
= GetDefaultMarginWidth(),
182 nCheckHeight
= m_pParent
->GetItemHeight();
187 HDC hdc
= (HDC
)dc
.GetHDC();
189 // create pens, brushes &c
190 COLORREF colBg
= ::GetSysColor(COLOR_WINDOW
);
191 AutoHPEN
hpenBack(colBg
),
192 hpenGray(RGB(0xc0, 0xc0, 0xc0));
194 SelectInHDC
selPen(hdc
, (HGDIOBJ
)hpenBack
);
195 AutoHBRUSH
hbrBack(colBg
);
196 SelectInHDC
selBrush(hdc
, hbrBack
);
198 // erase the background: it could have been filled with the selected colour
199 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
+ 1, rc
.GetBottom() + 1);
201 // shift check mark 1 pixel to the right, looks better like this
206 // first create a monochrome bitmap in a memory DC
207 MemoryHDC
hdcMem(hdc
);
208 MonoBitmap
hbmpCheck(nCheckWidth
, nCheckHeight
);
209 SelectInHDC
selBmp(hdcMem
, hbmpCheck
);
211 // then draw a check mark into it
212 RECT rect
= { 0, 0, nCheckWidth
, nCheckHeight
};
213 ::DrawFrameControl(hdcMem
, &rect
,
215 DFC_BUTTON
, DFCS_BUTTONCHECK
217 DFC_MENU
, DFCS_MENUCHECK
221 // finally copy it to screen DC
222 ::BitBlt(hdc
, x
, y
, nCheckWidth
, nCheckHeight
, hdcMem
, 0, 0, SRCCOPY
);
225 // now we draw the smaller rectangle
230 // draw hollow gray rectangle
231 (void)::SelectObject(hdc
, (HGDIOBJ
)hpenGray
);
233 SelectInHDC
selBrush2(hdc
, ::GetStockObject(NULL_BRUSH
));
234 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
239 // change the state of the item and redraw it
240 void wxCheckListBoxItem::Check(bool check
)
244 // index may be changed because new items were added/deleted
245 if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex
)
248 int index
= m_pParent
->GetItemIndex(this);
250 wxASSERT_MSG( index
!= wxNOT_FOUND
, wxT("what does this item do here?") );
252 m_nIndex
= (size_t)index
;
255 HWND hwndListbox
= (HWND
)m_pParent
->GetHWND();
259 if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
,
260 m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR
)
262 wxLogDebug(wxT("LB_GETITEMRECT failed"));
265 ::InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
);
268 // send an "item checked" event
269 void wxCheckListBoxItem::SendEvent()
271 wxCommandEvent
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId());
272 event
.SetInt(m_nIndex
);
273 event
.SetEventObject(m_pParent
);
274 m_pParent
->ProcessCommand(event
);
277 // ----------------------------------------------------------------------------
278 // implementation of wxCheckListBox class
279 // ----------------------------------------------------------------------------
281 // define event table
282 // ------------------
283 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
)
284 EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
)
285 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
)
291 // def ctor: use Create() to really create the control
292 wxCheckListBox::wxCheckListBox()
296 // ctor which creates the associated control
297 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
298 const wxPoint
& pos
, const wxSize
& size
,
299 int nStrings
, const wxString choices
[],
300 long style
, const wxValidator
& val
,
301 const wxString
& name
)
303 Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
);
306 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
307 const wxPoint
& pos
, const wxSize
& size
,
308 const wxArrayString
& choices
,
309 long style
, const wxValidator
& val
,
310 const wxString
& name
)
312 Create(parent
, id
, pos
, size
, choices
, style
, val
, name
);
315 bool wxCheckListBox::Create(wxWindow
*parent
, wxWindowID id
,
316 const wxPoint
& pos
, const wxSize
& size
,
317 int n
, const wxString choices
[],
319 const wxValidator
& validator
, const wxString
& name
)
321 return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
,
322 style
| wxLB_OWNERDRAW
, validator
, name
);
325 bool wxCheckListBox::Create(wxWindow
*parent
, wxWindowID id
,
326 const wxPoint
& pos
, const wxSize
& size
,
327 const wxArrayString
& choices
,
329 const wxValidator
& validator
, const wxString
& name
)
331 return wxListBox::Create(parent
, id
, pos
, size
, choices
,
332 style
| wxLB_OWNERDRAW
, validator
, name
);
335 // misc overloaded methods
336 // -----------------------
338 void wxCheckListBox::Delete(int N
)
340 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
341 wxT("invalid index in wxListBox::Delete") );
343 wxListBox::Delete(N
);
348 m_aItems
.RemoveAt(N
);
351 bool wxCheckListBox::SetFont( const wxFont
&font
)
354 for ( i
= 0; i
< m_aItems
.GetCount(); i
++ )
355 m_aItems
[i
]->SetFont(font
);
357 wxListBox::SetFont(font
);
362 // create/retrieve item
363 // --------------------
365 // create a check list box item
366 wxOwnerDrawn
*wxCheckListBox::CreateLboxItem(size_t nIndex
)
368 wxCheckListBoxItem
*pItem
= new wxCheckListBoxItem(this, nIndex
);
374 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT
*item
)
376 if ( wxListBox::MSWOnMeasure(item
) ) {
377 MEASUREITEMSTRUCT
*pStruct
= (MEASUREITEMSTRUCT
*)item
;
380 m_nItemHeight
= pStruct
->itemHeight
;
382 // add place for the check mark
383 pStruct
->itemWidth
+= wxOwnerDrawn::GetDefaultMarginWidth();
394 bool wxCheckListBox::IsChecked(size_t uiIndex
) const
396 wxCHECK_MSG( uiIndex
< (size_t)GetCount(), false, _T("bad wxCheckListBox index") );
398 return GetItem(uiIndex
)->IsChecked();
401 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
)
403 wxCHECK_RET( uiIndex
< (size_t)GetCount(), _T("bad wxCheckListBox index") );
405 GetItem(uiIndex
)->Check(bCheck
);
411 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
)
422 switch ( event
.GetKeyCode() )
433 case WXK_NUMPAD_SUBTRACT
:
444 wxArrayInt selections
;
446 if ( HasMultipleSelection() )
448 count
= GetSelections(selections
);
452 int sel
= GetSelection();
460 for ( int i
= 0; i
< count
; i
++ )
462 wxCheckListBoxItem
*item
= GetItem(selections
[i
]);
465 wxFAIL_MSG( _T("no wxCheckListBoxItem?") );
477 item
->Check( oper
== Set
);
481 wxFAIL_MSG( _T("what should this key do?") );
484 // we should send an event as this has been done by the user and
485 // not by the program
489 else // nothing to do
495 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
)
497 // clicking on the item selects it, clicking on the checkmark toggles
498 if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) {
499 int nItem
= HitTest(event
.GetX(), event
.GetY());
501 if ( nItem
!= wxNOT_FOUND
) {
502 wxCheckListBoxItem
*item
= GetItem(nItem
);
506 //else: it's not an error, just click outside of client zone
509 // implement default behaviour: clicking on the item selects it
514 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const
516 int nItem
= (int)::SendMessage
524 return nItem
>= m_noItems
? wxNOT_FOUND
: nItem
;
528 wxSize
wxCheckListBox::DoGetBestSize() const
530 wxSize best
= wxListBox::DoGetBestSize();
531 best
.x
+= wxOwnerDrawn::GetDefaultMarginWidth(); // add room for the checkbox