#include <applicat.h>
#include <automask.h>   
#include <config.h>
#include <golem.h>
#include <printer.h>
#include <printwin.h>
#include <urldefid.h>
#include <utility.h>
#include <viswin.h>

#include <agasys.h>

#include <bagn001a.h> 
#include <bagn003.h> 

HIDDEN TPrinter* _printer = NULL;

///////////////////////////////////////////////////////////
// Maschera impostazione stampante (fatta come si deve dopo 10 anni!)
///////////////////////////////////////////////////////////

class TPrinter_setup_mask : public TAutomask
{
private:
  bool _skip_events;

  TString _pdev, _font;
  PRINT_RCD* _pcd;
  int _pcd_size;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

  void fill_font_list();
  void fill_size_list();

public:
  void set_print_rcd(PRINT_RCD* pcd, int size);
  PRINT_RCD* get_print_rcd(int& size) const;

  TPrinter_setup_mask();
  virtual ~TPrinter_setup_mask();
};

void TPrinter_setup_mask::set_print_rcd(PRINT_RCD* pcd, int size)
{
  _pcd = pcd;
  _pcd_size = size;
  xvt_print_get_name(pcd, _pdev.get_buffer(), _pdev.size());
}

PRINT_RCD* TPrinter_setup_mask::get_print_rcd(int& size) const
{
  size = _pcd_size;
  return _pcd;
}

void TPrinter_setup_mask::fill_font_list()
{
  const int MAX_FAMILIES = 256;
  char* family[MAX_FAMILIES];
  const int num_families = (int)xvt_fmap_get_families(_pcd, family, MAX_FAMILIES);

  TToken_string fn(num_families * 16);  
  for (int i = 0; i < num_families; i++)
  {
    fn.add(family[i]);
    xvt_mem_free(family[i]);
  }  
  
  TString oldfont = _font; // Memorizzo il font corrente in quanto poi cambia

  TList_field& lst = (TList_field&)field(MSK_FONT);
  lst.replace_items(fn, fn);  

  // Controlla se il font c'e' ancora
  if (fn.get_pos(oldfont) < 0)
    oldfont = fn.get(0);
  set(MSK_FONT, oldfont, 0x1);
}

void TPrinter_setup_mask::fill_size_list()
{
  const int MAXSIZES = 128;
  long sizes[MAXSIZES]; 
  BOOLEAN scalable;

  const int num_sizes = (int)xvt_fmap_get_family_sizes(_pcd, _font.get_buffer(), sizes, &scalable, MAXSIZES);

  TToken_string pn1;
  if (scalable)
  {
    for (int i = 4; i <= 32; i++)
      pn1.add(i);
  }  
  else                           
  {
    if (num_sizes > 0)
    {
      for (int i = 0; i < num_sizes; i++)
        pn1.add(sizes[i]);
    }
    else 
      pn1.add(printer().get_char_size()); // semper better than nothing
  }

  TList_field& lst = (TList_field&)field(MSK_SIZE);
  const TString8 old_size = lst.get();
  lst.replace_items(pn1, pn1);
  lst.set(old_size);
  lst.enable(pn1.items() > 1);
}

bool TPrinter_setup_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  if (_skip_events)
    return TRUE;

  switch (o.dlg())
  {
  case MSK_PRINTERS:
    if (fe_modify)
    {             
      const TString& pdev = o.get();          // Nome stampante corrente
      if (pdev != _pdev)
      {
        TWait_cursor hourglass;
        if (_pcd != NULL) 
          xvt_print_destroy(_pcd);
        _pcd = xvt_print_create_by_name(&_pcd_size, pdev);
        _pdev = pdev;
        fill_font_list();
      }
    }
    break;
  case MSK_FONT:
    if (e == fe_modify)
    {
      TWait_cursor hourglass;
      const TString& family = o.get();
      if (_font != family)
      {
        _font = family;
        fill_size_list();
      }
    }   
    break;
  case DLG_SETPRINT:
    if (e == fe_button)
    {
      if (xvt_dm_post_page_setup(_pcd))
      {                         
        // see if user has changed printer
        // determine name of currently selected printer 
        TString name;
        xvt_print_get_name(_pcd, name.get_buffer(), name.size());
        set(MSK_PRINTERS, name, 0x1);    
      }
    }
    break;
  default:
    break;
  }
  return TRUE;
}

TPrinter_setup_mask::TPrinter_setup_mask() 
                   : TAutomask("bagn001a")
{
  TPrinter& pr = printer();
  set(MSK_FILENAME, pr.get_printerfile());
  
  const bool can_save = pr._config == "Printer";
  enable(DLG_OK, can_save);

  set_print_rcd(pr._print_rcd, pr._print_rcd_size);

  // Crea la lista delle stampanti
  TToken_string pn2; 
  SLIST plist = xvt_print_list_devices();             
  for (SLIST_ELT pitem = xvt_slist_get_first(plist);
       pitem != NULL; pitem = xvt_slist_get_next(plist, pitem)) 
  {
    const char* pname = xvt_slist_get(plist, pitem, NULL);
    pn2.add(pname);
  }
  xvt_slist_destroy(plist);

  _skip_events = TRUE;
  
  if (pr._printertype == fileprinter)
    set (MSK_TYPE, "1");
  else if (pr._printertype == screenvis)
    set(MSK_TYPE, "2");
  else
    set(MSK_TYPE, "0");

  TList_field& plst = (TList_field&)field (MSK_PRINTERS);
  plst.replace_items(pn2, pn2);                // Genera printer list
  set(MSK_PRINTERS, pr._prname);               
  
  _font = pr._fontname;
  fill_font_list();
  fill_size_list();

  set(MSK_FONT, _font);       // Fare solo quando la lista e' piena
  set(MSK_SIZE, pr._ch_size); // Fare solo quando la lista e' piena
  set(MSK_LINES, pr._lines_per_inch);
  set(MSK_ISGRAPHICS, pr.isgraphics() ? "X" : "");

  _skip_events = FALSE;
}

TPrinter_setup_mask::~TPrinter_setup_mask()
{
}

///////////////////////////////////////////////////////////
// TPrinter
///////////////////////////////////////////////////////////

TPrinter& printer()
{
  if (_printer == NULL)
    _printer = new TPrinter;
  return *_printer;
}

void printer_destroy()
{
  if (_printer != NULL)
  {
    delete _printer;
    _printer = NULL;
  }  
}

// ----------------------------------------------------------------------
// TPrint_intersector
// ----------------------------------------------------------------------

// TPrint_intersector: calcola intersezioni tra elementi grafici e 
// restituisce, riga per riga, i necessari caratteri di fincatura per
// finculare in modo carattere
// usata da viswin e printwin
class TPrint_intersector : public TString_array
{
    const char* _fink;    

    char check_intersection(int x, int y, char ch);
    void h_line(int x1, int y1, int len);
    void v_line(int x1, int y1, int len);

    // caratteri fincazione: l'ho fatto perche' e' inline                                              
    char f_topleft()      const { return _fink[0];  }                                              
    char f_topmiddle()    const { return _fink[1];  }                                              
    char f_topright()     const { return _fink[2];  }                                              
    char f_botleft()      const { return _fink[3];  }                                              
    char f_botmiddle()    const { return _fink[4];  }                                              
    char f_botright()     const { return _fink[5];  }                                              
    char f_centerleft()   const { return _fink[6];  }                                              
    char f_centermiddle() const { return _fink[7];  }                                              
    char f_centerright()  const { return _fink[8];  }                                              
    char f_horizontal()   const { return _fink[9];  }                                              
    char f_vertical()     const { return _fink[10]; }                                              

  public:
                                                                 
    // aggiunge un elemento grafico                                                           
    void add(TGraphic_shape s, int x1, int y1, int x2, int y2);
    // aggiunge alla stringa passata i necessari caratteri, leggendo
    // dalla pagina interna
    const char* get_chars(int line) const;  
    // sbianca tutto
    void clear();
    
    TPrint_intersector(const char* fink, int pagesize) : TString_array(pagesize) , _fink(fink)
    {}
    virtual ~TPrint_intersector() {}
};

char TPrint_intersector::check_intersection(int x, int y, char ch)
{ 
  char a = ' ', b = ' ', c = ' ', d = ' ';
  if (y > 0 && objptr(y-1) != NULL)
    b = row(y-1)[x];
  if (objptr(y+1) != NULL)
    d = row(y+1)[x];
  if (x > 0) a = row(y)[x-1];
  if (x < row(y).len()-1) c = row(y)[x+1]; 
  
  if (a == ' ' && b == ' ' && c != ' ' && d != ' ')
    ch = f_topleft();
  else if (a == ' ' && b != ' ' && c != ' ' && d == ' ')
    ch = f_botleft();
  else if (a != ' ' && b != ' ' && c == ' ' && d == ' ')
    ch = f_botright();
  else if (a != ' ' && b == ' ' && c == ' ' && d != ' ')
    ch = f_topright();
  else if (a != ' ' && b != ' ' && c == ' ' && d != ' ')
    ch = f_centerright();
  else if (a == ' ' && b != ' ' && c != ' ' && d != ' ')
    ch = f_centerleft();
  else if (a != ' ' && b != ' ' && c != ' ' && d == ' ')
    ch = f_botmiddle();
  else if (a != ' ' && b == ' ' && c != ' ' && d != ' ')
    ch = f_topmiddle();
  else if ((a != ' ' && b != ' ' && c != ' ' && d != ' ') ||
           ((a != ' ' && b == ' ' && c != ' ' && d == ' ') && ch == f_vertical()) ||
           ((a == ' ' && b != ' ' && c == ' ' && d != ' ') && ch == f_horizontal()))
    ch = f_centermiddle();
     
  return ch;
}

void TPrint_intersector::h_line(int x1, int y1, int len)
{          
  if (objptr(y1) == NULL) 
  {
    TString* ss = new TString(256);
    ss->spaces();
    TArray::add(ss, y1);
  }       
  
  TString& s = row(y1);
  int i;
  
  for (i = x1; i < x1+len; i++)
    s[i] = f_horizontal();
  for (i = x1; i < x1+len; i++)
    s[i] = check_intersection(i, y1, f_horizontal());
}

void TPrint_intersector::v_line(int x1, int y1, int len)
{  
	int i;
  for (i = y1; i < y1+len; i++)
  {
    if (objptr(i) == NULL) 
    {
      TString* ss = new TString(256);
      ss->spaces();
      TArray::add(ss, i);
    }
    row(i)[x1] = f_vertical();
  }
  for (i = y1; i < y1+len; i++)
    row(i)[x1] = check_intersection(x1, i, f_vertical());
}

void TPrint_intersector::add(TGraphic_shape s, int x1, int y1, int x2, int y2)
{     
  // rows start at 0
  y1 --;  y2 --;
  // columns pure, ma e' possibile che siano gia' 0 se la generazione
  // e' stata automatica (colonna 1. campo - 1)
  if (x1 > 0) x1 --;
  if (x2 > 0) x2 --;

  switch (s)
  {
    case line:   
      if (x1 == x2)       // vertical
        v_line(x1, y1, y2-y1+1);
      else if (y1 == y2)  // horizontal
        h_line(x1,y1, x2-x1+1);
      else error_box("Linee oblique non supportate in modalita' testo");
      break;
    case box: 
      h_line(x1, y1, x2-x1+1);
      h_line(x1, y2, x2-x1+1);
      v_line(x1, y1, y2-y1+1);
      v_line(x2, y1, y2-y1+1);
      break;
    default:
      break;
  }
}

const char* TPrint_intersector::get_chars(int line) const
{
   if (objptr(line) == NULL)
     return "";
   else
     return row(line);
}

void TPrint_intersector::clear()
{  
  for (int i = 0; i < items(); i++)
  {
    TString* s = (TString*)objptr(i);
    if (s != NULL)
      s->spaces();
  }
}

///////////////////////////////////////////////////////////
// TPrint_txt_info
///////////////////////////////////////////////////////////

struct TPrint_txt_info
{
  TTextfile*   _txt;
  word _copies;
  word _pagefrom;
  word _pageto;
  word _lastpage;

  bool edit();
  TPrint_txt_info(TTextfile& txt);
};

bool TPrint_txt_info::edit()
{
  TPrinter& p = printer();
  TMask msk("bagn003");
  msk.set(F_PRINTER, p.printername());
  msk.set(F_FORM, p.get_form_name());
  msk.set(F_FONT, p.fontname());
  msk.set(F_SIZE, p.get_char_size());
  msk.set(F_ISGRAPHICS, p.isgraphics() ? "X" : "");
  msk.set(F_FROMPAGE, _pagefrom);
  msk.set(F_TOPAGE, _lastpage);
  msk.set(F_COPIES, _copies);
  const bool ok = msk.run() == K_ENTER;
  if (ok)
  {
    _copies = msk.get_int(F_COPIES);
    _pagefrom = msk.get_int(F_FROMPAGE);
    _pageto = msk.get_int(F_TOPAGE);
    if (_pageto < _pagefrom || _pageto >= _lastpage)
      _pageto = 0;
  }
  return ok;
}

TPrint_txt_info::TPrint_txt_info(TTextfile& txt) 
               : _txt(&txt), _copies(1), _pagefrom(1), _pageto(0) 
{ 
  const word ps = printer().formlen(); 
  const long li = txt.lines();
  _lastpage = (li+ps-1) / ps;
}

// @cmember Ritorna vero se la stampante e' generica/solo testo
bool TPrinter::is_generic() const
{ 
  bool yes = _dots_per_line == 1;
  if (!yes)
    yes = _prname.find("Generic") >= 0;
  return yes; 
}

// @cmember Ritorna vero se sono attivati gli elementi grafici
bool TPrinter::isgraphics() const 
{ 
  return _isgraphics; 
}

// @cmember Attivati gli elementi grafici se possibile
bool TPrinter::set_graphics(bool g) 
{ 
  _isgraphics = g && !is_generic(); 
  return _isgraphics;
}

BOOLEAN TPrinter::start_print(long data)
{
  const TPrint_txt_info *pd = (TPrint_txt_info *) data;
  TTextfile& txt = *(pd->_txt);
  TPrinter& stampante = printer();
   
  const int vofs = stampante.get_line_offset(); 
  const int hofs = stampante.get_column_offset();
  
  if (stampante.is_generic() && (vofs != 0 || hofs != 0))
  {                  
    TTextfile new_txt;                   
    TString s(256);
    const long last_row = txt.lines();
    long out_row = 0;
    
    for (long row = (vofs < 0 ? -vofs : 0); row < last_row; row++)    
    {                          
       const int pagelen = stampante.formlen();
       const int page_row = (int) (out_row % pagelen);
       
       if (vofs > 0 && page_row == 0)
       {     
         const int page = (int) (out_row / pagelen);

         for (int i = vofs; i > 0; i--)
         {
           out_row++;
           if (page > 0)
             row++;
           new_txt.append("");
         }
       }               
       if (vofs < 0 && page_row == pagelen + vofs)
       {
         for (int i = -vofs; i > 0; i--)
         {
           out_row++;                     
           row++;
           new_txt.append("");
         }
       }
       s = txt.line(row);
       if (hofs < 0)
         s.ltrim(-hofs);
       else
         if(hofs > 0)
           s.lpad(s.len() + hofs);
       new_txt.append(s);
       out_row++;
    }
    TPrintwin pw(new_txt);
    pw.do_print(pd->_pagefrom, pd->_pageto, pd->_copies);
    return pw.aborted();
  }                 
  else
  {
    TPrintwin pw(txt);
    if (pw.win() != NULL_WIN)
      pw.do_print(pd->_pagefrom, pd->_pageto, pd->_copies);
    return pw.aborted();
  }
}

// utils del caz
HIDDEN int read_int (const char *s, int &n, int &cnt)
{
  char nbuf[16];
  
  while (!isdigit (s[cnt]))
    cnt++;

	int j;
  for (j = 0; isdigit (s[cnt]); j++)
    nbuf[j] = s[cnt++];
  nbuf[j] = '\0';

  return n = atoi (nbuf);
}

void TPrinter::parse_background(const char* bg_desc, TString_array& background)
{
  TString_array pix;
  char op, ch;
  int x1, x2, y1, y2;
  int id, cnt = 0; 
  
  TToken_string tt;
  TFilename bmp;

  if (!_fink_mode && _finker == NULL)
    _finker = new TPrint_intersector(_fink, _formlen);
  else if (_finker)
    _finker->clear();

  while ((ch = bg_desc[cnt++]) != '\0')
  {
    op = ch;
    tt = "";
    char bf[2];
    bf[1] = '\0';
    switch (op)
    {
    case ' ':               
    case '\t':
    case '\n':
      continue;             // ignore whitespace
      break;
    case 'i':
      cnt++;
      for (x1 = 0; bg_desc[cnt] != ','; x1++)
        bmp[x1] = bg_desc[cnt++];
      bmp[x1] = '\0';
      id = _image_names.find(bmp);
      if (id < 0) id = _image_names.add(bmp);
      read_int(bg_desc, x1, cnt); if (x1 <= 0) x1 = 1;
      read_int(bg_desc, y1, cnt); if (y1 <= 0) y1 = 1;
      read_int(bg_desc, x2, cnt); if (x2 <= 0) x2 = formwidth();
      read_int(bg_desc, y2, cnt); if (y2 <= 0) y2 = formlen();
      cnt++;
      if (_isgraphics)
      {
        tt << op;
        tt.add(id);
        tt.add(x1);
        tt.add(y1);
        tt.add(x2);  
        tt.add(y2); 
      } 
      break;  
    case 'l':               // line

    case 'b':               // box

    case 'r':               // round box
      cnt++;
      read_int (bg_desc, x1, cnt);
      read_int (bg_desc, y1, cnt);
      read_int (bg_desc, x2, cnt);
      read_int (bg_desc, y2, cnt);
      cnt++;                // skip separator
      if (_isgraphics && _fink_mode) 
      {
        tt << op;
        tt.add (x1 - 1);
        tt.add (y1 - 1);
        tt.add (x2 - 1);
        tt.add (y2 - 1); 
      }
      else
      {
        TGraphic_shape s = op == 'b' ? box : line;
        _finker->add(s, x1, y1, x2, y2);
      }
      break;
    case 't':               // text
      cnt++;
      read_int (bg_desc, x1, cnt);
      read_int (bg_desc, y1, cnt);
      cnt++;
      tt << op;
      tt.add (x1-1);
      tt.add (y1-1);
      tt << '|';
      while ((ch = bg_desc[cnt++]) != '}')
        tt << ch;
      break;
      
    case 'P':           // set pen style

    case 'B':           // set brush

    case 'W':           // set line width 

    case 'C':           // set pen color

      tt << op;
      bf[0] = bg_desc[cnt++];
      tt.add (bf);
      break;
    default:
      yesnofatal_box ("Unknown background opcode: %c", op);
      break;
    }
    pix.add (tt);
  }
                               
  // now build row descriptions

  // colors are listed in printapp:

  char curcol = 'n';
  char curpen = 'n';
  char curpat = 'n';
  char curwid = '1';

  for (int l = 0; l < _formlen; l++)
  {
    if (background.objptr(l) == NULL)            // Se la riga non esiste creala
    {                                        
      TToken_string* r = new TToken_string(15);           
      if (curcol != 'n') *r << 'C' << curcol;    // Setta valori se diversi da default
      if (curpat != 'n') *r << 'B' << curpat;
      if (curwid != '1') *r << 'W' << curwid;
      if (curpen != 'n') *r << 'P' << curcol;
      background.add(r, l);
    }
    
    TString& rwd = background.row(l);
    for (int j = 0; j < pix.items(); j++)
    {
      TToken_string& tt = pix.row(j);

      // la stringa contiene l'opcode piu' i parametri in binario, 
      // incrementati di 1 per evitare lo 0

      switch (tt.get_char(0))
      {
      case 'b':
        x1 = tt.get_int (1) + 1;
        y1 = tt.get_int (2) + 1;
        x2 = tt.get_int (3) + 1;
        y2 = tt.get_int (4) + 1;
        if (y1 == l + 1)        // at ze biginnin
        {
          rwd << 'u' << char (x1);
          rwd << 'r' << char (x1) << char (x2);
          rwd << 'u' << char (x2);
        }
        else if (y2 == l + 1)   // at ze end
        {
          rwd << 'o' << char (x1);
          rwd << 'r' << char (x1) << char (x2);
          rwd << 'o' << char (x2);
        }
        else if (y1 < l + 1 && y2 > l + 1)      // in ze middol
        {
          rwd << 'v' << char (x1);
          rwd << 'v' << char (x2);
        }
        break;
      case 'l':
        x1 = tt.get_int (1) + 1;
        y1 = tt.get_int (2) + 1;
        x2 = tt.get_int (3) + 1;
        y2 = tt.get_int (4) + 1;
        if (y1 == y2 && y1 == l + 1)    // orizzontale 
        {
          rwd << 'h' << char (x1) << char (x2);
        }
        else if (y1 <= l + 1 && y2 >= l + 1)    // verticale  

        {
          rwd << 'v' << char (x1);
        }
        break;
      case 'r':
        x1 = tt.get_int (1) + 1;
        y1 = tt.get_int (2) + 1;
        x2 = tt.get_int (3) + 1;
        y2 = tt.get_int (4) + 1;
        if (y1 == y2)    // orizzontale 
        {                          
          if (y1 == l+1)
            rwd << 'r' << char (x1) << char (x2);
        }
        else 
        {                  
          const int l1 = l+1;
          if (l1 >= y1 && l1 <= y2)    // verticale  
          {                       
            char code = 'v';
            if (y1 == l1) code = 'u'; else 
            if (y2 == l1) code = 'o';
            rwd << code << char(x1);
          }  
        }
        break;
      case 't':
        x1 = tt.get_int (1) + 1;
        y1 = tt.get_int (2) + 1;        // al gh'e'
        if (y1 == l + 1)
        {}
        break; 
      case 'i':
        id = tt.get_int();
        x1 = tt.get_int();
        y1 = tt.get_int();
        x2 = tt.get_int();
        y2 = tt.get_int();
        if (l+1 >= y1 && l+1 <= y2)                                    
          rwd << 'i' << char(id+1) << char(l-y1+2) << char(x1) 
            << char(x2-x1+1) << char(y2-y1+1);
        break;  
      case 'W':
        curwid = *(tt.get (1));
        rwd << 'W' << curwid;
        break;
      case 'P':
        curpen = *(tt.get (1));
        rwd << 'P' << curpen;
        break;
      case 'B':
        curpat = *(tt.get (1));
        rwd << 'B' << curpat;
        break;
      case 'C':
        curcol = *(tt.get (1));
        rwd << 'C' << curcol;
        break;
      default:
        break;  
      }
    }
  }
}

void TPrinter::setbackground(const char* b, int index)
{                       
  CHECK(index >= 0 && index <= 3, "Bad background index");
  _backgrounds.destroy(index);
  if (b && *b)
  {                                               
    TString_array* bg = new TString_array(formlen());
    _backgrounds.add(bg, index);
    parse_background(b, *bg);
  }  
}

TString_array& TPrinter::getbgdesc(word page) const
{
  int index = 0;
  if (page == 0 && _backgrounds.objptr(3)) 
    index = 3; 
  if (page == 1 && _backgrounds.objptr(2)) 
    index = 2; 
  if (index == 0 && (page & 0x1) == 0 && _backgrounds.objptr(1))    
    index = 1;
    
  TString_array* bg = (TString_array*)_backgrounds.objptr(index);
  if (bg == NULL)
  {
    bg = new TString_array(formlen());
    ((TPrinter*)this)->_backgrounds.add(bg, index);  // keep const
  }  
   
  return *bg;
}

//bool printers_on_key(TMask_field & f, KEY key);

////////// TPRINTROW //////////

TPrintrow::TPrintrow()
{
  reset();
}

TPrintrow::TPrintrow(const TPrintrow& pr)
{         
  reset();
  _row = pr.row ();
  memcpy (_attr, pr._attr, MAX_PR_WIDTH);
  memcpy (_cols, pr._cols, MAX_PR_WIDTH);
  _tab = pr._tab;
  if (pr._images)
    _images = new TToken_string(*pr._images);
  
  _currentstyle = pr._currentstyle;
  _currentcolor = pr._currentcolor;
  _lastpos = pr._lastpos;
}

TPrintrow::~TPrintrow()
{
  if (_images)
    delete _images;
}

TObject *TPrintrow::dup () const
{
  return new TPrintrow (*this);
}

const char *TPrintrow::class_name () const
{
  return "Printrow";
}

word TPrintrow::class_id()
  const
{
  return CLASS_PRINTROW;
}

TPrintrow& TPrintrow::reset()
{
  _row.spaces (sizeof(_attr));                       // Azzera testo

  memset(_attr, normalstyle, sizeof (_attr));        // Azzera stile
  
  _tab.reset();                                       // Azzera tabulazioni
  _images = NULL;
  _currentcolor = 'w';
  _currentcolor <<= 8;
  _currentcolor += 'n';
  for (int i = 0; i < MAX_PR_WIDTH; i++)
    _cols[i] = _currentcolor;                         // Azzera colori
  
  _lastpos = 0;
  _currentstyle = normalstyle;
  return *this;
}

const char* TPrintrow::row_codified() const
{
  char last_attr = -1;
  int last_color = -1;
  int k = 0, i = 0, len = 0;     

  const int kmax = 1024;
  char* tmp = get_tmp_string(kmax).get_buffer();
  
  // Calcolo lunghezza stringa
  for (i = _row.size()-1; i >= 0; i--)
  {
    if (_row[i] != ' ') 
    {
      len = i+1;
      break;
    }  
  }  

  for (i = 0; i < len; i++)
  {
    if (_tab[i])
    {
      tmp[k++] = '@'; 
      tmp[k++] = 't';
    }  
    
    if (_attr[i] != last_attr)
    {
      tmp[k++] = '@';
      switch (_attr[i])
      {
      case normalstyle:
        tmp[k++] = 'r';
        break;
      case boldstyle:
        tmp[k++] = 'b';
        break;
      case italicstyle:
        tmp[k++] = 'i';
        break;
      case underlinedstyle:
        tmp[k++] = 'u';
        break;
      }
      last_attr = _attr[i];
    }
    if (_cols[i] != last_color)
    {
      tmp[k++] = '$';
      tmp[k++] = '[';
      tmp[k++] = (char) (_cols[i] & 0x00ff);
      tmp[k++] = ',';
      tmp[k++] = (char) (_cols[i] >> 8);
      tmp[k++] = ']';
      last_color = _cols[i];
    }   
    if (_row[i] == '@' && strchr("<#>", _row[i+1]) == NULL)
      tmp[k++] = '@';  //  Escape for @
    tmp[k++] = _row[i];
  }
  CHECKD(k < kmax, "Internal buffer overflow ", k);
  tmp[k] = '\0';
  
  if (_images)
  {   
    strcat(tmp, "$[w,w]");   // Bianco su bianco!
    FOR_EACH_TOKEN((*_images), tok)
      strcat(tmp, tok);
  }
  
  return tmp;
}

TPrintrow& TPrintrow::put(const char *str, int position, int len)
{
  if (len <= 0)
    len = strlen (str);
  
  if (position < 0)
    position = _lastpos;
  else
    _tab.set(position);  

  char bg = 'w', fg = 'n';
  for (int i = 0; i < len; i++)
  {
    char c = str[i];
    if (c == '$' && str[i + 1] == '[')
    {
      ++i;
      fg = str[++i];
      c = str[++i];
      if (c == ']')
        bg = _currentcolor >> 8;
      else if (c == ',')
      {
        bg = str[++i];
        i++;
      }
      else
      {
        CHECK (0, "Error in color specification");
      }
      _currentcolor = (bg << 8) + fg;
    }
    else if (c == '@')
    {
      c = str[++i];
      switch (toupper (c))
      {
      case '@':  // Escape for @
        _row[position] = '@';
        _attr[position] = _currentstyle;
        _cols[position++] = _currentcolor;
        break; 
      case '#':
      case '<':
      case '>':
        // printer MUST handle them
      {
        const int n = (c == '#') ? 5 : ((c == '>') ? 10 : 8);
        _row[position] = '@';
        _attr[position] = _currentstyle;  
        _cols[position++] = _currentcolor;
        for (int j = 1; j < n; j++)
        {
          _row[position] = c;
          _attr[position] = _currentstyle;
          _cols[position++] = _currentcolor;
        }
      }
      break; 
      case 'T':
        _tab.set(position);
        break;
      case 'B':
        _currentstyle = boldstyle;
        break;
      case 'I':
        _currentstyle = italicstyle;
        break;
      case 'U':
        _currentstyle = underlinedstyle;
        break;
      case 'R':
        _currentstyle = normalstyle;
        break;
      case 'F':
        {                
          const int f = i;
          for ( ; str[i] && str[i] != ']'; i++);
          TString name = &str[f+2];
          name.cut(i-f-2);
          name.insert("i{"); name << '}';
          if (_images == NULL) _images = new TToken_string;
          _images->add(name);
        }
        break;
      default:
        // should be number followed by skip or jump
        if (isdigit (c))
        {
          // read number
          char digbuf[8];
          int cnt = 0;
          digbuf[cnt++] = c;
          while (isdigit (c = str[++i]))
            digbuf[cnt++] = c;
          digbuf[cnt] = '\0';
          int pp = atoi (digbuf);
          if (toupper (c) == 'G')
          {
            if (pp >= MAX_PR_WIDTH)
              fatal_box ("printrow reaches position %d", pp);
            if (pp > position)
              for (int k = position; k < pp; k++)
              {
                _attr[k] = _currentstyle;
                _cols[k] = _currentcolor;
              }
            position = pp;
            _tab.set(position);
          }
          else if (toupper (c) == 'J')
          {
            if (pp + position >= MAX_PR_WIDTH)
              fatal_box ("printrow reaches position %d", pp + position);
            for (int k = 0; k < pp; k++)
            {
              _attr[k + position] = _currentstyle;
              _cols[k + position] = _currentcolor;
            }
            position += pp;
            _tab.set(position);
          }
        }
        else
        {
          _row[position] = c;
          _attr[position] = _currentstyle;
          _cols[position++] = _currentcolor;
        }
      }                   // switch
    }
    else
    {
      _row[position] = c;
      _attr[position] = _currentstyle;
      _cols[position++] = _currentcolor;
    }
  }                             // for

  _lastpos = position;
  return *this;
}

////////// TPRINTER //////////

void TPrinter::set_printrcd()
{
  if (_print_rcd != NULL)
  {
    xvt_print_destroy(_print_rcd);
    _print_rcd = NULL;
    _print_rcd_size = 0;
  }

  bool ok = false;
  if (_prname.not_empty())
  {
    _print_rcd = xvt_print_create_by_name(&_print_rcd_size, _prname);
    ok = xvt_print_is_valid(_print_rcd) != 0;
  }
  if (!ok)
  {
    _print_rcd = xvt_print_create(&_print_rcd_size);
    xvt_print_get_name(_print_rcd, _prname.get_buffer(), _prname.size());
    ok = xvt_print_is_valid(_print_rcd) != 0;
  }
  if (!ok)
    error_box(FR("Errore di inizializzazione della stampante %s"), (const char*)_prname);
}

PRINT_RCD* TPrinter::get_printrcd(int *size)
{
  if (_print_rcd == NULL)
    set_printrcd();
  if (size != NULL) 
    *size = _print_rcd_size;  
  return _print_rcd;  
}

// @doc EXTERNAL

// @mfunc Setta le caratteristiche della stampante leggendole da <p _print_rcd>
void TPrinter::init_formlen(
  WINDOW prwin) // @parm Finestra effettiva di stampa (default NULL_WIN)

  // @comm Nel caso <p prwin> sia NULL_WIN vengono solamente aggiornati i valori
{
  long pw, ph, phr, pvr; // Printer width, height, horizontal and vertical resolution
	
  xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, get_printrcd(), &ph, &pw, &pvr, &phr);
  
  if (pvr > 0)
  {
    _formlen       = int(ph * _lines_per_inch / pvr);   // Total number of lines per page
    _dots_per_line = int(pvr / _lines_per_inch);        // Number of point per line
    _vert_offset   = (int)(ph - ((long)_formlen * _dots_per_line)) >> 1;

    if (prwin != NULL_WIN)
    {
      // Compute maximum number of chars per line
      int mincol = 0,maxcol=MAX_PR_WIDTH;
      TString256 spc; spc.fill('m', maxcol);
        
      _formwidth = maxcol;
      int  w = 0;
      while (mincol < maxcol )
      {
        w = xvt_dwin_get_text_width(prwin, (char*)(const char*)spc, _formwidth);
        if (w < pw)
          mincol = _formwidth+1;
        else
          maxcol=_formwidth-1;
        _formwidth=(mincol+maxcol)/2;
      }     

      _horz_offset = (_formwidth > 80) ? (int)(pw - w)/2 : 0;         
      if (_horz_offset < 0)
        _horz_offset = 0;
    }
    else 
    {
      _formwidth = int (pw * (_ch_size * 10 / 12) / phr);
      _horz_offset = 0;
    }                      
  }
}

TPrinter::TPrinter()
: _vf(NULL), _ch_size (12), _date (TODAY), _multiple_link (FALSE),
  _isgraphics (TRUE), _frozen (FALSE), _print_rcd(NULL), _lines_per_inch (6),
  _l_offset(0), _c_offset(0), _export_header(FALSE), _export_header_len(0),
  _appendfile(FALSE)

{
  _footerhandler = _headerhandler = NULL;
  _linkhandler = NULL;

  _formlen = 66;
  _formwidth = 0;

  _frompage = 0;
  _topage = 0xffff;
  _hwformfeed = FALSE;
  _currentpage = 1;
  _currentrow = 1;
  _fp = NULL;
  _headersize = 0;
  _footersize = 0;
  _isopen = FALSE;
  
//  xvt_print_get_default_device(_defPrinter.get_buffer(), _defPrinter.size());
  
	// read configuration file
  read_configuration ();

  init_formlen ();
  
  set_fincatura("+++++++++-|");                

  set_fink_mode(TRUE);
  _finker = _isgraphics && _fink_mode ? NULL : new TPrint_intersector(_fink, _formlen);
}

// @doc EXTERNAL

// @mfunc Legge la configurazione della stampante
void TPrinter::read_configuration(
  const char* parag)    // parm Nome del file di configurazione della stampante (default NULL)

  // @comm Se <p parag> e' NULL viene letta la configurazione della stamapnte di default.
{                  
  TWait_cursor hourglass;

  _config = parag;                                            // Inizializza nome configurazione
  if (_config.empty())                                        // Se non specificata ...
    _config = "Printer";                                      // ... usa configurazione standard

  TConfig* iniptr = NULL;                         
  if (_config != "Printer")                                   // Cerca configurazione speciale
  {                                                           
    iniptr = new TConfig(CONFIG_STAMPE, _config);
    const int what = iniptr->get_int("Type", NULL, -1, -1);
    if (what < 0)                                             // Se configurazione annullata ...
    { 
      delete iniptr; iniptr = NULL;                            
    }
  }
  if (iniptr == NULL)
    iniptr = new TConfig(CONFIG_GUI, "Printer");

  const int what = iniptr->get_int("Type", NULL, -1, 4);      // Tipo stampante
  _prname = iniptr->get("Name", NULL, -1, _defPrinter);       // Nome stampante corrente
  _printerfile = iniptr->get("File", NULL, -1, "");           // File di stampa                
  set_fontname(iniptr->get("Font", NULL, -1, XVT_FFN_FIXED));   // Nome del font
  _ch_size = iniptr->get_int("Size", NULL, -1, 10);           // Dimensione del font
  _lines_per_inch = iniptr->get_int("Lines", NULL, -1, 6);    // Linee per pollice
  set_graphics(iniptr->get_bool("Graphic", NULL, -1, TRUE));  // Grafica attiva

	bool read_rcd = FALSE;
	
	const TString& host = iniptr->get("Host");
	if (host.not_empty())
	{
    char hostname[256];
	  xvt_sys_get_host_name(hostname, sizeof(hostname));
    read_rcd = (host == hostname);  // Safe to read
		if (!read_rcd)
    {
			read_rcd = yesno_box(FR("Attenzione: le impostazioni della stampante sono relative alla stazione di lavoro %s.\n"
                              "Si desidera utilizzarle ugualmente sulla stazione %s?"), (const char*)host, hostname);
      if (!read_rcd)
      {
        // Forza la lettura parametri della stampante di default
        _prname.cut(0);
        set_printrcd(); 
      }
    }
	}
  
  if (read_rcd)
  {              
	  if (_print_rcd != NULL)
      xvt_print_destroy(_print_rcd);
    _print_rcd = xvt_print_create_by_name(&_print_rcd_size, _prname);

    if (xvt_print_is_valid(_print_rcd) && iniptr->exist("rcd", 0))
		{
      TToken_string s(256);         
      int i = 0;
			for (int index = 0; ; index++)
			{
				s = iniptr->get("rcd", NULL, index);
				if (s.not_empty())
				{
          byte* rcd = (byte*)_print_rcd;
					for (const char* n = s.get(0); n != NULL && i < _print_rcd_size; n = s.get())
						rcd[i++] = (byte)atoi(n);
				}
				else
					break;    
			}
		}

    if (xvt_print_is_valid(_print_rcd))
		{
      init_formlen();
      xvt_print_get_name(_print_rcd, _prname.get_buffer(), _prname.size());
		}
		else
    {
      error_box("Attenzione: la stampante corrente non e' valida.\n"
                "Si prega di selezionare e registrare una nuova stampante.");
      set_printrcd();
    }
  }           
  
  delete iniptr; iniptr = NULL;
  
  if (_printerfile.empty())
  {
    _printerfile.tempdir();
    _printerfile << SLASH;
  }                                                 
  
  if (_config == "Printer" || _printertype == winprinter)
  {
    switch (what)
    {
    case  1: _printertype = fileprinter; break;
    case  2: _printertype = screenvis; break;
    case  3: _printertype = exportprinter; break;
    default: _printertype = winprinter; break;
    }                     
  }
}


void TPrinter::save_configuration()
{ 
  TWait_cursor hourglass;

  CHECK(_config.not_empty(), "Invalid printer config");
  TConfig prini(_config == "Printer" ? CONFIG_GUI : CONFIG_STAMPE, _config);
  
  prini.set("Type", _printertype);              // Tipo stampante
  prini.set("Name", _prname);                   // Nome stampante corrente
  prini.set("File", _printerfile);              // File di stampa                
  prini.set("Font", _fontname);                 // Nome del font
  prini.set("Size", _ch_size);                  // Dimensione del font
  prini.set("Lines", _lines_per_inch);          // Linee per pollice
  prini.set("Graphic", _isgraphics ? "X" : ""); // Grafica attiva

  char hostname[256];
	xvt_sys_get_host_name(hostname, sizeof(hostname));
	prini.set("Host", hostname);
	prini.set("User", user());
  
	int n = 0, index = 0;       
  TToken_string val(256);             
  
  int rcdsize; 
  byte* rcd = (byte*)get_printrcd(&rcdsize);
  
  for (int i = 0; i < rcdsize; i++)
  {             
    val.add((int)rcd[i]);
    n++;
    if (n == 24)
    {
      prini.set("rcd", val, NULL, TRUE, index++);
      val.cut(n = 0);
    }
  }
  if (n > 0)
    prini.set("rcd", val, NULL, TRUE, index);
  
  for (index++; prini.remove("rcd", index); index++);
}

TPrinter::~TPrinter ()
{
  if (_print_rcd != NULL)
  {
    xvt_print_destroy(_print_rcd);
    _print_rcd = NULL;
  }                                                     
//  xvt_print_set_default_device(_defPrinter);
}

const char* TPrinter::class_name() const
{
  return "Printer";
}

word TPrinter::class_id() const
{
  return CLASS_PRINTER;
}

TPrintrow *TPrinter::getheaderline(int linetoget)
{
  return ((TPrintrow *)_header.objptr (linetoget));
}

TPrintrow *TPrinter::getfooterline(int linetoget)
{
  return ((TPrintrow *)_footer.objptr(linetoget));
}

// @doc EXTERNAL

// @mfunc Setta il contenuto di una line dell'header
void TPrinter::setheaderline (
  int linetoset,        // @parm Numero della linea da settare
  TPrintrow * line)     // @parm Contenuto della linea dell'header
  // @parm const TPrintrow& | line | Indirizzo con il contenuto della 
  //     linea dell'header

  // @syntax void setheaderline (int linetoset, TPrintrow* line);
  // @syntax void setheaderline (int linetoset, const TPrintrow& line);

{
  _header.add (line, linetoset);
  if (linetoset >= _headersize)
    _headersize = linetoset + 1;
}

void TPrinter::setheaderline (int linetoset, const TPrintrow & line)
{
  TPrintrow *p = new TPrintrow (line);
  setheaderline (linetoset, p);
}

// @doc EXTERNAL

// @mfunc Setta il contenuto di una line dell'header
void TPrinter::setfooterline (
  int linetoset,        // @parm Numero della linea da settare
  TPrintrow * line)     // @parm Contenuto della linea del footer
  // @parm const TPrintrow& | line | Indirizzo con il contenuto della 
  //     linea del footer

  // @syntax void setfooterline (int linetoset, TPrintrow* line);
  // @syntax void setfooterline (int linetoset, const TPrintrow& line);

{
  _footer.add (line, linetoset);
}

void TPrinter::setfooterline (int linetoset, const TPrintrow& line)
{
  TPrintrow *p = new TPrintrow (line);
  setfooterline (linetoset, p);
}

void TPrinter::resetheader ()
{
  _header.destroy ();
  _headersize = 0;
}

void TPrinter::resetfooter ()
{
  _footer.destroy ();
  // _footersize = 0;
}

// @doc EXTERNAL

// @mfunc Metodo base per la stampa
//
// @rdesc Ritorna il risulato della stampa:
//
// @flag TRUE | Se la stampa ha avuto successo
// @flag FALSE | Se la stampante non e' attiva
bool TPrinter::printrow(
  TPrintrow* rowtoprint) // @parm Riga da stampare

  // @comm Se la pagina logica corrente e' precedente alla prima pagina logica o successiva
  //     all'ultima pagina logica viene ritornato TRUE.
{
  if (!isopen ())
    return FALSE;

  if (_currentpage < _frompage || _currentpage > _topage)
    return TRUE;

  TString rw (rowtoprint == NULL ? "" : ((_printertype == screenvis || _printertype == winprinter || 
                                         _printertype == exportprinter) ? 
                                         rowtoprint->row_codified () :
                                         rowtoprint->row ()));
  rw.rtrim();

  int lun = rw.len ();
  int idx;
  
  if (_printertype != exportprinter)
  {
    for (idx = 0; idx < lun; idx++)
    {
      if (rw[idx] == '@') // gestione data e n. di pagina      
      {
        switch (rw[idx + 1])
        {
        case '#':
          rw.overwrite (format("%-5u", _currentpage), idx++);
          break;
        case '>':
          rw.overwrite (_date.string(full), idx++);
          break;
        case '<':
          rw.overwrite (_date.string(brief), idx++);
          break;
        default:
          break;
        }
      }
    }
  }

  if (_printertype == screenvis)
  {
    if (!_vf->frozen ())
      _vf->add_line(rw);
    else
      _frozen = TRUE;
  }
  else
  {
    // add line to txt
    if (!_frozen)
      _txt.append(rw);
  }

  return true;
}

word TPrinter::rows_left() const
{
  word left = _formlen - _currentrow - _footersize + 1;
  if (_currentrow < 2)
    left -= _headersize;
  return left;
}

// @doc EXTERNAL

// @mfunc Permette di stampare una riga
//
// @rdesc Ritorna il risultato della stampa:
//
// @flag TRUE | Se la stampa ha avuto successo
// @flag FALSE | Se non e' riuscito ad effettuare la stampa
bool TPrinter::print(
  TPrintrow& rowtoprint)        // @parm Riga da stampare

  // @comm Nel caso la riga non ci stia nella pagina logica corrente vengono stampanti il footer
  //     della pagina corrente e l'header della successiva prima prima della stampa della riga
  //     vera e propria.
{
  bool ok = TRUE;

  if (!(_printertype == exportprinter && !_export_header))
  {
    if (_currentrow > _formlen - _footersize)
      ok = printfooter ();

    if (ok && _currentrow == 1)
      ok = printheader();
  }
  if (ok)
  {
    ok = printrow(&rowtoprint);
    _currentrow++;
  }

  return ok;
}

bool TPrinter::printheader()
{
  if (_headerhandler)
    _headerhandler(*this);

  bool ok = TRUE;
  int i;
  
  for (i = 0; i < _headersize && ok; i++)
    ok = printrow(getheaderline(i));

//  _currentrow = _headersize + 1;
  _currentrow = i+1;
  
  return ok;
}

bool TPrinter::printfooter()
{
  if (_footerhandler)
    _footerhandler (*this);

  bool ok = TRUE;
  for (int i = 0; i < _footersize && ok; i++)
    ok = printrow(getfooterline(i));

  _currentrow = 1;
  _currentpage++;

  return ok;
}

// @doc EXTERNAL

// @mfunc Permette di settare il tipo di fincatura
void TPrinter::set_fink_mode(
  bool f)      // @parm Indica il tipo di fincatura:
  //
  // @flag TRUE | Fincatura di tipo grafico
  // @flag FALSE | Fincatura di tipo testo

  // @comm Viene prima controllato che la stampante supporti la modalita' grafica,
  //       in tal caso, se viene impostato il flag a TRUE e la stampante e' generica
  //       oppure non ha il flag di stampa elementi grafici attivato, la fincatura sara'
  //       in modo testo.
{
  _fink_mode = f && isgraphics();
}

// @doc EXTERNAL

// @mfunc Permette di saltare alcune righe dalla posizione corrente
//
// @rdesc Ritorna il risulato dell'operazione:
//
// @flag TRUE | Se e' riuscito a saltare le righe
// @flag FALSE | Se non e' riuscito a saltare le righe
bool TPrinter::skip(
  int linestoskip)      // @parm Vengono accettati solo valori positivi

  // @xref <mf TPrinter::jump>
{
  CHECK (linestoskip >= 0, "Linestoskip can't be negative");
  int jumpline = _currentrow + linestoskip;
  return jump(jumpline);
}

// @doc EXTERNAL

// @mfunc Permette di saltare alla riga indicata
//
// @rdesc Ritorna il risultato dell'operazione
//
// @flag TRUE | Se e' riuscito a saltare alla riga
// @flag FALSE | Se non e' riuscito a saltare alla riga o se viene inserito un formfeed
bool TPrinter::jump(
  int jumpline) // @parm Numero della riga a cui saltare nella stampa. Vengono accettai
  //     solo valori positivi

  // @comm Nel caso si cerchi di saltare ad una riga superiore alla lunghezza della pagina attiva
  //     viene inserito un formfeed.

  // @xref <mf TPrinter::skip>
{
  int i = 0;
  bool ok = TRUE;

  CHECK (jumpline >= 0, "Jumpline can't be negative");
  if (jumpline > _formlen - _footersize)
    ok = formfeed();
  else
    for (i = _currentrow; i < jumpline; i++)
      if (!printrow())
        ok = FALSE;
  if (jumpline > _formlen - _footersize)
    ok = FALSE;
  return ok;
}

bool TPrinter::formfeed()
{
  const int lastrow = _formlen - _footersize;
  for (; _currentrow + _export_header_len <= lastrow; _currentrow++)
    printrow();
  return printfooter();
}

void TPrinter::reset()
{
  resetheader();
  resetfooter();
  _currentpage = 1;
  _currentrow  = 1;
}

bool TPrinter::open()
{
//  xvt_print_set_default_device(_prname);

  if (_printertype == screenvis)
  { 
    CHECK(_vf == NULL, "Print preview already open");
    _vf = new TViswin (NULL, "Anteprima di stampa", TRUE, TRUE,
                       _linksdescr.items () > 0);
    _vf->open_modal ();
  }
  else 
  {
    // prepare text object for new text
    _txt.destroy();
    _txt.interactive(FALSE);
  }  

  _currentrow = 1;
  _currentpage = 1;

  return _isopen = TRUE;
}

bool TPrinter::set()
{
  main_app().disable_menu_item(M_FILE_PG_SETUP);

  const TString oldprn = _prname;  
	int old_rcd_size;
  PRINT_RCD* rcd = get_printrcd(&old_rcd_size);
  TString oldrcd(old_rcd_size);
  memcpy(oldrcd.get_buffer(), rcd, old_rcd_size);
                  
  TPrinter_setup_mask mask;
  const KEY k = mask.run();
  _print_rcd = mask.get_print_rcd(_print_rcd_size);

  if (k == K_ESC)
  {
    xvt_print_destroy(_print_rcd);
    _print_rcd = xvt_print_create_by_name(&_print_rcd_size, oldprn);
    memcpy(_print_rcd, oldrcd, _print_rcd_size);
  }
  else
  {
    _prname = mask.get(MSK_PRINTERS);

    switch (atoi (mask.get (MSK_TYPE)))
    {
    case 1:                       // file
      _printertype = fileprinter;
      _printerfile = mask.get (MSK_FILENAME);
      break;
    case 2:                       // video
      _printertype = screenvis; break;
    default:                      // stampante
      _printertype = winprinter; break;
    }

    set_fontname(mask.get(MSK_FONT));
  
    const int cs = mask.get_int(MSK_SIZE);
    if (cs > 4) _ch_size = cs;

    _lines_per_inch = mask.get_int (MSK_LINES);
    _isgraphics = mask.get_bool (MSK_ISGRAPHICS);
    init_formlen ();

    if (k == K_INS)
      save_configuration();
  }

  main_app().enable_menu_item (M_FILE_PG_SETUP);
  return TRUE;
}


// @doc EXTERNAL

// @mfunc Inserisce un file di export fatto da un'altra printer 
void TPrinter::merge_export_file(
  const char* file, 
  bool header)  // @parm Indica se gli header sono presenti nel file:
  //
  // @flag TRUE | Gli header sono nel file e quindi non vengono stampanti (default)
  // @flag FALSE | Gli header non sono nel file e quindi vengono stampanti

  // @comm Vengono inseriti nel file di export i formati e tutto il resto. Vengono ignorati gli
  //     header supponendo che siano gia' presenti nel file
{
  TTextfile txt(file); 
  
  for (long i = 0; i < txt.lines(); i++)
  {
    TPrintrow* p = new TPrintrow();  
    p->put(txt.line_formatted(i));
    if (header) printrow(p);  
    else        print(*p);   
  }              
}

// @doc EXTERNAL

// @mfunc Crea un segnalibro
//
// @rdesc Ritorna l'identificatore del segnalibro creato
int TPrinter::set_bookmark(
  const char* txt,      // @parm Nome del segnalibro da creare
  int father)           // @parm Identificatore del segnalibro padre a cui collegare quello 
  //     da creare
{                                                     
  if (_printertype == screenvis)
  {
    BkDef* bkd = new BkDef;
    
    bkd->_row       = (_currentrow + ((_currentpage - 1)*_formlen)) - 1;
    bkd->_father_id = father;
    bkd->_id        = _bookmarks.items() + 1;
    bkd->_txt       = txt;
    
    _bookmarks.add(bkd);
    
    return _bookmarks.items();
  }
  return 0;
}


void TPrinter::print_txt(TTextfile& txt)
{
  if (txt.lines() > 0)
  {
    TPrint_txt_info what(txt);
    if (what.edit())
    {
      xvt_print_open();
      xvt_print_start_thread(start_print, long(&what));
      xvt_print_close();
    }
  }  
}


void TPrinter::close ()
{
  if (isopen() && // la stampante era aperta e 
      _currentrow >1 && // .. ho stampato qualcosa ...
        _footersize>0 ) // ... c'� footer da stampare
    formfeed();

  if (_fp)
  {
    fclose (_fp);
    _fp = NULL;
  }

  if (_printertype == screenvis)
  {
    _vf->close_print();
    const KEY key = _vf->run ();
    if (_vf->is_open ()) _vf->close_modal ();  
    _bookmarks.destroy();   
    
    if (key == K_CTRL+'S')
    {
      _isopen = FALSE;
      _currentrow = _currentpage = 1;
      print_txt(_vf->text());
    }

    delete _vf; _vf = NULL;
  }
  else if (_printertype == exportprinter)
  {                          
    if (_exportfile.not_empty() && _txt.lines() > 0L)
    {
      ofstream txt(_exportfile); 
      for (long i = 0; i < _txt.lines(); i++)
        txt << _txt.line_formatted(i) << '\n';
      txt.close();
    }
  }
  else if (_printertype == winprinter && _txt.lines() > 0L)
  {     
    print_txt(_txt);
  }
  else if (_printertype == fileprinter)
  {
    FILE* fp = fopen(_printerfile, _appendfile ? "a" : "w"); 
    if (fp == NULL)
    {
      error_box(FR("Impossibile aprire il file %s"), (const char*)_printerfile); 
      return; 
    }
    for (long i = 0; i < _txt.lines(); i++)
      fprintf(fp,"%s\n", _txt.line(i));
    fclose(fp);
    message_box(FR("Stampa su file terminata. Nome archivio: %s"),(const char *)_printerfile);
  }
  
  if (_finker) 
  {
    delete _finker;
    _finker = NULL;
  }
  
  // Dealloca sfondi ormai inutili
  _backgrounds.destroy();
  freeze (FALSE);             
  _isopen = FALSE;
}

//  
// TFile_printer
// 

#include <bagn004.h>

const long disk_sizes[] = { 360000L, 1200000L, 720000L, 1400000L, 2880000L };

TFile_printer::TFile_printer (const char *ffile, const char *label, int len_rec, int num_rec_inizio, int num_rec_fine, int tipo_disco)
{
  set_printtype (fileprinter);

  _num_rec_testa_coda = num_rec_inizio + num_rec_fine;
  _file = ffile;
  _label = label;
  _len_rec = len_rec;
  _volume = 1;
  _size = disk_sizes[tipo_disco];
  _num_rec_volume = int ((_size / len_rec) - _num_rec_testa_coda);
  _nome_file_fissato = TRUE;
  _label_fissata = TRUE;
}

void TFile_printer::open()
{
}

void TFile_printer::close ()
{
}

bool TFile_printer::genera_dischetti ()
{
  int r;

  for (int i = 0; i < _tmp_files.items (); i++)
  {
    // Avvisa l'utente di inserire un dischetto
    r = yesno_box ("Inserire il dischetto n. %2d di %2d nel drive %2s",
                   i + 1, _volume, _drive);
    if (!r)
      return error_box ("Procedura interrotta!");

    // copia il file sul dischetto
    fcopy ((const char *) &_tmp_files[i], (const char *) _drive);
  }
  return TRUE;
}

TFile_printer::~TFile_printer ()
{
}

bool TFile_printer::set ()
{
  TMask m ("bagn004");
  int f;

  //
  // 
  // --------------------------------------------------------------------
  // Qui bisogna inserire la lista dei drive disponibili nella maschera
  // Per ora tale lista e' fissa e contiene solo A: e B:
  // --------------------------------------------------------------------
  // 
  // 

  m.set (F_FILE_DESTINAZIONE, _file);
  m.set (F_LABEL, _label);

  if (_nome_file_fissato)
    m.disable (F_FILE_DESTINAZIONE);

  if (_label_fissata)
    m.disable (F_LABEL);

  const bool ok = m.run () == K_ENTER;

  if (ok)
  {
    f = atoi(m.get(F_FORMATO_DISCO));
    _drive = m.get(F_DRIVE);
    _file = m.get(F_FILE_DESTINAZIONE);
    _label = m.get(F_LABEL);
    _size = disk_sizes[f];
    _num_rec_volume = int ((_size / _len_rec) - _num_rec_testa_coda);
  } 
  
  return ok;
}
 
const char* TPrinter::background_chars(int l) const 
{ 
  return _finker == NULL ? "" : _finker->get_chars(l); 
}

///////////////////////////////////////////////////////////
// Calcolo dimensione font
///////////////////////////////////////////////////////////

struct font_data
{ 
  TString _name;
  int     _size;
  int     _columns;
};

HIDDEN BOOLEAN calc_font_callback(long data)
{
  font_data& fd = *(font_data*)data;
  
  // Create print window
  WINDOW win = xvt_print_create_win(printer().get_printrcd(), "Calcolo font");
  
  if (win != NULL_WIN)
  {
    long pw, ph, phr, pvr; // Printer width, height, horizontal and vertical resolution
    xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, printer().get_printrcd(), &ph, &pw, &pvr, &phr);
  
    TString test(fd._columns);
    test.fill('M', fd._columns);
    int size;
    
    for (size = fd._size; size > 0; size--)
    {
      // Set print font
      xvt_set_font(win, fd._name, XVT_FS_NONE, size);

      const int w = xvt_dwin_get_text_width(win, (char*)(const char*)test, fd._columns);
      if (w <= pw)
        break;
    }
    
    if (size > 0)
      fd._size = size;  
      
    xvt_vobj_destroy(win);
  } 
  
  return win != NULL_WIN;
}

int TPrinter::calc_font_size(int columns) const
{ 
  font_data fd;
  fd._name = fontname();
  fd._size = get_char_size();           
  fd._columns = columns;
  
  xvt_print_open();
  xvt_print_start_thread(calc_font_callback, (long)&fd);
  xvt_print_close();
  
  return fd._size;
}

HIDDEN BOOLEAN calc_cols_callback(long data)
{
  int &numcols=*(int *)data;
  
  // Create print window
  WINDOW win = xvt_print_create_win(printer().get_printrcd(), "Calcolo numero colonne");
  
  if (win != NULL_WIN)
  {
    long pw, ph, phr, pvr; // Printer width, height, horizontal and vertical resolution
    xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, printer().get_printrcd(), &ph, &pw, &pvr, &phr);
    xvt_set_font(win, printer().fontname(), XVT_FS_NONE, printer().get_char_size());

    // Compute maximum number of chars per line
    int mincol = 0,maxcol=MAX_PR_WIDTH;
    TString spc(maxcol,'M' );  
      
    numcols = maxcol;
    int w;
    while (mincol < maxcol )
    {
      w = xvt_dwin_get_text_width(win, (char*)(const char*)spc, numcols);

      if (w < pw)
        mincol = numcols+1;
      else
        maxcol=numcols-1;
      numcols=(mincol+maxcol)/2;
    }     
      
    xvt_vobj_destroy(win);
  } 
  
  return win != NULL_WIN;
}

// Funzione chiamata solo da sv1200, ma non si capisce perche' non usi la formwidth()
int TPrinter::calc_num_cols() const
{ 
  int numcols;
  xvt_print_open();
  xvt_print_start_thread(calc_cols_callback, (long)&numcols);
  xvt_print_close();
  
  return numcols;
}