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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "checklst.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
31 #if wxUSE_CHECKLISTBOX && wxUSE_OWNER_DRAWN
34 #include "wx/object.h"
35 #include "wx/colour.h"
37 #include "wx/bitmap.h"
38 #include "wx/window.h"
39 #include "wx/listbox.h"
40 #include "wx/dcmemory.h"
42 #include "wx/settings.h"
47 #include "wx/ownerdrw.h"
48 #include "wx/checklst.h"
50 #include "wx/msw/wrapwin.h"
53 #include "wx/msw/private.h"
55 #if defined(__GNUWIN32_OLD__)
56 #include "wx/msw/gnuwin32/extra.h"
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
63 // get item (converted to right type)
64 #define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n)))
66 // ============================================================================
68 // ============================================================================
71 #if wxUSE_EXTENDED_RTTI
72 WX_DEFINE_FLAGS( wxCheckListBoxStyle
)
74 wxBEGIN_FLAGS( wxCheckListBoxStyle
)
75 // new style border flags, we put them first to
76 // use them for streaming out
77 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
78 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
79 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
80 wxFLAGS_MEMBER(wxBORDER_RAISED
)
81 wxFLAGS_MEMBER(wxBORDER_STATIC
)
82 wxFLAGS_MEMBER(wxBORDER_NONE
)
84 // old style border flags
85 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
86 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
87 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
88 wxFLAGS_MEMBER(wxRAISED_BORDER
)
89 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
90 wxFLAGS_MEMBER(wxBORDER
)
92 // standard window styles
93 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
94 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
95 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
96 wxFLAGS_MEMBER(wxWANTS_CHARS
)
97 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
98 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
99 wxFLAGS_MEMBER(wxVSCROLL
)
100 wxFLAGS_MEMBER(wxHSCROLL
)
102 wxFLAGS_MEMBER(wxLB_SINGLE
)
103 wxFLAGS_MEMBER(wxLB_MULTIPLE
)
104 wxFLAGS_MEMBER(wxLB_EXTENDED
)
105 wxFLAGS_MEMBER(wxLB_HSCROLL
)
106 wxFLAGS_MEMBER(wxLB_ALWAYS_SB
)
107 wxFLAGS_MEMBER(wxLB_NEEDED_SB
)
108 wxFLAGS_MEMBER(wxLB_SORT
)
109 wxFLAGS_MEMBER(wxLB_OWNERDRAW
)
111 wxEND_FLAGS( wxCheckListBoxStyle
)
113 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckListBox
, wxListBox
,"wx/checklst.h")
115 wxBEGIN_PROPERTIES_TABLE(wxCheckListBox
)
116 wxEVENT_PROPERTY( Toggle
, wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, wxCommandEvent
)
117 wxPROPERTY_FLAGS( WindowStyle
, wxCheckListBoxStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, wxLB_OWNERDRAW
/*flags*/ , wxT("Helpstring") , wxT("group")) // style
118 wxEND_PROPERTIES_TABLE()
120 wxBEGIN_HANDLERS_TABLE(wxCheckListBox
)
121 wxEND_HANDLERS_TABLE()
123 wxCONSTRUCTOR_4( wxCheckListBox
, wxWindow
* , Parent
, wxWindowID
, Id
, wxPoint
, Position
, wxSize
, Size
)
126 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
)
129 // ----------------------------------------------------------------------------
130 // declaration and implementation of wxCheckListBoxItem class
131 // ----------------------------------------------------------------------------
133 class wxCheckListBoxItem
: public wxOwnerDrawn
135 friend class WXDLLEXPORT wxCheckListBox
;
138 wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
);
141 virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
);
143 // simple accessors and operations
144 bool IsChecked() const { return m_bChecked
; }
146 void Check(bool bCheck
);
147 void Toggle() { Check(!IsChecked()); }
153 DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
)
155 wxCheckListBox
*m_pParent
;
159 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
)
160 : wxOwnerDrawn(wxEmptyString
, true) // checkable
166 // we don't initialize m_nCheckHeight/Width vars because it's
167 // done in OnMeasure while they are used only in OnDraw and we
168 // know that there will always be OnMeasure before OnDraw
171 SetMarginWidth(GetDefaultMarginWidth());
174 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
,
175 wxODAction act
, wxODStatus stat
)
178 stat
= (wxOwnerDrawn::wxODStatus
)(stat
| wxOwnerDrawn::wxODChecked
);
180 if ( wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) {
181 // ## using native API for performance and precision
182 size_t nCheckWidth
= GetDefaultMarginWidth(),
183 nCheckHeight
= m_pParent
->GetItemHeight();
188 HDC hdc
= (HDC
)dc
.GetHDC();
191 HPEN hpenBack
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_WINDOW
)),
192 hpenGray
= CreatePen(PS_SOLID
, 0, RGB(128, 128, 128)),
193 hpenPrev
= (HPEN
)SelectObject(hdc
, hpenBack
);
195 // we erase the 1-pixel border
196 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
198 // shift check mark 1 pixel to the right (it looks better like this)
202 // first create a monochrome bitmap in a memory DC
203 HDC hdcMem
= CreateCompatibleDC(hdc
);
204 HBITMAP hbmpCheck
= CreateBitmap(nCheckWidth
, nCheckHeight
, 1, 1, 0);
205 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcMem
, hbmpCheck
);
207 // then draw a check mark into it
212 rect
.right
= nCheckWidth
;
213 rect
.bottom
= nCheckHeight
;
216 DrawFrameControl(hdcMem
, &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
);
218 DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
);
221 // finally copy it to screen DC and clean up
222 BitBlt(hdc
, x
, y
, nCheckWidth
- 1, nCheckHeight
,
223 hdcMem
, 0, 0, SRCCOPY
);
225 SelectObject(hdcMem
, hbmpOld
);
226 DeleteObject(hbmpCheck
);
230 // now we draw the smaller rectangle
235 // draw hollow gray rectangle
236 (void)SelectObject(hdc
, hpenGray
);
237 HBRUSH hbrPrev
= (HBRUSH
)SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
238 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
241 (void)SelectObject(hdc
, hpenPrev
);
242 (void)SelectObject(hdc
, hbrPrev
);
244 DeleteObject(hpenBack
);
245 DeleteObject(hpenGray
);
248 dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight);
251 dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight);
252 dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y);
262 // change the state of the item and redraw it
263 void wxCheckListBoxItem::Check(bool check
)
267 // index may be changed because new items were added/deleted
268 if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex
)
271 int index
= m_pParent
->GetItemIndex(this);
273 wxASSERT_MSG( index
!= wxNOT_FOUND
, wxT("what does this item do here?") );
275 m_nIndex
= (size_t)index
;
278 HWND hwndListbox
= (HWND
)m_pParent
->GetHWND();
282 if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
,
283 m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR
)
285 wxLogDebug(wxT("LB_GETITEMRECT failed"));
288 ::InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
);
291 // send an "item checked" event
292 void wxCheckListBoxItem::SendEvent()
294 wxCommandEvent
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId());
295 event
.SetInt(m_nIndex
);
296 event
.SetEventObject(m_pParent
);
297 m_pParent
->ProcessCommand(event
);
300 // ----------------------------------------------------------------------------
301 // implementation of wxCheckListBox class
302 // ----------------------------------------------------------------------------
304 // define event table
305 // ------------------
306 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
)
307 EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
)
308 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
)
314 // def ctor: use Create() to really create the control
315 wxCheckListBox::wxCheckListBox()
319 // ctor which creates the associated control
320 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
321 const wxPoint
& pos
, const wxSize
& size
,
322 int nStrings
, const wxString choices
[],
323 long style
, const wxValidator
& val
,
324 const wxString
& name
)
326 Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
);
329 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
330 const wxPoint
& pos
, const wxSize
& size
,
331 const wxArrayString
& choices
,
332 long style
, const wxValidator
& val
,
333 const wxString
& name
)
335 Create(parent
, id
, pos
, size
, choices
, style
, val
, name
);
338 bool wxCheckListBox::Create(wxWindow
*parent
, wxWindowID id
,
339 const wxPoint
& pos
, const wxSize
& size
,
340 int n
, const wxString choices
[],
342 const wxValidator
& validator
, const wxString
& name
)
344 return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
,
345 style
| wxLB_OWNERDRAW
, validator
, name
);
348 bool wxCheckListBox::Create(wxWindow
*parent
, wxWindowID id
,
349 const wxPoint
& pos
, const wxSize
& size
,
350 const wxArrayString
& choices
,
352 const wxValidator
& validator
, const wxString
& name
)
354 return wxListBox::Create(parent
, id
, pos
, size
, choices
,
355 style
| wxLB_OWNERDRAW
, validator
, name
);
358 // misc overloaded methods
359 // -----------------------
361 void wxCheckListBox::Delete(int N
)
363 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
364 wxT("invalid index in wxListBox::Delete") );
366 wxListBox::Delete(N
);
371 m_aItems
.RemoveAt(N
);
374 bool wxCheckListBox::SetFont( const wxFont
&font
)
377 for ( i
= 0; i
< m_aItems
.GetCount(); i
++ )
378 m_aItems
[i
]->SetFont(font
);
380 wxListBox::SetFont(font
);
385 // create/retrieve item
386 // --------------------
388 // create a check list box item
389 wxOwnerDrawn
*wxCheckListBox::CreateLboxItem(size_t nIndex
)
391 wxCheckListBoxItem
*pItem
= new wxCheckListBoxItem(this, nIndex
);
397 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT
*item
)
399 if ( wxListBox::MSWOnMeasure(item
) ) {
400 MEASUREITEMSTRUCT
*pStruct
= (MEASUREITEMSTRUCT
*)item
;
403 m_nItemHeight
= pStruct
->itemHeight
;
405 // add place for the check mark
406 pStruct
->itemWidth
+= wxOwnerDrawn::GetDefaultMarginWidth();
417 bool wxCheckListBox::IsChecked(size_t uiIndex
) const
419 wxCHECK_MSG( uiIndex
< (size_t)GetCount(), false, _T("bad wxCheckListBox index") );
421 return GetItem(uiIndex
)->IsChecked();
424 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
)
426 wxCHECK_RET( uiIndex
< (size_t)GetCount(), _T("bad wxCheckListBox index") );
428 GetItem(uiIndex
)->Check(bCheck
);
434 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
)
445 switch ( event
.GetKeyCode() )
456 case WXK_NUMPAD_SUBTRACT
:
467 wxArrayInt selections
;
469 if ( HasMultipleSelection() )
471 count
= GetSelections(selections
);
475 int sel
= GetSelection();
483 for ( int i
= 0; i
< count
; i
++ )
485 wxCheckListBoxItem
*item
= GetItem(selections
[i
]);
488 wxFAIL_MSG( _T("no wxCheckListBoxItem?") );
500 item
->Check( oper
== Set
);
504 wxFAIL_MSG( _T("what should this key do?") );
507 // we should send an event as this has been done by the user and
508 // not by the program
512 else // nothing to do
518 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
)
520 // clicking on the item selects it, clicking on the checkmark toggles
521 if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) {
522 int nItem
= HitTest(event
.GetX(), event
.GetY());
524 if ( nItem
!= wxNOT_FOUND
) {
525 wxCheckListBoxItem
*item
= GetItem(nItem
);
529 //else: it's not an error, just click outside of client zone
532 // implement default behaviour: clicking on the item selects it
537 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const
539 int nItem
= (int)::SendMessage
547 return nItem
>= m_noItems
? wxNOT_FOUND
: nItem
;
551 wxSize
wxCheckListBox::DoGetBestSize() const
553 wxSize best
= wxListBox::DoGetBestSize();
554 best
.x
+= wxOwnerDrawn::GetDefaultMarginWidth(); // add room for the checkbox