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