1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        checkedlistctrl.cpp
3 // Purpose:     wxCheckedListCtrl
4 // Author:      Unknown ? (found at http://wiki.wxwidgets.org/wiki.pl?WxListCtrl)
5 // Modified by: Francesco Montorsi
6 // Created:     2005/06/29
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2005 Francesco Montorsi
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
15 
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19 
20 // includes
21 #include "checkedlistctrl.h"
22 #include <wx/image.h>
23 #include <wx/icon.h>
24 
25 
26 #if wxUSE_CHECKEDLISTCTRL
27 
28 // resources
29 #include "checked.xpm"
30 #include "unchecked.xpm"
31 #include "checked_dis.xpm"
32 #include "unchecked_dis.xpm"
33 
34 IMPLEMENT_CLASS(wxCheckedListCtrl, wxListCtrl)
35 BEGIN_EVENT_TABLE(wxCheckedListCtrl, wxListCtrl)
36     EVT_LEFT_DOWN(wxCheckedListCtrl::OnMouseEvent)
37     EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxCheckedListCtrl::OnActivateEvent)
38 END_EVENT_TABLE()
39 
40 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_CHECKED);
41 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
42 
43 
44 
45 // ------------------
46 // wxCHECKEDLISTCTRL
47 // ------------------
48 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pt,const wxSize & sz,long style,const wxValidator & validator,const wxString & name)49 bool wxCheckedListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pt,
50         const wxSize& sz, long style, const wxValidator& validator, const wxString& name)
51 {
52 	if (!wxListCtrl::Create(parent, id, pt, sz, style, validator, name))
53 		return FALSE;
54 
55     int img_size = 32;
56     m_imageList.Create(img_size, img_size, TRUE);
57     SetImageList(&m_imageList, wxIMAGE_LIST_SMALL);
58 
59 	// the add order must respect the wxCLC_XXX_IMGIDX defines in the headers !
60 
61     {wxImage i(unchecked_xpm);     wxBitmap bmp = wxBitmap(i.Scale(img_size, img_size)); m_imageList.Add(bmp);}
62     {wxImage i(checked_xpm);       wxBitmap bmp = wxBitmap(i.Scale(img_size, img_size)); m_imageList.Add(bmp);}
63     {wxImage i(unchecked_dis_xpm); wxBitmap bmp = wxBitmap(i.Scale(img_size, img_size)); m_imageList.Add(bmp);}
64     {wxImage i(checked_dis_xpm);   wxBitmap bmp = wxBitmap(i.Scale(img_size, img_size)); m_imageList.Add(bmp);}
65 
66 //    m_imageList.Add(wxIcon(unchecked_xpm));
67 //    m_imageList.Add(wxIcon(checked_xpm));
68 //    m_imageList.Add(wxIcon(unchecked_dis_xpm));
69 //    m_imageList.Add(wxIcon(checked_dis_xpm));
70 
71 	return TRUE;
72 }
73 
74 /* static */
GetItemImageFromAdditionalState(int addstate)75 int wxCheckedListCtrl::GetItemImageFromAdditionalState(int addstate)
76 {
77 	bool checked = (addstate & wxLIST_STATE_CHECKED) != 0;
78 	bool enabled = (addstate & wxLIST_STATE_ENABLED) != 0;
79 
80 	if (checked && enabled)
81 		return wxCLC_CHECKED_IMGIDX;
82 	else if (checked && !enabled)
83 		return wxCLC_DISABLED_CHECKED_IMGIDX;
84 	else if (!checked && enabled)
85 		return wxCLC_UNCHECKED_IMGIDX;
86 
87 	wxASSERT(!checked && !enabled);		// this is the last possibility
88 	return wxCLC_DISABLED_UNCHECKED_IMGIDX;
89 }
90 
GetBgColourFromAdditionalState(int additionalstate)91 wxColour wxCheckedListCtrl::GetBgColourFromAdditionalState(int additionalstate)
92 {
93 	if ((additionalstate & wxLIST_STATE_ENABLED) &&
94 		this->IsEnabled())
95 		return wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
96 #ifdef __WXMSW__
97 	return wxColour(212, 208, 200);
98 #else
99 	return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
100 #endif
101 }
102 
103 /* static */
GetAndRemoveAdditionalState(long * state,int statemask)104 int wxCheckedListCtrl::GetAndRemoveAdditionalState(long *state, int statemask)
105 {
106 	int additionalstate = 0;
107 	if (!state) return -1;
108 
109 	// extract the bits we are interested in
110 	bool checked = (*state & wxLIST_STATE_CHECKED) != 0;
111 	bool enabled = (*state & wxLIST_STATE_ENABLED) != 0;
112 
113 	// and set them in a different variable if they are included in the statemask
114 	if (checked && (statemask & wxLIST_STATE_CHECKED)) additionalstate |= wxLIST_STATE_CHECKED;
115 	if (enabled && (statemask & wxLIST_STATE_ENABLED)) additionalstate |= wxLIST_STATE_ENABLED;
116 
117 	// remove them from the original state var...
118 	*state &= ~wxLIST_STATE_CHECKED;
119 	*state &= ~wxLIST_STATE_ENABLED;
120 	return additionalstate;
121 }
122 
GetItem(wxListItem & info) const123 bool wxCheckedListCtrl::GetItem(wxListItem& info) const
124 {
125 	// wx internal wxListCtrl::GetItem remove from the state mask the
126 	// wxLIST_STATE_CHECKED & wxLIST_STATE_ENABLED bits since they
127 	// are not part of wx standard flags... so we need to check those
128 	// flags against the original wxListItem's statemask...
129 	wxListItem original(info);
130 
131 #ifdef __WXDEBUG__
132 	// we always want to retrieve also the image state for checking purposes...
133 	info.m_mask |= wxLIST_MASK_IMAGE;
134 #endif
135 
136 	if (!wxListCtrl::GetItem(info))
137 		return FALSE;
138 
139 	// these are our additional supported states: read them from m_stateList
140 	bool checked = (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED) != 0;
141 	bool enabled = (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED) != 0;
142 
143 	// now intercept state requests about enable or check mode
144 	if ((original.m_mask & wxLIST_MASK_STATE) &&
145 		(original.m_stateMask & wxLIST_STATE_CHECKED)) {
146 		info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
147 		info.m_stateMask |= wxLIST_STATE_CHECKED;
148 		info.m_mask |= wxLIST_MASK_STATE;		// contains valid info !
149 	}
150 	if ((original.m_mask & wxLIST_MASK_STATE) &&
151 		(original.m_stateMask & wxLIST_STATE_ENABLED)) {
152 		info.m_state |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
153 		info.m_stateMask |= wxLIST_STATE_ENABLED;
154 		info.m_mask |= wxLIST_MASK_STATE;		// contains valid info !
155 	}
156 
157 	// check that state & image are synch
158 //#ifdef __WXDEBUG__
159 #if 0
160 	wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
161 					wxT("Something wrong ! See InsertItem()"));
162 
163 	// read info by image index
164 	bool imagecheck = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
165 						(info.m_image == wxCLC_DISABLED_CHECKED_IMGIDX);
166 	bool imageenabled = (info.m_image == wxCLC_CHECKED_IMGIDX) ||
167 						(info.m_image == wxCLC_UNCHECKED_IMGIDX);
168 	wxASSERT_MSG((checked && imagecheck) || (!checked && !imagecheck),
169 		wxT("This is item has checked state but it's shown as unchecked (or viceversa)"));
170 	wxASSERT_MSG((enabled && imageenabled) || (!enabled && !imageenabled),
171 		wxT("This is item has enabled state but it's shown as disabled (or viceversa)"));
172 #endif
173 
174 	return TRUE;
175 }
176 
SetItem(wxListItem & info)177 bool wxCheckedListCtrl::SetItem(wxListItem& info)
178 {
179 	// remove the checked & enabled states from the state flag:
180 	// we'll store them in our separate array
181 	int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
182 
183 	// set image index
184 	// we will ignore the info.m_image field since we need
185 	// to overwrite it...
186 	if (info.m_mask & wxLIST_MASK_STATE) {
187 
188 		// if some state is not included in the state mask, then get the state info
189 		// from our internal state array
190 		if (!(info.m_stateMask & wxLIST_STATE_ENABLED))
191 			additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_ENABLED);
192 		if (!(info.m_stateMask & wxLIST_STATE_CHECKED))
193 			additionalstate |= (m_stateList[info.m_itemId] & wxLIST_STATE_CHECKED);
194 
195 		// state is valid: use it to determine the image to set...
196 		info.m_mask |= wxLIST_MASK_IMAGE;
197 		info.m_image = GetItemImageFromAdditionalState(additionalstate);
198 
199 		// since when changing the background color, also the foreground color
200 		// and the font of the item are changed, we try to respect the user
201 		// choices of such attributes
202 		info.SetTextColour(this->GetItemTextColour(info.GetId()));
203 #if wxCHECK_VERSION(2, 6, 2)
204 		// before wx 2.6.2 the wxListCtrl::SetItemFont function is missing
205 		info.SetFont(this->GetItemFont(info.GetId()));
206 #endif
207 
208 		// change the background color to respect the enabled/disabled status...
209 		info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
210 
211 		m_stateList[info.m_itemId] = additionalstate;
212 
213 	} else {
214 
215 		// state is invalid; don't change image
216 		info.m_mask &= ~wxLIST_MASK_IMAGE;
217 	}
218 
219 	// save the changes
220 	return wxListCtrl::SetItem(info);
221 }
222 
InsertItem(wxListItem & info)223 long wxCheckedListCtrl::InsertItem(wxListItem &info)
224 {
225 	int additionalstate = GetAndRemoveAdditionalState(&info.m_state, info.m_stateMask);
226 	if (!(info.m_mask & wxLIST_MASK_STATE) ||
227 		!(info.m_stateMask & wxLIST_STATE_ENABLED)) {
228 
229 		// if not specified, the default additional state is ENABLED
230 		additionalstate = wxLIST_STATE_ENABLED;
231 	}
232 
233 	// we always want to insert items with images...
234 	info.m_mask |= wxLIST_MASK_IMAGE;
235 	info.m_image = GetItemImageFromAdditionalState(additionalstate);
236 	info.SetBackgroundColour(GetBgColourFromAdditionalState(additionalstate));
237 
238 	int itemcount = GetItemCount();
239 	wxASSERT_MSG(info.m_itemId <= itemcount, wxT("Invalid index !"));
240 	wxASSERT_MSG((int)m_stateList.GetCount() == (int)GetItemCount(),
241 					wxT("Something wrong !"));
242 	if (info.m_itemId == itemcount) {
243 
244 		// we are adding a new item at the end of the list
245 		m_stateList.Add(additionalstate);
246 
247 	} else {
248 
249 		// we must shift all following items
250 		for (int i=itemcount; i > info.m_itemId; i++)
251 			m_stateList[i] = m_stateList[i-1];
252 		m_stateList[info.m_itemId] = additionalstate;
253 	}
254 
255 	return wxListCtrl::InsertItem(info);
256 }
257 
SetItemState(long item,long state,long stateMask)258 bool wxCheckedListCtrl::SetItemState(long item, long state, long stateMask)
259 {
260 	wxListItem li;
261 	li.SetId(item);
262 	li.SetMask(wxLIST_MASK_STATE);
263 	li.SetState(state);
264 	li.SetStateMask(stateMask);
265 
266 	// so we are sure to use wxCheckedListCtrl::SetItem
267 	// (and not wxListCtrl::SetItem)
268 	return SetItem(li);
269 }
270 
GetItemState(long item,long stateMask) const271 int wxCheckedListCtrl::GetItemState(long item, long stateMask) const
272 {
273 	wxListItem li;
274 	li.SetId(item);
275 	li.SetMask(wxLIST_MASK_STATE);
276 	li.SetStateMask(stateMask);
277 
278 	// so we are sure to use wxCheckedListCtrl::GetItem
279 	// (and not wxListCtrl::GetItem)
280 	if (!GetItem(li))
281 		return -1;
282 	return li.GetState();
283 }
284 
SetItem(long index,int col,const wxString & label,int WXUNUSED (imageId))285 long wxCheckedListCtrl::SetItem(long index, int col, const wxString& label, int WXUNUSED(imageId))
286 {
287 	wxListItem li;
288 	li.SetId(index);
289 	li.SetColumn(col);
290 	li.SetText(label);
291 	li.SetMask(wxLIST_MASK_TEXT);
292 
293 	// so we are sure to use wxCheckedListCtrl::SetItem
294 	// (and not wxListCtrl::SetItem)
295 	return SetItem(li);
296 }
297 
InsertItem(long index,const wxString & label,int WXUNUSED (imageIndex))298 long wxCheckedListCtrl::InsertItem( long index, const wxString& label, int WXUNUSED(imageIndex) )
299 {
300     wxListItem info;
301     info.m_text = label;
302     info.m_mask = wxLIST_MASK_TEXT;
303     info.m_itemId = index;
304     return InsertItem(info);
305 }
306 
Check(long item,bool checked)307 void wxCheckedListCtrl::Check(long item, bool checked)
308 {
309 	// NB: the "statemask" is not the "mask" of a list item;
310 	//     in the "mask" you use the wxLIST_MASK_XXXX defines;
311 	//     in the "statemask" you use the wxLIST_STATE_XXX defines
312 	//     to set a specific bit of the wxListInfo::m_state var
313 	if (checked)
314 		// the 2nd parameter says: activate the STATE bit relative to CHECK feature
315 		// the 3rd parameter says: set only *that* bit
316 		SetItemState(item, wxLIST_STATE_CHECKED, wxLIST_STATE_CHECKED);
317 	else
318 		SetItemState(item, 0, wxLIST_STATE_CHECKED);
319 }
320 
Enable(long item,bool enable)321 void wxCheckedListCtrl::Enable(long item, bool enable)
322 {
323 	if (enable)
324 		// the 2nd parameter says: activate the STATE bit relative to ENABLE feature
325 		// the 3rd parameter says: set only *that* bit
326 		SetItemState(item, wxLIST_STATE_ENABLED, wxLIST_STATE_ENABLED);
327 	else
328 		SetItemState(item, 0, wxLIST_STATE_ENABLED);
329 }
330 
EnableAll(bool enable)331 void wxCheckedListCtrl::EnableAll(bool enable)
332 {
333 	for (int i=0; i < GetItemCount(); i++)
334 		Enable(i, enable);
335 }
336 
CheckAll(bool check)337 void wxCheckedListCtrl::CheckAll(bool check)
338 {
339 	for (int i=0; i < GetItemCount(); i++)
340 		Check(i, check);
341 }
342 
DeleteItem(long item)343 bool wxCheckedListCtrl::DeleteItem(long item)
344 {
345 	// shift our additional state array
346 	//for (int i=item,max=GetItemCount(); i < max-1; i++)
347 	//	m_stateList[i] = m_stateList[i+1];
348 	m_stateList.RemoveAt(item, 1);
349 
350 	return wxListCtrl::DeleteItem(item);
351 }
352 
GetCheckedItemCount() const353 int wxCheckedListCtrl::GetCheckedItemCount() const
354 {
355 	int res = 0;
356 	for (int i=0; i<GetItemCount(); i++)
357 		if (IsChecked(i))
358 			res++;
359 
360 	return res;
361 }
362 
363 // event handlers
364 
OnMouseEvent(wxMouseEvent & event)365 void wxCheckedListCtrl::OnMouseEvent(wxMouseEvent& event)
366 {
367     if (!event.LeftDown()) {
368         event.Skip();
369 		return;
370 	}
371 
372 	int flags;
373 	long item = HitTest(event.GetPosition(), flags);
374 	if (item == wxNOT_FOUND || !IsEnabled(item)) {
375 
376 		// skip this item
377 		event.Skip();
378 		return;
379 	}
380 
381 	// user clicked exactly on the checkbox or on the item row ?
382 	bool processcheck = (flags & wxLIST_HITTEST_ONITEMICON) ||
383 		((GetWindowStyle() & wxCLC_CHECK_WHEN_SELECTING) &&
384 		(flags & wxLIST_HITTEST_ONITEM));
385 
386 	if (processcheck) {
387 
388 		wxListEvent ev(wxEVT_NULL, GetId());
389 		ev.m_itemIndex = item;
390 
391 		// send the check event
392 		if (IsChecked(item)) {
393 
394 			ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
395 			Check(item, FALSE);
396 			AddPendingEvent(ev);
397 
398 		} else {
399 
400 			ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_CHECKED);
401 			Check(item, TRUE);
402 			AddPendingEvent(ev);
403 		}
404 	}
405 
406 	event.Skip();
407 }
408 
OnActivateEvent(wxListEvent & event)409 void wxCheckedListCtrl::OnActivateEvent(wxListEvent& event)
410 {
411     long item = event.GetItem().GetId();
412     if (item == wxNOT_FOUND || !IsEnabled(item))
413     {
414         // skip this item
415         event.Skip();
416         return;
417     }
418 
419     wxListEvent ev(wxEVT_NULL, GetId());
420     ev.m_itemIndex = item;
421 
422     // send the check event
423     if (IsChecked(item))
424     {
425         ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_UNCHECKED);
426         Check(item, FALSE);
427         AddPendingEvent(ev);
428     } else {
429         ev.SetEventType(wxEVT_COMMAND_LIST_ITEM_CHECKED);
430         Check(item, TRUE);
431         AddPendingEvent(ev);
432     }
433     event.Skip();
434 }
435 
436 #endif		// wxUSE_CHECKEDLISTCTRL
437