dolphin/Externals/wxWidgets/src/msw/checkbox.cpp

589 lines
17 KiB
C++
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/checkbox.cpp
// Purpose: wxCheckBox
// Author: Julian Smart
// Modified by:
// Created: 04/01/98
// RCS-ID: $Id: checkbox.cpp 40331 2006-07-25 18:47:39Z VZ $
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_CHECKBOX
#include "wx/checkbox.h"
#ifndef WX_PRECOMP
#include "wx/brush.h"
#include "wx/dcscreen.h"
#include "wx/settings.h"
#endif
#include "wx/msw/uxtheme.h"
#include "wx/msw/private.h"
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
#ifndef BST_UNCHECKED
#define BST_UNCHECKED 0x0000
#endif
#ifndef BST_CHECKED
#define BST_CHECKED 0x0001
#endif
#ifndef BST_INDETERMINATE
#define BST_INDETERMINATE 0x0002
#endif
#ifndef DFCS_HOT
#define DFCS_HOT 0x1000
#endif
#ifndef DT_HIDEPREFIX
#define DT_HIDEPREFIX 0x00100000
#endif
#ifndef BP_CHECKBOX
#define BP_CHECKBOX 3
#endif
// these values are defined in tmschema.h (except the first one)
enum
{
CBS_INVALID,
CBS_UNCHECKEDNORMAL,
CBS_UNCHECKEDHOT,
CBS_UNCHECKEDPRESSED,
CBS_UNCHECKEDDISABLED,
CBS_CHECKEDNORMAL,
CBS_CHECKEDHOT,
CBS_CHECKEDPRESSED,
CBS_CHECKEDDISABLED,
CBS_MIXEDNORMAL,
CBS_MIXEDHOT,
CBS_MIXEDPRESSED,
CBS_MIXEDDISABLED
};
// these are our own
enum
{
CBS_HOT_OFFSET = 1,
CBS_PRESSED_OFFSET = 2,
CBS_DISABLED_OFFSET = 3
};
// ============================================================================
// implementation
// ============================================================================
#if wxUSE_EXTENDED_RTTI
WX_DEFINE_FLAGS( wxCheckBoxStyle )
wxBEGIN_FLAGS( wxCheckBoxStyle )
// new style border flags, we put them first to
// use them for streaming out
wxFLAGS_MEMBER(wxBORDER_SIMPLE)
wxFLAGS_MEMBER(wxBORDER_SUNKEN)
wxFLAGS_MEMBER(wxBORDER_DOUBLE)
wxFLAGS_MEMBER(wxBORDER_RAISED)
wxFLAGS_MEMBER(wxBORDER_STATIC)
wxFLAGS_MEMBER(wxBORDER_NONE)
// old style border flags
wxFLAGS_MEMBER(wxSIMPLE_BORDER)
wxFLAGS_MEMBER(wxSUNKEN_BORDER)
wxFLAGS_MEMBER(wxDOUBLE_BORDER)
wxFLAGS_MEMBER(wxRAISED_BORDER)
wxFLAGS_MEMBER(wxSTATIC_BORDER)
wxFLAGS_MEMBER(wxNO_BORDER)
// standard window styles
wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
wxFLAGS_MEMBER(wxCLIP_CHILDREN)
wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
wxFLAGS_MEMBER(wxWANTS_CHARS)
wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
wxFLAGS_MEMBER(wxVSCROLL)
wxFLAGS_MEMBER(wxHSCROLL)
wxEND_FLAGS( wxCheckBoxStyle )
IMPLEMENT_DYNAMIC_CLASS_XTI(wxCheckBox, wxControl,"wx/checkbox.h")
wxBEGIN_PROPERTIES_TABLE(wxCheckBox)
wxEVENT_PROPERTY( Click , wxEVT_COMMAND_CHECKBOX_CLICKED , wxCommandEvent )
wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
wxPROPERTY( Value ,bool, SetValue, GetValue, EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
wxPROPERTY_FLAGS( WindowStyle , wxCheckBoxStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
wxEND_PROPERTIES_TABLE()
wxBEGIN_HANDLERS_TABLE(wxCheckBox)
wxEND_HANDLERS_TABLE()
wxCONSTRUCTOR_6( wxCheckBox , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
#else
IMPLEMENT_DYNAMIC_CLASS(wxCheckBox, wxControl)
#endif
// ----------------------------------------------------------------------------
// wxCheckBox creation
// ----------------------------------------------------------------------------
void wxCheckBox::Init()
{
m_state = wxCHK_UNCHECKED;
m_isPressed =
m_isHot = false;
}
bool wxCheckBox::Create(wxWindow *parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos,
const wxSize& size, long style,
const wxValidator& validator,
const wxString& name)
{
Init();
if ( !CreateControl(parent, id, pos, size, style, validator, name) )
return false;
long msStyle = WS_TABSTOP;
if ( style & wxCHK_3STATE )
{
msStyle |= BS_3STATE;
}
else
{
wxASSERT_MSG( !Is3rdStateAllowedForUser(),
wxT("Using wxCH_ALLOW_3RD_STATE_FOR_USER")
wxT(" style flag for a 2-state checkbox is useless") );
msStyle |= BS_CHECKBOX;
}
if ( style & wxALIGN_RIGHT )
{
msStyle |= BS_LEFTTEXT | BS_RIGHT;
}
return MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, 0);
}
// ----------------------------------------------------------------------------
// wxCheckBox geometry
// ----------------------------------------------------------------------------
wxSize wxCheckBox::DoGetBestSize() const
{
static int s_checkSize = 0;
if ( !s_checkSize )
{
wxScreenDC dc;
dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
s_checkSize = dc.GetCharHeight();
}
wxString str = wxGetWindowText(GetHWND());
int wCheckbox, hCheckbox;
if ( !str.empty() )
{
GetTextExtent(GetLabelText(str), &wCheckbox, &hCheckbox);
wCheckbox += s_checkSize + GetCharWidth();
if ( hCheckbox < s_checkSize )
hCheckbox = s_checkSize;
}
else
{
wCheckbox = s_checkSize;
hCheckbox = s_checkSize;
}
#ifdef __WXWINCE__
hCheckbox += 1;
#endif
wxSize best(wCheckbox, hCheckbox);
CacheBestSize(best);
return best;
}
// ----------------------------------------------------------------------------
// wxCheckBox operations
// ----------------------------------------------------------------------------
void wxCheckBox::SetValue(bool val)
{
Set3StateValue(val ? wxCHK_CHECKED : wxCHK_UNCHECKED);
}
bool wxCheckBox::GetValue() const
{
return Get3StateValue() != wxCHK_UNCHECKED;
}
void wxCheckBox::Command(wxCommandEvent& event)
{
int state = event.GetInt();
wxCHECK_RET( (state == wxCHK_UNCHECKED) || (state == wxCHK_CHECKED)
|| (state == wxCHK_UNDETERMINED),
wxT("event.GetInt() returned an invalid checkbox state") );
Set3StateValue((wxCheckBoxState) state);
ProcessCommand(event);
}
wxCOMPILE_TIME_ASSERT(wxCHK_UNCHECKED == BST_UNCHECKED
&& wxCHK_CHECKED == BST_CHECKED
&& wxCHK_UNDETERMINED == BST_INDETERMINATE, EnumValuesIncorrect);
void wxCheckBox::DoSet3StateValue(wxCheckBoxState state)
{
m_state = state;
if ( !IsOwnerDrawn() )
::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM) state, 0);
else // owner drawn buttons don't react to this message
Refresh();
}
wxCheckBoxState wxCheckBox::DoGet3StateValue() const
{
return m_state;
}
bool wxCheckBox::MSWCommand(WXUINT cmd, WXWORD WXUNUSED(id))
{
if ( cmd != BN_CLICKED && cmd != BN_DBLCLK )
return false;
// first update the value so that user event handler gets the new checkbox
// value
// ownerdrawn buttons don't manage their state themselves unlike usual
// auto checkboxes so do it ourselves in any case
wxCheckBoxState state;
if ( Is3rdStateAllowedForUser() )
{
state = (wxCheckBoxState)((m_state + 1) % 3);
}
else // 2 state checkbox (at least from users point of view)
{
// note that wxCHK_UNDETERMINED also becomes unchecked when clicked
state = m_state == wxCHK_UNCHECKED ? wxCHK_CHECKED : wxCHK_UNCHECKED;
}
DoSet3StateValue(state);
// generate the event
wxCommandEvent event(wxEVT_COMMAND_CHECKBOX_CLICKED, m_windowId);
event.SetInt(state);
event.SetEventObject(this);
ProcessCommand(event);
return true;
}
// ----------------------------------------------------------------------------
// owner drawn checkboxes stuff
// ----------------------------------------------------------------------------
bool wxCheckBox::SetForegroundColour(const wxColour& colour)
{
if ( !wxCheckBoxBase::SetForegroundColour(colour) )
return false;
// the only way to change the checkbox foreground colour under Windows XP
// is to owner draw it
if ( wxUxThemeEngine::GetIfActive() )
MakeOwnerDrawn(colour.Ok());
return true;
}
bool wxCheckBox::IsOwnerDrawn() const
{
return
(::GetWindowLong(GetHwnd(), GWL_STYLE) & BS_OWNERDRAW) == BS_OWNERDRAW;
}
void wxCheckBox::MakeOwnerDrawn(bool ownerDrawn)
{
long style = ::GetWindowLong(GetHwnd(), GWL_STYLE);
// note that BS_CHECKBOX & BS_OWNERDRAW != 0 so we can't operate on
// them as on independent style bits
if ( ownerDrawn )
{
style &= ~(BS_CHECKBOX | BS_3STATE);
style |= BS_OWNERDRAW;
Connect(wxEVT_ENTER_WINDOW,
wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave));
Connect(wxEVT_LEAVE_WINDOW,
wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave));
Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxCheckBox::OnMouseLeft));
Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxCheckBox::OnMouseLeft));
Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(wxCheckBox::OnFocus));
Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(wxCheckBox::OnFocus));
}
else // reset to default colour
{
style &= ~BS_OWNERDRAW;
style |= HasFlag(wxCHK_3STATE) ? BS_3STATE : BS_CHECKBOX;
Disconnect(wxEVT_ENTER_WINDOW,
wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave));
Disconnect(wxEVT_LEAVE_WINDOW,
wxMouseEventHandler(wxCheckBox::OnMouseEnterOrLeave));
Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxCheckBox::OnMouseLeft));
Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(wxCheckBox::OnMouseLeft));
Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(wxCheckBox::OnFocus));
Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(wxCheckBox::OnFocus));
}
::SetWindowLong(GetHwnd(), GWL_STYLE, style);
if ( !ownerDrawn )
{
// ensure that controls state is consistent with internal state
DoSet3StateValue(m_state);
}
}
void wxCheckBox::OnMouseEnterOrLeave(wxMouseEvent& event)
{
m_isHot = event.GetEventType() == wxEVT_ENTER_WINDOW;
if ( !m_isHot )
m_isPressed = false;
Refresh();
event.Skip();
}
void wxCheckBox::OnMouseLeft(wxMouseEvent& event)
{
// TODO: we should capture the mouse here to be notified about left up
// event but this interferes with BN_CLICKED generation so if we
// want to do this we'd need to generate them ourselves
m_isPressed = event.GetEventType() == wxEVT_LEFT_DOWN;
Refresh();
event.Skip();
}
void wxCheckBox::OnFocus(wxFocusEvent& event)
{
Refresh();
event.Skip();
}
bool wxCheckBox::MSWOnDraw(WXDRAWITEMSTRUCT *item)
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)item;
if ( !IsOwnerDrawn() || dis->CtlType != ODT_BUTTON )
return wxCheckBoxBase::MSWOnDraw(item);
// calculate the rectangles for the check mark itself and the label
HDC hdc = dis->hDC;
RECT& rect = dis->rcItem;
RECT rectCheck,
rectLabel;
rectCheck.top =
rectLabel.top = rect.top;
rectCheck.bottom =
rectLabel.bottom = rect.bottom;
const int checkSize = GetBestSize().y;
const int MARGIN = 3;
const bool isRightAligned = HasFlag(wxALIGN_RIGHT);
if ( isRightAligned )
{
rectCheck.right = rect.right;
rectCheck.left = rectCheck.right - checkSize;
rectLabel.right = rectCheck.left - MARGIN;
rectLabel.left = rect.left;
}
else // normal, left-aligned checkbox
{
rectCheck.left = rect.left;
rectCheck.right = rectCheck.left + checkSize;
rectLabel.left = rectCheck.right + MARGIN;
rectLabel.right = rect.right;
}
// show we draw a focus rect?
const bool isFocused = m_isPressed || FindFocus() == this;
// draw the checkbox itself: note that this should really, really be in
// wxRendererNative but unfortunately we can't add a new virtual function
// to it without breaking backwards compatibility
// classic Win32 version -- this can be useful when we move this into
// wxRendererNative
#if defined(__WXWINCE__) || !wxUSE_UXTHEME
UINT state = DFCS_BUTTONCHECK;
if ( !IsEnabled() )
state |= DFCS_INACTIVE;
switch ( Get3StateValue() )
{
case wxCHK_CHECKED:
state |= DFCS_CHECKED;
break;
case wxCHK_UNDETERMINED:
state |= DFCS_PUSHED;
break;
default:
wxFAIL_MSG( _T("unexpected Get3StateValue() return value") );
// fall through
case wxCHK_UNCHECKED:
// no extra styles needed
break;
}
if ( wxFindWindowAtPoint(wxGetMousePosition()) == this )
state |= DFCS_HOT;
if ( !::DrawFrameControl(hdc, &rectCheck, DFC_BUTTON, state) )
{
wxLogLastError(_T("DrawFrameControl(DFC_BUTTON)"));
}
#else // XP version
wxUxThemeEngine *themeEngine = wxUxThemeEngine::GetIfActive();
if ( !themeEngine )
return false;
wxUxThemeHandle theme(this, L"BUTTON");
if ( !theme )
return false;
int state;
switch ( Get3StateValue() )
{
case wxCHK_CHECKED:
state = CBS_CHECKEDNORMAL;
break;
case wxCHK_UNDETERMINED:
state = CBS_MIXEDNORMAL;
break;
default:
wxFAIL_MSG( _T("unexpected Get3StateValue() return value") );
// fall through
case wxCHK_UNCHECKED:
state = CBS_UNCHECKEDNORMAL;
break;
}
if ( !IsEnabled() )
state += CBS_DISABLED_OFFSET;
else if ( m_isPressed )
state += CBS_PRESSED_OFFSET;
else if ( m_isHot )
state += CBS_HOT_OFFSET;
HRESULT hr = themeEngine->DrawThemeBackground
(
theme,
hdc,
BP_CHECKBOX,
state,
&rectCheck,
NULL
);
if ( FAILED(hr) )
{
wxLogApiError(_T("DrawThemeBackground(BP_CHECKBOX)"), hr);
}
#endif // 0/1
// draw the text
const wxString& label = GetLabel();
// first we need to measure it
UINT fmt = DT_NOCLIP;
// drawing underlying doesn't look well with focus rect (and the native
// control doesn't do it)
if ( isFocused )
fmt |= DT_HIDEPREFIX;
if ( isRightAligned )
fmt |= DT_RIGHT;
// TODO: also use DT_HIDEPREFIX if the system is configured so
// we need to get the label real size first if we have to draw a focus rect
// around it
if ( isFocused )
{
if ( !::DrawText(hdc, label, label.length(), &rectLabel,
fmt | DT_CALCRECT) )
{
wxLogLastError(_T("DrawText(DT_CALCRECT)"));
}
}
if ( !IsEnabled() )
{
::SetTextColor(hdc, ::GetSysColor(COLOR_GRAYTEXT));
}
if ( !::DrawText(hdc, label, label.length(), &rectLabel, fmt) )
{
wxLogLastError(_T("DrawText()"));
}
// finally draw the focus
if ( isFocused )
{
rectLabel.left--;
rectLabel.right++;
if ( !::DrawFocusRect(hdc, &rectLabel) )
{
wxLogLastError(_T("DrawFocusRect()"));
}
}
return true;
}
#endif // wxUSE_CHECKBOX