April 19, 2024, 06:41:09 PM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Numeric input

Started by John S, March 03, 2007, 05:50:28 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

John S

Paul,
Please help me get this to work.  I have spent a lot of time and I'm not getting any where.  I have to work on other aspects of this project for school.

I have editted the file way down (cut it to only a fraction of the original program).   There is one edit control which I want to filter input on and restrict to only "0" to "9" and ".".  I also want to restrict the number of "." present to one (1).  Eventually, I would like to include "-","+" and "e" for scientific notation (I can do that myself once I understand what's required).

I have attached an exe file as well as the src.  The Edit Box is labelled as EDIT_PNUM and I have established a CControl pointer as m_proj_info[2]

The code for the NEdit class is:

class NEdit : CEdit
{
declare virtual OnChar(unsigned int nChar, int nRepCnt), int
{
if (((nChar<ASC("0"))+(nChar>ASC("9")))*(nChar!=ASC(".")))
return true; // eat it
return false; // allow it
}
}
John Siino, Advanced Engineering Services and Software

Parker

I'm working on it.

There were two major problems.

One, you were creating 1024 NEdit controls with the line
m_NEdit         = new(NEdit, 1024);
I'm guessing you don't need that many of them, so I changed 1024 to 1.

Second, this line is a major part of changing the class of the control.
SetPropA(m_NEdit->m_hWnd,"THIS",m_NEdit+0);
There is a logical explanation for that which I can't quite remember, but it has something to do with the fact that you are dealing with CWindow classes and Windows is dealing with HWND window handles.

Also, as far as I can tell you don't need a CEdit field in NEdit.

I will post back with code that accepts numbers like 1.2e-3 and such.

Parker

This seems to work well.
class NEdit : CEdit
{
declare checkChar( unsigned int ch )
{
int startsel, endsel;
string *text;
int textlen;

GetSel( startsel, endsel );
text = GetText( );

if( ch >= '0' && ch <= '9' ) // decimal digits
return false;
if( ch == 8 ) // backspace
return false;
if( ch == '.' )
{
// Is there already a '.'? Only accept one.
if( strfind( *text, "." ) == 0 )
return false;
}
if( ch == 'e' || ch == 'E' )
{
// There can only be one 'E'
// Also, it can't be at the beginning of the number
if( strfind( *text, "E" ) == 0 && strfind( *text, "e" ) == 0 && startsel != 0 )
return false;
}
if( ch == '+' || ch == '-' )
{
// There must be an 'E' before this, or it must be at the beginning
if( startsel == 0 ) return false;
if( *text[startsel - 1] == 'E' || *text[startsel - 1] == 'e' ) return false;
}
return true;
}

declare virtual OnChar(unsigned int nChar, int nRepCnt), int
{
return checkChar( nChar );
}
}


The problems are that when you select text and try to replace it may function wrong (this is because the current text still has an E or . in it). The solution is to backspace selected text first. I was going to write code to work around this, but it would have made checkChar() at least twice more complicated.

I've attached the whole source file. I didn't look over it thoroughly, just the NEdit parts, so I can't guarantee it's bug free but it seems to work fine here.

John S

I GOT IT TO WORK!!!    ;D
John Siino, Advanced Engineering Services and Software

John S

Thanks Parker,
I posted my solution as you were posting yours!  I will look at what you've done.  I still need to check for multiple "." (decimals).  The problem I was having was that my program was too big and bulky to test stuff out.  So with the scaled down version I was able to see what went where.  I even used THIS!
John Siino, Advanced Engineering Services and Software

John S

March 03, 2007, 10:21:49 PM #5 Last Edit: March 03, 2007, 10:45:59 PM by John S
Works well.  Thanks Parker.  I tweaked it a bit to optimize the code a little.

Only issue now is that up to two (2) "+" s  and  "-" s  are possible in the first place and behind the e.

I'm chasing that down now.


class NEdit : CEdit
{
declare virtual OnChar( unsigned int nChar, int nRepCnt), int;
}

NEdit :: OnChar( unsigned int nChar, int nRepCnt), int
{
if(( nChar >= '0' && nChar <= '9' )||( nChar == 8 )) // // decimal digits & backspace
return false;

int startsel, endsel;
GetSel( startsel, endsel );
string *text;
int textlen;
text = StrLower(GetText( ));
nChar = ToAscii(StrLower(ToChar(nChar)));

if( nChar == '.' )
{
// There can only be one .
if( StrFind( *text, "." ) == 0 )
return false;
}

if( nChar == 'e' )
{
// There can only be one 'E', also, it can't be at the beginning of the number
if( StrFind( *text, "e" ) == 0 && startsel != 0 )
return false;
}

if( nChar == '+' || nChar == '-' )
{
// There must be an 'E' before this, or it must be at the beginning
if( startsel == 0 ) || ( *text[startsel - 1] == 'e' )
return false;

// There can only a maximum of two (2) '+' or '-'
if( StrFind( *text, "+" ) == 1 && StrFind( *text, "-" ) == 1 )
return false;
}
return true;
}
John Siino, Advanced Engineering Services and Software

John S

Got it to work just fine.  Thanks for the help Parker & Paul.
There a couple ways to screw up the input (entering a scientific value then deleting choice pieces).  I think I will use a message box as a warning.


class NEdit : CEdit
{
declare virtual OnChar( unsigned int nChar, int nRepCnt), int;
}

NEdit :: OnChar( unsigned int nChar, int nRepCnt), int
{
if(( nChar >= '0' && nChar <= '9' )||( nChar == 8 )) // // decimal digits & backspace
return false;

int startsel, endsel;
GetSel( startsel, endsel );
string *text;
int textlen;
text = StrLower(GetText( ));
nChar = ToAscii(StrLower(ToChar(nChar)));

if( nChar == '.' )
{
// There can only be one .
if( StrFind( *text, "." ) == 0 )
return false;
}

if( nChar == 'e' )
{
// There can only be one 'E', also, it can't be at the beginning of the number
if( StrFind( *text, "e" ) == 0 && startsel != 0 )
return false;
}

if( (nChar == '+') || (nChar == '-') )
{
// There can only a maximum of two (2) '+' or (2) '-' or one "+" and one "-"
// and they must be at the beginning or behind an E
if( ((startsel == 0)+(*text[startsel-1] == 'e'))
&& ( (StrFind(StrLeft(*text,1),"+") + sgn(StrFind(*text,"+",2))
+ StrFind(StrLeft(*text,1),"-") + sgn(StrFind(*text,"-",2))) < 2 ))
return false;
}

return true;
}
John Siino, Advanced Engineering Services and Software

BlankReg

I would also include support for using a comma as a decimal seperator as well. Quite a few European countries use that instead of a period "." I fallen foul of this a number of times in programs I've written.  ::)


John S

I'll look into that, thanks BlankReg.
John Siino, Advanced Engineering Services and Software

John S

I removed a few lines of that weren't doing anything.   I also added testing for commas ',' as decimals per BlankReg's comment.   Seems to be working pretty well now.
John Siino, Advanced Engineering Services and Software

Bruce Peaslee

March 04, 2007, 05:09:30 PM #10 Last Edit: March 04, 2007, 05:16:21 PM by peaslee
Quote from: John S on March 04, 2007, 04:36:50 PM
I removed a few lines that weren't doing anything.

I can't stand lazy code either ;)
Bruce Peaslee
"Born too loose."
iTired (There's a nap for that.)
Well, I headed for Las Vegas
Only made it out to Needles

Parker

I just remembered:

If you right click in the edit control, you can still paste whatever you want in. I think this is why some programs just alert you that it has an invalid format once it loses focus. People probably aren't likely to do that, and since V isn't a valid numeric character you can't use Ctrl+V (you have to right click->paste). Just keep that in mind.

Another thing:
You can type in e+-, e-+, e--, or e++ if you type one sign and then go back before it and type another one.

And finally: the line
text = StrLower(GetText( ));

will probably cause the program to lose memory. This was a bad decision on my part, though I'm not sure if not calling StrLower will cause this. I only did it because I didn't want any string buffer overflows, but I think you can safely assume that a number won't be longer than 255 characters, so I think you can change text to a string instead of string*.

John S

March 04, 2007, 10:31:31 PM #12 Last Edit: March 04, 2007, 11:41:56 PM by John S
Quote from: Parker on March 04, 2007, 05:58:13 PM
... If you right click in the edit control, you can still paste whatever you want in. ...
... You can type in e+-, e-+, e--, or e++ ...
... text = StrLower(GetText( ));

Thanks for all the help, Parker.

I will look at the right click / copy issue.  I've been thinking about how I might add a buffering variable to temporarily store the old value and test the new value.

I added the StrLower to shorten the code (eliminate tests for 'E' and just test for 'e').  I revisit that decision.

Regarding the e-+, etc., I thought I fixed that.  It must have crept in with one of my edits.   I've got to get back to working on the main program right now.  I was cleaning the code and now it won't run even though it compiles.  I think I have a lose pointer somewhere.

I found another one:  you can put a decimal point in front of a '+' as in:    123E.+123
I have to fix that too.
John Siino, Advanced Engineering Services and Software

John S

I fixed the e--/e-+/e+-/e++ problem and the '.+123'/'123e.+' type problem.  With regard to the cut&paste issue, I'm not sure how that gets picked up.  I think that would have to be tested in OnControl rather than OnChar.   Can you use OnChar with the OnControl on the same Control object?  I'll have to try it.


Here is the latest take on the NEdit class:


class NEdit : CEdit
{
declare virtual OnChar( unsigned int nChar, int nRepCnt), int;
}

NEdit :: OnChar( unsigned int nChar, int nRepCnt), int
{
if(( nChar >= '0' && nChar <= '9' )||( nChar == 8 )) // decimal digits & backspace
return false;

int startsel, endsel, count;
GetSel( startsel, endsel );
string *text;
int textlen;
text = StrLower(GetText( ));
nChar = ToAscii(StrLower(ToChar(nChar)));

if(( nChar == '.' )||( nChar == ',' ))
{
// There can only be one '.' and it must be before the 'E'
if( (StrFind( *text, "." ) == 0 ) && (StrFind( *text, "," ) == 0 )
&& ( *text[startsel] != '+' ) && ( *text[startsel] != '-' ) )
return false;
}

if( nChar == 'e' )
{
// There can only be one 'E', also, it can't be at the beginning of the number
if( StrFind( *text, "e" ) == 0 && startsel != 0 )
return false;
}

if( (nChar == '+') || (nChar == '-') )
{
count = StrFind(StrLeft(*text,1),"+") + sgn(StrFind(*text,"+",2))
+ StrFind(StrLeft(*text,1),"-") + sgn(StrFind(*text,"-",2));

// There can only a maximum of two (2) '+' or (2) '-' or one "+" and one "-"
// and they must be at the beginning or behind an E
if( (startsel == 0) && ((*text[0] != '+')&&(*text[0] != '-'))
&& ( count < 2 ) )
return false;

if( (*text[startsel-1] == 'e') && (*text[startsel] != '+') && (*text[startsel] != '-')
&& ( count < 2 ) )
return false;
}
return true;
}
John Siino, Advanced Engineering Services and Software

John S

I'm having trouble integrating NEdit class into my program.  I have tried doing this several ways but without luck. 

Here is a code snippet:


...
class TabDlg : CDialog
{
declare OnInitDialog(),int;
declare OnClose(),int;
declare OnControl(int nID, int nNotifyCode, unsigned int hControl), int;

...

//member pointers to copy the control class pointers into.
CTabCtrl *m_tab1; // pointer to tab dialog object

...

// pile info pointers
NEdit PileEdit[NPILEEDIT]; // numeric edit objects used in limiting numeric input
...


TabDlg::OnInitDialog(), int
{
m_tab1 = GetControl(TAB_1);

...


//------------------------------------------------------------------------------------------
// Initialize pile info tab pointers, etc.

...

// pile info edits
PileEdit[0]->Create(Col5, TABHT+LSpc*1,  CntrlW, LnHt, AES_RIGHT, L_EDIT, "50.00", this);
...
PileEdit[8]->Create(Col5, TABHT+LSpc*9,  CntrlW, LnHt, AES_RIGHT, Tip_A_EDIT, "0.785", this);

for ( i = 0; i < NPILEEDIT; i++ )
{
PileEdit[i].ShowWindow(SWHIDE);
}
...


John Siino, Advanced Engineering Services and Software

Ionic Wind Support Team

You need to dynamically allocate them in Aurora.  Arrays of classes will only construct the first element of the array.

Ionic Wind Support Team

John S

March 05, 2007, 10:23:55 PM #16 Last Edit: March 05, 2007, 10:28:50 PM by John S
I did that, though probably not right.  I found the problem.  It was in the OnControl Loop - I was getting an uncontrolled loop.
Can you verify what the array construction looks like?

is this right?

NEdit *PileEdit = NEW(NEdit, NPILEEDIT);

for ( i = 0; i < NPILEEDIT; i++ )
   {
      PileEdit.ShowWindow(SWHIDE);
   }

I was getting the feeling that the compiler didn't like seeing NEdit there.
John Siino, Advanced Engineering Services and Software

John Syl.

First of all, thanks guys for this tyhread, it has helped me over a problem I had.


Parker said:
QuoteIf you right click in the edit control, you can still paste whatever you want in. I think this is why some programs just alert you that it has an invalid format once it loses focus. People probably aren't likely to do that, and since V isn't a valid numeric character you can't use Ctrl+V (you have to right click->paste). Just keep that in mind.


If you modify the first line of code:
if(( nChar >= '0' && nChar <= '9' )||( nChar == 8 )) // decimal digits & backspace
return false;


to be:

if(( nChar >= '0' && nChar <= '9' )||( nChar <32 )) // decimal digits & backspace
return false;


This should allow control functions such as Ctrl+V to work, it did when I tried it on my code.

Thanks again
John
Intel 3.6 p4 ht, XP home,2 gb mem, 400 gb hd 20gb raid 0, Nvidia 6600le.
AMD k6-2 500, 40gb.

Started on PDP11 Assembler, BASIC, GWBASIC, 6502, Z80, 80x86, Java, Pascal, C, C++, 
IBasic (std & pro), Aurora, EBasic.  (Master of none, but it's been fun!)

Ionic Wind Support Team

John,
Try to understand that arrays of classes are not constructed.  That goes for dynamically created arrays as well.

Use an array of pointers.

NEdit *m_PileEdit[numcontrols]
...

for (i = 0; i < numcontrols; i++ )
{
m_PileEdit = NEW(NEdit, 1 )
m_PileEdit->Create(...
m_PileEdit->ShowWindow(SWHIDE);
}

Ionic Wind Support Team

John S

thanks Paul, John and Parker 
John Siino, Advanced Engineering Services and Software

John S

Paul,
I got it to work this way:

NEdit *m_PileEdit[numcontrols]
...

for (i = 0; i < numcontrols; i++ )
{
m_PileEdit = NEW(NEdit, 1 )
m_PileEdit->Create(...
m_PileEdit->ShowWindow(SWHIDE);
}


I had to add in the index.  My mistake was trying to generate the array by using:
   m_PileEdit = NEW(NEdit, numcontrols )
John Siino, Advanced Engineering Services and Software

John S

I updated the NEdit class (CEdit control for numeric/decimal input).  This update allows the user to highlight (select) the previous data in the edit box and press "." or "," to put a decimal in the first position.



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//  NEdit.src by John Siino
//  03/18/2007
//  a child class of CEdit which filters user input and restricts input to
//  numeric (decimal, scientific) format

class NEdit : CEdit
{
declare virtual OnChar( unsigned int nChar, int nRepCnt), int;
}

NEdit :: OnChar( unsigned int nChar, int nRepCnt), int
{
if(( nChar >= '0' && nChar <= '9' )||( nChar == 8 ))
return false;

int startsel, endsel, count;
GetSel( startsel, endsel );
string *text;
int textlen;
text = StrLower(GetText( ));
nChar = ToAscii(StrLower(ToChar(nChar)));

if(( nChar == '.' )||( nChar == ',' ))
{ // There can only be one '.' and it must be before the 'E'
if( (StrFind( *text, "." ) == 0 ) && (StrFind( *text, "," ) == 0 )
&& ( *text[startsel] != '+' ) && ( *text[startsel] != '-' ))
return false;
if( (StrFind( StrMid(*text, startsel, endsel-startsel ), "." ) != 0 )
|| (StrFind( StrMid(*text, startsel, endsel-startsel ), "," ) != 0 ) )
return false;
}

if( nChar == 'e' )
{ // There can only be one 'E', also, it can't be at the beginning of the number
if( StrFind( *text, "e" ) == 0 && startsel != 0 )
return false;
}

if( (nChar == '+') || (nChar == '-') )
{
count = StrFind(StrLeft(*text,1),"+") + sgn(StrFind(*text,"+",2))
+ StrFind(StrLeft(*text,1),"-") + sgn(StrFind(*text,"-",2));

// There can only a maximum of two (2) '+' or (2) '-' or one "+" and one "-"
// and they must be at the beginning or behind an E
if( (startsel == 0) && ((*text[0] != '+')&&(*text[0] != '-'))
&& ( count < 2 ) )
return false;

if( (*text[startsel-1] == 'e') && (*text[startsel] != '+') && (*text[startsel] != '-')
&& ( count < 2 ) )
return false;
}
return true;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

John Siino, Advanced Engineering Services and Software