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/msw/wrapwin.h" 
  33     #include "wx/object.h" 
  34     #include "wx/colour.h" 
  36     #include "wx/bitmap.h" 
  37     #include "wx/window.h" 
  38     #include "wx/listbox.h" 
  39     #include "wx/dcmemory.h" 
  40     #include "wx/settings.h" 
  44 #include "wx/ownerdrw.h" 
  48 #include "wx/msw/private.h" 
  50 // ---------------------------------------------------------------------------- 
  52 // ---------------------------------------------------------------------------- 
  54 // get item (converted to right type) 
  55 #define GetItem(n)    ((wxCheckListBoxItem *)(GetItem(n))) 
  57 // ============================================================================ 
  59 // ============================================================================ 
  62 #if wxUSE_EXTENDED_RTTI 
  63 WX_DEFINE_FLAGS( wxCheckListBoxStyle 
) 
  65 wxBEGIN_FLAGS( wxCheckListBoxStyle 
) 
  66     // new style border flags, we put them first to 
  67     // use them for streaming out 
  68     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  69     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  70     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  71     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  72     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  73     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  75     // old style border flags 
  76     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  77     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  78     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  79     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  80     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  81     wxFLAGS_MEMBER(wxBORDER
) 
  83     // standard window styles 
  84     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  85     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  86     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  87     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  88     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  89     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  90     wxFLAGS_MEMBER(wxVSCROLL
) 
  91     wxFLAGS_MEMBER(wxHSCROLL
) 
  93     wxFLAGS_MEMBER(wxLB_SINGLE
) 
  94     wxFLAGS_MEMBER(wxLB_MULTIPLE
) 
  95     wxFLAGS_MEMBER(wxLB_EXTENDED
) 
  96     wxFLAGS_MEMBER(wxLB_HSCROLL
) 
  97     wxFLAGS_MEMBER(wxLB_ALWAYS_SB
) 
  98     wxFLAGS_MEMBER(wxLB_NEEDED_SB
) 
  99     wxFLAGS_MEMBER(wxLB_SORT
) 
 100     wxFLAGS_MEMBER(wxLB_OWNERDRAW
) 
 102 wxEND_FLAGS( wxCheckListBoxStyle 
) 
 104 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckListBox
, wxListBox
,"wx/checklst.h") 
 106 wxBEGIN_PROPERTIES_TABLE(wxCheckListBox
) 
 107     wxEVENT_PROPERTY( Toggle 
, wxEVT_COMMAND_CHECKLISTBOX_TOGGLED 
, wxCommandEvent 
) 
 108     wxPROPERTY_FLAGS( WindowStyle 
, wxCheckListBoxStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, wxLB_OWNERDRAW 
/*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 109 wxEND_PROPERTIES_TABLE() 
 111 wxBEGIN_HANDLERS_TABLE(wxCheckListBox
) 
 112 wxEND_HANDLERS_TABLE() 
 114 wxCONSTRUCTOR_4( wxCheckListBox 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
) 
 117 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
) 
 120 // ---------------------------------------------------------------------------- 
 121 // declaration and implementation of wxCheckListBoxItem class 
 122 // ---------------------------------------------------------------------------- 
 124 class wxCheckListBoxItem 
: public wxOwnerDrawn
 
 126 friend class WXDLLEXPORT wxCheckListBox
; 
 129     wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
); 
 132     virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
); 
 134     // simple accessors and operations 
 135     bool IsChecked() const { return m_bChecked
; } 
 137     void Check(bool bCheck
); 
 138     void Toggle() { Check(!IsChecked()); } 
 144     wxCheckListBox 
*m_pParent
; 
 147     DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
) 
 150 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
) 
 151                   : wxOwnerDrawn(wxEmptyString
, true)   // checkable 
 157     // we don't initialize m_nCheckHeight/Width vars because it's 
 158     // done in OnMeasure while they are used only in OnDraw and we 
 159     // know that there will always be OnMeasure before OnDraw 
 161     // fix appearance for check list boxes: they don't look quite the same as 
 163     SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK
) - 
 164                       2*wxSystemSettings::GetMetric(wxSYS_EDGE_X
) + 1); 
 165     SetBackgroundColour(pParent
->GetBackgroundColour()); 
 168 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
 169                                     wxODAction act
, wxODStatus stat
) 
 171     // first draw the label 
 173         stat 
= (wxOwnerDrawn::wxODStatus
)(stat 
| wxOwnerDrawn::wxODChecked
); 
 175     if ( !wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) 
 179     // now draw the check mark part 
 180     size_t nCheckWidth  
= GetDefaultMarginWidth(), 
 181            nCheckHeight 
= m_pParent
->GetItemHeight(); 
 186     HDC hdc 
= (HDC
)dc
.GetHDC(); 
 188     // create pens, brushes &c 
 189     COLORREF colBg 
= ::GetSysColor(COLOR_WINDOW
); 
 190     AutoHPEN 
hpenBack(colBg
), 
 191              hpenGray(RGB(0xc0, 0xc0, 0xc0)); 
 193     SelectInHDC 
selPen(hdc
, (HGDIOBJ
)hpenBack
); 
 194     AutoHBRUSH 
hbrBack(colBg
); 
 195     SelectInHDC 
selBrush(hdc
, hbrBack
); 
 197     // erase the background: it could have been filled with the selected colour 
 198     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth 
+ 1, rc
.GetBottom() + 1); 
 200     // shift check mark 1 pixel to the right, looks better like this 
 205         // first create a monochrome bitmap in a memory DC 
 206         MemoryHDC 
hdcMem(hdc
); 
 207         MonoBitmap 
hbmpCheck(nCheckWidth
, nCheckHeight
); 
 208         SelectInHDC 
selBmp(hdcMem
, hbmpCheck
); 
 210         // then draw a check mark into it 
 211         RECT rect 
= { 0, 0, nCheckWidth
, nCheckHeight 
}; 
 212         ::DrawFrameControl(hdcMem
, &rect
, 
 214                            DFC_BUTTON
, DFCS_BUTTONCHECK
 
 216                            DFC_MENU
, DFCS_MENUCHECK
 
 220         // finally copy it to screen DC 
 221         ::BitBlt(hdc
, x
, y
, nCheckWidth
, nCheckHeight
, hdcMem
, 0, 0, SRCCOPY
); 
 224     // now we draw the smaller rectangle 
 229     // draw hollow gray rectangle 
 230     (void)::SelectObject(hdc
, (HGDIOBJ
)hpenGray
); 
 232     SelectInHDC 
selBrush2(hdc
, ::GetStockObject(NULL_BRUSH
)); 
 233     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 238 // change the state of the item and redraw it 
 239 void wxCheckListBoxItem::Check(bool check
) 
 243     // index may be changed because new items were added/deleted 
 244     if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex 
) 
 247         int index 
= m_pParent
->GetItemIndex(this); 
 249         wxASSERT_MSG( index 
!= wxNOT_FOUND
, wxT("what does this item do here?") ); 
 251         m_nIndex 
= (size_t)index
; 
 254     HWND hwndListbox 
= (HWND
)m_pParent
->GetHWND(); 
 258     if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
, 
 259                        m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR 
) 
 261         wxLogDebug(wxT("LB_GETITEMRECT failed")); 
 264     ::InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
); 
 267 // send an "item checked" event 
 268 void wxCheckListBoxItem::SendEvent() 
 270     wxCommandEvent 
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId()); 
 271     event
.SetInt(m_nIndex
); 
 272     event
.SetEventObject(m_pParent
); 
 273     m_pParent
->ProcessCommand(event
); 
 276 // ---------------------------------------------------------------------------- 
 277 // implementation of wxCheckListBox class 
 278 // ---------------------------------------------------------------------------- 
 280 // define event table 
 281 // ------------------ 
 282 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
) 
 283   EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
) 
 284   EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
) 
 290 // def ctor: use Create() to really create the control 
 291 wxCheckListBox::wxCheckListBox() 
 295 // ctor which creates the associated control 
 296 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 297                                const wxPoint
& pos
, const wxSize
& size
, 
 298                                int nStrings
, const wxString choices
[], 
 299                                long style
, const wxValidator
& val
, 
 300                                const wxString
& name
) 
 302     Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
); 
 305 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 306                                const wxPoint
& pos
, const wxSize
& size
, 
 307                                const wxArrayString
& choices
, 
 308                                long style
, const wxValidator
& val
, 
 309                                const wxString
& name
) 
 311     Create(parent
, id
, pos
, size
, choices
, style
, val
, name
); 
 314 bool wxCheckListBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 315                             const wxPoint
& pos
, const wxSize
& size
, 
 316                             int n
, const wxString choices
[], 
 318                             const wxValidator
& validator
, const wxString
& name
) 
 320     return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
, 
 321                              style 
| wxLB_OWNERDRAW
, validator
, name
); 
 324 bool wxCheckListBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 325                             const wxPoint
& pos
, const wxSize
& size
, 
 326                             const wxArrayString
& choices
, 
 328                             const wxValidator
& validator
, const wxString
& name
) 
 330     return wxListBox::Create(parent
, id
, pos
, size
, choices
, 
 331                              style 
| wxLB_OWNERDRAW
, validator
, name
); 
 334 // misc overloaded methods 
 335 // ----------------------- 
 337 void wxCheckListBox::Delete(unsigned int n
) 
 339     wxCHECK_RET( IsValid(n
), 
 340                  wxT("invalid index in wxListBox::Delete") ); 
 342     wxListBox::Delete(n
); 
 347     m_aItems
.RemoveAt(n
); 
 350 bool wxCheckListBox::SetFont( const wxFont 
&font 
) 
 353     for ( i 
= 0; i 
< m_aItems
.GetCount(); i
++ ) 
 354         m_aItems
[i
]->SetFont(font
); 
 356     wxListBox::SetFont(font
); 
 361 // create/retrieve item 
 362 // -------------------- 
 364 // create a check list box item 
 365 wxOwnerDrawn 
*wxCheckListBox::CreateLboxItem(size_t nIndex
) 
 367   wxCheckListBoxItem 
*pItem 
= new wxCheckListBoxItem(this, nIndex
); 
 373 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 375   if ( wxListBox::MSWOnMeasure(item
) ) { 
 376     MEASUREITEMSTRUCT 
*pStruct 
= (MEASUREITEMSTRUCT 
*)item
; 
 379     m_nItemHeight 
= pStruct
->itemHeight
; 
 381     // add place for the check mark 
 382     pStruct
->itemWidth 
+= wxOwnerDrawn::GetDefaultMarginWidth(); 
 393 bool wxCheckListBox::IsChecked(unsigned int uiIndex
) const 
 395     wxCHECK_MSG( IsValid(uiIndex
), false, _T("bad wxCheckListBox index") ); 
 397     return GetItem(uiIndex
)->IsChecked(); 
 400 void wxCheckListBox::Check(unsigned int uiIndex
, bool bCheck
) 
 402     wxCHECK_RET( IsValid(uiIndex
), _T("bad wxCheckListBox index") ); 
 404     GetItem(uiIndex
)->Check(bCheck
); 
 410 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
) 
 421     switch ( event
.GetKeyCode() ) 
 432         case WXK_NUMPAD_SUBTRACT
: 
 443         wxArrayInt selections
; 
 445         if ( HasMultipleSelection() ) 
 447             count 
= GetSelections(selections
); 
 451             int sel 
= GetSelection(); 
 459         for ( int i 
= 0; i 
< count
; i
++ ) 
 461             wxCheckListBoxItem 
*item 
= GetItem(selections
[i
]); 
 464                 wxFAIL_MSG( _T("no wxCheckListBoxItem?") ); 
 476                     item
->Check( oper 
== Set 
); 
 480                     wxFAIL_MSG( _T("what should this key do?") ); 
 483             // we should send an event as this has been done by the user and 
 484             // not by the program 
 488     else // nothing to do 
 494 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
) 
 496     // clicking on the item selects it, clicking on the checkmark toggles 
 497     if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) 
 499         int nItem 
= HitTest(event
.GetX(), event
.GetY()); 
 501         if ( nItem 
!= wxNOT_FOUND 
) 
 503             wxCheckListBoxItem 
*item 
= GetItem(nItem
); 
 507         //else: it's not an error, just click outside of client zone 
 511         // implement default behaviour: clicking on the item selects it 
 516 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const 
 518     int nItem 
= (int)::SendMessage
 
 526     return nItem 
>= (int)m_noItems 
? wxNOT_FOUND 
: nItem
; 
 530 wxSize 
wxCheckListBox::DoGetBestSize() const 
 532     wxSize best 
= wxListBox::DoGetBestSize(); 
 533     best
.x 
+= wxOwnerDrawn::GetDefaultMarginWidth();  // add room for the checkbox