#include <ctype.h>
#include <stdarg.h>

#ifndef __LONGDOUBLE__
#include <gm.h>
#endif

#include <progind.h>
#include <utility.h>
#include <tabutil.h>
#include <printapp.h>
#include <urldefid.h>

// @doc EXTERNAL

TLocalisamfile *fff;

const char* const printf_types = "dDiIuUoOxXfeEgGcCnNsSpPrRtTaA";

// _FieldTok flags

const word LONG_FLAG    = 0x0001;
const word PICTURE_FLAG = 0x0002;
const word PAD_FLAG     = 0x0004;
const word ALIGN_FLAG   = 0x0008;
const word TRANS_FLAG   = 0x0010;
const word DATE_FLAG    = 0x0020;
const word STRING_FLAG  = 0x0040;
const word NUMBER_FLAG  = 0x0080;
const word DEC_FLAG     = 0x0100;
const word FONT_FLAG    = 0x0200;
const word JUMP_FLAG    = 0x0400;
const word RECNO_FLAG   = 0x0800;
const word BOOLEAN_FLAG = 0x1000;
const word IGNORE_FILL  = 0x2000;

// =============================================================
// print token containers
// =============================================================

class _Transfield:public TObject
{
  friend class TPrint_application;
  TString _from,    // "from" value 
    _to,      // "to" value
    _fn;      // field name

  int _lognum;      // logical number

    public:
    _Transfield (int l, const char *fn, const char *from, const char *to):
    _from (3), _to (20), _fn (10)
  {
    _lognum = l;
    _from = from;
    _to = to;
    _fn = fn;
  }
};

class _Token:public TObject
{
  friend class TPrint_application;
  int _tag;
  int _row;
public:
  int tag ()
    {
      return _tag;
    }
  int row ()
    {
      return _row;
    }
  void tag (int t)
    {
      _tag = t;
    }
  void row (int r)
  {
    _row = r;
  }
  virtual ~ _Token () {}
};

class _PrintfTok : public _Token
// something to be printed (actually already printed on _val)
{
  friend class TPrint_application;
  TString _val;
    public:
    _PrintfTok (int rw, const char *val):_val (0)
  {
    tag (0);
    row (rw);
    _val = val;
  }
  virtual ~ _PrintfTok () {}
};

class _FieldTok : public _Token
// something more complex to be printed
{
  friend class TPrint_application;
  int _size, _dec;
  char _align;
  TString _fld;     // field description

  word _flags;      // all you need to know 

    public:
    _FieldTok (int rw, char *fld, word flags, char align = 'l',
         int size = -1, int dec = -1):
    _fld (20)
  {
    tag (1);
    row (rw);
    _fld = (const char *) fld;
    _flags = flags;
    _size = size, _align = align;
    _dec = dec;
    delete fld;
  }
  virtual ~ _FieldTok ()
  {
  }
};

class _PrintfRef : public _Token
// to be printed by reference via format
// must pass valid pointer to object 
{
  friend class TPrint_application;
  void *_what;
  char _type;
  TString _fmt;
    public:
    _PrintfRef (int rw, const char *fmt, char type, void *what):_fmt (1)
  {
    tag (2);
    row (rw);
    _type = type;
    _what = what;
    _fmt = fmt;
    _fmt[0] = '%';
  }
  virtual ~ _PrintfRef ()
  {
  }
};

void TPrint_application::_reset_tree(link_item * head)
{
  if (head)
  {
    if (head->_brother)
      _reset_tree (head->_brother);
    if (head->_son)
      _reset_tree (head->_son);
    delete head;
  }
}

// @mfunc Cerca per il nodo <p head> dove agganciarsi per rispettare la relazione
//
// @rdesc Ritorna il nodo a cui agganciarsi
link_item *TPrint_application::_look_print_node (
          link_item * head,   // @parm Nodo per cui effettuare la ricerca
          int logicnum)   // @parm Numero logico del file

// @comm E' necessario che non esistano piu' nodi per lo stesso file/alias, altrimenti la
//   la ricerca termina alla proma occorrenza incontrata
{
  link_item *s;
  while (head)
    {
      if (head->_logicnum == logicnum)
  return head;
      else if (head->_son)
  if ((s = _look_print_node (head->_son, logicnum)) != NULL)
    return s;
      head = head->_brother;
    }
  return NULL;
}

// @mfunc Setta un segnalibro nell'anteprima di stampa
//
// @rdesc Ritorna il numero identificatore il segnalibro creato
int TPrint_application::set_bookmark(
    const char* txt, // @parm Testo del segnalibro
    int father)      // @parm Identificatore del segnalibro padre (default -1)

// @comm L'aggiunta dei segnalibri causa la comparsa del menu 'Indice' nella viswin, 
//   con la lista dei segnalibri inseriti; questi possono essere legati ad albero n-ario 
//   specificando il bookmark padre, in tal caso il menu sara' gerarchico a sua volta.
//   <nl>Nella prossima release di XVT il menu indice sara' pop-up nella finestra

{ 
  return printer().set_bookmark(txt, father);
}

void TPrint_application::add_file (const char *tab, int from)
{
  add_file(TTable::name2log (tab), from);
}

// @mfunc Aggiunge un file del cursore nell'albero di stampa
void TPrint_application::add_file (
     int file,  // @parm Numero logico del file da aggiungere
     int from)  // @parm Posizione nell'albero di stampa nel quale aggiungere il file
    // @parm const char* | tab | Nome del del file da aggiungere

// @syntax add_file(int file, int from = 0);
// @syntax add_file(const char* tab, int from = 0);

{
  link_item *nw = new link_item (file);
  if (_pr_tree == NULL)
    {
      _pr_tree = nw;
      return;
    }
  
  if (from == 0)
    from = _pr_tree->_logicnum;
  
  link_item *fr = _look_print_node (_pr_tree, from);
  
  CHECKD (fr, "add_file: nonexistent node: logicnum = ", from);
  
  if (fr->_son)
    {
      fr = fr->_son;
      while (fr->_brother)
  fr = fr->_brother;
      fr->_brother = nw;
    }
  else
    fr->_son = nw;
}

// ---------------------------------------------------------------
// ------------ user function ------------------------------------

static char tb[120];

// @mfunc Permette di trovare un link ipertestuale
//
// @rdesc Ritorna l'ID del link ipertestuale trovato (-1 se non viene trovato)
int TPrint_application::find_link(
    const char* descr) const // @parm Testo del link da trovare

// @xref <mf TPrint_application::enable_link>

{
  const TArray& arr = printer().links();
  for (int i = 0; i < arr.items(); i++)
  {
    TToken_string& tt = (TToken_string&)arr[i];
    const TFixed_string d(tt.get(0));
    if (d == descr) 
      return i;
  }
  return -1;
}

// @mfunc Permette di abilitare determinati colori come indicatori di legame ipertestuale
//
// @rdesc Ritorna l'ID del link ipertestuale di cui sono stati abilitati i colori
int TPrint_application::enable_link(
    const char *descr,  // @parm Testo del link da abilitare
    char fg,    // @parm Colore del carattere
    char bg)    // @parm Colore dello sfondo (default 'w')

// @comm Quando si vogliono abilitare determinati colori come indicatori di legame ipertestuale,
//       si faccia enable_link nella create. L' ID ritornato viene passato a process_link
//       assieme al testo selezionato.

{     
  int lnk = find_link(descr);
  if (lnk < 0)
  {
    TToken_string *tt = new TToken_string(30);
    char b[2] = { '\0', '\0' };
    tt->add(descr);
    b[0] = fg;
    tt->add(b);
    b[0] = bg;
    tt->add(b);
    lnk = printer().links().add(tt);
  }

  return lnk;
}

void TPrint_application::disable_link (char fg, char bg)
{
  for (int i = 0; i < printer().links().items (); i++)
  {
    TToken_string & t = (TToken_string&)printer().links()[i];
    const char f = *(t.get(1));
    const char b = *(t.get());
    if (f == fg && b == bg)
    {
      printer().links().remove(i, TRUE);
      break;
    }
  }
}

// @mfunc Abilita/disabilita pił link per la <mf TPrint_application::enable_link>
void TPrint_application::set_multiple_link (
     bool on) // @parm Indica se effettuare il link con tutti gli elementi selezioanti della riga

// @comm Se si setta <p on> a TRUE anziche' la descrizione del testo selezionato
//   viene passata a <mf TPrint_application::enable_link> una tokenstring con tutti i
//    'bottoni' dello stesso colore presenti sulla riga.

{
  printer().setmultiplelink (on);
}

void TPrint_application::_pp_link (int id, const char *text)
{
  TPrint_application& prapp = (TPrint_application&)main_app();
  prapp.process_link(id, text); 
}

void TPrint_application::_pp_header (TPrinter &)
{
  TPrint_application& prapp = (TPrint_application&)main_app();
  prapp.preprocess_header();
  printer().resetheader();
  const int ii = prapp._header.last();
  // reset and add header/footer lines
  for (int i = 0; i <= ii; i++)
    if (prapp._header.objptr(i) != NULL)
      printer().setheaderline (i, new TPrintrow((TPrintrow &)prapp._header[i]));
}

void TPrint_application::_pp_footer (TPrinter &)
{
  TPrint_application& prapp = (TPrint_application&)main_app();

  prapp.preprocess_footer ();
  printer().resetfooter ();
  int ii = prapp._footer.last();
  for (int i = 0; i <= ii; i++)
    if (prapp._footer.objptr(i) != NULL)
      printer().setfooterline (i,
       new TPrintrow ((TPrintrow &) (prapp._footer)[i]));
}


// @mfunc Permette di stampare sullo sfondo e per variarne gli attributi
void TPrint_application::set_background (
     const char *bgdesc)  // @parm Stringa contente i codici per la stampa (vedi descrizione)


// @comm Occorre passare una stringa che contiene codici per stampare box, linee, bitmap
//   sullo sfondo, e per variarne gli attributi: se NULL lo azzera.
//   <nl>Il background e' stampato sia su <c TViswin> che su stampante, e riguarda
//   una PAGINA fisica e non le righe o le "pages" relative al cursore;
//       viene replicato ad ogni nuova pagina fisica a meno che non venga cancellato
//       o ridefinito in una <lt>qualcosa<gt>process<lt>qualcos'altro<gt>(). 
//        <nl><nl>CODICI BACKGROUND
//   <nl>Una stringa con n codici, opzionalmente separati da spazi o tab 
//       <nl>SETTINGS
//   @flag P<lt>n<gt> | Setta pen style (n = codice XVT)
//   @flag B<lt>n<gt> | Setta brush style (idem)
//   @flag W<lt>n<gt> | Altezza della linea in pixel
//   @flag C<lt>n<gt> | Colore della penna (codice colore solito)
// @comm <nl><nl> DRAWING COMMANDS
//     @flag i{string,x1,y1,x2,y2} | Disegna la bitmap <p string> (nome file) alle coordinate indicate
//       @flag l{x1,y1,x2,y2} | Linea da/a (la resa delle oblique dipende dalla stampante)
//   @flag b{x1,y1,x2,y2} | Box
//   @flag r{x1,y1,x2,y2} | Rounded box 
//   @flag t{text,x,y} | Testo <lt>text<gt> a <p x>, <p y>

{
  printer().setbackground (bgdesc);
}

const char* FLD (int lognum, const char *f, int from, int to)
{
  sprintf (tb, "%c|%d|%s|%d|%d", 'n', lognum, f, from, to);
  char *p = new char[strlen (tb) + 1];
  strcpy (p, tb);
  return p;
}

const char* FLD (int lognum, const char *f, const char *picture)
{
  sprintf (tb, "%c|%d|%s|%s", 'p', lognum, f, picture);
  char *p = new char[strlen (tb) + 1];
  strcpy (p, tb);
  return p;
}

const char* FLD (const char *tabname, const char *f, int from, int to)
{
  CHECKS (strlen (tabname) < 5, "Invalid table name", tabname);
  int lognum = TTable ::name2log (tabname);
  sprintf (tb, "%c|%d|%s|%d|%d", 'n', lognum, f, from, to);
  char *p = new char[strlen (tb) + 1];
  strcpy (p, tb);
  return p;
}

const char* FLD (const char *tabname, const char *f, const char *picture)
{
  CHECKS (strlen (tabname) < 5, "Invalid table name", tabname);
  int lognum = TTable ::name2log (tabname);
  sprintf (tb, "%c|%d|%s|%s", 'p', lognum, f, picture);
  char *p = new char[strlen (tb) + 1];
  strcpy (p, tb);
  return p;
}

TString& fill_str (TString & t, char f)
{
  for (int kk = t.len () - 1; kk >= 0; kk--)
    {
      if (t[kk] == ' ')
  t[kk] = f;
      else
  break;
    }
  for (kk = 0; kk < t.len (); kk++)
    {
      if (t[kk] == ' ')
  t[kk] = f;
      else
  break;
    }
  return t;
}

// ========================================================
// Printapp code at last
// ========================================================

//void TPrint_application::enable_link(const char* descr, char fg, char bg) 
// { 
// printer().setlinkdescr(descr);
// }

void TPrint_application::select_cursor (int c)
{
  if (c == -1)
    _cur = NULL;
  else
    _cur = (TCursor *) & _cursors[c];
}

TCursor* TPrint_application::get_cursor (int c)
{
  if (c == -1)
    return NULL;
  else
    return (TCursor *) & _cursors[c];
}

// @mfunc Aggiunge un cursore alla classe
//
// @rdesc Ritorna l'identificatore del cursore aggiunto
int TPrint_application::add_cursor (
    TCursor * c)  // @parm Cursore da aggiungere all'albero

// @comm Nel caso sia passato NULL a <p c> non viene utilizzato nessun file, ma la
//   <mf TPirnt_application::print_one> viene chaiamta e le iterazione sono valoutate da
//   pre_ e post_ process.

{
  if (c == NULL)
    return -1;
  _cursors.add (c);
  _cur = c;
  return _cursors.items () - 1;
}

void TPrint_application::reset_row (int r)
{
  r--;
  int tmp = _rows.items ();
  for (int j = 0; j < tmp; j++)
    {
      _Token *t = (_Token *) (_rows.objptr (j));
      if (t)
  {
    if (t->row () == r)
      _rows.add (NULL, j);
  }
    }
  _rows.pack ();
  if (_maxrow == r && _maxrow > 0)
    _maxrow--;
}

void TPrint_application::reset_print ()
{
  _rows.destroy ();
  _maxrow = 0;
  _print_defined = FALSE;
}

// @mfunc Permette di definire l'header della stampa
void TPrint_application::set_header (
     int r,     // @parm Numero della riga nella quale stampare l'header
     const char *fmt,   // @parm Testo dell'header da stampare
     ...)   // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>

{
  CHECK (r >= 1, "Header rows start at 1");
  va_list vl;
  va_start (vl, fmt);
  vsprintf (__tmp_string, fmt, vl);
  va_end (vl);

  TPrintrow *pp = (TPrintrow *)_header.objptr(r - 1);
  if (!pp)
  {
    pp = new TPrintrow;
    _header.add (pp, r - 1);
  }
  pp->put (__tmp_string);
}

// @mfunc Permette di definire il footer della stampa
void TPrint_application::set_footer (
     int r,     // @parm Numero della riga nella quale stampare il footer
     const char *fmt,   // @parm Testo del footer da stampare
     ...)   // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>

{
  CHECK (r >= 1, "Footer rows start at 1");
  va_list vl;
  va_start (vl, fmt);
  vsprintf (__tmp_string, fmt, vl);
  va_end (vl);
  TPrintrow *pp = (TPrintrow *) _footer.objptr (r - 1);
  if (pp == NULL)
  {
    pp = new TPrintrow;
    _footer.add (pp, r - 1);
  }
  pp->put (__tmp_string);
}

void TPrint_application::reset_header ()
{
  _header.destroy ();
  printer().resetheader ();
}

void TPrint_application::reset_footer ()
{
  _footer.destroy ();
  printer().resetfooter ();
}

void TPrint_application::fill_page (int from)
{
  from--;
  for (int i = (from == -1 ? _maxrow : from); i <= printer().formlen (); i++)
  {
    reset_row (i);
    set_row (i, "");
  }
}

void TPrint_application::merge_export_file(const char* file, bool header, bool direct)
{
  if (direct) printer().merge_export_file(file, header);
  else
  {                           
    set_row(_currow+1,"");
    TTextfile txt(file);  
    for (long i = 0l; i < txt.lines(); i++)
      set_row(_currow+(int)i+1 + (i == 0l ? 1 : 0), txt.line_formatted(i));  
  }
}

// @mfunc Permette di settare una riga di stampa
void TPrint_application::set_row (
     int r,     // @parm Numero della riga da settare
     const char *frmt,  // @parm Contenuto della riga da stampare
     ...)   // @parmvar Uno o piu' parametri corrispondenti ai codici in <p frmt>

// @comm COME SETTARE LE RIGHE DI STAMPA
//       <nl><nl>Questa funzione si usa come una printf per settare le righe di stampa
//       che vengono stampate da <mf TPrint_application::print>.
//       <nl>I codici per gli argomenti variabili sono di 3 tipi:
//       @flag @ | Si usa per stampare campi di database o informazioni di controllo
//                 posizione carrello e font
//                 ATTENZIONE: i codici di formato sono diversi da quelli di printf e
//                 sono elencati sotto. Per i campi di database occorre che il codice
//                 sia accoppiato ad una delle funzioni <f FLD> passata come argomento;
//                 questa provoca la stampa di campi della relazione corrente,
//                 posizionata come e' stato deciso nell'inizializzazione
//       @flag % | Si usa esattamente come in una printf con un plus: se il codice di 
//                 formato e' maiuscolo (es. S per stringa, D per intero) viene
//                 ignorato il carattere di riempimento eventualmente specificato
//                 con <mf TPrint_application::set_fillchar>. Cio' vale anche per i codici @ (vedi)
//                 <nl>E' possibile usare due codici aggiuntivi: r(R) e t(T). A questi
//                 va fatto seguire un PUNTATORE a <c real> o a <c TString>. Il formato 
//                 viene interpretato con le stesse regole di %t in dsprintf per real 
//                 (come %d o %f) e di %s per TString. Il puntatore NON
//                 viene memorizzato; per questo occorre il codice # (sotto).
//       @flag # | Si usa come % (stessi codici di printf) ma memorizza gli argomenti
//                 per riferimento: ovvero, ogni volta che la riga viene stampata
//                 viene stampato il contenuto in quel momento (che si puo' cambiare
//                 in una delle pre- o post- process). Cio' implica che:
//                 1) gli argomenti vanno passati per RIFERIMENTO (set_row(1,"#5d",&i))
//                 2) i puntatori devono rimanere validi e costanti tra la set_row e
//                    la fine della stampa
//                 Quindi, attenzione a %s con <c TString> ridimensionate; si possono
//                 usare solo se predimensionate alla dimensione massima, ma e' meglio
//                 usare char* o il codice apposito. I codici #r, #a e #t prendono puntatori a
//                 real, <c TParagraph_string> e a <c TString>, memorizzandoli. Non ci sono problemi con la resize.
//                 Comunque, il modo corretto di adoperare il codice # e'
//                 usarlo solo per stampare MEMBRI della classe derivata da TPrint_application
//
// @comm <nl><nl>CODICI DI CAMPO (da utilizzare con una delle funzione <f FLD>):
//       @flag @@ | Carattere @
//       @flag @[n[,{l<pipe>c<pipe>r}]s | STRING: n = pad, lcr = alignment
//       @flag @{[n[.d=0]]<pipe>[n[,{l<pipe>c<pipe>r}]]p}n | NUMBER: n = digits, d = decimals
//                                                           p = picture string (first matching arg)
//       @flag @[l]d | DATE: l = full year
//       @flag @f | BOOL: prints si/no
//       @flag @[n,{l<pipe>c<pipe>r}]t | Translated field (must set translation)
//          
// @comm Tutti questi codici possono essere usati anche maiuscoli, il che inibisce
//       l'uso del carattere di riempimento (set_fillchar) per uno specifico campo.
//       
//       <nl><nl>Per tutti i codici che riguardano la stampa di <c real> (@n, %r, #r)
//       se non vengono date ulteriori specifiche di formato viene usata
//       una picture che e' "" per default, ma puo' essere modificata con
//       <mf TPint_application::set_real_picture>.
//       <nl>Normalmente un real uguale a zero viene stampato come stringa vuota
//       a meno che non si specifichi TRUE nella <mf TPrint_application::set_print_zero>.
//       
//       <nl><nl>CODICI POSIZIONAMENTO E MOVIMENTO CARRELLO
//       @flag @<lt>n<gt>g | Vai a posizione n
//       @flag @<lt>n<gt>j | Salta di n posizioni (in orizzontale)
// @comm <nl><nl>CODICI STILE
//       @flag @b | Grassetto
//       @flag @i | Corsivo
//       @flag @u | Sottolineato
//       @flag @r | Ritorna allo stile normale
//       
// @comm <nl><nl>CODICI COLORE PER VISUALIZZAZIONE E COLLEGAMENTO
//       
//       <nl><nl>Se si vuole che in visualizzazione il testo sia colorato
//       si usa il codice $[]; tra le quadre si scrive il colore
//       di foreground, opzionalmente seguito da una virgola e dal
//       colore di background (bianco per default). I colori si 
//       specificano con un singolo carattere come segue:
//       @flag n | Nero
//       @flag g | Verde
//       @flag b | Blu
//       @flag c | Cyan
//       @flag y | Giallo
//       @flag v | Magenta
//       @flag m | Colore background maschere (azzurrotto)
//       @flag d | Grigio scuro
//       @flag l | Grigio chiaro
//       @flag k | Grigio normale
//       
// @comm <nl><nl>Se si fa <mf TPrint_application::enable_link> con un certo colore, tutto
//       cio che e' scritto in quel colore diventa selezionabile
//       e alla sua selezione (premendo 'Collega') si puo' associare
//       un'azione in <mf TPrint_application::process_link>. A quest'ultima viene passata
//       l'ID ritornata da enable_link() e il testo selezionato alla
//       pressione di Collega.  Vedere ba6 e stampare l'elenco (con 
//       Includi ditte abilitato) per un esempio.

{
  CHECK (r >= 1, "Print rows start at 1");

  r--;

  char digbuf[10];
  char strbuf[120];
  char fftt[120];
  char *fmt = fftt;
  char fill = _fillchar;

  word flags = 0;
  int size = 0, dec = 0, strind = 0;
  char ch, align = 'l';

  // let the poor programmer use format() at will
  strcpy (fmt, frmt);
  _print_defined = TRUE;

  _currow = r;
  if (_currow > _maxrow)
    _maxrow = _currow;

  va_list params;
  va_start (params, frmt);

  // parse format string
  while ((ch = *fmt++) != '\0')
  {
    if (ch == '@')
  {
    // check for pending string
    if (strind)
    {
      strbuf[strind] = '\0';
      _rows.add (new _PrintfTok (_currow, strbuf));
      strind = 0;
    }
    ch = *fmt++;
    if (isdigit (ch))
    {
      int i = 0;
      digbuf[i++] = ch;
        while (isdigit (ch = *fmt++))
        digbuf[i++] = ch;
      digbuf[i] = '\0';
      size = atoi (digbuf);
      flags |= PAD_FLAG;
      if (ch == '.')
    {
      // decimal size follows
      i = 0;
      digbuf[i++] = ch;
      while (isdigit (ch = *fmt++))
        digbuf[i] = ch;
      digbuf[i] = '\0';
      dec = atoi (digbuf);
      flags |= DEC_FLAG;
      // ch = *fmt++;
    }
      else if (ch == ',')
    {
      // aligment spec follows 
      align = (ch = *fmt++);
      CHECK (ch == 'l' || ch == 'r' || ch == 'c',
         "TPrint_application::set_row: invalid alignment spec");
      flags |= ALIGN_FLAG;
      ch = *fmt++;
    }
    }
    switch (ch)
    {
    // modifiers
    case 'l':
    case 'L':
      flags |= LONG_FLAG;
      ch = *fmt++;
      break;
    case 'p':
    case 'P':
      flags |= PICTURE_FLAG;
      ch = *fmt++;
      break;
    }
    switch (ch)
    {
    // codes
    case '@':
      _rows.add (new _PrintfTok (_currow, "@"));
      break;
    case 'b':
    case 'i':
    case 'u':
      case 'r':
    {
      char *xxxx = new char[2];
    xxxx[0] = ch;
    xxxx[1] = '\0';
    _rows.add (new _FieldTok (_currow, xxxx, FONT_FLAG));
    }
      break;
    case 'g':
    case 'j':
    {
    const char *xxx = format ("%c %d", ch, size);
    char *xxxx = new char[strlen (xxx) + 1];
    strcpy (xxxx, xxx);
    _rows.add (new _FieldTok (_currow, xxxx, JUMP_FLAG));
    }
      break;
    case 'T':
      flags |= IGNORE_FILL;
    case 't':
      flags |= TRANS_FLAG;
      break;
      case 'D':
      flags |= IGNORE_FILL;
    case 'd':
      flags |= DATE_FLAG;
      break;
    case 'F':
      flags |= IGNORE_FILL;
    case 'f':
      flags |= BOOLEAN_FLAG;
      break;
      case 'S':
      flags |= IGNORE_FILL;
    case 's':
      flags |= STRING_FLAG;
      break;
    case 'C':
      flags |= IGNORE_FILL;
    case 'c':
      flags |= RECNO_FLAG;
      break;
    case 'N':
      flags |= IGNORE_FILL;
    case 'n':
      flags |= NUMBER_FLAG;
      break;
    default:
      CHECK (0, "TPrint_application::set_row: invalid @ code");
      break;
    }
    if (flags & NUMBER_FLAG ||
        flags & DATE_FLAG ||
        flags & TRANS_FLAG ||
        flags & BOOLEAN_FLAG ||
        flags & STRING_FLAG)
    {
      char *xxx = va_arg (params, char *);
      _rows.add (new _FieldTok (_currow, xxx,
          flags, align, size, dec));
    }
    flags = 0x0000;
    align = 'l';
  }
    else
  {
    TString t;
    switch (ch)
      {
      case '#':
      case '%':
        {
    char ccc = ch;
    // check for pending string
    if (strind)
      {
        strbuf[strind] = '\0';
        _rows.add (new _PrintfTok (_currow, strbuf));
        strind = 0;
      }
    if ((ch = *fmt++) == ccc)
      _rows.add (new _PrintfTok (_currow, ccc == '%' ? "%" : "#"));
    else
      {
        // read format 
        t << ccc;
        bool islong = FALSE;
        while (strchr (printf_types, ch) == NULL)
          {
      t << ch;
      if (ch == 'l')
        islong = TRUE;
      ch = *fmt++;
      if (ch == '\0')
        fatal_box ("sorry, zer's samzing vruong"
             " uitz ioar format.");
          }
        if (isupper (ch))
          {
      ch = tolower (ch);
      fill = ' ';
          }
        if (ch == 't' || ch == 'a')
          t << 's';
        else if (ch == 'r')
#ifdef __LONGDOUBLE
          t << "Lf";
#else        
          t << 't';
#endif          
        else
          t << ch;
        if (ccc == '%')
          {
      TString q (60);
      switch (ch)
        {
        case 'd':
        case 'i':
        case 'u':
        case 'o':
        case 'x':
        case 'X':
          q.format (t, islong ? va_arg (params, long) :
              va_arg (params, int));
          break;
        case 'f':
        case 'e':
        case 'E':
        case 'G':
          q.format (t, islong ? va_arg (params, double) :
              va_arg (params, float));
          break;
        case 'c':
          q.format (t, va_arg (params, char));
          break;
        case 's':
          q.format (t, va_arg (params, char *));
          break;
        case 't': // TString

          q.format (t, (const char *)
        (TString) * ((va_arg (params, TString *))));
          break;
        case 'a': // TParagraph_string

          q.format (t, (const char *)
        (TParagraph_string) * ((va_arg (params, TParagraph_string *))));
          break;
        case 'r': // Real

          {
            const real& rrr = * va_arg (params, real *);
            if (t.len () == 2 && *_picture)
        {
          // no format specifications
          // use default picture
          q = rrr.string(_picture);
        }
        else
        {   
#ifdef __LONGDOUBLE__ 
          sprintf (__tmp_string, t, (long double)rrr);
#else        
          char *fff = (char*)(const char *)t;
          dsprintf (__tmp_string, fff, rrr.ptr());
#endif          
          q = __tmp_string;
        }
        if (rrr.is_zero () && !_print_zero)
          q.fill (' ');
          }
          break;
        default:
          break;
        }
      if (fill != ' ')
        q = fill_str (q, fill);
      fill = _fillchar;
      _rows.add (new _PrintfTok (_currow, q));
          }
        else
          _rows.add (new _PrintfRef (_currow, t, ch,
             va_arg (params, void *)));
      }
        }
        break;
      case '\n':    // ignore
        break;
      default:
        // add to string
        strbuf[strind++] = ch;
        if (!ch)
    fmt--;
        break;
      }
  }
    }
  if (strind)
    {
      strbuf[strind] = '\0';
      _rows.add (new _PrintfTok (_currow, strbuf));
      strind = 0;
    }
  va_end (params);
}

// @mfunc Setta i valori di traduzione dei campi
void TPrint_application::set_translation (
     int lognum,  // @parm Numero logido del file condenete il campo da tradurre
     const char *field, // @parm Campo di cui effettuare la straduzione
     const char *from,  // @parm Valore da tradurre
     const char *to)  // @parm Valore che assume il campo

// @comm Questa funzione occorre che sia chiamata per ogni traduzione da effettuare.
//   <nl>Esempio: set_translation(12,"STATOCIV","1","Celibe") provoca la stampa
//   automatica di stringhe al posto di determinati valori dei campi se e' dato
//   il codice @t.
//   <nl>Il posto giusto per chiamarla e' nella <mf TPrint_application::user_create>.
{
  _transtab.add (new _Transfield (lognum, field, from, to));
}

void TPrint_application::print()
{
  _cancelled = FALSE;

  // open printer if needed
  if (!(printer().isopen ()))
    if (!printer().open ())
      return;  
  
  // only external apps can change it
  _repeat_print = FALSE;
  int ncopies = printer().n_copies();
                                                       
   // never print multiple copies if printer is viswin
   // only application may repeat printing by setting _repeat_print
   int nc = printer().printtype() == screenvis ? 1 : ncopies;
   
  // NULL cursor passed only prints once
  // pre and post process do everything                                    
  
  if (_cur == NULL)
    { 
      //************************************************
      while (nc--)
  {
    
    int cnt = 0;
    bool ok = TRUE;
    do
      {
    if (preprocess_print (0, cnt))
      {
        int cnt2 = 0;
        do
          {
      if (preprocess_page (0, cnt2))
        {
                set_page (0, cnt2);
                ok = print_one (0);
        }
          }
        while (ok && postprocess_page (0, cnt2++) == REPEAT_PAGE);
      }
        }
    while (ok && postprocess_print (0, cnt++) == REPEAT_PAGE);
    // *****************************************************
  }
    }
  else
    {
      // cursor exists  *********************************************          
      while (nc--)
  {
    (*_cur) = 0l;
    _cur->freeze (TRUE);
    
    if (_cur->items () >= _wthr &&
        (_force_progind || printer ().printtype () != screenvis))
      _prind = new TProgind (_cur->items (), _wmess, _wcancel, _wbar, 35);
    print_tree (_pr_tree);
    _cur->freeze (FALSE);
    
    if (_prind)
      {
        delete _prind;;
        _prind = NULL;
      }                         
  }
      // ****************************************************************
    }                              
  if (!_repeat_print)
  {
    if (printer().isopen ())
    {
      printer().close();
      printer().resetheader();
      printer().resetfooter();
    }
    postclose_print ();  
  }
  else
    printer().formfeed();
}

bool TPrint_application::print_tree (link_item * head)
{
  bool go = TRUE;
  while (head)
  {
    head->_cnt = 0;
    if (_cur->is_first_match (head->_logicnum))
  {
    do
    {
      if (!preprocess_print (head->_logicnum, head->_cnt))
      break;
      do
    {
      // set print rows according to current file
      if (_force_setpage || _cur_file != head->_logicnum
          || !_print_defined)
      {
        reset_print ();
        set_page(head->_logicnum, head->_cnt);
        _cur_file = head->_logicnum;
      }
      int cnt2 = 0;
      do
      {
        if (!preprocess_page (head->_logicnum, cnt2))
            break;
        go = print_one (head->_logicnum);
        if (go && head->_son)
          go = print_tree (head->_son);
      }
      while (go && postprocess_page (head->_logicnum, cnt2++) ==
       REPEAT_PAGE);
    }
      while (go && _cur->next_match (head->_logicnum));
    }
    while (go && postprocess_print (head->_logicnum, head->_cnt++)
     == REPEAT_PAGE);
  }
    if (!go)
      break;
    go = TRUE;
    head = head->_brother;
  }
  return go;
}


// @mfunc Stampa un singolo record
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | Se e' riuscito a stampare il record
// @flag FALSE | Se la stampa non ha avuto successo
bool TPrint_application::print_one (
     int file) // @parm Numero logico del file di cui stampare il record

// @comm Dopo la stampa del record non viene spostato in avanti il cursore
{
  int i = 0;

  if ((_prind && _prind->iscancelled()) || printer().frozen())
    if (_cancelled = cancel_hook()) return FALSE;

  if (!_print_defined)
    return TRUE;

  if (_prind && file == _pr_tree->_logicnum)
    _prind->addstatus (1);

  TArray rw (_maxrow + 1);
  int *pos = new int[_maxrow + 1];

  for (i = 0; i <= _maxrow; i++)
    {
      rw.add (new TPrintrow ());
      pos[i] = -1;
    }

  // printing workhorse
  for (i = 0; i <= _maxrow; i++)
    for (int j = 0; j < _rows.items (); j++)
      {
  _Token *t = (_Token *) & (_rows[j]);
  if (!t)
    continue;   // should not happen

  if (t->row () == i)
    {
      char pic[36], fn[17];
      int ch, ln, from, to;

      if (t->tag () == 1)
        {
    // it's a _FieldTok
    _FieldTok *ft = (_FieldTok *) t;
    TString toprint;
    from = to = -1;

    if (ft->_flags & FONT_FLAG)
      {
        TPrintstyle st = normalstyle;
        switch (((const char *) (ft->_fld))[0])
          {
          case 'u':
      st = underlinedstyle;
      break;
          case 'b':
      st = boldstyle;
      break;
          case 'i':
      st = italicstyle;
      break;
          case 'r':
      st = normalstyle;
      break;
          }
        ((TPrintrow *) (&rw[ft->row ()]))->set_style (st);
      }
    else if (ft->_flags & JUMP_FLAG)
      {
        char ch;
        int p;

        ch = ft->_fld[0];
        p = atoi (((const char *) ft->_fld) + 2);
        if (ch == 'g')
          // go to
          pos[ft->row ()] = p;
        else
          // jump ahead
          pos[ft->row ()] =
      ((TPrintrow *) (&rw[ft->row ()]))->
      lastpos () + p;
      }
    else
      {
        if ((*(const char *) (ft->_fld)) == 'p')
          {
      // picture
      TToken_string ttt (ft->_fld, '|');
      ch = (ttt.get ())[0];
      ln = atoi ((const char *) ttt.get ());
      strcpy (fn, (const char *) ttt.get ());
      strcpy (pic, (const char *) ttt.get ());
          }
        else
          {
      TToken_string ttt (ft->_fld, '|');
      ch = (ttt.get ())[0];
      ln = atoi ((const char *) ttt.get ());
      strcpy (fn, (const char *) ttt.get ());
      from = atoi ((const char *) ttt.get ());
      to = atoi ((const char *) ttt.get ());
          }
        // get field val
        TLocalisamfile &f = _cur->file(ln);
        if (ft->_flags & TRANS_FLAG)
          {
      _Transfield *tr = NULL;
      // look up field value in translation table
      for (int i = 0; i < _transtab.items (); i++)
        {
          tr = (_Transfield *) & _transtab[i];
          if (tr->_fn == fn && tr->_lognum == ln)
            {
        // check value
        if (tr->_from == f.get (fn))
          break;
            }
        }
      if (i == _transtab.items ())
        toprint = "";
      else
        toprint = tr->_to;
          }
        else if (ft->_flags & DATE_FLAG)
          {
      const TDate d(f.get(fn));
      toprint = d.string (ft->_flags & LONG_FLAG ? full : brief);
      if (toprint.empty ())
        {
          toprint = (ft->_flags & LONG_FLAG ?
               "  -  -  " :
               "  -  -     ");
        }
          }
        else if (ft->_flags & BOOLEAN_FLAG)
          {
      toprint = f.get(fn) == "X" ? "Si" : "No";
          }
        else if (ft->_flags & NUMBER_FLAG)
          {
      TString pict (0);
      real r(f.get (fn));

      bool isreal = f.curr ().type (fn) == _realfld;

      if (ft->_flags & PICTURE_FLAG)
        pict = pic;
      else if (!(ft->_flags & DEC_FLAG) && *_picture
         && isreal)
        pict = _picture;

      if (pict.len () > 0)
        toprint = r.string (pict);
      else if (ft->_flags & DEC_FLAG)
        toprint = r.string (ft->_size, ft->_dec);
      else
        toprint = r.string ();

      if (r.is_zero () && !_print_zero)
        toprint.fill (' ');
          }
        else if (ft->_flags & STRING_FLAG)
          {
      toprint = f.curr().get (fn);
      // perform string extraction
      if (from != -1)
        toprint = toprint.sub (from, to);
      else if (to != -1)
        toprint = toprint.left (to);
          }
      }
    // adjust size and set fill char
    if (ft->_flags & PAD_FLAG)
      {
        if (!(ft->_flags & NUMBER_FLAG))
          {
      if (ft->_size < toprint.len ())
        toprint.cut (ft->_size);
      else
        toprint.left_just (ft->_size);
          }
        if (ft->_flags & ALIGN_FLAG)
          {
      if (ft->_align == 'r')
        toprint.right_just (ft->_size);
      else if (ft->_align == 'c')
        toprint.center_just (ft->_size);
      else if (ft->_align == 'l')
        toprint.left_just (ft->_size);
          }
      }
    if (_fillchar != ' ' && !(ft->_flags & IGNORE_FILL))
      toprint = fill_str (toprint, _fillchar);
    // add to print row
    ((TPrintrow *) (&rw[ft->row ()]))->put (toprint,
              pos[ft->row ()]);
    if (pos[ft->row ()] != -1)
      pos[ft->row ()] += toprint.len ();
        }

      else if (t->tag () == 0)
      {
        // it's a _PrintfTok
        _PrintfTok *pt = (_PrintfTok *) t;
        TString v = pt->_val;
        ((TPrintrow *) (&rw[pt->row ()]))->put (v, pos[pt->row ()]);
        if (pos[pt->row ()] != -1)
        {
        pos[pt->row ()] += v.len ();
        const char* s = v;
        while (*s && strncmp(s, "$[", 2) == 0)
        {  
           while (*s && *s != ']')
           { 
             pos[pt->row()]--;
             s++;
            }
            if (*s)
               pos[pt->row()]--;
            while (*s && *s != '$') s++;
         }  
         }
      }
      else
        if (t->tag () == 2)
        {
    // printf by reference
    _PrintfRef *pr = (_PrintfRef *) t;
    TString ps;
    TParagraph_string * para_str = NULL;
    bool islong = (pr->_fmt).find ('l') != -1;
    switch (pr->_type)
    {
    case 'd':
    case 'i':
    case 'u':
    case 'o':
    case 'x':
    case 'X':
      ps.format (pr->_fmt, islong ? *((long *) (pr->_what)) :
           *((int *) (pr->_what)));
      break;
    case 'f':
    case 'e':
      ps.format (pr->_fmt, islong ? *((double *) (pr->_what)) :
           *((float *) (pr->_what)));
      break;
    case 'c':
      ps.format (pr->_fmt, *((char *) (pr->_what)));
      break;
    case 's':
      ps.format (pr->_fmt, (char *) (pr->_what));
      break;
    case 't':
      ps.format (pr->_fmt, (const char *)
           (*((TString *) (pr->_what))));
      break;
    case 'a':
    {  
      para_str = ((TParagraph_string *) (pr->_what));
      const char * s = para_str->get();
      
      if (s != NULL)
        ps.format (pr->_fmt, s);
      break;
    }
    case 'r':
    {
      const real& rrr = *(real*)pr->_what;
      if (pr->_fmt.len () == 2 && *_picture)
      {
        strcpy (__tmp_string, rrr.string (_picture));
      }
      else
      {       
        const char* fff = pr->_fmt;
#ifdef __LONGDOUBLE__      
        sprintf(__tmp_string, fff, (long double)rrr);
#else            
        dsprintf(__tmp_string, (char*)fff, rrr.ptr());
#endif
      }
      ps = __tmp_string;
      if (rrr.is_zero () && !_print_zero)
        ps.fill (' ');
      break;
    }
    }

    ps = fill_str (ps, _fillchar);
    ((TPrintrow *) (&rw[pr->row ()]))->put (ps, pos[pr->row ()]);
    if (para_str != NULL)
    { 
      const char * s = para_str->get();
      int row = pr->row();           
      TPrintstyle xstyle = ((TPrintrow *)(&rw[row]))->get_style();
      
      while (s != NULL)
      {
        ps.format (pr->_fmt, s);
        ps = fill_str (ps, _fillchar); 
        row++;
        if (rw.objptr(row) == NULL)
          rw.add(new TPrintrow ());
        ((TPrintrow *) (&rw[row]))->set_style(xstyle); 
        ((TPrintrow *) (&rw[row]))->put(ps, pos[pr->row()]);  
        s = para_str->get();
      }
      ((TPrintrow *) (&rw[row]))->set_style(normalstyle);      
    }
    if (pos[pr->row ()] != -1)
      pos[pr->row ()] += ps.len ();
        }
    }
      }

  // print!                        
  const int last = rw.last();
  
  for (i = 0; i <= /*_maxrow*/ last; i++)
    {
      TPrintrow *pr = (TPrintrow *) & rw[i];
      if (!(printer().print(*pr)))
  break;
    }
  if (_auto_ff && last /* _maxrox */ < printer().formlen ())
    printer().formfeed ();
  delete pos;

  // TRUE if all rows have been printed
  // if stopped by preprocess_page returns ok
  return i == last /*_maxrow */ + 1;
}

bool TPrint_application::menu(MENU_TAG m)
{
  // funziona da se' fino a 20 voci della menubar
  if (m >= BAR_ITEM (1) && m <= BAR_ITEM (20))
    {
      _last_choice = m;
      do_print((m - BAR_ITEM (0)) / 100);
    }

  // Se non esistono altre voci di menu termina l'applicazione
  return xvt_test_menu_tag (BAR_ITEM (2));
}

bool TPrint_application::create()
{
  TApplication::create();
  printer().setfooterhandler (_pp_footer);
  printer().setheaderhandler (_pp_header);
  printer().setlinkhandler (_pp_link);
  if (user_create())
  {
    dispatch_e_menu (_last_choice);
    return TRUE;
  }
  else return FALSE;   
}

bool TPrint_application::destroy ()
{
  user_destroy();
  reset_files();
  _cursors.destroy();
  return TApplication::destroy ();
}

void TPrint_application::do_print(int n)
{
  while (set_print(n))
    {
      do { print(); } while(_repeat_print); 
      enable_print_menu();
    }
}

void TPrint_application::enable_print_menu()
{
  enable_menu_item(M_FILE_PRINT, TRUE);
}

void TPrint_application::disable_print_menu()
{
  enable_menu_item(M_FILE_PRINT, FALSE);
}

void TPrint_application::enable_setprint_menu()
{
  enable_menu_item(BAR_ITEM (1), TRUE);
}

void TPrint_application::disable_setprint_menu()
{
  enable_menu_item (BAR_ITEM (1), FALSE);
}

TPrint_application::TPrint_application ():TApplication (), _transtab (10),
                    _cursors (10), _header (10),
                    _footer (10), _rows (100)
{
  _cur = NULL;  
  _repeat_print = FALSE;
  _currow = _maxrow = 0;
  _auto_ff = FALSE;
  _wbar = _wcancel = TRUE;
  _wmess = "Stampa in corso\nPrego attendere";
  _wthr = 5;
  _confpr = "printer.ini";
  _fillchar = ' ';
  _pr_tree = NULL;
  _print_defined = FALSE;
  _force_progind = FALSE;
  _force_setpage = FALSE;
  _prind = NULL;
  _cur_file = 0;
  _picture = "";
  _print_zero = FALSE;
  _last_choice = BAR_ITEM (1);
}

void TPrint_application::reset_files() 
{ 
  if (_pr_tree != NULL)
  {
    _reset_tree(_pr_tree); 
    _pr_tree = NULL; 
  }  
} 


TPrint_application::~TPrint_application ()
{}
