#include <time.h>
#include <stdio.h>

#include <applicat.h>
#include <browfile.h>
#include <colors.h>
#include <msksheet.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#define NULL_PAGE 256

HIDDEN const char* const MASK_EXT = "msk";

///////////////////////////////////////////////////////////
// TMask methods
///////////////////////////////////////////////////////////

HIDDEN bool moving_focus = FALSE;


bool TMask::test_focus_change(WINDOW next)
{                  
  bool ok = TRUE;
  
  TMask_field& prev = fld(_focus);      
  _next_fld = next == NULL_WIN ? DLG_NULL : fld(find_field_win(next)).dlg();
  
  if (prev.win() != next)
  {      
    ok = prev.test_focus_change();
    if (!ok)                        // Test if previous field agrees ...
    {
      set_focus();
      prev.set_focusdirty(FALSE);
    }  
    else
    {
      if ( prev.focusdirty() )
        ok = on_dirty( prev );
    }

  }
  return ok;  
}

void TMask::control_handler(EVENT* ep)
{
  const WINDOW win = ep->v.ctl.ci.win;
  const WIN_TYPE type = ep->v.ctl.ci.type;

  TMask_field* f = (TMask_field*)xvt_vobj_get_data(win);
  CHECK(f != NULL, "Invalid field pointer in control");

  if (type == WC_CHECKBOX)
  {
    if (test_focus_change(win))
    {
      xvt_check_box(win, !xvt_get_checked_state(win));
      set_focus_win(win, FALSE);
      f->on_key(K_SPACE);
    }
    return;
  }

  if (type == WC_RADIOBUTTON)
  {
    if (moving_focus == FALSE)
    {
      if (test_focus_change(win))
      {
        ((TRadio_field*)f)->check_radiobutton(win);
        set_focus_win(win, FALSE);
        f->on_key(K_SPACE);
      }  
    } else moving_focus = FALSE;
    return;
  }

  if (type == WC_PUSHBUTTON)
  {
    if (test_focus_change(win))
    {
      set_focus_win(win, FALSE);
      f->on_hit();
    }  
    return;
  }
  
  if (type == WC_LISTBUTTON)
  {
    if (test_focus_change(win))
    {
      set_focus_win(win, FALSE);
      f->on_key(K_SPACE);
    }  
    return;
  }

  if (ep->v.ctl.ci.v.edit.focus_change)
  {
    if (ep->v.ctl.ci.v.edit.active)
    {                        
      if (test_focus_change(win))
      {
        set_focus_win(win, FALSE);
        f->set_focusdirty(FALSE);
      }  
    }
  }
  else
  { // Contents of control were changed
    f->on_key(K_SPACE);
  }
}

void TMask::handler(WINDOW win, EVENT* ep)
{               
  switch (ep->type)
  {
  case E_UPDATE:
#if XVT_OS == XVT_OS_WIN
    if (win != toolwin())
    {
      xvt_dwin_clear(win, MASK_BACK_COLOR);
      RCT r; xvt_vobj_get_client_rect(win, &r); r.right--; r.bottom--;
      xvt_draw_rect(win, r, MASK_LIGHT_COLOR, MASK_DARK_COLOR, 1);
    }
    else xvt_dwin_clear(win, MASK_DARK_COLOR);
#else
    xvt_dwin_clear(win, MASK_BACK_COLOR);
#endif
    update();
    return;
  case E_COMMAND:
    if (ep->v.cmd.tag == M_FILE_NEW)
      on_firm_change();
    break;  
  case E_CONTROL:
    switch(ep->v.ctl.id)
    {
    case DLG_OK:
      if (test_focus_change(ep->v.ctl.ci.win)) 
        stop_run(K_AUTO_ENTER); 
      break;
    case DLG_CANCEL  :
      if (test_focus_change(ep->v.ctl.ci.win)) 
        stop_run(K_ESC); 
      break;
    case DLG_QUIT    :
      if (test_focus_change(ep->v.ctl.ci.win)) 
        stop_run(K_FORCE_CLOSE); 
      break;
    case DLG_F9:  
    {
      WINDOW w = ep->v.ctl.ci.win;
      TMask_field* f = (TMask_field*)xvt_vobj_get_data(w);
      w = f->win();
      if (test_focus_change(w))
        f->on_key(K_F9);       // Attiva ricerca sul campo associato al bottone
    }    
      break;
    default:
      control_handler(ep);
      break;
    }
    break;
  default:
    break;
  }
  TWindow::handler(win, ep);
}

void TMask::init_mask()
{
  _sheets = _pages = 0;                       // Azzera numero pagine e sheets
  
  _enabled.set(MAX_PAGES);
  _enabled.set();                             // Abilita tutte le pagine
  
  _focus = _first_focus = 0;                  // Nessuno ha il focus
  _page = -1;                                 // Nessuna pagina corrente
  _handler = NULL;                            // Nessun handler utente
  _mode = NO_MODE;                            // Inizializza modo
  _exchange = 1.0;                            // Il cambio per la valuta e' la lira

  memset(_pagewin, 0, sizeof(_pagewin));
  memset(_pagepag, 0, sizeof(_pagepag));
  memset(_pagetag, 0, sizeof(_pagetag));
}


TMask::TMask(const char* title, int pages, int cols, int rows, int xpos,
             int ypos)
{
  init_mask();
  for (_pages = 0; _pages < pages; _pages++)
    _pagewin[_pages] = create(xpos, ypos, cols, rows, title, WSF_CLOSE, WD_MODAL);
  set_win(NULL_WIN);
  add_buttons();
}


void TMask::read_mask(const char* name, int num, int max)
{
  if (max <= 0) max = MAX_PAGES;           
  
  _source_file = name;
  _source_file.ext(MASK_EXT);
  _source_file.lower();
  TScanner scanner(_source_file);

  _sheetmask = num > 0;
  
  long start_t = clock();
  if (!_sheetmask) 
    _total_time = _build_time = _init_time = 0;
  
  for (int i = 0; i < num; i++)
  {
    while (scanner.ok())
      if (scanner.line() == "ENDMASK") break;
  }

  init_mask();

  main_app().begin_wait();
  while (scanner.ok() && scanner.popkey() != "EN")
  {
    if (scanner.key() == "PA")
    {
      CHECKD(_pages < MAX_PAGES, "Maschera con troppe pagine: ", _pages);
      _pagewin[_pages++] = read_page(scanner, FALSE);
      if (_pages >= max)
        break;
    } else
      if (scanner.key() == "TO")
      {
        CHECK(toolwin() == NULL_WIN, "La maschera puo' avere una sola TOOLBAR");
        _pagewin[MAX_PAGES] = read_page(scanner, TRUE);
      }
  }
  main_app().end_wait();
  
  if (_pages < 1)
    fatal_box("Impossibile leggere la maschera %s", name);

  add_buttons();
  
  if (!_sheetmask)
    _total_time = clock()-start_t;
}


void TMask::add_buttons()
{
#if XVT_OS == XVT_OS_WIN
  for (int p = 0; p < _pages; p++)
  {
    if (_pages > 1)
    {                                                    
      const long flags = (p < _pages-1 ? 0x1 : 0x0) | (p > 0 ? 0x2 : 0x0);
      _pagepag[p] = xvt_create_control(WC_PUSHBUTTON, 0, 0, 1, 1, "", 
                                       _pagewin[p], flags, 0, DLG_PAGE);
    }                                                                            
    if (toolwin())
      _pagetag[p] = xvt_create_control(WC_PUSHBUTTON, 0, 0, 0, 1, "",
                                       _pagewin[p], p, _pages, DLG_PAGETAGS);
  }
#else
  if (toolwin())
  {
    TString80 t;
    for (int p = 0; p < _pages; p++)
      t << ' ' << p+1;
    t << ' ';
    for (p = 0; p < _pages; p++)
    {
      const int k = p*2;
      t[k] = '['; t[k+2] = ']';
      RCT r; xvt_rect_set(&r, 0, 0, t.size()*CHARX, CHARY);
      xvt_ctl_create(WC_TEXT, &r, t, _pagewin[p], 0, 0, DLG_NULL);
      t[k] = ' ';
    }
  }
#endif
}


TMask::TMask(const char* maskname, int num, int max)
{
  if (maskname && *maskname)
    read_mask(maskname, num, max);
}


TMask::~TMask()
{
  for (int p = 0; p <= MAX_PAGES; p++)
    if (_pagewin[p])
    {
      xvt_vobj_destroy(_pagewin[p]);
      _pagewin[p] = NULL_WIN;
    }
}


void TMask::open()
{
  set_mask_fields();

  _focus = first_focus(0);
  if (toolwin() && _focus < 1)
    _focus = find_first_field(_pagewin[0], +1);

  if (!_open || _page != 0)
  {
    _open = TRUE;
    if (toolwin()) 
      xvt_vobj_set_visible(toolwin(), TRUE);
    next_page(0);
  }
  else
  {
    set_focus();
  }
}


int TMask::first_focus(short id)
{
  static int tempfirstfocus = 0;

  int f = _first_focus;
  if (id == 0)
  {
    if (tempfirstfocus)
    {
      f = tempfirstfocus;
      if (fld(f).dirty() == FALSE)
        fld(f).set_dirty();
    }
    tempfirstfocus = 0;
  }
  else
  {
    if (id > 0) 
    {
      _first_focus = id2pos(id);
      tempfirstfocus = 0;
    }  
    else 
      tempfirstfocus = id2pos(-id);
  }

  return f;
}

bool TMask::can_be_closed() const
{
  bool ok = TRUE;
  if (!query_mode() && is_running() && dirty()) 
    ok = yesno_box("Annullare i dati inseriti?");
  return ok;
}

void TMask::close()
{
  _open = FALSE;
  _page = -1;
  for (int p = 0; p <= MAX_PAGES; p++)
    if (_pagewin[p]) xvt_vobj_set_visible(_pagewin[p], FALSE);
}

void TMask::set_mask_fields() const
{
  for (int i = 0; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    const word id = f.class_id();
    if (id != CLASS_FIELD && id != CLASS_BUTTON_FIELD)
      f.set_window_data(f.get_field_data());
  }
}

short TMask::dirty() const
{
  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    const TMask_field& f = fld(i);
    const word id = f.class_id();
    if (f.dirty() && id != CLASS_FIELD && id != CLASS_BUTTON_FIELD && f.active()) 
      return f.dlg();
  }
  return 0;
}

void TMask::load_checks() const
{
  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (f.has_check())
      f.check(STARTING_CHECK);
  }
}

// Dis/abilita una pagina e tutte le successive
void TMask::enable_page(byte page, bool on)
{
  CHECK(page > 0, "Can't enable/disable first page");
  
  if (_enabled[page] != on) 
  {
    for (byte i = page; i < _pages; i++)                     
      _enabled.set(i, on);

#if XVT_OS == XVT_OS_WIN                                                       
    const byte p = on ? _pages : page;
    for (i = 0; i < page-1; i++)                     
      xvt_change_page_tags(NULL_WIN, FALSE, _pagetag[i], p);

    xvt_change_page_tags(_pagepag[page-1], on, _pagetag[page-1], p);
#endif                      
  }
}

// Controlla lo stato di abilitazione di una pagina    
// Certified 100%
bool TMask::page_enabled(byte page) const
{
  return _enabled[page];
}


void TMask::start_run()
{         
  const long start = clock();
  
  load_checks();

  const int max = fields();  
  
  _next_fld = DLG_NULL;
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if ((f.active() || f.ghost()) && 
        f.class_id() != CLASS_BUTTON_FIELD && f.dirty() <= TRUE)
    {
      f.set_dirty(FALSE);
      f.on_hit();           // Lancia messaggio di inizializzazione
    }  
  }

  // Make sure that "nearly" all fields are clean!
  for (i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (query_mode() && f.is_edit() && f.in_key(1) && 
        !f.automagic() && !f.get().empty())
    {
      f.set_dirty(TRUE);
    }  
    else    
    {  
      if (f.dirty() == TRUE)
        f.set_dirty(FALSE);
    }   
  }
  
  _init_time = clock()-start;
}

bool TMask::check_fields()
{
  WINDOW curpage = NULL_WIN;          // Page under test
  
  const bool sheet = is_sheetmask() && !is_open();

  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (!f.active()) continue;        // Don't test inactive fields
    if (f.parent() != curpage)
    {
      const int pa = find_parent_page(f);
      if (!page_enabled(pa))
        break;                        // Page disabled: end of test
      curpage = f.parent();           // Update current page
    }
    
    if (sheet) f.set_dirty();         // Force check in sheets

    if (f.on_key(K_ENTER) == FALSE)
    {
      if (is_open()) f.set_focus();
      return FALSE;
    }
  }
  return TRUE;
}


void TMask::get_mask_fields()
{
  TString256 window_data;
  for (int i = 0; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    
    const int acqua = f.class_id();
    if (acqua != CLASS_FIELD && acqua != CLASS_BUTTON_FIELD && acqua != CLASS_SHEET_FIELD)
    {
      window_data = f.get_window_data();
      if (f.dirty() == TRUE)
      {
        const char* fd = f.get_field_data();
        CHECK(fd, "NULL pointer comparison");
        if (window_data == fd)
          f.set_dirty(FALSE);
      }  
      f.set_field_data(window_data);
    }
  }
}


int TMask::id2pos(short id) const
{
  const int MAX_FIELDS = 256;
  static byte positions[MAX_FIELDS];      //  100 <= id < MAX_FIELDS
  const int max = fields();

  const int j = id-100;
  int pos = -1;
  if (j >= 0 && j < MAX_FIELDS)           // Try using cache
  {
    pos = positions[j];
    if (pos >= 0 && pos < max)
    {
      const TMask_field& f = fld(pos);
      if (f.dlg() == id)                  // Mask could have been changed!
        return pos;
    }
  }

  for (pos = 0; pos < max; pos++)         // Standard linear search
  {
    const TMask_field& f = fld(pos);
    if (f.dlg() == id)
    {
      if (j >= 0 && j < MAX_FIELDS)       // Store position for the next time
        positions[j] = pos;
      return pos;
    }
  }

  return -1;                              // Not found!
}


TMask_field& TMask::field(short id) const
{
  int pos = id2pos(id);

#ifdef DBG      
  if (pos < 0)
  {
    yesnofatal_box("Il campo %d non esiste", id);
    pos = 0;
  }  
#endif  
  
  return fld(pos);
}


TEdit_field& TMask::efield(short id) const
{
  TMask_field& f = field(id);
  CHECKD(f.is_edit(), "Impossibile trattare come editabile il campo ", id);
  return (TEdit_field&)f;
}


int TMask::find_field_win(WINDOW win) const
{
  if (fld(_focus).win() == win)
    return _focus;

  const int max = fields();
  for (int i = 0; i < max; i++)
    if (fld(i).win() == win) return i;

#ifdef DBG
  yesnofatal_box("Can't find the field given the child window");
#endif  
  return 0;
}


void TMask::set_focus_win(WINDOW win, bool force)
{
  _focus = find_field_win(win);
  if (force) set_focus();
}


int TMask::find_parent_page(const TMask_field& f) const
{
  const WINDOW pw = f.parent();
  for (int p = 0; p < _pages; p++)
    if (pw == _pagewin[p]) return p;
  return MAX_PAGES;     // Toolbar button
}


int TMask::find_active_field(int first, int dir) const
{
  const int max = fields()-1;
  WINDOW w, old = fld(_focus).parent();

  for (int i = first; ; i += dir)
  {
    if (i > max) i = 0;
    else if (i < 0) i = max;
    if (fld(i).active() && page_enabled(find_parent_page(fld(i))))
    {
      w = fld(i).parent();
      break;
    }
  }

  if (w != old)
  {
    int p = _page;
    if (old == toolwin())
    {
      if (dir > 0)
      { if (++p >= _pages) p = 0; }
    }
    else
    {
      if (dir < 0)
      { if (--p < 0) p = _pages-1; }
      else p = MAX_PAGES;
    }
    w = _pagewin[p];
    i = find_first_field(w, dir);
  }
  return i;
}

void TMask::set_focus()
{             
  _focus = find_active_field(_focus, +1);
  const TMask_field& f = fld(_focus);        
  const int page = find_parent_page(f);

  if (page != _page && page != MAX_PAGES)
  {
    const WINDOW pw = win();          // previous window
    _page = page;                     // update page number
    xvt_vobj_set_visible(win(), TRUE);         // show new page 
    if (pw) xvt_vobj_set_visible(pw, FALSE);   // hide old page
  }
  
  f.highlight();                          
}


// Move the focus to the next (+1) or previous(-1) valid control
void TMask::move_focus_field(int d)
{
  
  TMask_field& f = fld(_focus);                           
  if (f.class_id() == CLASS_RADIO_FIELD)
  {
    TRadio_field& radio = (TRadio_field&)f;
    moving_focus = TRUE;
    bool cont = radio.move_focus(d);
    moving_focus = FALSE;
    if (!cont) return;
  }

  const int focus = find_active_field(_focus+d, d);

  if (!test_focus_change(fld(focus).win()))
    return;

  if (fld(focus).parent() == f.parent() || check_current_page())
    _focus = focus;
  
  set_focus();
}

bool TMask::stop_run(KEY key)
{ 
  if (key != K_AUTO_ENTER && key != K_FORCE_CLOSE)
  {
    const int last = fields();
    bool found = FALSE;
    for (int i = 0; i < last; i++)
    {
      const TMask_field& f = fld(i);
      if (f.class_id() != CLASS_BUTTON_FIELD) continue;
      const TButton_field& b = (const TButton_field&)f;
      if (b.exit_key() == key)
      {
        if (b.active()) 
        {   
          found = TRUE;
          break;
        }  
      }  
    }
    if (!found)
    {
#ifdef DBG
      return error_box("Non e' attivo il bottone associato a %d", key);
#else  
      return FALSE;
#endif    
    }
  }
  
  if (key == K_CTRL_ENTER || key == K_AUTO_ENTER) key = K_ENTER; else
    if (key == K_FORCE_CLOSE) key = K_QUIT;
  
  if (key != K_ESC && key != K_QUIT && key != K_DEL && key != K_F9)
  {
    const bool ok = check_fields();
    if (!ok) return FALSE;
  }
  if (is_running())           // Gestisce correttamenete le maschere chiuse
  {
    get_mask_fields();
    _next_fld = DLG_NULL;
  }

  return TWindow::stop_run(key);
}

bool TMask::on_key(KEY key)
{
  if (_handler)
  {
    bool cont = _handler(*this, key);
    if (!cont) return FALSE;
  }

  switch(key)
  {        
  case K_AUTO_ENTER:
  case K_CTRL_ENTER:
  case K_QUIT:
  case K_ESC:
    stop_run(key);
    break;
  case K_UP:
  case K_BTAB:
  case K_SHIFT_TAB:
  case K_LEFT:
    move_focus_field(-1);
    break;
  case K_DOWN:
  case K_TAB:
  case K_RIGHT:
  case K_ENTER:
    move_focus_field(+1);
    break;
  case K_PREV:
    next_page(-1);
    break;
  case K_NEXT:
    next_page(+1);
    break; 
  case  K_F12:
    message_box("Lettura          = %ld:\n"
                "Creazione        = %ld:\n"
                "Inizializzazione = %ld",
                _total_time-_build_time, _build_time, _init_time);
    break;
  default:
    if (key > K_CTRL)
    {
      key -= K_CTRL;
      if (key >= K_F1 && key <= K_F12)
        next_page(1000 + key - K_F1);
      else  
      {
        const int last = fields();
        for (int i = 0; i < last; i++)
        {
          TMask_field& f = fld(i);
          if (f.class_id() != CLASS_BUTTON_FIELD || !f.active()) continue;
          TButton_field& b = (TButton_field&)f;
          if (b.virtual_key() == key)
          {
            bool ok = b.dlg() != DLG_CANCEL && b.dlg() != DLG_QUIT && b.dlg() != DLG_F9;
            if (!ok) ok = test_focus_change(b.win());
            if (ok)
              f.on_key(K_SPACE);
            break;
          }  
        }
      }                     
    }  
    else 
      return fld(_focus).on_key(key);
  }

  return TRUE;
}

bool TMask::on_dirty(TMask_field&)
{
  return TRUE;
}


TMask_field* TMask::parse_field(TScanner& scanner)
{
  if (scanner.key() == "ST") return new TEdit_field(this);
  if (scanner.key() == "NU") return new TReal_field(this);
  if (scanner.key() == "DA") return new TDate_field(this);
  if (scanner.key() == "BO") return new TBoolean_field(this);
  if (scanner.key() == "LI") return new TList_field(this);
  if (scanner.key() == "BU") return new TButton_field(this);
  if (scanner.key() == "TE") return new TMask_field(this);
  if (scanner.key() == "RA") return new TRadio_field(this);
  if (scanner.key() == "GR") return new TGroup_field(this);
  if (scanner.key() == "SP") 
  { 
    _sheets++;
    return new TSheet_field(this);
  }
  if (scanner.key() == "BR") return new TBrowsefile_field(this);


  return NULL;
}

WINDOW TMask::read_page(TScanner& scanner, bool toolbar)
{
  static int tooly;
  static RCT rect;

  TString80 title(scanner.string());

  RCT r;
  if (toolwin())
  {
    scanner.line();
    xvt_rect_set(&r, 0, 0, 0, tooly);
  }
  else
  {
    scanner.rectangle(r);
    if (toolbar)
    {
      tooly = r.top;
    }  
    else
    {
      if (_pages == 0) rect = r;
      else r = rect;
    }
  }

  WINDOW w;

  if (toolbar || toolwin())
  {
    w = create(0, r.top, 0, r.bottom,
               title, toolbar ? 0 : WSF_INVISIBLE, W_PLAIN);
  }
  else
  {
    w = create(r.left, r.top, r.right, r.bottom,
               title, WSF_CLOSE | WSF_INVISIBLE, WD_MODAL);
  }

  while (scanner.popkey() != "EN")
  { 
    
    TMask_field* f = parse_field(scanner);
#ifdef DBG    
    if (f == NULL) 
    {     
      const int f = fields();
      TString256 e("Unknown control at pos."); e << f;
      if (f) e << ". Last good was " << fld(f-1).dlg() << ": " << fld(f-1).prompt();
      fatal_box(e);
    }  
#endif    
    
    const long start = clock();
    
    f->construct(scanner, w);
    _field.add(f);
    
    _build_time += clock()-start;
  }
  
  set_win(NULL_WIN);
  return w;
}

int TMask::find_first_field(WINDOW w, int dir) const
{
  const int last = fields()-1;
  const int fi = (dir > 0) ? 0 : last;
  const int la = last-fi;
  for (int i = fi; i != la; i += dir)
  {
    const TMask_field& f = fld(i);
    if (f.parent() == w && f.active()) break;
  }
  return i;
}


bool TMask::check_current_page()
{
  if (!test_focus_change()) 
    return FALSE;
  /*  

     if (sheets() > 0) 
     return TRUE;  
     
     const int last = fields();
     const WINDOW page = win();
     
     for (int i = 0; i < last; i++)
     {
     TMask_field& f = fld(i);
     if (f.parent() == page && f.active() && f.on_key(K_ENTER) == FALSE)
     return FALSE;
     }
     */  
  return TRUE;
}


void TMask::next_page(int p)
{
  const int prev = _page;             // Previous page

  if (p != 0)
  {
    if (check_current_page() == FALSE)     // New style
      return;

    const int k = (p < 1000) ? _page+p : p-1000;
    if (k < 0 || k >= _pages || !page_enabled(k))
    {
      beep();
      return;
    }
    _page = k;
  } else _page = 0;

  if (_page != prev)
  {                          
    const WINDOW w = _pagewin[_page];
    xvt_vobj_set_visible(w, TRUE);
    if (prev >= 0) 
    {
      xvt_vobj_set_visible(_pagewin[prev], FALSE);
      _focus = find_first_field(w, +1);
    }   
  }

  set_focus();
}


void TMask::reset(short fld_id)
{
  if (fld_id < 1)
  {
    for (int f = 0; f < fields(); f++)
    {
      TMask_field& c = fld(f);
      c._flags.dirty = FALSE;
      c.reset();
    }
  } 
  else field(fld_id).reset();
}


void TMask::undo(short fld_id)
{
  if (fld_id < 1)
  {
    for (int f = 0; f < fields(); f++)
      fld(f).undo();
  } else field(fld_id).undo();
}


const TString& TMask::get(short fld_id) const
{
  return field(fld_id).get();
}

long TMask::get_long(short fld_id) const
{
  return atol(field(fld_id).get());
}

bool TMask::get_bool(short fld_id) const
{
  return field(fld_id).get().not_empty();
}

void TMask::set(short fld_id, const char* s, bool hit)
{ 
  TMask_field& f = field(fld_id);
  f.set(s);                      
  if (hit && (f.active() || f.ghost())) 
    f.on_key(K_TAB);
}

void TMask::set(short fld_id, long n, bool hit)
{
  char s[16];
  sprintf(s, "%ld", n);
  set(fld_id, s, hit);
}


void TMask::activate(bool on) 
{
  TWindow::activate(on);
  if (toolwin() != NULL_WIN)
    xvt_vobj_set_visible(toolwin(), on);
} 


void TMask::enable(short fld_id, bool on)
{
  if (fld_id <= 0)
  {        
    const int gr = -fld_id;
    for (int i = 0; i < fields(); i++) 
    {
      TMask_field& f = fld(i);
      if (gr == 0 || f.in_group(gr)) 
        f.enable(on);
    }    
  } 
  else field(fld_id).enable(on);
}


void TMask::enable_default(short fld_id)
{
  if (fld_id < 1)
  {
    for (int i = 0; i < fields(); i++)
      fld(i).enable_default();
  } else field(fld_id).enable_default();
}


byte TMask::num_keys() const
{
  word max = 0;
  for (int i = 0; i < fields(); i++)
  {
    word k = fld(i).last_key();
    if (k > max) max = k;
  }
  return max;
}

void TMask::enable_key(byte key, bool on)
{
  for (int i = 0; i < fields(); i++)
    if (fld(i).in_key(key))
    {
      if (on)
      {
        fld(i).enable_default();
        if (!fld(i).shown())
          fld(i).show_default();
      }
      else fld(i).disable();
    }
}


short TMask::get_key_field(byte key, bool first) const
{
  static int last = 0;

  if (first) last = 0;

  for (int i = last; i < fields(); i++)
  {
    if (fld(i).in_key(key))
    {
      last = i+1;
      return fld(i).dlg();
    }
  }

  return -1;
}

bool TMask::key_valid(int key) const
{
  for (short f = 0; f < fields(); f++)
  {
    TMask_field& c = fld(f);
    if (c.in_key(key) && c.required())
    {
      const TString & value = c.get();
      if (c.class_id() == CLASS_REAL_FIELD)
      {
        real z(value);
        if (z.is_zero())
        {
          c.reset();
          return FALSE;
        }
      }
      if (value.empty())
        return FALSE;
    }
  }
  return TRUE;
}


void TMask::show(short fld_id, bool on)
{
  if (fld_id <= 0)
  {        
    const int gr = -fld_id;
    for (int i = 0; i < fields(); i++) 
    {
      TMask_field& f = fld(i);
      if (gr == 0 || f.in_group(gr)) 
        f.show(on);
    }    
  } 
  else field(fld_id).show(on);
}



void TMask::show_default(short fld_id)
{
  if (fld_id <= 0)
  {
    for (int i = 0; i < fields(); i++)
      fld(i).show_default();
  } else field(fld_id).show_default();
}


void TMask::autoload(const TRelation* r)
{
  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (f.field() != NULL)
      f.autoload(r);
  }
}


void TMask::autosave(TRelation* r) const
{
  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    const TFieldref* fr =f.field();
    if (fr != NULL)
    {
      if (f.shown() || *fr->read(r) == '\0')
        f.autosave(r);
    }   
  }
}

void TMask::on_firm_change()
{
  TString16 firm; firm << main_app().get_firm();
  
  for (int i = 0; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    if (f._flags.firm)
    {
      f.set(firm);
      f.check(STARTING_CHECK); 
      f.on_hit();
    }  
  }  
}


void TMask::send_key(KEY key, short to, TMask_field* from)
{
  if (to == 0)
  {                
    WINDOW w = from ? from->parent() : _pagewin[0];
    dispatch_e_char(w, key);
    return;
  }

  if (to > 0)
  {        
    if (to == DLG_PAGE)
    {                
      CHECK(from, "You should specify a sender!");
      const int p = find_parent_page(*from)+1;
      CHECKD(p > 0 && p < _pages, "You should specify a good page, not ", p);
      key -= K_CTRL+K_SHIFT;
      enable_page(p, key == 's' || key == 'e');
    }
    else
    {
      const int pos = id2pos(to);
      if (pos >= 0) fld(pos).on_key(key);
#ifdef DBG
      else
        yesnofatal_box("Can't send key %u to field %d", key, to);
#endif  
    }
  }
  else
  {
    const int gr = -to;
    for (int i = 0; i < fields(); i++)
    {
      TMask_field& campo = fld(i);
      if (campo.in_group(gr))
        campo.on_key(key);
    }
  }
}

void TMask::set_handler(short fld_id, CONTROL_HANDLER handler)
{
  field(fld_id).set_handler(handler);
}

void TMask::set_handler(MASK_HANDLER handler)
{
  _handler = handler;
}

// aggiunta campi a runtime

WINDOW TMask::add_static (short id, int page, const char* prompt, int x,
                          int y, const char* flags)
{
  TMask_field* f = new TMask_field(this);
  f->construct(id, prompt, x, y, strlen(prompt), _pagewin[page], flags);
  _field.add(f);
  return f->win();
}

WINDOW TMask::add_string (short id, int page, const char* prompt, int x,
                          int y, int dim, const char* flags, int width)
{
  TEdit_field* f = new TEdit_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags, width);
  _field.add(f);
  return f->win();
}

WINDOW TMask::add_number (short id, int page, const char* prompt, int x,
                          int y, int dim, const char* flags, int ndec)
{
  TReal_field* f = new TReal_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags, ndec);
  _field.add(f);
  return f->win();
}

WINDOW TMask::add_date (short id, int page, const char* prompt, int x,
                        int y, const char* flags)
{
  TDate_field* f = new TDate_field(this);
  f->construct(id, prompt, x, y, 10, _pagewin[page], flags);
  _field.add(f);
  return f->win();
}

WINDOW TMask::add_button (short id, int page, const char* prompt, int x,
                          int y, int dx, int dy, const char* flags)
{
  TButton_field* f = new TButton_field(this);
  f->construct(id, prompt, x, y, dy, _pagewin[page], flags, dx);
  _field.add(f);
  return f->win();
} 


WINDOW TMask::add_radio(short id, int page, const char* prompt, int x,
                        int y, int dx, const char* codes, const char* items, const char* flags)
{
  TRadio_field* f = new TRadio_field(this);
  f->replace_items(codes, items);
  f->construct(id, prompt, x, y, dx, _pagewin[page], flags, dx);
  _field.add(f);
  return ((TMask_field*)f)->win();
}

bool TMask::save(bool append) const

{
  FILE*  f = fopen((const char *) _workfile, append ? "a" : "w");

  if (f == NULL) 
    return yesnofatal_box("Non posso aprire %s ", (const char*) _workfile);

  for (int i = 0; i < fields(); i++)
  {
    const short id = fld(i).dlg();
    if (id >= 100)
      fprintf(f, "%d|%s\n", id, (const char*) fld(i).get());
  }
  fprintf(f, "[EOM]\n");
  fclose(f);
  return TRUE;
}


bool TMask::load(bool reset) 

{
  FILE*  f = fopen((const char *) _workfile, "r");

  if (f == NULL) return FALSE;
  if (reset) _lastpos = 0;
  fseek(f, _lastpos, SEEK_SET);
  TToken_string t(256);
  char s[256];
  while (fgets(s, 255, f) != NULL && strncmp(s, "[EOM]", 5) != 0)
  {
    if (*s) s[strlen(s) - 1] = '\0';
    t = s;
    const short id = t.get_int();
    if (id >= 100)
    {
      const int pos = id2pos(id);
      if (pos >= 0) fld(pos).set(t.get());
    }
  }
  _lastpos = ftell(f);
  fclose(f);
  return TRUE;
}


const char* TMask::get_caption() const
{
  char* title = &__tmp_string[512];
  xvt_vobj_get_title(_pagewin[0], title, 80);
  return title;
}

void TMask::set_caption(const char* c)
{
  for (int p = 0; p < _pages; p++)
    xvt_vobj_set_title(_pagewin[p], (char*)c);
}


void TMask::set_exchange(bool show_value, const real& n)
{       
  const real nuo = (n.sign() <= 0) ? _exchange : n;

  main_app().begin_wait();
  for (int i = 0; i < fields(); i++)    
  {
    TMask_field& f = fld(i);
    const word id = f.class_id();
    if (id == CLASS_SHEET_FIELD || id == CLASS_REAL_FIELD && f.exchangeable())
      f.exchange(show_value, nuo);
  } 
  
  _exchange = nuo;                            // Update current exchange
  main_app().end_wait();
}