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/dcclient.h" 
  34     #include "wx/dcscreen.h" 
  35     #include "wx/settings.h" 
  38 #include "wx/msw/dc.h"          // for wxDCTemp 
  39 #include "wx/renderer.h" 
  40 #include "wx/msw/uxtheme.h" 
  41 #include "wx/msw/private/button.h" 
  42 #include "wx/msw/missing.h" 
  44 // ---------------------------------------------------------------------------- 
  46 // ---------------------------------------------------------------------------- 
  52 // these values are defined in tmschema.h (except the first one) 
  59     CBS_UNCHECKEDDISABLED
, 
  74     CBS_PRESSED_OFFSET 
= 2, 
  75     CBS_DISABLED_OFFSET 
= 3 
  78 // ============================================================================ 
  80 // ============================================================================ 
  82 // ---------------------------------------------------------------------------- 
  83 // wxCheckBox creation 
  84 // ---------------------------------------------------------------------------- 
  86 void wxCheckBox::Init() 
  88     m_state 
= wxCHK_UNCHECKED
; 
  93 bool wxCheckBox::Create(wxWindow 
*parent
, 
  95                         const wxString
& label
, 
  97                         const wxSize
& size
, long style
, 
  98                         const wxValidator
& validator
, 
 103     WXValidateStyle(&style
); 
 104     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 108     WXDWORD msStyle 
= MSWGetStyle(style
, &exstyle
); 
 110     msStyle 
|= wxMSWButton::GetMultilineStyle(label
); 
 112     return MSWCreateControl(wxT("BUTTON"), msStyle
, pos
, size
, label
, exstyle
); 
 115 WXDWORD 
wxCheckBox::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 117     // buttons never have an external border, they draw their own one 
 118     WXDWORD msStyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 120     if ( style 
& wxCHK_3STATE 
) 
 121         msStyle 
|= BS_3STATE
; 
 123         msStyle 
|= BS_CHECKBOX
; 
 125     if ( style 
& wxALIGN_RIGHT 
) 
 127         msStyle 
|= BS_LEFTTEXT 
| BS_RIGHT
; 
 133 // ---------------------------------------------------------------------------- 
 134 // wxCheckBox geometry 
 135 // ---------------------------------------------------------------------------- 
 137 wxSize 
wxCheckBox::DoGetBestClientSize() const 
 139     static int s_checkSize 
= 0; 
 144         dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 146         s_checkSize 
= dc
.GetCharHeight(); 
 149     wxString str 
= wxGetWindowText(GetHWND()); 
 151     int wCheckbox
, hCheckbox
; 
 154         wxClientDC 
dc(const_cast<wxCheckBox 
*>(this)); 
 155         dc
.SetFont(GetFont()); 
 156         dc
.GetMultiLineTextExtent(GetLabelText(str
), &wCheckbox
, &hCheckbox
); 
 157         wCheckbox 
+= s_checkSize 
+ GetCharWidth(); 
 159         if ( ::GetWindowLong(GetHwnd(), GWL_STYLE
) & BS_MULTILINE 
) 
 161             // We need to make the checkbox even wider in this case because 
 162             // otherwise it wraps lines automatically and not only on "\n"s as 
 163             // we need and this makes the size computed here wrong resulting in 
 164             // checkbox contents being truncated when it's actually displayed. 
 165             // Without this hack simple checkbox with "Some thing\n and more" 
 166             // label appears on 3 lines, not 2, under Windows 2003 using 
 167             // classic look and feel (although it works fine under Windows 7, 
 168             // with or without themes). 
 169             wCheckbox 
+= s_checkSize
; 
 172         if ( hCheckbox 
< s_checkSize 
) 
 173             hCheckbox 
= s_checkSize
; 
 177         wCheckbox 
= s_checkSize
; 
 178         hCheckbox 
= s_checkSize
; 
 184     wxSize 
best(wCheckbox
, hCheckbox
); 
 189 // ---------------------------------------------------------------------------- 
 190 // wxCheckBox operations 
 191 // ---------------------------------------------------------------------------- 
 193 void wxCheckBox::SetLabel(const wxString
& label
) 
 195     wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
); 
 197     wxCheckBoxBase::SetLabel(label
); 
 200 void wxCheckBox::SetValue(bool val
) 
 202     Set3StateValue(val 
? wxCHK_CHECKED 
: wxCHK_UNCHECKED
); 
 205 bool wxCheckBox::GetValue() const 
 207     return Get3StateValue() != wxCHK_UNCHECKED
; 
 210 void wxCheckBox::Command(wxCommandEvent
& event
) 
 212     int state 
= event
.GetInt(); 
 213     wxCHECK_RET( (state 
== wxCHK_UNCHECKED
) || (state 
== wxCHK_CHECKED
) 
 214         || (state 
== wxCHK_UNDETERMINED
), 
 215         wxT("event.GetInt() returned an invalid checkbox state") ); 
 217     Set3StateValue((wxCheckBoxState
) state
); 
 218     ProcessCommand(event
); 
 221 wxCOMPILE_TIME_ASSERT(wxCHK_UNCHECKED 
== BST_UNCHECKED
 
 222     && wxCHK_CHECKED 
== BST_CHECKED
 
 223     && wxCHK_UNDETERMINED 
== BST_INDETERMINATE
, EnumValuesIncorrect
); 
 225 void wxCheckBox::DoSet3StateValue(wxCheckBoxState state
) 
 228     if ( !IsOwnerDrawn() ) 
 229         ::SendMessage(GetHwnd(), BM_SETCHECK
, (WPARAM
) state
, 0); 
 230     else // owner drawn buttons don't react to this message 
 234 wxCheckBoxState 
wxCheckBox::DoGet3StateValue() const 
 239 bool wxCheckBox::MSWCommand(WXUINT cmd
, WXWORD 
WXUNUSED(id
)) 
 241     if ( cmd 
!= BN_CLICKED 
&& cmd 
!= BN_DBLCLK 
) 
 244     // first update the value so that user event handler gets the new checkbox 
 247     // ownerdrawn buttons don't manage their state themselves unlike usual 
 248     // auto checkboxes so do it ourselves in any case 
 249     wxCheckBoxState state
; 
 250     if ( Is3rdStateAllowedForUser() ) 
 252         state 
= (wxCheckBoxState
)((m_state 
+ 1) % 3); 
 254     else // 2 state checkbox (at least from users point of view) 
 256         // note that wxCHK_UNDETERMINED also becomes unchecked when clicked 
 257         state 
= m_state 
== wxCHK_UNCHECKED 
? wxCHK_CHECKED 
: wxCHK_UNCHECKED
; 
 260     DoSet3StateValue(state
); 
 263     // generate the event 
 264     wxCommandEvent 
event(wxEVT_COMMAND_CHECKBOX_CLICKED
, m_windowId
); 
 267     event
.SetEventObject(this); 
 268     ProcessCommand(event
); 
 273 // ---------------------------------------------------------------------------- 
 274 // owner drawn checkboxes stuff 
 275 // ---------------------------------------------------------------------------- 
 277 bool wxCheckBox::SetForegroundColour(const wxColour
& colour
) 
 279     if ( !wxCheckBoxBase::SetForegroundColour(colour
) ) 
 282     // the only way to change the checkbox foreground colour under Windows XP 
 283     // is to owner draw it 
 284     if ( wxUxThemeEngine::GetIfActive() ) 
 285         MSWMakeOwnerDrawn(colour
.IsOk()); 
 290 bool wxCheckBox::IsOwnerDrawn() const 
 293         (::GetWindowLong(GetHwnd(), GWL_STYLE
) & BS_OWNERDRAW
) == BS_OWNERDRAW
; 
 296 void wxCheckBox::MSWMakeOwnerDrawn(bool ownerDrawn
) 
 298     long style 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
 300     // note that BS_CHECKBOX & BS_OWNERDRAW != 0 so we can't operate on 
 301     // them as on independent style bits 
 304         style 
&= ~(BS_CHECKBOX 
| BS_3STATE
); 
 305         style 
|= BS_OWNERDRAW
; 
 307         Connect(wxEVT_ENTER_WINDOW
, 
 308                 wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 309         Connect(wxEVT_LEAVE_WINDOW
, 
 310                 wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 311         Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 312         Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 313         Connect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 314         Connect(wxEVT_KILL_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 316     else // reset to default colour 
 318         style 
&= ~BS_OWNERDRAW
; 
 319         style 
|= HasFlag(wxCHK_3STATE
) ? BS_3STATE 
: BS_CHECKBOX
; 
 321         Disconnect(wxEVT_ENTER_WINDOW
, 
 322                    wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 323         Disconnect(wxEVT_LEAVE_WINDOW
, 
 324                    wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave
)); 
 325         Disconnect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 326         Disconnect(wxEVT_LEFT_UP
, wxMouseEventHandler(wxCheckBox::OnMouseLeft
)); 
 327         Disconnect(wxEVT_SET_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 328         Disconnect(wxEVT_KILL_FOCUS
, wxFocusEventHandler(wxCheckBox::OnFocus
)); 
 331     ::SetWindowLong(GetHwnd(), GWL_STYLE
, style
); 
 335         // ensure that controls state is consistent with internal state 
 336         DoSet3StateValue(m_state
); 
 340 void wxCheckBox::OnMouseEnterOrLeave(wxMouseEvent
& event
) 
 342     m_isHot 
= event
.GetEventType() == wxEVT_ENTER_WINDOW
; 
 351 void wxCheckBox::OnMouseLeft(wxMouseEvent
& event
) 
 353     // TODO: we should capture the mouse here to be notified about left up 
 354     //       event but this interferes with BN_CLICKED generation so if we 
 355     //       want to do this we'd need to generate them ourselves 
 356     m_isPressed 
= event
.GetEventType() == wxEVT_LEFT_DOWN
; 
 362 void wxCheckBox::OnFocus(wxFocusEvent
& event
) 
 369 bool wxCheckBox::MSWOnDraw(WXDRAWITEMSTRUCT 
*item
) 
 371     DRAWITEMSTRUCT 
*dis 
= (DRAWITEMSTRUCT 
*)item
; 
 373     if ( !IsOwnerDrawn() || dis
->CtlType 
!= ODT_BUTTON 
) 
 374         return wxCheckBoxBase::MSWOnDraw(item
); 
 376     // calculate the rectangles for the check mark itself and the label 
 378     RECT
& rect 
= dis
->rcItem
; 
 382     rectLabel
.top 
= rect
.top
; 
 384     rectLabel
.bottom 
= rect
.bottom
; 
 385     const int checkSize 
= GetBestSize().y
; 
 386     const int MARGIN 
= 3; 
 388     const bool isRightAligned 
= HasFlag(wxALIGN_RIGHT
); 
 389     if ( isRightAligned 
) 
 391         rectCheck
.right 
= rect
.right
; 
 392         rectCheck
.left 
= rectCheck
.right 
- checkSize
; 
 394         rectLabel
.right 
= rectCheck
.left 
- MARGIN
; 
 395         rectLabel
.left 
= rect
.left
; 
 397     else // normal, left-aligned checkbox 
 399         rectCheck
.left 
= rect
.left
; 
 400         rectCheck
.right 
= rectCheck
.left 
+ checkSize
; 
 402         rectLabel
.left 
= rectCheck
.right 
+ MARGIN
; 
 403         rectLabel
.right 
= rect
.right
; 
 406     // show we draw a focus rect? 
 407     const bool isFocused 
= m_isPressed 
|| FindFocus() == this; 
 410     // draw the checkbox itself 
 415         flags 
|= wxCONTROL_DISABLED
; 
 416     switch ( Get3StateValue() ) 
 419             flags 
|= wxCONTROL_CHECKED
; 
 422         case wxCHK_UNDETERMINED
: 
 423             flags 
|= wxCONTROL_PRESSED
; 
 427             wxFAIL_MSG( wxT("unexpected Get3StateValue() return value") ); 
 430         case wxCHK_UNCHECKED
: 
 431             // no extra styles needed 
 435     if ( wxFindWindowAtPoint(wxGetMousePosition()) == this ) 
 436         flags 
|= wxCONTROL_CURRENT
; 
 438     wxRendererNative::Get(). 
 439         DrawCheckBox(this, dc
, wxRectFromRECT(rectCheck
), flags
); 
 442     const wxString
& label 
= GetLabel(); 
 444     // first we need to measure it 
 445     UINT fmt 
= DT_NOCLIP
; 
 447     // drawing underlying doesn't look well with focus rect (and the native 
 448     // control doesn't do it) 
 450         fmt 
|= DT_HIDEPREFIX
; 
 451     if ( isRightAligned 
) 
 453     // TODO: also use DT_HIDEPREFIX if the system is configured so 
 455     // we need to get the label real size first if we have to draw a focus rect 
 459         if ( !::DrawText(hdc
, label
.t_str(), label
.length(), &rectLabel
, 
 462             wxLogLastError(wxT("DrawText(DT_CALCRECT)")); 
 468         ::SetTextColor(hdc
, ::GetSysColor(COLOR_GRAYTEXT
)); 
 471     if ( !::DrawText(hdc
, label
.t_str(), label
.length(), &rectLabel
, fmt
) ) 
 473         wxLogLastError(wxT("DrawText()")); 
 476     // finally draw the focus 
 481         if ( !::DrawFocusRect(hdc
, &rectLabel
) ) 
 483             wxLogLastError(wxT("DrawFocusRect()")); 
 490 #endif // wxUSE_CHECKBOX