#include <xinclude.h>

#include <controls.h>
#include <browfile.h>
#include <colors.h>
#include <diction.h>
#include <msksheet.h>
#include <prefix.h>
#include <recarray.h>
#include <relation.h>
#include <urldefid.h>
#include <utility.h>

#include <user.h>

#define DLG_PAGETAGS 31000 

HIDDEN const char* const MASK_EXT = "msk";

///////////////////////////////////////////////////////////
// PAGE BUTTONS
///////////////////////////////////////////////////////////

class TPage_field : public TRadio_field
{       
  byte _def;

protected:
  virtual void current(int) { }               // Evita il reset
  virtual int current() const { return _def; }
  virtual bool is_loadable() const { return false; }
  virtual bool is_editable() const { return false; }

public:
  virtual void set_prompt(const char* p);
  void create(WINDOW parent);
  void set_default(byte d) { _def = d; }
  void show_button(int i, bool on);

  TPage_field(TMask* m) : TRadio_field(m) { }
  virtual ~TPage_field() { }
};

void TPage_field::create(WINDOW parent)
{
  _ctl = new TTagbutton_control(parent, _ctl_data._dlg, 0, 0, 80, 1, "", _values, _def);
}                  

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

void TPage_field::show_button(int i, bool on)
{
  TTagbutton_control* tag = (TTagbutton_control*)_ctl;
  tag->show_button(i, on);
}

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

void TMask::init_mask()
{
  _msg_field = 0;
  _msg_key   = 0;
  _pages = 0;                                 // Azzera numero pagine e sheets
  if (_mask_num == 0) _sheet = NULL;          // Non appartiene a nessuno sheet

  _enabled.set(MAX_PAGES);
  _enabled.set();                             // Abilita tutte le pagine
  _should_check = true;

  _focus = _first_focus = 0;                  // Il primo ha il focus
  _page = -1;                                 // Nessuna pagina corrente
  _handler = NULL;                            // Nessun handler utente
  _mode = NO_MODE;                            // Inizializza modo

  _error_severity = 0;
  _msg_field = 0;
  _msg_key = 0;
  _test_fld = -1;
  _last_test = -1;

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

TMask::TMask() 
     : _mask_num(0)
{ init_mask(); }


TMask::TMask(const char* title, int pages, int cols, int rows,
             int xpos, int ypos, WINDOW parent)
     : _mask_num(0)

{
  init_mask();
  for (_pages = 0; _pages < pages; _pages++)
    _pagewin[_pages] = create_interface(parent, xpos, ypos,
                                        cols, rows, title, this, pages > 1);
}

void TMask::set_locking(TBit_array& read_only, TToken_string& list, bool on)
{
  if (list.find('*') >= 0)
  {
    if (on)
    {
      read_only.set(fields());
      read_only.set();
    }
    else
      read_only.reset();
  }
  else
  {
	  FOR_EACH_TOKEN(list, fld)
	  {
      const TFixed_string id(fld);
      if (id[0] == '-' || id.ends_with("@")) // Gestione gruppi
      {
        const int grp = abs(atoi(fld));
        FOR_EACH_MASK_FIELD((*this), pos, f)
        {
          if (f->in_group(grp))
            read_only.set(pos, on);
        }
      }
      else
      {
		    const int pos = field2pos(fld);
  	    if (pos >= 0)
	  	    read_only.set(pos, on);
      }
    }
	}
}

// @mfunc Legge la maschera da file
void TMask::read_mask(
  const char* name, // @parm Nome della maschera da leggere (senza estensione)
  int num,          // @parm Numero della maschera da leggere all'interno del file
  int max)          // @parm Numero massimo di pagine che deve avere la maschera
{        
  TWait_cursor hourglass;
  if (max <= 0) max = MAX_PAGES;

  _source_file = name;
  _source_file.ext(MASK_EXT);
  _source_file.lower();
	_source_file.custom_path();
  TScanner scanner(_source_file);

  if (num != 0)
    _mask_num = num;

  for (int i = 0; i < num; i++)
  {
    while (scanner.ok())
      if (scanner.line() == "ENDMASK") break;
  }

  init_mask();

  TToken_string captions(80);

  while (scanner.ok() && scanner.popkey() != "EN")
  {
    if (scanner.key() == "PA")
    {
      CHECKD(_pages < MAX_PAGES, "Maschera con troppe pagine: ", _pages);
      WINDOW w = read_page(scanner, false);

      TString80 title;
      xvt_vobj_get_title(w, title.get_buffer(), title.size());
      captions.add(title);

      _pagewin[_pages++] = w;
      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);
      }
  }

  if (_pages <= 0)
    fatal_box("Impossibile leggere la maschera %s : %d", (const char*)_source_file, num);

  if (_pages > 1 || toolwin())
    add_tag_buttons(captions);

  if (num == 0)
	{

		TFilename prof; make_profile_name(prof);

    if (prof.exist())
    {
      // Costruisce la lista dei gruppi di appartenenza dell'utente
      const TString_array& uag = user_and_groups();
		  TConfig ini(prof);
      FOR_EACH_ARRAY_ROW_BACK(uag, c, utonto)
      {                    
		    *utonto << "_Locks";
        if (ini.set_paragraph(*utonto))
        {
          TBit_array read_only;
          TAuto_token_string lfields(ini.get("Lock"));
          TAuto_token_string ufields(ini.get("Unlock"));
          if (ufields.find("*")>=0)
          {
  		      set_locking(read_only, lfields, true);
          }
          else
          {
  		      set_locking(read_only, lfields, true);
            set_locking(read_only, ufields, false);
          }

		      FOR_EACH_MASK_FIELD((*this), j, f)
		      {
  		      if (f->is_loadable())
            {
              const bool ro = read_only[j];
              if (f->read_only() != ro)
				        f->set_read_only(ro);
            }
		      }
        }
      }
	  }
  }
}  

void TMask::add_field(TMask_field* f)
{
  const long pos = _field.add(f)+1;
  const short id = f->dlg();
  if (id > 0 && id < 512)
  {
    if (_position.objptr(id))
    {
      if (id > DLG_QUIT)
        NFCHECK("Il campo %d e' duplicato!", id);
    }
    else
      _position.TArray::add((TObject*)pos, id);
  }  
}

void TMask::add_tag_button(byte pag, TToken_string& tags, byte sel)
{                 
  TPage_field* pf = new TPage_field(this);
  pf->_ctl_data._dlg = DLG_PAGETAGS + 100 * pag;
  pf->replace_items("1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16", tags);
  pf->set_default(sel);
  pf->create(_pagewin[pag]);
  add_field(pf);
}

void TMask::add_tag_buttons(TToken_string& tags)
{
  for (byte p = 0; p < _pages; p++)
    add_tag_button(p, tags, p);
}

void TMask::add_default_tag_buttons()
{
  TToken_string tags(_pages * 6);
  for (int p = 1; p <= _pages; p++)
  {
    tags.add(TR("Pag."));
    tags << p;
  }
  add_tag_buttons(tags);
}

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


TMask::~TMask()
{     
	_field.destroy();
  for (int p = MAX_PAGES; p >= 0; p--)
    if (_pagewin[p])
    {       
      if (xvt_vobj_get_attr(_pagewin[p], ATTR_NATIVE_WINDOW) != 0)
        xvt_vobj_destroy(_pagewin[p]);
      _pagewin[p] = NULL_WIN;
    }
}

word TMask::class_id() const
{ return CLASS_MASK; }

bool TMask::is_kind_of(word c) const
{
  if (c == CLASS_MASK)
    return true;
  return TWindow::is_kind_of(c);
}

void TMask::open()
{
  if (!_open || _page != 0)
  {
    _open = true;
    if (toolwin())
      xvt_vobj_set_visible(toolwin(), true);
  }
  _focus = first_focus(0);
  set_focus_field(fld(_focus).dlg());
}

int TMask::find_first_active(WINDOW p) const
{
  const int max = fields();
  for (int f = 0; f < max; f++)
  {
    TMask_field& c = fld(f);
    if (c.active() && c.parent() == p)
      return f;
  }
  return -1;
}

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

  int f = _first_focus;
  if (id == 0)
  {
    if (tempfirstfocus >= 0)
    {
      f = tempfirstfocus;
      if (fld(f).dirty() == false)
        fld(f).set_dirty();
      tempfirstfocus = -1;
    }
    else
    {
      if (f < 0 || !fld(f).active())
      {
        f = find_first_active(_pagewin[0]);
        if (f < 0 && toolwin())
          f = find_first_active(toolwin());
      }
    }
  }
  else
  {
    if (id > 0)
    {
      f = _first_focus = id2pos(id);
      tempfirstfocus = -1;
    }
    else
      f = tempfirstfocus = id2pos(-id);
  }

  CHECKD(f >= 0 && f < fields(), "Invalid focus field ", f);
  return f;
}

TOperable_field& TMask::focus_field() const
{                        
  if (is_open())
  {
    const short focus = low_get_focus_id(win());
    if (focus > 0 )
      ((TMask *)this)->notify_focus_field(focus);
  }    
  
  TMask_field & f = fld(_focus);
  CHECK(f.is_kind_of(CLASS_OPERABLE_FIELD), "A non operable_field has the focus");
  return (TOperable_field&)f;
}

void TMask::set_focus_field(short id)
{
  notify_focus_field(id);
  if (is_open())
  {
    const TMask_field& f = field(id);
    int p = find_parent_page(f);
    if (p < 0 || p >= _pages || !page_enabled(p))
      p = 0;
    show_page(p);
  }
}

void TMask::notify_focus_field(short id)
{
  const int pos = id2pos(id);
  CHECKD(pos >= 0, "Can't set focus to field ", id);
  _focus = pos;
}

bool TMask::can_be_closed() const
{
  if (is_running())
  {
    bool ok = true;
    if ((edit_mode() || insert_mode()) && dirty() && id2pos(DLG_QUIT) < 0)
      ok = yesno_box(TR("Annullare i dati inseriti?"));
    if (ok)
      ((TMask*)this)->stop_run(K_FORCE_CLOSE);
    return false;
  }
  return true; // Should never happen!
}

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);
}

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

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

// @doc EXTERNAL

// @mfunc Abilita/disabilita una pagina e tutte le successive
void TMask::enable_page(
  byte page, // @parm Pagina da abilitare/disabilitare
  bool on)      // @parm Operazione da svolgere:
  //
  // @flag true | Abilita la pagina <p p> (default)
  // @flag false | Disabilita la pagina <p p>
{
  CHECK(page > 0, "Can't enable/disable first page");

  if (_enabled[page] != on)
  {
    for (byte i = page; i < _pages; i++)
    {
      _enabled.set(i, on);
      for (byte b = 0; b < _pages; b++)
      { 
        TPage_field& pf = (TPage_field&)field(DLG_PAGETAGS + 100 * b);
        pf.show_button(i, on);
      }
    }
  }
}

// Controlla lo stato di abilitazione di una pagina
// Certified 100%
bool TMask::page_enabled(byte p) const
{
  CHECKD(p <= MAX_PAGES, "Page too high ", (int)p);
  const bool on = _pagewin[p] != NULL_WIN && _enabled[p];
  return on;
}


void TMask::start_run()
{
  const long start = clock();
  const int max = fields();

  if (_should_check)
  {    
    TWait_cursor hourglass;
    
    load_checks();

    for (int i = 0; i < max; i++)
    {
      TMask_field& f = fld(i);
      if (f.dirty() <= 1)  // Attenzione puo' valere anche 3 per i very dirty!
      {
        f.set_dirty(false);
        const bool op = f.is_operable() && !f.is_kind_of(CLASS_BUTTON_FIELD);
        if (op && (f.active() || f.ghost()))
          f.on_hit();           // Lancia messaggio di inizializzazione
      }
    }
  }
  _should_check = true;

  // Make sure that "nearly" all fields are clean!
  for (int i = 0; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (f.dirty() == 1)     // Attenzione puo' valere anche 3 per i very dirty!
      f.set_dirty(false);
  }

  _last_test = -1;
}

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);
    const bool on = f.active() || (f.shown() && f.is_sheet());
    if (on)        // 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 sheet masks
      if (f.on_key(K_ENTER) == false)
      {
        if (is_open())
          set_focus_field(f.dlg());
        return false;
      }
    }
  }
  return true;
}

void TMask::check_field( short fld_id )
{          
  if (fld_id <= 0)
  {
    const int gr = -fld_id;
    for (int i = fields()-1; i >= 0; i--)
    {
      TMask_field& f = fld(i);
      if (gr == 0 || f.in_group(gr))
      {
        f.on_hit();
        f.check();
      }
    }
  }
  else 
  {
    field(fld_id).on_hit();
    field(fld_id).check();
  }
}

// @doc EXTERNAL

// @mfunc Converte un identificatore di campo nella sua posizione
//
// @rdesc Ritorna la posizione del campo nella maschera (-1 se non lo trova)
int TMask::id2pos(
  short id) const // @parm Identificatore del campo del quale trovare la posizione

  // @comm Cerca nella maschera il campo con identificatore <p id> e ne ritorna il numero ordinale
  //                     (es. il campo 120 e' il quarto della maschera)
{
  int pos = -1;
  if (id > 0)
  {
    if (id < 512)
      pos = int(long(_position.objptr(id))-1);
    else
    {
      for (pos = fields()-1; pos >= 0; pos--)  
        if (fld(pos).dlg() == id) 
          break;
    }    
  }
  return pos;
}


TMask_field& TMask::field(short id) const
{
  int pos = id2pos(id);
  if (pos < 0)
  {
    yesnofatal_box("Non esiste il campo %d sulla maschera %s", id, (const char*)_source_file);
    pos = 0;
  }
  return fld(pos);
}

int TMask::field2pos(const char* fieldname) const
{
  const int id = atoi(fieldname);
  int i;
  for (i = fields()-1; i >= 0; i--)
  {
    TMask_field& f = fld(i);
    if (id == 0)
    {
      const TFieldref* fr = f.field();
      if (fr != NULL && fr->name() == fieldname)
        break;
    }
    else
    {
      if (f.dlg() == id)
        break;
    }
  }
  return i;
}

TMask_field* TMask::find_by_fieldname(const char* fieldname) const
{
  const int i = field2pos(fieldname);
  return i >= 0 ? &fld(i) : NULL;
}

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;
}

TList_field& TMask::lfield(short id) const
{
  TMask_field& f = field(id);
  CHECKD(f.is_list(), "Impossibile trattare come listbox il campo ", id);
  return (TList_field&)f;
}

TSheet_field& TMask::sfield(short id) const
{
  TMask_field& f = field(id);
  CHECKD(f.is_sheet(), "Impossibile trattare come spreadsheet il campo ", id);
  return (TSheet_field&)f;
}

TTree_field& TMask::tfield(short id) const
{
  TMask_field& f = field(id);
  CHECKD(f.is_kind_of(CLASS_TREE_FIELD), "Impossibile trattare come albero il campo ", id);
  return (TTree_field&)f;
}

// @doc EXTERNAL

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
}

// @mfunc Forza la chiusura della maschera
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag true | E' riuscita a chiudere la maschera
// @flag false | Non e' riuscita a chiudere la maschera
bool TMask::stop_run(
  KEY key) // @parm Tasto che deve provocare la chiusura

  // @comm Permette di chiudere la maschera come se l'utente avesse premuto il tasto <p key>.
  //       Nel caso la maschera non si chiuda (es. un check fallito), ritorna false.
{                
  if (key != K_AUTO_ENTER && key != K_FORCE_CLOSE)
  {
    const int last = fields();
    int i;
    
    for (i = 0; i < last; i++)
    {
      const TMask_field& f = fld(i);
      if (f.active() && f.is_kind_of(CLASS_BUTTON_FIELD))
      {
        const TButton_field& b = (const TButton_field&)f;
        if (b.exit_key() == key)
          break;
      }
    }
    if (i >= last)
      return false;
  }

  if (key == K_CTRL_ENTER || key == K_AUTO_ENTER) 
    key = K_ENTER; 
  else
  {
    if (key == K_FORCE_CLOSE) 
      key = (id2pos(DLG_QUIT)<0 && id2pos(DLG_CANCEL)>=0) ? K_ESC : K_QUIT;
  }

  if (key != K_ESC && key != K_QUIT && key != K_DEL && key != K_F9)
  {
    bool ok = check_current_field();
    if (ok)
      ok = check_fields();
    if (ok) 
    {   
      if (id2pos(DLG_PROFILE) >= 0 && field(DLG_PROFILE).hidden())
        save_profile();
    }
    else
      return false;
  }

  return TWindow::stop_run(key);
}

void TMask::on_button(short)
{
/* Non devo fare niente !!! non essendo una TWindow */
}

void TMask::handler(WINDOW win, EVENT* ep)
{   
  static TSheet_field* _last_sheet = NULL;
                      
  if (ep->type == E_MOUSE_DOWN && ep->v.mouse.button == 1)
  {   
    _last_sheet = NULL;
    for (int f = fields()-1; f >= 0; f--)
    {            
      TMask_field& cur_fld = fld(f);
      if (cur_fld.shown() && cur_fld.parent() == win)  
      {                             
        RCT rct; cur_fld.get_rect(rct);
        if (xvt_rect_has_point(&rct, ep->v.mouse.where))
        {
          if (cur_fld.is_kind_of(CLASS_SHEET_FIELD))
            _last_sheet = (TSheet_field*)&cur_fld;
          else
            cur_fld.on_key(K_F11);
          break;
        }  
      }
    }
  
    if (_last_sheet)
    {
      MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR);
      if (menu != NULL)
      {         
        const PNT& p = ep->v.mouse.where;
        xvt_menu_popup(menu->child, win, p, XVT_POPUP_CENTER, 0); // verificare
        xvt_res_free_menu_tree(menu);       
      }  
    }
    return;
  }      
  if (ep->type == E_COMMAND && _last_sheet != NULL)
  {
    switch (ep->v.cmd.tag)
    {     
    case BROWSE_BAR+1: _last_sheet->save_columns_order(); break;            
    case BROWSE_BAR+2: _last_sheet->reset_columns_order(); break;
    case BROWSE_BAR+3: _last_sheet->on_key(K_F11); break;
    default: break;  
    }
    return;
  }

  if (ep->type == E_VSCROLL)
  {
    for (int i = 0; i < fields(); i++)
    {
      const TMask_field& ff = fld(i);
      if (ff._ctl != NULL)
      {
        XI_OBJ* obj = ff._ctl->xi_object();
        if (obj->type == 11)
        {
          const int dir = ep->v.scroll.what == SC_LINE_UP ? -3 : +3;
          xi_scroll(obj, dir);
          break;
        }
      }
    }
  }
  
  TWindow::handler(win, ep);
}  


// @doc EXTERNAL

// @mfunc Assegna una azione al tasto non standard
//
// @rdesc Ritrna se e' stto effetuato una azione:
//
// @flag true | Era prevista una azione collegata al tasto ed e' stata eseguita
// @flag false | Non era prevista nessuna azione collegata al tasto
bool TMask::on_key(
  KEY key) // @parm Tasto premuto sulla maschera

  // @comm Controlla se il tasto e' tra quelli standard previsti dalla maschera corrente, in caso
  //             contrario ne assegna una azione
{
  if (_handler)
  {
    const 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_PREV:
    if (fld(_focus).on_key(K_TAB))
      next_page(-1);
    break;
  case K_NEXT:
    if (fld(_focus).on_key(K_TAB))
      next_page(+1);
    break;
  case K_F1:
    {
      char key[_MAX_FNAME];
      xvt_fsys_parse_pathname(source_file(), NULL, NULL, key, NULL, NULL);

			EVENT e; memset(&e, 0, sizeof(e));
			e.type = E_HELP;
			e.v.help.tag = M_HELP_ONCONTEXT;
			e.v.help.obj = win();
			e.v.help.tid = (long)key;

			TFilename n = "campo";
			TString16 module = key; module.cut(2);
			if (module != "ba")
				n.insert(module);
	    FILE_SPEC fs; memset(&fs, 0, sizeof(FILE_SPEC));
			strcpy(fs.name, n);
      XVT_HELP_INFO hi = xvt_help_open_helpfile(&fs, 0);
			xvt_help_process_event(hi, win(), &e);
    }
    break;
  case K_F12:
		send_key(K_F12, focus_field().dlg());
    break;
  case K_CTRL+'+':
    if (is_running()) 
    {      
      // Cerco nella pagina corrente il primo spreadsheet a partire dal campo col focus
      if (_focus < 0) _focus = 0;
      const WINDOW myparent = fld(_focus).parent();
      for (int fire = _focus; fire < fields(); fire++)
      {
        TMask_field& f = fld(fire);
        if (f.parent() != myparent)
          break;
        if (f.is_kind_of(CLASS_SHEET_FIELD))
        {
          TSheet_field& s = (TSheet_field&)f;
          const int newrow = s.append() ? -1 : 0;
          notify_focus_field(s.dlg());
          s.insert(newrow, true, true);
        }
      }  
    }
    break;  
  default:
    if (key > K_CTRL)
    {
      key -= K_CTRL;
      if (key >= K_F1 && key <= K_F12)
      {
        const int page = key - K_F1;
        if (page < _pages && fld(_focus).on_key(K_TAB))
          show_page(page);
      }          
      else
      {
        for (int i = fields()-1; i >= 0; i--)
        {
          TMask_field& f = fld(i);
          if (f.active() && f.is_kind_of(CLASS_BUTTON_FIELD))
          {
            TButton_field& b = (TButton_field&)f;
            if (b.virtual_key() == key)
            {
              f.on_key(K_SPACE);
              break;
            }
          }
        }
      }
    }
  }

  return true;
}

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

int TMask::win2page(WINDOW w) const
{
	for (int p = MAX_PAGES; p >= 0; p--)
		if (w == _pagewin[p])
			return p;
	return -1;
}

TMask_field* TMask::parse_field(TScanner& scanner)
{
  const TString& k = scanner.key();
  if (k == "ST") return new TEdit_field(this);
  if (k == "NU") return new TReal_field(this);
  if (k == "DA") return new TDate_field(this);
  if (k == "BO") return new TBoolean_field(this);
  if (k == "TE") return new TText_field(this);
  if (k == "BU") return new TButton_field(this);
  if (k == "GR") return new TGroup_field(this);
  if (k == "LI") return new TList_field(this);
  if (k == "RA") return new TRadio_field(this);
  if (k == "CU") return new TCurrency_field(this);
  if (k == "ME") return new TMemo_field(this);
  if (k == "ZO") return new TZoom_field(this);      
  if (k == "GO") return new TGolem_field(this);      
  if (k == "BR") return new TBrowsefile_field(this);
  if (k == "SP") return new TSheet_field(this);
  if (k == "TR") return new TTree_field(this);
  if (k == "CL") return new TGolem_client_field(this);
  return NULL;
}

int TMask::sheets() const
{  
  int count = 0;
  for (int f = fields()-1; f >= 0; f--)
  {
    if (fld(f).is_sheet())
      count++;
  }
  return count;
}

// @doc EXTERNAL

// @mfunc Legge la pagina dal file
//
// @rdesc Ritorna l'handle della finestra creata
WINDOW TMask::read_page(
  TScanner& scanner, // @parm File dal quale leggere la pagina
  bool toolbar)      // @parm Indica se e' la toolbar

  // @comm Il parametro <p toolbar> e' utilizzato per indicare se la pagina deve essere visualizzata
  //                     a tutto schermo (true) oppure no
{
  static int tooly;
  static RCT rect;

  const TString title = dictionary_translate(scanner.string());

  RCT r;
  if (toolwin()) // Pagina successiva ad una toolbar
  {
    scanner.line();
    xvt_rect_set(&r, 0, 0, 0, tooly);
  }
  else
  {
    scanner.rectangle(r.left, r.top, r.right, r.bottom);
    if (toolbar)
    {
      if (r.top > 15)  // Rendi negative le coordinate delle toolbar per ...
        r.top -= 23;   // ... ottimizzare l'uso dello schermo ad alta risoluzione
      tooly = r.top;
    }
    else
    {
      if (_pages == 0)
      {
        if (!is_sheetmask()) 
          rect = r;
      }    
      else 
        r = rect;
    }
  }

  bool orecchie = _pagewin[0] != NULL_WIN || toolwin();
  if (!orecchie && !toolbar)                 // Controlla se la maschera ha piu' di una pagina
  {
    const streampos pos = scanner.tellg();   // Memorizza posizione dello scanner
    while (true) 
    {
      const TString& l = scanner.line();  
    
      if (l.empty() || l == "ENDMASK")       // Fine maschera
        break;
    
      if (l.compare("PA", 2, true) == 0)     // Ho trovato un'altra pagina!
      {
        orecchie = true;                     // Quindi devo metterci le orecchie
        break;
      }
    }
    scanner.seekg(pos);                      // Ripristina posizione dello scanner
  }

  WINDOW w;
  if (toolbar || toolwin())
  {                  
    if (toolbar)
      w = create_interface(NULL_WIN, 0, r.top, 0, 0, title, this, false);
    else  
      w = create_interface(NULL_WIN, 0, r.top, 0, tooly, title, this, orecchie);
  }
  else
  {
    w = create_interface(NULL_WIN, r.left, r.top, r.right, r.bottom, title, this, orecchie);
  }

  while (scanner.popkey() != "EN")
  { 
    TMask_field* f = parse_field(scanner);
    if (f == NULL)
    {
#ifdef DBG    
      const int f = fields();
      TString e;
      e << "Campo non riconosciuto alla posizione " << f;
      if (f > 0) 
        e << ".\nL'ultimo riconosciuto e' " << fld(f-1).dlg() << ": " << fld(f-1).prompt();
      error_box(e);
#endif      
      while (scanner.popkey() != "EN");
    }
    else
    {
      const long start = clock();
      f->construct(scanner, w);
      add_field(f);
    }
  }
  return w;
}

bool TMask::check_current_field() const
{
  bool ok = true;
  if (_focus >= 0)
  {
    TMask_field& ff = fld(_focus);
    if (ff.focusdirty())
      ok = ff.on_key(K_TAB);
  }
  return ok;
}

WINDOW TMask::insert_page(const char* title, int dove)
{
  WINDOW w = NULL_WIN;

  if (dove < 0 || dove >= MAX_PAGES)
  {
    CHECK(toolwin() == NULL_WIN, "One single toolbar, please!");
    const int nHeight = atoi(title);
    w = create_interface(NULL_WIN, 0, -nHeight, 0, 0, "", this, false);
    _pagewin[MAX_PAGES] = w;

    if (_pages > 0)
    {
      RCT rctool; xvt_vobj_get_client_rect(w, &rctool);
      RCT rctask; xvt_vobj_get_client_rect(TASK_WIN, &rctask);
      rctask.bottom -= rctool.bottom;
      for (int i = 0; i < _pages; i++)
        xvt_vobj_move(win(i), &rctask);
    }
  } else
  if (_pages < MAX_PAGES)
  {
    w = create_interface(NULL_WIN, 0, 0, 0, 0, title, this, true);

    if (toolwin() != NULL_WIN)
    {
      RCT rctool; xvt_vobj_get_outer_rect(toolwin(), &rctool);
      RCT rctwin; xvt_vobj_get_outer_rect(w, &rctwin);
      rctwin.bottom = rctool.top-1;
      xvt_vobj_move(w, &rctwin);
    }
    
    int i;
    for (i = _pages; i > dove; i--)
      _pagewin[i] = _pagewin[i-1];
    _pagewin[dove] = w;
    _pages++;

    TToken_string tags;
    for (i = 0; i < _pages; i++)
    {
      char str[80];
      tags.add(xvt_vobj_get_title(_pagewin[i], str, sizeof(str)));
    }

    // Kill page tags
    for (i = fields()-1; i > 0; i--)
    {
      TMask_field& tag = fld(i);
      if (tag.dlg() >= DLG_PAGETAGS)
      {
        tag.destroy();
        _field.destroy(i);
      }
      else
        break;
    }
    // Create page tags
    add_tag_buttons(tags);
  }
  return w;
}

// @doc EXTERNAL

// @mfunc Mostra la prossima/precedente pagina
void TMask::next_page(
  int p) // @parm Pagina alla quale saltare

  // @comm Il valore <p p> puo' assumere i seguenti valori:
  //
  // @flag -1 | Mostra la pagina precedente
  // @flag 0 | Mostra la pagina corrente
  // @flag +1 | Mostra la pagina successiva
  // @flag 1000+n | Mostra la pagina n-esima
{
  const int prev = _page;             // Previous page

  if (p != 0)
  {
    const int k = (p < 1000) ? _page+p : p-1000;
    if (k < 0 || k >= _pages || !page_enabled(k))
    {
      beep();
      return;
    }
    _page = k;
  } 
  else 
  { 
    if (_page < 0 || _page >= _pages)
      _page = 0;
  }
  if (_page != prev)
  {
    xvt_vobj_set_visible(win(), true);
    if (prev >= 0)
      xvt_vobj_set_visible(_pagewin[prev], false);
  }

  TMask_field& ff = fld(_focus);
  if (ff.parent() != win() || !ff.active())
  {
    _focus = find_first_active(win());
    if (_focus < 0 && toolwin() != NULL_WIN)
      _focus = find_first_active(toolwin());
  }

  TWindow::set_focus();

  if (_focus >= 0)
  {
    TMask_field& ff = fld(_focus);
    if (ff.active())
      ff.highlight();
  }
}

void TMask::show_page(int p)
{
  CHECKD(p >= 0 && p < _pages, "Pagina errata ", p);
  next_page(1000 + p);
}

void TMask::reset(short fld_id)
{
  if (fld_id <= 0)
  {
    const int gr = -fld_id;
    for (int f = fields()-1; f >= 0; f--)
    {
      TMask_field& c = fld(f);
      if (gr == 0 || c.in_group(gr))
      {
        c._flags.dirty = c._flags.focusdirty = false;
        c.reset();
      }
    }
  }
  else field(fld_id).reset();
}

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

const TString& TMask::get(const char * fld_id) const
{
  const TString * s = &EMPTY_STRING;
  for (int i = 0 ; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    const TFieldref * campo = f.field();
    if (campo != NULL && campo->name() == fld_id)
    {
      s= &f.get();
      if (f.active())
        break;
    }
  }   
  return *s;
}

long TMask::get_long(short fld_id) const
{
//  const TString& s = field(fld_id).get();
  const TString& s = get(fld_id);
  return atol(s);
}

bool TMask::get_bool(short fld_id) const
{
//  const TString& s = field(fld_id).get();
  const TString& s = get(fld_id);
  return s.not_empty();
}

real TMask::get_real(short fld_id) const
{
//  const TString& s = field(fld_id).get();
  const TString& s = get(fld_id);
  return real(s);
}

TDate TMask::get_date(short fld_id) const
{
//  const TString& s = field(fld_id).get();
  const TString& s = get(fld_id);
  return TDate(s);
}

TCurrency& TMask::get_currency(short fld_id, TCurrency& c) const
{ 
  const TMask_field& cf = field(fld_id);
  if (cf.class_id() == CLASS_CURRENCY_FIELD)
  {
    ((TCurrency_field&)cf).get_currency(c);
  }
  else
  {           
    real n(cf.get());
    c.force_value("");
    c.set_num(n);
  }    
  return c;
}

// @doc EXTERNAL

// @mfunc Setta il campo con un valore
void TMask::set(
  short fld_id,   // @parm Identificatore del campo da settare
  const char* s,  // @parm Stringa da assegnare al campo
  byte hit)       // @parm Indica se occorre rifare i controlli una volta settato il campo
  //                      con il nuovo valore (default false)
  // @parm long | n | Numero da asegnare al campo

  // @syntax set(short fld_id, const char *s, bool hit);
  // @syntax set(short fld_id, long n, bool hit);
{
  TMask_field& f = field(fld_id);
  f.set(s);
  if ((f.active() || f.ghost()))
  {
    f.set_dirty();
    if (hit & 0x2) 
      f.check(); 
    if (hit & 0x1) 
      f.on_hit();
  }
}

void TMask::set(
  const char * fld_id,   // @parm FIELD del campo da settare
  const char* str,  // @parm Stringa da assegnare al campo
  byte hit)       // @parm Indica se occorre rifare i controlli una volta settato il campo
  //                      con il nuovo valore (default false)
  // @parm long | n | Numero da asegnare al campo

  // @syntax set(short fld_id, const char *s, bool hit);
  // @syntax set(short fld_id, long n, bool hit);
{   
  for (int i = 0 ; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    const TFieldref * campo = f.field();
    if (campo != NULL && campo->name() == fld_id)
      set(f.dlg(), str, hit);
  }
}

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

void TMask::set(short fld_id, const real& n, byte hit)
{
  CHECK(id2pos(fld_id) < 0 || field(fld_id).is_edit(), "Can't set a real value in a non edit field");
  set(fld_id, n.string(), hit);
}

void TMask::set(short fld_id, const TDate& d, byte hit)
{
  CHECK(id2pos(fld_id) < 0 || field(fld_id).is_kind_of(CLASS_DATE_FIELD), "Can't set a date in a non-date field");
  set(fld_id, d.string(), hit);
}

void TMask::set(short fld_id, const TCurrency& n, byte hit)
{          
  CHECK(field(fld_id).is_kind_of(CLASS_CURRENCY_FIELD), "Can't set a currency in a non-currency field");
//  CHECK(id2pos(fld_id) < 0 || field(fld_id).is_edit(), "Can't set a currency in a non edit field");
  set(fld_id, n.get_num().string(), hit);
}

// @doc EXTERNAL

// @mfunc Permette di attivare/disattivare tutta la pagina
void TMask::activate(
  bool on) // @parm Indica l'operazione da svolgere sul campo:
  //
  // @flag true | Attiva la pagina(default)
  // @flag false | Disattiva la pagina
{
  TWindow::activate(on);
  if (toolwin() != NULL_WIN)
    xvt_vobj_set_visible(toolwin(), on);
}

// @doc EXTERNAL

// @mfunc Abilita/disabilita un campo
void TMask::enable(
  short fld_id, // @parm Identificatore del campo da abilitare (0 tutti i campi, <0 id gruppo)
  bool on)         // @parm Indica l'operazione da svolgere sul campo:
  //
  // @flag true | Abilita il campo (default)
  // @flag false | Disabilita il campo
{
  if (fld_id <= 0)
  {
    const int gr = -fld_id;
    for (int i = fields()-1; i >= 0; 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 <= 0)
  {
    const int gr = -fld_id;
    for (int i = fields()-1; i >= 0; i--)
    {
      TMask_field& f = fld(i);
      if (gr == 0 || f.in_group(gr))
        f.enable_default();
    }
  }
  else
    field(fld_id).enable_default();
}


word TMask::num_keys() const
{
  word max = 0;
  for (int i = fields()-1; i >= 0; i--)
  {
    const TMask_field& f = fld(i);
    if (f.is_editable())
    {
      word k = ((const TEditable_field&)f).last_key();
      if (k > max) max = k;
    }
  }
  return max;
}

// @doc EXTERNAL

// @mfunc Abilita/disabilita i campi di una chiave sulla maschera
void TMask::enable_key(
  word key,     // @parm Chiave di cui abilitare il campo
  bool on)      // @parm Indica l'operazione da svolgere sul tasto:
  //
  // @flag true | Abilita il campo (default)
  // @flag false | Disabilita il campo

{
  for (int i = fields()-1; i >= 0; i--)
  {
    TMask_field& f = fld(i);
    if (f.in_key(key))
    {
      if (on)
      {
        f.enable_default();
        if (!f.shown())
          f.show_default();
      }
      else
        f.disable();
    }
  }
}

// @doc EXTERNAL

// @mfunc Ritorna il l'identificatore di un campo della chiave <p key>
//
// @rdesc Ritorna l'identificatore del campo cercato
TEditable_field* TMask::get_key_field(
  word key,         // @parm Chiave di cui controllare l'esistenza
  bool first) const // @parm Indica se la ricerca dev partire dell'inizio. Assume i valori:
  //
  // @flag true | Comincia la ricerca dal primo campo della maschera
  // @flag false | Comincia la ricerca dal campo attuale nella maschera
{
  static int last = 0;

  if (first) last = 0;

  const int max = fields();
  for (int i = last; i < max; i++)
  {
    TMask_field& f = fld(i);
    if (f.in_key(key))
    {
      last = i+1;
      return (TEditable_field*)&f;
    }
  }

  return NULL;
}

bool TMask::key_valid(word key) const
{
  const int maxflds = fields();
  bool full = false;
  for (int f = 0; f < maxflds; f++)

  {
    TMask_field& c = fld(f);
    if (c.is_editable() && c.shown() && c.in_key(key))
    {
      if (c.empty())
      {
        if (c.required())
          return false;

      }
      else
        full = true;
    }
  }
  return full;

}

// @doc EXTERNAL

// @mfunc Permette di mostrare/nascondere un campo
void TMask::show(
  short fld_id, // @parm Campo da mostrare/nascondere (default -1)
  bool on)         // @parm Indica l'operazione da svolgere sul campo:
  //
  // @flag true | Mostra il campo(default)
  // @flag false | Nasconde il campo

  // @comm Se <p fld_id> e' -1 allora permette di operare su tutti i campi della maschera

{
  if (fld_id <= 0)
  {
    const int gr = -fld_id;
    for (int i = fields()-1; i >= 0; i--)
    {
      TMask_field& f = fld(i);
      if (gr == 0 || f.in_group(gr))
        f.show(on);
    }
  }
  else field(fld_id).show(on);
}

// @doc EXTERNAL
// @mfunc Rimette lo stato di default del campo
void TMask::show_default(
  short fld_id) // @parm Identificatore del campo da risettare (default -1)

  // @comm Se <p parm> Assume il valore -1 vuole dire che vengono risettati tutti i campi della amschera
{
  if (fld_id <= 0)
  {
    for (int i = fields()-1; i >= 0; i--)
      fld(i).show_default();
  } else field(fld_id).show_default();
}

void TMask::autoload(const TRelation& r)
{
  FOR_EACH_MASK_FIELD((*this), i, f) if (f->is_loadable())
    ((TLoadable_field*)f)->autoload(r);
}

void TMask::autosave(TRelation& r) const
{
  FOR_EACH_MASK_FIELD((*this), i, f) if (f->is_loadable())
  {
    bool save = f->shown();
    if (!save && f->is_editable())
    {
      // tenta di effettuare il save dei campi Edit hidden:
      // salva il nuovo valore solo se il precedente era blank
      if (f->field() != NULL) 
      {
        const char* str = f->field()->read(r);
        save = *str == '\0';
      } 
    }
    if (save)
      ((TLoadable_field*)f)->autosave(r);
  }
}

void TMask::on_firm_change()
{
  TString16 firm; 
  firm << prefix().get_codditta();
  for (int i = fields()-1; i >= 0; i--)
  {
    TMask_field& f = fld(i);
    if (f._flags.firm)
    {
      f.set(firm);
      f.check(STARTING_CHECK);
      f.on_hit();
    } 
  }
}


void TMask::on_idle()
{
  if (_focus >= 0 && _focus < fields())
  {
    if (fld(_focus).is_operable())
    {
      TOperable_field& s = (TOperable_field&)fld(_focus);
      s.on_idle();

      if (_msg_field > 0)
      {
        TMask_field& f = field(_msg_field);
        _msg_field = 0;
        if (_msg_key > 0)
          f.on_key(_msg_key);
      }
    }
    if (_error_severity > 0)
    {
      switch(_error_severity)
      {
      case 2:
        warning_box("%s", (const char*)_error_message); break;
      case 3:
        error_box("%s", (const char*)_error_message);   break;
      default:
        message_box("%s", (const char*)_error_message); break;
      }
      _error_severity = 0;
      set_focus();
    }
    if (_test_fld >= 0)
    {
      const TOperable_field & f = focus_field();
      if (_last_test != f.dlg())
      {     
        TEditable_field & e = (TEditable_field &) field(_test_fld);

        if (!f.in_key(0) || !e.has_a_common_key(f))
        {
          e.test_key_complete(false);
          _test_fld = -1;
        }
        _last_test = f.dlg();
      }
    }
  }
}

// @doc EXTERNAL

// @mfunc Permette di mandare un tasto ad un campo
void TMask::send_key(
  KEY key,           // @parm Codice del tasto da spedire
  short to,          // @parm Identificatore del campo che deve ricevere
  TMask_field* from) // @parm Campo che spedisce il tasto
{
  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)
      {
        if (_msg_field > 0)
          on_idle();
        _msg_field = to;
        _msg_key   = key;
      }
#ifdef DBG
      else
        NFCHECK("Can't send key %u to field %d", key, to);
#endif
    }
  }
  else
  {
    const int gr = -to;
    for (int i = fields()-1; i >= 0; i--)
    {
      TMask_field& campo = fld(i);
      if (campo.in_group(gr))
        campo.on_key(key);
    }
  }
}

// @doc EXTERNAL

// @mfunc Permette di mandare un handler ad controllo o ad una maschera
void TMask::set_handler(
  short fld_id,            // @parm Identificatere del campo che deve ricevere l'handler
  CONTROL_HANDLER handler) // @parm Handler da spedire al campo
  // @parm MASK_HANDLER | handler | Handler da spedire alla maschera

  // @syntax set_handler(short fld_id, CONTROL_HANDLER handler);
  // @syntax set_handler(MASK_HANDLER handler);
  //
  // @comm Nel primo caso viene mandato un <t CONTROL_HANDLER> al campo indicato
  //       da <p fld_id>, mentre nel secondo viene mandato un <t MASK_HANDLER>
  //       alla maschera corrente

{
  TMask_field& f = field(fld_id);
  CHECKD(f.is_operable(), "Can't set an handler to non-operable field ", fld_id);
  ((TOperable_field&)f).set_handler(handler);
}

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

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo testo alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TMask_field& TMask::add_static (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  const char* flags)  // @parm Flag di controllo del campo (deafult "")

  // @xref <mf TMask::add_string> <mf TMask::add_number> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TText_field* f = new TText_field(this);
  f->construct(id, prompt, x, y, 0, _pagewin[page], flags);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo stringa alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TEdit_field& TMask::add_string (
  short id,         // @parm Numero identificatore del campo da aggiungere
  int page,         // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                    // @parm Coordinata x (in caratteri)
  int y,                    // @parm Coordinata y (in caratteri)
  int dim,          // @parm Lunghezza del campo sulla maschera
  const char* flags,  // @parm Flag di controllo del campo (defailt "")
  int width)        // @parm Lunghezza totale del campo stringa (default 0)

  // @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TEdit_field* f = new TEdit_field(this);
  f->construct(id, prompt, x, y, dim, win(page), flags, width);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo bottone alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TButton_field& TMask::add_button (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,              // @parm Coordinata x (in caratteri)
  int y,              // @parm Coordinata y (in caratteri)
  int dx,             // @parm Larghezza del campo (in caratteri, default 9)
  int dy,             // @parm Altezza del campo (in caratteri, default 1)
  const char* flags,  // @parm Flag di controllo del campo (default "")
  short bmpup,        // @parm Icona normale
  short bmpdn)        // @parm Icona premuta

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TButton_field* f = new TButton_field(this);
  f->construct(id, prompt, x, y, dy, win(page), flags, dx);
  if (bmpup > 0)
    f->set_bmp(bmpup, bmpdn);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo boolean alla maschera
//
// @rdesc Ritorna il descrittore del campo aggiunto
TBoolean_field& TMask::add_boolean (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  const char* flags)  // @parm Flag di controllo del campo (default "")

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TBoolean_field* f = new TBoolean_field(this);
  f->construct(id, prompt, x, y, strlen(prompt), _pagewin[page], flags);
  add_field(f);
  return *f;
}

TCheckbutton_field& TMask::add_checkbutton (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,              // @parm Coordinata x (in caratteri)
  int y,              // @parm Coordinata y (in caratteri)
  int dx,             // @parm Larghezza del campo (in caratteri, default 9)
  int dy,             // @parm Altezza del campo (in caratteri, default 1)
  const char* flags,  // @parm Flag di controllo del campo (default "")
  short bmpup,        // @parm Icona normale
  short bmpdn)        // @parm Icona premuta

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TCheckbutton_field* f = new TCheckbutton_field(this);
  f->construct(id, prompt, x, y, dy, _pagewin[page], flags, dx);
  if (bmpup > 0)
    ((TCheckbutton_control*)f->_ctl)->set_icon(bmpup, bmpdn);
  add_field(f);
  return *f;
}


// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo numerico alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TReal_field& TMask::add_number (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  int dim,         // @parm Lunghezza del campo sulla maschera
  const char* flags,  // @parm Flag di controllo del campo (default "")
  int ndec)        // @parm Numero di decimali (default 0)

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TReal_field* f = new TReal_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags, ndec);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo importo alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TCurrency_field& TMask::add_currency (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  int dim,         // @parm Lunghezza del campo sulla maschera
  const char* flags,  // @parm Flag di controllo del campo (default "")
  short driver)        // @parm Campo con codice valuta

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TCurrency_field* f = new TCurrency_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags, 0);
  if (driver != 0)
    f->add_driver(driver);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo data alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TDate_field& TMask::add_date (
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  const char* flags)  // @parm Flag di controllo del campo (default "")

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TDate_field* f = new TDate_field(this);
  f->construct(id, prompt, x, y, 10, _pagewin[page], flags);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo lista alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TList_field& TMask::add_list (
  short id,         // @parm Numero identificatore del campo da aggiungere
  int page,         // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                    // @parm Coordinata x (in caratteri)
  int y,                    // @parm Coordinata y (in caratteri)
  int dim,          // @parm Lunghezza del campo sulla maschera
  const char* flags,  // @parm Flag di controllo del campo (default "")
  const char* codes, // @parm tokenstring con i codici (NULL def.)
  const char* items) // @parm tokenstring con gli items (NULL def.)

  // @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TList_field* f = new TList_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags);
  f->replace_items(codes,items);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo radio button alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TRadio_field& TMask::add_radio(
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  int dx,                  // @parm Larghezza del campo (in caratteri)
  const char* codes,  // @parm Array di codici delle voci
  const char* items,  // @parm Array di prompt delle voci
  const char* flags)  // @parm Flag di controllo del campo (default "")

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_date> <mf TMask::add_button> <mf TMask::add_memo>
{
  TRadio_field* f = new TRadio_field(this);
  f->replace_items(codes, items);
  f->construct(id, prompt, x, y, dx, _pagewin[page], flags, dx);
  add_field(f);
  return *f;
}


// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo zoom alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TZoom_field& TMask::add_zoom (
  short id,         // @parm Numero identificatore del campo da aggiungere
  int page,         // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                    // @parm Coordinata x (in caratteri)
  int y,                    // @parm Coordinata y (in caratteri)
  int dim,          // @parm Lunghezza del campo sulla maschera
  const char* flags,  // @parm Flag di controllo del campo (defailt "")
  int width)        // @parm Lunghezza totale del campo stringa (default 0)

  // @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
  //     <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
{
  TZoom_field* f = new TZoom_field(this);
  f->construct(id, prompt, x, y, dim, _pagewin[page], flags, width);
  add_field(f);
  return *f;
}

// @doc EXTERNAL

// @mfunc Aggiunge runtime un campo memo alla maschera
//
// @rdesc Ritorna l'handle del campo creato
TMemo_field& TMask::add_memo(
  short id,        // @parm Numero identificatore del campo da aggiungere
  int page,        // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,                   // @parm Coordinata x (in caratteri)
  int y,                   // @parm Coordinata y (in caratteri)
  int dx,                  // @parm Larghezza del campo (in caratteri, deafilt 78)
  int dy,                  // @parm Altezza del campo (in caratteri, default 4)
  const char* flags)  // @parm Flag di controllo del campo (default "")

  // @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
  //     <mf TMask::add_date> <mf TMask::add_button> <mf TMask::add_radio>
{
  TMemo_field* f = new TMemo_field(this);
  f->construct(id, prompt, x, y, dy, _pagewin[page], flags, dx);
  add_field(f);
  return *f;
}

TTree_field& TMask::add_tree (
  short id,         // @parm Numero identificatore del campo da aggiungere
  int page,         // @parm Pagina nel quale aggiungere il campo
  int x,            // @parm Coordinata x (in caratteri)
  int y,            // @parm Coordinata y (in caratteri)
  int dx,           // @parm Larghezza del campo sulla maschera
  int dy,           // @parm Altezza del campo sulla maschera
  const char* flags)// @parm Flag di controllo del campo (default "")
{
  TTree_field* f = new TTree_field(this);
  f->construct(id, "", x, y, dy, _pagewin[page], flags, dx);
  add_field(f);
  return *f;
}

TGroup_field& TMask::add_groupbox (
  short id,         // @parm Numero identificatore del campo da aggiungere
  int page,         // @parm Pagina nel quale aggiungere il campo
  const char* prompt, // @parm Prompt del campo
  int x,            // @parm Coordinata x (in caratteri)
  int y,            // @parm Coordinata y (in caratteri)
  int dx,           // @parm Larghezza del campo sulla maschera
  int dy,           // @parm Altezza del campo sulla maschera
  const char* flags)// @parm Flag di controllo del campo (default "")
{
  TGroup_field* f = new TGroup_field(this);
  f->construct(id, prompt, x, y, dy, _pagewin[page], flags, dx);
  add_field(f);
  return *f;
}


// @doc EXTERNAL

// @mfunc Salva i valori dei campi della maschera sul file di salvataggio
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag true | Se l'operazione e' avvenuta corretamente
// @flag false | Se non si riesce a creare il file di salvataggio
bool TMask::save(
  bool append) const // @parm Indica se creare il file o appendere (true) le informazioni
  //       ad uno gia' esistente (false, default).

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

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

  const int max = fields();
  for (int i = 0; i < max; i++)
  {
    TMask_field& c = fld(i);
    if (c.is_editable())
      fprintf(f, "%d|%s\n", c.dlg(), (const char*)c.get());
  }
  fprintf(f, "[EOM]\n");
  fclose(f);
  return true;
}


// @doc EXTERNAL

// @mfunc Legge i valori dei campi della maschera da file di salvataggioo
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag true | Se l'operazione e' avvenuta corretamente
// @flag false | Se non si riesce a leggere il file di salvataggio
bool TMask::load(
  bool reset) // @parm Indica la posizione di lettura del file:
  //
  // @flag true | Comincia la lettura dell'inizio
  // @flag false | Comincia la lettura dalla posizione corrente dell'offset

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

  if (f == NULL) return false;
  if (reset) _lastpos = 0;
  fseek(f, _lastpos, SEEK_SET);
  TToken_string t(256);
  char* buffer = t.get_buffer();
  while (fgets(buffer, t.size(), f) != NULL && t != "[EOM]")


  {
    if (t.not_empty())
    {
      t.rtrim();
      const int pos = id2pos(t.get_int(0));
      if (pos >= 0) fld(pos).set(t.get());
    }
  }
  _lastpos = ftell(f);
  fclose(f);
  return true;
}

// @doc EXTERNAL

// @mfunc Copia i valori dei campi dalla maschera <p m>
//
void TMask::copy_values(
  const TMask& m) // @parm Maschera sorgente

{
  reset();
  const int nfields = fields();
  for (int i = 0; i < nfields; i++)
  {
    TMask_field& dest_field = fld( i );

    if (dest_field.is_editable() )
    {
      const int pos = m.id2pos(dest_field.dlg());
      if (pos >= 0)
      {
        const TMask_field& source_field = m.fld(pos);
        dest_field.set( source_field.get( ) );
      }
    }
  }
}

bool TMask::make_profile_name(TFilename& f) const
{
  f = source_file().name();
  f.ext("ini");
  if (!f.custom_path())   // Prova a cercare la configurazione in custom ...
  {                       // ... altrimenti riprova in config
    f =::firm2dir(-1);    // Directory dati
    f.add("config");      // Directory config
    f.add(source_file().name()); // Nome Maschera
    f.ext("ini");         // Estensione
  }
  return f.exist();
}

int TMask::save_profile(int num, const char* desc) const
{
  TFilename prof; 
  make_profile_name(prof);
  TConfig ini(prof, "Main");

  if (num == 0)
    num = ini.get_int(user());
  if (num <= 0)
  {
    TString_array p;     
    TBit_array b(256); 
    b.set(255); b.set(); b.reset(0);
    ini.list_paragraphs(p);
    FOR_EACH_ARRAY_ROW_BACK(p, r, row)
    {
      num = atoi(*row);
      if (num > 0)
        b.reset(num);
    }
    num = int(b.first_one());
  }

  TString16 para; para << num;                  
  ini.set(user(), para);
                        
  TString description = desc;
  if (ini.set_paragraph(para))        
  {
    if (description.blank())
      description = ini.get("Description");
    ini.remove_all();
  }
  else
  {
    if (description.blank())          
      description << "Profilo standard per " << user();
  }
  ini.set("Description", description);
    
  TString16 name;  

  for (int i = 0; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    if (f.is_loadable() && f.get_default().empty())
    {     
      name.format("F_%d", f.dlg());
      if (f.is_sheet())
      {
        TSheet_field& s = (TSheet_field&)f;
        FOR_EACH_SHEET_ROW(s, r, row)  
          ini.set(name, *row, NULL, true, r);
      }
      else
      {
        if (!f.is_firm())
          ini.set(name, f.get());
      }
    }
  }
  return num;
}

int TMask::load_profile(int num, bool reset)
{
  TFilename prof; 
  if (make_profile_name(prof))
  {
    TConfig ini(prof, "Main");
    if (num <= 0)
      num = ini.get_int(user());
    else
      ini.set(user(), num);
  
    TString16 name; name << num;  
    TAssoc_array& var = ini.list_variables(name);
		if (var.items() > 0)
		{
			for (int pos = fields()-1; pos >= 0; pos--)
			{
				TMask_field& f = fld(pos);
				if (f.is_loadable() && f.get_default().empty())
				{
					name.format("F_%d", f.dlg());
					if (reset || var.objptr(name) != NULL)
					{
						if (f.is_sheet())
						{
							TSheet_field& sf = (TSheet_field&)f;
							sf.destroy(); 
							for (int r = 0; ini.exist(name,r); r++)
								sf.row(r) = ini.get(name, NULL, r);
							sf.force_update();  
						}
						else
						{
							if (!f.is_firm())
								f.set(ini.get(name));
						}
					}
				}
			}
		}
  }
  
  return num;
}

bool TMask::kill_profile(int num)
{
  TFilename prof; 
  make_profile_name(prof);
  
  TConfig ini(prof, "Main");
  if (num <= 0)
    num = ini.get_int(user());
 
  TString8 name; name << num;
  const bool ok = ini.set_paragraph(name);
  if (ok)
    ini.remove_all();

  return ok;
}

const char* TMask::get_caption(TString& str) const
{
  char* title = str.get_buffer(128);
  xvt_vobj_get_title(_pagewin[0], title, str.size());
  return title;
}

void TMask::set_caption(const char* c)
{
  TToken_string captions(c);

  bool bTags = false;
  // Kill any page tags
  for (int i = fields()-1; i > 0; i--)
  {
    TMask_field& tag = fld(i);
    if (tag.dlg() >= DLG_PAGETAGS)
		{
      tag.destroy();
      _field.destroy(i);
      bTags = true;
		}
    else
      break;
  }
    
  if (bTags)
  {
	  byte p;
	  for (p = 1; p <_pages; p++)
		  if (!page_enabled(p))
			  break;
	  while (captions.items() < p)
    {
      const char* k = captions.get(0);
		  captions.add(k);
    }
    add_tag_buttons(captions); // Create page tags
  }
  else
  {
	  for (byte p = 0; p < _pages; p++)
    {
      const char* k = captions.get(p);
      if (k == NULL) k = captions.get(0);
		  xvt_vobj_set_title(_pagewin[p], k);   
    }
  }
}

void TMask::post_error_message(const char* msg, int sev)
{
  CHECK(sev > 0 && msg, "Bad error message posted");
  if (_error_severity > 0)          // C'e' gia' un messaggio d'errore in coda
    on_idle();
  _error_message  = msg;
  _error_severity = sev;
}

// @doc INTERNAL

// @mfunc costruttore di copia
TTimed_box::TTimed_box(const char * header,const char * message,int seconds,short button_id,int x,int y)
  : TMask(header,1,x,y)
{
  // costruisce una maschera run time
  add_memo(FIRST_FIELD, 0, "", 1, 0,-1,-3);
  set(FIRST_FIELD, message);
  
  // setta il timer per l'evento
  _timer_delay=seconds * 1000 + 1;
  _timer_id=XVT_TIMER_ERROR;
  _button_id=button_id;
}

void TTimed_box::start_run()
{
  if (_timer_id!=XVT_TIMER_ERROR)
    xvt_timer_destroy(_timer_id);
  _timer_id=xvt_timer_create(win(),_timer_delay);
  TMask::start_run();
}                     

void TTimed_box::handler(WINDOW win, EVENT* ep)
{
  if (ep->type == E_TIMER && ep->v.timer.id==_timer_id)
    send_key(K_SPACE, _button_id);
  TMask::handler(win, ep);
}

TTimed_box::~TTimed_box()
{
  if (_timer_id != XVT_TIMER_ERROR)
    xvt_timer_destroy(_timer_id);
}

TTimed_breakbox::TTimed_breakbox(const char * message,int seconds,int x,int y)
  : TTimed_box("Richiesta di interruzione",message,seconds,DLG_OK,x,y)
{
  add_button(DLG_CANCEL, 0, TR("Interrompi"), -22, -1, 12, 2,"",0);
  add_button(DLG_OK,     0, TR("Riprova"), -12, -1, 12, 2,"",0);
}

TTimed_breakbox::~TTimed_breakbox()
{}

// @doc INTERNAL

// @mfunc costruttore di copia
TYesnoallnone_box::TYesnoallnone_box(const char * message, int default_key)
  : TMask("Richiesta",1,40,8)

{
  // costruisce una maschera run time
  add_memo(FIRST_FIELD, 0, "", 1, 0,-1,-3);
  set(FIRST_FIELD, message);
  disable(FIRST_FIELD);
  add_button(DLG_OK,        0, TR("Tutti"), -14, -1, 8, 2,"",0);
  add_button(FIRST_FIELD+1, 0, TR("Si"), -24, -1, 8, 2,"",0).set_exit_key(K_YES);
  add_button(FIRST_FIELD+2, 0, TR("No"), -34, -1, 8, 2,"",0).set_exit_key(K_NO);
  add_button(DLG_CANCEL,    0, TR("Nessuno"), -44, -1, 8, 2,"",0);
  switch (default_key)
  {
    case K_ENTER: 
      first_focus(DLG_OK); break;
    case K_ESC: 
      first_focus(DLG_CANCEL); break;
    case K_NO:
      first_focus(FIRST_FIELD+2); break;
    default:
      first_focus(FIRST_FIELD+1);
  }
}

TYesnoallnone_box::~TYesnoallnone_box()
{}