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 license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "checklst.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
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"
53 #if defined(__GNUWIN32_OLD__)
54 #include "wx/msw/gnuwin32/extra.h"
57 // ----------------------------------------------------------------------------
59 // ----------------------------------------------------------------------------
61 // get item (converted to right type)
62 #define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n)))
64 // ============================================================================
66 // ============================================================================
68 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
)
70 // ----------------------------------------------------------------------------
71 // declaration and implementation of wxCheckListBoxItem class
72 // ----------------------------------------------------------------------------
74 class wxCheckListBoxItem
: public wxOwnerDrawn
76 friend class WXDLLEXPORT wxCheckListBox
;
79 wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
);
82 virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
);
84 // simple accessors and operations
85 bool IsChecked() const { return m_bChecked
; }
87 void Check(bool bCheck
);
88 void Toggle() { Check(!IsChecked()); }
94 DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
)
96 wxCheckListBox
*m_pParent
;
100 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
)
101 : wxOwnerDrawn(wxEmptyString
, TRUE
) // checkable
107 // we don't initialize m_nCheckHeight/Width vars because it's
108 // done in OnMeasure while they are used only in OnDraw and we
109 // know that there will always be OnMeasure before OnDraw
112 SetMarginWidth(GetDefaultMarginWidth());
116 * JACS - I've got the owner-draw stuff partially working with WIN16,
117 * with a really horrible-looking cross for wxCheckListBox instead of a
118 * check - could use a bitmap check-mark instead, defined in wx.rc.
119 * Also there's a refresh problem whereby it doesn't always draw the
120 * check until you click to the right of it, which is OK for WIN32.
123 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
,
124 wxODAction act
, wxODStatus stat
)
127 stat
= (wxOwnerDrawn::wxODStatus
)(stat
| wxOwnerDrawn::wxODChecked
);
129 if ( wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) {
130 // ## using native API for performance and precision
131 size_t nCheckWidth
= GetDefaultMarginWidth(),
132 nCheckHeight
= m_pParent
->GetItemHeight();
137 HDC hdc
= (HDC
)dc
.GetHDC();
140 HPEN hpenBack
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_WINDOW
)),
141 hpenGray
= CreatePen(PS_SOLID
, 0, RGB(128, 128, 128)),
142 hpenPrev
= (HPEN
)SelectObject(hdc
, hpenBack
);
144 // we erase the 1-pixel border
145 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
147 // shift check mark 1 pixel to the right (it looks better like this)
151 // first create a monochrome bitmap in a memory DC
152 HDC hdcMem
= CreateCompatibleDC(hdc
);
153 HBITMAP hbmpCheck
= CreateBitmap(nCheckWidth
, nCheckHeight
, 1, 1, 0);
154 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcMem
, hbmpCheck
);
156 // then draw a check mark into it
157 #if defined(__WIN32__) && !defined(__SC__)
161 rect
.right
= nCheckWidth
;
162 rect
.bottom
= nCheckHeight
;
164 DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
);
166 // In WIN16, draw a cross
167 HPEN blackPen
= CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
168 HPEN whiteBrush
= (HPEN
)GetStockObject(WHITE_BRUSH
);
169 HPEN hPenOld
= (HPEN
)::SelectObject(hdcMem
, blackPen
);
170 HPEN hBrushOld
= (HPEN
)::SelectObject(hdcMem
, whiteBrush
);
171 ::SetROP2(hdcMem
, R2_COPYPEN
);
172 Rectangle(hdcMem
, 0, 0, nCheckWidth
, nCheckHeight
);
173 MoveTo(hdcMem
, 0, 0);
174 LineTo(hdcMem
, nCheckWidth
, nCheckHeight
);
175 MoveTo(hdcMem
, nCheckWidth
, 0);
176 LineTo(hdcMem
, 0, nCheckHeight
);
177 ::SelectObject(hdcMem
, hPenOld
);
178 ::SelectObject(hdcMem
, hBrushOld
);
179 ::DeleteObject(blackPen
);
182 // finally copy it to screen DC and clean up
183 BitBlt(hdc
, x
, y
, nCheckWidth
- 1, nCheckHeight
,
184 hdcMem
, 0, 0, SRCCOPY
);
186 SelectObject(hdcMem
, hbmpOld
);
187 DeleteObject(hbmpCheck
);
191 // now we draw the smaller rectangle
196 // draw hollow gray rectangle
197 (void)SelectObject(hdc
, hpenGray
);
198 HBRUSH hbrPrev
= (HBRUSH
)SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
199 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
202 (void)SelectObject(hdc
, hpenPrev
);
203 (void)SelectObject(hdc
, hbrPrev
);
205 DeleteObject(hpenBack
);
206 DeleteObject(hpenGray
);
209 dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight);
212 dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight);
213 dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y);
223 // change the state of the item and redraw it
224 void wxCheckListBoxItem::Check(bool check
)
228 // index may be changed because new items were added/deleted
229 if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex
)
232 int index
= m_pParent
->GetItemIndex(this);
234 wxASSERT_MSG( index
!= wxNOT_FOUND
, wxT("what does this item do here?") );
236 m_nIndex
= (size_t)index
;
239 HWND hwndListbox
= (HWND
)m_pParent
->GetHWND();
244 if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
,
245 m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR
)
247 wxLogDebug(wxT("LB_GETITEMRECT failed"));
250 // FIXME this doesn't work if the listbox is scrolled!
251 size_t nHeight
= m_pParent
->GetItemHeight();
252 size_t y
= m_nIndex
* nHeight
;
256 rcUpdate
.right
= GetDefaultMarginWidth() ;
257 rcUpdate
.bottom
= y
+ nHeight
;
260 InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
);
263 // send an "item checked" event
264 void wxCheckListBoxItem::SendEvent()
266 wxCommandEvent
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId());
267 event
.SetInt(m_nIndex
);
268 event
.SetEventObject(m_pParent
);
269 m_pParent
->ProcessCommand(event
);
272 // ----------------------------------------------------------------------------
273 // implementation of wxCheckListBox class
274 // ----------------------------------------------------------------------------
276 // define event table
277 // ------------------
278 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
)
279 EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
)
280 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
)
286 // def ctor: use Create() to really create the control
287 wxCheckListBox::wxCheckListBox()
291 // ctor which creates the associated control
292 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
293 const wxPoint
& pos
, const wxSize
& size
,
294 int nStrings
, const wxString choices
[],
295 long style
, const wxValidator
& val
,
296 const wxString
& name
)
298 Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
);
301 bool wxCheckListBox::Create(wxWindow
*parent
, wxWindowID id
,
302 const wxPoint
& pos
, const wxSize
& size
,
303 int n
, const wxString choices
[],
305 const wxValidator
& validator
, const wxString
& name
)
307 return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
,
308 style
| wxLB_OWNERDRAW
, validator
, name
);
311 // misc overloaded methods
312 // -----------------------
314 void wxCheckListBox::Delete(int N
)
316 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
317 wxT("invalid index in wxListBox::Delete") );
319 wxListBox::Delete(N
);
324 m_aItems
.RemoveAt(N
);
327 bool wxCheckListBox::SetFont( const wxFont
&font
)
330 for ( i
= 0; i
< m_aItems
.GetCount(); i
++ )
331 m_aItems
[i
]->SetFont(font
);
333 wxListBox::SetFont(font
);
338 // create/retrieve item
339 // --------------------
341 // create a check list box item
342 wxOwnerDrawn
*wxCheckListBox::CreateLboxItem(size_t nIndex
)
344 wxCheckListBoxItem
*pItem
= new wxCheckListBoxItem(this, nIndex
);
350 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT
*item
)
352 if ( wxListBox::MSWOnMeasure(item
) ) {
353 MEASUREITEMSTRUCT
*pStruct
= (MEASUREITEMSTRUCT
*)item
;
356 m_nItemHeight
= pStruct
->itemHeight
;
358 // add place for the check mark
359 pStruct
->itemWidth
+= wxOwnerDrawn::GetDefaultMarginWidth();
370 bool wxCheckListBox::IsChecked(size_t uiIndex
) const
372 wxCHECK_MSG( uiIndex
< (size_t)GetCount(), FALSE
, _T("bad wxCheckListBox index") );
374 return GetItem(uiIndex
)->IsChecked();
377 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
)
379 wxCHECK_RET( uiIndex
< (size_t)GetCount(), _T("bad wxCheckListBox index") );
381 GetItem(uiIndex
)->Check(bCheck
);
387 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
)
398 switch ( event
.KeyCode() )
409 case WXK_NUMPAD_SUBTRACT
:
420 wxArrayInt selections
;
422 if ( HasMultipleSelection() )
424 count
= GetSelections(selections
);
429 selections
.Add(GetSelection());
432 for ( int i
= 0; i
< count
; i
++ )
434 wxCheckListBoxItem
*item
= GetItem(selections
[i
]);
437 wxFAIL_MSG( _T("no wxCheckListBoxItem?") );
449 item
->Check( oper
== Set
);
453 wxFAIL_MSG( _T("what should this key do?") );
456 // we should send an event as this has been done by the user and
457 // not by the program
461 else // nothing to do
467 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
)
469 // clicking on the item selects it, clicking on the checkmark toggles
470 if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) {
471 int nItem
= HitTest(event
.GetX(), event
.GetY());
473 if ( nItem
!= wxNOT_FOUND
) {
474 wxCheckListBoxItem
*item
= GetItem(nItem
);
478 //else: it's not an error, just click outside of client zone
481 // implement default behaviour: clicking on the item selects it
486 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const
489 int nItem
= (int)::SendMessage
497 // FIXME this doesn't work when the listbox is scrolled!
498 int nItem
= y
/ m_nItemHeight
;
501 return nItem
>= m_noItems
? wxNOT_FOUND
: nItem
;