1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/msw/checklst.cpp 3// Purpose: implementation of wxCheckListBox class 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 16.11.97 7// RCS-ID: $Id: checklst.cpp 62511 2009-10-30 14:11:03Z JMS $ 8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 9// Licence: wxWindows licence 10/////////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20// For compilers that support precompilation, includes "wx.h". 21#include "wx/wxprec.h" 22 23#ifdef __BORLANDC__ 24 #pragma hdrstop 25#endif 26 27#if wxUSE_CHECKLISTBOX && wxUSE_OWNER_DRAWN 28 29#include "wx/checklst.h" 30 31#ifndef WX_PRECOMP 32 #include "wx/msw/wrapwin.h" 33 #include "wx/object.h" 34 #include "wx/colour.h" 35 #include "wx/font.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" 41 #include "wx/log.h" 42#endif 43 44#include "wx/ownerdrw.h" 45 46#include <windowsx.h> 47 48#include "wx/msw/private.h" 49 50// ---------------------------------------------------------------------------- 51// private functions 52// ---------------------------------------------------------------------------- 53 54// get item (converted to right type) 55#define GetItem(n) ((wxCheckListBoxItem *)(GetItem(n))) 56 57// ============================================================================ 58// implementation 59// ============================================================================ 60 61 62#if wxUSE_EXTENDED_RTTI 63WX_DEFINE_FLAGS( wxCheckListBoxStyle ) 64 65wxBEGIN_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) 74 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) 82 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) 92 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) 101 102wxEND_FLAGS( wxCheckListBoxStyle ) 103 104IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckListBox, wxListBox,"wx/checklst.h") 105 106wxBEGIN_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 109wxEND_PROPERTIES_TABLE() 110 111wxBEGIN_HANDLERS_TABLE(wxCheckListBox) 112wxEND_HANDLERS_TABLE() 113 114wxCONSTRUCTOR_4( wxCheckListBox , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size ) 115 116#else 117IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox, wxListBox) 118#endif 119 120// ---------------------------------------------------------------------------- 121// declaration and implementation of wxCheckListBoxItem class 122// ---------------------------------------------------------------------------- 123 124class wxCheckListBoxItem : public wxOwnerDrawn 125{ 126friend class WXDLLIMPEXP_FWD_CORE wxCheckListBox; 127public: 128 // ctor 129 wxCheckListBoxItem(wxCheckListBox *pParent, size_t nIndex); 130 131 // drawing functions 132 virtual bool OnDrawItem(wxDC& dc, const wxRect& rc, wxODAction act, wxODStatus stat); 133 134 // simple accessors and operations 135 bool IsChecked() const { return m_bChecked; } 136 137 void Check(bool bCheck); 138 void Toggle() { Check(!IsChecked()); } 139 140 void SendEvent(); 141 142private: 143 bool m_bChecked; 144 wxCheckListBox *m_pParent; 145 size_t m_nIndex; 146 147 DECLARE_NO_COPY_CLASS(wxCheckListBoxItem) 148}; 149 150wxCheckListBoxItem::wxCheckListBoxItem(wxCheckListBox *pParent, size_t nIndex) 151 : wxOwnerDrawn(wxEmptyString, true) // checkable 152{ 153 m_bChecked = false; 154 m_pParent = pParent; 155 m_nIndex = nIndex; 156 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 160 161 // fix appearance for check list boxes: they don't look quite the same as 162 // menu icons 163 SetOwnMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK) - 164 2*wxSystemSettings::GetMetric(wxSYS_EDGE_X) + 1); 165 166 SetBackgroundColour(pParent->GetBackgroundColour()); 167} 168 169bool wxCheckListBoxItem::OnDrawItem(wxDC& dc, const wxRect& rc, 170 wxODAction act, wxODStatus stat) 171{ 172 // first draw the label 173 if ( IsChecked() ) 174 stat = (wxOwnerDrawn::wxODStatus)(stat | wxOwnerDrawn::wxODChecked); 175 176 if ( !wxOwnerDrawn::OnDrawItem(dc, rc, act, stat) ) 177 return false; 178 179 180 // now draw the check mark part 181 size_t nCheckWidth = GetDefaultMarginWidth(), 182 nCheckHeight = m_pParent->GetItemHeight(); 183 184 int x = rc.GetX(), 185 y = rc.GetY(); 186 187 HDC hdc = (HDC)dc.GetHDC(); 188 189 // create pens, brushes &c 190 COLORREF colBg = wxColourToRGB(GetBackgroundColour()); 191 AutoHPEN hpenBack(colBg), 192 hpenGray(RGB(0xc0, 0xc0, 0xc0)); 193 194 SelectInHDC selPen(hdc, (HGDIOBJ)hpenBack); 195 AutoHBRUSH hbrBack(colBg); 196 SelectInHDC selBrush(hdc, hbrBack); 197 198 // erase the background: it could have been filled with the selected colour 199 Rectangle(hdc, x, y, x + nCheckWidth + 1, rc.GetBottom() + 1); 200 201 // shift check mark 1 pixel to the right, looks better like this 202 x++; 203 204 if ( IsChecked() ) 205 { 206 // first create a monochrome bitmap in a memory DC 207 MemoryHDC hdcMem(hdc); 208 MonoBitmap hbmpCheck(nCheckWidth, nCheckHeight); 209 SelectInHDC selBmp(hdcMem, hbmpCheck); 210 211 // then draw a check mark into it 212 RECT rect = { 0, 0, nCheckWidth, nCheckHeight }; 213 ::DrawFrameControl(hdcMem, &rect, 214#ifdef __WXWINCE__ 215 DFC_BUTTON, DFCS_BUTTONCHECK 216#else 217 DFC_MENU, DFCS_MENUCHECK 218#endif 219 ); 220 221 // finally copy it to screen DC 222 ::BitBlt(hdc, x, y, nCheckWidth, nCheckHeight, hdcMem, 0, 0, SRCCOPY); 223 } 224 225 // now we draw the smaller rectangle 226 y++; 227 nCheckWidth -= 2; 228 nCheckHeight -= 2; 229 230 // draw hollow gray rectangle 231 (void)::SelectObject(hdc, (HGDIOBJ)hpenGray); 232 233 SelectInHDC selBrush2(hdc, ::GetStockObject(NULL_BRUSH)); 234 Rectangle(hdc, x, y, x + nCheckWidth, y + nCheckHeight); 235 236 if (stat & wxODHasFocus) 237 { 238 RECT rect; 239 wxCopyRectToRECT(rc, rect); 240 DrawFocusRect(hdc, &rect); 241 } 242 243 return true; 244} 245 246// change the state of the item and redraw it 247void wxCheckListBoxItem::Check(bool check) 248{ 249 m_bChecked = check; 250 251 // index may be changed because new items were added/deleted 252 if ( m_pParent->GetItemIndex(this) != (int)m_nIndex ) 253 { 254 // update it 255 int index = m_pParent->GetItemIndex(this); 256 257 wxASSERT_MSG( index != wxNOT_FOUND, wxT("what does this item do here?") ); 258 259 m_nIndex = (size_t)index; 260 } 261 262 HWND hwndListbox = (HWND)m_pParent->GetHWND(); 263 264 RECT rcUpdate; 265 266 if ( ::SendMessage(hwndListbox, LB_GETITEMRECT, 267 m_nIndex, (LPARAM)&rcUpdate) == LB_ERR ) 268 { 269 wxLogDebug(wxT("LB_GETITEMRECT failed")); 270 } 271 272 ::InvalidateRect(hwndListbox, &rcUpdate, FALSE); 273} 274 275// send an "item checked" event 276void wxCheckListBoxItem::SendEvent() 277{ 278 wxCommandEvent event(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, m_pParent->GetId()); 279 event.SetInt(m_nIndex); 280 event.SetEventObject(m_pParent); 281 m_pParent->ProcessCommand(event); 282} 283 284// ---------------------------------------------------------------------------- 285// implementation of wxCheckListBox class 286// ---------------------------------------------------------------------------- 287 288// define event table 289// ------------------ 290BEGIN_EVENT_TABLE(wxCheckListBox, wxListBox) 291 EVT_KEY_DOWN(wxCheckListBox::OnKeyDown) 292 EVT_LEFT_DOWN(wxCheckListBox::OnLeftClick) 293END_EVENT_TABLE() 294 295// control creation 296// ---------------- 297 298// def ctor: use Create() to really create the control 299wxCheckListBox::wxCheckListBox() 300{ 301} 302 303// ctor which creates the associated control 304wxCheckListBox::wxCheckListBox(wxWindow *parent, wxWindowID id, 305 const wxPoint& pos, const wxSize& size, 306 int nStrings, const wxString choices[], 307 long style, const wxValidator& val, 308 const wxString& name) 309{ 310 Create(parent, id, pos, size, nStrings, choices, style, val, name); 311} 312 313wxCheckListBox::wxCheckListBox(wxWindow *parent, wxWindowID id, 314 const wxPoint& pos, const wxSize& size, 315 const wxArrayString& choices, 316 long style, const wxValidator& val, 317 const wxString& name) 318{ 319 Create(parent, id, pos, size, choices, style, val, name); 320} 321 322bool wxCheckListBox::Create(wxWindow *parent, wxWindowID id, 323 const wxPoint& pos, const wxSize& size, 324 int n, const wxString choices[], 325 long style, 326 const wxValidator& validator, const wxString& name) 327{ 328 return wxListBox::Create(parent, id, pos, size, n, choices, 329 style | wxLB_OWNERDRAW, validator, name); 330} 331 332bool wxCheckListBox::Create(wxWindow *parent, wxWindowID id, 333 const wxPoint& pos, const wxSize& size, 334 const wxArrayString& choices, 335 long style, 336 const wxValidator& validator, const wxString& name) 337{ 338 return wxListBox::Create(parent, id, pos, size, choices, 339 style | wxLB_OWNERDRAW, validator, name); 340} 341 342// misc overloaded methods 343// ----------------------- 344 345void wxCheckListBox::Delete(unsigned int n) 346{ 347 wxCHECK_RET( IsValid(n), 348 wxT("invalid index in wxListBox::Delete") ); 349 350 wxListBox::Delete(n); 351 352 // free memory 353 delete m_aItems[n]; 354 355 m_aItems.RemoveAt(n); 356} 357 358bool wxCheckListBox::SetFont( const wxFont &font ) 359{ 360 unsigned int i; 361 for ( i = 0; i < m_aItems.GetCount(); i++ ) 362 m_aItems[i]->SetFont(font); 363 364 wxListBox::SetFont(font); 365 366 return true; 367} 368 369// create/retrieve item 370// -------------------- 371 372// create a check list box item 373wxOwnerDrawn *wxCheckListBox::CreateLboxItem(size_t nIndex) 374{ 375 wxCheckListBoxItem *pItem = new wxCheckListBoxItem(this, nIndex); 376 return pItem; 377} 378 379// return item size 380// ---------------- 381bool wxCheckListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item) 382{ 383 if ( wxListBox::MSWOnMeasure(item) ) { 384 MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item; 385 386 // save item height 387 m_nItemHeight = pStruct->itemHeight; 388 389 // add place for the check mark 390 pStruct->itemWidth += wxOwnerDrawn::GetDefaultMarginWidth(); 391 392 return true; 393 } 394 395 return false; 396} 397 398// check items 399// ----------- 400 401bool wxCheckListBox::IsChecked(unsigned int uiIndex) const 402{ 403 wxCHECK_MSG( IsValid(uiIndex), false, _T("bad wxCheckListBox index") ); 404 405 return GetItem(uiIndex)->IsChecked(); 406} 407 408void wxCheckListBox::Check(unsigned int uiIndex, bool bCheck) 409{ 410 wxCHECK_RET( IsValid(uiIndex), _T("bad wxCheckListBox index") ); 411 412 GetItem(uiIndex)->Check(bCheck); 413} 414 415// process events 416// -------------- 417 418void wxCheckListBox::OnKeyDown(wxKeyEvent& event) 419{ 420 // what do we do? 421 enum 422 { 423 None, 424 Toggle, 425 Set, 426 Clear 427 } oper; 428 429 switch ( event.GetKeyCode() ) 430 { 431 case WXK_SPACE: 432 oper = Toggle; 433 break; 434 435 case WXK_NUMPAD_ADD: 436 case '+': 437 oper = Set; 438 break; 439 440 case WXK_NUMPAD_SUBTRACT: 441 case '-': 442 oper = Clear; 443 break; 444 445 default: 446 oper = None; 447 } 448 449 if ( oper != None ) 450 { 451 wxArrayInt selections; 452 int count = 0; 453 if ( HasMultipleSelection() ) 454 { 455 count = GetSelections(selections); 456 } 457 else 458 { 459 int sel = GetSelection(); 460 if (sel != -1) 461 { 462 count = 1; 463 selections.Add(sel); 464 } 465 } 466 467 for ( int i = 0; i < count; i++ ) 468 { 469 wxCheckListBoxItem *item = GetItem(selections[i]); 470 if ( !item ) 471 { 472 wxFAIL_MSG( _T("no wxCheckListBoxItem?") ); 473 continue; 474 } 475 476 switch ( oper ) 477 { 478 case Toggle: 479 item->Toggle(); 480 break; 481 482 case Set: 483 case Clear: 484 item->Check( oper == Set ); 485 break; 486 487 default: 488 wxFAIL_MSG( _T("what should this key do?") ); 489 } 490 491 // we should send an event as this has been done by the user and 492 // not by the program 493 item->SendEvent(); 494 } 495 } 496 else // nothing to do 497 { 498 event.Skip(); 499 } 500} 501 502void wxCheckListBox::OnLeftClick(wxMouseEvent& event) 503{ 504 // clicking on the item selects it, clicking on the checkmark toggles 505 if ( event.GetX() <= wxOwnerDrawn::GetDefaultMarginWidth() ) 506 { 507 int nItem = HitTest(event.GetX(), event.GetY()); 508 509 if ( nItem != wxNOT_FOUND ) 510 { 511 wxCheckListBoxItem *item = GetItem(nItem); 512 item->Toggle(); 513 item->SendEvent(); 514 } 515 //else: it's not an error, just click outside of client zone 516 } 517 else 518 { 519 // implement default behaviour: clicking on the item selects it 520 event.Skip(); 521 } 522} 523 524int wxCheckListBox::DoHitTestItem(wxCoord x, wxCoord y) const 525{ 526 int nItem = (int)::SendMessage 527 ( 528 (HWND)GetHWND(), 529 LB_ITEMFROMPOINT, 530 0, 531 MAKELPARAM(x, y) 532 ); 533 534 return nItem >= (int)m_noItems ? wxNOT_FOUND : nItem; 535} 536 537 538wxSize wxCheckListBox::DoGetBestSize() const 539{ 540 wxSize best = wxListBox::DoGetBestSize(); 541 best.x += wxOwnerDrawn::GetDefaultMarginWidth(); // add room for the checkbox 542 CacheBestSize(best); 543 return best; 544} 545 546#endif 547