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 license 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21 #pragma implementation "checklst.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.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/ownerdrw.h" 
  40 #include "wx/settings.h" 
  41 #include "wx/dcmemory.h" 
  42 #include "wx/msw/checklst.h" 
  49 #include "wx/msw/gnuwin32/extra.h" 
  52 // ---------------------------------------------------------------------------- 
  54 // ---------------------------------------------------------------------------- 
  56 // get item (converted to right type) 
  57 #define GetItem(n)    ((wxCheckListBoxItem *)(GetItem(n))) 
  59 // ============================================================================ 
  61 // ============================================================================ 
  63 #if !USE_SHARED_LIBRARY 
  64   IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
) 
  67 // ---------------------------------------------------------------------------- 
  68 // declaration and implementation of wxCheckListBoxItem class 
  69 // ---------------------------------------------------------------------------- 
  71 class wxCheckListBoxItem 
: public wxOwnerDrawn
 
  73 friend class wxCheckListBox
; 
  76   wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
); 
  79   virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
); 
  82   bool IsChecked() const  { return m_bChecked
;        } 
  83   void Check(bool bCheck
); 
  84   void Toggle() { Check(!IsChecked()); } 
  88   wxCheckListBox 
*m_pParent
; 
  92 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
) 
  93                   : wxOwnerDrawn("", TRUE
)   // checkable 
  99   // we don't initialize m_nCheckHeight/Width vars because it's 
 100   // done in OnMeasure while they are used only in OnDraw and we 
 101   // know that there will always be OnMeasure before OnDraw 
 104   SetFont(wxSystemSettings::GetSystemFont(wxSYS_ANSI_VAR_FONT
)); 
 105   SetMarginWidth(GetDefaultMarginWidth()); 
 109  * JACS - I've got the owner-draw stuff partially working with WIN16, 
 110  * with a really horrible-looking cross for wxCheckListBox instead of a 
 111  * check - could use a bitmap check-mark instead, defined in wx.rc. 
 112  * Also there's a refresh problem whereby it doesn't always draw the 
 113  * check until you click to the right of it, which is OK for WIN32. 
 116 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
 117                                     wxODAction act
, wxODStatus stat
) 
 120     stat 
= (wxOwnerDrawn::wxODStatus
)(stat 
| wxOwnerDrawn::wxODChecked
); 
 122   if ( wxOwnerDrawn::OnDrawItem(dc
, rc
, act
, stat
) ) { 
 123     // ## using native API for performance and precision 
 124     size_t nCheckWidth  
= GetDefaultMarginWidth(), 
 125          nCheckHeight 
= m_pParent
->GetItemHeight(); 
 130     HDC hdc 
= (HDC
)dc
.GetHDC(); 
 133     HPEN hpenBack 
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_WINDOW
)), 
 134          hpenGray 
= CreatePen(PS_SOLID
, 0, RGB(128, 128, 128)), 
 135          hpenPrev 
= (HPEN
)SelectObject(hdc
, hpenBack
); 
 137     // we erase the 1-pixel border 
 138     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 140     // shift check mark 1 pixel to the right (it looks better like this) 
 144       // first create a monochrome bitmap in a memory DC 
 145       HDC hdcMem 
= CreateCompatibleDC(hdc
); 
 146       HBITMAP hbmpCheck 
= CreateBitmap(nCheckWidth
, nCheckHeight
, 1, 1, 0); 
 147       HBITMAP hbmpOld 
= (HBITMAP
)SelectObject(hdcMem
, hbmpCheck
); 
 149       // then draw a check mark into it 
 150       RECT rect 
= { 0, 0, nCheckWidth
, nCheckHeight 
}; 
 154       DrawFrameControl(hdcMem
, &rect
, DFC_MENU
, DFCS_MENUCHECK
); 
 157       // In WIN16, draw a cross 
 158       HPEN blackPen 
= CreatePen(PS_SOLID
, 1, RGB(0, 0, 0)); 
 159       HPEN whiteBrush 
= GetStockObject(WHITE_BRUSH
); 
 160       HPEN hPenOld 
= ::SelectObject(hdcMem
, blackPen
); 
 161       HPEN hBrushOld 
= ::SelectObject(hdcMem
, whiteBrush
); 
 162       ::SetROP2(hdcMem
, R2_COPYPEN
); 
 163       Rectangle(hdcMem
, 0, 0, nCheckWidth
, nCheckHeight
); 
 164       MoveTo(hdcMem
, 0, 0); 
 165       LineTo(hdcMem
, nCheckWidth
, nCheckHeight
); 
 166       MoveTo(hdcMem
, nCheckWidth
, 0); 
 167       LineTo(hdcMem
, 0, nCheckHeight
); 
 168       ::SelectObject(hdcMem
, hPenOld
); 
 169       ::SelectObject(hdcMem
, hBrushOld
); 
 170       ::DeleteObject(blackPen
); 
 173       // finally copy it to screen DC and clean up 
 174       BitBlt(hdc
, x
, y
, nCheckWidth 
- 1, nCheckHeight
, 
 175              hdcMem
, 0, 0, SRCCOPY
); 
 177       SelectObject(hdcMem
, hbmpOld
); 
 178       DeleteObject(hbmpCheck
); 
 182     // now we draw the smaller rectangle 
 187     // draw hollow gray rectangle 
 188     (void)SelectObject(hdc
, hpenGray
); 
 189     HBRUSH hbrPrev  
= (HBRUSH
)SelectObject(hdc
, GetStockObject(NULL_BRUSH
)); 
 190     Rectangle(hdc
, x
, y
, x 
+ nCheckWidth
, y 
+ nCheckHeight
); 
 193     (void)SelectObject(hdc
, hpenPrev
); 
 194     (void)SelectObject(hdc
, hbrPrev
); 
 196     DeleteObject(hpenBack
); 
 197     DeleteObject(hpenGray
); 
 200     dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight); 
 203       dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight); 
 204       dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y); 
 214 // change the state of the item and redraw it 
 215 void wxCheckListBoxItem::Check(bool check
) 
 219     // index may be chanegd because new items were added/deleted 
 220     if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex 
) 
 223         int index 
= m_pParent
->GetItemIndex(this); 
 225         wxASSERT_MSG( index 
!= wxNOT_FOUND
, "what does this item do here?" ); 
 227         m_nIndex 
= (size_t)index
; 
 230     HWND hwndListbox 
= (HWND
)m_pParent
->GetHWND(); 
 235         if ( ::SendMessage(hwndListbox
, LB_GETITEMRECT
, 
 236                            m_nIndex
, (LPARAM
)&rcUpdate
) == LB_ERR 
) 
 238             wxLogDebug("LB_GETITEMRECT failed"); 
 241         // FIXME this doesn't work if the listbox is scrolled! 
 242         size_t nHeight 
= m_pParent
->GetItemHeight(); 
 243         size_t y 
= m_nIndex 
* nHeight
; 
 244         RECT rcUpdate 
= { 0, y
, GetDefaultMarginWidth(), y 
+ nHeight
}; 
 247     InvalidateRect(hwndListbox
, &rcUpdate
, FALSE
); 
 249     wxCommandEvent 
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId()); 
 250     event
.SetInt(m_nIndex
); 
 251     event
.SetEventObject(m_pParent
); 
 252     m_pParent
->ProcessCommand(event
); 
 255 // ---------------------------------------------------------------------------- 
 256 // implementation of wxCheckListBox class 
 257 // ---------------------------------------------------------------------------- 
 259 // define event table 
 260 // ------------------ 
 261 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
) 
 262   EVT_CHAR(wxCheckListBox::OnChar
) 
 263   EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
) 
 269 // def ctor: use Create() to really create the control 
 270 wxCheckListBox::wxCheckListBox() : wxListBox() 
 274 // ctor which creates the associated control 
 275 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 276                                const wxPoint
& pos
, const wxSize
& size
, 
 277                                int nStrings
, const wxString choices
[], 
 278                                long style
, const wxValidator
& val
, 
 279                                const wxString
& name
) 
 282     Create(parent
, id
, pos
, size
, nStrings
, choices
, 
 283            style 
| wxLB_OWNERDRAW
, val
, name
); 
 286 void wxCheckListBox::Delete(int N
) 
 288     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 289                  "invalid index in wxListBox::Delete" ); 
 291     wxListBox::Delete(N
); 
 299 void wxCheckListBox::InsertItems(int nItems
, const wxString items
[], int pos
) 
 301     wxCHECK_RET( pos 
>= 0 && pos 
<= m_noItems
, 
 302                  "invalid index in wxCheckListBox::InsertItems" ); 
 304     wxListBox::InsertItems(nItems
, items
, pos
); 
 307     for ( i 
= 0; i 
< nItems
; i
++ ) { 
 308         wxOwnerDrawn 
*pNewItem 
= CreateItem((size_t)(pos 
+ i
)); 
 309         pNewItem
->SetName(items
[i
]); 
 310         m_aItems
.Insert(pNewItem
, (size_t)(pos 
+ i
)); 
 311         ListBox_SetItemData((HWND
)GetHWND(), i 
+ pos
, pNewItem
); 
 315 // create/retrieve item 
 316 // -------------------- 
 318 // create a check list box item 
 319 wxOwnerDrawn 
*wxCheckListBox::CreateItem(size_t nIndex
) 
 321   wxCheckListBoxItem 
*pItem 
= new wxCheckListBoxItem(this, nIndex
); 
 323     pItem
->SetFont(m_font
); 
 330 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT 
*item
) 
 332   if ( wxListBox::MSWOnMeasure(item
) ) { 
 333     MEASUREITEMSTRUCT 
*pStruct 
= (MEASUREITEMSTRUCT 
*)item
; 
 336     m_nItemHeight 
= pStruct
->itemHeight
; 
 338     // add place for the check mark 
 339     pStruct
->itemWidth 
+= wxOwnerDrawn::GetDefaultMarginWidth(); 
 350 bool wxCheckListBox::IsChecked(size_t uiIndex
) const 
 352   return GetItem(uiIndex
)->IsChecked(); 
 355 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
) 
 357   GetItem(uiIndex
)->Check(bCheck
); 
 363 void wxCheckListBox::OnChar(wxKeyEvent
& event
) 
 365   if ( event
.KeyCode() == WXK_SPACE 
) 
 366     GetItem(GetSelection())->Toggle(); 
 371 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
) 
 373   // clicking on the item selects it, clicking on the checkmark toggles 
 374   if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) { 
 376       size_t nItem 
= (size_t)::SendMessage
 
 381                                 MAKELPARAM(event
.GetX(), event
.GetY()) 
 384         // FIXME this doesn't work when the listbox is scrolled! 
 385         size_t nItem 
= ((size_t)event
.GetY()) / m_nItemHeight
; 
 388     if ( nItem 
< (size_t)m_noItems 
) 
 389       GetItem(nItem
)->Toggle(); 
 390     //else: it's not an error, just click outside of client zone 
 393     // implement default behaviour: clicking on the item selects it