#include <browfile.h>
#include <colors.h>
#include <controls.h>
#include <execp.h>     
#include <golem.h>
#include <image.h>
#include <mailbox.h>
#include <printapp.h>
#include <spotlite.h>
#include <urldefid.h>
#include <utility.h>
#include <viswin.h>

#include <bagn005.h>

const char* const PRINT_FONT = XVT_FFN_FIXED;
int PRINT_HEIGHT = 10;

#define BUTTONROW_SIZE (_showbuts ? 4 : 0)
#define X_OFFSET       (_rulers ? 6 : 1)
#define Y_OFFSET       (_rulers ? 1 : 0)
#define TEXTROWS       (rows() - Y_OFFSET - BUTTONROW_SIZE)
#define TEXTCOLUMNS    (columns() - X_OFFSET)

#define BACKGROUND MASK_BACK_COLOR
#define FOREGROUND NORMAL_COLOR

#define K_CTRL_DOWN   (K_CTRL  + K_DOWN)
#define K_CTRL_UP     (K_CTRL  + K_UP)
#define K_SHIFT_UP    (K_SHIFT + K_UP)
#define K_SHIFT_DOWN  (K_SHIFT + K_DOWN)
#define K_SHIFT_LEFT  (K_SHIFT + K_LEFT)
#define K_SHIFT_RIGHT (K_SHIFT + K_RIGHT)
#define K_ALT_RIGHT   (K_CTRL  + 'K')
#define K_ALT_LEFT    (K_CTRL  + 'L')
#define CTRL_C        (K_CTRL  + 'C')
#define CTRL_G        (K_CTRL  + 'G')
#define CTRL_E        (K_CTRL  + 'E')
#define CTRL_S        (K_CTRL  + 'S')
#define CTRL_R        (K_CTRL  + 'R')
#define CTRL_P        (K_CTRL  + 'P')

class _BkMenuItem : public TObject
{         
public:
  
  TString   _txt;  
  TArray*   _arr;  
  int       _id;    // per comodita'
  
  _BkMenuItem(const char* t = "") 
  { _txt = t; _arr = NULL; }
  virtual ~_BkMenuItem() 
  { if (_arr != NULL) delete _arr; }
};

class _BkMenuDesc : public TObject
{
public:
  TString_array   _menu;  
  int             _father_id;
  _BkMenuDesc() {}
  virtual ~_BkMenuDesc()  {}
};

int TViswin::tabx(int x) const
{
  return char2pixel(x);
}

int TViswin::taby(int y) const
{
  return y * CHARY;
}

// @doc INTERNAL

// @mfunc Abilita/Disabilita una voce di menu'
void TViswin::check_menu_item(
     MENU_TAG item, // @parm Voce del menu' da abilitare
     bool on)       // @parm Operazione da svolgere sulla voce di menu':
            // @flag TRUE | Viene abilitata la voce
            // @flag TRUE | Viene disabilitata la voce
{
  ignore_xvt_errors(TRUE);
  xvt_menu_set_item_checked(win(), item, on);
  xvt_menu_update(win());
  ignore_xvt_errors(FALSE);
}

void TViswin::enable_menu_item(MENU_TAG item, bool on)
{            
  ignore_xvt_errors(TRUE);
  xvt_menu_set_item_enabled(win(), item, on);
  xvt_menu_update(win());
  ignore_xvt_errors(FALSE);
}

HIDDEN  _BkMenuItem* find_menu_node(TArray& tree, int id)
{
  _BkMenuItem* fnd = NULL;  
  
  for (int m = 0; m < tree.items(); m++)
  {                                    
    _BkMenuItem& bkit = (_BkMenuItem&) tree[m]; 
    
    if (bkit._id == id)
      return &bkit;

    else if (bkit._arr != NULL)   
    {  
      if ((fnd = find_menu_node(*(bkit._arr), id)) != NULL)
        break;
    }
  }  
  return fnd;
}

HIDDEN void build_menu_tree(TArray& flat, TArray& tree, int level)
{
  // find ID in flat array
  _BkMenuDesc* bds = NULL;
  int i;
  
  for (i = 0; i < flat.items(); i++)
  { 
    _BkMenuDesc& bdss = (_BkMenuDesc&)flat[i];
    if (bdss._father_id == 1000 + level)
    {
      bds = &bdss;
      break; 
    }
  }
  if (bds == NULL) 
  {
    bds             = new _BkMenuDesc;  
    bds->_father_id = 1000 + level;
    flat.add(bds);
  }
  
  for (i = 0; i < tree.items(); i++)
  {
    _BkMenuItem& bki = (_BkMenuItem&)tree[i];
    bds->_menu.add(bki._txt);
    if (bki._arr != NULL)
      build_menu_tree(flat, *(bki._arr), bki._id);
  }  
}

void TViswin::build_index_menu()
{
  if (_menu_present) return;
  
  // builds bk_menu tree and index menu 
  TArray bk_tree;
  TToken_string tt(128);
  int i; 
  
  // build tree                
  for (i = 0; i < _bookmarks->items(); i++)
  {
    BkDef& bkd = (BkDef&)(*_bookmarks)[i];
    
    tt.format("%d", bkd._id + 1000);
    tt.add(bkd._txt);
    
    _BkMenuItem* bkit = new _BkMenuItem((const char*)tt);
    bkit->_id          = bkd._id;
    
    int father_id = bkd._father_id == -1 ? 0 : bkd._father_id;
    if (father_id == 0) // toplevel
      bk_tree.add(bkit);
    else
    {
      _BkMenuItem* father = find_menu_node(bk_tree, father_id); 
      if (father != NULL)
      {
        if (father->_arr == NULL) 
          father->_arr = new TArray(4); 
        father->_arr->add(bkit);
      }
    }
  } 
  
  // build menu                         
  if (bk_tree.items() > 0)
  {
    TString_array top(1); top.add("1000|Indice");
    add_menu(top); 
  }            
  
  TArray flat(4);  
  build_menu_tree(flat, bk_tree, 0);
  
  for (i = 0; i < flat.items(); i++)
  { 
    _BkMenuDesc& bds = (_BkMenuDesc&)flat[i];
    if (bds._menu.items() > 0)
      add_menu(bds._menu, bds._father_id);
  }
  
  _menu_present = TRUE;
}  

void TViswin::exec_link()
{
  if (_linkID != -1)
  {
    if (!_toplevel)
    {
      // link da browsefile_field
      MASK_LINKHANDLER pl = _brwfld->_lh;
      if (pl != NULL)
        pl(_brwfld->mask(), _linkID,
           _multiple ? (const char*)_multiple_link : (const char*)_linktxt, TRUE); 
    }       
    else
    {
      bool reload = false;
      LINKHANDLER pl = printer().getlinkhandler();
      if (pl)
        reload = pl(_linkID, _multiple ? (const char*)_multiple_link : (const char*)_linktxt);   

      if (main_app().class_id() == CLASS_PRINT_APPLICATION)
      {
        if (!reload)
        { // Backward compatibility mode
          // dai opzione per rifare la stampa se e' arrivato un messaggio dall'applicazione chiamata 
          TMailbox m;  
          reload = m.next_s(MSG_LN) != NULL;
        }
        if (reload && yesno_box(TR("Si desidera riaggiornare la stampa?")))
        {
          TPrint_application& papp = (TPrint_application&)main_app();
          papp.repeat_print();
          papp.current_cursor()->update();
          xvtil_statbar_refresh ();
          stop_run(K_ENTER);
        }
      }
    } // _toplevel 
    if (_toplevel)
    {
      set_focus();
      _inside_linkexec = TRUE;
      check_link();
      force_update();
      do_events();
      check_link (&_point);
      _inside_linkexec = FALSE;  
    }
  } // linkID != -1
  else beep();
}


void TViswin::display_link (long y, long x1, long x2, const char *d)
{
  if (!_link_displayed)
  {
    paint_link (y, x1, x2);
    _link_displayed = TRUE;
    if (_in_update) return;

    TString80 dd; dd.strncpy(d, 50);
    xvtil_statbar_set(dd);

    if (_showbuts) 
      enable_button(DLG_LINK);
    if (_toplevel) 
      enable_menu_item(M_VISWIN_LINK, true);
    
    if (!_toplevel && !_inside_linkexec)
    {
      // chiama l'handler per maschere con FALSE come terzo parametro
      MASK_LINKHANDLER pl = _brwfld->_lh;
      if (pl != NULL)
        pl(_brwfld->mask(), _linkID,
           _multiple ? (const char*)_multiple_link : (const char*)_linktxt, FALSE); 
    }
  }
}

// @doc INTERNAL

// @mfunc Cancella il link
void TViswin::erase_link (
  long y,       // @parm Altezza del link da disegnare
  long x1,      // @parm Prima coordinata x del link da cancellare
  long x2)      // @parm Seconda coordinata x del link da cancellare
{
  if (_link_displayed)
  {
    paint_link (y, x1, x2);
    _link_displayed = FALSE;

    if (!_in_update)
    {
      xvtil_statbar_set("");
      xvtil_statbar_refresh();
      if (_showbuts) 
        disable_button(DLG_LINK);
      if (_toplevel) 
        enable_menu_item(M_VISWIN_LINK, FALSE);
    }
  }
}
// @doc INTERNAL

// @mfunc Disegna il link
void TViswin::paint_link (
  long y,       // @parm Altezza del link da disegnare
  long x1,      // @parm Prima coordinata x del link da disegnare
  long x2)      // @parm Seconda coordinata x del link da disegnare
{
  if (adjust_box (x1, x2, y))
    invert_bar ((int)(x1 + (long)X_OFFSET), (int)(y + (long)Y_OFFSET), 
                (int)(x2 + (long)X_OFFSET), (int)(y + (long)Y_OFFSET+1l));
}

// @doc INTERNAL

// @mfunc Modifica le dimensione di un box
// @rdesc Ritorna se sono state modificate le dimensioni del box:
// @flag TRUE   | Sono state riassegnate le coordinate a <p x1> e <p x2>
// @flag  FALSE | Non sono state riassegnate le misure del box poiche' <p y> e' nel testo
bool TViswin::adjust_box (
  long &x1,  // @parm Prima coordinata da riassegnare
  long &x2,  // @parm Seconda coordinata da riassegnare
  long y)    // @parm Valore della riga che deve essere nel testo

  // @comm Sistema <p x1> e <p x2> in modo che il minore sia il primo, e controlla che <p y> sia 
  //       nel testo (box e' su una sola riga, usata solo per i link)
{
  if (y < origin ().y || y > origin ().y + _textrows)
    return FALSE;
  if (origin ().x > x1)
    x1 = origin ().x;
  if (origin ().x + _textcolumns < x2)
    x2 = origin ().x + _textcolumns;
  return TRUE;
}

// @doc INTERNAL

// @mfunc Controlla se la posizione cursore <p where> cade su un link ipertestuale e si 
//        comporta di conseguenza
// @rdesc Ritorna se la posizione cade sul link
// @flag TRUE | Se ha constatato la corrispondenza
// @flag FALSE | Se non ha constatato la corrispondenza
bool TViswin::check_link (
  TPoint* p) // @parm Posizione del cursore (default NULL)

  // @comm Se <p p> e' NULL indica la posizione corrente del cursore

{
  static int old_id = -1, plinkID = -1;
  static long y, x1, x2;
  static long py, px1, px2;

  if (p == NULL)                // erase and go
  {
    if (old_id != -1)
    {
      erase_link (y, x1, x2);
      old_id = _linkID = plinkID = -1;
      py = px1 = px2 = x1 = y = x2 = 0l;
    }
    return FALSE;
  }

  // poi se e' il caso lo si risistema    
  if (p == &_point)
    plinkID = -1;

  TArray & h = _txt.hotspots();
	
	for (int i = 0; i < h.items (); i++)
  {
    TToken_string & t = (TToken_string &) h[i];
    t.restart ();
    long ty = t.get_long ();
    long tx1 = t.get_long ();
    long tx2 = t.get_long ();
    if (p->y == ty && p->x < tx2 && p->x >= tx1)
    {
      // ci siamo
      int id = (int) t.get_long (4);

      if (ty != y || tx1 != x1 || tx2 != x2)
      {
        if (old_id != -1)
          erase_link (y, x1, x2);
        TToken_string & ttt = (TToken_string &) (*_links)[id];
        ttt.restart ();
        _descr = ttt.get ();
        _linktxt = t.get(3); 
        if (_multiple)
        {
          // get all parts of the same color 
          const char *cp;
          _txt.read_line (ty);
          _multiple_link = "";
          
          const char fg = *ttt.get(1);
          const char bg = *ttt.get(2);
          while ((cp = _txt.piece ()))
          {
            if (_txt.get_foreground() == fg && _txt.get_background() == bg)
            {    
              _multiple_link.add (cp);
              _descr << ' ' << cp;
            }  
          }
        }  
        else
          _descr << _linktxt;
          
        old_id = _linkID = id;
        y = ty;
        x1 = tx1;
        x2 = tx2;
        display_link (y, x1, x2, _descr);
      }
      if (p == &_point)
      {
        _pdescr = _descr;
        plinkID = id;
        px1 = x1;
        px2 = x2;
        py = y;
      }
      return TRUE;
    }
  }
  // non sono su un bottone: puo' esserci il point 
  if (old_id != -1 && plinkID == -1)
  {
    old_id = _linkID = -1;
    erase_link (y, x1, x2);
    x1 = x2 = y = 0l;
  }
  // se point e' su un bottone, evidenzia quello
  else if (plinkID != -1 && (x1 != px1 || x2 != px2 || y != py))
  {
    // erase old one
    erase_link (y, x1, x2);
    old_id = _linkID = plinkID;
    x1 = px1;
    x2 = px2;
    y = py;
    _descr = _pdescr;
    display_link (y, x1, x2, _descr);
    
    return TRUE;
  }
  return FALSE;
}

bool TViswin::in_text (const TPoint & p) const
{
  if (p.x > (X_OFFSET - 1) && p.x < columns() && 
      p.y > (Y_OFFSET - 1) && p.y < (rows() - BUTTONROW_SIZE))
    return TRUE;
  return FALSE;
}


// @doc INTERNAL

// @mfunc Indica se occorre ridisegnare la selezione
//
// @rdesc Ritorna se e' necessario ridipingere la selezione
bool TViswin::need_paint_sel (
  bool smart)   // @parm Indica se ridisegnarla in ogni caso
  //
  // @flag TRUE | Indica il ridisegno solamente se e' necessario
  // @flag FALSE | Indica il ridisegno in ogni caso
{
  TPoint p1, p2;
  adjust_selection (p1, p2);
  long end = origin ().y + _textrows;
  bool r = _isselection;
  if (smart)
    r = r && (
      (origin ().y >= p1.y - 1 && origin ().y <= p2.y + 1) ||
      (end >= p1.y - 1 && end <= p2.y + 1));
  return r;
}

void TViswin::erase_selection ()
{
  if (_sel_displayed)
    paint_selection ();
  _sel_displayed = FALSE;
  if (_toplevel)
	{
    enable_menu_item(M_VISWIN_COPY, FALSE);
    enable_menu_item(M_VISWIN_CLEAR, FALSE);
	}
}

void TViswin::display_selection ()
{
  if (!_sel_displayed)
    paint_selection ();
  _sel_displayed = TRUE;   
  if (_toplevel)
	{
    enable_menu_item(M_VISWIN_COPY, TRUE);
    enable_menu_item(M_VISWIN_CLEAR, TRUE);
	}
}

// @doc INTERNAL

// @mfunc Riposizona inizio e fine della selezione
void TViswin::adjust_selection (
  TPoint & p1, // @parm Primo punto per il riposizionamento della selezione
  TPoint & p2) // @parm Secondo punto per il riposizionamento della selezione

  // @comm Viene asseganto a <p p1> e <p p2> le coordinate di inizio e fine selezione
{
  if (_sel_start.y < _sel_end.y)
  {
    p1 = _sel_start;
    p2 = _sel_end;
  }
  else
  {
    p1 = _sel_end;
    p2 = _sel_start;
  }
}

void TViswin::shift_screen(scroll dir)
{
  RCT r;

  if (_scrolling)
    return;
  _scrolling = TRUE;
  // origin() e' gia' stata modificata
  switch (dir)
  {
  case up:
  case down:
    xvt_rect_set(&r, 0, taby(Y_OFFSET)/*+1*/, 
                 tabx(int(_textcolumns) + X_OFFSET), taby(int(_textrows) + Y_OFFSET));
    xvt_dwin_scroll_rect (win(), &r, 0, dir == down ? taby(1) : -taby(1));
    paint_row (dir == up ? origin ().y + _textrows - 1 : origin ().y);
    break;
  case left:
  case right:
    xvt_rect_set(&r, 
                 tabx(X_OFFSET), 0, 
                 tabx(int(_textcolumns) + X_OFFSET)/* + 2*/,
                 taby(int(_textrows) + 1)/* - 2*/);
    xvt_dwin_scroll_rect (win (), &r, dir == right ? tabx(1) : -tabx(1), 0);

    paint_column (dir == left ? origin ().x + _textcolumns - 1 : origin ().x, dir == left);
    break;
  default:
    break;
  }
  _scrolling = FALSE;
}

// @doc INTERNAL

// @mfunc Aggiunge un bottone alla finestra
//
// @rdesc Ritorna un puntatore al bottone (TControl) aggiunto
TPushbutton_control* TViswin::add_button (
  short id,             // @parm Identificatore del bottone da aggiungere
  const char *caption,  // @parm Testo del bottone da aggiungere
  short bup,            // @parm Identificatore della bitmap normale
  short bdn)            // @parm Identificatore della bitmap pramuta

  // @comm Aggiunge i bottoni nella finestra
{
  TPushbutton_control* b = new TPushbutton_control(win(), id, -11, -1, 12, 2, "",
                                                   caption, bup, bdn); 
  _button.add(b);
  return b;
}

bool TViswin::enable_button(short id, bool on)
{
  int i;
  for (i = _button.last(); i >= 0; i--)
  {
    TPushbutton_control* b = (TPushbutton_control*)_button.objptr(i);
    if (b->id() == id)
    {
      b->enable(on);
      break;
    }
  }
  return i >= 0;
}

void TViswin::repos_buttons ()
{                         
  if (_toplevel)
  {                                    
    RCT wr; xvt_vobj_get_client_rect(win(), &wr);    
    const int bar_h = BUTTONROW_SIZE * CHARY;
    const int bar_y = wr.bottom - bar_h + 1;
    
    for (int i = 0; i < _modules.items(); i++)
    {
      TImage& image = (TImage&)_modules[i];
      const int iy = bar_y + (bar_h - image.height()) / 2;
      image.set_pos(4, iy);
    }  

    const int buttons = _button.items();
    if (buttons > 0)
    {
      RCT br; ((TPushbutton_control&)_button[0]).get_rect(br);
      const int width = br.right - br.left;
      int space = ((wr.right - wr.left) - buttons * width) / (buttons + 1);
      if (space < 0)
        space = 0;
    
      int x = space;
      const int by = br.top;
      for (int b = 0; b < buttons; b++)
      {
        const PNT p = { by, x };
        xvt_rect_set_pos(&br, p);
        ((TPushbutton_control&)_button[b]).set_rect(br);
        x += space + width;
      }
    }
  }    
}

void TViswin::open ()
{
  set_scroll_max (MAXLEN - 1, _txt.lines () <= _textrows ? _txt.lines () : _txt.lines () - _textrows);
  repos_buttons();
  TScroll_window::open();
  show_buttons(_showbuts);
  force_update (); 
}

// prints the window contents
void TViswin::paint_screen ()
{
  RCT clip; get_clip_rect(clip);
  clip.left = clip.top = 0;
  xvt_dwin_set_clip(win(), &clip);

  for (int j = 0; j < _textrows; j++)
  {
    const long rw = origin ().y + j;
    if (rw < _txt.lines ())
      paint_row (rw);
    else 
    {
      if (!_isopen)
      {
        autoscroll (FALSE);
        set_mode (M_COPY);
        set_pen (COLOR_BLACK);

        PNT b, e;
        b.h = tabx(X_OFFSET-1);
        b.v = taby((int)(j + (long)Y_OFFSET - origin().y))/* -2 *****/;
        e.h = tabx(columns());
        e.v = b.v;
        xvt_dwin_draw_set_pos(win(), b);
        xvt_dwin_draw_line(win(), e);
        set_pen (COLOR_LTGRAY);
        e.v++;
        b.v++;
        xvt_dwin_draw_set_pos (win (), b);
        xvt_dwin_draw_line (win (), e);
        set_brush (COLOR_DKGRAY);
        bar (X_OFFSET-1, (int)(j+(long)Y_OFFSET-origin().y),
             (int)columns()+1, (int)(rows()-(long)BUTTONROW_SIZE));
        autoscroll (TRUE);
      }
      break;
    }
  }
}



// @doc INTERNAL

// @mfunc Permette di stampare il background
void TViswin::paint_background(
  long j,       // @parm Numero della riga di cui stampare il background
  int row)      // @parm Numero della riga a video sulla quale viene stampato il background
{
  TPrinter& pr = printer();

  if (_toplevel)
  {
    const word page = word((j-1) / pr.formlen() + 1);
    _bg = &pr.getbgdesc(page);
  }

  const bool isbackground = _bg->items() > 0 && pr.isgraphics();
  const bool fink_mode = pr.get_fink_mode();
  const int rw = (int)(j % (long)_formlen);
  const int ox = (int)origin().x;
  const int mx = ox + columns();

  if (!fink_mode)
  {        
    const char* line = pr.background_chars(rw);
    set_color(COLOR_BLACK, COLOR_WHITE);
    if (line != NULL && (int)strlen(line) > ox)
      stringat (X_OFFSET, row, &line[ox]);
    // return;
  }
  if (!isbackground)
    return;
  
  const TString& rwd = _bg->row(rw);

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

  int x1, x2, y1, y2, id;
  PNT b, e;
  int cnt = 0;
  char ch;

  bool isbeg = FALSE;
  bool isend = FALSE;

  while ((ch = rwd[cnt++]) != '\0')
  {
    if (!fink_mode && (ch == 'v' || ch == 'o' || ch == 'u' || ch == 'h' || ch == 'r'))
      continue;
    switch (ch)
    {
    case 'v':           // verticale intera

      x1 = (byte)rwd[cnt++];
      if (x1 >= ox && x1 <= mx)
      {
        x1 += (X_OFFSET-1) - ox;
        b.h = e.h = tabx(2*x1+1)/2;
        b.v = taby(row);
        e.v = taby(row+1);
        xvt_dwin_draw_set_pos(win(), b);
        xvt_dwin_draw_line(win(), e);
      }  
      break;
    case 'o':           // verticale pezzo sopra

      x1 = (byte)rwd[cnt++];
      if (x1 >= ox && x1 <= mx)
      {
        x1 += (X_OFFSET -1) - ox;
        b.h = e.h = tabx(2*x1+1) / 2;
        b.v = taby(row);
        e.v = taby(2*row+1)/2;
        xvt_dwin_draw_set_pos (win(), b);
        xvt_dwin_draw_line (win(), e);
      }  
      break;
    case 'u':           // verticale pezzo sotto

      x1 = (byte)rwd[cnt++];
      if (x1 >= ox && x1 <= mx)
      {
        x1 += (X_OFFSET -1) - ox;
        b.h = e.h = tabx(2*x1+1)/2;
        b.v = taby(2*row+1)/2;
        e.v = taby(row + 1);
        xvt_dwin_draw_set_pos(win(), b);
        xvt_dwin_draw_line(win(), e);
      }  
      break;
    case 'h':           // orizzontale intera
//      x1 = (byte) rwd[cnt++] + (X_OFFSET -1) - ox;
//      x2 = (byte) rwd[cnt++] + (X_OFFSET -1) - ox;
      x1 = (byte) rwd[cnt++];
      x2 = (byte) rwd[cnt++];
      if (x1 <= mx && x2 >= ox)
      {
        x1 += (X_OFFSET -1) - ox; 
        if (x1 < X_OFFSET) x1 = X_OFFSET;
        x2 += (X_OFFSET -1) - ox;
        b.v = e.v = taby(2*row+1) / 2;
        b.h = tabx(x1);
        e.h = tabx(x2 + 1);
        xvt_dwin_draw_set_pos(win(), b);
        xvt_dwin_draw_line(win(), e);  
      }
      break;
    case 'r':           // orizzontale scorciata agli estremi
      x1 = (byte) rwd[cnt++];
      x2 = (byte) rwd[cnt++];
      isbeg = ox <= (x1-1);
      isend = mx >= (x2-1);
      if (x1 <= mx && x2 >= ox)
      {
        x1 += (X_OFFSET -1) - ox; 
        if (x1 < X_OFFSET) x1 = X_OFFSET;
        x2 += (X_OFFSET -1) - ox;
        b.v = e.v = taby(2*row+1) / 2;
        if (isbeg) b.h = tabx(2*x1+1) / 2;
        else b.h = tabx(x1);
        if (isend) e.h = tabx(2*x2+1) / 2;   
        else e.h = tabx(x2 +1);
        xvt_dwin_draw_set_pos(win(), b);
        xvt_dwin_draw_line(win(), e);
      }  
      break;  
      
    case 'i':
      id = (byte)(rwd[cnt++])-1;   // Numero immagine
      y1 = (byte)(rwd[cnt++])-1;   // Riga sorgente
      x1 = (byte)(rwd[cnt++])-1;   // Colonna destinazione
      x2 = (byte)(rwd[cnt++]);     // Larghezza destinazione (in caratteri)
      y2 = (byte)(rwd[cnt++]);     // Altezza destinazione (in caratteri)
      if (id >= 0)
      {                       
        TImage* i = (TImage*)_images.objptr(id);
        if (i == NULL)
        {
          const TString_array& a = pr.image_names();
          const TImage src(a.row(id));
          if (src.ok())
          {
            i = new TImage(src, x2*CHARX, y2*CHARY);
            _images.add(i, id);
          }
        }  
        if (i && i->ok())
        {        
          RCT src; xvt_rect_set(&src, 0, y1*CHARY, i->width(), min((y1+1)*CHARY, i->height()));
          RCT dst; xvt_rect_set(&dst, 0, 0, tabx(x2), taby(1)); 
          xvt_rect_offset(&dst, tabx(x1-origin().x+X_OFFSET), taby(row));
          i->draw(win(), dst, src);
        }  
      }  
      break;
    case 'W':
      curwid = rwd[cnt++];
      set_pen (trans_color (curcol), curwid - '0', trans_brush (curpat),
               trans_pen (curpen));
      break;
    case 'P':
      curpen = rwd[cnt++];
      set_pen (trans_color (curcol), curwid - '0', trans_brush (curpat),
               trans_pen (curpen));
      break;
    case 'B':
      curpat = rwd[cnt++];
      set_pen (trans_color (curcol), curwid - '0', trans_brush (curpat),
               trans_pen (curpen));
      break;
    case 'C':
      curcol = rwd[cnt++];
      set_pen (trans_color (curcol), curwid - '0', trans_brush (curpat),
               trans_pen (curpen));
      break;
    default:
      break;
    }
  }
  // restore default pen
  set_pen (COLOR_BLACK);
}

void TViswin::get_clip_rect(RCT& clip) const
{
  xvt_vobj_get_client_rect(win(), &clip);
  if (_rulers)
  {
    clip.left += tabx(X_OFFSET);
    clip.top += taby(1);
  }  
  if (_showbuts)
    clip.bottom = clip.top + taby(int(_textrows));
}

void TViswin::paint_image(int row, const char* cp)
{
  TToken_string tok(cp+2, ',');
  TImage img(tok.get(0));
                       
  RCT clip; get_clip_rect(clip);
  xvt_dwin_set_clip(win(), &clip);
    
  const int x = tabx(X_OFFSET+tok.get_int()-int(origin().x)-1);
  const int y = taby(row);
  int dx = tabx(tok.get_int(3)-tok.get_int(1)+1);
  int dy = taby(tok.get_int(4)-tok.get_int(2)+1);
  if (img.ok())
  {
    const double ratiox = double(img.width()) / dx;
    const double ratioy = double(img.height()) / dy;
    const double ratio = ratiox > ratioy ? ratiox : ratioy;
    dx = int(img.width() / ratio);
    dy = int(img.height() / ratio);
    RCT dst; xvt_rect_set(&dst, x, y, x+dx, y+dy);
    img.draw(win(), dst);
  }  
  else
  { 
    RCT dst; xvt_rect_set(&dst, x, y, x+dx, y+dy);
    set_pen(COLOR_RED);
    xvt_dwin_draw_rect(win(), &dst);
  }

  xvt_dwin_set_clip(win(), NULL);
}

void TViswin::paint_row (long j)
{
  if (need_paint_sel (FALSE))
  {
    TPoint p1, p2;
    adjust_selection (p1, p2);
  }

  const long y = origin ().y;
  const int row = (int) (j + (long)Y_OFFSET - y);

  autoscroll(FALSE);
  set_font(PRINT_FONT, XVT_FS_NONE, PRINT_HEIGHT);

  set_mode(M_COPY);
  set_opaque_text(TRUE);
  if (_rulers)
  { 
    set_color(FOREGROUND, BACKGROUND);  
    set_brush(BACKGROUND);
    printat(0, row, "%05ld", j + 1);
  }
  if (_scrolling)
  {
    hide_pen ();
    set_brush (COLOR_WHITE);
    RCT r;
    r.top = row * CHARY;
    r.left = tabx(X_OFFSET -1),
    r.bottom = r.top + taby(1)/* + 1*/;
    r.right = tabx(255);
    xvt_dwin_draw_rect (win (), &r);
  }
  
  paint_background(j, row);
  set_opaque_text(FALSE);
  
  const char *cp;
  int pos = 0;     
  _txt.read_line (j, origin().x);
  while ((cp = _txt.piece ()) != NULL)
  {
    const int st   = _txt.get_style();    
    const COLOR bg = trans_color(_txt.get_background());
    const COLOR fg = trans_color(_txt.get_foreground()); 
    if (bg == fg)
    {                       
      if (*cp == 'i')
        paint_image(row, cp);
    }
    else
    {
      set_font(PRINT_FONT, st, PRINT_HEIGHT);
      set_color(fg, bg);
      stringat(X_OFFSET+pos, row, cp);
    }  
    pos += strlen (cp);
  }
  if (_scrolling && (pos < _textcolumns))
  {
    set_color (COLOR_BLACK, COLOR_WHITE);
    TString256 fill; fill.spaces(256);
    stringat(X_OFFSET+pos, row, fill);
  }

  if ((j % _formlen) == (_formlen - 1) && _toplevel)         // last row
  {
    PNT b, e;

    b.h = tabx(X_OFFSET -1);
    b.v = taby(row + Y_OFFSET) - 1;
    e.h = tabx(164);
    e.v = taby(row + Y_OFFSET) - 1;
    set_pen (COLOR_LTGRAY, 2, PAT_SOLID, P_DASH);
    xvt_dwin_draw_set_pos (win(), b);
    xvt_dwin_draw_line (win(), e);
  }

  autoscroll (TRUE);
}

// @doc INTERNAL

// @mfunc Disegna una colonna
void TViswin::paint_column (
  long j,       // @parm Colonna da disegnare
  bool end)     // @parm Indica se si tratta dell'ultima colonna:
  //
  // @flag TRUE | E' l'ultima colonna ed implica uno scroll orizzonatle avvenuto
  // @flag FALSE | Non e' l'ultima colonna
{
  paint_header ();
  set_opaque_text (FALSE);
  set_mode (M_COPY);
  TPoint p1, p2;
  if (need_paint_sel (FALSE))
    adjust_selection (p1, p2);
  set_color (COLOR_BLACK, COLOR_WHITE);

  autoscroll (FALSE);
  
  RCT clipper;
  xvt_rect_set(&clipper, tabx(X_OFFSET), 0, tabx(X_OFFSET+1), taby(int(_textrows + 1)));
  if (end)
    xvt_rect_offset(&clipper, tabx(int(_textcolumns-1)), 0);
  xvt_dwin_set_clip(win(), &clipper);
  
  const int col = end ? (int) (_textcolumns + X_OFFSET -1) : X_OFFSET;
  set_brush(COLOR_WHITE);
  bar(col, Y_OFFSET, col+1, int(_textrows + Y_OFFSET));
  const long orig_y = origin().y;
  const long last = min(_txt.lines() - orig_y, _textrows);
  for (long l = 0L; l < last; l++)
  {
    const long riga_txt = orig_y + l;
    const int  riga_scr = (int)l+Y_OFFSET;
    paint_background (riga_txt, riga_scr);

    if ((riga_txt% _formlen) == (_formlen - 1) && _toplevel)
    {
      PNT b, e;
  
      b.h = tabx(short(col));
      b.v = taby(riga_scr+1) -1 ;
      e.h = tabx(short(col+1));
      e.v = taby(riga_scr+1) -1;
      set_pen (COLOR_LTGRAY, 2, PAT_SOLID, P_DASH);
      xvt_dwin_draw_set_pos (win(), b);
      xvt_dwin_draw_line (win(), e);
    }

    const char *c = (const char *) _txt.line (riga_txt);

    const int st = _txt.get_style ((int) j);
    set_font (PRINT_FONT, st, PRINT_HEIGHT);

    COLOR bg = trans_color (_txt.get_background ((int) j));
    COLOR fg = trans_color (_txt.get_foreground ((int) j));
    set_color (fg, bg);
    
    char str[2] = { (unsigned int)j < strlen (c) ? c[(int)j] : ' ', '\0' };
    stringat (col, riga_scr, str);
  }
  
  xvt_dwin_set_clip(win(), NULL);
  
  autoscroll (TRUE);
}

void TViswin::draw_crossbars ()
  // prints reference crossbars
{
  if (_cross.v > taby(1) && _cross.v < taby(rows () - BUTTONROW_SIZE) &&
      _cross.h > tabx (X_OFFSET - 1) && _cross.h < tabx(columns()) )
  {
    set_pen (COLOR_BLACK);
    set_mode (M_XOR);

    PNT b1, e1, b2, e2;

    autoscroll (FALSE);
    b1.h = _cross.h;
    b1.v = taby(Y_OFFSET);
    e1.h = _cross.h;
    e1.v = taby((rows() - BUTTONROW_SIZE + (_rulers ? 0 : 1)));
    b2.h = tabx(X_OFFSET -1);
    b2.v = _cross.v;
    e2.h = tabx(columns());
    e2.v = _cross.v;
    xvt_dwin_draw_set_pos (win (), b1);
    xvt_dwin_draw_line (win (), e1);
    xvt_dwin_draw_set_pos (win (), b2);
    xvt_dwin_draw_line (win (), e2);
    autoscroll (TRUE);
  }
}

void TViswin::display_crossbar ()
{
  if (!_cross_displayed)
    draw_crossbars ();
  _cross_displayed = TRUE;
}

void TViswin::erase_crossbar ()
{
  if (_cross_displayed)
    draw_crossbars ();
  _cross_displayed = FALSE;
}

void TViswin::display_point()
{
  if (!_point_displayed)
    paint_point ();
  _point_displayed = TRUE;
}

void TViswin::erase_point()
{
  if (_point_displayed)
    paint_point ();
  _point_displayed = FALSE;
}

// @doc INTERNAL

// @mfunc Disegna il cursore in xor nello stile del momento 
void TViswin::paint_point (
  bool erase)   // @parm Indica di cancellare il cursore precedente (default FALSE):
  //
  // @flag TRUE | Cancella il cursore precedente
  // @flag FALSE | Mantiene il cursore precedente
{
  
  autoscroll (FALSE);
  
  const int x = int(_point.x - origin().x + X_OFFSET);
  const int y = int(_point.y - origin().y + Y_OFFSET);

  if (_isbar)
  {
    invert_bar (X_OFFSET, y, (int)columns(), y+1);
    invert_bar (x, Y_OFFSET, x+1, (int)(rows() - BUTTONROW_SIZE));
  }
  else
  {
    invert_bar (x, y, x+1, y+1);
    if (_rulers)
    {
      invert_bar (0, y, X_OFFSET-1, y+1);
      invert_bar (x, 0, x+1, 1);
    }
  }
  autoscroll (TRUE);

}

// draw screen header
void TViswin::paint_header ()
{                           
  if (!_rulers) 
    return;       
  
  set_mode (M_COPY);
  set_opaque_text (TRUE);
  set_color (FOREGROUND, BACKGROUND);
  set_font (PRINT_FONT, XVT_FS_NONE, PRINT_HEIGHT);

  TString16 htmpst;
  for (int i = 1; i < 26; i++)
  {
    htmpst.format ("%d", i);
    htmpst.right_just (10, '.');
    xvt_dwin_draw_text(win(), tabx(i*10 - 4 - int(origin().x)), CHARY-2, htmpst, 10);
  }
  autoscroll (FALSE);
  set_color (COLOR_WHITE, BACKGROUND);
  printat (0, 0, "P.%3ld", (origin().y / _formlen) + 1L);
  autoscroll (TRUE);
}

void TViswin::paint_selection ()
{
  TPoint p1, p2;
  adjust_selection (p1, p2);

  // paint rows
  for (long l = p1.y; l <= p2.y; l++)
  {
    int top, left, right;
    top = (int) (l - origin().y + Y_OFFSET);
    if (top > 0 && top <= _textrows)
    {
      left  = p1.y == l ? (int)(p1.x-origin().x + (long)X_OFFSET) : X_OFFSET;
      right = p2.y == l ? (int)(p2.x-origin().x + (long)X_OFFSET) :
      (int)(_textcolumns + (long)X_OFFSET);
      autoscroll (FALSE);
      invert_bar (left, top, right, top + 1);
      autoscroll (TRUE);
    }
  }
}

// @doc INTERNAL

// @mfunc Disegna la pagina che scorre
void TViswin::paint_waitbar()
{                          
  HIDDEN int pic = 0;
  ((TImage&)_modules[pic]).draw(win());
  pic = (pic+1) & 0x3;
}
        
void TViswin::txt_clear(COLOR color)
{
  autoscroll(FALSE);
  set_brush(color); 
  set_mode(M_COPY);
  bar ((X_OFFSET-1), Y_OFFSET -1, columns()+16, rows()-BUTTONROW_SIZE);
}        

bool TViswin::can_be_closed() const
{
  ((TViswin*)this)->on_button(DLG_QUIT);
  return FALSE;
}
        
void TViswin::update ()
{
  if (_scrolling || _in_update)
    return;
  
  _in_update = TRUE;
  
  erase_point();
  autoscroll(FALSE);
  set_mode(M_COPY);

  if (!_toplevel)
    TField_window::update();

  check_link();
  if (_isselection)
    erase_selection ();
  txt_clear(COLOR_WHITE);
  set_brush (BACKGROUND);

  if (_rulers) 
  {
    bar (0, 0, columns() + 1, 1);
    bar (0, 0, 5, rows() + 1);  
  }
  if (_showbuts && _isopen)
    paint_waitbar();
  else 
    if (_showbuts) ((TImage&)_modules[4]).draw(win());
  autoscroll (TRUE);
  paint_screen();
  paint_header();
  if (_isselection)
    display_selection();

  display_point(); 
  check_link(&_point);
  autoscroll (TRUE);
  _need_scroll = none;
  _in_update = FALSE;
}

void TViswin::abort_print ()
{
  if (yesno_box ("Interruzione della stampa su video?"))
  {
    _txt.freeze ();
    freeze ();
    printer().freeze();
    set_focus ();
    close_print();
  }
}

void TViswin::scroll_error(long x, long y)
{
  beep();
  update_thumb(x, y);
}

void TViswin::on_button(short dlg)
{
  switch (dlg)
  {
    case DLG_QUIT:
     if (_isopen) 
       abort_print();
     else
     {
       xvtil_statbar_set("");
       xvtil_statbar_refresh();
       stop_run(K_ENTER);
     } 
     break;
   case DLG_PRINT:                      
     dispatch_e_menu(win(), M_VISWIN_PRINT);
     break;
   case DLG_PDF:                      
      xvtil_statbar_set ("");
      xvtil_statbar_refresh ();
      stop_run(CTRL_P);
     break;
   case DLG_SAVEREC:
     check_link();
     call_editor();
     update();
     check_link(&_point);
     break;
   case DLG_LINK:
     exec_link();
     break; 
   default: 
     TWindow::on_button(dlg);
     break;
   }
}
                
                
long TViswin::handler (WINDOW win, EVENT * ep)
{
  static bool ignore = FALSE;

  int kdiff_x, kdiff_y;
  PNT newcross;
  TPoint p;
  bool tlnk = FALSE;
  int kdiff;
  long new_origin;
  switch (ep->type)
  {
  case E_USER:
    if (ep->v.user.id == E_ADDLINE)
    {
      set_scroll_max (MAXLEN - 1, _txt.lines () <= _textrows ?
                      _txt.lines () : _txt.lines () - _textrows);
    }
    else if (ep->v.user.id == E_ADDLINE_ONSCREEN)
    {
      set_scroll_max (MAXLEN - 1, _txt.lines () <= _textrows ? _txt.lines () : _txt.lines () - _textrows);
      erase_point ();
      if (need_paint_sel (FALSE))
        erase_selection ();
      paint_row (_txt.lines () - 1l);
      if (need_paint_sel (FALSE))
        display_selection ();
      display_point ();
      force_update ();
    }
    autoscroll (FALSE);
    _textrows = TEXTROWS;
    _textcolumns = TEXTCOLUMNS;
    autoscroll (TRUE);
    break;                                      
  case E_COMMAND:
    if (ep->v.cmd.tag > 1000 &&  ep->v.cmd.tag < 2000)
    {    
      // bookmark
      int index = ep->v.cmd.tag - 1001;
      BkDef& bds = (BkDef&)(*_bookmarks)[index];
      goto_pos(bds._row, 0l);
    }
    else switch(ep->v.cmd.tag)
    {
    case M_VISWIN_STOP:     // interrompi
      dispatch_e_char(win, K_ESC);
      break;
    case M_VISWIN_LINK:      // collega
      dispatch_e_char(win, CTRL_G);
      break;
    case M_VISWIN_EDIT:    // esporta
      dispatch_e_char(win, CTRL_E);
      break;
    case M_VISWIN_PRINT:  // stampa
      xvtil_statbar_set ("");
      xvtil_statbar_refresh ();
      stop_run(CTRL_S);
      break;
    case M_SHOW_RULERS:   // mostra righelli
      show_rulers(_rulers == 0);
      check_menu_item(M_SHOW_RULERS, _rulers != 0);
      break;
    case M_SHOW_BUTTONS:         // mostra bottoni
      show_buttons(!_showbuts);
      check_menu_item(M_SHOW_BUTTONS, _showbuts);
      break;
    case M_VISWIN_QUIT:          // chiudi
      xvtil_statbar_set ("");
      xvtil_statbar_refresh ();
      stop_run (K_ENTER);
      break;
    case M_VISWIN_COPY:     // copia nella clipboard   
      dispatch_e_char(win, CTRL_C);
      break;
    case M_VISWIN_CLEAR:    // annulla selezione 
      dispatch_e_char(win,K_ENTER);
      break;
    case M_VISWIN_SEARCH:   // cerca  
      find();
      break;
    case M_VISWIN_NEXT:   // cerca il prossimo  
      find_next();
      break;
    case M_VISWIN_REDRAW:     // ridisegna
      refresh();
      break;
    }
    break;
  case E_CONTROL:
    break;
  case E_TIMER:
    if (ep->v.timer.id == _timer)
    {
      xvt_timer_destroy (_timer);
      _timer = 0;
    }
    else if (ep->v.timer.id == _wtimer)
    {
      if (_showbuts) 
        paint_waitbar ();
      if (!_isopen)
      {
        xvt_timer_destroy (_wtimer);
        _wtimer = 0;
      }
    }
    break;
  case E_MOUSE_DBL:
    break;
  case E_MOUSE_DOWN:
    autoscroll(FALSE);
    p = dev2log(ep->v.mouse.where);
    autoscroll(TRUE);

    xvt_win_trap_pointer (win);

    if (ep->v.mouse.button == 0)        // left button: text selection
    {
      if (need_paint_sel (FALSE))
      {
        erase_selection ();
        _isselection = FALSE;
      }
      if (!in_text (p) || (p.y + origin().y) >= _txt.lines ())
      {
        ignore = TRUE;
        break;
      }

      erase_point ();
//      _sel_start = ep->v.mouse.where;
      _sel_start = p;
      _sel_start.x += (origin ().x - X_OFFSET);
      _sel_start.y += (origin ().y - Y_OFFSET);

      _sel_end = _sel_start;
      _selecting = TRUE;
    }
    else
    {
      // show crossbars 
      _cross = ep->v.mouse.where;
      display_crossbar ();
      _iscross = TRUE;
    }
    break;
  case E_MOUSE_UP:
    xvt_win_release_pointer ();

    if (ep->v.mouse.button == 0)        // left button: text selection/move
      // point

    {
      autoscroll(FALSE);
      p = dev2log(ep->v.mouse.where);
      autoscroll(TRUE);

      if (_isopen && _toplevel && (p.x >= 4 && p.x <= X_OFFSET) &&
          (p.y >= (int)rows() - BUTTONROW_SIZE && p.y <= (int)rows() - Y_OFFSET))
      {
        abort_print ();
        ignore = TRUE;
      }
      if (ignore)
      {
        ignore = FALSE;
        _selecting = FALSE;
        break;
      }
      // confirm selection & store 
      p.x += (origin ().x - X_OFFSET);
      p.y += (origin ().y - Y_OFFSET);

      if (_sel_start == p)
      {
        if (_isselection)
        {
          _isselection = FALSE;
          erase_selection ();
        }
        if (_sel_start == _point && !_selecting)
        {
          dispatch_e_char (win, K_F5);
        }
        else
        {
          _point.x = p.x;
          _point.y = p.y;
          if (_point.y > _txt.lines ())
            _point.y = _txt.lines () - 1l;
          if (_point.y < 0)
            _point.y = 0;
          if (_point.x < 0)
            _point.x = 0;
          if (_point.x > 255)
            _point.x = 255;

          check_link (&_point);
          display_point ();
        }
      }
      else
      {
        _sel_end.x = p.x;
        _sel_end.y = p.y;
        if (_sel_end.y >= _txt.lines ())
          _sel_end.y = _txt.lines () - 1l;
        if (_sel_end.y < 0)
          _sel_end.y = 0;
        if (_sel_end.x < 0)
          _sel_end.x = 0;
        if (_sel_end.x > 255)
          _sel_end.x = 255;
        _point = _sel_end;
        _isselection = _selecting;
        check_link (&_point);
        display_point ();
      }
      _selecting = FALSE;
    }
    else
    {
      erase_crossbar ();
      _iscross = FALSE;
    }
    break;
  case E_MOUSE_MOVE:
  {
    /*  
       if (!_selecting && !_iscross)       // no buttons pressed
       {
       p = ep->v.mouse.where;
       if (in_text (p))
       {
       p.x += (origin ().x - 6);
       p.y += (origin ().y - 1);
       check_link (&p);
       }
       }
       */
    if (_selecting || _iscross)
    {
      autoscroll(FALSE);
      p = dev2log(ep->v.mouse.where);
      autoscroll(TRUE);

      if (_selecting)
        _isselection = TRUE;

      // scroll if necessary
      if (p.y >= _textrows + 1l)
      {
        if (_isselection)
          erase_selection ();
        if (_iscross)
          erase_crossbar ();
        dispatch_e_scroll (win, K_DOWN);
        if (_isselection)
          display_selection ();
        if (_iscross)
          display_crossbar ();
      }
      else if (p.y <= 0l)
      {
        if (_isselection)
          erase_selection ();
        if (_iscross)
          erase_crossbar ();
        dispatch_e_scroll (win, K_UP);
        if (_isselection)
          display_selection ();
        if (_iscross)
          display_crossbar ();
      }
      else if (p.x <= (long)(X_OFFSET - 1))
      {
        if (_isselection)
          erase_selection ();
        if (_iscross)
          erase_crossbar ();
        dispatch_e_scroll (win, K_LEFT);
        if (_isselection)
          display_selection ();
        if (_iscross)
          display_crossbar ();
      }
      else if (p.x >= _textcolumns + X_OFFSET)
      {
        if (_isselection)
          erase_selection ();
        if (_iscross)
          erase_crossbar ();
        dispatch_e_scroll (win, K_RIGHT);
        if (_isselection)
          display_selection ();
        if (_iscross)
          display_crossbar ();
      }
      if (_selecting)
      {
        if (in_text (p))
        {
          p.x += (origin ().x - X_OFFSET);
          p.y += (origin ().y - Y_OFFSET);
          _point = p;
          if (_point.y >= _txt.lines ())
            _point.y = _txt.lines () - 1l;
        }
        if (_point != _sel_end)
        {
          erase_selection ();
          _sel_end = _point;
          display_selection ();
        }
      }
    }
    if (_iscross)
    {
      newcross = ep->v.mouse.where;
      if (_cross.h != newcross.h || _cross.v != newcross.v)
      {
        erase_crossbar ();
        _cross = newcross;
        display_crossbar ();
      }
    }
  }
    break;
  case E_SIZE:
    if (is_open ())
    {               
      check_link ();
      erase_point ();
      if (_isselection)
        erase_selection ();
      autoscroll (FALSE);
      _textrows = TEXTROWS;
      _textcolumns = TEXTCOLUMNS;
      autoscroll (TRUE);
      repos_buttons ();
      display_point ();
      force_update ();
      do_events ();
      check_link (&_point);
    }
    break;
  case E_HSCROLL:
  case E_VSCROLL:
  {
    erase_point ();
    tlnk = TRUE;
    switch (ep->v.scroll.what)
    {
    case SC_PAGE_UP:
      if (ep->type == E_VSCROLL)
      {
        if (origin().y > 0)
        {
          kdiff = (int) (_point.y - origin ().y);
          new_origin = origin ().y > _textrows ?
            origin().y - _textrows + 1l : 0;
          _point.y = new_origin + kdiff;
          check_link ();
          update_thumb (origin ().x, new_origin);
          update ();    // ORRIIIBILE!

          check_link (&_point);
        }
        else scroll_error(-1, 0);
      }
      else
      {
        if (origin ().x > 0)
        {
          kdiff = (int) (_point.x - origin ().x);
          new_origin = origin ().x > _textcolumns ?
            origin().x - _textcolumns + 1 : 0;
          _point.x = new_origin + kdiff;
          check_link ();
          update_thumb(new_origin, origin().y);
          update ();    // AAAARGH!
          check_link (&_point);
        }
        else scroll_error(0, -1);
      }
      break;
    case SC_LINE_UP:
      if (ep->type == E_VSCROLL)
      {
        if (origin ().y > 0l)
        {
          _point.y--;
          if (need_paint_sel ())
            erase_selection ();
          check_link ();
          update_thumb (origin().x, origin().y - 1l);
          _need_scroll = down;
        }
        else scroll_error(-1, 0);
      }
      else
      {
        if (origin ().x > 0l)
        {
          if (need_paint_sel (FALSE))
            erase_selection ();
          check_link ();
          update_thumb (origin().x - 1l, origin().y);
          _point.x--;
          _need_scroll = right;
        }
        else scroll_error(0, -1);
      }
      break;
    case SC_PAGE_DOWN:
      if (ep->type == E_VSCROLL)
      {
        if ((origin().y + _textrows) < _txt.lines())
        {
          kdiff = (int) (_point.y - origin ().y);
          new_origin = (_txt.lines () - origin ().y) >
            (_textrows * 2l) ?
              origin ().y + _textrows - 1 : _txt.lines () - _textrows;
          _point.y = new_origin + kdiff;
          check_link ();
          update_thumb (origin ().x, new_origin);
          update ();    // AAAARGH!

          check_link (&_point);
        }
        else scroll_error(-1, _txt.lines()-_textrows/*-1*/);
      }
      else
      {
        if ((origin().x + _textcolumns) < 256)
        {
          kdiff = (int) (_point.x - origin ().x);
          new_origin = (256 - origin ().x) > _textcolumns ?
            origin ().x + _textcolumns - 1 : 256 - _textcolumns;
          _point.x = new_origin + kdiff;
          check_link ();
          update_thumb (new_origin, origin().y);
          update ();    // AAAARGH!

          check_link (&_point);
        }
        else scroll_error(255-_textcolumns, -1);
      }
      break;
    case SC_LINE_DOWN:
      if (ep->type == E_VSCROLL)
      {
        if ((origin().y + _textrows) < _txt.lines ())
        {
          if (need_paint_sel ())
            erase_selection ();
          check_link ();
          update_thumb (origin ().x, origin ().y + 1l);
          _point.y++;
          _need_scroll = up;
        }
        else scroll_error(-1, _txt.lines()-_textrows/*-1*/);
      }
      else
      {
        if ((origin ().x + _textcolumns) < 256)
        {
          if (need_paint_sel (FALSE))
            erase_selection ();
          check_link ();
          update_thumb (origin ().x + 1l, origin ().y);
          _need_scroll = left;
          _point.x++;
        }
        else scroll_error(255-_textcolumns, -1);
      }
      break;
    case SC_THUMB:

      check_link ();
      kdiff_x = (int) (_point.x - origin ().x);
      kdiff_y = (int) (_point.y - origin ().y);

      p.x = ep->type == E_VSCROLL ? origin ().x : ep->v.scroll.pos;
      p.y = ep->type == E_HSCROLL ? origin ().y : ep->v.scroll.pos;

      if ((p.y + _textrows) >= _txt.lines ())
        p.y = _txt.lines () - _textrows -1;
      if ((p.x + _textcolumns) >= 255)
        p.x = 255 - _textcolumns;

      update_thumb (p.x, p.y);

      _point.x = ep->type == E_VSCROLL ? origin ().x :
      origin ().x + kdiff_x;
      _point.y = ep->type == E_HSCROLL ? origin ().y :
      origin ().y + kdiff_y;
      update ();
      check_link (&_point);
      break;

    default:
      break;
    }
    // for failed scrollings
    if (!_selecting && _need_scroll == none)
    {
      check_link (&_point);
      display_point ();
    }
  }
    break;
  default:
    break;
  }
  if (_need_scroll != none)
  {
    const scroll tmp = _need_scroll;
    _need_scroll = none;
    shift_screen (tmp);
    if (!_selecting)
    {
      check_link (&_point);
      display_point ();
    }
    if (_isselection)
      display_selection ();
  }
  return TWindow::handler (win, ep);
}

bool TViswin::on_key(KEY key)
{
  if (_timer)
    return TRUE;
  _timer = xvt_timer_create (win (), 50l);
  
  EVENT_TYPE type = E_USER;
  if (key == K_UP || key == K_DOWN || key == K_LEFT || key == K_RIGHT)
    if (_selflag)
      key += K_SHIFT;

  if (_selecting && key != K_SHIFT_UP && key != K_SHIFT_DOWN
      && key != K_SHIFT_LEFT && key != K_SHIFT_RIGHT)
    _selecting = FALSE;

  switch (key)
  {
  case K_F7:
    find();
    break;
  case K_F3:
    find_next();
    break;
  case CTRL_E:
    if (_isedit)
    {
      check_link ();
      call_editor ();
      set_focus ();
      update ();
      check_link (&_point);
    }
    break;
  case CTRL_G:
    exec_link();
    break;
  case CTRL_C:
    sel_to_clipboard();
    break;
  case CTRL_S:
    if (_isprint)
      stop_run(CTRL_S);
    break;
  case CTRL_P:
    if (_isprint)
      stop_run(CTRL_P);
    break;
  case CTRL_R:
    check_link ();
    force_update ();
    do_events ();
    check_link (&_point);
    break;
  case K_ESC:
    if (_isopen && _toplevel)
      abort_print ();
    else
    {
      xvtil_statbar_set ("");
      xvtil_statbar_refresh ();
      stop_run (K_ESC);
    }
    break;
  case K_ENTER:
    if (_isselection)
    {
      erase_selection ();
      _isselection = FALSE;
    }        
    if (_toplevel)
      exec_link();
    break;
  case K_TAB: 
    if (!_toplevel)
    {
      TMask& m = owner().mask();
      const int pos = m.id2pos(owner().dlg());
      for (int i = pos+1; i < m.fields(); i++)
      {
        const TMask_field& f = m.fld(i);
        if (f.active())
        {
          m.set_focus_field(f.dlg());
          break;
        }
      }
    }
    break;  
  case K_BTAB: 
    if (!_toplevel)
    {
      TMask& m = owner().mask();
      const int pos = m.id2pos(owner().dlg());
      for (int i = pos-1; i >= 0; i--)
      {
        const TMask_field& f = m.fld(i);
        if (f.active())
        {
          m.set_focus_field(f.dlg());
          break;
        }
      }
    }
    break;
  case K_SPACE:
  case K_CTRL_ENTER:
    if (_linkID != -1)
      exec_link();
    break;
  case K_LHOME:
    update_thumb (0, 0);
    _point.set (0, 0);
    check_link (&_point);
    force_update();
    break;
  case K_LEND:
    update_thumb (0, _txt.lines () - _textrows);
    _point.set (0, _txt.lines () - 1);
    check_link (&_point);
    force_update();
    break;
  case K_RIGHT:
  case K_LEFT:
  case K_WRIGHT:
  case K_WLEFT:
  case K_ALT_RIGHT:
  case K_ALT_LEFT:
  case K_SHIFT_RIGHT:
  case K_SHIFT_LEFT:
    type = E_HSCROLL;
    break;
  case K_UP:
  case K_DOWN:
  case K_NEXT:
  case K_PREV:
  case K_CTRL_UP:
  case K_CTRL_DOWN:
  case K_SHIFT_UP:
  case K_SHIFT_DOWN:
    type = E_VSCROLL;
    break;
  case K_F5:
    check_link ();
    erase_point ();
    _isbar = !_isbar;
    force_update ();
    do_events ();
    check_link (&_point);
    break;
  case K_F6:
    _selflag = !_selflag;
    break;
  case '+':
    if (PRINT_HEIGHT <= 14)
    {
      PRINT_HEIGHT += 2;
      force_update();
    }
    break;
  case '-':
    if (PRINT_HEIGHT >= 6)
    {
      PRINT_HEIGHT -= 2;
      force_update();
    }
  default:
    break;
  }

  switch (type)
  {
  case E_HSCROLL:
  case E_VSCROLL:
  {
    erase_point ();
    check_link ();
    switch (key)
    {
    case K_PREV:
      dispatch_e_scroll (win(), K_PREV);
      break;
    case K_NEXT:
      dispatch_e_scroll (win(), K_NEXT);
      break;
    case K_WLEFT:
      dispatch_e_scroll (win(), K_BTAB);
      break;
    case K_WRIGHT:
      dispatch_e_scroll (win(), K_TAB);
      break;
    case K_CTRL_UP:
      dispatch_e_scroll (win(), K_UP);
      break;
    case K_CTRL_DOWN:    
      dispatch_e_scroll (win(), K_DOWN);
      break;
    case K_ALT_LEFT:
      dispatch_e_scroll (win(), K_LEFT);
      break;
    case K_ALT_RIGHT:
      dispatch_e_scroll (win(), K_RIGHT);
      break;
    case K_UP:
    case K_SHIFT_UP:
      if (key == K_SHIFT_UP)
      {
        if (need_paint_sel (FALSE))
          erase_selection ();
        if (!_selecting)
        {
          _sel_start = _point;
          _selecting = TRUE;
        }
      }
      if (_point.y > 0l)
      {
        if (_point.y == origin ().y)
        {
          if (need_paint_sel ())
            erase_selection ();
          update_thumb (origin ().x, --_point.y);
          _need_scroll = down;
        }
        else
          _point.y--;
        if (key == K_SHIFT_UP)
        {
          _sel_end = _point;
          _isselection = TRUE;
        }
      }
      else
        beep ();
      break;
    case K_LEFT:
    case K_SHIFT_LEFT:
      if (_point.x > 0l)
      {
        if (key == K_SHIFT_LEFT)
        {
          if (need_paint_sel (FALSE))
            erase_selection ();
          if (!_selecting)
          {
            _sel_start = _point;
            _selecting = TRUE;
          }
        }
        if (_point.x == origin ().x)
        {
          if (need_paint_sel (FALSE))
            erase_selection();
          update_thumb (--_point.x, origin ().y);
          _need_scroll = right;
        }
        else
          _point.x--;
        if (key == K_SHIFT_LEFT)
        {
          _sel_end = _point;
          _isselection = TRUE;
        }
      }
      else
        beep ();
      break;
    case K_DOWN:
    case K_SHIFT_DOWN:
      if (_point.y < (_txt.lines() - 1))
      {
        if (key == K_SHIFT_DOWN)
        {
          if (need_paint_sel (FALSE))
            erase_selection();
          if (!_selecting)
          {
            _sel_start = _point;
            _selecting = TRUE;
          }
        }
        if (_point.y == origin().y + _textrows - 1)
        {
          if (need_paint_sel())
            erase_selection();
          // check_link();
          update_thumb (origin().x, (++_point.y) - _textrows + 1);
          _need_scroll = up;
        }
        else
          _point.y++;
        if (key == K_SHIFT_DOWN)
        {
          _sel_end = _point;
          _isselection = TRUE;
        }
      }
      else
        beep ();
      break;
    case K_RIGHT:
    case K_SHIFT_RIGHT:
      if (_point.x < 256)
      {
        if (key == K_SHIFT_RIGHT)
        {
          if (need_paint_sel (FALSE))
            erase_selection ();
          if (!_selecting)
          {
            _sel_start = _point;
            _selecting = TRUE;
          }
        }
        if (_point.x == (origin ().x + _textcolumns - 1))
        {
          if (need_paint_sel (FALSE))
            erase_selection ();
          //           check_link();
          update_thumb ((++_point.x) - _textcolumns + 1l, origin ().y);
          _need_scroll = left;
        }
        else
          _point.x++;
        if (key == K_SHIFT_RIGHT)
        {
          _sel_end = _point;
          _isselection = TRUE;
        }
      }
      else
        beep();
      break;
    default:
      break;
    }
    if (_need_scroll != none)
    {
      scroll tmp = _need_scroll;
      _need_scroll = none;
      shift_screen (tmp);
    }
    if (_isselection)
      display_selection ();
    check_link (&_point);
    update();
  }
  break;
default:
  break;
}
  return TWindow::on_key (key);
}

void TViswin::sel_to_clipboard()
{                  
  if (!_isselection) 
    return;

  TPoint p1, p2;
  adjust_selection (p1, p2);
  
  TString_array txt;
  _txt.write(txt, &p1, &p2);
  
  long size = 0l; int eol_len = strlen(EOL_SEQ);
  
  xvt_cb_open(TRUE);  // open clipboard        
  
  int i; 
  for (i = 0; i < txt.items(); i++)
    size += txt.row(i).len() + eol_len; 
  char* p = (char*)xvt_cb_alloc_data(size);  // allocate clipboard  
  if (p == NULL)
  {
    error_box("Impossibile allocare una clipboard di %ld bytes", size);
    return;  
  }
  // put data           
  for (i = 0; i < txt.items(); i++)
  {
    const TString& s = txt.row(i);
		const int slen = s.len();
		int j;
    for (j = 0; j < slen; j++)
      *p++ = s[j];
    for (j = 0; j < eol_len; j++)
      *p++ = EOL_SEQ[j];
  }
  if (!xvt_cb_put_data(CB_TEXT, NULL, size, (PICTURE)NULL))
    error_box("Errore di copiatura del testo nella clipboard");
  
  xvt_cb_free_data();  // free memory    
  xvt_cb_close();      // close clipboard 
}

bool TViswin::call_editor()
{
  TFilename pdf;
  const KEY key = spotlite_ask_name(pdf);
  bool ok = key >= 'A' && key <= 'Z';
  switch (key)
  {
  case 'A':
    ok = printer().print_pdf(_txt, pdf); 
    if (ok)
      spotlite_notify(pdf);
    break;
  case 'X': 
    ok = _txt.write_xls(pdf);
    if (ok)
      ::edit_url(pdf);
    break;
  case 'E': 
    if (_isselection)
    {
      TPoint p1, p2;
      adjust_selection (p1, p2);
      ok = _txt.write (pdf, &p1, &p2);
    }
    else
      ok = _txt.write (pdf, NULL, NULL);
    if (ok)
      ::edit_url(pdf);
    break;
  case 'M':
    ok = printer().print_pdf(_txt, pdf);
    if (ok)
      spotlite_send_mail(pdf);
    break;
  case 'P': 
    ok = printer().print_pdf(_txt, pdf); 
    if (ok)
      xvt_sys_goto_url(pdf, "open");
    break;
  default:
    ok = false;
    break;
  }
  return ok;
}

void TViswin::add_line (const char *l)
{
  if (_isopen && !_frozen)
  {
    if (_txt.frozen ()) // error writing files
    {
      close_print ();
      return;
    }
    _txt.append (l);
    const long tot = _txt.lines();
    const bool visible = (tot - origin().y) <= _textrows;
    if (visible)
    {
      EVENT ev;
      ev.type = E_USER;   
      ev.v.user.id = E_ADDLINE_ONSCREEN;
      xvt_win_dispatch_event (win(), &ev);
    }
    else
       set_scroll_max (MAXLEN - 1, tot-1);
    if ((tot & 0xF) == 0)
      do_events ();
  }
}

void TViswin::destroy_lines()
{
  if (_isopen && !_frozen)
  {
    _txt.destroy();
    force_update();
  }
}

void TViswin::close_print ()
{
  _isopen = FALSE;
  if (_showbuts) 
  {
    xvt_timer_destroy (_wtimer);
    _wtimer = 0;
  }
  if (_toplevel) 
  {
    xvt_menu_set_item_title(win(), M_VISWIN_QUIT, TR("Fine\tESC"));
    enable_menu_item(M_VISWIN_STOP, FALSE); 
    if (_isedit)
      enable_button(DLG_SAVEREC);
    if (_isprint)
    {
      enable_button(DLG_PRINT);
      enable_menu_item(M_VISWIN_PRINT, TRUE);
    }

    // build bookmark menu tree
    _bookmarks = &(printer().get_bookmarks());
    if (_bookmarks->items() > 0)
      build_index_menu();
  }
  
  const TImage* i = (TImage*)_images.objptr(0);
  set_scroll_max (MAXLEN - 1, _txt.lines () <= _textrows ? _txt.lines () : _txt.lines () - _textrows);

  force_update ();
}                       

void TViswin::goto_end()
{
  goto_pos(0l, _txt.lines () - _textrows);
}

void TViswin::goto_top()
{                     
  goto_pos(0l,0l);
}

// @doc INTERNAL

// @mfunc Sposta la visualizzazione di stampa alla posizione indicata
void TViswin::goto_pos(
  long r,               // @parm Riga a cui spostare la visualizzazione di stampa
  long c,               // @parm Colonna a cui spostare la visualizzazione di stampa
  bool moveorigin)      // @parm Indica se occorre spostare l'origine (default TRUE)

  // @xref <mf TViswin::goto_top> e <mf TViswin::goto_end>
{         
  if (r >= _txt.lines() || c >= 256)
    return;           
  check_link();            
  erase_point();
  if (_isselection) erase_selection();
  _point.x = c;
  _point.y = r;
  if (!moveorigin) 
    c = (c > (origin().x + _textcolumns)) ? c - origin().x : 0l;
  if (_txt.lines() > _textrows)
    update_thumb(c,r); 
  check_link(&_point);
  if (_isselection) display_selection();
  display_point();
  refresh();
}

void TViswin::refresh()
{ 
  force_update();
}

// @doc INTERNAL

// @mfunc Funzione di ricerca non interattiva
//
// @rdesc Ritorna la riga in cui e' stata trovato il testo (-1 se non e' stato trovato)
long TViswin::search(
  const char* txt,      // @parm Testo da cercare
  int& pos,             // @parm Intero in cui posizionare la posizione del carattere
  long from,    // @parm Posizione all'interno della riga da cui iniziare la ricerca (default 0)
  bool down,    // @parm Indica se la ricerca va effettuata all'indietro:
  //
  // @flag TRUE | Ricerca dalla posizione indicata all'inizio della riga (default)
  // @flag FALSE | Ricerca dalla posizione indicata alla fine della riga
  bool cs,              // @parm Indica se ricerca il testo con criterio case sensitive (default FALSE)      
  bool regx)            // @parm indica se considerare espressioni regolari (def FALSE)
{
  return _txt.search(txt,pos,from,down, cs, regx);                        
}

// @doc INTERNAL

// @mfunc Funzione di sostituzione di un testo all'interno di una riga non interattiva
//
// @rdesc Ritorna la posizione in cui e' stato sostituito il testo. Ritorna -1 se non e' stata
//        fatto nessuna operazione di sostituzione.
int TViswin::replace(
  long l,               // @parm Numero della riga nella quale sostituire il testo
  const char* txt,      // @parm Testo da inserire nella riga
  int pos,              // @parm Posizione nella quale inserire il testo (default 0)
  int len)              // @parm Lunghezza del testo da sostituire (default -1)

{                   
  if (l == -1l) return -1;
  int ret =  _txt.replace(l,txt,pos,len);
  if (ret != -1)
  {
    TPoint p; p.x = pos; p.y = l; 
  }       
  return ret;
}


void TViswin::find()
{             
  TMask m("bagn005");
  
  m.set(F_STRING, _txt_to_find);
  m.set(F_DIRECT, _down_dir ? "D" : "U");
  m.set(F_CASE,   _case_sensitive ? "X" : "");
  m.set(F_REGEXP, _regexp ? "X" : "");
  
  if (m.run() == K_ENTER)
  {
    _txt_to_find    = m.get(F_STRING);
    _down_dir       = m.get(F_DIRECT) == "D";
    _case_sensitive = m.get_bool(F_CASE);  
    _regexp         = m.get_bool(F_REGEXP);
    
    int x; 
    
    long l = search(_txt_to_find, x, _point.y, _down_dir, _case_sensitive,
                    _regexp);
    if (l == -1l)
    { 
      _last_found.y = -1l;
      beep();
    }
    else 
    {
      goto_pos(l, (long)x, FALSE);
      _last_found.x = (long)x; 
      _last_found.y = l;  
    }
  }
  if (_toplevel) enable_menu_item(M_VISWIN_NEXT, TRUE);
}

void TViswin::find_next()
{ 
  int x;
  
  if (_txt_to_find.empty() || _last_found.y == -1l) 
    beep();
  else
  {
    long l = search(_txt_to_find, x, _point.y+(_down_dir ? 1l : -1l),
                    _down_dir, _case_sensitive, _regexp);
    if (l == -1)
      beep();
    else 
      goto_pos(l,x,FALSE);
  }
}                                     

void TViswin::show_rulers (bool on)
{             
  _rulers   = on;
  autoscroll(FALSE);
  _textrows = TEXTROWS;
  _textcolumns = TEXTCOLUMNS;
  autoscroll(TRUE);
  refresh();
}

// @doc INTERNAL

// @mfunc Mostra/nasconde i bottoni della finestra di anteprima
void TViswin::show_buttons(
  bool on)      // @parm Indica l'operazione da svolgere:
  //
  // @flag TRUE | Mostra i bottoni della finestra (default)
  // @flag FALSE | Nasconde i bottoni della finestra
{ 
  _showbuts = on;
  autoscroll (FALSE);
  _textrows = TEXTROWS;
  _textcolumns = TEXTCOLUMNS;
  autoscroll(TRUE);

  for (int i = 0; i < _button.items(); i++) 
     ((TPushbutton_control&)_button[i]).show(on);
  refresh();
}

TViswin::TViswin(const char *fname,
                 const char *title,
                 bool editbutton,
                 bool printbutton,
                 bool linkbutton, 
                 int x, int y,
                 int height, int width,                  
                 byte rulers,    // overridden by config parms
                 WINDOW parent,
                 TBrowsefile_field* brwfld):
                 TField_window(x, y, width, height, parent, brwfld),
                 _filename (fname), _islink (linkbutton), _isedit (editbutton),
                 _isprint (printbutton), _iscross (FALSE), _selecting (FALSE), _isselection (FALSE),
                 _isbar (FALSE), _scrolling (FALSE), _timer (0), _wtimer(0), _selflag (FALSE), _sel_displayed (FALSE), 
                 _link_displayed (FALSE), _cross_displayed (FALSE), _point_displayed (FALSE), 
                 _need_scroll (none), _txt (fname, BUFFERSIZE), _txt_to_find(64), _in_update(FALSE),
                 _down_dir(TRUE), _case_sensitive(FALSE), _regexp(FALSE), _multiple (FALSE),
                 _frozen (FALSE), _rulers(rulers), _showbuts(FALSE), _menu_present(FALSE), _brwfld(brwfld)
{
  TWait_cursor hourglass;

  if (title == NULL)
    title = fname ? fname : "Anteprima di stampa";
  
  _last_found.y = -1;
  
  _isopen = fname == NULL || *fname <= ' ';
  if (_isopen)
    _filename = _txt.name();

  if (parent == NULL_WIN)
    parent = TASK_WIN; 

  _toplevel = brwfld == NULL; // parent == TASK_WIN;
  
  if (_toplevel)
  {
    // load info from config on buttons and rulers
    TConfig cnf(CONFIG_USER, "Visualizzazione");
    _showbuts = cnf.get_bool("Bottoni",  NULL, -1,TRUE);
    
    // Se rulers vale 3 allora leggi dal config il vero valore
    if (_rulers != 0 && _rulers != 1)
      _rulers = cnf.get_bool("Righelli", NULL, -1, TRUE);  

    PRINT_HEIGHT = cnf.get_int("FontSize", NULL, -1, PRINT_HEIGHT);
  }
	else
		PRINT_HEIGHT = 10;

//  height = rows(); qui
  
  const int larg = 76;
  const int alt = 20;

  RCT r;

  xvt_vobj_get_client_rect (parent, &r);
  int maxlarg = width == 0 ? (r.right / CHARX - 6)  : width;   
  int maxalt = height == 0 ? (r.bottom / CHARY - 6) : height;  
  
  if (_toplevel && larg > maxlarg)
    maxlarg = larg;
  if (_toplevel && alt > maxalt)
    maxalt = alt;

	int i;
  for (i = 0; i < 4; i++)
    _modules.add(new TImage(BMP_MODULE1 + i), i);
  _modules.add(new TImage(BMP_MODULE), i);
	for (i = 0; i < _modules.items(); i++)
		((TImage*)_modules.objptr(i))->convert_transparent_color(MASK_BACK_COLOR);
  
  if (_toplevel) 
  {
    const long flags = WSF_HSCROLL | WSF_VSCROLL | WSF_SIZE;
    WIN_TYPE rt = W_DOC;
    WINDOW myself = create(x, y, maxlarg, maxalt, title, flags, rt, parent,
                         _toplevel ? VISWIN_BAR : 0);
    attach_interface(myself, BACKGROUND);
  }
  else
  {
    // La finestra e' gia' creata automaticamente dal TWindowed_field
  }

  set_opaque_text (TRUE);
  set_font (PRINT_FONT, XVT_FS_NONE, PRINT_HEIGHT);

  if (_toplevel)
  {      
    if (width == 0 && height == 0)        
      maximize();
    enable_menu_item(M_VISWIN_COPY, FALSE);
    enable_menu_item(M_VISWIN_CLEAR, FALSE);
    enable_menu_item(M_SHOW_RULERS, TRUE);
    enable_menu_item(M_SHOW_BUTTONS, TRUE);
    check_menu_item(M_SHOW_RULERS, _rulers != 0);                              
    check_menu_item(M_SHOW_BUTTONS, _showbuts);        
    enable_menu_item(M_VISWIN_PRINT, FALSE);
  }

  if (_txt.lines() > 0l)
    set_scroll_max (MAXLEN - 1, _txt.lines () <= _textrows ?
                    _txt.lines () : _txt.lines () - _textrows);

  if (_toplevel)
  {
    add_button (DLG_QUIT, "", BMP_QUIT, BMP_QUITDN);
    
    if (_isedit)
    {
      add_button(DLG_SAVEREC, BR("~Registra", 8), BMP_SAVEREC, BMP_SAVERECDN);
      disable_button(DLG_SAVEREC);
    }
    if (_islink)
    {
      add_button(DLG_LINK, "", BMP_LINK);
      disable_button(DLG_LINK);
    }             
    if (_isprint)
    {
      add_button (DLG_PRINT, "", BMP_PRINT);
      disable_button(DLG_PRINT);
      enable_menu_item(M_VISWIN_PRINT, FALSE);      
    }
  }
  
  if (_isopen && _showbuts)
    _wtimer = xvt_timer_create(win(), 750L);

  _point.set (0, 0);
  autoscroll (FALSE);
  _textrows = TEXTROWS;
  _textcolumns = TEXTCOLUMNS;
  autoscroll (TRUE);

  _links = _toplevel ? &(printer().links()) : &(_brwfld->_links);
  _multiple = _toplevel ? (printer ().ismultiplelink()) : 
  (_brwfld->is_multiple_link());   
  
  _bg = _toplevel ? &(printer().getbgdesc()) : &_brwfld->get_bg_desc(); 
  _formlen = _toplevel ? printer().formlen() : height;
  _linkID = -1;
  _inside_linkexec = FALSE;

  for (i = 0; i < _links->items (); i++)
  {
    TToken_string & t = (TToken_string &) (*_links)[i];
    char f = *(t.get(1));
    char b = *(t.get(2));
    t.restart();
    _txt.set_hotspots(f, b);
  } 
}

TViswin ::~TViswin ()
{             
  // avoid deleting child window (already deleted by mask)
  if (!_toplevel) 
    set_win(NULL_WIN);
}

///////////////////////////////////////////////////////////
// Campo di visualizzazione sulle maschere
///////////////////////////////////////////////////////////

// Certified 100%
TBrowsefile_field::TBrowsefile_field(TMask* m)
                 : TWindowed_field(m), _lh(NULL), _m_link(FALSE), _background(36)
{}

// Certified 100%
word TBrowsefile_field::class_id() const
{
  return CLASS_BROWSEFILE_FIELD;
}

// Certified 100%
TBrowsefile_field::~TBrowsefile_field()
{
}

// Certified 100%
TField_window* TBrowsefile_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
{                            
  return new TViswin(_ctl_data._prompt, _ctl_data._prompt, FALSE, FALSE, FALSE, 
                     x, y, dy, dx, _flags.rightjust ? TRUE : FALSE, parent, this);
}


long TBrowsefile_field::set_text(const char* file, const char* line)
  // se line != NULL ritorna il numero dell'ultima riga del file che
  // comincia (trimmata) come passato; se non la trova ritorna -1
{       
  long ret = -1; 

  FILE* instr = NULL; fopen_s(&instr, file, "r"); // Secured FILE* instr = fopen(file, "r");
  if (instr != NULL) 
  {
    TWait_cursor hourglass;
  
    TString256 tmpp;
    long lines = 0l;   
  
    while (!feof(instr))
    {
      if (fgets(tmpp.get_buffer(), tmpp.size(), instr) == NULL) 
        break;            
    
      char& last = tmpp[tmpp.len()-1];                        
      if (last == '\n') 
        last = '\0';
    
      add_line(tmpp);
      if (line != NULL)
      {
        if (tmpp.find(line) != -1)
          ret = lines;
      }                   
      lines++;
    }                         
    fclose(instr);         
  }
  vis_win().close_print();                                    
  
  return ret;
}                                  

int TBrowsefile_field::find_link(const char* descr)
{
  for (int i = 0; i < _links.items(); i++)
  {
    TToken_string& tt = (TToken_string&)_links[i];
    const TFixed_string d(tt.get(0));
    if (d == descr) 
      return i;
  }
  return -1;
}

int TBrowsefile_field::enable_link(const char *descr, char fg, char bg)
{     
  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 = _links.add(tt);
    vis_win()._txt.set_hotspots(fg, bg);
  }

  return lnk;
}


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

void TBrowsefile_field::set_background(const char* bg)
{
  printer().parse_background(bg, _background);
} 
  
void TBrowsefile_field::add_line(const char* l)            
{ 
  vis_win().add_line(l);
} 

void TBrowsefile_field::close() 
{ 
  vis_win().close_print(); 
}

void TBrowsefile_field::goto_pos(long r, long c)
{
  vis_win().goto_pos(r,c,TRUE);
}

void  TBrowsefile_field::goto_top()           
{ 
  vis_win().goto_top();  
}

void  TBrowsefile_field::goto_end()           
{ 
  vis_win().goto_end(); 
}                       

void  TBrowsefile_field::refresh()
{
  vis_win().refresh();
}

const char* TBrowsefile_field::get_text(long line, int column, int len)
{ 
  return vis_win()._txt.line(line,(long)column, len); 
}

long TBrowsefile_field::lines()
{
  return vis_win()._txt.lines();
}