1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/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 licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #if wxUSE_CHECKLISTBOX && wxUSE_OWNER_DRAWN 
  29 #include "wx/checklst.h" 
  32     #include "wx/object.h" 
  33     #include "wx/colour.h" 
  35     #include "wx/bitmap.h" 
  36     #include "wx/window.h" 
  37     #include "wx/listbox.h" 
  38     #include "wx/dcmemory.h" 
  40     #include "wx/settings.h" 
  45 #include "wx/ownerdrw.h" 
  47 #include "wx/msw/wrapwin.h" 
  50 #include "wx/msw/private.h" 
  52 // ---------------------------------------------------------------------------- 
  54 // ---------------------------------------------------------------------------- 
  56 // get item (converted to right type) 
  57 #define GetItem(n)    ((wxCheckListBoxItem *)(GetItem(n))) 
  59 // ============================================================================ 
  61 // ============================================================================ 
  64 #if wxUSE_EXTENDED_RTTI 
  65 WX_DEFINE_FLAGS( wxCheckListBoxStyle 
) 
  67 wxBEGIN_FLAGS( wxCheckListBoxStyle 
) 
  68     // new style border flags, we put them first to 
  69     // use them for streaming out 
  70     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  71     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  72     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  73     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  74     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  75     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  77     // old style border flags 
  78     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  79     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  80     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  81     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  82     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  83     wxFLAGS_MEMBER(wxBORDER
) 
  85     // standard window styles 
  86     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  87     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  88     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  89     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  90     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  91     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  92     wxFLAGS_MEMBER(wxVSCROLL
) 
  93     wxFLAGS_MEMBER(wxHSCROLL
) 
  95     wxFLAGS_MEMBER(wxLB_SINGLE
) 
  96     wxFLAGS_MEMBER(wxLB_MULTIPLE
) 
  97     wxFLAGS_MEMBER(wxLB_EXTENDED
) 
  98     wxFLAGS_MEMBER(wxLB_HSCROLL
) 
  99     wxFLAGS_MEMBER(wxLB_ALWAYS_SB
) 
 100     wxFLAGS_MEMBER(wxLB_NEEDED_SB
) 
 101     wxFLAGS_MEMBER(wxLB_SORT
) 
 102     wxFLAGS_MEMBER(wxLB_OWNERDRAW
) 
 104 wxEND_FLAGS( wxCheckListBoxStyle 
) 
 106 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckListBox
, wxListBox
,"wx/checklst.h") 
 108 wxBEGIN_PROPERTIES_TABLE(wxCheckListBox
) 
 109     wxEVENT_PROPERTY( Toggle 
, wxEVT_COMMAND_CHECKLISTBOX_TOGGLED 
, wxCommandEvent 
) 
 110     wxPROPERTY_FLAGS( WindowStyle 
, wxCheckListBoxStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, wxLB_OWNERDRAW 
/*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 111 wxEND_PROPERTIES_TABLE() 
 113 wxBEGIN_HANDLERS_TABLE(wxCheckListBox
) 
 114 wxEND_HANDLERS_TABLE() 
 116 wxCONSTRUCTOR_4( wxCheckListBox 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
) 
 119 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
) 
 122 // ---------------------------------------------------------------------------- 
 123 // declaration and implementation of wxCheckListBoxItem class 
 124 // ---------------------------------------------------------------------------- 
 126 class wxCheckListBoxItem 
: public wxOwnerDrawn
 
 128 friend class WXDLLEXPORT wxCheckListBox
; 
 131     wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
); 
 134     virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
); 
 136     // simple accessors and operations 
 137     bool IsChecked() const { return m_bChecked
; } 
 139     void Check(bool bCheck
); 
 140     void Toggle() { Check(!IsChecked()); } 
 146     wxCheckListBox 
*m_pParent
; 
 149     DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
) 
 152 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
) 
 153                   : wxOwnerDrawn(wxEmptyString
, true)   // checkable 
 159     // we don't initialize m_nCheckHeight/Width vars because it's 
 160     // done in OnMeasure while they are used only in OnDraw and we 
 161     // know that there will always be OnMeasure before OnDraw 
 163     // fix appearance for check list boxes: they don't look quite the same as 
 165     SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK
) - 
 166                       2*wxSystemSettings::GetMetric(wxSYS_EDGE_X
) + 1); 
 167     SetBackgroundColour(pParent
->GetBackgroundColour()); 
 170 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
 171                                     wxODAction act
, wxODStatus stat
) 
 173     // first draw the label 
 175         stat 
= (wxOwnerDrawn::wxODStatus
)(stat 
| wxOwnerDrawn::wxODChecked
); 
 177     if ( !wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) 
 181     // now draw the check mark part 
 182     size_t nCheckWidth  
= GetDefaultMarginWidth(), 
 183            nCheckHeight 
= m_pParent
->GetItemHeight(); 
 188     HDC hdc 
= (HDC
)dc
.GetHDC(); 
 190     // create pens, brushes &c 
 191     COLORREF colBg 
= ::GetSysColor(COLOR_WINDOW
); 
 192     AutoHPEN 
hpenBack(colBg
), 
 193              hpenGray(RGB(0xc0, 0xc0, 0xc0)); 
 195     SelectInHDC 
selPen(hdc
, (HGDIOBJ
)hpenBack
); 
 196     AutoHBRUSH 
hbrBack(colBg
); 
 197     SelectInHDC 
selBrush(hdc
, hbrBack
); 
 199     // erase the background: it could have been filled with the selected colour 
 200     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth 
+ 1, rc
.GetBottom() + 1); 
 202     // shift check mark 1 pixel to the right, looks better like this 
 207         // first create a monochrome bitmap in a memory DC 
 208         MemoryHDC 
hdcMem(hdc
); 
 209         MonoBitmap 
hbmpCheck(nCheckWidth
, nCheckHeight
); 
 210         SelectInHDC 
selBmp(hdcMem
, hbmpCheck
); 
 212         // then draw a check mark into it 
 213         RECT rect 
= { 0, 0, nCheckWidth
, nCheckHeight 
}; 
 214         ::DrawFrameControl(hdcMem
, &rect
, 
 216                            DFC_BUTTON
, DFCS_BUTTONCHECK
 
 218                            DFC_MENU
, DFCS_MENUCHECK
 
 222         // finally copy it to screen DC 
 223         ::BitBlt(hdc
, x
, y
, nCheckWidth
, nCheckHeight
, hdcMem
, 0, 0, SRCCOPY
); 
 226     // now we draw the smaller rectangle 
 231     // draw hollow gray rectangle 
 232     (void)::SelectObject(hdc
, (HGDIOBJ
)hpenGray
); 
 234     SelectInHDC 
selBrush2(hdc
, ::GetStockObject(NULL_BRUSH
)); 
 235     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 240 // change the state of the item and redraw it 
 241 void wxCheckListBoxItem::Check(bool check
) 
 245     // index may be changed because new items were added/deleted 
 246     if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex 
) 
 249         int index 
= m_pParent
->GetItemIndex(this); 
 251         wxASSERT_MSG( index 
!= wxNOT_FOUND
, wxT("what does this item do here?") ); 
 253         m_nIndex 
= (size_t)index
; 
 256     HWND hwndListbox 
= (HWND
)m_pParent
->GetHWND(); 
 260     if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
, 
 261                        m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR 
) 
 263         wxLogDebug(wxT("LB_GETITEMRECT failed")); 
 266     ::InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
); 
 269 // send an "item checked" event 
 270 void wxCheckListBoxItem::SendEvent() 
 272     wxCommandEvent 
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId()); 
 273     event
.SetInt(m_nIndex
); 
 274     event
.SetEventObject(m_pParent
); 
 275     m_pParent
->ProcessCommand(event
); 
 278 // ---------------------------------------------------------------------------- 
 279 // implementation of wxCheckListBox class 
 280 // ---------------------------------------------------------------------------- 
 282 // define event table 
 283 // ------------------ 
 284 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
) 
 285   EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
) 
 286   EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
) 
 292 // def ctor: use Create() to really create the control 
 293 wxCheckListBox::wxCheckListBox() 
 297 // ctor which creates the associated control 
 298 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 299                                const wxPoint
& pos
, const wxSize
& size
, 
 300                                int nStrings
, const wxString choices
[], 
 301                                long style
, const wxValidator
& val
, 
 302                                const wxString
& name
) 
 304     Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
); 
 307 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 308                                const wxPoint
& pos
, const wxSize
& size
, 
 309                                const wxArrayString
& choices
, 
 310                                long style
, const wxValidator
& val
, 
 311                                const wxString
& name
) 
 313     Create(parent
, id
, pos
, size
, choices
, style
, val
, name
); 
 316 bool wxCheckListBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 317                             const wxPoint
& pos
, const wxSize
& size
, 
 318                             int n
, const wxString choices
[], 
 320                             const wxValidator
& validator
, const wxString
& name
) 
 322     return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
, 
 323                              style 
| wxLB_OWNERDRAW
, validator
, name
); 
 326 bool wxCheckListBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 327                             const wxPoint
& pos
, const wxSize
& size
, 
 328                             const wxArrayString
& choices
, 
 330                             const wxValidator
& validator
, const wxString
& name
) 
 332     return wxListBox::Create(parent
, id
, pos
, size
, choices
, 
 333                              style 
| wxLB_OWNERDRAW
, validator
, name
); 
 336 // misc overloaded methods 
 337 // ----------------------- 
 339 void wxCheckListBox::Delete(unsigned int n
) 
 341     wxCHECK_RET( IsValid(n
), 
 342                  wxT("invalid index in wxListBox::Delete") ); 
 344     wxListBox::Delete(n
); 
 349     m_aItems
.RemoveAt(n
); 
 352 bool wxCheckListBox::SetFont( const wxFont 
&font 
) 
 355     for ( i 
= 0; i 
< m_aItems
.GetCount(); i
++ ) 
 356         m_aItems
[i
]->SetFont(font
); 
 358     wxListBox::SetFont(font
); 
 363 // create/retrieve item 
 364 // -------------------- 
 366 // create a check list box item 
 367 wxOwnerDrawn 
*wxCheckListBox::CreateLboxItem(size_t nIndex
) 
 369   wxCheckListBoxItem 
*pItem 
= new wxCheckListBoxItem(this, nIndex
); 
 375 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 377   if ( wxListBox::MSWOnMeasure(item
) ) { 
 378     MEASUREITEMSTRUCT 
*pStruct 
= (MEASUREITEMSTRUCT 
*)item
; 
 381     m_nItemHeight 
= pStruct
->itemHeight
; 
 383     // add place for the check mark 
 384     pStruct
->itemWidth 
+= wxOwnerDrawn::GetDefaultMarginWidth(); 
 395 bool wxCheckListBox::IsChecked(unsigned int uiIndex
) const 
 397     wxCHECK_MSG( IsValid(uiIndex
), false, _T("bad wxCheckListBox index") ); 
 399     return GetItem(uiIndex
)->IsChecked(); 
 402 void wxCheckListBox::Check(unsigned int uiIndex
, bool bCheck
) 
 404     wxCHECK_RET( IsValid(uiIndex
), _T("bad wxCheckListBox index") ); 
 406     GetItem(uiIndex
)->Check(bCheck
); 
 412 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
) 
 423     switch ( event
.GetKeyCode() ) 
 434         case WXK_NUMPAD_SUBTRACT
: 
 445         wxArrayInt selections
; 
 447         if ( HasMultipleSelection() ) 
 449             count 
= GetSelections(selections
); 
 453             int sel 
= GetSelection(); 
 461         for ( int i 
= 0; i 
< count
; i
++ ) 
 463             wxCheckListBoxItem 
*item 
= GetItem(selections
[i
]); 
 466                 wxFAIL_MSG( _T("no wxCheckListBoxItem?") ); 
 478                     item
->Check( oper 
== Set 
); 
 482                     wxFAIL_MSG( _T("what should this key do?") ); 
 485             // we should send an event as this has been done by the user and 
 486             // not by the program 
 490     else // nothing to do 
 496 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
) 
 498     // clicking on the item selects it, clicking on the checkmark toggles 
 499     if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) 
 501         int nItem 
= HitTest(event
.GetX(), event
.GetY()); 
 503         if ( nItem 
!= wxNOT_FOUND 
) 
 505             wxCheckListBoxItem 
*item 
= GetItem(nItem
); 
 509         //else: it's not an error, just click outside of client zone 
 513         // implement default behaviour: clicking on the item selects it 
 518 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const 
 520     int nItem 
= (int)::SendMessage
 
 528     return nItem 
>= (int)m_noItems 
? wxNOT_FOUND 
: nItem
; 
 532 wxSize 
wxCheckListBox::DoGetBestSize() const 
 534     wxSize best 
= wxListBox::DoGetBestSize(); 
 535     best
.x 
+= wxOwnerDrawn::GetDefaultMarginWidth();  // add room for the checkbox