1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/checklst.cpp
3 // Purpose: implementation of wxCheckListBox class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 16.11.97
7 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_CHECKLISTBOX && wxUSE_OWNER_DRAWN
27
28 #include "wx/checklst.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/msw/wrapcctl.h"
32 #include "wx/object.h"
33 #include "wx/colour.h"
34 #include "wx/font.h"
35 #include "wx/bitmap.h"
36 #include "wx/window.h"
37 #include "wx/listbox.h"
38 #include "wx/dcmemory.h"
39 #include "wx/settings.h"
40 #include "wx/log.h"
41 #endif
42
43 #include "wx/ownerdrw.h"
44
45 #include <windowsx.h>
46
47 #include "wx/renderer.h"
48 #include "wx/msw/private.h"
49 #include "wx/msw/dc.h"
50
51 // ----------------------------------------------------------------------------
52 // private functions
53 // ----------------------------------------------------------------------------
54
55 // get item (converted to right type)
56 #define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n)))
57
58 namespace
59 {
60 // space around check mark bitmap in pixels
61 static const int CHECKMARK_EXTRA_SPACE = 1;
62
63 // space between check bitmap and text label
64 static const int CHECKMARK_LABEL_SPACE = 2;
65
66 } // anonymous namespace
67
68 // ============================================================================
69 // implementation
70 // ============================================================================
71
72 // ----------------------------------------------------------------------------
73 // declaration and implementation of wxCheckListBoxItem class
74 // ----------------------------------------------------------------------------
75
76 class wxCheckListBoxItem : public wxOwnerDrawn
77 {
78 public:
79 // ctor
80 wxCheckListBoxItem(wxCheckListBox *parent);
81
82 // drawing functions
83 virtual bool OnDrawItem(wxDC& dc, const wxRect& rc, wxODAction act, wxODStatus stat);
84
85 // simple accessors and operations
GetParent() const86 wxCheckListBox *GetParent() const
87 { return m_parent; }
88
GetIndex() const89 int GetIndex() const
90 { return m_parent->GetItemIndex(const_cast<wxCheckListBoxItem*>(this)); }
91
GetName() const92 wxString GetName() const
93 { return m_parent->GetString(GetIndex()); }
94
95
IsChecked() const96 bool IsChecked() const
97 { return m_checked; }
98
Check(bool bCheck)99 void Check(bool bCheck)
100 { m_checked = bCheck; }
101
Toggle()102 void Toggle()
103 { Check(!IsChecked()); }
104
105 private:
106 wxCheckListBox *m_parent;
107 bool m_checked;
108
109 wxDECLARE_NO_COPY_CLASS(wxCheckListBoxItem);
110 };
111
wxCheckListBoxItem(wxCheckListBox * parent)112 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox *parent)
113 {
114 m_parent = parent;
115 m_checked = false;
116
117 wxSize size = wxRendererNative::Get().GetCheckBoxSize(parent);
118 size.x += 2 * CHECKMARK_EXTRA_SPACE + CHECKMARK_LABEL_SPACE;
119
120 SetMarginWidth(size.GetWidth());
121 SetBackgroundColour(parent->GetBackgroundColour());
122 }
123
OnDrawItem(wxDC & dc,const wxRect & rc,wxODAction act,wxODStatus stat)124 bool wxCheckListBoxItem::OnDrawItem(wxDC& dc, const wxRect& rc,
125 wxODAction act, wxODStatus stat)
126 {
127 // first draw the label
128 if ( !wxOwnerDrawn::OnDrawItem(dc, rc, act, stat) )
129 return false;
130
131 // now draw the check mark part
132 wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
133 HDC hdc = GetHdcOf(*impl);
134
135 wxSize size = wxRendererNative::Get().GetCheckBoxSize(GetParent());
136
137 // first create bitmap in a memory DC
138 MemoryHDC hdcMem(hdc);
139 CompatibleBitmap hBmpCheck(hdc, size.GetWidth(), size.GetHeight());
140
141 // then draw a check mark into it
142 {
143 SelectInHDC selBmp(hdcMem, hBmpCheck);
144
145 int flags = wxCONTROL_FLAT;
146 if ( IsChecked() )
147 flags |= wxCONTROL_CHECKED;
148
149 wxDCTemp dcMem(hdcMem);
150 wxRendererNative::Get().DrawCheckBox(GetParent(), dcMem, wxRect(size), flags);
151 } // select hBmpCheck out of hdcMem
152
153 // finally draw bitmap to screen
154
155 // position of check mark bitmap
156 int x = rc.GetX() + CHECKMARK_EXTRA_SPACE;
157 int y = rc.GetY() + (rc.GetHeight() - size.GetHeight()) / 2;
158
159 UINT uState = stat & wxOwnerDrawn::wxODSelected ? wxDSB_SELECTED : wxDSB_NORMAL;
160
161 // checkmarks should not be mirrored in RTL layout
162 DWORD oldLayout = impl->GetLayoutDirection() == wxLayout_RightToLeft ? LAYOUT_RTL : 0;
163 if ( oldLayout & LAYOUT_RTL )
164 ::SetLayout(hdc, oldLayout | LAYOUT_BITMAPORIENTATIONPRESERVED);
165 wxDrawStateBitmap(hdc, hBmpCheck, x, y, uState);
166 if ( oldLayout & LAYOUT_RTL )
167 ::SetLayout(hdc, oldLayout);
168
169 return true;
170 }
171
172 // ----------------------------------------------------------------------------
173 // implementation of wxCheckListBox class
174 // ----------------------------------------------------------------------------
175
176 // define event table
177 // ------------------
BEGIN_EVENT_TABLE(wxCheckListBox,wxListBox)178 BEGIN_EVENT_TABLE(wxCheckListBox, wxListBox)
179 EVT_KEY_DOWN(wxCheckListBox::OnKeyDown)
180 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick)
181 END_EVENT_TABLE()
182
183 // control creation
184 // ----------------
185
186 // def ctor: use Create() to really create the control
187 wxCheckListBox::wxCheckListBox()
188 {
189 }
190
191 // ctor which creates the associated control
wxCheckListBox(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,int nStrings,const wxString choices[],long style,const wxValidator & val,const wxString & name)192 wxCheckListBox::wxCheckListBox(wxWindow *parent, wxWindowID id,
193 const wxPoint& pos, const wxSize& size,
194 int nStrings, const wxString choices[],
195 long style, const wxValidator& val,
196 const wxString& name)
197 {
198 Create(parent, id, pos, size, nStrings, choices, style, val, name);
199 }
200
wxCheckListBox(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & val,const wxString & name)201 wxCheckListBox::wxCheckListBox(wxWindow *parent, wxWindowID id,
202 const wxPoint& pos, const wxSize& size,
203 const wxArrayString& choices,
204 long style, const wxValidator& val,
205 const wxString& name)
206 {
207 Create(parent, id, pos, size, choices, style, val, name);
208 }
209
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)210 bool wxCheckListBox::Create(wxWindow *parent, wxWindowID id,
211 const wxPoint& pos, const wxSize& size,
212 int n, const wxString choices[],
213 long style,
214 const wxValidator& validator, const wxString& name)
215 {
216 return wxListBox::Create(parent, id, pos, size, n, choices,
217 style | wxLB_OWNERDRAW, validator, name);
218 }
219
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)220 bool wxCheckListBox::Create(wxWindow *parent, wxWindowID id,
221 const wxPoint& pos, const wxSize& size,
222 const wxArrayString& choices,
223 long style,
224 const wxValidator& validator, const wxString& name)
225 {
226 return wxListBox::Create(parent, id, pos, size, choices,
227 style | wxLB_OWNERDRAW, validator, name);
228 }
229
230 // create/retrieve item
231 // --------------------
232
233 // create a check list box item
CreateLboxItem(size_t WXUNUSED (n))234 wxOwnerDrawn *wxCheckListBox::CreateLboxItem(size_t WXUNUSED(n))
235 {
236 wxCheckListBoxItem *pItem = new wxCheckListBoxItem(this);
237 return pItem;
238 }
239
240 // return item size
241 // ----------------
MSWOnMeasure(WXMEASUREITEMSTRUCT * item)242 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item)
243 {
244 if ( wxListBox::MSWOnMeasure(item) )
245 {
246 MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item;
247
248 wxSize size = wxRendererNative::Get().GetCheckBoxSize(this);
249 size.x += 2 * CHECKMARK_EXTRA_SPACE;
250 size.y += 2 * CHECKMARK_EXTRA_SPACE;
251
252 // add place for the check mark
253 pStruct->itemWidth += size.GetWidth();
254
255 if ( pStruct->itemHeight < static_cast<unsigned int>(size.GetHeight()) )
256 pStruct->itemHeight = size.GetHeight();
257
258 return true;
259 }
260
261 return false;
262 }
263
264 // check items
265 // -----------
266
IsChecked(unsigned int uiIndex) const267 bool wxCheckListBox::IsChecked(unsigned int uiIndex) const
268 {
269 wxCHECK_MSG( IsValid(uiIndex), false, wxT("bad wxCheckListBox index") );
270
271 return GetItem(uiIndex)->IsChecked();
272 }
273
Check(unsigned int uiIndex,bool bCheck)274 void wxCheckListBox::Check(unsigned int uiIndex, bool bCheck)
275 {
276 wxCHECK_RET( IsValid(uiIndex), wxT("bad wxCheckListBox index") );
277
278 GetItem(uiIndex)->Check(bCheck);
279 RefreshItem(uiIndex);
280 }
281
Toggle(unsigned int uiIndex)282 void wxCheckListBox::Toggle(unsigned int uiIndex)
283 {
284 wxCHECK_RET( IsValid(uiIndex), wxT("bad wxCheckListBox index") );
285
286 GetItem(uiIndex)->Toggle();
287 RefreshItem(uiIndex);
288 }
289
290 // process events
291 // --------------
292
OnKeyDown(wxKeyEvent & event)293 void wxCheckListBox::OnKeyDown(wxKeyEvent& event)
294 {
295 // what do we do?
296 enum
297 {
298 NONE,
299 TOGGLE,
300 SET,
301 CLEAR
302 } oper;
303
304 switch ( event.GetKeyCode() )
305 {
306 case WXK_SPACE:
307 oper = TOGGLE;
308 break;
309
310 case WXK_NUMPAD_ADD:
311 case '+':
312 oper = SET;
313 break;
314
315 case WXK_NUMPAD_SUBTRACT:
316 case '-':
317 oper = CLEAR;
318 break;
319
320 default:
321 oper = NONE;
322 }
323
324 if ( oper != NONE )
325 {
326 wxArrayInt selections;
327 int count = 0;
328 if ( HasMultipleSelection() )
329 {
330 count = GetSelections(selections);
331 }
332 else
333 {
334 int sel = GetSelection();
335 if (sel != -1)
336 {
337 count = 1;
338 selections.Add(sel);
339 }
340 }
341
342 for ( int i = 0; i < count; i++ )
343 {
344 int nItem = selections[i];
345
346 switch ( oper )
347 {
348 case TOGGLE:
349 Toggle(nItem);
350 break;
351
352 case SET:
353 case CLEAR:
354 Check(nItem, oper == SET);
355 break;
356
357 default:
358 wxFAIL_MSG( wxT("what should this key do?") );
359 }
360
361 // we should send an event as this has been done by the user and
362 // not by the program
363 SendEvent(nItem);
364 }
365 }
366 else // nothing to do
367 {
368 event.Skip();
369 }
370 }
371
OnLeftClick(wxMouseEvent & event)372 void wxCheckListBox::OnLeftClick(wxMouseEvent& event)
373 {
374 // clicking on the item selects it, clicking on the checkmark toggles
375
376 int nItem = HitTest(event.GetX(), event.GetY());
377
378 if ( nItem != wxNOT_FOUND )
379 {
380 wxRect rect;
381 GetItemRect(nItem, rect);
382
383 // convert item rect to check mark rect
384 wxSize size = wxRendererNative::Get().GetCheckBoxSize(this);
385 rect.x += CHECKMARK_EXTRA_SPACE;
386 rect.y += (rect.GetHeight() - size.GetHeight()) / 2;
387 rect.SetSize(size);
388
389 if ( rect.Contains(event.GetX(), event.GetY()) )
390 {
391 // people expect to get "kill focus" event for the currently
392 // focused control before getting events from the other controls
393 // and, equally importantly, they may prevent the focus change from
394 // taking place at all (e.g. because the old control contents is
395 // invalid and needs to be corrected) in which case we shouldn't
396 // generate this event at all
397 SetFocus();
398 if ( FindFocus() == this )
399 {
400 Toggle(nItem);
401 SendEvent(nItem);
402
403 // scroll one item down if the item is the last one
404 // and isn't visible at all
405 int h;
406 GetClientSize(NULL, &h);
407 if ( rect.GetBottom() > h )
408 ScrollLines(1);
409 }
410 }
411 else
412 {
413 // implement default behaviour: clicking on the item selects it
414 event.Skip();
415 }
416 }
417 else
418 {
419 // implement default behaviour on click outside of client zone
420 event.Skip();
421 }
422 }
423
DoGetBestClientSize() const424 wxSize wxCheckListBox::DoGetBestClientSize() const
425 {
426 wxSize best = wxListBox::DoGetBestClientSize();
427
428 // add room for the checkbox
429 wxSize size = wxRendererNative::Get().GetCheckBoxSize(const_cast<wxCheckListBox*>(this));
430 size.x += 2 * CHECKMARK_EXTRA_SPACE;
431 size.y += 2 * CHECKMARK_EXTRA_SPACE;
432
433 best.x += size.GetWidth();
434 if ( best.y < size.GetHeight() )
435 best.y = size.GetHeight();
436
437 CacheBestSize(best);
438 return best;
439 }
440
441 #endif // wxUSE_CHECKLISTBOX
442