1 /////////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     implementation of wxCheckListBox class 
   4 // Author:      David Webster 
   8 // Copyright:   (c) David Webster 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  13 // headers & declarations 
  14 // ============================================================================ 
  16 // For compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  21 #include "wx/object.h" 
  22 #include "wx/colour.h" 
  24 #include "wx/bitmap.h" 
  25 #include "wx/window.h" 
  26 #include "wx/listbox.h" 
  27 #include "wx/ownerdrw.h" 
  28 #include "wx/settings.h" 
  29 #include "wx/dcmemory.h" 
  30 #include "wx/os2/checklst.h" 
  36 // ---------------------------------------------------------------------------- 
  38 // ---------------------------------------------------------------------------- 
  40 // get item (converted to right type) 
  41 #define GetItem(n)    ((wxCheckListBoxItem *)(GetItem(n))) 
  43 // ============================================================================ 
  45 // ============================================================================ 
  47   IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox
, wxListBox
) 
  49 // ---------------------------------------------------------------------------- 
  50 // declaration and implementation of wxCheckListBoxItem class 
  51 // ---------------------------------------------------------------------------- 
  53 class wxCheckListBoxItem 
: public wxOwnerDrawn
 
  55 friend class wxCheckListBox
; 
  58   wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
); 
  61   virtual bool OnDrawItem(wxDC
& dc
, const wxRect
& rc
, wxODAction act
, wxODStatus stat
); 
  64   bool IsChecked() const  { return m_bChecked
;        } 
  65   void Check(bool bCheck
); 
  66   void Toggle() { Check(!IsChecked()); } 
  70   wxCheckListBox 
*m_pParent
; 
  74 wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox 
*pParent
, size_t nIndex
) 
  75                   : wxOwnerDrawn("", TRUE
)   // checkable 
  81   // we don't initialize m_nCheckHeight/Width vars because it's 
  82   // done in OnMeasure while they are used only in OnDraw and we 
  83   // know that there will always be OnMeasure before OnDraw 
  86   SetMarginWidth(GetDefaultMarginWidth()); 
  90  * JACS - I've got the owner-draw stuff partially working with WIN16, 
  91  * with a really horrible-looking cross for wxCheckListBox instead of a 
  92  * check - could use a bitmap check-mark instead, defined in wx.rc. 
  93  * Also there's a refresh problem whereby it doesn't always draw the 
  94  * check until you click to the right of it, which is OK for WIN32. 
  97 bool wxCheckListBoxItem::OnDrawItem(wxDC
& dc
, const wxRect
& rc
, 
  98                                     wxODAction act
, wxODStatus stat
) 
 101     stat 
= (wxOwnerDrawn::wxODStatus
)(stat 
| wxOwnerDrawn::wxODChecked
); 
 106   if ( wxOwnerDrawn::OnDrawItem(dc, rc, act, stat) ) { 
 107     // ## using native API for performance and precision 
 108     size_t nCheckWidth  = GetDefaultMarginWidth(), 
 109          nCheckHeight = m_pParent->GetItemHeight(); 
 114     HDC hdc = (HDC)dc.GetHDC(); 
 117     HPEN hpenBack = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_WINDOW)), 
 118          hpenGray = CreatePen(PS_SOLID, 0, RGB(128, 128, 128)), 
 119          hpenPrev = (HPEN)SelectObject(hdc, hpenBack); 
 121     // we erase the 1-pixel border 
 122     Rectangle(hdc, x, y, x + nCheckWidth, y + nCheckHeight); 
 124     // shift check mark 1 pixel to the right (it looks better like this) 
 128       // first create a monochrome bitmap in a memory DC 
 129       HDC hdcMem = CreateCompatibleDC(hdc); 
 130       HBITMAP hbmpCheck = CreateBitmap(nCheckWidth, nCheckHeight, 1, 1, 0); 
 131       HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmpCheck); 
 133       // then draw a check mark into it 
 137       rect.right  = nCheckWidth ; 
 138       rect.bottom = nCheckHeight ; 
 142       DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK); 
 145       // In WIN16, draw a cross 
 146       HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); 
 147       HPEN whiteBrush = (HPEN)GetStockObject(WHITE_BRUSH); 
 148       HPEN hPenOld = (HPEN)::SelectObject(hdcMem, blackPen); 
 149       HPEN hBrushOld = (HPEN)::SelectObject(hdcMem, whiteBrush); 
 150       ::SetROP2(hdcMem, R2_COPYPEN); 
 151       Rectangle(hdcMem, 0, 0, nCheckWidth, nCheckHeight); 
 152       MoveTo(hdcMem, 0, 0); 
 153       LineTo(hdcMem, nCheckWidth, nCheckHeight); 
 154       MoveTo(hdcMem, nCheckWidth, 0); 
 155       LineTo(hdcMem, 0, nCheckHeight); 
 156       ::SelectObject(hdcMem, hPenOld); 
 157       ::SelectObject(hdcMem, hBrushOld); 
 158       ::DeleteObject(blackPen); 
 161       // finally copy it to screen DC and clean up 
 162       BitBlt(hdc, x, y, nCheckWidth - 1, nCheckHeight, 
 163              hdcMem, 0, 0, SRCCOPY); 
 165       SelectObject(hdcMem, hbmpOld); 
 166       DeleteObject(hbmpCheck); 
 170     // now we draw the smaller rectangle 
 175     // draw hollow gray rectangle 
 176     (void)SelectObject(hdc, hpenGray); 
 177     HBRUSH hbrPrev  = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
 178     Rectangle(hdc, x, y, x + nCheckWidth, y + nCheckHeight); 
 181     (void)SelectObject(hdc, hpenPrev); 
 182     (void)SelectObject(hdc, hbrPrev); 
 184     DeleteObject(hpenBack); 
 185     DeleteObject(hpenGray); 
 188 //    dc.DrawRectangle(x, y, nCheckWidth, nCheckHeight); 
 190 //    if ( IsChecked() ) { 
 191 //      dc.DrawLine(x, y, x + nCheckWidth, y + nCheckHeight); 
 192 //      dc.DrawLine(x, y + nCheckHeight, x + nCheckWidth, y); 
 202 // change the state of the item and redraw it 
 203 void wxCheckListBoxItem::Check(bool check
) 
 207     // index may be chanegd because new items were added/deleted 
 208     if ( m_pParent
->GetItemIndex(this) != (int)m_nIndex 
) 
 211         int index 
= m_pParent
->GetItemIndex(this); 
 213         wxASSERT_MSG( index 
!= wxNOT_FOUND
, wxT("what does this item do here?") ); 
 215         m_nIndex 
= (size_t)index
; 
 218     HWND hwndListbox 
= (HWND
)m_pParent
->GetHWND(); 
 223     if ( ::SendMessage(hwndListbox, LB_GETITEMRECT, 
 224                        m_nIndex, (LPARAM)&rcUpdate) == LB_ERR ) 
 226         wxLogDebug(wxT("LB_GETITEMRECT failed")); 
 229     InvalidateRect(hwndListbox, &rcUpdate, FALSE); 
 231     wxCommandEvent 
event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, m_pParent
->GetId()); 
 232     event
.SetInt(m_nIndex
); 
 233     event
.SetEventObject(m_pParent
); 
 234     m_pParent
->ProcessCommand(event
); 
 237 // ---------------------------------------------------------------------------- 
 238 // implementation of wxCheckListBox class 
 239 // ---------------------------------------------------------------------------- 
 241 // define event table 
 242 // ------------------ 
 243 BEGIN_EVENT_TABLE(wxCheckListBox
, wxListBox
) 
 244   EVT_CHAR(wxCheckListBox::OnChar
) 
 245   EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick
) 
 251 // def ctor: use Create() to really create the control 
 252 wxCheckListBox::wxCheckListBox() : wxListBox() 
 256 // ctor which creates the associated control 
 257 wxCheckListBox::wxCheckListBox(wxWindow 
*parent
, wxWindowID id
, 
 258                                const wxPoint
& pos
, const wxSize
& size
, 
 259                                int nStrings
, const wxString choices
[], 
 261                                long style
, const wxValidator
& val
, 
 263                                const wxString
& name
) 
 266     Create(parent
, id
, pos
, size
, nStrings
, choices
, 
 267            style 
| wxLB_OWNERDRAW
, val
, name
); 
 270 void wxCheckListBox::Delete(int N
) 
 272     wxCHECK_RET( N 
>= 0 && N 
< m_noItems
, 
 273                  wxT("invalid index in wxListBox::Delete") ); 
 275     wxListBox::Delete(N
); 
 280     m_aItems
.RemoveAt(N
); 
 283 void wxCheckListBox::InsertItems(int nItems
, const wxString items
[], int pos
) 
 285     wxCHECK_RET( pos 
>= 0 && pos 
<= m_noItems
, 
 286                  wxT("invalid index in wxCheckListBox::InsertItems") ); 
 288     wxListBox::InsertItems(nItems
, items
, pos
); 
 291     for ( i 
= 0; i 
< nItems
; i
++ ) { 
 292         wxOwnerDrawn 
*pNewItem 
= CreateItem((size_t)(pos 
+ i
)); 
 293         pNewItem
->SetName(items
[i
]); 
 294         m_aItems
.Insert(pNewItem
, (size_t)(pos 
+ i
)); 
 295 //        ListBox_SetItemData((HWND)GetHWND(), i + pos, pNewItem); 
 300 bool wxCheckListBox::SetFont( const wxFont 
&font 
) 
 303     for (i
=0; i 
< m_aItems
.GetCount(); i
++) 
 304         m_aItems
[i
]->SetFont(font
); 
 305     wxListBox::SetFont(font
); 
 309 // create/retrieve item 
 310 // -------------------- 
 312 // create a check list box item 
 313 wxOwnerDrawn 
*wxCheckListBox::CreateItem(size_t nIndex
) 
 315   wxCheckListBoxItem 
*pItem 
= new wxCheckListBoxItem(this, nIndex
); 
 323 bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item) 
 325   if ( wxListBox::MSWOnMeasure(item) ) { 
 326     MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item; 
 329     m_nItemHeight = pStruct->itemHeight; 
 331     // add place for the check mark 
 332     pStruct->itemWidth += wxOwnerDrawn::GetDefaultMarginWidth(); 
 343 bool wxCheckListBox::IsChecked(size_t uiIndex
) const 
 345   return GetItem(uiIndex
)->IsChecked(); 
 348 void wxCheckListBox::Check(size_t uiIndex
, bool bCheck
) 
 350   GetItem(uiIndex
)->Check(bCheck
); 
 356 void wxCheckListBox::OnChar(wxKeyEvent
& event
) 
 358   if ( event
.KeyCode() == WXK_SPACE 
) 
 359     GetItem(GetSelection())->Toggle(); 
 364 void wxCheckListBox::OnLeftClick(wxMouseEvent
& event
) 
 366   // clicking on the item selects it, clicking on the checkmark toggles 
 367   if ( event
.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) { 
 370       size_t nItem = (size_t)::SendMessage 
 375                                 MAKELPARAM(event.GetX(), event.GetY()) 
 380     if ( nItem 
< (size_t)m_noItems 
) 
 381       GetItem(nItem
)->Toggle(); 
 382     //else: it's not an error, just click outside of client zone 
 385     // implement default behaviour: clicking on the item selects it