#define STRICT
#define XVT_INCL_NATIVE
#include <xvt.h>

#include <applicat.h>
#include <colors.h>
#include <controls.h>
#include <defmask.h>
#include <execp.h>
#include <golem.h>
#include <mailbox.h>
#include <mask.h>
#include <prefix.h> 
#include <relation.h>
#include <sheet.h>
#include <tabutil.h>
#include <urldefid.h>
#include <utility.h>
#include <validate.h>
#include <xvtility.h>
#include <text.h>


// @doc INTERNAL

// @func HIDDEN void | smart_trim | Elimina gli spazi iniziali e/o finali da un stringa
HIDDEN void smart_trim(
            TString & s, // @parm Stringa
            byte mode    // @parm operazione da effettuare ( 0 = nessuna operazione
                         //       1 = trimma a sinistra
                         //       2 = trimma a destra
                         //       3 = trimma da entrambi i lati
                       )

{
  switch (mode)
  {
    case  1: s.ltrim(); break;
    case  2: s.rtrim(); break;
    case  3: s.trim(); break;
    default: break;
  }
}


///////////////////////////////////////////////////////////
// Field Flags
///////////////////////////////////////////////////////////

// Certified 100%
TMask_field::TField_Flags::TField_Flags()
{
  automagic = persistent   = FALSE;
  enabled = enable_default = TRUE;
  shown = show_default     = TRUE;
  uppercase = rightjust    = FALSE;
  zerofilled = button      = FALSE;
  dirty = focusdirty       = FALSE;
  roman = exchange         = FALSE;
  firm = ghost             = FALSE;
  password                 = FALSE;
  trim                     = 3;
  pipeallowed              = FALSE;
}

// Certified 100%
char TMask_field::TField_Flags::update(const char* f)
{
  for (const char* s = f; *s; s++)
    switch(*s)
    {
    case '#':
      trim = 2; break;
    case '@':
      trim = 1; break;
    case ' ':  
    case '_':
      trim = 0; break;  
    case '*':
      password = TRUE; break;
    case 'A':
      automagic = persistent = TRUE; break;
    case 'B':
      button = TRUE; break;
    case 'D': 
      enable_default = enabled = FALSE; break;
    case 'F': 
      firm = persistent = TRUE; break;
    case 'G': 
      ghost = TRUE; break;
    case 'H': 
      show_default  = shown = FALSE; break;
    case 'M': 
      roman = TRUE; break;
    case 'P': 
      persistent = TRUE; break;
    case 'R': 
      rightjust = TRUE; break;
    case 'U': 
      uppercase = TRUE; break;
    case 'V': 
      exchange = TRUE; break;
    case 'Z': 
      zerofilled = TRUE; break;
    default :  
      CHECKS(FALSE, "FLAG sconosciuto in ", f);
      break;
    }
  return *s;
}


///////////////////////////////////////////////////////////
// TMask_field
///////////////////////////////////////////////////////////

TMask_field::TControl_data TMask_field::_ctl_data;

void TMask_field::TControl_data::reset()
{
  _dlg = -1;
  _x = _y = -1;
  _width = 0;
  _height = 1;
  _size = 0;
  _decimals = 0;
  _bmp_up = _bmp_dn = 0;
  _prompt.cut(0);
  _flags.cut(0);
  _park.spaces(256);
  _park.cut(0);
}


TMask_field::TMask_field(TMask* m)
           : _mask(m), _ctl(NULL), _groups(NULL)
{
}

// Certified 100%
TMask_field::~TMask_field()
{
  if (_ctl) delete _ctl;
  if (_groups) delete _groups;
}

// Certified 100%
const char* TMask_field::class_name() const
{ return "Field"; }

// Certified 100%
word TMask_field::class_id() const
{ return CLASS_FIELD; }

// Certified 100%
bool TMask_field::is_kind_of(word cid) const
{ return cid == CLASS_FIELD || TObject::is_kind_of(cid); }

// Certified 100%
bool TMask_field::ok() const
{ return _ctl != NULL; }

// Certified 100%
void TMask_field::parse_head(TScanner&)
{}                                                  

short TMask_field::dlg() const 
{ 
  return _ctl ? _ctl->id() : _ctl_data._dlg; 
}

void TMask_field::highlight() const
{ 
#ifdef DBG
  yesnofatal_box("Can't set the focus to non-operable field %d", dlg());
#endif
}

void TMask_field::set_focus() const
{                                                
#ifdef DBG
  yesnofatal_box("Can't set the focus to non-operable field %d", dlg());
#endif
}



// @doc INTERNAL

// Certified 100%     
// @mfunc Converte una stringa in un identificatore di campo.
short TMask_field::atodlg(
  const char* s) const       // @parm stringa da convertire in numero
// @comm  In modo DBG controlla anche che sia un numero valido
{
  short d = s ? atoi(s) : 0;

#ifdef DBG
  if (d == 0 || d < -1 || d > 1000)
  {
    yesnofatal_box("Identificatore non valido nel campo %d: '%s'", _ctl_data._dlg, s);
    d = DLG_NULL;
  }
#endif  

  return d;
}

// @doc EXTERNAL

// @mfunc Costruisce il campo
void TMask_field::construct(
  short id,            // @parm Identificatore del campo
  const char* prompt,  // @parm Prompt del campo
  int x,               // @parm Posizione in x (in caratteri) del campo nella maschera
  int y,               // @parm Posizione in y (in caratteri) del campo nella maschera
  int len,             // @parm Lunghezza del buffer campo
  WINDOW parent,       // @parm Finestra padre alla quale assegnare il campo
  const char* flags,   // @parm Flags del campo (default "")
  int width)           // @parm Lunghezza a video del campo
  // @parm TScanner& | scanner | File dal quale leggere leggere le informazioni

  // @syntax void construct(TScanner& scanner, WINDOW parent);  
  // @syntax void construct(short id, const char* prompt, int x, int y, int len, WINDOW parent, const char* flags, int width);
  //                            
  // @comm E' possibile costruire il campo leggendo direttamente dal file, oppure passare alla funzione
  //                     tutti i parametri necessari alla definizione del campo stesso
{          
  _ctl_data.reset();
  _ctl_data._x = x; 
  _ctl_data._y = y;
  _ctl_data._prompt = prompt;
  _ctl_data._size = len;
  switch(class_id())
  {
  case CLASS_REAL_FIELD:
    ((TReal_field*)this)->set_decimals(width);
    _ctl_data._width = len;
    break;          
  case CLASS_BUTTON_FIELD:
    _ctl_data._height = len;
    _ctl_data._width = width <= 0 ? 12 : width;
    break;
  case CLASS_MEMO_FIELD:
    _ctl_data._height = len;
    _ctl_data._width  = width;
    _ctl_data._size   = len * width;
    break;  
  default: 
    _ctl_data._width = width == 0 ? len : width;
    break;
  }  
  
  _ctl_data._dlg = id;
  
  _ctl_data._flags = flags;
  _flags.update(flags);

  create(parent);
}


void TMask_field::construct(TScanner& scanner, WINDOW parent)
{ 
  _ctl_data.reset();
  _ctl_data._dlg = atodlg(scanner.pop());

  parse_head(scanner);
  
  scanner.popkey();                 // BEGIN
#ifdef DBG  
  if (scanner.key() != "BE")          
  {
    yesnofatal_box("Testata errata o BEGIN mancante nel campo %d", _ctl_data._dlg);
    scanner.push();
  }
#endif          

  while(scanner.popkey() != "EN")       // END of control
    parse_item(scanner);

  create(parent);
}

void TMask_field::set_group(byte group) 
{ 
  if (_groups == NULL) 
    _groups = new TBit_array;
  _groups->set(long(group)); 
  _groups->set(0L);
}


bool TMask_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "PR")                    // PROMPT
  {
    _ctl_data._x      = scanner.integer();
    _ctl_data._y      = scanner.integer();
    _ctl_data._prompt = scanner.string();
    return TRUE;
  }

  if (scanner.key() == "FL")                    // FLAGS
  {
    const char* f = scanner.string();
    if (_flags.update(f) == '\0')
      _ctl_data._flags << f;
    return TRUE;
  }

  if (scanner.key() == "GR")
  {        
    if (_groups == NULL)
      _groups = new TBit_array;
    _groups->set(scanner.line());
    
    return TRUE;
  }

  NFCHECK("'%s' Token non riconosciuto nel campo %d: %s", 
          (const char*)scanner.token(), (int)dlg(), (const char*)_ctl_data._prompt);
  return FALSE;
}

// Certified 100%
WINDOW TMask_field::parent() const
{             
  CHECK(_ctl, "Can't get the parent of a NULL control");
  return _ctl->parent(); 
}

RCT& TMask_field::get_rect(RCT& r) const
{
  if (_ctl) _ctl->get_rect(r);
  else memset(&r, 0, sizeof(r));
  return r;
}

void TMask_field::set_rect(const RCT& r)
{
  if (_ctl) _ctl->set_rect(r);
}

// Certified 100%
// @doc EXTERNAL

// @mfunc Abilita/disabilita il campo (lo rende scrivibile)
void TMask_field::enable(
  bool on) // @parm Operazione da svolgere:
  //
  // @flag TRUE | Abilita il campo (default)
  // @flag FALSE | Disabilita il campo
{
  _flags.enabled = on;
}


// Certified 100%
void TMask_field::enable_default()
{
  const bool ed = _flags.enable_default;
  enable(ed);
}


// Certified 100%
// @doc EXTERNAL

// @mfunc Permette di rendere visibile/invisibile un campo
void TMask_field::show(
  bool on) // @parm Operazione da svolgere:
  //
  // @flag TRUE | Rendere visibile il campo (default)
  // @flag FALSE | Rendere invisibile il campo
{
  if (_ctl) 
  {
    _ctl->show(on);
    _flags.shown = on;
  }  
}


// Certified 100%
void TMask_field::show_default()
{          
  const bool sd = _flags.show_default;
  show(sd);
}


// Certified 100%
bool TMask_field::active() const
{
  return is_operable() && enabled() && shown();
}


void TMask_field::set_dirty(bool d) 
{                
  if (_flags.dirty == 3 && d == FALSE)
    return;
  _flags.dirty = d; 
  set_focusdirty(d); 
}



// Certified 100%
const char* TMask_field::prompt() const
{
  return _ctl ? _ctl->caption() : "";
}

// Certified 100%
void TMask_field::reset()
{                      
  if (!_flags.persistent)
    set("");
}

// Certified 100%
void TMask_field::set_prompt(const char* p)
{                           
  if (_ctl) _ctl->set_caption(p);
}


void TMask_field::set(const char*)
{ 
}

void TMask_field::set(long n)
{
  char s[16]; sprintf(s, "%ld", n);
  set(s);
}

const TString& TMask_field::get() const
{        
  return _ctl_data._park.cut(0);
}

// @doc EXTERNAL

// @mfunc Verifica se il campo deve essere sottoposto a check
//
// @rdesc Ritorna il risultato:
//
// @flag TRUE | Se il campo e' da sottoporre a check
// @flag FALSE | Se il campo non e' da sottoporre check
bool TMask_field::to_check(
  KEY k,               // @parm Tasto da passare al campo come simulazione di inizio controllo
  bool checkrun) const // @parm Permette di ritornare TRUE se la maschera e' in start_run() mode
{   
  bool yes = (k == K_TAB && focusdirty()) || (k == K_ENTER && dirty());
  
  if (!yes && checkrun)
    yes = k == K_TAB && !mask().is_running();
  
  return yes;
}

void TMask_field::send_key(KEY k, short to) 
{ 
  _mask->send_key(k, to, this); 
}

void TMask_field::check_type(CheckType)
{ CHECK(0, "Can't set check type to non-edit field"); }


void TMask_field::set_handler(CONTROL_HANDLER)
{ CHECK(0, "Can't set the handler to a TMask_field"); }


bool TMask_field::on_hit()
{ 
  CHECK(0, "Can't hit non-operable field"); 
  return FALSE;
}


// Certified 100%
bool TMask_field::on_key(KEY key)
{
#ifdef DBG  
  if (key > (K_CTRL+K_SPACE))
    return error_box("Tasto ignorato %d", key);
#endif      

  return TRUE;
}

#define build_msg() char* _msg = (char*)(const char*)_ctl_data._park;va_list argptr;va_start(argptr,fmt);vsprintf(_msg,fmt,argptr);va_end(argptr)

// @doc EXTERNAL

// @mfunc Crea una error-box relativo al campo (chiama <f error_box>)
//
// @rdesc Ritorna sempre FALSE
bool TMask_field::error_box(
  const char* fmt,  // @parm Formato della stringa da scrivere
  ...) const        // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>
  
  // @comm Se il campo e'       uno sheet viene aggiornata la barra di stato, altrimenti
  //             viene creata una finestra di errore normale.
  
{
  build_msg();
  if (mask().is_sheetmask() && !mask().is_running())
  {                       
    xvt_statbar_set(_msg);
    beep();
  }  
  else
  {
    mask().post_error_message(_msg, 3);
  }
  return FALSE;
}

bool TMask_field::message_box(const char* fmt, ...) const
{            
  build_msg();
  mask().post_error_message(_msg, 1);
  return FALSE;
}

// @doc EXTERNAL

// @mfunc Crea una warning-box relativo al campo (chiama <f warning_box>)
//
// @rdesc Ritorna sempre FALSE
bool TMask_field::warning_box(
  const char* fmt,  // @parm Formato della stringa da scrivere
  ...) const        // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>
  
  // @comm Se il campo e'       uno sheet viene aggiornata la barra di stato, altrimenti
  //             viene creata una finestra di warning normale.
{                         
  build_msg();
  
  if (mask().is_sheetmask() && !mask().is_running())
  {
    xvt_statbar_set(_msg);
    beep();
  }  
  else
  {
    mask().post_error_message(_msg, 2);
  }
  return FALSE;
}

bool TMask_field::yesno_box(const char* fmt, ...) const
{                         
  set_focus();
  build_msg();
  const bool yes = ::yesno_box("%s", _msg);
  set_focus();
  return yes;
}

KEY TMask_field::yesnocancel_box(const char* fmt, ...) const
{                         
  set_focus();
  build_msg();
  const KEY k = ::yesnocancel_box("%s", _msg);
  set_focus();
  return k;
}

///////////////////////////////////////////////////////////
// TText_field
///////////////////////////////////////////////////////////

// Certified 100%
word TText_field::class_id() const
{ return CLASS_TEXT_FIELD; }

// Certified 100%
bool TText_field::is_kind_of(word cid) const
{ return cid == CLASS_TEXT_FIELD || TMask_field::is_kind_of(cid); }

// Certified 100%
void TText_field::create(WINDOW parent)
{
  _ctl = new TText_control(parent, _ctl_data._dlg,
                           _ctl_data._x, _ctl_data._y,  
                           0, 1, 
                           _ctl_data._flags, _ctl_data._prompt);
}
///////////////////////////////////////////////////////////
// TGroup_field
///////////////////////////////////////////////////////////

TGroup_field::TGroup_field(TMask* mask) : TMask_field(mask)
{
  _flags.persistent = TRUE;
}

// _size means _heigth
void TGroup_field::parse_head(TScanner& scanner)
{
  _ctl_data._width = scanner.integer();
  _ctl_data._size  = scanner.integer();
}

void TGroup_field::create(WINDOW parent)
{
  _ctl = new TGroupbox_control(parent, _ctl_data._dlg,
                               _ctl_data._x, _ctl_data._y,  
                               _ctl_data._width, _ctl_data._size, 
                               _ctl_data._flags, _ctl_data._prompt);
}

///////////////////////////////////////////////////////////
// TOperable_field
///////////////////////////////////////////////////////////

TOperable_field::TOperable_field(TMask* m)
               : TMask_field(m), _handler(NULL), _message(NULL)
{ }               

TOperable_field::~TOperable_field()
{ 
  if (_message) delete _message;
}               

TToken_string* TOperable_field::message(int m, bool crea)
{ 
  TToken_string* msg = _message ? _message->rowptr(m) : NULL; 
  if (msg == NULL && crea)
  {
    if (_message == NULL) 
      _message = new TString_array(2);
    msg = new TToken_string(16);
    _message->add(msg, m);
  }
  return msg;
}


bool TOperable_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "HE")                    // HELP
  {
    scanner.string();                           // Ignored from this version
    return TRUE;
  } 
  
  if (scanner.key() == "ME")
  {
    const TString& m = scanner.line().strip_spaces();
    message(0, TRUE)->add(m);
    return TRUE;
  }
  
  return TMask_field::parse_item(scanner);
}

void TOperable_field::set_focus() const
{                                                
  mask().set_focus_field(dlg());
}  

// Certified 100%
bool TOperable_field::is_kind_of(word cid) const
{ return cid == CLASS_OPERABLE_FIELD || TMask_field::is_kind_of(cid); }

void TOperable_field::enable(bool on)
{                                                
  TMask_field::enable(on);
  if (_ctl) _ctl->enable(on);
}  

bool TOperable_field::on_key(
  KEY key) // @parm Tasto notificato
{ 
  switch(key)
  {  
  case K_F11:
    {          
      TString msg(80);
      msg = "State utilizzando il campo ";
      msg << dlg() << " della maschera " << mask().source_file();
      if (field() != NULL)
        msg << "\ncorrispondente al campo su record " << *field();
      message_box(msg);
    }  
    break;
  case K_CTRL + K_TAB:
    mask().notify_focus_field(dlg());    
    return handler(key);
  default:   
    return handler(key);
  }
  return TMask_field::on_key(key);
}

  
// Certified 50%
HIDDEN void modify_list(bool add, TMask_field& f, TToken_string& msg)
{
#ifdef DBG
  if (f.class_id() != CLASS_LIST_FIELD)
  {
    yesnofatal_box("Can't add/delete items of non list-box field %d", f.dlg());
    return;
  }  
#endif  
  TList_field& l = (TList_field&)f;
  
  TToken_string item(16); 
  item = msg.get(); 
  if (add) item.add(msg.get()); 
  item.strip("\"'");
  if (add)
    l.add_item(item);
  else
    l.delete_item(item);  
}


// Certified 99%
HIDDEN const char* copy_value(TToken_string& msg, const TString& val)
{
  int from = msg.get_int()-1;
  int to = -1;
  if (from < 0) from = 0;
  else to = msg.get_int();
  return val.sub(from, to);
}

HIDDEN void run_app(TMask& mask, TToken_string& msg)
{
  TFilename a;
  
  for (const char* m = msg.get(1); m; m = msg.get())
  {
    if (a.not_empty())                    
      a << ' ';
    
    for (const char* f = m; *f; f++)
    {
      if (*f == '#') 
      {
        const int id = atoi(++f);
        a << mask.get(id);
        break;
      }
      else
        a << *f;
    }  
  }
  TExternal_app app(a);
  app.run();
  mask.set_focus();
}

// Certified 90%
bool TOperable_field::do_message(int num)
{               
  const int MAX_CMD = 17;
  static const char* commands[MAX_CMD] =
  {
    "ADD",         //  0
    "CLEAR",       //  1
    "CO",          //  2
    "DEL",         //  3
    "DIRTY",       //  4
    "DISABLE",     //  5
    "ENABLE",      //  6
    "ENABLEDEF",   //  7 
    "EXIT",        //  8
    "HIDE",        //  9 
    "NORMAL",      // 10
    "PUSH",        // 11
    "REQUIRED",    // 12
    "RESET",       // 13
    "RU",          // 14  
    "SHOW",        // 15
    "TAB",         // 16       
    };
                               
  TToken_string* mess = message(num);
  if (mess == NULL || mess->empty()) 
    return FALSE;

  TToken_string msg(16, ',');
  TString256 value;

  for (const char* m = mess->get(0); m && *m; m = mess->get())
  {
    KEY key = 0;
    msg = m;
    value = msg.get();
    const char* dlg = msg.get();
    
    int cmd = -1;
    if (isalpha(value[0]))                       // binary search
    {         
      int f = 0, l = MAX_CMD-1;                    
      while (TRUE)
      {
        cmd = (f+l)>>1;                          
        const int cmp = strcmp(value, commands[cmd]);
        if (cmp == 0) break;
        if (cmp > 0) f = cmd+1;
        else         l = cmd-1;
        if (f > l) 
        {
          cmd = -1;
          break;
        }  
      }
    }

    if (cmd == 8)
    {
      mask().stop_run(atoi(dlg));
      continue;
    } else 
      if (cmd == 14)
      {
        run_app(mask(), msg); 
        continue;
      }  

    short fld = (dlg && dlg[0] > ' ') ? atodlg(dlg) : 0;
    const bool broadcast = dlg && strchr(dlg, '@');
    if (broadcast) fld = -fld;
    
    if (value[0] == '"') value = value.strip("\"'");
    else switch (cmd)
    {
    case 0:
      modify_list(TRUE, mask().field(fld), msg); continue;
    case 1:
      mask().disable(fld); 
      mask().reset(fld);
      continue; 
    case 2:
      value = copy_value(msg, get()); break;
    case 3:
      modify_list(FALSE, mask().field(fld), msg); continue;
    case 4:
      mask().field(fld).set_dirty(); continue;
    case 5:
      mask().disable(fld); continue;
    case 6:
      mask().enable(fld); continue;
    case 7:
      mask().enable_default(fld); continue;
    case 9:
      mask().hide(fld); continue;
    case 10:
      mask().field(fld).check_type(CHECK_NORMAL); continue;
    case 11:
      mask().field(fld).on_hit(); continue;
    case 12:
      mask().field(fld).check_type(CHECK_REQUIRED); continue;
    case 13:
      key = K_F2; break;
    case 15:
      mask().show(fld); continue;
    case 16:
      mask().field(fld).on_key(K_TAB); continue;
    default:                        
      key = atoi(value);
      break;
    }

    if (key)
    {
      if (key > 0)
        send_key(key, fld);
    }
    else
    {
      // Setta a value il campo fld solo se ha un valore diverso da value
      if (broadcast)
      {    
        const int max = mask().fields();
        for (int i = 0; i < max; i++)
        {
          TMask_field& f = mask().fld(i);
          if (f.in_group((int)-fld))
          {
            const TString& prev = f.get();
            if (value != prev)
            {
              f.set(value);
              if (f.shown() || f.ghost())
                f.on_hit();
            }
          }
        }
      }
      else
      {
        TMask_field& f = mask().field(fld);
        const char* prev = f.get();
        if (value != prev)
        {
          f.set(value);
          if (f.shown() || f.ghost())
            f.on_hit();
        }
      }
    }
  }

  return TRUE;
}

void TOperable_field::highlight() const
{
  if (_ctl) _ctl->set_focus();
}

bool TOperable_field::handler(KEY k)
{ 
  bool ok;
  if (_handler)
    ok = _handler(*this, k);
  else
    ok = TRUE;  
  return ok;
}

// Certified 90%
bool TOperable_field::on_hit()
{   
  const bool ok = handler(K_SPACE);
  if (ok && has_message())
    do_message(0);
  return ok;
}

///////////////////////////////////////////////////////////
// Button_field
///////////////////////////////////////////////////////////

TButton_field::TButton_field(TMask* m)
             : TOperable_field(m)
{
}


word TButton_field::class_id() const
{ return CLASS_BUTTON_FIELD; }

bool TButton_field::is_kind_of(word cid) const
{ return cid == CLASS_BUTTON_FIELD || TOperable_field::is_kind_of(cid); }

void TButton_field::parse_head(TScanner& scanner)
{
  _ctl_data._width = scanner.integer();
  if (_ctl_data._width <= 0) _ctl_data._width = 10;
  _ctl_data._height  = scanner.integer(); // Height
  if (_ctl_data._height <= 0) _ctl_data._height = 1;
}


bool TButton_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "PI")
  {        
    const short bmp = (short)scanner.integer();
    if (_ctl_data._bmp_up == 0)
      _ctl_data._bmp_up = bmp;
    else  
      _ctl_data._bmp_dn = bmp;
    return bmp > 0;
  }
  return TOperable_field::parse_item(scanner);
}


void TButton_field::create(WINDOW parent)
{
  _exit_key = 0;
  _virtual_key = 0;
  switch (dlg())
  {
  case DLG_OK:
    if (_ctl_data._prompt.empty())
      _ctl_data._prompt = "~Conferma";   
    _virtual_key = 'C';
    _exit_key = K_ENTER;
    if (_ctl_data._bmp_up == 0)
      _ctl_data._bmp_up = BMP_OK;
    break;
  case DLG_CANCEL:
    if (_ctl_data._prompt.empty())
      _ctl_data._prompt = "Annulla";
    _virtual_key = _exit_key = K_ESC;
    if (_ctl_data._bmp_up == 0 && _ctl_data._prompt == "Annulla")
      _ctl_data._bmp_up = BMP_CANCEL;
    break;
  case DLG_DELREC:  
    if (_ctl_data._prompt.empty())
      _ctl_data._prompt = "~Elimina"; 
    _virtual_key = 'E';
    _exit_key = K_DEL;
    if (_ctl_data._bmp_up == 0)     
    {
      _ctl_data._bmp_up = BMP_DELREC; 
      _ctl_data._bmp_dn = BMP_DELRECDN;
    }  
    break;
  case DLG_PRINT:
    if (_ctl_data._prompt.empty())
      _ctl_data._prompt = "~Stampa"; 
    _virtual_key = 'S';
    _exit_key = K_ENTER;
    if (_ctl_data._bmp_up <= 0)
      _ctl_data._bmp_up = BMP_PRINT;
    break;
  case DLG_QUIT:
    if (_ctl_data._prompt.empty())
      _ctl_data._prompt = "Fine"; 
    _virtual_key = K_F4;
    _exit_key = K_QUIT;                 
    if (_ctl_data._bmp_up <= 0)
    {
      _ctl_data._bmp_up = BMP_QUIT;
      _ctl_data._bmp_dn = BMP_QUITDN;
    }  
    break;
  case DLG_SELECT:
    if (_ctl_data._bmp_up <= 0)
      _ctl_data._bmp_up = BMP_SELECT;
    _exit_key = K_ENTER;
    break;
  default:
    break;
  }

  TToken_string* mess = message(0);
  if (mess)
  {
      TToken_string msg(mess->get(0), ','); 
      const TString m(msg.get(0));
      if (m == "EXIT") 
        _exit_key = msg.get_int();
      else 
        if (msg.get_int() == 0) _exit_key = atoi(m);
  } 
  if (_virtual_key == 0)
  { 
    const int n = _ctl_data._prompt.find('~');
    _virtual_key = (n >= 0) ? toupper(_ctl_data._prompt[n+1]) : _exit_key;
  }
  
  _ctl = new TPushbutton_control(parent, _ctl_data._dlg,
                                 _ctl_data._x, _ctl_data._y, 
                                 _ctl_data._width+2, _ctl_data._height,
                                 _ctl_data._flags, _ctl_data._prompt,
                                 _ctl_data._bmp_up, _ctl_data._bmp_dn);
}

bool TButton_field::on_key(KEY key)
{  
  bool ok = TRUE;
  if (key == K_SPACE)   
  {
    if (dlg() != DLG_CANCEL && dlg() != DLG_QUIT)
    {
      const TMask& m = mask();
      if (parent() == m.toolwin())
        ok = m.focus_field().on_key(K_TAB);
    }
    if (ok)
    {
      ok = on_hit();
      if (ok && _exit_key > 0 && !has_message())
        mask().stop_run(_exit_key);
    }    
  }  
  else
    ok = TOperable_field::on_key(key);
  return ok;
}

void TButton_field::set_bmp(short up, short dn)
{
  ((TPushbutton_control*)_ctl)->set_bmp(up, dn);
}

///////////////////////////////////////////////////////////
// TEditable_field
///////////////////////////////////////////////////////////

TEditable_field::TEditable_field(TMask* m)
               : TOperable_field(m), _prompt(NULL), _field(NULL), _keys(NULL),
                 _validate(NULL), _warning(NULL)
{ }


TEditable_field::~TEditable_field()
{
  if (_prompt)   delete _prompt;
  if (_field)    delete _field;
  if (_keys)     delete _keys;
  if (_validate) delete _validate;
  if (_warning)  delete _warning;
}  
 

void TEditable_field::set_warning(const char* w)
{
  if (_warning)
    *_warning = w;
  else
    _warning = new TString(w);  
  if (_warning->empty())
  {
    delete _warning;
    _warning = NULL;
  }  
}

// Certified 100%
const char* TEditable_field::prompt() const
{
  return _prompt ? _prompt->caption() : "";
}


// Ritorna il messaggio d'errore associato al campo
const char* TEditable_field::get_warning() const
{ return _warning ? (const char*)*_warning : ""; }

bool TEditable_field::parse_item(TScanner& scanner)
{ 
  const TString& k = scanner.key();

  if (k == "FI")                    // FIELD
  {                                           
    set_field(scanner.line());
    return TRUE;
  }

  if (k == "KE")                    // KEY
  {
    if (_keys == NULL)
      _keys = new TBit_array;
    _keys->set(scanner.line());
    _keys->set(0L);
    return TRUE;
  }

  if (k == "CU")                    // CUSTOM DATA
  { 
    if (_userdata == NULL)
      _userdata = new TToken_string(scanner.string());
    else  
      _userdata->add(scanner.string());
    return TRUE;
  }     
  
  if (scanner.key() == "VA")
  {                    
#ifdef DBG  
    if (_validate)
      return yesnofatal_box("VALIDATE duplicata nel campo %d", _ctl_data._dlg);
#endif    
    _validate = new TValidation_data;

    const char* n = scanner.pop();
    _validate->_func = isdigit(*n) ? atoi(n) : -1;
#ifdef DBG    
    if (_validate->_func < 0)
      return yesnofatal_box("Funzione di validazione '%s' errata nel campo %d", 
                            n, _ctl_data._dlg);
#endif

    const int nparms = scanner.integer();
#ifdef DBG    
    if (nparms < 0 || nparms > 100)
      return yesnofatal_box("Numero di parametri VALIDATE errato nel campo %d: %d", 
                            _ctl_data._dlg, nparms);
#endif
    
    for (int i = 0; i < nparms; i++)
      _validate->_parms.add(scanner.operand());

    return TRUE;
  }

  if (scanner.key() == "WA")
  {             
    set_warning(scanner.string());
    return TRUE;
  }
  
  return TOperable_field::parse_item(scanner);
}

void TEditable_field::set_key(byte key) 
{        
  if (_keys == NULL)
    _keys = new TBit_array;
  _keys->set(long(key)); 
  _keys->set(0L);
}

// Certified 90%
word TEditable_field::last_key() const
{
  long u = _keys ? _keys->last_one() : 0;
  if (u < 0) u = 0;
  return (word)u;
}

// Certified 70%
word TEditable_field::first_key() const
{
  if (_keys == NULL)
    return 0;
  const int last = last_key();
  for (int i = 1; i <= last; i++)
    if (in_key(i) == TRUE)
      break;
  return (word)i;
}

// Certified 70%
bool TEditable_field::has_a_common_key(const TMask_field & f) const

{              
  const int last = last_key();
  for (int i = 1; i <= last; i++)
    if (in_key(i) && f.in_key(i))
      return TRUE;
  return FALSE;
} 

bool TEditable_field::test_key_complete(bool normal) const
{
  TMask& m = mask();
  for (int k = 1; k <= MAX_KEYS; k++)
  {
    if (in_key(k) && m.key_valid(k))
    {
      bool complete = TRUE;
      for (TEditable_field* e = m.get_key_field(k, TRUE); e; e = m.get_key_field(k, FALSE))
      {
        e->set_focusdirty(FALSE);       
        if (e->is_edit())
          complete &= e->required();     
      }                           
      if (normal && !complete)
        m.set_test_field(dlg());           
      else
        m.stop_run(K_AUTO_ENTER);
      return TRUE;
    }
  }  
  return FALSE;
}

// Certified 90%
void TEditable_field::reset_key(byte key)
{        
  if (_keys)
  {
    _keys->reset(long(key));
    if (key == 0 || last_key() == 0)       
    {
      delete _keys;         // Se non ci sono piu' chiavi azzera tutto
      _keys = NULL;
    }  
  }  
}

// @doc EXTERNAL

// @mfunc Chiama l' eventuale funzione di validazione
//
// @rdesc Ritorna il risultato dell funzione di validazione:
//
// @flag TRUE | Se la validata ha avuto successo
// @flag FALSE | Se la validata no ha avuto successo
bool TEditable_field::validate(
  KEY k) // @parm Tasto da passare alla funzione di validazione
{              
  bool ok = TRUE;
  if (_validate)
    ok = ::validate(_validate->_func, *this, k, _validate->_parms);
  return ok;  
}               


// Certified 90%
// @doc EXTERNAL

// @mfunc Crea il prompt del controllo
//
// @rdesc Ritorna la lunghezza del prompt creato
word TEditable_field::create_prompt(
  WINDOW parent, // @parm Finestra padre alla quale assegnare il prompt del controllo
  word width,     // @parm Larghezza del controllo
  word height)    // @parm Altezza del controllo
{
  if (_ctl_data._prompt.not_empty())
  {
    TString flags;
    if (hidden()) flags << 'H';

    if (height < 3)
    {                          
      width = _ctl_data._prompt.len();
      for (word i = 0; i < width && _ctl_data._prompt[(int)i] == '@'; i += 2);
      width -= i;
      _prompt = new TText_control(parent, -1, _ctl_data._x, _ctl_data._y, 
                                  0, 1, flags, _ctl_data._prompt);
    }
    else
    {
      if (right_justified()) flags << 'R';
      _prompt = new TGroupbox_control(parent, -1, _ctl_data._x, _ctl_data._y, 
                                      width, height, flags, _ctl_data._prompt);
    }                              
  }

  return width;
}

void TEditable_field::show(bool on)
{
  TOperable_field::show(on);
  if (_prompt) _prompt->show(on);
}

void TEditable_field::set_prompt(const char* p)
{        
  CHECKD(_prompt, "Can't set prompt to control", dlg());                 
  _prompt->set_caption(p);
}

// Certified 90%
void TEditable_field::set_field(const TString& fr)
{
  if (_field != NULL)  
    *_field = fr;
  else
    _field = new TFieldref(fr, 0);
}


// Certified 100%
// Legge il valore attuale del campo in formato RAW
const TString& TEditable_field::get() const
{
  return _str;
}

// Certified 100%
// Aggiusta un valore letto da file in formato RAW
const char* TEditable_field::reformat(const char* data) const
{
  return data;
}

// Certified 100%                  
// Setta il valore RAW e WIN del campo con un valore RAW
void TEditable_field::set(const char* data)
{
  _str = reformat(data);
  const TString& val = raw2win(_str);
  set_window_data(val);  
  
  const TMask& m = mask();
  if (m.is_running() || m.is_sheetmask())            
    set_dirty();
}

// Certified 100%                  
// Converte da formato RAW a formato WIN
const char* TEditable_field::raw2win(const char* data) const
{
  return reformat(data);
}

// Certified 100%                  
// Converte da formato WIN a formato RAW
const char* TEditable_field::win2raw(const char* data) const
{
  return reformat(data);
}

bool TEditable_field::autosave(TRelation& r) const
{
  if (_field)
    _field->write(get(), r);
  return _field != NULL;
}

bool TEditable_field::autoload(const TRelation& r)
{
  if (_field)
    set(_field->read(r));
  return _field != NULL;
}

///////////////////////////////////////////////////////////
// Boolean_field
///////////////////////////////////////////////////////////

TBoolean_field::TBoolean_field(TMask* m)
              : TEditable_field(m)
{}

word TBoolean_field::class_id() const
{ return CLASS_BOOLEAN_FIELD; }


void TBoolean_field::create(WINDOW parent)
{             
  _ctl = new TCheckbox_control(parent, _ctl_data._dlg,
                               _ctl_data._x, _ctl_data._y, 0,
                               _ctl_data._flags, _ctl_data._prompt);
}

void TBoolean_field::set_prompt(const char* p)
{
  _ctl->set_caption(p);
}

const char* TBoolean_field::reformat(const char* data) const
{
  return (data && *data > ' ') ? "X" : ""; 
}

void TBoolean_field::set_window_data(const char* data)
{ 
  if (_ctl)
    ((TCheckbox_control*)_ctl)->check(*data > ' ');
}

TString& TBoolean_field::get_window_data()
{ 
  const bool on = _ctl ? ((TCheckbox_control*)_ctl)->checked() : FALSE;
  _str = on ? "X" : "";
  return _str;
}

bool TBoolean_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "ME")
  {
    const bool tf = scanner.integer() ? TRUE : FALSE;   // Message TRUE or FALSE
    TToken_string* ts = message(tf, TRUE);
    ts->add(scanner.line().strip_spaces());
    return TRUE;
  }

  return TEditable_field::parse_item(scanner);
}

bool TBoolean_field::on_hit()
{                          
  const bool ok = handler(K_SPACE);
  if (ok)
  {
    const bool on = get() == "X";
    do_message(on);
  }  
  return ok;
}

bool TBoolean_field::on_key(KEY key)
{
  if (key == K_SPACE) 
  { 
    get_window_data();
    set_dirty();
    on_hit();
    return TRUE;
  }  
  return TEditable_field::on_key(key);
}

///////////////////////////////////////////////////////////
// TBrowse_button
///////////////////////////////////////////////////////////

TBrowse_button::TBrowse_button(TEdit_field* f) 
              : _fld(f)
{ }
  
TBrowse_button::~TBrowse_button()
{
}

// Certified 100%
TEditable_field& TBrowse_button::field(short id) const
{
  if (id > 0)
  {
    TMask_field& f = _fld->mask().field(id); 
    CHECKD(f.is_editable(), "Can't use in a browse the field ", id);
    return (TEditable_field&)f;
  }
  return *_fld;
}

///////////////////////////////////////////////////////////
// TList_sheet
///////////////////////////////////////////////////////////



// Certified 100%
TList_sheet::TList_sheet(TEdit_field* f, const char* caption, const char* head)
           : TBrowse_button(f), _row(-1), _caption(caption), _head(head)
{ }

// Certified 100%
TList_sheet::~TList_sheet()
{ }


// Certified 100%
void TList_sheet::parse_input(TScanner& scanner)
{
  _inp_id.add(scanner.pop());
}

// Certified 100%
void TList_sheet::parse_item(TScanner& scanner)
{
  _data.add(new TToken_string(scanner.string()));
}

// Certified 100%
void TList_sheet::parse_output(TScanner& scanner)
{
  _out_id.add(scanner.pop());
}

// il numero di riga selezionata
int TList_sheet::do_input()
{
  if (_inp_id.empty_items()) 
    return -2;          // List empty!

  _inp_id.restart();
  TToken_string rowsel(80);

  for (const char* fld = _inp_id.get(); fld; fld = _inp_id.get())
  {
    if (*fld == '"')
    {
      rowsel.add(fld+1);
      if (rowsel.not_empty()) rowsel.cut(rowsel.len()-1);
    }
    else
    {
      const short id = field().atodlg(fld);
      if (id > 0)
      { 
        const TMask_field& f = field(id);
        if (f.class_id() == CLASS_ZOOM_FIELD)
        {
          const TZoom_field& z = (TZoom_field&)f;
          rowsel.add(z.get_first_line());
        }
        else
           rowsel.add(f.get());
      }
      else rowsel.add("");
    }
  }

  TString fd, it;
  for (int i = 0 ; i < _data.items(); i++)
  {
    TToken_string& ts =_data.row(i);

    ts.restart();
    for ( const char* item = rowsel.get(0); item ; item = rowsel.get())
    {
      it = item; it.trim();
      fd = ts.get(); fd.trim();
      if (fd != it) break;
    }
    if (!item) return i;
  }

  return -1;                 // Value not found!
}


// Certified 50%
void TList_sheet::do_output(CheckTime t)
{
  if (_row < 0 || t == FINAL_CHECK) 
    return;
  
  _out_id.restart();
  TToken_string& rowsel = _data.row(_row);
  rowsel.restart();
  for (const char* fld = _out_id.get(); fld; fld = _out_id.get())
  {
    const short id = field().atodlg(fld);
    TMask_field& f = field(id);
    const char* val = rowsel.get();
    if (t != STARTING_CHECK || f.field() == NULL)
    {           
      const bool hit = f.get() != val;
      f.set(val);
      if (field().dlg() != id && hit)
      {
        f.on_hit();
        f.check();
      }
    }
  }
}


// Certified 100%
KEY TList_sheet::run()
{
  TArray_sheet sci(3, 3, -3, -3, _caption, _head);
  sci.rows_array() = _data;

  _row = do_input();
  sci.select(_row);
  const KEY k = sci.run();

  switch (k)
  {
  case K_ENTER:
    _row = (int)sci.selected();
    do_output();
    break;
  default:
    break;
  }

  return k;
}


// Certified 100%
bool TList_sheet::check(CheckTime t)
{                                       
  _row = do_input();
  const bool passed = _row != -1;
  if (passed) do_output(t);
  return passed;
}


///////////////////////////////////////////////////////////
// TBrowse
///////////////////////////////////////////////////////////

// Certified 100%
TBrowse::TBrowse(TEdit_field* f, TRelation* r, int key, const char* filter)
       : TBrowse_button(f),
         _relation(r), _cursor(new TCursor (r, filter, key)),
         _filter(filter), _secondary(FALSE)
{}


// Certified 100%
TBrowse::TBrowse(TEdit_field* f, TCursor* c)
       : TBrowse_button(f),
         _relation(NULL), _cursor(c), _secondary(FALSE)
{}


// Certified 100%
TBrowse::~TBrowse()
{
  // Se e' stato usato il primo costruttore per cui devo distruggere la relazione ed il cursore
  if (_relation)
  {
    delete _cursor;
    delete _relation;
  }
}


// Certified 100%
void TBrowse::parse_display(TScanner& scanner)
{              
  const char* s;
  s = scanner.string();
  _head.add(s);
  s = scanner.line();
  _items.add(s);
}


void TBrowse::parse_input(TScanner& scanner)
{
  const char* s = scanner.pop();
  _inp_fn.add(s);

  s = scanner.pop();
  if (*s == '"')        // Constant string
  {
    scanner.push();
    TString& str = scanner.line();
    _inp_id.add(str);

    if (_cursor->file().num() == LF_GOLEM)       // GOLEM 2 detected!
    {               
      str.strip("\"");
      _insert = "G";
      _insert << str;
    }
  }
  else            // Field on the mask
  {                   
    CHECKS(_inp_id.get_pos(s) < 0, "Duplicate input field ", s);
    _inp_id.add(s);
    if (scanner.popkey() == "SE") _inp_id << '@';       // Special FILTERing field
    else scanner.push();
  }
}


void TBrowse::parse_output(TScanner& scanner)
{
  const char* s = scanner.pop();
#ifdef DBG      
  field().atodlg(s);
#endif  
  _out_id.add(s);
  s = scanner.pop();
  _out_fn.add(s);  
  _secondary = FALSE;
}


bool TBrowse::parse_copy(const TString& what, const TBrowse& b)
{
  const bool all = what == "AL";
  if (all || what == "US")
  {
    set_insert(b.get_insert());
    _filter = b.get_filter();
    if (!field().has_warning() && b.field().has_warning()) 
      field().set_warning(b.field().get_warning());
    if (!all) return TRUE;
  }
  if (all || what == "IN")
  {
    _inp_id = b._inp_id;
    _inp_fn = b._inp_fn;
    if (!all) return TRUE;
  }
  if (all || what == "DI")
  {
    _head = b._head;
    _items = b._items;
    if (!all) return TRUE;
  }
  if (all || what == "OU")
  {
    _out_id = b._out_id;
    _out_fn = b._out_fn;
    _secondary = b.field().has_check();
  }
  return TRUE;
}


void TBrowse::parse_join(TScanner& scanner)
{
  TString80 j(scanner.pop());             // File or table

  CHECKS(_relation, "Can't join to NULL relation ", (const char*)j);

  int to;
  if (scanner.popkey() == "TO")         // TO keyword
  {
    const char* t = scanner.pop();
    to = name2log(t);
  }
  else
  {
    to = 0;                  // _relation->lfile()->num();
    scanner.push();
  }

  int key = 1;
  if (scanner.popkey() == "KE")
    key = scanner.integer();
  else scanner.push();

  int alias = 0;
  if (scanner.popkey() == "AL")
    alias = scanner.integer();
  else scanner.push();

  TToken_string exp(80);
  if (scanner.pop() == "INTO")
  {
    const char* r = scanner.pop();
    while (strchr(r, '=') != NULL)
    {
      exp.add(r);
      r = scanner.pop();
    }
  }
  scanner.push();

#ifdef DBG      
  if (exp.empty()) yesnofatal_box("JOIN senza espressioni INTO");
#endif  
               
  if (isdigit(j[0]))
    _relation->add(atoi(j), exp, key, to, alias);   // join file
  else
  {
#ifdef DBG      
    if (j.len() > 4)
      yesnofatal_box("'%s' non e' una tabella valida: %d", (const char*)j);
    else  
#endif          
      _relation->add(j, exp, key, to, alias);      // join table
  }
}


void TBrowse::parse_insert(TScanner& scanner)
{
  const TString& key = scanner.popkey();
  _insert.cut(0);
  if (key != "NO")
  {
    _insert << key[0] << scanner.line();
    _insert.trim();
  }
}

// Ritorna il numero di inputs senza contare quelli che funzionano solo da filtro
int TBrowse::input_fields()
{
  int inp = 0;
  for (const char* fld = _inp_id.get(0); fld; fld = _inp_id.get())
  {                                             
    if (*fld != '"' && strchr(fld, '@') == NULL)
    {
      TMask_field& f = field(f.atodlg(fld));
      if (f.active() && f.is_editable())
        inp++;
    }    
  }
  return inp;    
}

const char* TBrowse::get_input_fields() const
{
  return _inp_id;
}

const char* TBrowse::get_input_field_names() const
{
  return _inp_fn;
}


const char* TBrowse::get_output_fields() const
{
  return _out_id;
}


// @doc INTERNAL

// @mfunc Ritorna il numero di campi non vuoti e non filtrati
//
// @rdesc Numero di campi non vuoti e non filtrati
int TBrowse::do_input(
  bool filter) // @parm Indica se effettuare il filtro sulla selezione

  // @comm Questa funzione serve ai <c TCursor_sheet>
{
  int ne = 0;
  if (_inp_id.empty()) return ne;

  TRectype& cur = _cursor->curr();

  cur.zero();
  TRectype filtrec(cur);

  _inp_id.restart();
  _inp_fn.restart();

  TString256 val;                  // Value to output
  bool tofilter;

  for (const char* fld = _inp_id.get(); fld; fld = _inp_id.get())
  {
    if (*fld == '"')
    {
      val = (fld+1);
      if (val.not_empty()) val.rtrim(1);
      tofilter = filter;
    }
    else
    {
      const short id  = field().atodlg(fld);
      const bool filter_flag = strchr(fld, '@') != NULL;
      tofilter = filter && filter_flag;
      val = field(id).get();
      
      const TMask_field& f = field(id);
      if (f.class_id() == CLASS_DATE_FIELD && f.right_justified())
      {
        const TDate d(val);
        val = d.string(ANSI);
      }
      
      if (f.is_edit() && val.not_empty() && !filter_flag) 
        ne++;          // Increment not empty fields count
    }

    TFieldref fldref(_inp_fn.get(), 0); // Output field
    fldref.write(val, *_cursor->relation());
    if (tofilter)
    {
      if (val.empty()) val.fill('~', fldref.len(cur));
      fldref.write(val, filtrec);
    }
  }

  if (!filter) return ne;

  TString work(_filter.size());
  bool filter_update = FALSE;
  
  for (int i = 0; _filter[i]; i++)
  {
    if (_filter[i] == '"')
    { 
      do
      {
        work << _filter[i++];
      } while (_filter[i] && _filter[i] != '"');
      work << '"';
      if (!_filter[i]) break;
    }
    else
      if (_filter[i] == '#')
      {
        work << '"' << field(atoi(&_filter[++i])).get() << '"';
        while (isspace(_filter[i])) i++;
        while (isdigit(_filter[i])) i++;
        i--;
      }
      else
      {
        work << _filter[i];
        if (_filter[i] == '-' && _filter[i + 1] == '>')
          filter_update = TRUE; 
      }
  }

  _cursor->setfilter(work, filter_update);
  _cursor->setregion(filtrec, filtrec);

  return ne;
}


void TBrowse::do_output(CheckTime t)
{   
  if (t == FINAL_CHECK) 
    return;

  TString sum;
  TToken_string flds(24, '+');

  _out_fn.restart();
  for (const char* fld = _out_id.get(0); fld && *fld; fld = _out_id.get())
  {
    const short id = field().atodlg(fld);
    TMask_field& f = field(id);

    flds = _out_fn.get();
    if (t != STARTING_CHECK || f.field() == NULL || f.mask().mode() == MODE_INS)
    {
      sum.cut(0);
      for(const char* fr = flds.get(0); fr; fr = flds.get())
      {  
        if (*fr == '"')
        {       
          sum << (fr+1);
          sum.rtrim(1);
        }
        else
        {
          const TFieldref fld(fr, 0);
          sum << fld.read(*_cursor->relation());
        }  
      }
                    
      const bool hit = f.get() != sum;
      f.set(sum);
      if (field().dlg() != id && hit)
      {
        f.on_hit();
        f.check();
      }
    }
  }
}


void TBrowse::do_clear()
{                  
  for (TString16 fld = _out_id.get(0); fld.not_empty(); fld = _out_id.get())
  {                      
    const short id = field().atodlg(fld);
    TMask_field& f = field(atoi(fld));
    if (f.field() == NULL && _inp_id.get_pos(fld) < 0) 
    {
      const bool hit = f.get().not_empty();
      f.reset();
      if (field().dlg() != id && hit)
      {
        f.on_hit();
        f.check();
      }
    }
  }
}



bool TBrowse::do_link(bool insert)
{
  bool ok = FALSE;
  TString app;

  if (_insert[0] == 'M')
  {
    TString nm(_insert.mid(1));
    if (nm.compare("batb", 4, TRUE) == 0)    // Programma gestione tabelle
      app = format("ba3 -0 %s", (const char*)nm.mid(4));
    // Obbligatorio usare la format globale
    else                              // Programma generico di browse/edit
      app = format("ba3 -3 %s %d", (const char*)nm, _cursor->file().num());
    // Obbligatorio usare la format globale
  }
  else
  {
    app = &_insert[1];
  }
  if (app.find('#') != -1)
  {
    TString w(app);
    app = "";
    for (const char* f = w; *f; f++)
    {
      if (*f == '#') 
      {
        const int id = atoi(++f);
        app << field(id).get();
        while (isspace(*f)) ++f;
        while (isdigit(*f)) ++f; 
        if (*f)
          app << ' ' << *f;
        else 
          break;
      }            
      else
        app << *f;
    }
  }  
  TMailbox mail;
  if (insert)
  {
    TMessage msg(app, MSG_AI, "");
    mail.send(msg);
  }
  else
  {  
    TToken_string body;
    
    body.add(_cursor->key());          
    _inp_fn.restart();
    for (const char* i = _inp_id.get(0); i; i = _inp_id.get())
    {                             
      TString inp_id(i);
      TString16 field_name(_inp_fn.get());
      TString s(field_name);
      const int pos = s.find(",");

      if (pos >= 0)
        s.cut(pos);

      s << "=";
      if (inp_id[0] == '"')    
      {
        inp_id.ltrim(1);                      
        inp_id.rtrim(1);
        s << inp_id;                      
      }
      else
      {
        const short id = field().atodlg(inp_id);
        const TEditable_field & f = field(id);
       
        TFieldref fldref(field_name, 0);
        s << fldref.read(*_cursor->relation());
      }                                       
      body.add(s);
    }  
    TMessage msg(app, MSG_ED, body);
    mail.send(msg);
  }

  TExternal_app a(app);
  a.run();
  field().mask().set_focus();
  
  if (mail.check())
  {
    TMessage* rcv = mail.next_s(insert ? MSG_AI : MSG_LN);
    if (rcv != NULL) _rec = atoi(rcv->body());
    if (_rec > 0)
    {
      _cursor->file().readat(_rec);
      ok = _cursor->ok();
      if (ok) do_output();
#ifdef DBG      
      else yesnofatal_box("Selezione da programma esterno errata");
#endif      
    }
  }
  return ok;
}


TToken_string& TBrowse::create_siblings(TToken_string& siblings)
{
  const TMask& mask = field().mask();
  siblings = "";                      // Azzera la lista dei campi associati
  
  TBit_array key(4);                  // Elenco delle chiavi gia' utilizzate
  key.set(_cursor->key());

  TString fn;                         // Nome campo
  
  // Scorre la lista dei campi di output
  int n = 0;  
  for (const char* i = _out_id.get(0); i; i = _out_id.get(), n++)
  {
    const short id = field().atodlg(i);
    const TEditable_field& f = field(id);
    if (!f.active() || !f.is_edit())  // Scarta i campi non editabili
      continue;
    const TEdit_field& e = (const TEdit_field&)f;
    const TBrowse* b = e.browse();
    if (b == NULL) 
      continue;                       // Scarta i campi senza ricerca
    
    const TCursor* c = b->cursor();  
    
    // Considera ricerche sullo stesso file ma con chiave diversa
    if (c->file().num() == _cursor->file().num() && 
        (key[c->key()] == FALSE || id == field().dlg()))      
    {
      fn = _out_fn.get(n);                    // Legge nome del campo su file          
      int pos = _items.get_pos(fn);           // Determina header corrispondente
      if (pos < 0)                            // Se non lo trova identico ...
      {
        const int q = fn.find('[');
        if (q > 0)
        {
          fn.cut(q);
          pos = _items.get_pos(fn);           // ... ritenta senza parentesi
        }
      }
      if (pos >= 0)
      {
        siblings.add(id);
        const char* h = _head.get(pos); 
        siblings.add(h);
        const int et = siblings.find('@');
        if (et > 0) siblings.cut(et);
        key.set(c->key());                    // Marca la chiave come usata
      }  
    }
  }
  
  return siblings;
}


KEY TBrowse::run()
{
  do_input(TRUE);
  _cursor->read(_isgteq);

  const TLocalisamfile& f = _cursor->file();
  TString caption(prefix().description(f.name()));
  if (!isalnum(caption[0])) 
    caption = "Selezione";
  
  KEY k = K_ESC;
  long selected = 0;

  TToken_string siblings; 
  create_siblings(siblings);                            
  TToken_string vals;
  
{
  byte buttons = 0;
  if (_insert.not_empty())
  {   
    // Mette il bottone di gestione, a meno che ...   
    if (_cursor->items() == 0) buttons = 2; // Non mette il bottone collega se non ci sono elementi
    else buttons = 3; 
    if (_insert[0] != 'G')
    {          
      const TString& maskname = field().mask().source_file();
      if (maskname.compare("batb", 4, TRUE) == 0 && field().in_key(0))
      {
        const char* tabname = _cursor->file().name();
        if (maskname.mid(4, 3).compare(tabname, 3, TRUE) == 0) 
          buttons = 0;
      }
    }
  }
  for (const char* i = _inp_id.get(0); i; i = _inp_id.get())
  {            
    if (*i != '"' && strchr(i, '@') == NULL)
    {
      const short id = field().atodlg(i);
      const TEditable_field& f = field(id);
      if (f.active() && f.is_editable()) 
      {           
        vals.add(i);
        vals.add(f.get());               
        vals.add((int)f.dirty());
      }
    }
  }  
    

  TBrowse_sheet s(_cursor, _items, caption, _head, buttons, &field(), siblings);
  k = s.run();
  selected = s.selected();
}

  switch (k)
  {                
    case K_CTRL+'G':    
      if (_insert[0] == 'G')
      {
        field().set(format("%ld", selected+1));
        TGolem golem(&_insert[1], atol(field().get()));
        k = golem.edit() ? K_ENTER : K_ESC;
        if (k = K_ESC)
          error_box("Impossibile modificare l'oggetto");
      }
      else
      { 
        *_cursor = selected;
        k = do_link(FALSE) ? K_ENTER : K_ESC;
      }
      break;  
    case K_INS:
      if (_insert[0] == 'G')
      {
        TGolem gol(&_insert[1], 0);
        if (gol.import())
          field().set(format("%ld", gol.id()));
        k = K_ENTER;
      }
      else
        k = do_link(TRUE) ? K_ENTER : K_ESC;
      break;
    case K_ENTER:
      *_cursor = selected;
      do_output();
      break;
    default:
      {
        for (const char* i = vals.get(0); i && *i; i = vals.get())
        {
          const short id = field().atodlg(i);
          TEditable_field& f = field(id);
          f.set(vals.get());
          f.set_dirty(vals.get_int());
        }
      }
      if (k >= K_CTRL)
      {
        TMask& m = field().mask();                          
        const int tag = k - K_CTRL - K_F1;
        const short id = siblings.get_int(tag * 2);
        TEdit_field& ef = m.efield(id);
        ef.set_focus();
        k = K_F9;
        if (m.is_running())
          m.send_key(k, id);
      }
      break;
  }
    
  return k;
}

bool TBrowse::check(CheckTime t)
{
  bool passed = TRUE;

  if (_secondary == TRUE && t != RUNNING_CHECK)
    return TRUE;

  if (field().check_type() != CHECK_NONE)
  {
    const TMaskmode mode = (TMaskmode)field().mask().mode();

    CheckType chk = field().check_type();
    if (chk == CHECK_REQUIRED && (t == STARTING_CHECK || mode == MODE_QUERY)) 
      chk = CHECK_NORMAL;
    
    const int ne = do_input(TRUE);
    if (ne || chk == CHECK_REQUIRED)
    {
      _cursor->setkey();
      _cursor->file().read(_isequal);
      passed = _cursor->ok();
      
      if (t != FINAL_CHECK)
      {
        if (passed)
        {
          _cursor->repos();
          do_output(t);
        }
        else 
        {    
          if (chk == CHECK_SEARCH)
          {   
            passed = TRUE;
          }
          else
          {
            do_clear();
            if (!field().mask().query_mode() && field().check_enabled())
              field().set_dirty(3);
          }    
        }  
      }
      else
      {
        if (chk == CHECK_SEARCH)
          passed = TRUE;
      }
    }
    else
    {         
      if (chk == CHECK_SEARCH)
        passed = TRUE;
      else
      {  
        if (t != FINAL_CHECK) 
          do_clear();
      }  
    }  
  }
  return passed;
}

bool TBrowse::empty_check()
{
  if (field().mask().query_mode() || field().check_type() != CHECK_REQUIRED)
    return TRUE;
  else
    return do_input() > 0;
}


///////////////////////////////////////////////////////////
// TEdit_field
///////////////////////////////////////////////////////////

TEdit_field::TEdit_field(TMask* mask)
: TEditable_field(mask), _browse(NULL),
  _check(CHECK_NONE), _check_enabled(FALSE), _forced(FALSE)
{ }

TEdit_field::~TEdit_field()
{
  if (_browse) delete _browse;
}

word TEdit_field::class_id() const
{ return CLASS_EDIT_FIELD; }


void TEdit_field::parse_head(TScanner& scanner)
{
  _ctl_data._size  = scanner.integer();
#ifdef DBG  
  if (_ctl_data._size <= 0)
  {
    _ctl_data._size = 8;
    yesnofatal_box("Il campo %d ha dimensione nulla (uso %d)", 
                   _ctl_data._dlg, _ctl_data._size);
  }
#endif
  _ctl_data._width = scanner.integer();
  if (_ctl_data._width == 0) 
    _ctl_data._width = _ctl_data._size;
}


const TBrowse* TEdit_field::parse_browse(TScanner& scanner) const
{
  const int id = scanner.integer();
  const TEdit_field& f = mask().efield(id);
  const TBrowse* b = f.browse();
  if (b == NULL)
    NFCHECK("La USE del campo %d e' nulla e non puo' essere copiata nel campo %d", id, dlg());
  return b;
}

bool TEdit_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "PI")            // PICTURE
  {
    _picture = scanner.string();
    return TRUE;
  }

  if (scanner.key() == "CH")
  { 
    scanner.pop();
    if (scanner.key() == "NO") _check = CHECK_NORMAL; else 
    if (scanner.key() == "RE") _check = CHECK_REQUIRED; else 
    if (scanner.key() == "FO") { _check = CHECK_REQUIRED; _forced = TRUE; } else 
    if (scanner.key() == "SE") _check = CHECK_SEARCH;
    else _check = CHECK_NONE;
    if (sheet())
    {         
#ifdef DBG          
       NFCHECK("Checktype inutile sul campo %d", dlg());
#endif                       
       _check = CHECK_NONE;
    }
    return TRUE;
  }

  if (scanner.key() == "US")            // USE
  {
#ifdef DBG  
    if (_browse != NULL)
      return yesnofatal_box("USE duplicata nel campo %d", dlg());
#endif
    
    int key = 1;
    TRelation* r = NULL;

    const int logicnum = scanner.integer();
    TString16 tabmaskname;

    if (logicnum > 0) 
      r = new TRelation(logicnum);
    else
    {
      tabmaskname  = scanner.pop();
#ifdef DBG  
      if (tabmaskname.len() > 4)
        return yesnofatal_box("'%s' non e' una tabella valida: %d",
                              (const char*)tabmaskname, dlg());
#endif                    
      r = new TRelation(tabmaskname);
    }

    if (scanner.popkey() == "KE")
    {
      key = scanner.integer();
#ifdef DBG  
      if (key < 1 || key > 15)
      {
        yesnofatal_box("Chiave %d non valida nella USE del campo %d", key, dlg());
        key = 1;
      }
#endif      
    }
    else scanner.push();

    const char* filter = "";
    if (scanner.popkey() == "SE")
      filter = (const char*)scanner.line();
    else
      scanner.push();
        
    _browse = new TBrowse(this, r, key, filter);
  
    if (tabmaskname.not_empty())
    {
      tabmaskname.insert("MBATB", 0);
      browse()->set_insert(tabmaskname);
    }
  
    _check_enabled = TRUE;
    return TRUE;
  }

  if (scanner.key() == "CO")                    // Copyuse
  {
    const TString16 what(scanner.popkey());

    const TBrowse* b = parse_browse(scanner);
    if (b)
    {
      if (what == "US" || what == "AL")
        _browse = new TBrowse(this, b->cursor());
  
      if (_browse) 
      {
        _check_enabled = TRUE;
        return browse()->parse_copy(what, *b);
      }
    }
#ifdef DBG
    return yesnofatal_box("Impossibile COPY senza USE nel campo %d", dlg());
#endif
  }

  if (scanner.key() == "JO")
  {
#ifdef DBG      
    if(browse() == NULL) 
      return yesnofatal_box("JOIN senza USE nel campo %d", dlg());
    else  
#endif
      browse()->parse_join(scanner);
    return TRUE;
  }

  if (scanner.key() == "SH")            // SHEET
  {
#ifdef DBG    
    if (_browse) 
      return yesnofatal_box("SHEET duplicato nel campo %d", _ctl_data._dlg);
#endif
    _browse = new TList_sheet(this, _ctl_data._prompt, scanner.string()); 
    _check_enabled = TRUE;
    return TRUE;
  }

  if (scanner.key() == "IT")            // ITEM
  {
#ifdef DBG    
    if (sheet() == NULL) 
      return yesnofatal_box("ITEM senza SHEET nel campo %d", _ctl_data._dlg);
    else  
#endif    
    sheet()->parse_item(scanner);
    return TRUE;
  }

  if (scanner.key() == "IN")
  {
    if (_browse) 
      _browse->parse_input(scanner);
#ifdef DBG    
    else 
      yesnofatal_box("INPUT senza USE o SHEET nel campo %d", _ctl_data._dlg);
#endif      
    return TRUE;
  }

  if (scanner.key() == "DI")
  {
#ifdef DBG    
    if(!browse()) 
      return yesnofatal_box("DISPLAY senza USE nel campo %d", _ctl_data._dlg);
    else
#endif
    browse()->parse_display(scanner);
    return TRUE;
  }

  if (scanner.key() == "OU")
  {
    if (_browse) 
      _browse->parse_output(scanner);
#ifdef DBG    
    else 
      return yesnofatal_box("OUTPUT senza USE nel campo %d", dlg());
#endif    
    return TRUE;
  }

  if (scanner.key() == "AD")
  {
#ifdef DBG    
    if(browse() == NULL) 
      return yesnofatal_box("ADD senza USE nel campo %d", dlg());
    else  
#endif    
    browse()->parse_insert(scanner);
    return TRUE;
  }

  if (scanner.key() == "ME")
  {                
    TString& l = scanner.line().strip_spaces();
    int m = 0;
    if (l[0] == '0')
    {            
      l.ltrim(1);
      l.ltrim();    
      m = 1;
    }                      
    message(m, TRUE)->add(l);
    return TRUE;
  }

  return TEditable_field::parse_item(scanner);
}


void TEdit_field::create(WINDOW parent)
{
  const int len = create_prompt(parent, 0, 0);
  
  _size = _ctl_data._size;
  _ctl_data._x += len;
  
  if (_browse)                // Decide se attivare o meno il bottone di ricerca
    _ctl_data._flags << 'B';
  
  _ctl = new TField_control(parent, _ctl_data._dlg,
                            _ctl_data._x, _ctl_data._y, 
                            _ctl_data._width, _ctl_data._size,
                            _ctl_data._flags, "");
  
  if (_browse && !enabled_default())
    ((TField_control*)_ctl)->show_button(FALSE);
}

const char* TEdit_field::reformat(const char* str) const
{
  TString& s = _ctl_data._park;
  s = str;
  const byte trim_mode = _flags.trim;
  smart_trim(s, trim_mode);

  if (s.not_empty())
  {            
    word len = s.len();
    if (len > size())
    {
#ifdef DBG      
      yesnofatal_box("Campo %d troppo lungo: %s > %d", dlg(), str, _size);
#endif      
      s.cut(len = size());
    }

    if (_flags.zerofilled)
    {                     
      if  (len < size())
      {
        if (isdigit(s[0]) && real::is_natural(s))
          s.right_just(size(), '0');
      }
      for (const char * w = (const char *) s; *w == '0'; w++) ;
      if (*w == '\0')
        s.cut(0);
    }                                                        

    if (_flags.uppercase)
      s.upper();
  } 
  
  return s;
}

const char* TEdit_field::raw2win(const char* raw) const
{
  return reformat(raw);
}

const char* TEdit_field::win2raw(const char* win) const
{
  return reformat(win);
}

void TEdit_field::set_window_data(const char* data)
{                 
  _ctl->set_caption(data);
}

void TEdit_field::autoselect(bool on)
{
  _ctl->autoselect(on);
}

TString& TEdit_field::get_window_data()
{
  _str = win2raw(_ctl->caption());
  return _str;
}

// Certified 90%
bool TEdit_field::on_hit()
{              
  const int vf = validate_func();
  if (vf >= 0)
  {               
    if (vf == AUTOEXIT_FUNC || vf == NUMCALC_FUNC || vf == STRCALC_FUNC || vf == 21)
    {
      const bool ok = validate(K_TAB);     // Check validation expression
      if (!ok) return FALSE;
    }
  }
  
  const bool ok = handler(K_TAB);
  if (ok && has_message())
  {
    if (empty() && message(1)) 
      do_message(1);
    else 
      do_message(0);
  }
  return ok;
} 

// @doc EXTERNAL

// @mfunc Mostra un messaggio di errore di default per il campo
//
// @rdesc Ritorna sempre FALSE
bool TEdit_field::default_error_box() const

// @comm Se il campo non possiede un warning particolare chiama la <f error_box>
//       indicando genericamente che il valore immesso non e' valido, altrimenti
//       riporta il warning associato al campo
{                 
  if (!has_warning()) 
  {                   
    const TString p(prompt());
    if (isalnum(p[0]))
    {
      const TString val(get());
      
      error_box("Valore non valido per %s: %s", (const char*)p, (const char*)val);
    }
    else  
      error_box("Valore non valido: %s", (const char*)get());
  }    
  else
    error_box(get_warning());
  return FALSE;  
}

bool TEdit_field::on_key(KEY key)
{            
  const int vf = validate_func();
  static bool search_in_progress = FALSE;
  
  switch(key)
  {  
  case K_CTRL+K_TAB:
    set_focusdirty(FALSE);
    break;              
  case K_TAB:
    if (vf >= 0 &&  (vf == AUTOEXIT_FUNC || vf == NUMCALC_FUNC || vf == STRCALC_FUNC))
      set_focusdirty();            // Forza validate
    if (to_check(K_TAB, TRUE))
    {
      if (class_id() == CLASS_EDIT_FIELD) // Altrimenti l'ha gia' fatto
      {
        const TString& raw = get_window_data();
        set_window_data(raw2win(raw));
      }
      
      bool ok = validate(key);     // Check validation expression
      if (!ok)
      {      
        if (has_warning()) 
          default_error_box();
        return FALSE;
      }

      TMask& m = mask();
      const bool query = m.query_mode();
      
      if (_browse)
      {
        if (sheet()) 
        {
          ok = query || sheet()->check();  // Check consistency
        }  
        else
        {
//          if (check_enabled() && (!query || forced()) && vf != 21) 
          if (check_enabled() && vf != 21)
          { 
            if (!query || forced()) 
              ok = browse()->check();                                
            else
              browse()->check();                                
          }                    
        }    
      }                   
      if (ok) 
        ok = on_hit();
      else           
      {
        if (empty())
          ok = TRUE;
        else
          default_error_box();       
       }
        
      
      if (ok)
      {                
        set_focusdirty(FALSE);
        if (query && m.is_running() && in_key(0))
          test_key_complete();
      }
      else
        set_focusdirty(!empty());
      
      return ok;
    }
    else
      handler(K_TAB);
    break;
  case K_ENTER:
//    if (field() != NULL || mask().mode() == NO_MODE)
    {
      bool ok = validate(K_ENTER);                           // Check validation expression
      if (!ok)
      {
        if (has_warning()) 
          default_error_box();
        return FALSE;
      }
      
      if (!mask().query_mode() || forced())
      {
        ok = !(check_type() == CHECK_REQUIRED && empty());
        // check consistency
        if (ok && _browse)
        {
          if (sheet()) 
            ok = sheet()->check(FINAL_CHECK);  
          else 
          {
            if (ok && check_enabled() && vf != 21) // 21 = NOT_EMPTY_CHECK_FIELD
            {
              if (dirty()) ok = browse()->check(FINAL_CHECK);     // Check consistency
              else ok = browse()->empty_check();
            }
          }
        }
        if (!ok) return default_error_box();
      }  
    }
    break;
  case K_F2:
    set("");
    break;
  case K_F9:
    if (check_enabled())
    {  
      if (focusdirty()) 
        get_window_data();

      KEY k = K_ESC;
      if (_browse) k = _browse->run();
      else beep();

      if (k != K_F9) set_focus();
      
      if (k == K_ENTER)
      {     
        set_dirty();
        if (on_hit())
        {
          TMask & m = mask();
    
          if (m.is_running())   
          {                     
            set_focusdirty(FALSE);       // Evita doppia esecuzione handlers!
            if (m.query_mode() && in_key(0))
              test_key_complete();
            if (m.is_running())
            {                 
              set_focusdirty(FALSE);
              send_key(K_TAB, 0);        // Passa al campo successivo
            }  
          }
        }
        return TRUE;
      } 
      else 
        return FALSE;
    }
    else
      if (_browse == NULL && has_button())
        return handler(K_F9);
    break;                
  case K_CTRL+K_SPACE:
    set_dirty(TRUE);
    return handler(K_SPACE);
  default:
    break;
  }

  return TEditable_field::on_key(key);
}

// @mfunc Controlla se il campo ha la necessita' di eseguire una ricerca sul cursore o
//        sulla tabella associata al momento della sua inizializzazione
//
// @rdesc Ritorna TRUE se il campo ha un TArray_sheet associato oppure un TCursor_sheet 
//        con check_type non nullo.
bool TEdit_field::has_check() const
{          
  bool ok = FALSE;
  if (_browse)
    ok = browse() == NULL || check_type() != CHECK_NONE;
  return ok;  
}

bool TEdit_field::check(CheckTime t)
{                
  bool ok = TRUE;
  if (check_enabled() || (t == STARTING_CHECK && shown()))
  {                        
    if (_browse && validate_func() != 21)
      ok = _browse->check(t);
  }
  return ok;
}

// @doc EXTERNAL

void TEdit_field::enable(bool on)
{
  TEditable_field::enable(on);
  if (_browse)
    ((TField_control*)_ctl)->show_button(on);
}

// @mfunc Permette di abilitare/disabilitare un check di un campo
void TEdit_field::enable_check(
  bool on) // @parm Operazione da svolgere
  //
  // @flag TRUE | Abilita il check del campo (default)
  // @flag FALSE | Disabilita il check del campo
{
  _check_enabled = on;
  ((TField_control*)_ctl)->show_button(on);
}


///////////////////////////////////////////////////////////
// Date_field
///////////////////////////////////////////////////////////

TDate_field::TDate_field(TMask* m) : TEdit_field(m)
{ }

word TDate_field::class_id() const
{ return CLASS_DATE_FIELD; }

// Certified 100%
bool TDate_field::is_kind_of(word cid) const
{ return cid == CLASS_DATE_FIELD || TEdit_field::is_kind_of(cid); }

void TDate_field::create(WINDOW w)
{
  // Elimina il flag R che si interpreta come salva in formato ANSI 
  _ctl_data._flags.strip("AR");    
  
  TEdit_field::create(w);
  if (automagic())
  {
    TDate d(TODAY);
    set(d.string());
  }
}

void TDate_field::parse_head(TScanner&) 
{
  _ctl_data._size = _ctl_data._width = 10;
}

// @doc EXTERNAL

// @mfunc Permette di formattare la data secondo i flag correnti
//
// @rdesc Ritorna la data formattata
const char* TDate_field::win2raw(
  const char* datum) const // @parm Stringa contenenete la data da formattare
  
  // @comm Permette di gestire anche alcuni date particolari come:
  //
  // @flag IERI | Inserisce la data del giorno precedente a quella del sistema
  // @flag OGGI | Inserisce la data del sistema
  // @flag DOMANI | Inserisce la data del giorno successivo a quella del sistema
  // @flag PRIMO | Primo giorno dell'anno (01-01-aa)
  // @flag ULTIMO | Ultimo giorno dell'anno (31-12-aa)
  // @flag NATALE | Giorno di natale (25-12-aa)
{ 
  TString& s = _ctl_data._park;
  s = datum; s.trim();
  if (s.not_empty())
  {
    if (isdigit(s[0]) && s.len() < 10)
    {                   
      long d;
      int m, y;
      const int items = sscanf(s, "%ld-%d-%d", &d, &m, &y);
      switch(items)
      {      
      case 1:              
        d = atoi(s.left(2));
        m = atoi(s.mid(2, 2)); 
        y = atoi(s.mid(4));
        break;
      case 2:  
        y = 0;
        break;
      case 3: 
        break;
      default:            
        d = 0;
        break;
      }
      if (d > 0)
      {
        if (m <= 0 || y <= 0)
        {
          const TDate oggi(TODAY);
          if (m <= 0) m = oggi.month();
          if (y <= 0) y = oggi.year();
        }  
        if (y < 100) 
          y += (y <= 10) ? 2000 : 1900;
        s.format("%02d-%02d-%4d", int(d), m, y);
      }  
    }
    else
    {          
      bool ok = TRUE;
      TDate g(TODAY);
      s.upper();
      if (s == "IERI")
      {
        --g;
      } else
      if (s == "DOMANI")
      {
        ++g;
      } else
      if (s == "POSDOMANI")
      {
        g += 2;
      } else
      if (s == "PRIMO") 
      {
        g.set_month(1);
        g.set_day(1);
      } else
      if (s == "NATALE")
      {
        g.set_month(12);
        g.set_day(25);
      } else
      if (s == "ULTIMO")
      { 
        g.set_month(12);
        g.set_day(31);
      } else
      if (s != "OGGI")
        ok = FALSE;
      if (ok)
        s = g.string();  
    }
  }

  return s;
}

const char* TDate_field::reformat(const char* str) const
{                
  TString& s = _ctl_data._park;
  s = str;
  if (s.len() == 8) 
    s = TDate(str).string();
  else
    if (s.blank())
      s.cut(0);  
  return s;  
}

const char* TDate_field::raw2win(
  const char* datum) const // @parm Stringa contenenete la data da formattare
{
  return reformat(datum);  
}

bool TDate_field::on_key(KEY key)
{
  if (to_check(key))
  {
    const TString& data = get_window_data();  
    if (data.not_empty())        // data not empty
    {
      if (!TDate::isdate(data))
      {
        error_box("Data errata o formato non valido");
        return FALSE;
      }
      else
        set_window_data(data);
    }
  }
  else
  {
    if (_ctl->is_edit_key(key))
    {                                                          
      const bool ok = strchr("-0123456789ADEGILMNOPRSTUadegilmnoprstu", key) != NULL;
      if (!ok) beep();
      return ok;   
    }
  }

  return TEdit_field::on_key(key);
}

bool TDate_field::autosave(TRelation& r) const
{         
  if (field())
  {
    const char* td = get();
    if (right_justified())
    {
      TDate d(td);
      td = d.string(ANSI);
    }
    field()->write(td, r);
    return TRUE;
  }
  return FALSE;
}

///////////////////////////////////////////////////////////
// Real_field
///////////////////////////////////////////////////////////

TReal_field::TReal_field(TMask* m) : TEdit_field(m)
{ }

word TReal_field::class_id() const
{ return CLASS_REAL_FIELD; }

// Certified 100%
bool TReal_field::is_kind_of(word cid) const
{ return cid == CLASS_REAL_FIELD || TEdit_field::is_kind_of(cid); }

void TReal_field::create(WINDOW w)
{ 
  _ctl_data._flags.strip("AFM");
  if (!roman())
    _ctl_data._flags << 'R';     // Forza l'allineamento a destra per i numeri
  
  TEdit_field::create(w);
  
  if (_flags.firm)         
    TMask_field::set(main_app().get_firm()); 
  else
    if (automagic())
      TMask_field::set(TDate(TODAY).year());
}

bool TReal_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "PI")            // PICTURE
  {
    _picture = scanner.string();
    if (_decimals == 0)
    {
      if (_picture[0] == '.')
        _decimals = atoi(&_picture[1]);
      else
      {  
        const int comma = _picture.find(',');
        if (comma >= 0)
          _decimals = _picture.len() - comma - 1;
      }    
    }  
//#ifdef DBG                                          
//    if (_ctl_data._size > 9 && _ctl_data._size != 15) 
//      ::warning_box("Guy propone una dimensione di %d per il campo %d: %s\nMa probabilmente ha toppato ...", 
//                    preferred_size, _ctl_data._dlg, (const char*)_ctl_data._prompt);
//#endif    
    return TRUE;
  }
  
  return TEdit_field::parse_item(scanner);
}

const char* TReal_field::reformat(const char* data) const
{
  TString& str = _ctl_data._park;
  
  if (!real::is_null(data))
  {       
    str = data;
    if (_flags.zerofilled)
    {  
      word s = size();
      if (roman())
      {
        s = decimals();
        if (s <= 0) s = 4;
      }
      str.right_just(s, '0');
    }  
  }
  else
    str.cut(0);
  return str;  
}


bool TReal_field::on_key(KEY key)
{
  if (to_check(key))
  {
    const TString& n = get_window_data();
    if (roman())
    {
      const int r = atoi(n);
      if (r < 0) return error_box("Numero romano errato");
    }
    else
    {
      if (!real::is_real(n))
        return error_box("Valore numerico non valido");
      if (_flags.uppercase && real(n).sign() < 0)
        return error_box("Il numero deve essere positivo");
      
      if (key == K_TAB && _flags.firm)
      {                         
        const long f = atol(n);
        if (f > 0 && prefix().exist(f))
          main_app().set_firm(f);
      }  
    }
    set_window_data(raw2win(n));
  } 
  else
  {
    if (_ctl->is_edit_key(key))
    {                                                          
      bool ok = TRUE;
      switch (key)                                     
      {                 
        case '.': 
          ok = !(_flags.zerofilled || _flags.roman || size() < 4); 
          break;
        case ',': 
          ok = _decimals > 0; 
          break;
        case '-': 
          ok = !_flags.uppercase; 
          break;
        default : 
          if (_flags.roman)
            ok = strchr("0123456789IVXLCDMivxlcdm", key) != NULL; 
          else
            ok = strchr("0123456789", key) != NULL; 
          break;
      }      
      if (!ok)
        beep();  
      return ok;
    }
  }

  return TEdit_field::on_key(key);
}

void TReal_field::parse_head(TScanner& scanner)
{
  _ctl_data._size  = scanner.integer();

#ifdef DBG      
  if (_ctl_data._size <= 0)
  {
    _ctl_data._size = 9;
    yesnofatal_box("Il campo %d ha dimensione nulla (uso %d)", _ctl_data._dlg, _ctl_data._size);
  }
#endif
  
  _ctl_data._width = _ctl_data._size;
  _decimals = scanner.integer();
}

const char* TReal_field::raw2win(const char* data) const
{           
  TString& s = _ctl_data._park;
  
  if (data == NULL) data = "";
  
  if (roman())
  {
    s = itor(atoi(data));
  }
  else
  { 
    if (!real::is_null(data))
    {
      real n(data);
      if (exchangeable() && decimals())
      {
        const real& e = mask().exchange();
        n /= e;
      }
      if (_picture.empty())
      {
        if (_flags.zerofilled)
          s = n.stringa(_size, _decimals, '0');
        else
          s = n.stringa(0, _decimals);  
      }  
      else
      {
        s = n.string(_picture);
      }  
    } 
    else 
      s.cut(0);
  } 
  
  return s;
}

const char* TReal_field::win2raw(const char* data) const
{
  TString& str = _ctl_data._park;

  if (roman())
  {
    int r = atoi(data);    
    if (r == 0)            // Se non e' scritto in arabo converti da romano
      r = rtoi(data);
    if (r > 0)
    {
      int d = decimals();
      if (d < 1) d = 4;
      if (_flags.zerofilled)
        str.format("%0*d", d, r);
      else
        str.format("%*d", d, r);
    }
    else 
      str.cut(0);
  }
  else
  {
    real n(real::ita2eng(data));
    if (exchangeable() && decimals())
    {      
      const real& e = mask().exchange();
      n *= e;
    }
    if (n.is_zero()) 
      str.cut(0);
    else 
    {
      if (_flags.zerofilled)
        str = n.string(_size, _decimals, '0');
      else
        str = n.string();
    }
  }     

  return str;
} 


void TReal_field::set_decimals(int d) 
{ 
  _decimals = d; 
  if (_picture[0] == '.')
  {
    if (d > 0) _picture.format(".%d", d);
    else _picture = ".";
  }
}


void TReal_field::exchange(bool show_value, const real& nuo)
{                                
  const int dec = show_value ? 2 : 0;            // Quanti decimali voglio
  const bool value = decimals() != 0;            // Sono attualmente in valuta ?
  
  if (show_value == value && show_value) return; // Se cambio da valuta a valuta esco subito! 
  
  if (mask().is_running())
  {
    const char* n = get_window_data();
    if (*n)
    {
      const real& vec = mask().exchange();
      real r(n); 
      if (!show_value) 
        r *= nuo;
      r /= vec;
      r.round(dec);
      set_decimals(dec);
      TEdit_field::set_window_data(r.string(_picture));
    }  
  }  
  
  if (decimals() != dec)
    set_decimals(dec);
}

///////////////////////////////////////////////////////////
// List_field
///////////////////////////////////////////////////////////

TList_field::TList_field(TMask* m) 
           : TEditable_field(m), _size(0)
{ }

word TList_field::class_id() const
{ return CLASS_LIST_FIELD; }


void TList_field::read_item(TScanner& scanner)
{
  TToken_string ts(scanner.string());
  _codes.add(ts.get());
  
  const char* v = ts.get();
  _values.add(v);
  
  CHECKS(v == NULL || strlen(v) <= (word)_ctl_data._width, "List item is too long:", v);

  ts.cut(0);
  while (scanner.popkey() == "ME")
    ts.add(scanner.line().strip_spaces());
  scanner.push();
  *message(-1, TRUE) = ts;
}

void TList_field::parse_head(TScanner& scanner)
{
  _ctl_data._size = scanner.integer();
  _ctl_data._width = scanner.integer();
  if (_ctl_data._width == 0) 
    _ctl_data._width = _ctl_data._size;
}

bool TList_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "IT")            // ITEM
  {
    read_item(scanner);
    return TRUE;
  }

  return TEditable_field::parse_item(scanner);
}

int TList_field::items() const
{
  return _codes.items();
}

// @doc EXTERNAL

// @mfunc Aggiunge delle voci al list sheet
void TList_field::add_item(   
  const char* s) // @parm Voci da aggiungere
  
  // @comm Se il parametro <p s> e' passato con il formato di una <c TToken_string> vengono
  //                     aggiunte tante voci quanti sono gli elementi.
{
  TToken_string t(s);         
  TString item(t.get());
  const int pos = _codes.get_pos(item);
  
  if (pos < 0)
  {                                         
    _codes.add(item);
    item = t.get();
    _values.add(item);
    ((TListbox_control*)_ctl)->set_values(_codes, _values);
  }     
}


void TList_field::delete_item(const char* s)
{
  const TString t(s);         
  const int pos = _codes.get_pos(t);
  
  if (pos >= 0 )
  {                                         
    _codes.destroy(pos);
    _values.destroy(pos);
    ((TListbox_control*)_ctl)->set_values(_codes, _values);
    
    if (pos == current())
    {
      current(0);
      if (active() || ghost()) 
        on_hit();
    }
  }     
}


void TList_field::add_list()
{
  if (roman() && _codes.items() < 12)
  {
    TString csafe, vsafe;
    if (atoi(_codes) > 0)
    {
      csafe = _codes; _codes = "";
      vsafe = _values; _values = "";
    }
    _codes.add("01|02|03|04|05|06|07|08|09|10|11|12");
    _values.add("Gennaio|Febbraio|Marzo|Aprile|Maggio|Giugno|"
                "Luglio|Agosto|Settembre|Ottobre|Novembre|Dicembre");
    if (atoi(csafe) > 0)
    {
      _codes.add(csafe);
      _values.add(vsafe);
      if (message(0))
      {
        *message(_codes.items()-1, TRUE) = *message(0);
        message(0)->cut(0);
      }
    }
  }   
  ((TListbox_control*)_ctl)->set_values(_codes, _values);

  if (roman() && automagic())
  {
    const char* init = format("%02d", TDate(TODAY).month());
    set(init);
  }
  else
    current(0);
}                                                                    


const char* TList_field::win2raw(const char* data) const
{                                                     
  // fool the compiler to keep const
  TToken_string& codes = ((TList_field*)this)->_codes;
  TToken_string& value = ((TList_field*)this)->_values;
                
  _ctl_data._park = data;                       // data puo' venire da una TToken_string::get
  int pos = 0;
  
  if (_ctl_data._park.len() > 2)                // E' veramente una descrizione
    pos = value.get_pos(_ctl_data._park);
  else                                          // Oppure e' un codice
    pos = codes.get_pos(_ctl_data._park);
  
  if (pos < 0 && _ctl_data._park.blank())
  {              
    _ctl_data._park = codes.get(0);             // Uso codes come riferimento per blank
    if (_ctl_data._park.blank())
      pos = 0;
  }  
  
  CHECKS(pos >= 0, "Unknown listbox value:", data);
  return codes.get(pos);              
}

const char* TList_field::raw2win(const char* data) const
{
  // fool the compiler to keep const
  TToken_string& codes = ((TList_field*)this)->_codes;
  TToken_string& value = ((TList_field*)this)->_values;
  
  _ctl_data._park = data;                       // data puo' venire da una TToken_string::get
  int pos = codes.get_pos(_ctl_data._park);
  if (pos < 0 && _ctl_data._park.blank())
  {              
    _ctl_data._park = codes.get(0);
    if (_ctl_data._park.blank())
      pos = 0;
  }  
  CHECKS(pos >= 0, "Unknown listbox code:", (const char*)_ctl_data._park);
  return value.get(pos);              
}

bool TList_field::select_by_ofs(int n)
{
  const bool changed = ((TListbox_control*)_ctl)->select_by_ofs(n);
  if (changed)
    _str = _codes.get(current());
  return changed;  
}

bool TList_field::select_by_initial(char c)
{
  const bool changed = ((TListbox_control*)_ctl)->select_by_initial(c);
  if (changed)
    _str = _codes.get(current());
  return changed;  
}                                            

// @doc EXTERNAL

// @mfunc Sostituisce alle voci correnti quelle passate come parametri
void TList_field::replace_items(
  const char* codes,  // @parm Codici da sostituire ai correnti
  const char* values) // @parm Voci da sostituire a quelle correnti

  // @comm I parametri <p codes> e <p values> devono essere TToken_string se lo voci da sostiutire
  //                     sono piu' di una
{
  _codes = codes;
  _values = values;
  if (_ctl)
    add_list();
}


void TList_field::create(WINDOW parent)
{
  const int len = create_prompt(parent, 0, 0);
  _ctl_data._x += len;
  
  _size = _ctl_data._width;
  
  _ctl = new TListbox_control(parent, _ctl_data._dlg,
                            _ctl_data._x, _ctl_data._y,
                            _ctl_data._width,
                            _ctl_data._flags, _ctl_data._prompt, 
                            _codes, _values);
  add_list();
}

int TList_field::str2curr(const char* data)
{
  TString str(data);

  if (roman() && str.len() < 2)
    str.insert("0",0);
  if (_flags.uppercase)
    str.upper();        

  int i = str.blank() ? 0 : _codes.get_pos(str);
  if (i < 0)      // Se non trova il codice ritenta dopo trim
  {
    for (i = 0; str[i] == '0' || str[i] == ' '; i++);
    if (i > 0)
    {
      str.ltrim(i);
      i = _codes.get_pos(str);
    }
  }

  if (i < 0)
  {
    if (items() && str.not_empty())
      NFCHECK("'%s' non e' un valore valido per il campo %s: %d",
              data, prompt(), dlg());
    i = 0;
  }
  return i;
}

void TList_field::reset()
{         
  if (!_flags.persistent)
     current(0);
}

void TList_field::set(const char* data)
{
  const int i = str2curr(data);
  current(i);
//  if (mask().is_running())
    set_dirty();
}

void TList_field::set_window_data(const char* data)
{                                      
  NFCHECK(0,"So' passato da 'sta stronza!!!");
  const int i = str2curr(win2raw(data));
  current(i);
}

void TList_field::current(int n)
{                 
  if (_ctl)     
    ((TListbox_control*)_ctl)->select(n);
  _str = _codes.get(n);
  _str.trim();
}

int TList_field::current() const
{                                                       
  int c;
  if (_ctl)
    c = ((TListbox_control*)_ctl)->selected();
  else
    c = ((TList_field*)this)->_codes.get_pos(_str);   // Fool the compiler for const sake
  return c;   
}

TString& TList_field::get_window_data()
{                                    
  const int c = current();
  _str = ((TList_field*)this)->_codes.get(c);
  _str.trim();
  return _str;
}

bool TList_field::on_hit()
{
  const bool ok = handler(K_SPACE);
  if (ok)
  {
    const int n = current();
    do_message(n);
  }  
  return ok;
}

bool TList_field::on_key(KEY key)
{                 
  switch(key)
  {         
  case K_CTRL+K_TAB:
    set_focusdirty(FALSE);
    break;              
  case K_SPACE:
    get_window_data();
    set_dirty();
    return on_hit();
  case K_F2:  
    current(0);
    break;
  default: 
    if (to_check(key, TRUE))
    {         
      bool ok = TRUE;
      if (validate_func() >= 0)
      {
        ok = validate(key);
        if (!ok) 
        {
          if (has_warning()) 
            return error_box(get_warning());
        }    
      }
    }
    break;
  }  
  return TEditable_field::on_key(key);
}
           
// Certified 100%
bool TList_field::is_kind_of(word cid) const
{ return cid == CLASS_LIST_FIELD || TEditable_field::is_kind_of(cid); }

           
           
///////////////////////////////////////////////////////////
// TRadio_field
///////////////////////////////////////////////////////////

TRadio_field::TRadio_field(TMask* mask)
            : TList_field(mask)
{ }

// Annulla _ctl altrimenti verrebbe cancellato due volte essendo anche nell'array
TRadio_field::~TRadio_field()
{ }


word TRadio_field::class_id() const
{
  return CLASS_RADIO_FIELD;
}

void TRadio_field::create(WINDOW parent)
{
  const int items = _codes.items();
  if (_ctl_data._prompt.not_empty())
  {
    const word dy = _flags.zerofilled ? 3 : items+2;
    create_prompt(parent, _ctl_data._width, dy);
  }
  
  _ctl = new TRadiobutton_control(parent, _ctl_data._dlg, 
                                  _ctl_data._x + 1, _ctl_data._y + 1, 
                                  _ctl_data._width - 2, _flags.zerofilled ? 1 : items,
                                  _ctl_data._flags, _values);
  current(0);
}


int TRadio_field::current() const
{
  int c;
  if (_ctl)
    c = ((TRadiobutton_control*)_ctl)->get_checked();
  else
    c = TList_field::current(); 
  return c;
}


void TRadio_field::current(int n)
{
  if (_ctl)
    ((TRadiobutton_control*)_ctl)->check_button(n);
  _str = _codes.get(n);
  _str.trim();
}


///////////////////////////////////////////////////////////
// TMemo_field
///////////////////////////////////////////////////////////

TMemo_field::TMemo_field(TMask* mask) : TEdit_field(mask)
{
}

TMemo_field::~TMemo_field()
{                     
}

void TMemo_field::parse_head(TScanner& scanner)
{
  _ctl_data._width  = scanner.integer();
  _ctl_data._height = scanner.integer();
}

void TMemo_field::create(WINDOW parent)
{                     
  create_prompt(parent, 0, 1);  
  
  if (_ctl_data._height > 1)
    _ctl_data._height--;
  
  _size = 8192;
  _ctl = new TMultiline_control(parent, _ctl_data._dlg, 
                                _ctl_data._x, _ctl_data._y+1,
                                _ctl_data._width, _ctl_data._height, _size,
                                _ctl_data._flags, "");
}           

// Certified 100%
// Aggiusta un valore letto da file in formato RAW
const char* TMemo_field::reformat(const char* data) const
{
  return data;
}

const char* TMemo_field::raw2win(const char* data) const
{
  if (strchr(data, '\n') == NULL)
    return data;         
    
  TString& s = _ctl_data._park;
  s = data;
  for (int i = 0; s[i]; i++)
    if (s[i] == '\n') s[i] = '\r';
  return s;
}

const char* TMemo_field::win2raw(const char* data) const
{
  if (strchr(data, '\r') == NULL)
    return data;
  _ctl_data._park = data;
  for (char* s = (char*)(const char*)_ctl_data._park; *s; s++)
    if (*s == '\r') *s = '\n';
  return _ctl_data._park;
}

bool TMemo_field::on_key(KEY k)
{
  if (k == K_TAB && focusdirty())
    get_window_data();
  return TEdit_field::on_key(k);
}


///////////////////////////////////////////////////////////
// Zoom_field
///////////////////////////////////////////////////////////

TZoom_field::TZoom_field(TMask* mask) 
           : TEdit_field(mask)
{
} 

TZoom_field::~TZoom_field( )
{
} 

void TZoom_field::create(WINDOW parent)
{
  _ctl_data._flags << 'B';
  TEdit_field::create(parent);
}

bool TZoom_field::on_key( KEY key )
{ 
  switch (key)
  {
  case K_TAB:
    if (focusdirty())
      get_window_data();
    break;
  case K_F9:
    if (browse() != NULL)
      break;
  case K_F8:  
  {
    get_window_data();
    TMask m("Zoom", 1, 72, 18);
    m.add_memo(101, 0, prompt(), 1, 0, -1, -3);
    m.add_button(DLG_OK,     0, "Conferma", -12, -1, 9, 2);
    m.add_button(DLG_CANCEL, 0, "Annulla", -22, -1, 9, 2);
    m.set(101, _str);
    if (m.run() != K_ESC)
    {
      _str = m.get(101);
      set_window_data(raw2win(_str));
    }
    return TRUE;
  }
    break;
  default:
    break;
  }  
  return TEdit_field::on_key(key);  
} 

// Certified 100%
// Aggiusta un valore letto da file in formato RAW
const char* TZoom_field::reformat(const char* data) const
{
  return data;
}

const char* TZoom_field::raw2win(const char* data) const
{         
  char* n = strchr(data, '\n');
  if (n != NULL) *n = '\0';
  _ctl_data._park = data;
  if (n != NULL) *n = '\n';
  return _ctl_data._park;
}

const char* TZoom_field::win2raw(const char* data) const
{                
  TString& str = _ctl_data._park;
  str = data;                         
  
  // Elimino improbabili righe successive da data
  int a_capo = str.find('\r');        
  if (a_capo >= 0) str.cut(a_capo);
  
  // Aggiungo le righe del memo a partire dalla seconda
  a_capo = _str.find('\n');
  if (a_capo >= 0)
  {
    const char* d = _str;
    str << (d+a_capo);
  }  
  
  return str;  
}

const char* TZoom_field::get_first_line() const 
{ return raw2win(_str); }
