Add wxSpinCtrl::SetBase() to allow entering hexadecimal numbers.

Add a generic SetBase() API even though right now only bases 10 and 16 are
supported as we might support other ones (e.g. 8?) in the future. Implement it
for MSW, GTK and generic versions.

Add controls allowing to test this feature to the widgets sample.

Add "base" property support to the XRC handler for wxSpinCtrl, document it and
test it in the xrc sample.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72414 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-08-30 20:24:38 +00:00
parent 36090ae57e
commit 9e565667d0
14 changed files with 328 additions and 23 deletions

View File

@@ -537,6 +537,7 @@ All:
All (GUI):
- Add new wxSimplebook class.
- Support hexadecimal numbers in wxSpinCtrl.
- Respect window max size in wxBoxSizer (Nathan Ridge).
- Add support for searching in wxWebView for MSW and GTK (Allonii).
- Add possibility to hide and show again wxRibbonBar pages (wxBen).

View File

@@ -1495,7 +1495,12 @@ HTML markup. Note that the markup has to be escaped:
@subsubsection xrc_wxspinctrl wxSpinCtrl
wxSpinCtrl supports the properties as @ref xrc_wxspinbutton.
wxSpinCtrl supports the same properties as @ref xrc_wxspinbutton and, since
wxWidgets 2.9.5, another one:
@beginTable
@row3col{base, integer,
Numeric base, currently can be only 10 or 16 (default: 10).}
@endTable
@subsubsection xrc_wxsplitterwindow wxSplitterWindow

View File

@@ -260,7 +260,7 @@ protected:
class WXDLLIMPEXP_CORE wxSpinCtrl : public wxSpinCtrlGenericBase
{
public:
wxSpinCtrl() {}
wxSpinCtrl() { Init(); }
wxSpinCtrl(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxString& value = wxEmptyString,
@@ -270,6 +270,8 @@ public:
int min = 0, int max = 100, int initial = 0,
const wxString& name = wxT("wxSpinCtrl"))
{
Init();
Create(parent, id, value, pos, size, style, min, max, initial, name);
}
@@ -299,11 +301,24 @@ public:
void SetRange( int minVal, int maxVal ) { DoSetRange(minVal, maxVal); }
void SetIncrement(int inc) { DoSetIncrement(inc); }
virtual int GetBase() const { return m_base; }
virtual bool SetBase(int base);
protected:
virtual void DoSendEvent();
virtual bool DoTextToValue(const wxString& text, double *val);
virtual wxString DoValueToText(double val);
private:
// Common part of all ctors.
void Init()
{
m_base = 10;
}
int m_base;
DECLARE_DYNAMIC_CLASS(wxSpinCtrl)
};
@@ -363,6 +378,11 @@ public:
void SetIncrement(double inc) { DoSetIncrement(inc); }
void SetDigits(unsigned digits);
// We don't implement bases support for floating point numbers, this is not
// very useful in practice.
virtual int GetBase() const { return 10; }
virtual bool SetBase(int WXUNUSED(base)) { return 0; }
protected:
virtual void DoSendEvent();

View File

@@ -89,7 +89,7 @@ protected:
class WXDLLIMPEXP_CORE wxSpinCtrl : public wxSpinCtrlGTKBase
{
public:
wxSpinCtrl() {}
wxSpinCtrl() { Init(); }
wxSpinCtrl(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxString& value = wxEmptyString,
@@ -99,6 +99,8 @@ public:
int min = 0, int max = 100, int initial = 0,
const wxString& name = wxS("wxSpinCtrl"))
{
Init();
Create(parent, id, value, pos, size, style, min, max, initial, name);
}
@@ -127,6 +129,18 @@ public:
void SetRange( int minVal, int maxVal ) { DoSetRange(minVal, maxVal); }
void SetIncrement(int inc) { DoSetIncrement(inc); }
virtual int GetBase() const { return m_base; }
virtual bool SetBase(int base);
private:
// Common part of all ctors.
void Init()
{
m_base = 10;
}
int m_base;
DECLARE_DYNAMIC_CLASS(wxSpinCtrl)
};
@@ -180,6 +194,9 @@ public:
void SetIncrement(double inc) { DoSetIncrement(inc); }
void SetDigits(unsigned digits);
virtual int GetBase() const { return 10; }
virtual bool SetBase(int WXUNUSED(base)) { return false; }
DECLARE_DYNAMIC_CLASS(wxSpinCtrlDouble)
};

View File

@@ -62,6 +62,11 @@ public:
// another wxTextCtrl-like method
void SetSelection(long from, long to);
// wxSpinCtrlBase methods
virtual int GetBase() const;
virtual bool SetBase(int base);
// implementation only from now on
// -------------------------------
@@ -148,6 +153,12 @@ private:
// Common part of all ctors.
void Init();
// Adjust the text control style depending on whether we need to enter only
// digits or may need to enter something else (e.g. "-" sign, "x"
// hexadecimal prefix, ...) in it.
void UpdateBuddyStyle();
DECLARE_DYNAMIC_CLASS(wxSpinCtrl)
DECLARE_EVENT_TABLE()
wxDECLARE_NO_COPY_CLASS(wxSpinCtrl);

View File

@@ -51,6 +51,10 @@ public:
virtual void SetSnapToTicks(bool snap_to_ticks) = 0;
// void SetDigits(unsigned digits) - wxSpinCtrlDouble only
// The base for numbers display, e.g. 10 or 16.
virtual int GetBase() const = 0;
virtual bool SetBase(int base) = 0;
// Select text in the textctrl
virtual void SetSelection(long from, long to) = 0;
@@ -134,6 +138,15 @@ typedef void (wxEvtHandler::*wxSpinDoubleEventFunction)(wxSpinDoubleEvent&);
#include "wx/generic/spinctlg.h"
#endif
namespace wxPrivate
{
// This is an internal helper function currently used by all ports: return the
// string containing hexadecimal representation of the given number.
extern wxString wxSpinCtrlFormatAsHex(long val, long maxVal);
} // namespace wxPrivate
#endif // wxUSE_SPINCTRL
#endif // _WX_SPINCTRL_H_

View File

@@ -114,6 +114,15 @@ public:
long style = wxSP_ARROW_KEYS, int min = 0, int max = 100,
int initial = 0, const wxString& name = "wxSpinCtrl");
/**
Returns the numerical base being currently used, 10 by default.
@see SetBase()
@since 2.9.5
*/
int GetBase() const;
/**
Gets maximal allowable value.
*/
@@ -129,6 +138,27 @@ public:
*/
int GetValue() const;
/**
Sets the base to use for the numbers in this control.
Currently the only supported values are 10 (which is the default) and
16.
Changing the base allows the user to enter the numbers in the specified
base, e.g. with "0x" prefix for hexadecimal numbers, and also displays
the numbers in the specified base when they are changed using the spin
control arrows.
@param base
Numeric base, currently only 10 and 16 are supported.
@return
@true if the base was successfully changed or @false if it failed,
usually meaning that either the base is not 10 or 16.
@since 2.9.5
*/
bool SetBase(int base);
/**
Sets range of allowable values.

View File

@@ -58,10 +58,12 @@ enum
SpinBtnPage_Clear,
SpinBtnPage_SetValue,
SpinBtnPage_SetMinAndMax,
SpinBtnPage_SetBase,
SpinBtnPage_CurValueText,
SpinBtnPage_ValueText,
SpinBtnPage_MinText,
SpinBtnPage_MaxText,
SpinBtnPage_BaseText,
SpinBtnPage_SpinBtn,
SpinBtnPage_SpinCtrl,
SpinBtnPage_SpinCtrlDouble
@@ -105,6 +107,7 @@ protected:
void OnButtonClear(wxCommandEvent& event);
void OnButtonSetValue(wxCommandEvent& event);
void OnButtonSetMinAndMax(wxCommandEvent& event);
void OnButtonSetBase(wxCommandEvent& event);
void OnCheckOrRadioBox(wxCommandEvent& event);
@@ -118,6 +121,7 @@ protected:
void OnUpdateUIValueButton(wxUpdateUIEvent& event);
void OnUpdateUIMinMaxButton(wxUpdateUIEvent& event);
void OnUpdateUIBaseButton(wxUpdateUIEvent& event);
void OnUpdateUIResetButton(wxUpdateUIEvent& event);
@@ -136,6 +140,9 @@ protected:
// the spinbtn range
int m_min, m_max;
// and numeric base
int m_base;
// the controls
// ------------
@@ -156,7 +163,8 @@ protected:
// the text entries for set value/range
wxTextCtrl *m_textValue,
*m_textMin,
*m_textMax;
*m_textMax,
*m_textBase;
private:
DECLARE_EVENT_TABLE()
@@ -171,9 +179,11 @@ BEGIN_EVENT_TABLE(SpinBtnWidgetsPage, WidgetsPage)
EVT_BUTTON(SpinBtnPage_Reset, SpinBtnWidgetsPage::OnButtonReset)
EVT_BUTTON(SpinBtnPage_SetValue, SpinBtnWidgetsPage::OnButtonSetValue)
EVT_BUTTON(SpinBtnPage_SetMinAndMax, SpinBtnWidgetsPage::OnButtonSetMinAndMax)
EVT_BUTTON(SpinBtnPage_SetBase, SpinBtnWidgetsPage::OnButtonSetBase)
EVT_UPDATE_UI(SpinBtnPage_SetValue, SpinBtnWidgetsPage::OnUpdateUIValueButton)
EVT_UPDATE_UI(SpinBtnPage_SetMinAndMax, SpinBtnWidgetsPage::OnUpdateUIMinMaxButton)
EVT_UPDATE_UI(SpinBtnPage_SetBase, SpinBtnWidgetsPage::OnUpdateUIBaseButton)
EVT_UPDATE_UI(SpinBtnPage_Reset, SpinBtnWidgetsPage::OnUpdateUIResetButton)
@@ -218,13 +228,16 @@ SpinBtnWidgetsPage::SpinBtnWidgetsPage(WidgetsBookCtrl *book,
m_spinbtn = NULL;
m_spinctrl = NULL;
m_spinctrldbl = NULL;
m_textValue = NULL;
m_textMin = NULL;
m_textMax = NULL;
m_textValue =
m_textMin =
m_textMax =
m_textBase = NULL;
m_min = 0;
m_max = 10;
m_base = 10;
m_sizerSpin = NULL;
}
@@ -295,6 +308,13 @@ void SpinBtnWidgetsPage::CreateContent()
sizerMiddle->Add(sizerRow, 0, wxALL | wxGROW, 5);
sizerRow = CreateSizerWithTextAndButton(SpinBtnPage_SetBase,
"Set &base",
SpinBtnPage_BaseText,
&m_textBase);
m_textBase->SetValue("10");
sizerMiddle->Add(sizerRow, 0, wxALL | wxGROW, 5);
// right pane
wxSizer *sizerRight = new wxBoxSizer(wxVERTICAL);
sizerRight->SetMinSize(150, 0);
@@ -445,6 +465,22 @@ void SpinBtnWidgetsPage::OnButtonSetMinAndMax(wxCommandEvent& WXUNUSED(event))
m_spinctrldbl->SetRange(minNew, maxNew);
}
void SpinBtnWidgetsPage::OnButtonSetBase(wxCommandEvent& WXUNUSED(event))
{
unsigned long base;
if ( !m_textBase->GetValue().ToULong(&base) || !base )
{
wxLogWarning("Invalid base value.");
return;
}
m_base = base;
if ( !m_spinctrl->SetBase(m_base) )
{
wxLogWarning("Setting base %d failed.", m_base);
}
}
void SpinBtnWidgetsPage::OnButtonSetValue(wxCommandEvent& WXUNUSED(event))
{
long val;
@@ -474,6 +510,12 @@ void SpinBtnWidgetsPage::OnUpdateUIMinMaxButton(wxUpdateUIEvent& event)
mn <= mx);
}
void SpinBtnWidgetsPage::OnUpdateUIBaseButton(wxUpdateUIEvent& event)
{
unsigned long base;
event.Enable( m_textBase->GetValue().ToULong(&base) && base );
}
void SpinBtnWidgetsPage::OnUpdateUIResetButton(wxUpdateUIEvent& event)
{
event.Enable( !m_chkVert->GetValue() ||

View File

@@ -854,7 +854,8 @@ lay them out using wxSizers, absolute positioning, everything you like!
<object class="wxSpinCtrl" name="controls_spinctrl">
<size>100,-1</size>
<max>100</max>
<value>0</value>
<value>17</value>
<base>16</base>
</object>
</object>
</object>

View File

@@ -103,4 +103,17 @@ wxCONSTRUCTOR_6( wxSpinCtrl, wxWindow*, Parent, wxWindowID, Id, \
wxSize, Size, long, WindowStyle )
wxString wxPrivate::wxSpinCtrlFormatAsHex(long val, long maxVal)
{
// We format the value like this is for compatibility with (native
// behaviour of) wxMSW
wxString text;
if ( maxVal < 0x10000 )
text.Printf(wxS("0x%04lx"), val);
else
text.Printf(wxS("0x%08lx"), val);
return text;
}
#endif // wxUSE_SPINCTRL

View File

@@ -562,6 +562,30 @@ void wxSpinCtrlGenericBase::SetSelection(long from, long to)
// wxSpinCtrl
//-----------------------------------------------------------------------------
bool wxSpinCtrl::SetBase(int base)
{
// Currently we only support base 10 and 16. We could add support for base
// 8 quite easily but wxMSW doesn't support it natively so don't bother.
if ( base != 10 && base != 16 )
return false;
if ( base == m_base )
return true;
// Update the current control contents to show in the new base: be careful
// to call DoTextToValue() before changing the base...
double val;
const bool hasValidVal = DoTextToValue(m_textCtrl->GetValue(), &val);
m_base = base;
// ... but DoValueToText() after doing it.
if ( hasValidVal )
m_textCtrl->SetValue(DoValueToText(val));
return true;
}
void wxSpinCtrl::DoSendEvent()
{
wxSpinEvent event( wxEVT_COMMAND_SPINCTRL_UPDATED, GetId());
@@ -574,7 +598,7 @@ void wxSpinCtrl::DoSendEvent()
bool wxSpinCtrl::DoTextToValue(const wxString& text, double *val)
{
long lval;
if ( !text.ToLong(&lval) )
if ( !text.ToLong(&lval, GetBase()) )
return false;
*val = static_cast<double>(lval);
@@ -584,7 +608,19 @@ bool wxSpinCtrl::DoTextToValue(const wxString& text, double *val)
wxString wxSpinCtrl::DoValueToText(double val)
{
switch ( GetBase() )
{
case 16:
return wxPrivate::wxSpinCtrlFormatAsHex(static_cast<long>(val),
GetMax());
default:
wxFAIL_MSG( wxS("Unsupported spin control base") );
// Fall through
case 10:
return wxString::Format("%ld", static_cast<long>(val));
}
}
#endif // !wxHAS_NATIVE_SPINCTRL

View File

@@ -359,6 +359,78 @@ wxSpinCtrlGTKBase::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
// wxSpinCtrl
//-----------------------------------------------------------------------------
extern "C"
{
static gboolean
wx_gtk_spin_input(GtkSpinButton* spin, gdouble* val, wxSpinCtrl* win)
{
// We might use g_ascii_strtoll() here but it's 2.12+ only, so use our own
// wxString function even if this requires an extra conversion.
const wxString
text(wxString::FromUTF8(gtk_entry_get_text(GTK_ENTRY(spin))));
long lval;
if ( !text.ToLong(&lval, win->GetBase()) )
return FALSE;
*val = lval;
return TRUE;
}
static gint
wx_gtk_spin_output(GtkSpinButton* spin, wxSpinCtrl* win)
{
const gint val = gtk_spin_button_get_value_as_int(spin);
gtk_entry_set_text
(
GTK_ENTRY(spin),
wxPrivate::wxSpinCtrlFormatAsHex(val, win->GetMax()).utf8_str()
);
return TRUE;
}
} // extern "C"
bool wxSpinCtrl::SetBase(int base)
{
// Currently we only support base 10 and 16. We could add support for base
// 8 quite easily but wxMSW doesn't support it natively so don't bother
// with doing something wxGTK-specific here.
if ( base != 10 && base != 16 )
return false;
if ( base == m_base )
return true;
m_base = base;
// We need to be able to enter letters for any base greater than 10.
gtk_spin_button_set_numeric( GTK_SPIN_BUTTON(m_widget), m_base <= 10 );
if ( m_base != 10 )
{
g_signal_connect( GTK_SPIN_BUTTON(m_widget), "input",
G_CALLBACK(wx_gtk_spin_input), this);
g_signal_connect( GTK_SPIN_BUTTON(m_widget), "output",
G_CALLBACK(wx_gtk_spin_output), this);
}
else
{
g_signal_handlers_disconnect_by_func(GTK_SPIN_BUTTON(m_widget),
(gpointer)wx_gtk_spin_input,
this);
g_signal_handlers_disconnect_by_func(GTK_SPIN_BUTTON(m_widget),
(gpointer)wx_gtk_spin_output,
this);
}
return true;
}
//-----------------------------------------------------------------------------
// wxSpinCtrlDouble
//-----------------------------------------------------------------------------

View File

@@ -425,6 +425,27 @@ wxSpinCtrl::~wxSpinCtrl()
gs_spinForTextCtrl.erase(GetBuddyHwnd());
}
// ----------------------------------------------------------------------------
// wxSpinCtrl-specific methods
// ----------------------------------------------------------------------------
int wxSpinCtrl::GetBase() const
{
return ::SendMessage(GetHwnd(), UDM_GETBASE, 0, 0);
}
bool wxSpinCtrl::SetBase(int base)
{
if ( !::SendMessage(GetHwnd(), UDM_SETBASE, base, 0) )
return false;
// Whether we need to be able enter "x" or not influences whether we should
// use ES_NUMBER for the buddy control.
UpdateBuddyStyle();
return true;
}
// ----------------------------------------------------------------------------
// wxTextCtrl-like methods
// ----------------------------------------------------------------------------
@@ -443,16 +464,28 @@ void wxSpinCtrl::SetValue(int val)
wxSpinButton::SetValue(val);
// normally setting the value of the spin button is enough as it updates
// its buddy control automatically ...
if ( wxGetWindowText(m_hwndBuddy).empty() )
// Normally setting the value of the spin button is enough as it updates
// its buddy control automatically but in a couple of situations it doesn't
// do it, for whatever reason, do it explicitly then:
const wxString text = wxGetWindowText(m_hwndBuddy);
// First case is when the text control is empty and the value is 0: the
// spin button just leaves it empty in this case, while we want to show 0
// in it.
if ( text.empty() && !val )
{
::SetWindowText(GetBuddyHwnd(), wxT("0"));
}
// Another one is when we're using hexadecimal base but the user input
// doesn't start with "0x" -- we prefer to show it to avoid ambiguity
// between decimal and hexadecimal.
if ( GetBase() == 16 &&
(text.length() < 3 || text[0] != '0' ||
(text[1] != 'x' && text[1] != 'X')) )
{
// ... but sometimes it doesn't, notably when the value is 0 and the
// text control is currently empty, the spin button seems to be happy
// to leave it like this, while we really want to always show the
// current value in the control, so do it manually
::SetWindowText(GetBuddyHwnd(),
wxString::Format(wxT("%d"), val).t_str());
wxPrivate::wxSpinCtrlFormatAsHex(val, m_max).t_str());
}
m_oldValue = GetValue();
@@ -462,10 +495,10 @@ void wxSpinCtrl::SetValue(int val)
int wxSpinCtrl::GetValue() const
{
wxString val = wxGetWindowText(m_hwndBuddy);
const wxString val = wxGetWindowText(m_hwndBuddy);
long n;
if ( (wxSscanf(val, wxT("%ld"), &n) != 1) )
if ( !val.ToLong(&n, GetBase()) )
n = INT_MIN;
if ( n < m_min )
@@ -504,12 +537,19 @@ void wxSpinCtrl::SetRange(int minVal, int maxVal)
wxSpinButton::SetRange(minVal, maxVal);
UpdateBuddyStyle();
}
void wxSpinCtrl::UpdateBuddyStyle()
{
// this control is used for numeric entry so restrict the input to numeric
// keys only -- but only if we don't need to be able to enter "-" in it as
// otherwise this would become impossible
// otherwise this would become impossible and also if we don't use
// hexadecimal as entering "x" of the "0x" prefix wouldn't be allowed
// neither then
const DWORD styleOld = ::GetWindowLong(GetBuddyHwnd(), GWL_STYLE);
DWORD styleNew;
if ( minVal < 0 )
if ( m_min < 0 || GetBase() != 10 )
styleNew = styleOld & ~ES_NUMBER;
else
styleNew = styleOld | ES_NUMBER;

View File

@@ -96,6 +96,10 @@ wxObject *wxSpinCtrlXmlHandler::DoCreateResource()
GetLong(wxT("value"), DEFAULT_VALUE),
GetName());
const long base = GetLong(wxS("base"), 10);
if ( base != 10 )
control->SetBase(base);
SetupWindow(control);
return control;