1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/checkbox.cpp 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/checkbox.h" 
  33     #include "wx/dcscreen.h" 
  34     #include "wx/settings.h" 
  37 #include "wx/msw/uxtheme.h" 
  38 #include "wx/msw/private.h" 
  40 // ---------------------------------------------------------------------------- 
  42 // ---------------------------------------------------------------------------- 
  45     #define BST_UNCHECKED 0x0000 
  49     #define BST_CHECKED 0x0001 
  52 #ifndef BST_INDETERMINATE 
  53     #define BST_INDETERMINATE 0x0002 
  57     #define DFCS_HOT 0x1000 
  61     #define DT_HIDEPREFIX 0x00100000 
  68 // these values are defined in tmschema.h (except the first one) 
  75     CBS_UNCHECKEDDISABLED
, 
  90     CBS_PRESSED_OFFSET 
= 2, 
  91     CBS_DISABLED_OFFSET 
= 3 
  94 // ============================================================================ 
  96 // ============================================================================ 
  98 #if wxUSE_EXTENDED_RTTI 
  99 WX_DEFINE_FLAGS( wxCheckBoxStyle 
) 
 101 wxBEGIN_FLAGS( wxCheckBoxStyle 
) 
 102     // new style border flags, we put them first to 
 103     // use them for streaming out 
 104     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
 105     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
 106     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
 107     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
 108     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
 109     wxFLAGS_MEMBER(wxBORDER_NONE
) 
 111     // old style border flags 
 112     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
 113     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
 114     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
 115     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
 116     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
 117     wxFLAGS_MEMBER(wxNO_BORDER
) 
 119     // standard window styles 
 120     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 121     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 122     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 123     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 124     wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE
) 
 125     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 126     wxFLAGS_MEMBER(wxVSCROLL
) 
 127     wxFLAGS_MEMBER(wxHSCROLL
) 
 129 wxEND_FLAGS( wxCheckBoxStyle 
) 
 131 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckBox
, wxControl
,"wx/checkbox.h") 
 133 wxBEGIN_PROPERTIES_TABLE(wxCheckBox
) 
 134     wxEVENT_PROPERTY( Click 
, wxEVT_COMMAND_CHECKBOX_CLICKED 
, wxCommandEvent 
) 
 136     wxPROPERTY( Font 
, wxFont 
, SetFont 
, GetFont 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 137     wxPROPERTY( Label
,wxString
, SetLabel
, GetLabel
, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 138     wxPROPERTY( Value 
,bool, SetValue
, GetValue
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 139     wxPROPERTY_FLAGS( WindowStyle 
, wxCheckBoxStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 140 wxEND_PROPERTIES_TABLE() 
 142 wxBEGIN_HANDLERS_TABLE(wxCheckBox
) 
 143 wxEND_HANDLERS_TABLE() 
 145 wxCONSTRUCTOR_6( wxCheckBox 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxString 
, Label 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle 
) 
 147 IMPLEMENT_DYNAMIC_CLASS(wxCheckBox
, wxControl
) 
 151 // ---------------------------------------------------------------------------- 
 152 // wxCheckBox creation 
 153 // ---------------------------------------------------------------------------- 
 155 void wxCheckBox::Init() 
 157     m_state 
= wxCHK_UNCHECKED
; 
 162 bool wxCheckBox::Create(wxWindow 
*parent
, 
 164                         const wxString
& label
, 
 166                         const wxSize
& size
, long style
, 
 167                         const wxValidator
& validator
, 
 168                         const wxString
& name
) 
 172     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 175     long msStyle 
= WS_TABSTOP
; 
 177     if ( style 
& wxCHK_3STATE 
) 
 179         msStyle 
|= BS_3STATE
; 
 183         wxASSERT_MSG( !Is3rdStateAllowedForUser(), 
 184             wxT("Using wxCH_ALLOW_3RD_STATE_FOR_USER") 
 185             wxT(" style flag for a 2-state checkbox is useless") ); 
 186         msStyle 
|= BS_CHECKBOX
; 
 189     if ( style 
& wxALIGN_RIGHT 
) 
 191         msStyle 
|= BS_LEFTTEXT 
| BS_RIGHT
; 
 194     return MSWCreateControl(wxT("BUTTON"), msStyle
, pos
, size
, label
, 0); 
 197 // ---------------------------------------------------------------------------- 
 198 // wxCheckBox geometry 
 199 // ---------------------------------------------------------------------------- 
 201 wxSize 
wxCheckBox::DoGetBestSize() const 
 203     static int s_checkSize 
= 0; 
 208         dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 210         s_checkSize 
= dc
.GetCharHeight(); 
 213     wxString str 
= wxGetWindowText(GetHWND()); 
 215     int wCheckbox
, hCheckbox
; 
 218         GetTextExtent(GetLabelText(str
), &wCheckbox
, &hCheckbox
); 
 219         wCheckbox 
+= s_checkSize 
+ GetCharWidth(); 
 221         if ( hCheckbox 
< s_checkSize 
) 
 222             hCheckbox 
= s_checkSize
; 
 226         wCheckbox 
= s_checkSize
; 
 227         hCheckbox 
= s_checkSize
; 
 233     wxSize 
best(wCheckbox
, hCheckbox
); 
 238 // ---------------------------------------------------------------------------- 
 239 // wxCheckBox operations 
 240 // ---------------------------------------------------------------------------- 
 242 void wxCheckBox::SetValue(bool val
) 
 244     Set3StateValue(val 
? wxCHK_CHECKED 
: wxCHK_UNCHECKED
); 
 247 bool wxCheckBox::GetValue() const 
 249     return Get3StateValue() != wxCHK_UNCHECKED
; 
 252 void wxCheckBox::Command(wxCommandEvent
& event
) 
 254     int state 
= event
.GetInt(); 
 255     wxCHECK_RET( (state 
== wxCHK_UNCHECKED
) || (state 
== wxCHK_CHECKED
) 
 256         || (state 
== wxCHK_UNDETERMINED
), 
 257         wxT("event.GetInt() returned an invalid checkbox state") ); 
 259     Set3StateValue((wxCheckBoxState
) state
); 
 260     ProcessCommand(event
); 
 263 wxCOMPILE_TIME_ASSERT(wxCHK_UNCHECKED 
== BST_UNCHECKED
 
 264     && wxCHK_CHECKED 
== BST_CHECKED
 
 265     && wxCHK_UNDETERMINED 
== BST_INDETERMINATE
, EnumValuesIncorrect
); 
 267 void wxCheckBox::DoSet3StateValue(wxCheckBoxState state
) 
 270     if ( !IsOwnerDrawn() ) 
 271         ::SendMessage(GetHwnd(), BM_SETCHECK
, (WPARAM
) state
, 0); 
 272     else // owner drawn buttons don't react to this message 
 276 wxCheckBoxState 
wxCheckBox::DoGet3StateValue() const 
 281 bool wxCheckBox::MSWCommand(WXUINT cmd
, WXWORD 
WXUNUSED(id
)) 
 283     if ( cmd 
!= BN_CLICKED 
&& cmd 
!= BN_DBLCLK 
) 
 286     // first update the value so that user event handler gets the new checkbox 
 289     // ownerdrawn buttons don't manage their state themselves unlike usual 
 290     // auto checkboxes so do it ourselves in any case 
 291     wxCheckBoxState state
; 
 292     if ( Is3rdStateAllowedForUser() ) 
 294         state 
= (wxCheckBoxState
)((m_state 
+ 1) % 3); 
 296     else // 2 state checkbox (at least from users point of view) 
 298         // note that wxCHK_UNDETERMINED also becomes unchecked when clicked 
 299         state 
= m_state 
== wxCHK_UNCHECKED 
? wxCHK_CHECKED 
: wxCHK_UNCHECKED
; 
 302     DoSet3StateValue(state
); 
 305     // generate the event 
 306     wxCommandEvent 
event(wxEVT_COMMAND_CHECKBOX_CLICKED
, m_windowId
); 
 309     event
.SetEventObject(this); 
 310     ProcessCommand(event
); 
 315 // ---------------------------------------------------------------------------- 
 316 // owner drawn checkboxes stuff 
 317 // ---------------------------------------------------------------------------- 
 319 bool wxCheckBox::SetForegroundColour(const wxColour
& colour
) 
 321     if ( !wxCheckBoxBase::SetForegroundColour(colour
) ) 
 324     // the only way to change the checkbox foreground colour under Windows XP 
 325     // is to owner draw it 
 326     if ( wxUxThemeEngine::GetIfActive() ) 
 327         MakeOwnerDrawn(colour
.Ok()); 
 332 bool wxCheckBox::IsOwnerDrawn() const 
 335         (::GetWindowLong(GetHwnd(), GWL_STYLE
) & BS_OWNERDRAW
) == BS_OWNERDRAW
; 
 338 void wxCheckBox::MakeOwnerDrawn(bool ownerDrawn
) 
 340     long style 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
 342     // note that BS_CHECKBOX & BS_OWNERDRAW != 0 so we can't operate on 
 343     // them as on independent style bits 
 346         style 
&= ~(BS_CHECKBOX 
| BS_3STATE
); 
 347         style 
|= BS_OWNERDRAW
; 
 349         Connect(wxEVT_ENTER_WINDOW
, 
 350                 wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 351         Connect(wxEVT_LEAVE_WINDOW
, 
 352                 wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 353         Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 354         Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 355         Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 356         Connect(wxEVT_KILL_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 358     else // reset to default colour 
 360         style 
&= ~BS_OWNERDRAW
; 
 361         style 
|= HasFlag(wxCHK_3STATE
) ? BS_3STATE 
: BS_CHECKBOX
; 
 363         Disconnect(wxEVT_ENTER_WINDOW
, 
 364                    wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 365         Disconnect(wxEVT_LEAVE_WINDOW
, 
 366                    wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 367         Disconnect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 368         Disconnect(wxEVT_LEFT_UP
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 369         Disconnect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 370         Disconnect(wxEVT_KILL_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 373     ::SetWindowLong(GetHwnd(), GWL_STYLE
, style
); 
 377         // ensure that controls state is consistent with internal state 
 378         DoSet3StateValue(m_state
); 
 382 void wxCheckBox::OnMouseEnterOrLeave(wxMouseEvent
& event
) 
 384     m_isHot 
= event
.GetEventType() == wxEVT_ENTER_WINDOW
; 
 393 void wxCheckBox::OnMouseLeft(wxMouseEvent
& event
) 
 395     // TODO: we should capture the mouse here to be notified about left up 
 396     //       event but this interferes with BN_CLICKED generation so if we 
 397     //       want to do this we'd need to generate them ourselves 
 398     m_isPressed 
= event
.GetEventType() == wxEVT_LEFT_DOWN
; 
 404 void wxCheckBox::OnFocus(wxFocusEvent
& event
) 
 411 bool wxCheckBox::MSWOnDraw(WXDRAWITEMSTRUCT 
*item
) 
 413     DRAWITEMSTRUCT 
*dis 
= (DRAWITEMSTRUCT 
*)item
; 
 415     if ( !IsOwnerDrawn() || dis
->CtlType 
!= ODT_BUTTON 
) 
 416         return wxCheckBoxBase::MSWOnDraw(item
); 
 418     // calculate the rectangles for the check mark itself and the label 
 420     RECT
& rect 
= dis
->rcItem
; 
 424     rectLabel
.top 
= rect
.top
; 
 426     rectLabel
.bottom 
= rect
.bottom
; 
 427     const int checkSize 
= GetBestSize().y
; 
 428     const int MARGIN 
= 3; 
 430     const bool isRightAligned 
= HasFlag(wxALIGN_RIGHT
); 
 431     if ( isRightAligned 
) 
 433         rectCheck
.right 
= rect
.right
; 
 434         rectCheck
.left 
= rectCheck
.right 
- checkSize
; 
 436         rectLabel
.right 
= rectCheck
.left 
- MARGIN
; 
 437         rectLabel
.left 
= rect
.left
; 
 439     else // normal, left-aligned checkbox 
 441         rectCheck
.left 
= rect
.left
; 
 442         rectCheck
.right 
= rectCheck
.left 
+ checkSize
; 
 444         rectLabel
.left 
= rectCheck
.right 
+ MARGIN
; 
 445         rectLabel
.right 
= rect
.right
; 
 448     // show we draw a focus rect? 
 449     const bool isFocused 
= m_isPressed 
|| FindFocus() == this; 
 452     // draw the checkbox itself: note that this should really, really be in 
 453     // wxRendererNative but unfortunately we can't add a new virtual function 
 454     // to it without breaking backwards compatibility 
 456     // classic Win32 version -- this can be useful when we move this into 
 458 #if defined(__WXWINCE__) || !wxUSE_UXTHEME 
 459     UINT state 
= DFCS_BUTTONCHECK
; 
 461         state 
|= DFCS_INACTIVE
; 
 462     switch ( Get3StateValue() ) 
 465             state 
|= DFCS_CHECKED
; 
 468         case wxCHK_UNDETERMINED
: 
 469             state 
|= DFCS_PUSHED
; 
 473             wxFAIL_MSG( _T("unexpected Get3StateValue() return value") ); 
 476         case wxCHK_UNCHECKED
: 
 477             // no extra styles needed 
 481     if ( wxFindWindowAtPoint(wxGetMousePosition()) == this ) 
 484     if ( !::DrawFrameControl(hdc
, &rectCheck
, DFC_BUTTON
, state
) ) 
 486         wxLogLastError(_T("DrawFrameControl(DFC_BUTTON)")); 
 489     wxUxThemeEngine 
*themeEngine 
= wxUxThemeEngine::GetIfActive(); 
 493     wxUxThemeHandle 
theme(this, L
"BUTTON"); 
 498     switch ( Get3StateValue() ) 
 501             state 
= CBS_CHECKEDNORMAL
; 
 504         case wxCHK_UNDETERMINED
: 
 505             state 
= CBS_MIXEDNORMAL
; 
 509             wxFAIL_MSG( _T("unexpected Get3StateValue() return value") ); 
 512         case wxCHK_UNCHECKED
: 
 513             state 
= CBS_UNCHECKEDNORMAL
; 
 518         state 
+= CBS_DISABLED_OFFSET
; 
 519     else if ( m_isPressed 
) 
 520         state 
+= CBS_PRESSED_OFFSET
; 
 522         state 
+= CBS_HOT_OFFSET
; 
 524     HRESULT hr 
= themeEngine
->DrawThemeBackground
 
 535         wxLogApiError(_T("DrawThemeBackground(BP_CHECKBOX)"), hr
); 
 540     const wxString
& label 
= GetLabel(); 
 542     // first we need to measure it 
 543     UINT fmt 
= DT_NOCLIP
; 
 545     // drawing underlying doesn't look well with focus rect (and the native 
 546     // control doesn't do it) 
 548         fmt 
|= DT_HIDEPREFIX
; 
 549     if ( isRightAligned 
) 
 551     // TODO: also use DT_HIDEPREFIX if the system is configured so 
 553     // we need to get the label real size first if we have to draw a focus rect 
 557         if ( !::DrawText(hdc
, label
, label
.length(), &rectLabel
, 
 560             wxLogLastError(_T("DrawText(DT_CALCRECT)")); 
 566         ::SetTextColor(hdc
, ::GetSysColor(COLOR_GRAYTEXT
)); 
 569     if ( !::DrawText(hdc
, label
, label
.length(), &rectLabel
, fmt
) ) 
 571         wxLogLastError(_T("DrawText()")); 
 574     // finally draw the focus 
 579         if ( !::DrawFocusRect(hdc
, &rectLabel
) ) 
 581             wxLogLastError(_T("DrawFocusRect()")); 
 588 #endif // wxUSE_CHECKBOX