1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        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 // ---------------------------------------------------------------------------- 
  21 #pragma implementation "checklst.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/object.h" 
  35     #include "wx/colour.h" 
  37     #include "wx/bitmap.h" 
  38     #include "wx/window.h" 
  39     #include "wx/listbox.h" 
  40     #include "wx/dcmemory.h" 
  42     #include "wx/settings.h" 
  47 #include "wx/ownerdrw.h" 
  48 #include "wx/checklst.h" 
  53 #if defined(__GNUWIN32_OLD__) 
  54     #include "wx/msw/gnuwin32/extra.h" 
  57 // ---------------------------------------------------------------------------- 
  59 // ---------------------------------------------------------------------------- 
  61 // get item (converted to right type) 
  62 #define GetItem(n)    ((wxCheckListBoxItem *)(GetItem(n))) 
  64 // ============================================================================ 
  66 // ============================================================================ 
  68 IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
) 
  70 // ---------------------------------------------------------------------------- 
  71 // declaration and implementation of wxCheckListBoxItem class 
  72 // ---------------------------------------------------------------------------- 
  74 class wxCheckListBoxItem 
: public wxOwnerDrawn
 
  76 friend class WXDLLEXPORT wxCheckListBox
; 
  79   wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
); 
  82   virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
); 
  84   // simple accessors and operations 
  85   bool IsChecked() const { return m_bChecked
; } 
  87   void Check(bool bCheck
); 
  88   void Toggle() { Check(!IsChecked()); } 
  94     DECLARE_NO_COPY_CLASS(wxCheckListBoxItem
) 
  96   wxCheckListBox 
*m_pParent
; 
 100 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
) 
 101                   : wxOwnerDrawn(wxEmptyString
, TRUE
)   // checkable 
 107   // we don't initialize m_nCheckHeight/Width vars because it's 
 108   // done in OnMeasure while they are used only in OnDraw and we 
 109   // know that there will always be OnMeasure before OnDraw 
 112   SetMarginWidth(GetDefaultMarginWidth()); 
 116  * JACS - I've got the owner-draw stuff partially working with WIN16, 
 117  * with a really horrible-looking cross for wxCheckListBox instead of a 
 118  * check - could use a bitmap check-mark instead, defined in wx.rc. 
 119  * Also there's a refresh problem whereby it doesn't always draw the 
 120  * check until you click to the right of it, which is OK for WIN32. 
 123 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
 124                                     wxODAction act
, wxODStatus stat
) 
 127     stat 
= (wxOwnerDrawn::wxODStatus
)(stat 
| wxOwnerDrawn::wxODChecked
); 
 129   if ( wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) { 
 130     // ## using native API for performance and precision 
 131     size_t nCheckWidth  
= GetDefaultMarginWidth(), 
 132          nCheckHeight 
= m_pParent
->GetItemHeight(); 
 137     HDC hdc 
= (HDC
)dc
.GetHDC(); 
 140     HPEN hpenBack 
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_WINDOW
)), 
 141          hpenGray 
= CreatePen(PS_SOLID
, 0, RGB(128, 128, 128)), 
 142          hpenPrev 
= (HPEN
)SelectObject(hdc
, hpenBack
); 
 144     // we erase the 1-pixel border 
 145     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 147     // shift check mark 1 pixel to the right (it looks better like this) 
 151       // first create a monochrome bitmap in a memory DC 
 152       HDC hdcMem 
= CreateCompatibleDC(hdc
); 
 153       HBITMAP hbmpCheck 
= CreateBitmap(nCheckWidth
, nCheckHeight
, 1, 1, 0); 
 154       HBITMAP hbmpOld 
= (HBITMAP
)SelectObject(hdcMem
, hbmpCheck
); 
 156       // then draw a check mark into it 
 157 #if defined(__WIN32__) && !defined(__SC__) 
 161       rect
.right  
= nCheckWidth
; 
 162       rect
.bottom 
= nCheckHeight
; 
 164       DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
); 
 166       // In WIN16, draw a cross 
 167       HPEN blackPen 
= CreatePen(PS_SOLID
, 1, RGB(0, 0, 0)); 
 168       HPEN whiteBrush 
= (HPEN
)GetStockObject(WHITE_BRUSH
); 
 169       HPEN hPenOld 
= (HPEN
)::SelectObject(hdcMem
, blackPen
); 
 170       HPEN hBrushOld 
= (HPEN
)::SelectObject(hdcMem
, whiteBrush
); 
 171       ::SetROP2(hdcMem
, R2_COPYPEN
); 
 172       Rectangle(hdcMem
, 0, 0, nCheckWidth
, nCheckHeight
); 
 173       MoveTo(hdcMem
, 0, 0); 
 174       LineTo(hdcMem
, nCheckWidth
, nCheckHeight
); 
 175       MoveTo(hdcMem
, nCheckWidth
, 0); 
 176       LineTo(hdcMem
, 0, nCheckHeight
); 
 177       ::SelectObject(hdcMem
, hPenOld
); 
 178       ::SelectObject(hdcMem
, hBrushOld
); 
 179       ::DeleteObject(blackPen
); 
 182       // finally copy it to screen DC and clean up 
 183       BitBlt(hdc
, x
, y
, nCheckWidth 
- 1, nCheckHeight
, 
 184              hdcMem
, 0, 0, SRCCOPY
); 
 186       SelectObject(hdcMem
, hbmpOld
); 
 187       DeleteObject(hbmpCheck
); 
 191     // now we draw the smaller rectangle 
 196     // draw hollow gray rectangle 
 197     (void)SelectObject(hdc
, hpenGray
); 
 198     HBRUSH hbrPrev  
= (HBRUSH
)SelectObject(hdc
, GetStockObject(NULL_BRUSH
)); 
 199     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 202     (void)SelectObject(hdc
, hpenPrev
); 
 203     (void)SelectObject(hdc
, hbrPrev
); 
 205     DeleteObject(hpenBack
); 
 206     DeleteObject(hpenGray
); 
 209     dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight); 
 212       dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight); 
 213       dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y); 
 223 // change the state of the item and redraw it 
 224 void wxCheckListBoxItem::Check(bool check
) 
 228     // index may be changed because new items were added/deleted 
 229     if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex 
) 
 232         int index 
= m_pParent
->GetItemIndex(this); 
 234         wxASSERT_MSG( index 
!= wxNOT_FOUND
, wxT("what does this item do here?") ); 
 236         m_nIndex 
= (size_t)index
; 
 239     HWND hwndListbox 
= (HWND
)m_pParent
->GetHWND(); 
 244         if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
, 
 245                            m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR 
) 
 247             wxLogDebug(wxT("LB_GETITEMRECT failed")); 
 250         // FIXME this doesn't work if the listbox is scrolled! 
 251         size_t nHeight 
= m_pParent
->GetItemHeight(); 
 252         size_t y 
= m_nIndex 
* nHeight
; 
 256         rcUpdate
.right  
= GetDefaultMarginWidth() ; 
 257         rcUpdate
.bottom 
= y 
+ nHeight 
; 
 260     InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
); 
 263 // send an "item checked" event 
 264 void wxCheckListBoxItem::SendEvent() 
 266     wxCommandEvent 
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId()); 
 267     event
.SetInt(m_nIndex
); 
 268     event
.SetEventObject(m_pParent
); 
 269     m_pParent
->ProcessCommand(event
); 
 272 // ---------------------------------------------------------------------------- 
 273 // implementation of wxCheckListBox class 
 274 // ---------------------------------------------------------------------------- 
 276 // define event table 
 277 // ------------------ 
 278 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
) 
 279   EVT_KEY_DOWN(wxCheckListBox::OnKeyDown
) 
 280   EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
) 
 286 // def ctor: use Create() to really create the control 
 287 wxCheckListBox::wxCheckListBox() 
 291 // ctor which creates the associated control 
 292 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 293                                const wxPoint
& pos
, const wxSize
& size
, 
 294                                int nStrings
, const wxString choices
[], 
 295                                long style
, const wxValidator
& val
, 
 296                                const wxString
& name
) 
 298     Create(parent
, id
, pos
, size
, nStrings
, choices
, style
, val
, name
); 
 301 bool wxCheckListBox::Create(wxWindow 
*parent
, wxWindowID id
, 
 302                             const wxPoint
& pos
, const wxSize
& size
, 
 303                             int n
, const wxString choices
[], 
 305                             const wxValidator
& validator
, const wxString
& name
) 
 307     return wxListBox::Create(parent
, id
, pos
, size
, n
, choices
, 
 308                              style 
| wxLB_OWNERDRAW
, validator
, name
); 
 311 // misc overloaded methods 
 312 // ----------------------- 
 314 void wxCheckListBox::Delete(int N
) 
 316     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 317                  wxT("invalid index in wxListBox::Delete") ); 
 319     wxListBox::Delete(N
); 
 324     m_aItems
.RemoveAt(N
); 
 327 bool wxCheckListBox::SetFont( const wxFont 
&font 
) 
 330     for ( i 
= 0; i 
< m_aItems
.GetCount(); i
++ ) 
 331         m_aItems
[i
]->SetFont(font
); 
 333     wxListBox::SetFont(font
); 
 338 // create/retrieve item 
 339 // -------------------- 
 341 // create a check list box item 
 342 wxOwnerDrawn 
*wxCheckListBox::CreateLboxItem(size_t nIndex
) 
 344   wxCheckListBoxItem 
*pItem 
= new wxCheckListBoxItem(this, nIndex
); 
 350 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 352   if ( wxListBox::MSWOnMeasure(item
) ) { 
 353     MEASUREITEMSTRUCT 
*pStruct 
= (MEASUREITEMSTRUCT 
*)item
; 
 356     m_nItemHeight 
= pStruct
->itemHeight
; 
 358     // add place for the check mark 
 359     pStruct
->itemWidth 
+= wxOwnerDrawn::GetDefaultMarginWidth(); 
 370 bool wxCheckListBox::IsChecked(size_t uiIndex
) const 
 372     wxCHECK_MSG( uiIndex 
< (size_t)GetCount(), FALSE
, _T("bad wxCheckListBox index") ); 
 374     return GetItem(uiIndex
)->IsChecked(); 
 377 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
) 
 379     wxCHECK_RET( uiIndex 
< (size_t)GetCount(), _T("bad wxCheckListBox index") ); 
 381     GetItem(uiIndex
)->Check(bCheck
); 
 387 void wxCheckListBox::OnKeyDown(wxKeyEvent
& event
) 
 398     switch ( event
.GetKeyCode() ) 
 409         case WXK_NUMPAD_SUBTRACT
: 
 420         wxArrayInt selections
; 
 422         if ( HasMultipleSelection() ) 
 424             count 
= GetSelections(selections
); 
 428             int sel 
= GetSelection(); 
 436         for ( int i 
= 0; i 
< count
; i
++ ) 
 438             wxCheckListBoxItem 
*item 
= GetItem(selections
[i
]); 
 441                 wxFAIL_MSG( _T("no wxCheckListBoxItem?") ); 
 453                     item
->Check( oper 
== Set 
); 
 457                     wxFAIL_MSG( _T("what should this key do?") ); 
 460             // we should send an event as this has been done by the user and 
 461             // not by the program 
 465     else // nothing to do 
 471 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
) 
 473   // clicking on the item selects it, clicking on the checkmark toggles 
 474   if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) { 
 475     int nItem 
= HitTest(event
.GetX(), event
.GetY()); 
 477     if ( nItem 
!= wxNOT_FOUND 
) { 
 478       wxCheckListBoxItem 
*item 
= GetItem(nItem
); 
 482     //else: it's not an error, just click outside of client zone 
 485     // implement default behaviour: clicking on the item selects it 
 490 int wxCheckListBox::DoHitTestItem(wxCoord x
, wxCoord y
) const 
 493     int nItem 
= (int)::SendMessage
 
 501     // FIXME this doesn't work when the listbox is scrolled! 
 502     int nItem 
= y 
/ m_nItemHeight
; 
 505   return nItem 
>= m_noItems 
? wxNOT_FOUND 
: nItem
;