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"
33 #include "wx/object.h"
34 #include "wx/colour.h"
36 #include "wx/bitmap.h"
37 #include "wx/window.h"
38 #include "wx/listbox.h"
39 #include "wx/ownerdrw.h"
40 #include "wx/settings.h"
41 #include "wx/dcmemory.h"
42 #include "wx/msw/checklst.h"
49 #include "wx/msw/gnuwin32/extra.h"
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 // get item (converted to right type)
57 #define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n)))
59 // ============================================================================
61 // ============================================================================
63 #if !USE_SHARED_LIBRARY
64 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
)
67 // ----------------------------------------------------------------------------
68 // declaration and implementation of wxCheckListBoxItem class
69 // ----------------------------------------------------------------------------
71 class wxCheckListBoxItem
: public wxOwnerDrawn
73 friend class wxCheckListBox
;
76 wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
);
79 virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
);
82 bool IsChecked() const { return m_bChecked
; }
83 void Check(bool bCheck
);
84 void Toggle() { Check(!IsChecked()); }
88 wxCheckListBox
*m_pParent
;
92 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox
*pParent
, size_t nIndex
)
93 : wxOwnerDrawn("", TRUE
) // checkable
99 // we don't initialize m_nCheckHeight/Width vars because it's
100 // done in OnMeasure while they are used only in OnDraw and we
101 // know that there will always be OnMeasure before OnDraw
104 SetFont(wxSystemSettings::GetSystemFont(wxSYS_ANSI_VAR_FONT
));
105 SetMarginWidth(GetDefaultMarginWidth());
109 * JACS - I've got the owner-draw stuff partially working with WIN16,
110 * with a really horrible-looking cross for wxCheckListBox instead of a
111 * check - could use a bitmap check-mark instead, defined in wx.rc.
112 * Also there's a refresh problem whereby it doesn't always draw the
113 * check until you click to the right of it, which is OK for WIN32.
116 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
,
117 wxODAction act
, wxODStatus stat
)
120 stat
= (wxOwnerDrawn::wxODStatus
)(stat
| wxOwnerDrawn::wxODChecked
);
122 if ( wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) {
123 // ## using native API for performance and precision
124 size_t nCheckWidth
= GetDefaultMarginWidth(),
125 nCheckHeight
= m_pParent
->GetItemHeight();
130 HDC hdc
= (HDC
)dc
.GetHDC();
133 HPEN hpenBack
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_WINDOW
)),
134 hpenGray
= CreatePen(PS_SOLID
, 0, RGB(128, 128, 128)),
135 hpenPrev
= (HPEN
)SelectObject(hdc
, hpenBack
);
137 // we erase the 1-pixel border
138 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
140 // shift check mark 1 pixel to the right (it looks better like this)
144 // first create a monochrome bitmap in a memory DC
145 HDC hdcMem
= CreateCompatibleDC(hdc
);
146 HBITMAP hbmpCheck
= CreateBitmap(nCheckWidth
, nCheckHeight
, 1, 1, 0);
147 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcMem
, hbmpCheck
);
149 // then draw a check mark into it
150 RECT rect
= { 0, 0, nCheckWidth
, nCheckHeight
};
154 DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
);
157 // In WIN16, draw a cross
158 HPEN blackPen
= CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
159 HPEN whiteBrush
= GetStockObject(WHITE_BRUSH
);
160 HPEN hPenOld
= ::SelectObject(hdcMem
, blackPen
);
161 HPEN hBrushOld
= ::SelectObject(hdcMem
, whiteBrush
);
162 ::SetROP2(hdcMem
, R2_COPYPEN
);
163 Rectangle(hdcMem
, 0, 0, nCheckWidth
, nCheckHeight
);
164 MoveTo(hdcMem
, 0, 0);
165 LineTo(hdcMem
, nCheckWidth
, nCheckHeight
);
166 MoveTo(hdcMem
, nCheckWidth
, 0);
167 LineTo(hdcMem
, 0, nCheckHeight
);
168 ::SelectObject(hdcMem
, hPenOld
);
169 ::SelectObject(hdcMem
, hBrushOld
);
170 ::DeleteObject(blackPen
);
173 // finally copy it to screen DC and clean up
174 BitBlt(hdc
, x
, y
, nCheckWidth
- 1, nCheckHeight
,
175 hdcMem
, 0, 0, SRCCOPY
);
177 SelectObject(hdcMem
, hbmpOld
);
178 DeleteObject(hbmpCheck
);
182 // now we draw the smaller rectangle
187 // draw hollow gray rectangle
188 (void)SelectObject(hdc
, hpenGray
);
189 HBRUSH hbrPrev
= (HBRUSH
)SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
190 Rectangle(hdc
, x
, y
, x
+ nCheckWidth
, y
+ nCheckHeight
);
193 (void)SelectObject(hdc
, hpenPrev
);
194 (void)SelectObject(hdc
, hbrPrev
);
196 DeleteObject(hpenBack
);
197 DeleteObject(hpenGray
);
200 dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight);
203 dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight);
204 dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y);
214 // change the state of the item and redraw it
215 void wxCheckListBoxItem::Check(bool check
)
219 // index may be chanegd because new items were added/deleted
220 if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex
)
223 int index
= m_pParent
->GetItemIndex(this);
225 wxASSERT_MSG( index
!= wxNOT_FOUND
, "what does this item do here?" );
227 m_nIndex
= (size_t)index
;
230 HWND hwndListbox
= (HWND
)m_pParent
->GetHWND();
235 if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
,
236 m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR
)
238 wxLogDebug("LB_GETITEMRECT failed");
241 // FIXME this doesn't work if the listbox is scrolled!
242 size_t nHeight
= m_pParent
->GetItemHeight();
243 size_t y
= m_nIndex
* nHeight
;
244 RECT rcUpdate
= { 0, y
, GetDefaultMarginWidth(), y
+ nHeight
};
247 InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
);
249 wxCommandEvent
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId());
250 event
.SetInt(m_nIndex
);
251 event
.SetEventObject(m_pParent
);
252 m_pParent
->ProcessCommand(event
);
255 // ----------------------------------------------------------------------------
256 // implementation of wxCheckListBox class
257 // ----------------------------------------------------------------------------
259 // define event table
260 // ------------------
261 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
)
262 EVT_CHAR(wxCheckListBox::OnChar
)
263 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
)
269 // def ctor: use Create() to really create the control
270 wxCheckListBox::wxCheckListBox() : wxListBox()
274 // ctor which creates the associated control
275 wxCheckListBox::wxCheckListBox(wxWindow
*parent
, wxWindowID id
,
276 const wxPoint
& pos
, const wxSize
& size
,
277 int nStrings
, const wxString choices
[],
278 long style
, const wxValidator
& val
,
279 const wxString
& name
)
282 Create(parent
, id
, pos
, size
, nStrings
, choices
,
283 style
| wxLB_OWNERDRAW
, val
, name
);
286 void wxCheckListBox::Delete(int N
)
288 wxCHECK_RET( N
>= 0 && N
< m_noItems
,
289 "invalid index in wxListBox::Delete" );
291 wxListBox::Delete(N
);
299 void wxCheckListBox::InsertItems(int nItems
, const wxString items
[], int pos
)
301 wxCHECK_RET( pos
>= 0 && pos
<= m_noItems
,
302 "invalid index in wxCheckListBox::InsertItems" );
304 wxListBox::InsertItems(nItems
, items
, pos
);
307 for ( i
= 0; i
< nItems
; i
++ ) {
308 wxOwnerDrawn
*pNewItem
= CreateItem((size_t)(pos
+ i
));
309 pNewItem
->SetName(items
[i
]);
310 m_aItems
.Insert(pNewItem
, (size_t)(pos
+ i
));
311 ListBox_SetItemData((HWND
)GetHWND(), i
+ pos
, pNewItem
);
315 // create/retrieve item
316 // --------------------
318 // create a check list box item
319 wxOwnerDrawn
*wxCheckListBox::CreateItem(size_t nIndex
)
321 wxCheckListBoxItem
*pItem
= new wxCheckListBoxItem(this, nIndex
);
322 if ( m_windowFont
.Ok() )
323 pItem
->SetFont(m_windowFont
);
330 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT
*item
)
332 if ( wxListBox::MSWOnMeasure(item
) ) {
333 MEASUREITEMSTRUCT
*pStruct
= (MEASUREITEMSTRUCT
*)item
;
336 m_nItemHeight
= pStruct
->itemHeight
;
338 // add place for the check mark
339 pStruct
->itemWidth
+= wxOwnerDrawn::GetDefaultMarginWidth();
350 bool wxCheckListBox::IsChecked(size_t uiIndex
) const
352 return GetItem(uiIndex
)->IsChecked();
355 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
)
357 GetItem(uiIndex
)->Check(bCheck
);
363 void wxCheckListBox::OnChar(wxKeyEvent
& event
)
365 if ( event
.KeyCode() == WXK_SPACE
)
366 GetItem(GetSelection())->Toggle();
368 wxListBox::OnChar(event
);
371 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
)
373 // clicking on the item selects it, clicking on the checkmark toggles
374 if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) {
376 size_t nItem
= (size_t)::SendMessage
381 MAKELPARAM(event
.GetX(), event
.GetY())
384 // FIXME this doesn't work when the listbox is scrolled!
385 size_t nItem
= ((size_t)event
.GetY()) / m_nItemHeight
;
388 if ( nItem
< (size_t)m_noItems
)
389 GetItem(nItem
)->Toggle();
390 //else: it's not an error, just click outside of client zone
393 // implement default behaviour: clicking on the item selects it