wxNumberCtrl - a flexible wxTextCtrl accepting numbers only

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
C_Bastian
Earned some good credits
Earned some good credits
Posts: 102
Joined: Thu Sep 29, 2005 3:47 pm

wxNumberCtrl - a flexible wxTextCtrl accepting numbers only

Post by C_Bastian » Sun Sep 16, 2007 9:49 am

Hi,

I came up to the point that I need a control for the input of numbers which should be highly flexible. Perhaps someone finds it usual, so I put it here.

This control behaves like a wxTextCtrl (which it is derived from), but accepts numbers only. You can define min and max values, you can define if the number should be int only (no decimal point accepted) or that certain numbers should not be shown (eg. if you want to have a blank field instead of showing a "0").

If you find any errors or write any extensions, please post them here.

Tested under Windows with VC only.

So here we go:

wxNumberCtrl.h

Code: Select all



#ifndef _WXNUMBERCTRL_H_
#define _WXNUMBERCTRL_H_

#ifdef __GNUG__
#pragma interface "wxNumberCtrl.cpp"
#endif

// includes
#include "wx/wx.h"
#include "wx/textctrl.h"

class wxNumberCtrl: public wxTextCtrl
{
    DECLARE_CLASS( wxNumberCtrl )
    public:
        wxNumberCtrl( ){};

        wxNumberCtrl(wxWindow* parent, wxWindowID id, const int value = 0, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0, const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr)
            {
                Create( parent, id, value, pos, size, style, validator, idname);
            };
            
        wxNumberCtrl(wxWindow* parent, wxWindowID id, wxString s, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0, const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr)
            {
                Create( parent, id, 0, pos, size, style, validator, idname);
            };
        
        /// Creation
        bool Create(wxWindow* parent, wxWindowID id, const int value = 0, 
            const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, 
            long style = 0 , const wxValidator& validator = wxDefaultValidator, 
            const wxString& idname = wxTextCtrlNameStr);
            
        // Methoden zum Zugriff auf interne Variablen
        
        //Lesen
        int GetInt();
        double GetDouble();
        
        //Setzen
        
        void SetRingBell(bool ring);
        void SetInt(int i);
        void SetDouble(double d, wxString s=wxT("%0.9g"));
        void SetRange(double minimum, double maximum);
        void SetNoShow(double ns, bool b=true);
        void AcceptIntOnly(bool b);
       
    private:
        
        DECLARE_EVENT_TABLE()

        void OnKillFocus(wxFocusEvent& event);        
        void OnChar(wxKeyEvent& event);
        void OnKeyUp(wxKeyEvent& event);
        
        
        wxString oldvalue;
        int min, max;
        
        double noshownumber;
                
        bool ringbell, noshow, intonly;
};
#endif
wxNumberCtrl.cpp:

Code: Select all


#ifdef __GNUG__
#pragma implementation "wxNumberCtrl.h"
#endif

#include "wx/wx.h"
#include "wx/textctrl.h"
#include "wxNumberCtrl.h"

IMPLEMENT_CLASS( wxNumberCtrl, wxTextCtrl )

BEGIN_EVENT_TABLE( wxNumberCtrl, wxTextCtrl )
    EVT_KEY_DOWN(wxNumberCtrl::OnChar )
    EVT_KEY_UP(wxNumberCtrl::OnKeyUp)
    EVT_KILL_FOCUS(wxNumberCtrl::OnKillFocus )
END_EVENT_TABLE()


bool wxNumberCtrl::Create(wxWindow* parent, wxWindowID id, const int value, 
            const wxPoint& pos, const wxSize& size, 
            long style, const wxValidator& validator, 
            const wxString& idname)
{
    wxString s;
    if (value!=0)
        s.Printf("%i",value);
    max=INT_MAX;
    min=-(INT_MAX-1);  
    noshow=false;
    
    if (!wxTextCtrl::Create(parent, id, s, pos, size, style, validator, idname))
        return false;
    else
        return true;
    

}

void wxNumberCtrl::SetRange(double minimum, double maximum)
{
    double d;
    if (minimum>maximum)
    {
        d=minimum;
        minimum=maximum;
        maximum=d;
    }
    min=minimum;
    max=maximum;
   
}

void wxNumberCtrl::SetRingBell(bool ring)
{
    ringbell=ring;
}

int wxNumberCtrl::GetInt() 
{
    wxString s;
    long val;
    s=GetValue();
    if (noshow)
    {
        if (GetLastPosition()==0)
            val=noshownumber;
        else
            s.ToLong(&val, 10);
    }
    else
    {
        s.ToLong(&val, 10);
    }
    return (int) val;
}

void wxNumberCtrl::SetInt(int i)
{
    wxString s;
    s.Printf("%i",i);
    SetValue(s);
    if ((noshow)&&(i==noshownumber))
        Clear();

}

double wxNumberCtrl::GetDouble()
{
    wxString s; 
    s=wxTextCtrl::GetValue();
    double val;   
    if (noshow)
    {
        if (GetLastPosition()==0)
            val=noshownumber;
        else
            s.ToDouble(&val);
    }
    else
    {
        s.ToDouble(&val);
    }
    return val;
}

void wxNumberCtrl::SetDouble(double d, wxString s)
{
    wxString t;
    t.Printf(s,d);
    SetValue(t);   
    if ((noshow)&&(d==noshownumber))
        Clear();
}

void wxNumberCtrl::OnChar(wxKeyEvent& event)
{
    oldvalue=GetValue();

    switch (event.GetKeyCode())
    {
    
        case '0':
        case WXK_NUMPAD0:  
            if (!((GetDouble()==0)&&(oldvalue.Find('.')==-1))) //keine doppelte Null
                event.Skip();
            break;
        
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case WXK_NUMPAD1:
        case WXK_NUMPAD2:
        case WXK_NUMPAD3:
        case WXK_NUMPAD4:
        case WXK_NUMPAD5:
        case WXK_NUMPAD6:
        case WXK_NUMPAD7:
        case WXK_NUMPAD8:
        case WXK_NUMPAD9:
            if(GetDouble()==0) Clear();
            
        case WXK_DELETE:    
        case WXK_RIGHT:
        case WXK_LEFT:        
        case WXK_BACK:
        case WXK_TAB: 
        case WXK_NUMPAD_END:
        case WXK_NUMPAD_BEGIN:
        case WXK_HOME:
        case WXK_END:
        case WXK_RETURN:
            event.Skip();  
            break;
        
        case WXK_NUMPAD_DECIMAL:
        case '.':
        case ',':
            if (!intonly)
                if (oldvalue.Find('.')==-1) //keine doppelten Punkte zulassen
                    WriteText(".");  
            break;
        
        case WXK_NUMPAD_SUBTRACT:
        case '-':
            if (min<0)
                event.Skip();
            break;
            
        default:
            if (ringbell) wxBell();
                event.Skip(false);
            break;
    }

}

void wxNumberCtrl::OnKeyUp(wxKeyEvent& event)
{
    double d=GetDouble();
    if ((d>max)||(d<min))
    {
        SetValue(oldvalue);
        if (ringbell) wxBell();
    }

}

void wxNumberCtrl::SetNoShow(double ns, bool b)
{
    noshow=b;
    noshownumber=ns;
}


void wxNumberCtrl::OnKillFocus(wxFocusEvent& event)
{    
    if (noshow)
    {
        if (GetDouble()==noshownumber)
            Clear();
    }
}

void wxNumberCtrl::AcceptIntOnly(bool b)
{
    intonly=b;
}

Have fun.

Sebastian

priyank_bolia
wxWorld Domination!
wxWorld Domination!
Posts: 1339
Joined: Wed Aug 03, 2005 8:10 am
Location: BANGALORE, INDIA
Contact:

Post by priyank_bolia » Mon Sep 17, 2007 7:43 am

You can also use validators and wxRegExValidator

kingkamg
I live to help wx-kind
I live to help wx-kind
Posts: 187
Joined: Tue Apr 08, 2008 1:45 pm

Post by kingkamg » Fri Jun 27, 2008 10:06 am

wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST);

wxArrayString list;

wxString valid_chars(wxT(" 0123456789"));
size_t len = valid_chars.Length();
for (size_t i=0; i<len; i++)
list.Add(wxString(valid_chars.GetChar(i)));

validator.SetIncludes(list);
wxTextCtrl* textCtrl = new wxTextCtrl( this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, 0,validator );

nuvola_notturna
Knows some wx things
Knows some wx things
Posts: 30
Joined: Sun Dec 26, 2004 4:47 pm
Location: Italy

Post by nuvola_notturna » Wed Nov 19, 2008 9:45 am

kingkamg wrote:wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST);

wxArrayString list;

wxString valid_chars(wxT(" 0123456789"));
size_t len = valid_chars.Length();
for (size_t i=0; i<len; i++)
list.Add(wxString(valid_chars.GetChar(i)));

validator.SetIncludes(list);
wxTextCtrl* textCtrl = new wxTextCtrl( this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, 0,validator );
I also was looking for a kind of "number validator", and I run into this.
Using SetIncludes() is ok to set valid characters, but it doesn't consider the actual value inserted. So, how can I set something like max and min values, etc..
Actually, I was thinking about writing my own Validator, rather than deriving the control itself. Do you think there's a simpler way?
OS: Mac OS X ver 10.5 Compiler: gcc ver 4.0.1
Windows: XP Professional x64 v 2003 SP2 Compiler: MinGW
wxWidgets 2.8.10 for All Platforms

...scusate non mi lego a questa schiera morrò pecora nera...

priyank_bolia
wxWorld Domination!
wxWorld Domination!
Posts: 1339
Joined: Wed Aug 03, 2005 8:10 am
Location: BANGALORE, INDIA
Contact:

Post by priyank_bolia » Wed Nov 19, 2008 11:10 am

I guess you can write an regular expression to check for the min and max range: like between 100 and 200
something like: [1][0-9][0-9]|200
Deriving a new control, won't be good idea just for validation, that the validator job.

nuvola_notturna
Knows some wx things
Knows some wx things
Posts: 30
Joined: Sun Dec 26, 2004 4:47 pm
Location: Italy

Post by nuvola_notturna » Wed Nov 19, 2008 11:35 am

priyank_bolia wrote:I guess you can write an regular expression to check for the min and max range: like between 100 and 200
something like: [1][0-9][0-9]|200
Deriving a new control, won't be good idea just for validation, that the validator job.
Thanks for your help. Regex if fine but I think I'll try to derive a new validator (I agree with you about deriving a new control), just in order to have something more flexible (for example I would like to simply pass the range values, in a dynamic way).
OS: Mac OS X ver 10.5 Compiler: gcc ver 4.0.1
Windows: XP Professional x64 v 2003 SP2 Compiler: MinGW
wxWidgets 2.8.10 for All Platforms

...scusate non mi lego a questa schiera morrò pecora nera...

priyank_bolia
wxWorld Domination!
wxWorld Domination!
Posts: 1339
Joined: Wed Aug 03, 2005 8:10 am
Location: BANGALORE, INDIA
Contact:

Post by priyank_bolia » Thu Nov 20, 2008 7:34 am

Just a thought:
You can write a customizable validator, so that it can check not only for integers but also floating point values, and the number of digits after the decimal.
Also in European languages, the symbol for decimal and thousand separators are different, and also the location of separator, like in US its thousand separator but in India its power of 100 like: 9,99,000.50 Rs.
This would enable a generic validator that can be used by all without mush hassle.

Post Reply