/* $Id: tinplong.cpp 1.1 1995/07/10 12:49:01 leon Exp leon $
 */

/*-----------------------------------------------------------------------
TInputLong is a derivitave of TInputLine designed to accept long integer
numeric input.  Since both the upper and lower limit of acceptable numeric
input can be set, TInputLong may be used for short integer or unsigned shorts
as well.  Option flag bits allow optional hex input and display.  A blank
field may optionally be rejected or interpreted as zero.

Data Members

ushort ilOptions;
   the options (see constructor discussion).

long lLim, uLim;
   the lower and upper limits.


Member functions

TInputLong( const TRect& bounds, int aMaxLen, long lowerLim,
		    long upperLim, ushort flags, const char* labelName = 0 ) :

  Creats a TInputLong control.  Flags may be a combination of:

  ilHex = 1,          //will enable hex input with leading '0x'
  ilBlankEqZero = 2,  //No input (blank) will be interpreted as '0'
  ilDisplayHex = 4;   //Number displayed as hex when possible

  The optional labelName adds an identifier to any range error message.
  Normally, this would be the same string as the field's label (minus
  the '~'s).


virtual void TInputLong::write( opstream& os );
virtual void *TInputLong::read( ipstream& is );
static TStreamable *build();
TInputLong( StreamableInit streamableInit);

virtual ushort dataSize();
virtual void getData( void *rec );
virtual void setData( void *rec );

  The transfer methods.  dataSize is sizeof(long) and rec should be
  the address of a long.


virtual Boolean rangeCheck();

  Returns True if the entered string evaluates to a number >= lowerLim and
  <= upperLim.


virtual void error();

  error is called when rangeCheck fails.  It displays a messagebox
  indicating the allowable range.  The optional labelName parameter in the
  constructor is worked into the message to help identify the field.


virtual void  handleEvent( TEvent& event );

  handleEvent() filters out characters which are not appropriate to numeric
  input.  Tab and  Tab cause a call to rangeCheck() and a call to error()
  if rangeCheck() returns false.  The input must be valid to Tab from the
  view.  There's no attempt made to stop moving to another view with the mouse.


virtual Boolean valid( ushort command );

  if TInputLine.valid() is true and Cmd is neither cmValid or cmCancel, valid
  then calls rangeCheck().  If rangeCheck() is false, then error() is called
  and valid() returns False.

------------------------------------------------------------------------*/

//#define Uses_TKeys

#define Uses_TInputLong

#define Uses_TInputLine

#define Uses_TDrawBuffer

#define Uses_TEvent

#define Uses_opstream

#define Uses_ipstream

#define Uses_MsgBox

#include <tvision/tv.h>

#include <stdlib.h>

#include "input.h"

__link( RInputLine)

#if !defined( __CTYPE_H )

#include <ctype.h>

#endif  // __CTYPE_H


#if !defined( __STRING_H )

#include <String.h>

#endif  // __STRING_H


#if !defined( __DOS_H )

#include <Dos.h>

#endif  // __DOS_H


char * formHexStr(char *s, long L)
{
    if (L < 0)
      {
       s[0] = '-';  s[1] = '0';  s[2] = 'x';
       ltoa(-L, &s[3], 16);
      }
    else
      {s[0] = '0';  s[1] = 'x';
       ltoa(L, &s[2], 16);
      }
    return s;
}

/**
 ** Creates input field for long integers
 **/
TInputLong::TInputLong( const TRect& bounds, int aMaxLen, long lowerLim,
		    long upperLim, ushort flags, const char* labelName ) :
    TInputLine(bounds, aMaxLen),
    lLim (lowerLim),
    uLim(upperLim),
    ilOptions(flags),
    label(newStr(labelName))

{
   if (ilOptions & ilDisplayHex)
       ilOptions |= ilHex;
   if (ilOptions & ilBlankEqZero)
       strcpy(data, "0");
}

/** {duh} **/
TInputLong::~TInputLong()
{
    if (label) delete label;
}

/** Returns size of the data **/
ushort TInputLong::dataSize()
{
    return sizeof(long);
}

/** Returns data **/
void TInputLong::getData( void *rec )
{
   long L;
   if (data[0] == '\0' && (ilOptions & ilBlankEqZero))
     strcpy(data, "0");
   if ( (ilHex & ilOptions) && (strchr(data, 'X') || strchr(data, 'x')) )
	  L = strtol(data, 0, 16);
   else L = strtol(data, 0, 10);
   *(long*)rec = L;
}

/** Handles view events **/
void  TInputLong::handleEvent( TEvent& event )
{
    if (event.what == evKeyDown)
      {switch (event.keyDown.keyCode)
	{
	case kbTab:
	case kbShiftTab:
	   if (!rangeCheck())
	    {error();
	     selectAll(True);
	     clearEvent(event);
	    }
	   break;
	}
      }
    if (event.keyDown.charScan.charCode)
      {
	int ch = toupper(event.keyDown.charScan.charCode);
	switch (ch)
	  {
	  case '-' : if ( !(lLim < 0 && (curPos == 0 || selEnd > 0)) )
			clearEvent(event); break;
	  case 'X' : if ( (ilOptions & ilHex) &&
			 (curPos == 1 && data[0] == '0')
		      || (curPos == 2 && data[0] == '-' && data[1] == '0') );
		     else clearEvent(event);
		     break;
	  case 'A' :
	  case 'B' :
	  case 'C' :
	  case 'D' :
	  case 'E' :
	  case 'F' : if (!strchr(data, 'X') && !strchr(data, 'x') )
			clearEvent(event);
		     break;
	  default : if ((ch < '0' || ch > '9') && (ch < 1 || ch > 0x1B))
			clearEvent(event);
		    break;
	  }
      }
    TInputLine::handleEvent(event);
}

/** Sets data **/
void TInputLong::setData( void *rec )
{
    char s[40];
    long L = *(long*)rec;
    if (L > uLim) L = uLim;
    else if (L < lLim) L = lLim;
    if (ilOptions & ilDisplayHex)
	formHexStr(s, L);
    else ltoa(L, s, 10);
    memcpy( data, s, maxLen );
    data[maxLen] = EOS;
    selectAll( True );
}

/** Checks long int range **/
Boolean TInputLong::rangeCheck()
{
   long L;
   char *endptr;
   if (data[0] == '\0')
     if (ilOptions & ilBlankEqZero)
       strcpy(data, "0");
     else return False;
   if ( (ilHex & ilOptions) && (strchr(data, 'X') || strchr(data, 'x')) )
	  L = strtol(data, &endptr, 16);
   else L = strtol(data, &endptr, 10);
   return Boolean( (L >= lLim) && (L <= uLim) && (*endptr == 0) );
}

/** Outputs error message box **/
void TInputLong::error()
#define SIZE 60

{  char sl[40], su[40], s[SIZE];
   if (label == 0)  s[0] = '\0';
   else  {
      strcpy(s, "\"");
      strncat(s, label, SIZE-4);
      strcat(s, "\"\n");
      }
   if (ilHex & ilOptions)
     messageBox(mfError | mfOKButton, "%sValue not within range %ld(%s) to %ld(%s)",
		s, lLim, formHexStr(sl, lLim), uLim, formHexStr(su, uLim));
   else
     messageBox(mfError | mfOKButton, "%sValue not within range %ld to %ld",
		s, lLim, uLim);
}

/** Validates input field **/
Boolean TInputLong::valid(ushort command)
{
   Boolean rslt = TInputLine::valid(command);
   if (rslt && (command != 0) && (command != cmCancel))
     {rslt = rangeCheck();
      if (!rslt)
	{error();
	 select();
	 selectAll(True);
	}
     }
   return rslt;
}

/** {duh} **/
void TInputLong::write( opstream& os )
{
    TInputLine::write( os );
    os << ilOptions << lLim << uLim;
    os.writeString(label);
}

/** {duh } **/
void *TInputLong::read( ipstream& is )
{
    TInputLine::read( is );
    is >> ilOptions >> lLim >> uLim;
    label = is.readString();
    return this;
}

/** {duh} **/
TStreamable *TInputLong::build()
{
    return new TInputLong( streamableInit );
}

TInputLong::TInputLong( StreamableInit ) : TInputLine( streamableInit )
{
}

const char * const near TInputLong::name = "TInputLong";


TStreamableClass RInputLong( TInputLong::name,
                              TInputLong::build,
                              __DELTA(TInputLong)
                            );



syntax highlighted by Code2HTML, v. 0.9.1