#define XI_INTERNAL
#include <xinclude.h>
#include <statbar.h>    

#include <applicat.h>
#include <colors.h>
#include <config.h>
#include <controls.h>
#include <relation.h>
#include <sheet.h>
#include <toolfld.h>
#include <urldefid.h>
#include <utility.h>
#include <xvtility.h>

///////////////////////////////////////////////////////////
// TSheet_control
///////////////////////////////////////////////////////////

class TSheet_control : public TControl
{
  enum { FIRST_FIELD = 101, MAX_COL = 256 };

  TSheet* _sheet;
  long _cur_rec;
  
  // @cmember:(INTERNAL) Tipo di ogni colonna
  byte _type[MAX_COL];

  // @cmember:(INTERNAL) Indica se e attivata la gestione dei check delle righe
  bool _check_enabled;

  // @cmember:(INTERNAL) Array di righe attivate
  TBit_array _checked;

  // @cmember:(INTERNAL) Array di righe disabilitate
  TBit_array _disabled;  
  
  // @cmember:(INTERNAL) Array delle largezze standard
  int _default_width[MAX_COL];
  
  byte _save_columns_order;

protected:  // TControl
  //@cmember Gestisce gli eventi delle celle
  virtual bool event_handler(XI_OBJ* itf, XI_EVENT* xiev);
  
protected:  
  long items() const { return _sheet->items(); }
  TToken_string& row(long n) const { return _sheet->row(n); }

  int rec2row(long rec) const;
  long row2rec(int row) const;
  
  void make_current(long rec);
  XI_OBJ* find_column(int col) const;
  XI_OBJ* find_column(const char* head) const;

public:  // TControl
  virtual void set_rect(const RCT& r);
  
public:                   
  long selected() const { return _cur_rec; }
  void select(long n);
  void set_focus_rec(long rec);
  
  void enable_check(bool on);
  bool check_enabled() const { return _check_enabled; }
  void check(long n, bool on);
  void toggle(long n);
  bool checked(long n) const { return _checked[n]; }
  long checked() const { return _checked.ones(); }
  bool one_checked() const;
  
  int visible_rows() const;
  
  void enable_row(long r, bool on);
  void disable_row(long r);
  bool row_enabled(long n) const { return _disabled[n] == FALSE; }
  bool one_enabled() const { return _disabled.ones() < _sheet->items(); }
  
  byte column_type(int c) const { CHECKD(c >= 0 && c < MAX_COL, "Bad column ", c); return _type[c]; }
  void align_column(int c, bool right);

  void set_columns_order(TToken_string* order);
  void save_columns_order(const TEdit_field& f) const;
  void load_columns_order(const TEdit_field& f);
  
  void update(long n = -1);
  bool head(int c, TString& str) const;

  TSheet_control(WINDOW sheet, short cid, short x, short y, short dx, short dy,              
                 const char* flags, const char* head);
  virtual ~TSheet_control() {}
};


TSheet_control::TSheet_control(
  WINDOW parent,         // @parm Finestra alla quale appartiene lo spreadsheet
  short cid,             // @parm Identificatore
  short x,               // @parm Coordinata x (in caratteri) nel quale posizionare lo spreadsheet
  short y,               // @parm Coordinata y (in caratteri) nel quale posizionare lo spreadsheet
  short dx,              // @parm Larghezza (in caratteri) dello spreasheet
  short dy,              // @parm Lunghezza (in caratteri) dello spreasheet
  const char* flags,     // @parm Flags di abilitazione
  const char* head)      // @parm Titolo delle colonne
              : _sheet(NULL), _cur_rec(0), _check_enabled(FALSE), 
                _save_columns_order(FALSE)
                
{
  const int NUMBER_WIDTH = 7;
  short v_width[MAX_COL];
  short m_width[MAX_COL];
  int fixed_columns = 1;                               // Number of fixed columns
  int lines_in_cell = 1;
  
  _sheet = (TSheet*)xvt_vobj_get_data(parent);
  
  // Calcolo larghezza massima tabella

  TToken_string header(head);
  TToken_string new_header(header.size());
  int f_width = NUMBER_WIDTH;                          // Stima larghezza colonne fisse
  int max_width = f_width;                             // Stima larghezza della colonna piu' grande
  int tot_width = f_width;
  int lines_in_header = 1;
  const char * h;
  
  int i = 0;
  for (h = header.get(); h && i < MAX_COL; h = header.get(), i++)
  {
    _type[i] = ' ';
    TString testa(h);
    
    char* t = testa.get_buffer();
    for (int c = 0; t[c]; c++)
    {
      if (t[c] == '\\' && (t[c+1] == 'n' || t[c+1] == 'r'))
      {
        t[c] = '\n';
        strcpy(t+c+1, t+c+2);
      }
      if (t[c] == '\n')
        lines_in_header = 2;
    }
    
    const int at = testa.rfind('@');
    int v = testa.len();                             // Video width
    if (at >= 0)
    {
      const TString& wi = testa.mid(at+1);
      v = atoi(wi);
      if (wi.find('F') >= 0) 
      {
        fixed_columns++;
        f_width += v+1;
      }    
      if (wi.find('R') >= 0)
        _type[i] = 'R'; else  
      if (wi.find('V') >= 0)
        _type[i] = 'V'; else
      if (wi.find('P') >= 0)
        _type[i] = 'P'; else
      if (wi.find('M') >= 0)
        _type[i] = 'M'; else
      if (wi.find('C') >= 0)
        _type[i] = 'C';

      if (i == 0 && v <= 1)
      { 
        _type[i] = 'C';
        _check_enabled = TRUE;
        fixed_columns++;
        f_width += v+1;
      }  

      testa.cut(at);
      if (v == 0) v = at;
    } 
    
    if (v > 64) 
    {
      lines_in_cell = (v-1) / 64 + 1;
      v = 64;
    }  
    m_width[i] = v*lines_in_cell + 1; 
    v_width[i] = v+1;
    if (v > max_width) 
      max_width = v;
    
    new_header.add(testa);

    tot_width += v_width[i];
    if (tot_width > 2048)  // Taglio corto con gli sheet enormi > 16000 pixel
      break;
  }
  
  // Calcola rettangolo massimo per lo sheet
  XI_OBJ* itf = get_interface(parent);     
  const XI_RCT rct = coord2rct(itf, x, y, -2, -2);

  // Controlla se ci sono troppe colonne fisse
  if ((f_width+max_width)*XI_FU_MULTIPLE > rct.right)
    fixed_columns = 1;
    

  XI_OBJ_DEF* listdef = xi_add_list_def(NULL, cid,
                                        rct.top, rct.left, rct.bottom-rct.top,
                                        XI_ATR_ENABLED | XI_ATR_VISIBLE | XI_ATR_NAVIGATE,
                                        NORMAL_COLOR, NORMAL_BACK_COLOR,     // normal
                                        NORMAL_COLOR, DISABLED_BACK_COLOR,   // disabled
                                        FOCUS_COLOR,                         // active
                                        0);
                                        
  listdef->app_data = (long)this;
  
  XI_LIST_DEF* l = listdef->v.list;
  l->min_heading_height = xi_button_calc_height_font(xi_get_system_font()) * lines_in_header;
  l->max_lines_in_cell  = lines_in_cell;
  l->sizable_columns    = TRUE;
  l->movable_columns    = TRUE;
  l->fixed_columns      = fixed_columns;
  l->scroll_bar         = TRUE;                        
  l->scroll_bar_button  = TRUE;
  l->white_space_color  = MASK_DARK_COLOR;
  l->rule_color         = MASK_DARK_COLOR;
#ifdef LINUX
  l->scroll_on_thumb_track = TRUE;
#endif

  // Definizione della prima colonna (numero di riga)
  const long attr = XI_ATR_VISIBLE | XI_ATR_RJUST | XI_ATR_SELECTABLE;                                            
  XI_OBJ_DEF* coldef = xi_add_column_def(listdef, FIRST_FIELD+1000-1, attr, 0, 
                                         2 * XI_FU_MULTIPLE, NUMBER_WIDTH , "");
  
  coldef->app_data = (long)this;
  coldef->v.column->heading_platform = TRUE;
  coldef->v.column->column_platform  = TRUE;
  coldef->v.column->center_heading   = TRUE; 

  for (h = new_header.get(0), i = 0; h && i < MAX_COL; h = new_header.get(), i++)
  {                  
    long attr = XI_ATR_VISIBLE | XI_ATR_ENABLED | XI_ATR_AUTOSCROLL | XI_ATR_READONLY; 
    if (i == 0 && _type[i] == 'C')
      attr |= XI_ATR_SELECTABLE;
    if (_type[i] == 'R' || _type[i] == 'V' || _type[i] == 'P')         // Right, Currency, Price
      attr |= XI_ATR_RJUST;
    coldef = xi_add_column_def(listdef, FIRST_FIELD+i+1000, attr, i+1,
                               v_width[i] * XI_FU_MULTIPLE, m_width[i], (char*)h);

    coldef->app_data = (long)this;
    coldef->v.column->heading_platform = TRUE;
    coldef->v.column->center_heading = TRUE;
    if (m_width[i] > 64)
      coldef->v.column->wrap_text = TRUE;
    
    if (i == 0 && _type[i] == 'C') 
    {
      coldef->v.column->icon_rid = ICO_CHECK_ON;
      coldef->v.column->icon_mode = XIM_ICON_HAS_PRIORITY;
      if (l->min_heading_height < 20)
        l->min_heading_height = 20;
    }
  }
                                
  // Attenzione: negli sheet molto grandi succede che rd.right < 0
  XI_RCT rd; xi_get_def_rect(listdef, &rd); 
  if (rd.right < 0 || (rd.right - rd.left) > (rct.right - rct.left))
    l->width = rct.right - rct.left;

  _obj = xi_create(itf, listdef);     // Create the whole thing!
  xi_dequeue();                       // Flush events in XOL
  xi_tree_free(listdef);              // Free definitions
               
  CHECKD(_obj, "Can't create spreadsheet ", cid);   
  update_tab_cid();
  
  // Se la finestra e' troppo grande riducila
  RCT cli; xvt_vobj_get_client_rect(parent, &cli);
  xi_get_rect(_obj, (XinRect*)&rct);
    
  const int extra = (cli.right - (rct.right-rct.left+2*XI_FU_MULTIPLE)) / 2;
  if (extra > XI_FU_MULTIPLE)
  {
    WINDOW main_win = _sheet->win();
    xvt_vobj_get_outer_rect(main_win, &cli);
    cli.left += extra;
    cli.right -= extra;
    xvt_vobj_move(main_win, &cli);
    xvt_vobj_get_client_rect(parent, &cli);
  }

  int num;
  XI_OBJ** column = xi_get_member_list(_obj, &num);
  for (i = 0; i < num; i++) 
  {
    XI_RCT rct; xi_get_rect(column[i], &rct);
    _default_width[i] = rct.right - rct.left;
  }
} 

void TSheet_control::set_rect(const RCT& r)
{
  const int width  = r.right - r.left - 2*XI_FU_MULTIPLE;
  const int height = r.bottom - r.top;
  xi_set_list_size(_obj, height, width);
}

// Converts a row number in the correspondig record number
int TSheet_control::rec2row(long record) const
{
  int rows;
  const long* rec = xi_get_list_info(_obj, &rows);
  int r = int(record - rec[0]);
  if (r < 0 || r >= rows) 
    r = -1;
  return r;
}

// Converts a row number in the correspondig record number
long TSheet_control::row2rec(int row) const
{
  int rows;
  const long* handle = xi_get_list_info(_obj, &rows);
  
  if (row < 0) 
    row = 0;
  else 
  {
    if (row >= rows) 
      row = rows-1;
  }    
  const long r = handle[row];
  CHECKD(r >= 0 && r < items(), "Sheet line out of range: ", row);
  
  return r;
} 

int TSheet_control::visible_rows() const
{
  return xi_get_visible_rows(_obj, NULL, NULL);
}

void TSheet_control::update(long n)
{
  if (n >= 0)
  {
    const int riga = rec2row(n);
    if (riga >= 0)
    {                 
      XI_OBJ row;
      XI_MAKE_ROW(&row, _obj, riga);
      xi_cell_request(&row);           
    }
  }
  else
  {
    int num = 0;
    const long* handle = xi_get_list_info(_obj, &num);
    const long tot = items();
    bool scroll_first = tot == 0; // || !owner().mask().is_running();
    if (!scroll_first)
    {
      int first = 0, last = 0;
      xi_get_visible_rows(_obj, &first, &last);   
      n = handle[first];
      scroll_first = n > tot;
    }
    
    if (scroll_first)
      xi_scroll(_obj, XI_SCROLL_FIRST);
    else
      xi_scroll_rec(_obj, n, NORMAL_COLOR, XI_ATR_ENABLED, 0);
  }  
}

void TSheet_control::make_current(long rec)
{               
  const long old = _cur_rec;
  _cur_rec = rec;
  
  XI_OBJ o; 
  const int oldrow = rec2row(old);
  if (oldrow >= 0)
  {
    XI_MAKE_ROW(&o, _obj, oldrow);
    xi_cell_request(&o);
  }  
  const int newrow = rec2row(rec);
  if (newrow >= 0 && newrow != oldrow)
  {
    XI_MAKE_ROW(&o, _obj, newrow);
    xi_cell_request(&o);
  }   
}

void TSheet_control::select(long rec)
{     
  if (rec >= 0 && rec < items())
  {
    int rows;
    const long* handle = xi_get_list_info(_obj, &rows);
            
    int first = 0, last = 0;
    xi_get_visible_rows(_obj, &first, &last);

    if (rec < handle[first] || rec > handle[last])
      xi_scroll_rec(_obj, rec, NORMAL_COLOR, XI_ATR_ENABLED, 0);
  }  
  make_current(rec);
}

void TSheet_control::set_focus_rec(long rec)
{
  if (rec < 0) 
    rec = selected();
  int r = rec2row(rec);
  if (r < 0)
  {
    select(rec);
    r = rec2row(rec);
  }                 
  
  const int c = _type[0] == 'C' ? 2 : 1;
  XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, r, c);
  xi_set_focus(&cell);
}                                         

XI_OBJ* TSheet_control::find_column(
  int col) const    // @parm  Indice o identificatore colonna
{     
  CHECKD(col >= 0, "Bad column ", col);
  if (col < FIRST_FIELD)         // Se e' un indice trasformalo in identificatore
    col += FIRST_FIELD + 1000;
  
  int num;
  XI_OBJ** columns = xi_get_member_list(_obj, &num);
  int c;
  
  for (c = num-1; c >= 0; c--)
  {          
    if (columns[c]->cid == col)
      break;
  }  
  return c >= 0 ? columns[c] : NULL;
}

XI_OBJ* TSheet_control::find_column(
  const char* head) const    // @parm  testata colonna
{     
  int num;
  XI_OBJ** column = xi_get_member_list(_obj, &num);
  int c;
  
  for (c = num-1; c >= 0; c--)
  {         
    char str[80];     
    xi_get_text(column[c], str, sizeof(str));
    if (strcmp(str, head) == 0)
      break;
  }  
  return c >= 0 ? column[c] : NULL;
}

bool TSheet_control::head(int c, TString& str) const
{
  XI_OBJ* col = find_column(c);
  if (col != NULL)
    xi_get_text(col, str.get_buffer(80), 80);
  else
    str.cut(0);
  return col != NULL;
}

void TSheet_control::enable_check(bool on)
{
  _check_enabled = on && _type[0] == 'C';
  if (_type[0] == 'C')
  {
    XI_OBJ* column = find_column(1101);
    CHECK(column, "Can't find checkable column");
    dword attr = xi_get_attrib(column);
    if (_check_enabled) attr |= XI_ATR_ENABLED;
    else attr &= ~XI_ATR_ENABLED;

    xi_set_attrib(column, attr);          // Set new attributes
  }
}

bool TSheet_control::one_checked() const
{
  bool yes = FALSE;
  if (check_enabled())
  {
    const long first = _checked.first_one();
    yes =  first >= 0;
  }
  return yes;
}

// @mfunc Permette di attivare/disattivare una riga
void TSheet_control::check(
  long n,  // @parm Numero della riga da attivare/disattivare
  bool on) // @parm Operazione da effettuare sulla riga:
  //
  // @flag TRUE | Attiva la riga <p n>.esima (default)
  // @flag FALSE | Disattiva la riga <p n>.esima
{
  if (n < 0)
  {
    if (on)
    {
      const long tot = items()-1;
			if (tot >= 0)
			{
				_checked.set(tot);            // Forza le dimensioni del bit array
				_checked.set();               // Setta tutti i bit
      
				// Elimina i bit in eccesso alla fine dell'array
				for (long i = _checked.items()-1; i > tot; i--)
					_checked.reset(i);
      
				// Elimina i bit corrispondenti a righe disabilitate
				if (_disabled.first_one() >= 0)
				{ 
					for (long i = tot; i >= 0; i--)
						if (_disabled[i]) _checked.reset(i);
				}   
			}
    } 
    else
      _checked.reset();
    
    // Aggiorna tutta la prima colonna
    XI_OBJ* column = xi_get_obj(_obj, FIRST_FIELD+1000);
    xi_cell_request(column);
  }     
  else           
  {
    if (!_disabled[n])
    {
      _checked.set(n, on);            
      // Aggiorna la riga  
      if (_sheet->is_running())
      {
        XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, rec2row(n), 1);
        xi_cell_request(&cell);
      }
    }
  }  
}

void TSheet_control::toggle(long n)
{
  check(n, !checked(n));
}

// @doc INTERNAL

// @mfunc Abilita/disabilita una riga
void TSheet_control::enable_row(
  long n,  // @parm Numero della riga da abilitare/diabilitare (default -1)
  bool on) // @parm Operazione da svolgere sulla riga
  //
  // @flag TRUE | Abilita la riga <p n>-esima (default)
  // @flag FALSE | Disabilita la riga <p n>-esima

  // @comm Se <p n> e' minore di 0 allora vengono abilitate/disabilitate tutte le righe
{              
  if (n >= 0)
  {
    _disabled.set(n, !on);
  }  
  else                     
  {
    if (on) 
      _disabled.reset();
    else
    {
      const long tot = items()-1;
      _disabled.set(tot);      // Forza le dimensioni del bit-array
      _disabled.set();         // Disabilita tutte le righe
      
      // Resetta i bit in ecesso alla fine del bit-array
      for (long i = _disabled.items()-1; i > tot; i--)
        _disabled.reset(i);
    }
  }  
  update(n);
}       

void TSheet_control::save_columns_order(const TEdit_field& field) const
{
  if (_save_columns_order)
  {
    TString parag; 
    if (_sheet->get_ini_paragraph(field, parag))
    {
      TConfig config(CONFIG_USER, parag);   // Apre il file di configurazione

      TToken_string order(127);             // Nuovo ordine delle colonne
      if (_save_columns_order == TRUE)      // Se vale 3 devo solo resettare
      {
        int num;
        XI_OBJ** column = xi_get_member_list(_obj, &num);
        for (int i = 0; i < num; i++)       // Scorre tutte le colonne
        {             
				  TString h;
          char head[80];

          xi_get_text(column[i], head, sizeof(head));
				  for (const char * s = head; *s; s++)
				  {
					  if (*s == '\n')
						  h << "\\n";
					  else
						  h << *s;
				  }
          order.add(h);
          RCT rct; xi_get_rect(column[i], (XinRect *) &rct);
          order << ',' << rct.right - rct.left;
        }
        config.set("Browse", order, NULL, TRUE, field.dlg());
      }  
      else
        config.remove("Browse", field.dlg());
    }
  }  
}

void TSheet_control::load_columns_order(const TEdit_field& field)
{
  TFilename parag; 
  if (_sheet->get_ini_paragraph(field, parag))
  {
    TConfig config(CONFIG_USER, parag);
    const int index = field.dlg();
    TToken_string order(esc(config.get("Browse", NULL, index)));
    if (order.empty_items())
      config.remove("Browse", index);
    else
      set_columns_order(&order);
  }  
  _save_columns_order = FALSE;  
}                   

void TSheet_control::align_column(int c, bool right)
{
  XI_OBJ* column = find_column(c);
  CHECKD(column, "Can't find numeric column ", c);
  dword attr = xi_get_attrib(column);
  if (right)
    attr |= XI_ATR_RJUST;
  else
    attr &= ~XI_ATR_RJUST;
  xi_set_attrib(column, attr);       
  _type[c] = right ? 'R' : ' ';
}

void TSheet_control::set_columns_order(TToken_string* order)
{                                 
  XI_OBJ* itf = get_interface();
  XI_OBJ* focus = xi_get_focus(itf);
  xi_set_focus(itf);
  
  int num_cols;
  xi_get_member_list(_obj, &num_cols);  //verificare
  
  // Costante da sottrarre nella xi_column_set_pixel_width altrimenti la somma due volte!
  const int offset = 2 * (int)xi_get_pref(XI_PREF_COLUMN_OFFSET);
  
  if (order == NULL)
  {
    for (int index = 0; index < num_cols; index++)
    {                                
      const short cid = FIRST_FIELD + 1000 + index - 1;
      XI_OBJ* col = find_column(cid);
      if (col)
      {
        xi_move_column(col, index); 
        RCT rct; xi_get_rect(col, (XinRect*)&rct);
        if (_default_width[index] != rct.right - rct.left)
          xi_column_set_pixel_width(col, _default_width[index]-offset);
      }    
    }  
    _save_columns_order = 0x3;
  }
  else
  {              
    TString head(23);
    TToken_string col(8, ',');
    int pos = 0;
    for (col = order->get(0); !col.blank(); col = order->get(), pos++)
    {
      head = col.get(0);
      const int width = col.get_int();
      XI_OBJ* column = find_column(head);
      if (column)                        // Controlla che esista ancora
      {             
        if (pos > 0 && pos < num_cols)
          xi_move_column(column, pos);   // Sposta la colonna se possibile
        if (width > XI_FU_MULTIPLE)      // Se ha una larghezza valida
          xi_column_set_pixel_width(column, width - offset);
      }
    }  
  }
  
  if (focus)
    xi_set_focus(focus);
}
       
HIDDEN long _rec_to_select = -1;
// Certified 75%
bool TSheet_control::event_handler(XI_OBJ* itf, XI_EVENT *xiev)
{
  BOOLEAN& refused = xiev->refused;   

  switch (xiev->type)
  {
  case XIE_GET_FIRST:
    if (items() > 0L)
    {
      long n = items() * (long)xiev->v.rec_request.percent / 100L;
      if (n < 0L) n = 0L;
      xiev->v.rec_request.data_rec = n;
    }
    else
      refused = TRUE;
    break;
  case XIE_GET_LAST:
    xiev->v.rec_request.data_rec = items()-1;
    break;
  case XIE_GET_PREV:
  case XIE_GET_NEXT:
    {
      const long n = xiev->v.rec_request.spec_rec + (xiev->type == XIE_GET_NEXT ? +1 : -1) ;
      if (n < 0 || n >= items())
        refused = TRUE;
      else
        xiev->v.rec_request.data_rec = n;
    }
    break;
  case XIE_CELL_REQUEST:
  {
    const long rec = xiev->v.cell_request.rec;
    const char* src = NULL;
    int nm;
    XI_OBJ** obj = xi_get_member_list(xiev->v.cell_request.list, &nm);
    const int num = xiev->v.cell_request.col_nbr;
    const int cid = obj[num]->cid - 1000;

    if (cid >= FIRST_FIELD)
    {
      if (rec >= 0 && rec < items())
      { 
        const int col = cid - FIRST_FIELD;
        switch (_type[col])
        {
        case 'C':                                 // Set value for "checkable" cell
          {
            const bool on = col == 0 ? checked(rec) : row(rec).get_char(col) > '0';
            xiev->v.cell_request.icon_rid = on ? ICO_CHECK_ON : ICO_CHECK_OFF;
          }  
          break;
        case 'M':                                 // Set value for "roman" cell
          src = itor(row(rec).get_int(col));    
          break;
        case 'V':                                 // Set value for "value" cell
          {                                      
            const real r = row(rec).get(col);
            TCurrency c(r);
            src = c.string(true);    
          }
          break;
        case 'P':                                 // Set value for "price" cell
          {                                      
            const real r = row(rec).get(col);
            TPrice c(r);
            src = c.string(true);    
          }
          break;
        default:                                  // Set value for "normal" cell
          src = row(rec).get(col);                
          break;
        }  

        if (_disabled[rec])
        {
          xiev->v.cell_request.color = DISABLED_COLOR; 
          xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; 
        } 
        else
          if (rec == _cur_rec)
          {                                                
            xiev->v.cell_request.color = FOCUS_COLOR; 
            xiev->v.cell_request.back_color = FOCUS_BACK_COLOR; 
          }
          else
            _sheet->get_cell_colors(rec, col, xiev->v.cell_request.color, xiev->v.cell_request.back_color); 
      }  
      else
        refused = TRUE;
    } 
    else
    { 
      xiev->v.cell_request.color = NORMAL_COLOR; 
//      src = format("%ld", rec+1);   // Niente piu' numeri di riga!
    } 

    char* dst = xiev->v.cell_request.s;
    if (src && *src)
    {
      const int len = xiev->v.cell_request.len;
      strncpy(dst, src, len);
      dst[len-1] = '\0';
    }  
    else
      *dst = '\0';
  }
    break;
  case XIE_DBL_CELL:
    {
      const long rec = row2rec(xiev->v.xi_obj->v.cell.row);
      if (!_disabled[rec])
      {
        make_current(rec);
        _sheet->stop_run(K_ENTER);
      }
      else
        refused = TRUE;
    }  
    break;   
  case XIE_ON_LIST:    
    _sheet->notify_focus_field(id());
    break;
  case XIE_ON_ROW:
    {            
      const long rec = row2rec(xiev->v.xi_obj->v.row);
      if (_disabled[rec])
      {
        refused = TRUE;
        // Cerca la prossima riga abilitata e valida
        const int dir = rec > selected() ? +1: -1;
        const long max = items();
        for (long r = rec+dir; r >= 0 && r < max; r += dir) if (!_disabled[r])
        {
          _sheet->post_select(r);
          break;
        }    
      }  
      else                    
      {
        if (_sheet->_select_row < 0)
          make_current(rec);
        else 
          _rec_to_select = rec;
      }
    }
    break;
  case XIE_GET_PERCENT:
  {
    const long rec = xiev->v.get_percent.record;
    long n = items(); if (n <= 0) n = 1;
    xiev->v.get_percent.percent = int(rec * 100L / n);
  }
    break;
  case XIE_SELECT:
    if (xiev->v.xi_obj->type == XIT_ROW)
    {           
      const int row = xiev->v.select.xi_obj->v.row;
      const long rec = row2rec(row);
      if (row_enabled(rec))
      {
        int num;
        XI_OBJ** column = xi_get_member_list(_obj, &num);
        const int col = xiev->v.select.column;
      
        const short cid = column[col]->cid - 1000;
        if (cid >= FIRST_FIELD)
        { 
          if (check_enabled())
            toggle(rec);
        }
        else 
        {
          if (_cur_rec == rec)
            _sheet->on_key(K_CTRL+'G'); 
        }
        const int c = _type[0] == 'C' ? 2 : 1;
        XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, row, c);
        xi_set_focus(&cell);
        make_current(rec);
      }
    }  
    refused = TRUE;
    break;  
  case XIE_COL_MOVE:           
    if (xiev->v.column.in_fixed || 
        xiev->v.column.col_nbr < xi_get_fixed_columns(xiev->v.column.list))
      refused = TRUE;
    else
      _save_columns_order = TRUE;
    break;  
  case XIE_COL_SIZE:            
    {
      if (_obj->v.list->hsb_win) 
        _save_columns_order = TRUE;
      else
        refused = TRUE;  // Non permetto il resize in assenza di scroll bar orizzontale
    }
    break;  
  case XIE_BUTTON:
    if (xiev->v.xi_obj->type == XIT_LIST)
      _sheet->on_key(K_CTRL+'N');
    break;         
  case XIE_XVT_EVENT:
    if (xiev->v.xvte.type == E_CHAR)
    {
      const KEY k = e_char_to_key(&xiev->v.xvte);
      long rec = -1;
      switch (k)
      {                         

        case K_SPACE: 
          if (_check_enabled)
            check(selected(), !checked(selected()));
          break;
        case K_ENTER:
          if (_disabled[_cur_rec])
            break;
        case K_ESC:
          _sheet->stop_run(k);
          break;
        case K_LHOME:                                      
          rec = 0; 
          break;
        case K_PREV: 
          rec = _cur_rec - visible_rows(); 
          if (rec < 0) rec = 0; 
          break;
        case K_NEXT: 
          rec = _cur_rec + visible_rows(); 
          if (rec >= items())
            rec = items()-1;
          break;
        case K_LEND:  
          rec = items()-1; 
          break;
        default:     
          break;
      }
      if (rec >= 0) 
      {
        _sheet->post_select(rec);
        refused = TRUE;
      }  
    }  
    break;  
  default:
    break;
  }
  
  return !refused;
}

///////////////////////////////////////////////////////////               
// TQuery_field
///////////////////////////////////////////////////////////               

class TQuery_field : public TOperable_field
{                       
protected:
  virtual void create(WINDOW parent);

public:                      
  TSheet_control* sheet() const { return (TSheet_control*)_ctl; }

  TQuery_field(TMask* m) : TOperable_field(m) { }
  virtual ~TQuery_field() { }
};

void TQuery_field::create(WINDOW parent)
{ 
  _ctl = new TSheet_control(parent, dlg(), 
                            _ctl_data._x, _ctl_data._y, 
                            _ctl_data._width, (int)_ctl_data._size, 
                            _ctl_data._flags, _ctl_data._prompt);
}

///////////////////////////////////////////////////////////               
// TSheet
///////////////////////////////////////////////////////////               

#define DLG_QUERY 25883
    
TSheet::TSheet(short x, short y, short dx, short dy,
               const char* title, const char* head, 
               byte buttons, short sht_y, WINDOW parent)
      : TMask(title, 1, dx, dy, x, y, parent), 
        _sheet(NULL), _select_row(-1), _parked(-1)
{ 
  create_bar(1);                 // Crea toolbar in alto

  TQuery_field* qf = new TQuery_field(this);
  qf->construct(DLG_QUERY, head, 1, sht_y, 0, page_win(0), "", 0);
  fields_array().add(qf);
  _sheet = qf->sheet();

  if (!(buttons & 0x10)) 
    add_button(DLG_SELECT, TR("~Selezione"), K_ENTER, TOOL_OK);

  if (check_enabled())   
  {
    add_button(DLG_USER, TR("~Tutti"), 0, TOOL_MULTISEL);
    set_handler(DLG_USER, tutti_handler);
  }  

  if (buttons & 0x01) add_button(DLG_LINK,   TR("Colle~ga"), K_CTRL+'G', TOOL_LINK);
  if (buttons & 0x02) add_button(DLG_NEWREC, TR("~Nuovo"), K_INS, TOOL_NEWREC);
  if (buttons & 0x04) add_button(DLG_DELREC, TR("~Elimina"), K_DEL, TOOL_DELREC);

  add_button(DLG_EXPORT, TR("~Excel"), 0, TOOL_EXCEL);
  set_handler(DLG_EXPORT, export_handler);

  if (buttons & 0x08) add_button(DLG_QUIT,   "Fine", K_ESC, TOOL_QUIT);
  else                add_button(DLG_CANCEL, "Annulla", K_ESC, TOOL_CANCEL);

  xvt_toolbar_realize(toolbar()); // Necessario per calcolare dimensioni corrette dello sheet
} 

TSheet::~TSheet()
{
//  delete _sheet;   // Guy bestia:  Already deleted by TMask!
}

TSheet_control& TSheet::sheet()
{ return *_sheet; }

// Costruisce l'identificatore del paragrafo contenente la disposizione
// delle colonne del campo f
bool TSheet::get_ini_paragraph(const TEdit_field& f, TString& name) const
{ 
  const TMask& m = f.mask();
  name = m.source_file().name();      
  if (name.blank())            // Maschera dinamica
    name = main_app().name();
  else
    name = name.before(".");   // Nome della maschera senza estensione
  const int index = m.number();
  CHECKD(index >= 0 && index <= 8, "Bad mask index:", index);
  if (index > 0)          // Aggiunge l'eventuale numero di sotto-maschera
    name << '(' << index << ')';
  return true;
}

// @doc INTERNAL

// @mfunc Aggiunge un bottone nella finestra
void TSheet::add_button(
  short id,              // @parm Identificatore del bottone da aggiungere
  const char* caption,   // @parm Testo del bottone da aggiungere
  KEY key,               // @parm Combinazione di tasti corrispondente
  short bmp_up,          // @parm Bitmap normale
  short bmp_dn)          // @parm Bitmap premuta
{         
  if (id > 0 && bmp_up <= 0)
  {
    if (toolwin() == NULL_WIN)
      create_bar(-2); // Forza la creazione della bottom bar
    TButton_field& butt = TMask::add_button(id, MAX_PAGES, caption, 0, -1, 11, 2);
    butt.set_exit_key(key);
  }
  else 
  {
    TButton_tool& butt = add_button_tool(id, caption, bmp_up);
    butt.set_exit_key(key);
  }
}

bool TSheet::get_cell_colors(int row, int col, COLOR& fore, COLOR& back) const 
{ 
  fore = back = 0;   // Default colors
  if (EASY_RIDER && (row & 1))
    back = EASY_RIDER_COLOR;
  return false;      // Not assigned
}  

void TSheet::repos_buttons() const
{      
  if (_sheet == NULL)
    return;                  // Sono ancora in fase di creazione: aspetta!

  WINDOW btnwin = toolwin(); // was win()
  if (btnwin != NULL_WIN)
  {
    int buttons = 0;
    RCT br;
    FOR_EACH_MASK_FIELD((*this), f, c)
    {            
      if (c->parent() == btnwin && c->is_kind_of(CLASS_BUTTON_FIELD))
      {
        if (buttons == 0) 
          c->get_rect(br);
        buttons++;
      } 
    }
    if (buttons > 0)
    {
      RCT wr; xvt_vobj_get_client_rect(btnwin, &wr);
      const short width = br.right - br.left;
      const short height = br.bottom - br.top;
        
      int space = (wr.right - buttons * width) / (buttons+1);
      if (space < 0) space = 0;
      int x = space;
      const int y = (wr.bottom - height) / 2;

      FOR_EACH_MASK_FIELD((*this), f, c)
      {                                    
        if (c->parent() == btnwin && c->is_kind_of(CLASS_BUTTON_FIELD))
        {                  
          buttons--;
          const PNT p = { y, x };
          xvt_rect_set_pos(&br, p);
          c->set_rect(br);
          x += space+width;
        }
      }  
    }
  }

  // Aggiusta anche lo spreadsheet se necessario
  TMask_field& s = field(DLG_QUERY);
  RCT wr; xvt_vobj_get_client_rect(s.parent(), &wr);
  RCT br; s.get_rect(br);
  if (br.bottom > wr.bottom || (wr.bottom-br.bottom) > 48)
  {
    br.bottom = wr.bottom - br.left; // Lascio uno spazio in fondo pari al bordo sinistro
    s.set_rect(br);
  }
}

void TSheet::force_update(int r)
{
  _sheet->update(r);
}

void TSheet::start_run()
{                          
  const bool on = items() > 0 && _sheet->one_enabled();

  // Abilita selezione se c'e' almeno un elemento
  int pos = id2pos(DLG_SELECT);
  if (pos >= 0)
    fld(pos).enable(on);     
    
  pos = id2pos(DLG_LINK);
  if (pos >= 0)
    fld(pos).enable(on);     
    
  reset_parked();
    
  repos_buttons();
  force_update();
  
  if (on)      
  {
    long i = selected();
    if (i < 0 || i >= items())
      i = 0;                   
    if (row_disabled(i))
      for (i = 0; row_disabled(i); i++);
    post_select(i);
  }  
}

long TSheet::selected() const
{ return _sheet->selected(); }

bool TSheet::check_enabled() const
{ return _sheet->check_enabled(); }

void TSheet::enable_check(bool on)
{ _sheet->enable_check(on); }

void TSheet::check(long n, bool on)
{ _sheet->check(n, on); }

bool TSheet::checked(long n) const
{ return _sheet->checked(n); }

long TSheet::checked() const
{ return _sheet->checked(); }

bool TSheet::one_checked() const
{ return _sheet->one_checked(); }

void TSheet::enable_row(long r, bool on)
{ _sheet->enable_row(r, on); }

bool TSheet::row_enabled(long r) const
{ return _sheet->row_enabled(r); }
      
bool TSheet::on_key(KEY key)
{
  switch(key)
  {
  case K_ESC:
    stop_run(key);
    break;
  case K_CTRL+'E':
    if (items() && id2pos(DLG_SAVEREC) >= 0)
      export_handler(field(DLG_SAVEREC), K_SPACE);
    break;
  case K_DEL:
    if (items() && id2pos(DLG_DELREC) >= 0)
      stop_run(K_DEL);
    break;
  case K_CTRL+'N':
    if (id2pos(DLG_NEWREC) >= 0)
      stop_run(K_INS);
    break;
  case K_CTRL+'G':
    if (id2pos(DLG_LINK) >= 0)
      stop_run(key);
    break;
  case K_CTRL+'S':
    if (id2pos(DLG_SELECT) >= 0)
      stop_run(K_ENTER);
    break;  
  default: 
    key -= K_CTRL;            
    if (key >= 'A' && key <= 'Z')
    {
      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);
            if (b.exit_key() > 0)
              stop_run(b.exit_key());
            return TRUE;
          }
        }
      }
    }
    break;
  }

  if (check_enabled() && items() > 0)
  { 
    switch(key)
    {
    case K_CTRL+'T':
      tutti_handler(field(DLG_USER), K_SPACE);
      break;
    case K_F7: 
      uncheck(-1); 
      break;
    case K_F8:   
      check(-1);
      break;
    default: 
      break;
    }
  }

  return true;
}

// @doc INTERNAL

// @mfunc Seleziona una riga facendola diventare corrente
void TSheet::select(
  long n)  // @parm Riga da selezionare
{
  _sheet->select(n);
}

void TSheet::post_select(long rec)
{
  _select_row = rec;
}

void TSheet::on_idle()
{
  if (_select_row >= 0)
  {    
    const short focus_id = low_get_focus_id(curr_win());  
    _sheet->select(_select_row);
    if (focus_id == _sheet->id())
      _sheet->set_focus_rec(-1);
    _select_row = -1;
  }
  TMask::on_idle();
}

// @doc INTERNAL

// @mfunc Ritorna il contenuto di una riga
//
// @rdesc Ritorna la <c TToken_string> con tutti gli elemnti della riga
TToken_string& TSheet::row(
  long n)  // @parm Riga di cui ritorna il contenuto (default -1)

  // @comm Se viene passato un numero di riga minore di 1 viene ritornato il contenuto della riga
  //   selezionata.
{
  if (n < 0) 
    n = selected();
  if (n != _parked)
    get_row(_parked = n, _park);
  return _park;
}

bool TSheet::tutti_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {       
    TSheet& s = (TSheet&)f.mask();
		if (s.check_enabled())
			s.check(-1, !s.one_checked());
  }
  return true;
}

static TString& clean_white_space(TString& str)
{
  str.trim();
  char* buff = str.get_buffer();
  for (char* b = buff; *b; b++)
  {
    if (*b > '\0' && *b < ' ') 
      *b = ' ';
  }
  str.strip_double_spaces();
  return str;
}

bool TSheet::export_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {       
    TSheet& s = (TSheet&)f.mask();
    TString cap; s.get_caption(cap);
    for (char* buf = cap.get_buffer(); *buf; buf++)
    {
      switch (*buf)
      {
      case '?':
      case '(':
      case ')':
      case '/':
      case '\\':
      case '*': *buf = ' '; break;
      default :  break;
      }
    }
    cap.strip_spaces();
    if (cap.blank())
      cap = "export";
    TFilename name;
    name.tempdir(); name.add(cap); name.ext("xls");
    if (name.full()) // Dummy test
    {
      ofstream xls(name);
      const char sep = '\t';
      TToken_string tab(128, sep);

      TString str;

      int columns = 0;
      XI_OBJ** pcols = xi_get_member_list(s._sheet->xi_object(), &columns);
      for (int c = 1; c < columns; c++)
      {
        xi_get_text(pcols[c], str.get_buffer(), str.size());
        tab.add(clean_white_space(str));
      }
      xls << tab << endl;

      for (long i = 0; i < s.items(); i++)
      {
        const TToken_string& r = s.row(i);
        tab.cut(0);
        for (int c = 1; c < columns; c++)
        {
          const int idx = pcols[c]->cid - 1101;
          r.get(idx, str);
          clean_white_space(str);
          if (str.not_empty())
          {
            const char ct = s._sheet->column_type(idx);
            if (ct == 'P' || ct == 'V')
              xvt_str_number_format(str.get_buffer(), str.size());
            tab.add(str, c-1);
          }
        }
        xls << tab << endl;
      }
    }
    if (name.exist())
      xvt_sys_goto_url(name, "open");
  }
  return true;
}

void TSheet::save_columns_order(const TEdit_field& field)
{ sheet().save_columns_order(field); } 

void TSheet::set_columns_order(TToken_string* col)
{ sheet().set_columns_order(col); } 

void TSheet::load_columns_order(const TEdit_field& field)
{ sheet().load_columns_order(field); } 

///////////////////////////////////////////////////////////
// TArray_sheet
///////////////////////////////////////////////////////////

TArray_sheet::TArray_sheet(short x, short y, short dx, short dy, const char* caption, 
                           const char* head, byte buttons, short sht_y)
            : TSheet(x, y, dx, dy, caption, head, buttons, sht_y)
{ 
  TToken_string cap(caption);
  if (cap.items() > 1)
  {
    cap.get(0);
    FOR_EACH_TOKEN(cap, tok)
      create_page(tok, 0);
  }
}

// Certified 99%
bool TArray_sheet::destroy(int i)               
{
  uncheck(-1);
  enable_row(-1); 
  reset_parked();
  return _data.destroy(i, TRUE);
}                                                             

// @doc EXTERNAL

// @mfunc Aggiunge un elemento allo sheet
//
// @rdesc Ritorna il numero di elementi presenti nello sheet
long TArray_sheet::add( 
  const TToken_string& s)  // @parm Oggetto da aggiungere
  // @parm const TToken_string | *s | Oggetto da aggiungere passato per indirizzo

  // @syntax add(const TToken_string& s)
  // @syntax add(const TToken_string* s)
{
  const long n = _data.add(s);
  if (n >= 0 && s[0]=='X' && check_enabled())
    check(n);
  return n;
}

long TArray_sheet::add(TToken_string* s)
{
  const long n = _data.add(s);
  if (n >= 0 && (*s)[0]=='X' && check_enabled())
    check(n);
  return n;
}

long TArray_sheet::insert(const TToken_string& s, long n)
{
  _data.insert(s, (int)n);
  const int maxrows=int(items());
  for (int r = maxrows-1; r >n ; r--)
  {
    const bool enab= row_enabled(r);
    enable_row(r,row_enabled(r+1));
    enable_row(r+1,enab);
  }
  return n;
}

///////////////////////////////////////////////////////////
// TCursor_sheet
///////////////////////////////////////////////////////////

bool TCursor_sheet::in_key(const TFieldref& fr) const
{
  const int logic = _cursor->file().num();
  if (fr.file() != 0 && fr.file() != logic)
    return false;

  const RecDes& rd = prefix().get_recdes(logic);

  const int key = _cursor->key();
  const KeyDes& kd = rd.Ky[key-1];

  for (int i = 0; i < kd.NkFields; i++)
  {
    const int n = kd.FieldSeq[i] % MaxFields;
    if (strcmp(rd.Fd[n].Name, fr.name()) == 0)
      return true;
  }

  return false;
}

TCursor_sheet::TCursor_sheet(TCursor* cursor, const char* fields,
                             const char* title, const char* head, 
                             byte buttons, short sht_y, WINDOW parent)
             : TSheet(sht_y ? 3 : 0, sht_y ? 3 : 0, sht_y ? -3 : 0, sht_y ? -3 : 0, 
                      title, head, buttons, sht_y, parent), _cursor(cursor)
{
  TToken_string fldlst(fields);
  int campo = 0;
  for (const char* t = fldlst.get(0); t; t = fldlst.get(), campo++)
  {
    if (*t > ' ' && *t != '"')
    {
      const TFieldref fr(t, 0);
      TRecfield* rf = new TRecfield(_cursor->curr(fr.file()),
                                    fr.name(), fr.from(), fr.to() - 1);
      _fields.add(rf, campo);
      
      const TFieldtypes tipo = rf->type();       
      if (tipo == _intfld || tipo == _longfld || tipo == _realfld)
      {
        byte c = sheet().column_type(campo);
        if (c == ' ') 
          sheet().align_column(campo, TRUE);
      }  

      _key_cols.set(campo, in_key(fr));
    }
  }  
}

HIDDEN bool _can_post = FALSE;

KEY TCursor_sheet::run()
{
  _cursor->freeze(TRUE);

  select(_cursor->pos());
  _can_post = TRUE;
  
  const KEY k = TSheet::run();
  _cursor->freeze(FALSE);
  return k;
}

long TCursor_sheet::get_items() const
{ return _cursor ? _cursor->items() : 0; }

void TCursor_sheet::get_row(long row, TToken_string& l)
{
  *_cursor = (TRecnotype)row;
  l.cut(0);
  const int last = _fields.last();
  for (int i = 0; i <= last; i++)
  {
    const TRecfield* rf = (const TRecfield*)_fields.objptr(i);
    const char* s = rf ? (const char*)*rf : "";
    l.add(s);
  }
}

bool TCursor_sheet::get_cell_colors(int row, int col, COLOR& fore, COLOR& back) const
{
  if (_key_cols[col])
  {
    fore = 0;
    back = REQUIRED_BACK_COLOR; // Evidenzia i campi chiave
    return true;
  }
  return TSheet::get_cell_colors(row, col, fore, back);
}

///////////////////////////////////////////////////////////
// TBrowse_sheet
///////////////////////////////////////////////////////////

HIDDEN TBrowse_sheet* _cur_browse = NULL;

void TBrowse_sheet::add_custom_filter(const char* regexp)
{
  TString filter = _original_filter; // Costruisco il nuovo filtro per estensione del vecchio
  if (regexp && *regexp)
  {
    if (_original_filter.not_empty())
    {
      filter.insert("(");
      filter << ")&&(";
    }
    filter << regexp;
    if (_original_filter.not_empty())
      filter << ')';
  }

  TCursor& c = *cursor();
  if (filter != c.filter()) // Cambio il filtro se necessario :-)
  {
    c.freeze(false);
    c.setfilter(filter, true);
    c.items();            // Forzo la ricostruzione del cursore
    c.freeze(true);
    
    // Forzo un aggiornamento a basso livello
    TQuery_field& qf = (TQuery_field&)TMask::field(DLG_QUERY);
    XI_OBJ* obj = qf.sheet()->xi_object();
    xi_scroll_percent(obj, 100);
    xi_scroll_percent(obj, 0);

    _sel = 0;
  }
}

bool TBrowse_sheet::browse_field_handler(TMask_field& f, KEY k)
{
  long rec = -1;
  
  if (k == K_F2)
  {
    f.reset();
    k = K_SPACE;
  }
  
  if (k == K_SPACE)
  {                                           
    TEdit_field& e = _cur_browse->field();   // Campo padre dello sheet
    TMask& m = e.mask();                     // Maschera che contiene lo sheet
    TMask_field& c = m.field(f.dlg());       // Campo corrispondente sulla maschera
    
    // Ricopia su campo maschera
    if (f.is_edit())
    {
      TEdit_field& ef = (TEdit_field&)f;
      const TString& wd = ef.get_window_data();
      c.set(wd);
    }
    else
      c.set(f.get());                          
    
    TBrowse* b = e.browse();
    if (b != NULL)
    {
      b->do_input(FALSE);
      rec = b->cursor()->read(_isgteq);
    }  
  }

  if (rec >= 0 && rec != _cur_browse->selected())
  {
    _cur_browse->select(rec);        // Non mettere post_select
    _can_post = TRUE;
    
    RCT r; f.get_rect(r);
    xvt_dwin_invalidate_rect(f.parent(), &r);  // Non ricordo a cosa serva aggiornare il campo di testo
  }  
  return TRUE;
}

bool TBrowse_sheet::last_browse_field_handler(TMask_field& f, KEY k)
{
  const bool ok = browse_field_handler(f, k);
  if (ok) 
  {       
    if (k == K_TAB && _can_post)
    {
      _cur_browse->post_select(_cur_browse->selected());
      _can_post = FALSE;
    }
    else
    {
      if (k == K_CTRL+K_TAB)
      {
        RCT r; f.get_rect(r);
        xvt_dwin_invalidate_rect(f.parent(), &r);
      }  
    }
  }  
  
  return ok;  
}

bool TBrowse_sheet::filter_handler(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TString expr;                  // Espressione di filtro complessiva
    if (!f.get().empty())          // Filtro attivato!
    {
      const short id = f.dlg()-500;
      TString e = f.mask().get(id);  // Espressione regolare
      e.strip("\"'");                // Tolgo caratteri che potrebbero dare problemi  
      if (!e.blank())
      {
        const TBrowse& b = *_cur_browse->field().browse();
        TToken_string ids = b.get_input_fields();
        const int pos = ids.get_pos(id);
        if (pos >= 0)
        {
          TToken_string fns = b.get_input_field_names();
          expr << fns.get(pos) << "?=\"" << e << '"';
        }
      }
      if (expr.empty())
        f.reset();
    }
    _cur_browse->add_custom_filter(expr);
  }
  return true;
}

// @doc EXTERNAL

// @mfunc Gestisce l'handler della finestra
long TBrowse_sheet::handler(
  WINDOW win, // @parm Finestra da gestire
  EVENT* ep)  // @parm Evento da gestire nella finestra
{                 
  switch (ep->type)
  {
  case E_MOUSE_DOWN:
    switch (ep->v.mouse.button )
    {      
    case  1: 
      { 
        RCT r; sheet().get_rect(r);
        if (xvt_rect_has_point(&r, ep->v.mouse.where))
        {
          MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR);
          if (menu)
          {         
				    dictionary_translate_menu(menu);
            xvt_menu_popup(menu->child, win, ep->v.mouse.where, XVT_POPUP_LEFT_ALIGN, NULL);
            xvt_res_free_menu_tree(menu);       
            return 0L; // no default handling!
          }  
        }
      }
      break;
    default:
      {
        RCT r; sheet().get_rect(r);
        if (xvt_rect_has_point(&r, ep->v.mouse.where) && _rec_to_select >= 0)
        {
          post_select(_rec_to_select);
          _rec_to_select = -1;     
        }
      }  
      break;
    }  
    break;
  case E_COMMAND:
    switch (ep->v.cmd.tag-BROWSE_BAR)
    {     
    case 1:
      save_columns_order(field());
		  return 0L;
    case 2:
      set_columns_order(NULL);
      return 0L;
    case 3:
      fld(0).on_key(K_F11);
      return 0L;
    case 4:
      on_key(K_CTRL + 'E');
      return 0L;
    default:
      break;  
    }
    break;
  default:
    break;
  }
  
  return TCursor_sheet::handler(win, ep);
}

bool lst_handler(TMask_field& lst, KEY k)
{
  if (k == K_SPACE)
  {
    TMask& m = lst.mask();
    if (m.is_running())
    {
      const int sel = atoi(lst.get());
      m.on_key(K_CTRL + K_F1 + sel);
    }
  }
  return true;
}

void TBrowse_sheet::create_key_selector(TToken_string& ca)
{
  const int items = ca.items();
  if (items > 1)
  {
    TToken_string co;
    for (int i = 0; i < items; i++)
      co.add(i);

    TList_field& lst = add_list(69, 0, PR("Ordinamento per "), 1, 0, 16, "", co, ca);
    lst.set_handler(lst_handler);
  }
}

void TBrowse_sheet::update_key_selector(int sel)
{
  if (id2pos(69) >= 0) // Has been created?
    set(69, sel, 0);   // Don't fire any events!
}

TBrowse_sheet::TBrowse_sheet(TCursor* cursor, const char* fields,
                             const char* title, const char* head, byte buttons, 
                             TEdit_field& f, TToken_string& sibling, CONTROL_HANDLER custom_filter_handler)
             : TCursor_sheet(cursor, fields, title, head, buttons, 
                             f.browse() ? f.browse()->input_fields()+1 : 1), 
               _field(f), _sel(0), _original_filter(cursor->filter()),
							 _custom_filter_handler(custom_filter_handler)

{
  const bool normal = f.browse() != NULL && sibling.full();

  TToken_string ca;   // Tag buttons
  if (normal)         // normal mask query
  {
    int n = 0;
    for (const char* s = sibling.get(0); s && *s; s = sibling.get(), n++)
    { 
      const short id = f.atodlg(s);
      const char* pr = sibling.get();
      if (id == f.dlg())
        _sel = n;
      ca.add(pr);
    }                       
  }
  else                // ABnormal application query: F9 or F8 handler!
  {
    ca = f.prompt();
    if (ca.blank())
      ca = TR("Codice");
  }
  create_key_selector(ca);
  
  TToken_string tids = head;
  TToken_string tfns = fields;                                       
  TToken_string ids, fns;   
  if (f.browse() != NULL)
  {
    ids = f.browse()->get_input_fields();   
    fns = f.browse()->get_input_field_names();  
  }
  
  TEditable_field* e = NULL;
  int y = 1;

  bool first = true;

  FOR_EACH_TOKEN(ids, i)
  {                                     
    if (*i != '\0' && *i != '"' && strchr(i, '@') == NULL)
    {
      const short id = f.atodlg(i);
      const TMask_field& c = f.mask().field(id);
      if (c.is_editable() && c.active())
      { 
        int pos = ids.get_pos(id);
        CHECK(pos >= 0, "Invalid input field");
        TString80 p = fns.get(pos);
        pos = tfns.get_pos(p);
        if (pos >= 0)
        {
          p = tids.get(pos);
          pos = p.find('@');
          if (pos >= 0) 
            p.cut(pos);
        }  
        else 
          p.cut(0);
      
        if (p.empty())
        {
          p = c.prompt();
          int a; 
          // Toglie spazi e simboli iniziali dal prompt
          for (a = 0; p[a] && !isalnum(p[a]); a++);
          p.ltrim(a); 
        }  
        p.left_just(16);
        
        TString8 flags;
        
				if (c.roman())           flags << 'M';
        if (c.right_justified()) flags << 'R';
        if (c.uppercase())       flags << 'U';
        if (c.zerofilled())      flags << 'Z';
        if (!normal)             flags << 'D'; // Disable ABnormal fields
        
        const int csize = c.size();
        
        switch (c.class_id())
        {
        case CLASS_EDIT_FIELD:
          {
            const int sz = csize > 50 ? 50 : csize; // Dimensione del campo di ricerca
            e = &add_string(c.dlg(), 0, p, 1, y++, csize, flags, sz);
            // Aggiunge campo con le icone di filtraggio
						add_checkbutton(c.dlg()+500, 0, "", sz+p.len()+ 2, y-1, 2, 1, "", 10112, 10113).set_handler(_custom_filter_handler != NULL ? _custom_filter_handler : filter_handler);         
          }
          break;
        case CLASS_ZOOM_FIELD:
          {
            e = &add_string(c.dlg(), 0, p, 1, y++, 32000, flags, 50);
            // Aggiunge campo con le icone di filtraggio
            add_checkbutton(c.dlg()+500, 0, "", 52 + p.len(), y-1, 2, 1, "", 10112, 10113).set_handler(_custom_filter_handler != NULL ? _custom_filter_handler : filter_handler);         
          }
          break;
        case CLASS_REAL_FIELD:  
          e = &add_number(c.dlg(), 0, p, 1, y++, csize, flags);
          break;
        case CLASS_DATE_FIELD:  
          e = &add_date(c.dlg(), 0, p, 1, y++, flags);
          break;
        default:
          e = NULL;
          break;
        }    
        if (e != NULL && normal)
        {
          e->set_handler(browse_field_handler);
          e->set(c.get());
          if (e->dlg() == f.dlg() || first)
					{
            first_focus(e->dlg());
						first = false;
					}
        }
      }
    }    
  }     
  
  if (e != NULL && normal)
    e->set_handler(last_browse_field_handler);   

  // carica ordine colonne anche per ricerche costruite run-time
  load_columns_order(f);
}

bool TBrowse_sheet::on_key(KEY k)
{
  if (k >= K_CTRL+K_F1 && k < K_CTRL+K_F10)
  {            
    const int what = k - K_CTRL - K_F1;
    if (what >= 0 && what != _sel)
      TWindow::stop_run(k);
    return true;  
  }
  return TCursor_sheet::on_key(k);
}

KEY TBrowse_sheet::run()
{       
  _cur_browse = this;
  update_key_selector(_sel);
  const KEY key = TCursor_sheet::run();
  _cur_browse = NULL;
  return key;
}