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