// @(#)root/gui:$Name:  $:$Id: TGNumberEntry.cxx,v 1.7 2004/04/22 16:28:29 brun Exp $
// Author: Daniel Sigg   03/09/2001

/*************************************************************************
 * Copyright (C) 1995-2001, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGNumberEntry, TGNumberEntryField and TGNumberFormat                 //
//                                                                      //
// TGNumberEntry is a number entry input widget with up/down buttons.   //
// TGNumberEntryField is a number entry input widget.                   //
// TGNumberFormat contains enum types to specify the numeric format.    //
//                                                                      //
// The number entry widget is based on TGTextEntry but allows only      //
// numerical input. The widget support numerous formats including       //
// integers, hex numbers, real numbers, fixed fraction reals and        //
// time/date formats. The widget also allows to restrict input values   //
// to non-negative or positive numbers and to specify explicit limits.  //
//                                                                      //
// The following styles are supported:                                  //
// kNESInteger:        integer number                                   //
// kNESRealOne:        real number with one digit (no exponent)         //
// kNESRealTwo:        real number with two digits (no exponent)        //
// kNESRealThree:      real number with three digits (no exponent)      //
// kNESRealFour:       real number with four digits (no exponent)       //
// kNESReal:           arbitrary real number                            //
// kNESDegree:         angle in degree:minutes:seconds format           //
// kNESMinSec:         time in minutes:seconds format                   //
// kNESHourMin:        time in hour:minutes format                      //
// kNESHourMinSec:     time in hour:minutes:seconds format              //
// kNESDayMYear:       date in day/month/year format                    //
// kNESMDayYear:       date in month/day/year format                    //
// kNESHex:            hex number                                       //
//                                                                      //
// The following attributes can be specified:                           //
// kNEAAnyNumber:      any number is allowed                            //
// kNEANonNegative:    only non-negative numbers are allowed            //
// kNEAPositive:       only positive numbers are allowed                //
//                                                                      //
// Explicit limits can be specified individually:                       //
// kNELNoLimits:       no limits                                        //
// kNELLimitMin:       lower limit only                                 //
// kNELLimitMax        upper limit only                                 //
// kNELLimitMinMax     both lower and upper limits                      //
//                                                                      //
// TGNumberEntryField is a plain vanilla entry field, whereas           //
// TGNumberEntry adds two small buttons to increase and decrease the    //
// numerical value in the field. The number entry widgets also support  //
// using the up and down cursor keys to change the numerical values.    //
// The step size can be selected with control and shift keys:           //
// --                  small step (1 unit/factor of 3)                  //
// shift               medium step (10 units/factor of 10)              //
// control             large step (100 units/factor of 30)              //
// shift-control       huge step (1000 units/factor of 100)             //
//                                                                      //
// The steps are either linear or logarithmic. The default behaviour    //
// is set when the entry field is created, but it can be changed by     //
// pressing the alt key at the same time.                               //
//                                                                      //
// Changing the number in the widget will generate the event:           //
// kC_TEXTENTRY, kTE_TEXTCHANGED, widget id, 0.                         //
// Hitting the enter key will generate:                                 //
// kC_TEXTENTRY, kTE_ENTER, widget id, 0.                               //
// Hitting the tab key will generate:                                   //
// kC_TEXTENTRY, kTE_TAB, widget id, 0.                                 //
//                                                                      //
//Begin_Html
/*
<img src="numberentry.jpg">
*/
//End_Html
//

#include "TGNumberEntry.h"
#include "KeySymbols.h"
#include "TTimer.h"
#include "TSystem.h"
#include "TGToolTip.h"
#include "Riostream.h"
#include <ctype.h>


ClassImp(TGNumberFormat);
ClassImp(TGNumberEntryField);
ClassImp(TGNumberEntry);



//////////////////////////////////////////////////////////////////////////
//                                                                      //
// Miscellanous routines for handling numeric values <-> strings        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
enum RealStyle {                // Style of real
   kRSInt = 0,                  // Integer
   kRSFrac = 1,                 // Fraction only
   kRSExpo = 2,                 // Exponent only
   kRSFracExpo = 3              // Fraction and Exponent
};

//______________________________________________________________________________
struct RealInfo_t {
   // Style of real
   RealStyle fStyle;
   // Number of fractional digits
   Int_t fFracDigits;
   // Base of fractional digits
   Int_t fFracBase;
   // Integer number
   Int_t fIntNum;
   // Fraction
   Int_t fFracNum;
   // Exponent
   Int_t fExpoNum;
   // Sign
   Int_t fSign;
};

//______________________________________________________________________________
const Double_t kEpsilon = 1E-12;

//______________________________________________________________________________
const Int_t mDays[13] =
    { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

//______________________________________________________________________________
static Long_t Round(Double_t x)
{
   if (x > 0) {
      return (Long_t) (x + 0.5);
   } else if (x < 0) {
      return (Long_t) (x - 0.5);
   } else {
      return 0;
   }
}

//______________________________________________________________________________
static Long_t Truncate(Double_t x)
{
   if (x > 0) {
      return (Long_t) (x + kEpsilon);
   } else if (x < 0) {
      return (Long_t) (x - kEpsilon);
   } else {
      return 0;
   }
}

//______________________________________________________________________________
static Bool_t IsLeapYear(Int_t year)
{
   return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
}

//______________________________________________________________________________
static Bool_t IsGoodChar(char c, TGNumberFormat::EStyle style,
                         TGNumberFormat::EAttribute attr)
{
   if (isdigit(c)) {
      return kTRUE;
   }
   if (isxdigit(c) && (style == TGNumberFormat::kNESHex)) {
      return kTRUE;
   }
   if ((c == '-') && (style == TGNumberFormat::kNESInteger) &&
       (attr == TGNumberFormat::kNEAAnyNumber)) {
      return kTRUE;
   }
   if ((c == '-') &&
       ((style == TGNumberFormat::kNESRealOne) ||
        (style == TGNumberFormat::kNESRealTwo) ||
        (style == TGNumberFormat::kNESRealThree) ||
        (style == TGNumberFormat::kNESRealFour) ||
        (style == TGNumberFormat::kNESReal) ||
        (style == TGNumberFormat::kNESDegree) ||
        (style == TGNumberFormat::kNESMinSec)) &&
       (attr == TGNumberFormat::kNEAAnyNumber)) {
      return kTRUE;
   }
   if ((c == '-') && (style == TGNumberFormat::kNESReal)) {
      return kTRUE;
   }
   if (((c == '.') || (c == ',')) &&
       ((style == TGNumberFormat::kNESRealOne) ||
        (style == TGNumberFormat::kNESRealTwo) ||
        (style == TGNumberFormat::kNESRealThree) ||
        (style == TGNumberFormat::kNESRealFour) ||
        (style == TGNumberFormat::kNESReal) ||
        (style == TGNumberFormat::kNESDegree) ||
        (style == TGNumberFormat::kNESMinSec) ||
        (style == TGNumberFormat::kNESHourMin) ||
        (style == TGNumberFormat::kNESHourMinSec) ||
        (style == TGNumberFormat::kNESDayMYear) ||
        (style == TGNumberFormat::kNESMDayYear))) {
      return kTRUE;
   }
   if ((c == ':') &&
       ((style == TGNumberFormat::kNESDegree) ||
        (style == TGNumberFormat::kNESMinSec) ||
        (style == TGNumberFormat::kNESHourMin) ||
        (style == TGNumberFormat::kNESHourMinSec) ||
        (style == TGNumberFormat::kNESDayMYear) ||
        (style == TGNumberFormat::kNESMDayYear))) {
      return kTRUE;
   }
   if ((c == '/') &&
       ((style == TGNumberFormat::kNESDayMYear) ||
        (style == TGNumberFormat::kNESMDayYear))) {
      return kTRUE;
   }
   if (((c == 'e') || (c == 'E')) && (style == TGNumberFormat::kNESReal)) {
      return kTRUE;
   }
   return kFALSE;
}

//______________________________________________________________________________
static char *EliminateGarbage(char *text,
                              TGNumberFormat::EStyle style,
                              TGNumberFormat::EAttribute attr)
{
   if (text == 0) {
      return 0;
   }
   for (Int_t i = strlen(text) - 1; i >= 0; i--) {
      if (!IsGoodChar(text[i], style, attr)) {
         memmove(text + i, text + i + 1, strlen(text) - i);
      }
   }
   return text;
}

//______________________________________________________________________________
static Long_t IntStr(const char *text)
{
   Long_t l = 0;
   Int_t Sign = 1;
   for (UInt_t i = 0; i < strlen(text); i++) {
      if (text[i] == '-') {
         Sign = -1;
      } else if ((isdigit(text[i])) && (l < 100000000)) {
         l = 10 * l + (text[i] - '0');
      }
   }
   return Sign * l;
}

//______________________________________________________________________________
static char *StrInt(char *text, Long_t i, Int_t Digits)
{
   sprintf(text, "%li", TMath::Abs(i));
   TString s = text;
   while (Digits > s.Length()) {
      s = "0" + s;
   }
   if (i < 0) {
      s = "-" + s;
   }
   strcpy(text, (const char *) s);
   return text;
}

//______________________________________________________________________________
static TString StringInt(Long_t i, Int_t Digits)
{
   char text[256];
   StrInt(text, i, Digits);
   return TString(text);
}

//______________________________________________________________________________
static char *RealToStr(char *text, const RealInfo_t & ri)
{
   char *p = text;
   if (text == 0) {
      return 0;
   }
   strcpy(p, "");
   if (ri.fSign < 0) {
      strcpy(p, "-");
      p++;
   }
   StrInt(p, TMath::Abs(ri.fIntNum), 0);
   p += strlen(p);
   if ((ri.fStyle == kRSFrac) || (ri.fStyle == kRSFracExpo)) {
      strcpy(p, ".");
      p++;
      StrInt(p, TMath::Abs(ri.fFracNum), ri.fFracDigits);
      p += strlen(p);
   }
   if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
      strcpy(p, "e");
      p++;
      StrInt(p, ri.fExpoNum, 0);
      p += strlen(p);
   }
   return text;
}

//______________________________________________________________________________
static Double_t StrToReal(const char *text, RealInfo_t & ri)
{
   char *s;
   char *Frac;
   char *Expo;
   char *Minus;
   char buf[256];

   if ((text == 0) || (strlen(text) == 0)) {
      ri.fStyle = kRSInt;
      ri.fIntNum = 0;
      ri.fSign = 1;
      return 0.0;
   }
   strncpy(buf, text, sizeof(buf) - 1);
   s = buf;
   Frac = strchr(s, '.');
   if (Frac == 0) {
      Frac = strchr(s, ',');
   }
   Expo = strchr(s, 'e');
   Minus = strchr(s, '-');
   if (Expo == 0) {
      Expo = strchr(s, 'E');
   }
   if ((Frac != 0) && (Expo != 0) && (Frac > Expo)) {
      Frac = 0;
   }
   if ((Minus != 0) && ((Expo == 0) || (Minus < Expo))) {
      ri.fSign = -1;
   } else {
      ri.fSign = 1;
   }
   if ((Frac == 0) && (Expo == 0)) {
      ri.fStyle = kRSInt;
   } else if (Frac == 0) {
      ri.fStyle = kRSExpo;
   } else if (Expo == 0) {
      ri.fStyle = kRSFrac;
   } else {
      ri.fStyle = kRSFracExpo;
   }
   if (Frac != 0) {
      *Frac = 0;
      Frac++;
   }
   if (Expo != 0) {
      *Expo = 0;
      Expo++;
   }
   ri.fIntNum = TMath::Abs(IntStr(s));
   if (Expo != 0) {
      ri.fExpoNum = IntStr(Expo);
   } else {
      ri.fExpoNum = 0;
   }
   if (ri.fExpoNum > 999) {
      ri.fExpoNum = 999;
   }
   if (ri.fExpoNum < -999) {
      ri.fExpoNum = -999;
   }
   ri.fFracDigits = 0;
   ri.fFracBase = 1;
   ri.fFracNum = 0;
   if (Frac != 0) {
      for (UInt_t i = 0; i < strlen(Frac); i++) {
         if (isdigit(Frac[i])) {
            if (ri.fFracNum < 100000000) {
               ri.fFracNum = 10 * ri.fFracNum + (Frac[i] - '0');
               ri.fFracDigits++;
               ri.fFracBase *= 10;
            }
         }
      }
   }
   if ((ri.fFracDigits == 0) && (ri.fStyle == kRSFrac)) {
      ri.fStyle = kRSInt;
   }
   if ((ri.fFracDigits == 0) && (ri.fStyle == kRSFracExpo)) {
      ri.fStyle = kRSExpo;
   }
   switch (ri.fStyle) {
   case kRSInt:
      return ri.fSign * ri.fIntNum;
   case kRSFrac:
      return ri.fSign *
          (ri.fIntNum + (Double_t) ri.fFracNum / ri.fFracBase);
   case kRSExpo:
      return ri.fSign * (ri.fIntNum * TMath::Power(10, ri.fExpoNum));
   case kRSFracExpo:
      return ri.fSign * (ri.fIntNum +
                         (Double_t) ri.fFracNum / ri.fFracBase) *
          TMath::Power(10, ri.fExpoNum);
   }
   return 0;
}

//______________________________________________________________________________
static ULong_t HexStrToInt(const char *s)
{
   ULong_t w = 0;
   for (UInt_t i = 0; i < strlen(s); i++) {
      if ((s[i] >= '0') && (s[i] <= '9')) {
         w = 16 * w + (s[i] - '0');
      } else if ((toupper(s[i]) >= 'A') && (toupper(s[i]) <= 'F')) {
         w = 16 * w + (toupper(s[i]) - 'A' + 10);
      }
   }
   return w;
}

//______________________________________________________________________________
static char *IntToHexStr(char *text, ULong_t l)
{
   const char *const Digits = "0123456789ABCDEF";
   char buf[64];
   char *p = buf + 62;
   strcpy(p, "");
   while (l > 0) {
      *(--p) = Digits[l % 16];
      l /= 16;
   }
   if (strlen(p) == 0) {
      strcpy(text, "0");
   } else {
      strcpy(text, p);
   }
   return text;
}

//______________________________________________________________________________
static char *mIntToStr(char *text, Long_t l, Int_t Digits)
{
   TString s;
   Int_t Base;
   switch (Digits) {
   case 0:
      Base = 1;
      break;
   case 1:
      Base = 10;
      break;
   case 2:
      Base = 100;
      break;
   case 3:
      Base = 1000;
      break;
   default:
   case 4:
      Base = 10000;
      break;
   }
   s = StringInt(TMath::Abs(l) / Base, 0) + "." +
       StringInt(TMath::Abs(l) % Base, Digits);
   if (l < 0) {
      s = "-" + s;
   }
   strcpy(text, (const char *) s);
   return text;
}

//______________________________________________________________________________
static char *dIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
{
   TString s;
   if (Sec) {
      s = StringInt(TMath::Abs(l) / 3600, 0) + Del +
          StringInt((TMath::Abs(l) % 3600) / 60, 2) + Del +
          StringInt(TMath::Abs(l) % 60, 2);
   } else {
      s = StringInt(TMath::Abs(l) / 60, 0) + Del +
          StringInt(TMath::Abs(l) % 60, 2);
   }
   if (l < 0) {
      s = "-" + s;
   }
   strcpy(text, (const char *) s);
   return text;
}

//______________________________________________________________________________
static void GetNumbers(const char *s, Int_t & Sign,
                       Long_t & n1, Int_t maxd1,
                       Long_t & n2, Int_t maxd2,
                       Long_t & n3, Int_t maxd3, const char *Delimiters)
{
   Long_t n;
   Long_t d = 0;
   Sign = +1;
   n1 = 0;
   n2 = 0;
   n3 = 0;
   if (*s == '-') {
      Sign = -1;
      s++;
   }
   if (!isdigit(*s)) {
      return;
   }
   while ((*s != 0) && ((strchr(Delimiters, *s) == 0) || (maxd2 == 0))) {
      if (isdigit(*s) && (d < maxd1)) {
         if (n1 < 100000000) {
            n1 = 10 * n1 + (*s - '0');
         }
         d++;
      }
      s++;
   }
   if (strcspn(s, Delimiters) == strlen(s)) {
      return;
   }
   Int_t dummy = 0;
   GetNumbers(s + 1, dummy, n2, maxd2, n3, maxd3, n, d, Delimiters);
}

//______________________________________________________________________________
static Long_t GetSignificant(Long_t l, Int_t Max)
{
   while (TMath::Abs(l) >= Max) {
      l /= 10;
   }
   return l;
}

//______________________________________________________________________________
static void AppendFracZero(char *text, Int_t Digits)
{
   char *p;
   Int_t Found = 0;
   p = strchr(text, '.');
   if (p == 0) {
      p = strchr(text, ',');
   }
   if (p == 0) {
      return;
   }
   p++;
   for (UInt_t i = 0; i < strlen(p); i++) {
      if (isdigit(*p)) {
         Found++;
      }
   }
   while (Found < Digits) {
      strcpy(p + strlen(p), "0");
      Found++;
   }
}

//______________________________________________________________________________
static Long_t MakeDateNumber(const char * /*text*/, Long_t Day,
                             Long_t Month, Long_t Year)
{
   Day = TMath::Abs(Day);
   Month = TMath::Abs(Month);
   Year = TMath::Abs(Year);
   if (Year < 100) {
      Year += 2000;
   }
   Month = GetSignificant(Month, 100);
   if (Month > 12)
      Month = 12;
   if (Month == 0)
      Month = 1;
   Day = GetSignificant(Day, 100);
   if (Day == 0)
      Day = 1;
   if (Day > mDays[Month])
      Day = mDays[Month];
   if ((Month == 2) && (Day > 28) && !IsLeapYear(Year))
      Day = 28;
   return 10000 * Year + 100 * Month + Day;
}

//______________________________________________________________________________
static Long_t TranslateToNum(const char *text,
                             TGNumberFormat::EStyle style, RealInfo_t & ri)
{
   Long_t n1;
   Long_t n2;
   Long_t n3;
   Int_t sign;
   switch (style) {
   case TGNumberFormat::kNESInteger:
      GetNumbers(text, sign, n1, 12, n2, 0, n3, 0, "");
      return sign * n1;
   case TGNumberFormat::kNESRealOne:
      GetNumbers(text, sign, n1, 12, n2, 1, n3, 0, ".,");
      return sign * (10 * n1 + GetSignificant(n2, 10));
   case TGNumberFormat::kNESRealTwo:
      {
         char buf[256];
         strncpy(buf, text, sizeof(buf) - 1);
         AppendFracZero(buf, 2);
         GetNumbers(buf, sign, n1, 12, n2, 2, n3, 0, ".,");
         return sign * (100 * n1 + GetSignificant(n2, 100));
      }
   case TGNumberFormat::kNESRealThree:
      {
         char buf[256];
         strncpy(buf, text, sizeof(buf) - 1);
         AppendFracZero(buf, 3);
         GetNumbers(buf, sign, n1, 12, n2, 3, n3, 0, ".,");
         return sign * (1000 * n1 + GetSignificant(n2, 1000));
      }
   case TGNumberFormat::kNESRealFour:
      {
         char buf[256];
         strncpy(buf, text, sizeof(buf) - 1);
         AppendFracZero(buf, 4);
         GetNumbers(buf, sign, n1, 12, n2, 4, n3, 0, ".,");
         return sign * (10000 * n1 + GetSignificant(n2, 10000));
      }
   case TGNumberFormat::kNESReal:
      return (Long_t) StrToReal(text, ri);
   case TGNumberFormat::kNESDegree:
      GetNumbers(text, sign, n1, 12, n2, 2, n3, 2, ".,:");
      return sign * (3600 * n1 + 60 * GetSignificant(n2, 60) +
                     GetSignificant(n3, 60));
   case TGNumberFormat::kNESHourMinSec:
      GetNumbers(text, sign, n1, 12, n2, 2, n3, 2, ".,:");
      return 3600 * n1 + 60 * GetSignificant(n2, 60) +
          GetSignificant(n3, 60);
   case TGNumberFormat::kNESMinSec:
      GetNumbers(text, sign, n1, 12, n2, 2, n3, 0, ".,:");
      return sign * (60 * n1 + GetSignificant(n2, 60));
   case TGNumberFormat::kNESHourMin:
      GetNumbers(text, sign, n1, 12, n2, 2, n3, 0, ".,:");
      return 60 * n1 + GetSignificant(n2, 60);
   case TGNumberFormat::kNESDayMYear:
      GetNumbers(text, sign, n1, 2, n2, 2, n3, 4, ".,/");
      return MakeDateNumber(text, n1, n2, n3);
   case TGNumberFormat::kNESMDayYear:
      GetNumbers(text, sign, n2, 2, n1, 2, n3, 4, ".,/");
      return MakeDateNumber(text, n1, n2, n3);
   case TGNumberFormat::kNESHex:
      return HexStrToInt(text);
   }
   return 0;
}

//______________________________________________________________________________
static char *TranslateToStr(char *text, Long_t l,
                            TGNumberFormat::EStyle style, const RealInfo_t & ri)
{
   switch (style) {
   case TGNumberFormat::kNESInteger:
      return StrInt(text, l, 0);
   case TGNumberFormat::kNESRealOne:
      return mIntToStr(text, l, 1);
   case TGNumberFormat::kNESRealTwo:
      return mIntToStr(text, l, 2);
   case TGNumberFormat::kNESRealThree:
      return mIntToStr(text, l, 3);
   case TGNumberFormat::kNESRealFour:
      return mIntToStr(text, l, 4);
   case TGNumberFormat::kNESReal:
      return RealToStr(text, ri);
   case TGNumberFormat::kNESDegree:
      return dIntToStr(text, l, kTRUE, '.');
   case TGNumberFormat::kNESHourMinSec:
      return dIntToStr(text, l % (24 * 3600), kTRUE, ':');
   case TGNumberFormat::kNESMinSec:
      return dIntToStr(text, l, kFALSE, ':');
   case TGNumberFormat::kNESHourMin:
      return dIntToStr(text, l % (24 * 60), kFALSE, ':');
   case TGNumberFormat::kNESDayMYear:
      {
         TString date =
             StringInt(TMath::Abs(l) % 100, 0) + "/" +
             StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
             StringInt(TMath::Abs(l) / 10000, 0);
         return strcpy(text, (const char *) date);
      }
   case TGNumberFormat::kNESMDayYear:
      {
         TString date =
             StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
             StringInt(TMath::Abs(l) % 100, 0) + "/" +
             StringInt(TMath::Abs(l) / 10000, 0);
         return strcpy(text, (const char *) date);
      }
   case TGNumberFormat::kNESHex:
      return IntToHexStr(text, (ULong_t) l);
   }
   return 0;
}

//______________________________________________________________________________
static Double_t RealToDouble(const RealInfo_t ri)
{
   // Convert to double format;
   switch (ri.fStyle) {
      // Integer type real
   case kRSInt:
      return (Double_t) ri.fSign * ri.fIntNum;
      // Fraction type real
   case kRSFrac:
      return (Double_t) ri.fSign * ((Double_t) TMath::Abs(ri.fIntNum) +
                                    (Double_t) ri.fFracNum / ri.fFracBase);
      // Exponent only
   case kRSExpo:
      return (Double_t) ri.fSign * ri.fIntNum *
          TMath::Power(10, ri.fExpoNum);
      // Fraction and exponent
   case kRSFracExpo:
      return (Double_t) ri.fSign * ((Double_t) TMath::Abs(ri.fIntNum) +
                                    (Double_t) ri.fFracNum /
                                    ri.fFracBase) * TMath::Power(10,
                                                                 ri.fExpoNum);
   }
   return 0;
}

//______________________________________________________________________________
static void CheckMinMax(Long_t & l, TGNumberFormat::EStyle style,
                        TGNumberFormat::ELimit limits,
                        Double_t min, Double_t max)
{
   if ((limits == TGNumberFormat::kNELNoLimits) ||
       (style == TGNumberFormat::kNESReal)) {
      return;
   }
   // check min
   if ((limits == TGNumberFormat::kNELLimitMin) ||
       (limits == TGNumberFormat::kNELLimitMinMax)) {
      Long_t lower;
      switch (style) {
      case TGNumberFormat::kNESRealOne:
         lower = Round(10.0 * min);
         break;
      case TGNumberFormat::kNESRealTwo:
         lower = Round(100.0 * min);
         break;
      case TGNumberFormat::kNESRealThree:
         lower = Round(1000.0 * min);
         break;
      case TGNumberFormat::kNESRealFour:
         lower = Round(10000.0 * min);
         break;
      case TGNumberFormat::kNESHex:
         lower = (ULong_t) Round(min);
         break;
      default:
         lower = Round(min);
         break;
      }
      if (style != TGNumberFormat::kNESHex) {
         if (l < lower)
            l = lower;
      } else {
         if (lower < 0)
            lower = 0;
         if ((ULong_t) l < (ULong_t) lower)
            l = lower;
      }
   }
   // check max
   if ((limits == TGNumberFormat::kNELLimitMax) ||
       (limits == TGNumberFormat::kNELLimitMinMax)) {
      Long_t upper;
      switch (style) {
      case TGNumberFormat::kNESRealOne:
         upper = Round(10.0 * max);
         break;
      case TGNumberFormat::kNESRealTwo:
         upper = Round(100.0 * max);
         break;
      case TGNumberFormat::kNESRealThree:
         upper = Round(1000.0 * max);
         break;
      case TGNumberFormat::kNESRealFour:
         upper = Round(10000.0 * max);
         break;
      case TGNumberFormat::kNESHex:
         upper = (ULong_t) Round(max);
         break;
      default:
         upper = Round(max);
         break;
      }
      if (style != TGNumberFormat::kNESHex) {
         if (l > upper)
            l = upper;
      } else {
         if (upper < 0)
            upper = 0;
         if ((ULong_t) l > (ULong_t) upper)
            l = upper;
      }
   }
}

//______________________________________________________________________________
static void IncreaseReal(RealInfo_t & ri, Double_t mag, Bool_t logstep,
                         TGNumberFormat::ELimit limits =
                         TGNumberFormat::kNELNoLimits, Double_t min = 0,
                         Double_t max = 1)
{
   // Convert to double format;
   Double_t x = RealToDouble(ri);

   // apply step
   if (logstep) {
      x *= mag;
   } else {
      switch (ri.fStyle) {
      case kRSInt:
         x = x + mag;
         break;
      case kRSFrac:
         x = x + mag / ri.fFracBase;
         break;
      case kRSExpo:
         x = x + mag * TMath::Power(10, ri.fExpoNum);
         break;
      case kRSFracExpo:
         x = x + (mag / ri.fFracBase) * TMath::Power(10, ri.fExpoNum);
         break;
      }
   }
   // check min
   if ((limits == TGNumberFormat::kNELLimitMin) ||
       (limits == TGNumberFormat::kNELLimitMinMax)) {
      if (x < min)
         x = min;
   }
   // check max
   if ((limits == TGNumberFormat::kNELLimitMax) ||
       (limits == TGNumberFormat::kNELLimitMinMax)) {
      if (x > max)
         x = max;
   }
   // check format after log step
   if ((x != 0) && logstep && (TMath::Abs(mag) > kEpsilon)) {
      for (int j = 0; j < 10; j++) {
         // Integer: special case
         if ((ri.fStyle == kRSInt) && (TMath::Abs(x) < 1) &&
             (TMath::Abs(x) > kEpsilon)) {
            ri.fStyle = kRSFrac;
            ri.fFracDigits = 1;
            ri.fFracBase = 10;
            continue;
         }
         if ((ri.fStyle == kRSInt) && (TMath::Abs(x) > 10000)) {
            ri.fStyle = kRSFracExpo;
            ri.fExpoNum = 4;
            ri.fFracDigits = 4;
            ri.fFracBase = 10000;
            Long_t rest = Round(TMath::Abs(x)) % 10000;
            for (int k = 0; k < 4; k++) {
               if (rest % 10 != 0) {
                  break;
               }
               ri.fFracDigits--;
               ri.fFracBase /= 10;
               rest /= 10;
            }
            if (ri.fFracDigits == 0) {
               ri.fStyle = kRSExpo;
            }
            continue;
         }
         if (ri.fStyle == kRSInt)
            break;

         // caluclate first digit
         Double_t y;
         if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
            y = TMath::Abs(x) * TMath::Power(10, -ri.fExpoNum);
         } else {
            y = TMath::Abs(x);
         }
         // adjust exponent if num < 1
         if ((Truncate(y) == 0) && (y > 0.001)) {
            if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
               ri.fExpoNum--;
            } else {
               ri.fStyle = kRSFracExpo;
               ri.fExpoNum = -1;
            }
            continue;
         }
         // adjust exponent if num > 10
         if (Truncate(y) >= 10) {
            if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
               ri.fExpoNum++;
            } else {
               ri.fStyle = kRSFracExpo;
               ri.fExpoNum = 1;
            }
            continue;
         }
         break;
      }
   }
   // convert back to RealInfo_t
   switch (ri.fStyle) {
      // Integer type real
   case kRSInt:
      {
         ri.fSign = (x < 0) ? -1 : 1;
         ri.fIntNum = Round(TMath::Abs(x));
         break;
      }
      // Fraction type real
   case kRSFrac:
      {
         ri.fSign = (x < 0) ? -1 : 1;
         ri.fIntNum = Truncate(TMath::Abs(x));
         ri.fFracNum = Round((TMath::Abs(x) - TMath::Abs(ri.fIntNum)) * ri.fFracBase);
         break;
      }
      // Exponent only
   case kRSExpo:
      {
         ri.fSign = (x < 0) ? -1 : 1;
         ri.fIntNum = Round(TMath::Abs(x) * TMath::Power(10, -ri.fExpoNum));
         if (ri.fIntNum == 0) {
            ri.fStyle = kRSInt;
         }
         break;
      }
      // Fraction and exponent
   case kRSFracExpo:
      {
         ri.fSign = (x < 0) ? -1 : 1;
         Double_t y = TMath::Abs(x) * TMath::Power(10, -ri.fExpoNum);
         ri.fIntNum = Truncate(y);
         ri.fFracNum = Round((y - TMath::Abs(ri.fIntNum)) * ri.fFracBase);
         if ((ri.fIntNum == 0) && (ri.fFracNum == 0)) {
            ri.fStyle = kRSFrac;
         }
         break;
      }
   }

   // check if the back conversion violated limits
   if (limits != TGNumberFormat::kNELNoLimits) {
      x = RealToDouble(ri);
      // check min
      if ((limits == TGNumberFormat::kNELLimitMin) ||
          (limits == TGNumberFormat::kNELLimitMinMax)) {
         if (x < min) {
            char text[256];
            sprintf(text, "%g", min);
            StrToReal(text, ri);
         }
      }
      // check max
      if ((limits == TGNumberFormat::kNELLimitMax) ||
          (limits == TGNumberFormat::kNELLimitMinMax)) {
         if (x > max) {
            char text[256];
            sprintf(text, "%g", max);
            StrToReal(text, ri);
         }
      }
   }
}

//______________________________________________________________________________
static void IncreaseDate(Long_t & l, TGNumberFormat::EStepSize step, Int_t sign)
{
   Long_t year;
   Long_t month;
   Long_t day;

   // get year/month/day format
   year = l / 10000;
   month = (TMath::Abs(l) / 100) % 100;
   if (month > 12)
      month = 12;
   if (month == 0)
      month = 1;
   day = TMath::Abs(l) % 100;
   if (day > mDays[month])
      day = mDays[month];
   if ((month == 2) && (day > 28) && !IsLeapYear(year)) {
      day = 28;
   }
   if (day == 0)
      day = 0;

   // apply step
   if (step == TGNumberFormat::kNSSHuge) {
      year += sign * 10;
   } else if (step == TGNumberFormat::kNSSLarge) {
      year += sign;
   } else if (step == TGNumberFormat::kNSSMedium) {
      month += sign;
      if (month > 12) {
         month = 1;
         year++;
      }
      if (month < 1) {
         month = 12;
         year--;
      }
   } else if (step == TGNumberFormat::kNSSSmall) {
      day += sign;
      if ((sign > 0) &&
          ((day > mDays[month]) ||
           ((month == 2) && (day > 28) && !IsLeapYear(year)))) {
         day = 1;
         month++;
         if (month > 12) {
            month = 1;
            year++;
         }
      }
      if ((sign < 0) && (day == 0)) {
         month--;
         if (month < 1) {
            month = 12;
            year--;
         }
         day = mDays[month];
      }
   }
   // check again for valid date
   if (year < 0)
      year = 0;
   if (day > mDays[month])
      day = mDays[month];
   if ((month == 2) && (day > 28) && !IsLeapYear(year)) {
      day = 28;
   }
   l = 10000 * year + 100 * month + day;
}




//______________________________________________________________________________
TGNumberEntryField::TGNumberEntryField(const TGWindow * p, Int_t id,
                                       Double_t val, GContext_t norm,
                                       FontStruct_t font, UInt_t option,
                                       ULong_t back)
   : TGTextEntry(p, new TGTextBuffer(), id, norm, font, option, back),
     fNeedsVerification(kFALSE), fNumStyle(kNESReal), fNumAttr(kNEAAnyNumber),
     fNumLimits(kNELNoLimits)
{
   // Constructs a number entry field
   fStepLog = kFALSE;
   SetAlignment(kTextRight);
   SetNumber(val);
}

//______________________________________________________________________________
TGNumberEntryField::TGNumberEntryField(const TGWindow * parent,
                                       Int_t id, Double_t val,
                                       EStyle style, EAttribute attr,
                                       ELimit limits, Double_t min,
                                       Double_t max)
   : TGTextEntry(parent, "", id), fNeedsVerification(kFALSE), fNumStyle(style),
     fNumAttr(attr), fNumLimits(limits), fNumMin(min), fNumMax(max)
{
   // Constructs a number entry field
   fStepLog = kFALSE;
   SetAlignment(kTextRight);
   SetNumber(val);
}

//______________________________________________________________________________
void TGNumberEntryField::SetNumber(Double_t val)
{
   // Set the numeric value (floating point representation)
   switch (fNumStyle) {
   case kNESInteger:
      SetIntNumber(Round(val));
      break;
   case kNESRealOne:
      SetIntNumber(Round(10.0 * val));
      break;
   case kNESRealTwo:
      SetIntNumber(Round(100.0 * val));
      break;
   case kNESRealThree:
      SetIntNumber(Round(1000.0 * val));
      break;
   case kNESRealFour:
      SetIntNumber(Round(10000.0 * val));
      break;
   case kNESReal:
      {
         char text[256];
         sprintf(text, "%g", val);
         SetText(text);
         break;
      }
   case kNESDegree:
      SetIntNumber(Round(val));
      break;
   case kNESHourMinSec:
      SetIntNumber(Round(val));
      break;
   case kNESMinSec:
      SetIntNumber(Round(val));
      break;
   case kNESHourMin:
      SetIntNumber(Round(val));
      break;
   case kNESDayMYear:
      SetIntNumber(Round(val));
      break;
   case kNESMDayYear:
      SetIntNumber(Round(val));
      break;
   case kNESHex:
      SetIntNumber((UInt_t) (TMath::Abs(val) + 0.5));
      break;
   }
}

//______________________________________________________________________________
void TGNumberEntryField::SetIntNumber(Long_t val)
{
   // Set the numeric value (integer representation)
   char text[256];
   RealInfo_t ri;
   if (fNumStyle == kNESReal) {
      TranslateToStr(text, val, kNESInteger, ri);
   } else {
      TranslateToStr(text, val, fNumStyle, ri);
   }
   SetText(text);
}

//______________________________________________________________________________
void TGNumberEntryField::SetTime(Int_t hour, Int_t min, Int_t sec)
{
   // Set the numeric value (time format)
   switch (fNumStyle) {
   case kNESHourMinSec:
      SetIntNumber(3600 * TMath::Abs(hour) + 60 * TMath::Abs(min) +
                   TMath::Abs(sec));
      break;
   case kNESMinSec:
      {
         SetIntNumber(60 * min + sec);
         break;
      }
   case kNESHourMin:
      SetIntNumber(60 * TMath::Abs(hour) + TMath::Abs(min));
      break;
   default:
      break;
   }
}

//______________________________________________________________________________
void TGNumberEntryField::SetDate(Int_t year, Int_t month, Int_t day)
{
   // Set the numeric value (date format)
   switch (fNumStyle) {
   case kNESDayMYear:
   case kNESMDayYear:
      {
         SetIntNumber(10000 * TMath::Abs(year) + 100 * TMath::Abs(month) +
                      TMath::Abs(day));
      }
   default:
      {
         break;
      }
   }
}

//______________________________________________________________________________
void TGNumberEntryField::SetHexNumber(ULong_t val)
{
   // Set the numeric value (hex format)
   SetIntNumber((Long_t) val);
}

//______________________________________________________________________________
void TGNumberEntryField::SetText(const char *text)
{
   // Set the value (text format)
   char buf[256];
   strncpy(buf, text, sizeof(buf) - 1);
   EliminateGarbage(buf, fNumStyle, fNumAttr);
   TGTextEntry::SetText(buf);
   fNeedsVerification = kFALSE;
}

//______________________________________________________________________________
Double_t TGNumberEntryField::GetNumber() const
{
   // Get the numeric value (floating point representation)
   switch (fNumStyle) {
   case kNESInteger:
      return (Double_t) GetIntNumber();
   case kNESRealOne:
      return (Double_t) GetIntNumber() / 10.0;
   case kNESRealTwo:
      return (Double_t) GetIntNumber() / 100.0;
   case kNESRealThree:
      return (Double_t) GetIntNumber() / 1000.0;
   case kNESRealFour:
      return (Double_t) GetIntNumber() / 10000.0;
   case kNESReal:
      {
         char text[256];
         RealInfo_t ri;
         strcpy(text, GetText());
         return StrToReal(text, ri);
      }
   case kNESDegree:
      return (Double_t) GetIntNumber();
   case kNESHourMinSec:
      return (Double_t) GetIntNumber();
   case kNESMinSec:
      return (Double_t) GetIntNumber();
   case kNESHourMin:
      return (Double_t) GetIntNumber();
   case kNESDayMYear:
      return (Double_t) GetIntNumber();
   case kNESMDayYear:
      return (Double_t) GetIntNumber();
   case kNESHex:
      return (Double_t) (ULong_t) GetIntNumber();
   }
   return 0;
}

//______________________________________________________________________________
Long_t TGNumberEntryField::GetIntNumber() const
{
   // Get the numeric value (integer representation)
   RealInfo_t ri;
   return TranslateToNum(GetText(), fNumStyle, ri);
}

//______________________________________________________________________________
void TGNumberEntryField::GetTime(Int_t & hour, Int_t & min, Int_t & sec) const
{
   // Get the numeric value (time format)
   switch (fNumStyle) {
   case kNESHourMinSec:
      {
         Long_t l = GetIntNumber();
         hour = TMath::Abs(l) / 3600;
         min = (TMath::Abs(l) % 3600) / 60;
         sec = TMath::Abs(l) % 60;
         break;
      }
   case kNESMinSec:
      {
         Long_t l = GetIntNumber();
         hour = 0;
         min = TMath::Abs(l) / 60;
         sec = TMath::Abs(l) % 60;
         if (l < 0) {
            min *= -1;
            sec *= -1;
         }
         break;
      }
   case kNESHourMin:
      {
         Long_t l = GetIntNumber();
         hour = TMath::Abs(l) / 60;
         min = TMath::Abs(l) % 60;
         sec = 0;
         break;
      }
   default:
      {
         hour = 0;
         min = 0;
         sec = 0;
         break;
      }
   }
}

//______________________________________________________________________________
void TGNumberEntryField::GetDate(Int_t & year, Int_t & month, Int_t & day) const
{
   // Get the numeric value (date format)
   switch (fNumStyle) {
   case kNESDayMYear:
   case kNESMDayYear:
      {
         Long_t l = GetIntNumber();
         year = l / 10000;
         month = (l % 10000) / 100;
         day = l % 100;
         break;
      }
   default:
      {
         year = 0;
         month = 0;
         day = 0;
         break;
      }
   }
}

//______________________________________________________________________________
ULong_t TGNumberEntryField::GetHexNumber() const
{
   // Get the numeric value (hex format)
   return (ULong_t) GetIntNumber();
}

//______________________________________________________________________________
Int_t TGNumberEntryField::GetCharWidth(const char *text) const
{
   // Get the text width in pixels
   return gVirtualX->TextWidth(fFontStruct, text, strlen(text));
}

//______________________________________________________________________________
void TGNumberEntryField::IncreaseNumber(EStepSize step,
                                        Int_t stepsign, Bool_t logstep)
{
   // Increase the number value
   Long_t l;
   RealInfo_t ri;
   Long_t mag = 0;
   Double_t rmag = 0.0;
   Int_t sign = stepsign;

   // svae old text field
   TString oldtext = GetText();
   // Get number
   if (fNumStyle != kNESReal) {
      l = GetIntNumber();
   } else {
      StrToReal(oldtext, ri);
   }

   // magnitude of step
   if ((fNumStyle == kNESDegree) || (fNumStyle == kNESHourMinSec) ||
       (fNumStyle == kNESMinSec) || (fNumStyle == kNESHourMin) ||
       (fNumStyle == kNESDayMYear) || (fNumStyle == kNESMDayYear) ||
       (fNumStyle == kNESHex)) {
      logstep = kFALSE;
      switch (step) {
      case kNSSSmall:
         mag = 1;
         break;
      case kNSSMedium:
         mag = 10;
         break;
      case kNSSLarge:
         mag = 100;
         break;
      case kNSSHuge:
         mag = 1000;
         break;
      }
   } else {
      Int_t msd = TMath::Abs((fNumStyle == kNESReal) ? ri.fIntNum : l);
      while (msd >= 10)
         msd /= 10;
      Bool_t odd = (msd < 3);
      if (sign < 0)
         odd = !odd;
      switch (step) {
      case kNSSSmall:
         rmag = (!logstep) ? 1. : (odd ? 3. : 10. / 3.);
         break;
      case kNSSMedium:
         rmag = (!logstep) ? 10. : 10.;
         break;
      case kNSSLarge:
         rmag = (!logstep) ? 100. : (odd ? 30. : 100. / 3.);
         break;
      case kNSSHuge:
         rmag = (!logstep) ? 1000. : 100.;
         break;
      }
      if (sign < 0)
         rmag = logstep ? 1. / rmag : -rmag;
   }

   // sign of step
   if (sign == 0) {
      logstep = kFALSE;
      rmag = 0;
      mag = 0;
   } else {
      sign = (sign > 0) ? 1 : -1;
   }
   // add/multiply step
   switch (fNumStyle) {
   case kNESInteger:
   case kNESRealOne:
   case kNESRealTwo:
   case kNESRealThree:
   case kNESRealFour:
      {
         l = logstep ? Round(l * rmag) : Round(l + rmag);
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         if ((l < 0) && (fNumAttr == kNEANonNegative))
            l = 0;
         if ((l <= 0) && (fNumAttr == kNEAPositive))
            l = 1;
         break;
      }
   case kNESReal:
      {
         IncreaseReal(ri, rmag, logstep, fNumLimits, fNumMin, fNumMax);
         if (((fNumAttr == kNEANonNegative) ||
              (fNumAttr == kNEAPositive)) && (ri.fSign < 0)) {
            ri.fIntNum = 0;
            ri.fFracNum = 0;
            ri.fExpoNum = 0;
            ri.fSign = 1;
         }
         break;
      }
   case kNESDegree:
      {
         if (mag > 60)
            l += sign * 36 * mag;
         else if (mag > 6)
            l += sign * 6 * mag;
         else
            l += sign * mag;
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         if ((l < 0) && (fNumAttr == kNEANonNegative))
            l = 0;
         if ((l <= 0) && (fNumAttr == kNEAPositive))
            l = 1;
         break;
      }
   case kNESHourMinSec:
      {
         if (mag > 60)
            l += sign * 36 * mag;
         else if (mag > 6)
            l += sign * 6 * mag;
         else
            l += sign * mag;
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         if (l < 0)
            l = (24 * 3600) - ((-l) % (24 * 3600));
         if (l > 0)
            l = l % (24 * 3600);
         break;
      }
   case kNESMinSec:
      {
         if (mag > 6)
            l += sign * 6 * mag;
         else
            l += sign * mag;
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         if ((l < 0) && (fNumAttr == kNEANonNegative))
            l = 0;
         if ((l <= 0) && (fNumAttr == kNEAPositive))
            l = 1;
         break;
      }
   case kNESHourMin:
      {
         if (mag > 6)
            l += sign * 6 * mag;
         else
            l += sign * mag;
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         if (l < 0)
            l = (24 * 60) - ((-l) % (24 * 60));
         if (l > 0)
            l = l % (24 * 60);
         break;
      }
   case kNESDayMYear:
   case kNESMDayYear:
      {
         IncreaseDate(l, step, sign);
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         break;
      }
   case kNESHex:
      {
         ULong_t ll = (ULong_t) l;
         if (mag > 500)
            ll += sign * 4096 * mag / 1000;
         else if (mag > 50)
            ll += sign * 256 * mag / 100;
         else if (mag > 5)
            ll += sign * 16 * mag / 10;
         else
            ll += sign * mag;
         l = (Long_t) ll;
         CheckMinMax(l, fNumStyle, fNumLimits, fNumMin, fNumMax);
         break;
      }
   }
   if (fNumStyle != kNESReal) {
      SetIntNumber(l);
   } else {
      char buf[256];
      RealToStr(buf, ri);
      SetText(buf);
   }
}

//______________________________________________________________________________
void TGNumberEntryField::SetFormat(EStyle style, EAttribute attr)
{
   // Set the numerical format

   Double_t val = GetNumber();
   fNumStyle = style;
   fNumAttr = attr;
   if ((fNumAttr != kNEAAnyNumber) && (val < 0))
      val = 0;
   SetNumber(val);
   // make sure we have a valid number by increasaing it by 0
   IncreaseNumber(kNSSSmall, 0, kFALSE);
}

//______________________________________________________________________________
void TGNumberEntryField::SetLimits(ELimit limits,
                                   Double_t min, Double_t max)
{
   // Set the numerical limits.

   Double_t val = GetNumber();
   fNumLimits = limits;
   fNumMin = min;
   fNumMax = max;
   SetNumber(val);
   // make sure we have a valid number by increasaing it by 0
   IncreaseNumber(kNSSSmall, 0, kFALSE);
}

//______________________________________________________________________________
void TGNumberEntryField::SetState(Bool_t state)
{
   // Set the active state
   if (!state && fNeedsVerification) {
      // make sure we have a valid number by increasaing it by 0
      IncreaseNumber(kNSSSmall, 0, kFALSE);
   }
   TGTextEntry::SetState(state);
}

//______________________________________________________________________________
Bool_t TGNumberEntryField::HandleKey(Event_t * event)
{
   // Handle keys

   if (!IsEnabled()) {
      return TGTextEntry::HandleKey(event);
   }

   Int_t n;
   char tmp[10];
   UInt_t keysym;
   gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
   n = strlen(tmp);

   // intercept up key
   if ((EKeySym) keysym == kKey_Up) {
      // Get log step / alt key
      Bool_t logstep = fStepLog;
      if (event->fState & kKeyMod1Mask)
         logstep = !logstep;
      // shift-cntrl-up
      if ((event->fState & kKeyShiftMask) &&
          (event->fState & kKeyControlMask)) {
         IncreaseNumber(kNSSHuge, 1, logstep);
      }
      // cntrl-up
      else if (event->fState & kKeyControlMask) {
         IncreaseNumber(kNSSLarge, 1, logstep);
      }
      // shift-up
      else if (event->fState & kKeyShiftMask) {
         IncreaseNumber(kNSSMedium, 1, logstep);
      }
      // up
      else {
         IncreaseNumber(kNSSSmall, 1, logstep);
      }
      return kTRUE;
   }
   // intercept down key
   else if ((EKeySym) keysym == kKey_Down) {
      // Get log step / alt key
      Bool_t logstep = fStepLog;
      if (event->fState & kKeyMod1Mask)
         logstep = !logstep;
      // shift-cntrl-down
      if ((event->fState & kKeyShiftMask) &&
          (event->fState & kKeyControlMask)) {
         IncreaseNumber(kNSSHuge, -1, logstep);
      }
      // cntrl-down
      else if (event->fState & kKeyControlMask) {
         IncreaseNumber(kNSSLarge, -1, logstep);
      }
      // shift-down
      else if (event->fState & kKeyShiftMask) {
         IncreaseNumber(kNSSMedium, -1, logstep);
      }
      // down
      else {
         IncreaseNumber(kNSSSmall, -1, logstep);
      }
      return kTRUE;
   }
   // intercept printable characters
   else if (n && (keysym < 127) && (keysym >= 32) &&
            ((EKeySym) keysym != kKey_Delete) &&
            ((EKeySym) keysym != kKey_Backspace) &&
            ((event->fState & kKeyControlMask) == 0)) {
      if (IsGoodChar(tmp[0], fNumStyle, fNumAttr)) {
         return TGTextEntry::HandleKey(event);
      } else {
         return kTRUE;
      }
   }
   // otherwise use default behaviour
   else {
      return TGTextEntry::HandleKey(event);
   }
}

//______________________________________________________________________________
Bool_t TGNumberEntryField::HandleFocusChange(Event_t * event)
{
   // Handle focus change

   if (IsEnabled() && fNeedsVerification &&
       (event->fCode == kNotifyNormal) &&
       (event->fState != kNotifyPointer) && (event->fType == kFocusOut)) {
      // make sure we have a valid number by increasing it by 0
      IncreaseNumber(kNSSSmall, 0, kFALSE);
   }

   return TGTextEntry::HandleFocusChange(event);
}

//______________________________________________________________________________
void TGNumberEntryField::TextChanged(const char *text)
{
   // Text has changed message
   TGTextEntry::TextChanged(text);
   fNeedsVerification = kTRUE;
}

//______________________________________________________________________________
void TGNumberEntryField::ReturnPressed()
{
   // Return was pressed
   if (fNeedsVerification) {
      // make sure we have a valid number by increasing it by 0
      IncreaseNumber(kNSSSmall, 0, kFALSE);
   }
   TGTextEntry::ReturnPressed();
}

//______________________________________________________________________________
void TGNumberEntryField::Layout()
{
   // Layout
   if (GetAlignment() == kTextRight) {
      End(kFALSE);
   } else {
      Home(kFALSE);
   }
}



//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGNumberEntryLayout                                                  //
//                                                                      //
// Layout manager for number entry widget                               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
class TGNumberEntryLayout : public TGLayoutManager {
protected:
   TGNumberEntry *fBox;        // pointer to numeric control box

public:
   TGNumberEntryLayout(TGNumberEntry *box) { fBox = box; }
   virtual void Layout();
   virtual TGDimension GetDefaultSize() const;
};

//______________________________________________________________________________
void TGNumberEntryLayout::Layout()
{
   if (fBox == 0) {
      return;
   }
   UInt_t w = fBox->GetWidth();
   UInt_t h = fBox->GetHeight();
   UInt_t upw = 2 * h / 3;
   UInt_t uph = h / 2;
   Int_t upx = (w > h) ? (Int_t) w - (Int_t) upw : -1000;
   Int_t upy = 0;
   Int_t downx = (w > h) ? (Int_t) w - (Int_t) upw : -1000;
   Int_t downy = h / 2;
   UInt_t downw = upw;
   UInt_t downh = h - downy;
   UInt_t numw = (w > h) ? w - upw : w;
   UInt_t numh = h;
   if (fBox->GetNumberEntry())
      fBox->GetNumberEntry()->MoveResize(0, 0, numw, numh);
   if (fBox->GetButtonUp())
      fBox->GetButtonUp()->MoveResize(upx, upy, upw, uph);
   if (fBox->GetButtonDown())
      fBox->GetButtonDown()->MoveResize(downx, downy, downw, downh);
}

//______________________________________________________________________________
TGDimension TGNumberEntryLayout::GetDefaultSize() const
{
   return fBox->GetSize();
}



//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRepeatTimer                                                         //
//                                                                      //
// timer for numeric control box buttons                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

class TGRepeatFireButton;

//______________________________________________________________________________
class TRepeatTimer : public TTimer {
private:
   TGRepeatFireButton *fButton;	// Fire button

 public:
   TRepeatTimer(TGRepeatFireButton * button, Long_t ms)
    : TTimer(ms, kTRUE), fButton(button) { }
   virtual Bool_t Notify();
};



//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRepeatFireButton                                                    //
//                                                                      //
// Picture button which fires repeatly as long as the button is pressed //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
class TGRepeatFireButton : public TGPictureButton {
protected:
   TRepeatTimer             *fTimer;
   Int_t                     fIgnoreNextFire;
   TGNumberFormat::EStepSize fStep;
   Bool_t                    fStepLog;
   Bool_t                    fDoLogStep;

public:
   TGRepeatFireButton(const TGWindow *p, const TGPicture *pic,
                      Int_t id, Bool_t logstep)
    : TGPictureButton(p, pic, id), fTimer(0), fIgnoreNextFire(0),
       fStep(TGNumberFormat::kNSSSmall), fStepLog(logstep) { }
   virtual ~TGRepeatFireButton() { delete fTimer; }

   virtual Bool_t HandleButton(Event_t *event);
   void FireButton();
   virtual void SetLogStep(Bool_t on = kTRUE) { fStepLog = on; }
};

//______________________________________________________________________________
Bool_t TGRepeatFireButton::HandleButton(Event_t * event)
{
   //

   const Int_t t0 = 200;
   if (fTip)
      fTip->Hide();

   if (fState == kButtonDisabled)
      return kTRUE;

   if (event->fType == kButtonPress) {
      // Get log step / alt key
      fDoLogStep = fStepLog;
      if (event->fState & kKeyMod1Mask)
         fDoLogStep = !fDoLogStep;
      if ((event->fState & kKeyShiftMask) &&
          (event->fState & kKeyControlMask)) {
         fStep = TGNumberFormat::kNSSHuge;
      } else if (event->fState & kKeyControlMask) {
         fStep = TGNumberFormat::kNSSLarge;
      } else if (event->fState & kKeyShiftMask) {
         fStep = TGNumberFormat::kNSSMedium;
      } else {
         fStep = TGNumberFormat::kNSSSmall;
      }
      SetState(kButtonDown);
      fIgnoreNextFire = 0;
      FireButton();
      fIgnoreNextFire = 2;

      if (fTimer == 0) {
         fTimer = new TRepeatTimer(this, t0);
      }
      fTimer->Reset();
      gSystem->AddTimer(fTimer);
   } else {
      SetState(kButtonUp);
      if (fTimer != 0) {
         fTimer->Remove();
         fTimer->SetTime(t0);
      }
   }

   return kTRUE;
}

//______________________________________________________________________________
void TGRepeatFireButton::FireButton()
{
   if (fIgnoreNextFire <= 0) {
      SendMessage(fMsgWindow, MK_MSG(kC_COMMAND, kCM_BUTTON),
                  fWidgetId, (Long_t) fStep + (fDoLogStep ? 100 : 0));
   } else {
      fIgnoreNextFire--;
   }
}

//______________________________________________________________________________
Bool_t TRepeatTimer::Notify()
{
   fButton->FireButton();
   Reset();
   if ((long)fTime>20) fTime -= 10;
   return kFALSE;
}

//______________________________________________________________________________
TGNumberEntry::TGNumberEntry(const TGWindow *parent,
                             Double_t val, Int_t wdigits, Int_t id,
                             EStyle style,
                             EAttribute attr,
                             ELimit limits, Double_t min, Double_t max)
 : TGCompositeFrame(parent, 10 * wdigits, 25), fButtonToNum(kTRUE)
{
   // Constructs a numeric entry widget

   fWidgetId  = id;
   fMsgWindow = parent;
   fPicUp = fClient->GetPicture("arrow_up.xpm");
   if (!fPicUp)
      Error("TGNumberEntry", "arrow_up.xpm not found");
   fPicDown = fClient->GetPicture("arrow_down.xpm");
   if (!fPicDown)
      Error("TGNumberEntry", "arrow_down.xpm not found");

   // create gui elements
   fNumericEntry = new TGNumberEntryField(this, id, val, style, attr,
                                          limits, min, max);
   fNumericEntry->Associate(parent);
   AddFrame(fNumericEntry, 0);
   fButtonUp = new TGRepeatFireButton(this, fPicUp, 1,
                                      fNumericEntry->IsLogStep());
   fButtonUp->Associate(this);
   AddFrame(fButtonUp, 0);
   fButtonDown = new TGRepeatFireButton(this, fPicDown, 2,
                                        fNumericEntry->IsLogStep());
   fButtonDown->Associate(this);
   AddFrame(fButtonDown, 0);

   // resize
   Int_t h = fNumericEntry->GetDefaultHeight();
   Int_t charw = fNumericEntry->GetCharWidth("0123456789");
   Int_t w = charw * TMath::Abs(wdigits) / 10 + 8 + 2 * h / 3;
   SetLayoutManager(new TGNumberEntryLayout(this));
   Resize(w, h);
}

//______________________________________________________________________________
TGNumberEntry::~TGNumberEntry()
{
   // Destructs a numeric entry widget
   delete fButtonUp;
   delete fButtonDown;
   delete fNumericEntry;
}

//______________________________________________________________________________
void TGNumberEntry::Associate(const TGWindow *w)
{
   // Make w the window that will receive the generated messages.

   TGWidget::Associate(w);
   fNumericEntry->Associate(w);
}

//______________________________________________________________________________
void TGNumberEntry::SetLogStep(Bool_t on)
{
   // Set log steps
   fNumericEntry->SetLogStep(on);
   ((TGRepeatFireButton *) fButtonUp)->SetLogStep(fNumericEntry->IsLogStep());
   ((TGRepeatFireButton *) fButtonDown)->SetLogStep(fNumericEntry->IsLogStep());
}

//______________________________________________________________________________
void TGNumberEntry::SetState(Bool_t enable)
{
   // Set the active state
   if (enable) {
      fButtonUp->SetState(kButtonUp);
      fButtonDown->SetState(kButtonUp);
      fNumericEntry->SetState(kTRUE);
   } else {
      fButtonUp->SetState(kButtonDisabled);
      fButtonDown->SetState(kButtonDisabled);
      fNumericEntry->SetState(kFALSE);
   }
}

//______________________________________________________________________________
void TGNumberEntry::SetButtonToNum(Bool_t state)
{
   // Send button messages to the number field (true) or parent widget (false).
   // When the message is sent to the parent widget, it is responsible to change
   // the numerical value accordingly. This can be useful to implement cursors
   // which move from data point to data point. For the message being sent
   // see ProcessMessage().

   fButtonToNum = state;
}

//______________________________________________________________________________
Bool_t TGNumberEntry::ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
{
   // Process the up/down button messages. If fButtonToNum is false the
   // following message is sent: kC_COMMAND, kCM_BUTTON, widget id, param
   // param % 100 is the step size
   // param % 10000 / 100 != 0 indicates log step
   // param / 10000 != 0 indicates button down

   switch (GET_MSG(msg)) {
   case kC_COMMAND:
      {
         if ((GET_SUBMSG(msg) == kCM_BUTTON) &&
             (parm1 >= 1) && (parm1 <= 2)) {
            if (fButtonToNum) {
               Int_t sign = (parm1 == 1) ? 1 : -1;
               EStepSize step = (EStepSize) (parm2 % 100);
               Bool_t logstep = (parm2 >= 100);
               fNumericEntry->IncreaseNumber(step, sign, logstep);
            } else {
               SendMessage(fMsgWindow, msg, fWidgetId,
                           10000 * (parm1 - 1) + parm2);
               ValueChanged(10000 * (parm1 - 1) + parm2);
            }
         // Emit a signal needed by pad editor
         ValueSet(10000 * (parm1 - 1) + parm2);
         }
         break;
      }
   }
   return kTRUE;
}

//______________________________________________________________________________
void TGNumberEntry::ValueChanged(Long_t val)
{
   // Emit ValueChanged(Long_t) signal. This signal is emitted when
   // fButtonToNum is false. The val has the following meaning:
   // val % 100 is the step size
   // val % 10000 / 100 != 0 indicates log step
   // val / 10000 != 0 indicates button down

   Emit("ValueChanged(Long_t)", val);
}

//______________________________________________________________________________
void TGNumberEntry::ValueSet(Long_t val)
{
   // Emit ValueSet(Long_t) signal. This signal is emitted when the  
   // number entry value is changed. The val has the following meaning:
   // val % 100 is the step size
   // val % 10000 / 100 != 0 indicates log step
   // val / 10000 != 0 indicates button down

   Emit("ValueSet(Long_t)", val);
}

//______________________________________________________________________________
void TGNumberEntry::SavePrimitive(ofstream &out, Option_t *)
{
   // Save a number entry widget as a C++ statement(s) on output stream out.

   // to calculate the digits parameter
   Int_t w = fNumericEntry->GetWidth();
   Int_t h = fNumericEntry->GetHeight();
   Int_t charw  = fNumericEntry->GetCharWidth("0123456789");
   Int_t digits = (30*w - 240 -20*h)/(3*charw) + 3;

   // for time format
   Int_t hour, min, sec;
   GetTime(hour, min, sec);

   // for date format
   Int_t yy, mm, dd;
   GetDate(yy, mm, dd);

   out << "   TGNumberEntry *";
   out << GetName() << " = new TGNumberEntry(" << fParent->GetName() << ", (Double_t) ";
   switch (GetNumStyle()){
      case kNESInteger:
         out << GetIntNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESRealOne:
         out << GetNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESRealTwo:
         out << GetNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESRealThree:
         out << GetNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESRealFour:
         out << GetNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESReal:
         out << GetNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESDegree:
         out << GetIntNumber() << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESMinSec:
         out << min*60 + sec << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESHourMin:
         out << hour*60 + min << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESHourMinSec:
         out << hour*3600 + min*60 + sec << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESDayMYear:
         out << yy << mm << dd << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESMDayYear:
         out << yy << mm << dd << "," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      case kNESHex:
      {  char hex[150];
         ULong_t l = GetHexNumber();
         IntToHexStr(hex, l);
         out << "0x" << hex << "U," << digits << "," << WidgetId()
             << ",(TGNumberFormat::EStyle) " << GetNumStyle();
         break;
      }
   }
   if (GetNumMax() ==1) {
      if (GetNumMin() == 0) {
         if (GetNumLimits() == kNELNoLimits) {
            if (GetNumAttr() == kNEAAnyNumber) {
               out << ");" << endl;
            } else {
               out << ",(TGNumberFormat::EAttribute) " << GetNumAttr() << ");" << endl;
            }
         } else {
            out << ",(TGNumberFormat::EAttribute) " << GetNumAttr()
                << ",(TGNumberFormat::ELimit) " << GetNumLimits() << ");" << endl;
         }
      } else {
         out << ",(TGNumberFormat::EAttribute) " << GetNumAttr()
             << ",(TGNumberFormat::ELimit) " << GetNumLimits()
             << "," << GetNumMin() << ");" << endl;
      }
   } else {
         out << ",(TGNumberFormat::EAttribute) " << GetNumAttr()
             << ",(TGNumberFormat::ELimit) " << GetNumLimits()
             << "," << GetNumMin() << "," << GetNumMax() << ");" << endl;
   }
}
