#include <applicat.h>
#include <checks.h>
#include <urldefid.h>
#include <utility.h>

#define __WINDOW_CPP
#include <window.h>
#include <colors.h>

#if XVT_OS == XVT_OS_WIN
#define STRICT
#include <windows.h>
#endif

// @doc EXTERNAL

HIDDEN MENU_ITEM* find_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar)
{                                               
  MENU_ITEM* fnd = NULL;  
  MENU_ITEM* mn  = ismbar ? &menu[0] : &menu->child[0];

  for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m])
  {
    fnd = mn->tag == id ? mn : find_menu_item(mn, id, FALSE);
    if (fnd != NULL) break;
  } 
  
  return fnd;
}

HIDDEN bool remove_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar)
{                                               
  MENU_ITEM* mn  = ismbar ? &menu[0] : &menu->child[0];

  for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m])
  {
    if (mn->tag == id) 
    { 
      do    
      {
        xvt_mem_rep((DATA_PTR)mn, (DATA_PTR)(mn+1), sizeof(MENU_ITEM), 1);
        mn++;
      } 
      while (mn->tag != 0); 
      return TRUE;
    }
    else if (remove_menu_item(mn, id, FALSE)) 
      return TRUE;
  }
  
  return FALSE;
}


HIDDEN void set_menu_item(MENU_ITEM& m, TToken_string& tt)
{                        
  MENU_TAG tag  = tt.get_int(0);
  TString  flag = tt.items() <=  2 ? "": tt.get(2);
  char* text = NULL; 
  if (strlen(tt.get(1)) > 0)
  {
    text = (char*)xvt_mem_alloc(strlen(tt.get(1)) + 1);
    strcpy(text, tt.get(1));  
  }

  m.tag       = tag;
  m.text      = text;
  m.enabled   = !(flag.find('D') != -1);
  m.checkable = flag.find('C') != -1 || flag.find('c') != -1;
  m.checked   = flag.find('c') != -1;      
  m.separator = text == NULL;
} 

///////////////////////////////////////////////////////////
// TWindow_manager
///////////////////////////////////////////////////////////

// @class TWindow_manager | Classe per la gestione di un array di finestre modali
class TWindow_manager
{
  // @author:(INTERNAL) Guido

  // @access Private Member
  
  // @ccost MAX_WIN | 8 | Numero massimo di finestre modali
  enum { MAX_WIN = 8 };         

  // @cmember Stack contenente la finestra attiva
  TWindow* _window[MAX_WIN];    
  // @cmember Stack pointer
  char _current;                
  // @cmember Permette di abilitare/disabilitare il menu' della task window (stesso 
  //                                    funzionamento della <mf TMask::enable>)
  void menu_enable(bool) const; 
  
  // @cmember Tiene sempre disponibile un file con un numero di handle inferiore a 20
  //                                    (per sopperire ad una mancanza di XVT)
  FILE* _lowhandle;
  
public:
  // @cmember Costruttore
  TWindow_manager(); 
  // @cmember Distruttore
  ~TWindow_manager();
  
  // @cmember Libera lo spazio per aprire il file (vedi <p _lowhandle>)
  void free_handle();
  // @cmember Occupa lo spazio per aprire il file (vedi <p _lowhandle>)
  void lock_handle();

  // @cmember Registra la finestra <p m> corrente 
  void reg(TWindow* m);
  // @cmember De-registra la finestra corrente
  void unreg(const TWindow* m);
  
  // @cmember Ritorna il puntatore alla finestra corrente
  TWindow* cur_win() const 
  { return (_current < 0) ? NULL : _window[_current]; } 

  // @cmember Chiude tutte le finestre modali aperte
  void destroy();
  // @cmember Ritorna TRUE se la finestra corrente puo' essere chiusa
  bool can_close() const; 
} WinManager;


TWindow_manager::TWindow_manager() : _current(-1), _lowhandle(NULL)
{
  lock_handle();
}

TWindow_manager::~TWindow_manager()
{ 
  destroy(); 
  free_handle();
}  

void TWindow_manager::destroy()
{
  while (_current >= 0)
  {
    TWindow* w = cur_win();
    w->stop_run(K_FORCE_CLOSE);
    w->close_modal();
  }
}

void TWindow_manager::lock_handle()
{
  CHECK(_lowhandle == NULL, "Can't relock low handle");
  _lowhandle = fopen("bagn001a.msk", "r");
  CHECK(_lowhandle != NULL, "Can't lock low handle");
}

void TWindow_manager::free_handle()
{
  CHECK(_lowhandle, "Can't unlock low handle");
  fclose(_lowhandle);
  _lowhandle = NULL;
}


bool TWindow_manager::can_close() const 
{ 
  bool ok = TRUE;
  if (_current >= 0)
    ok = cur_win()->can_be_closed();
  return ok;
}


// Dis/abilitazione del menu principale
HIDDEN void xvt_menu_enable(MENU_ITEM* m, bool on)
{
  while (m->tag)
  {
    switch(m->tag)
    {
    case MENU_FILE:      // Leave it as is
    case -1:             // Separator
      break;             
    default:
      xvt_menu_set_item_enabled(TASK_WIN, m->tag, on);
      break;
    }
    m++;
  }
}


void TWindow_manager::menu_enable(bool on) const
{
  MENU_ITEM *mi = xvt_menu_get_tree(TASK_WIN);
  xvt_menu_enable(mi, on);
  xvt_menu_update(TASK_WIN);
  xvt_res_free_menu_tree(mi);
}


void TWindow_manager::reg(TWindow* m)
{
  _current++;
  CHECK(_current < MAX_WIN, "Too many windows");

  switch (_current)
  {
  case 0 : 
    menu_enable(FALSE);                      
  {                                                                                   
    const bool on = main_app().firm_change_enabled();
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, on);
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, on);
  }
    break;
  case 1 : 
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, FALSE); 
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, FALSE);  
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, FALSE);
  default: 
    _window[_current-1]->deactivate(); break;
  }

  _window[_current] = m;
}


void TWindow_manager::unreg(const TWindow* m)
{
#ifdef DBG
  if (m != cur_win())
  {
    yesnofatal_box("E' successo un casino nel Window Manager");
    return;
  }
#endif

  _current--;
  
  if (_current < 0)
  {
    menu_enable(TRUE);
  }  
  else
  {
    cur_win()->activate();
    
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, _current == 0);
    
    const bool cf = _current == 0 && main_app().firm_change_enabled();
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, cf);
    xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, cf);
    
    xvt_menu_update(TASK_WIN);
    cur_win()->set_focus();
  }     
}

// @func Chiude tutte le finestre aperte
void close_all_dialogs()
{
  WinManager.destroy();
}

// @func Indica se possono essere chiuse le finestre aperte
//
// @rdesc Ritorna il risultato della ricerca:
//
// @flag TRUE | Se le finestre aperte possono essere chiuse
// @flag FALSE | Se le finestre aperte non possono essere chiuse
bool can_close()
{
  return WinManager.can_close();
}

// @func Ritorna l'handle della finestra corrente
//
// @rdesc Restituisce l'handle della finestra corrente.
WINDOW cur_win()

  // @comm Se non esiste una finestra corrente allora ritorna NULL_WIN
{
  const TWindow* w = WinManager.cur_win();
  return w ? w->win() : NULL_WIN;
}

///////////////////////////////////////////////////////////
// TImage
///////////////////////////////////////////////////////////


// Certified 99%
// @mfunc Setta l'immagine e le sue dimensioni
//
// @rdesc Ritorna l'immagine stessa
XVT_IMAGE TImage::set(
  XVT_IMAGE i) // @parm Immagine da settare

  // @comm L'immagine precedente viene cancellata quando viene settata una nuova
{
  if (_image) 
    xvt_image_destroy(_image);
  _image = i;
  if (i)
  {
    _src.left = _src.top = 0;
    xvt_image_get_dimensions(i, &_src.right, &_src.bottom);
    _dst = _src;
  } 
  return _image;
}

// Certified 100%
// @mfunc Legge l'immagine dal file
XVT_IMAGE TImage::load(
  const char* n) // @parm Nome del file contenente l'immagine
{               
  WinManager.free_handle();    
  XVT_IMAGE i = xvt_image_read_bmp((char*)n);
  WinManager.lock_handle();    
  
  if (i != NULL) set(i);
  return i;  
}

// Certified 100%
XVT_IMAGE TImage::load(short id)
{                         
  return set(xvt_res_get_image(id));
}


// Certified 100%
TImage::TImage(const char* n) : _image(NULL)
{
  if (n && *n) load(n);
}

// Certified 100%
TImage::TImage(short id) : _image(NULL)
{
  if (id > 0) load(id);
}

// Certified 90%
TImage::TImage(const TImage& im, short w, short h) : _image(NULL)
{ 
  const XVT_IMAGE_FORMAT fmt = xvt_image_get_format(im._image);
  set(xvt_image_create(fmt, w, h, NULL));
  
  if (ok())
  { 
    if (fmt == XVT_IMAGE_CL8)
    {
      const short colors = xvt_image_get_ncolors(im._image);
      xvt_image_set_ncolors(_image, colors);
      for (short c = 0; c < colors; c++) 
        xvt_image_set_clut(_image, c, xvt_image_get_clut((XVT_IMAGE)im._image, c));
    }    
    xvt_image_transfer(_image, (XVT_IMAGE)im._image, &_src, (RCT*)&im._src);
  }  
}

// Certified 100%
TImage::~TImage()
{     
  if (_image != NULL)                    
    xvt_image_destroy(_image);
}

// Certified 100%
// @mfunc Permette di settare la posizione della figura
void TImage::set_pos(
  int x, // @parm Coordinata x dell'immagine da settare
  int y) // @parm Coordinata y dell'immagine da settare
  
  // @comm Permette di aggiornare il mebro <p _dst> sommandogli i valori                 
  //                     passati con <p x> e <p y>
{
  _dst = _src;
  xvt_rect_offset(&_dst, x, y);
}               

// Certified 100%
void TImage::draw(WINDOW w) const
{   
  xvt_dwin_draw_image(w, _image, (RCT*)&_dst, (RCT*)&_src);
}

// Certified 100%
// @mfunc Permette di gestire il disegno dell'immagine sullo schermo
void TImage::draw(
  WINDOW w,    // @parm Immagine da disegnare
  int x,       // @parm Coordinata x in cui disegnare l'immagine
  int y) const // @parm Coordinata y in cui disegnare l'immagine
  // @parm RCT& | _src | Rettangolo contenente l'immagine da disegnare
  // @parm RCT& | _dst | Rettangolo in cui disegnare l'immagine

  // @syntax void draw(WINDOW w);
  // @syntax void draw(WINDOW w, int x, int y);
  // @syntax void draw(WINDOW w, const RCT& dst);
  // @syntax void draw(WINDOW w, const RCT& dst, const RCT& src);

  // @comm Nel caso utilizzo l'ultima sintassi e' possibile disegnare solo una parte
  //                     dell'immagine, precisamente delle dimensioni <p _dst> se tale parametro e'
  //                     minore di <p _pst>
{                                       
  RCT dst = _src;
  xvt_rect_offset(&dst, x, y);
  xvt_dwin_draw_image(w, _image, &dst, (RCT*)&_src);
}

// Certified 100%
void TImage::draw(WINDOW w, const RCT& dst) const
{
  xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&_src);
}

// Certified 100%
void TImage::draw(WINDOW w, const RCT& dst, const RCT& src) const
{
  xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&src);
}

// Certified 99%
// @mfunc Fa corrispondere la palette della finestra a quella dell'immagine 
void TImage::set_palette(
  WINDOW w) const // @parm Finestra a cui settare la palette
{
  XVT_PALETTE p = xvt_palet_create(XVT_PALETTE_USER, NULL);
  xvt_palet_add_colors_from_image(p, _image);
  xvt_vobj_set_palet(w, p);
  xvt_palet_destroy(p);
}

// Certified 100%
void TImage::set_clut(byte n, COLOR c)
{
  if (xvt_image_get_format(_image) == XVT_IMAGE_CL8)                  
    xvt_image_set_clut(_image, n, c);
}

// Certified 99%
// @mfunc Setta i colori dell'immagine  in modo da renderlo trasparente
void TImage::convert_to_default_colors()

  // @comm Legge nell'immagine i colori CYAN e DARK_CYAN e li setta a seconda del colore
  //                     della finestra per fare in modo di rendere trasparenti tali colori.
{
  if (MASK_BACK_COLOR != COLOR_DKCYAN && xvt_image_get_format(_image) == XVT_IMAGE_CL8)                  
  { 
    short dx, dy; xvt_image_get_dimensions(_image, &dx, &dy);
    for (short y = 0; y < dy; y++) for (short x = 0; x < dx; x++)
    {     
      const COLOR c = xvt_image_get_pixel(_image, x, y);
      switch (c)
      {
      case COLOR_DKCYAN & 0x00FFFFFF:
        xvt_image_set_pixel(_image, x, y, MASK_BACK_COLOR); break;
      case COLOR_CYAN & 0x00FFFFFF:
        xvt_image_set_pixel(_image, x, y, MASK_LIGHT_COLOR); break;
      case COLOR_GRAY & 0x00FFFFFF:
        xvt_image_set_pixel(_image, x, y, MASK_DARK_COLOR); break;
      default:
        break;  
      }
    }
  } 
}

///////////////////////////////////////////////////////////
// TWindow
///////////////////////////////////////////////////////////

DRAW_CTOOLS TWindow::_ct;
bool TWindow::_ctools_saved;


TWindow::TWindow()
: _win(NULL_WIN), _open(FALSE), _modal(FALSE), _active(TRUE),
  _running(FALSE), _pixmap(FALSE), _lastkey(0)
{}


long XVT_CALLCONV1 TWindow::window_handler(WINDOW win, EVENT* ep)
{
  TWindow* w = (TWindow*)xvt_vobj_get_data(win);
  CHECK(w != NULL, "Invalid window");
  w->handler(win, ep);

  return 0L;
}


// @mfunc Crea la finestra
//
// @rdesc Ritorna l'handle della finestra creata
WINDOW TWindow::create(
  short x,           // @parm Coordinata x della finestra
  short y,           // @parm Coordinata y della finestra
  short dx,          // @parm Larghezza della finestra
  short dy,          // @parm Altezza della finestra
  const char* title, // @parm Titolo da assegnare alla finestra (default "")
  long flags,        // @parm Flag della finestra (default WSF_NONE)
  WIN_TYPE wt,       // @parm Tipo di finestra da creare (default W_DOC)
  WINDOW parent,     // @parm Handler della finestra padre (default NULL_WIN)
  int menu)          // @parm Menu' da assegnare alla finestra (default 0)
{
  if (menu == 0) flags |= WSF_NO_MENUBAR;

  if (parent == NULL_WIN) parent = TASK_WIN;
  if (parent == TASK_WIN) flags |= WSF_INVISIBLE;

  _win = xvt_create_window(
    wt,
    x, y, dx, dy,
    title, 
    menu, parent,
    flags, 
    window_handler,
    PTR_LONG(this)
    );

  CHECK(_win, "Can't create a window");

  return _win;
}


TWindow::~TWindow()
{
  if (_win != NULL_WIN)
  {
    xvt_vobj_destroy(_win);
    _win = NULL_WIN;
  }
}


void TWindow::open()
{
  CHECK(win() != NULL_WIN, "Can't open a NULL window");
  xvt_vobj_set_visible(win(), _open = TRUE);
  xvt_scr_set_focus_vobj(win());  
  xvt_vobj_raise(win());          
}


void TWindow::open_modal()
{
  set_modal(TRUE);
  _open = TRUE;
  open();

  WinManager.reg(this);
}


void TWindow::close()
{
  CHECK(_win != NULL_WIN, "Can't close a NULL window");
  xvt_vobj_set_visible(_win, _open = FALSE);
}


void TWindow::close_modal()
{
  WinManager.unreg(this);
  close();
  _open = FALSE;
}

bool TWindow::stop_run(KEY key)
{
  _running = FALSE;
  _lastkey = key;
  return TRUE;
}

bool TWindow::can_be_closed() const
{
  const bool ok = !is_modal();
  if (!ok) 
    error_box("Chiudere la finestra attiva prima di uscire dal programma");
  return ok;
}

// @mfunc Esegue la finestra
//
// @rdesc Ritorna l'ultimo tasto premuto nella finestra
KEY TWindow::run()

  // @comm Se la finestra non era aperta la apre in modo modale
{
  const bool was_open = is_open();

  start_run();
  _running = TRUE;

  if (!was_open) open_modal();
  else  open();

  while (_running)
    do_events();

  if (!was_open) close_modal();

  return last_key();
}

// @mfunc Gestisce l'handler della finestra
void TWindow::handler(
  WINDOW win, // @parm Finestra da gestire
  EVENT* ep)  // @parm Evento da gestire nella finestra
{
  switch(ep->type)
  {
  case E_CLOSE:
    stop_run(K_ESC);
    break;
  case E_UPDATE:
    xvt_dwin_clear(win, NORMAL_BACK_COLOR);
    update();
    break;
  case E_CHAR:
    on_key(e_char_to_key(ep));
    break;
  case E_DESTROY:
    _win = NULL_WIN;
    break;
  default:
    break;
  }
}

void TWindow::on_idle()
{
  // Non c'e' niente da fare qui, ma non si puo' mai sapere
}


TPoint TWindow::size() const
{
  RCT r;
  xvt_vobj_get_client_rect(win() ? win() : TASK_WIN, &r);
  return TPoint(r.right / CHARX, r.bottom / CHARY);
}

WINDOW TWindow::parent() const
{
  return xvt_vobj_get_parent(win());
}


void TWindow::set_focus()
{
  if (_win)
  {
    xvt_scr_set_focus_vobj(_win);
    xvt_vobj_raise(_win);        
  }
}


void TWindow::iconize() const
{
#if XVTWS != WMWS
  HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW);
  ShowWindow(hwnd, SW_MINIMIZE);
#endif
}

void TWindow::maximize() const
{
#if XVTWS != WMWS
  HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW);
  ShowWindow(hwnd, SW_SHOWMAXIMIZED);
#else            
  RCT r; xvt_rect_set(&r, 1,1,79,23);
  xvt_vobj_move(win(),&r);
#endif
}

// @mfunc Attiva/disattiva la finestra
void TWindow::activate(
  bool on) // @parm Indica l'operazione da svolgere sulla finestra:
  //
  // @flag TRUE | Abilita la finestra (default)
  // @flag FALSE | Disabilita la finestra
{
  xvt_vobj_set_enabled(win(), _active = on);
}


void TWindow::set_caption(const char* title)
{
  xvt_vobj_set_title(win(), (char*)title);
}


const char* TWindow::get_caption() const
{
  char* title = &__tmp_string[512];
  xvt_vobj_get_title(win(), title, 80);
  return title;
}


void TWindow::force_update()
{
  if (win() != NULL_WIN)
    xvt_dwin_invalidate_rect(win(), NULL);
}


bool TWindow::save_ctools()
{
  if (_ctools_saved == FALSE)
  {
    xvt_dwin_get_draw_ctools(win(), &_ct);
    return _ctools_saved = TRUE;
  }
  return FALSE;
}


bool TWindow::restore_ctools()
{
  if (_ctools_saved)
  {
    xvt_dwin_set_draw_ctools(win(), &_ct);
    _ctools_saved = FALSE;
    return TRUE;
  }
  return FALSE;
}


void TWindow::set_color(COLOR fore, COLOR back)
{
  xvt_dwin_set_fore_color(win(), fore);
  xvt_dwin_set_back_color(win(), back);
}


// @mfunc Sceglie la penna da utilizzare nella finestra
void TWindow::set_pen(
  COLOR color,     // @parm Colore della penna
  int width,       // @parm Larghezza del tratto (default 1)
  PAT_STYLE pat,   // @parm Stile del pattern (default PAT_SOLID)
  PEN_STYLE style) // @parm Stile della penna (default P_SOLID)
{
  CPEN pen;

  pen.width = width;
  pen.pat   = pat;
  pen.style = style;
  pen.color = color;

  xvt_dwin_set_cpen(win(), &pen);
}


void TWindow::hide_pen()
{
  xvt_dwin_set_std_cpen(win(), TL_PEN_HOLLOW);
}


void TWindow::set_brush(COLOR color, PAT_STYLE pat)
{
  CBRUSH brush = { pat, color };
  xvt_dwin_set_cbrush(win(), &brush);
}


void TWindow::hide_brush()
{
  CBRUSH brush = { PAT_HOLLOW, COLOR_WHITE };
  xvt_dwin_set_cbrush(win(), &brush);
}


HIDDEN void swap(short& a, short& b)
{
  short tmp = a;
  a = b;
  b = tmp;
}


// @mfunc Disegna un rettangolo con la possibilita' di settare la penna e il draw_mode
void TWindow::frame(
  short left,   // @parm Lato sinistro del rettangolo
  short top,    // @parm Lato superiore del rettangolo
  short right,  // @parm Lato destro del rettangolo
  short bottom, // @parm Lato inferiore del rettangolo
  int flag)     // @parm Flag da assegnare per il disegno del rettangolo

  // @comm Se <p left> <gt> <p right> oppure <p top> <gt> <p bottom> i valori
  //       vengono scambiati per permettere il disegno corretto del rettangolo
{
  if (left > right) swap(left, right);
  if (top > bottom) swap(top, bottom);

  const bool saved = flag && save_ctools();

  if (flag & 1) hide_pen();
  if (flag & 2) hide_brush();
  if (flag & 4)
  {
    set_mode(M_XOR);
    set_brush(COLOR_BLACK);     // Needed for Windows
  }

  const PNT f = log2dev(left,top);
  const PNT t = log2dev(right,bottom);
  RCT r;
  r.top = f.v; r.left = f.h;
  r.bottom = t.v; r.right = t.h;

#if XVTWS != WMWS
  if (flag & 2)
  {
    r.left += CHARX>>1; r.top += CHARY>>1;
    r.right-= CHARX>>1; r.bottom -= CHARY>>1;
  }
#endif

  xvt_dwin_draw_rect(win(), &r);

  if (saved) restore_ctools();
}


void TWindow::rect(short left, short top, short right, short bottom)
{
  frame(left, top, right, bottom, 2);
}


void TWindow::bar(short left, short top, short right, short bottom)
{
  frame(left, top, right, bottom, 1);
}


void TWindow::invert_bar(short left, short top, short right, short bottom)
{
  frame(left, top, right, bottom, 5);
}

// @mfunc Setta opaque_text
void TWindow::set_opaque_text(
  bool o) // @parm Indica la modalita' di scrittura dell'opaque_text
{
  // @comm Quando <p o> e' FALSE il testo viene scritto in modo trasparente
  DRAW_CTOOLS ct;
  xvt_dwin_get_draw_ctools(win(), &ct);
  ct.opaque_text = o;
  xvt_dwin_set_draw_ctools(win(), &ct);
}


// @mfunc Sceglie il font da utilizzare nella finestra
void TWindow::set_font(
  const char* family, // @parm Famiglia di appartenenza del font (default XVT_FFN_FIXED)
  int style,          // @parm Stile del font (default 0)
  int dim)            // @parm Dimensione del font (default 0)
{
  xvt_set_font(win(), family, style, dim);
}

PNT TWindow::log2dev(long x, long y) const
{
  PNT pnt;

  pnt.h = (int)x;
  pnt.v = (int)y;

  if (!_pixmap)
  {
    pnt.h *= CHARX;
    pnt.v *= CHARY;
  }
  
  return pnt;
}

TPoint TWindow::dev2log(const PNT& p) const
{
  TPoint pnt(_pixmap ? p.h : p.h/CHARX, _pixmap ? p.v : p.v/CHARY);
  return pnt;
}


void TWindow::stringat(short x, short y, const char* str)
{
  PNT pnt = log2dev(x,y);
#if XVTWS != WMWS
  pnt.v += BASEY;
#endif

  xvt_dwin_draw_text(win(), pnt.h, pnt.v, (char *)str, -1);
}

// @mfunc Scrive il testo formattato nella finestra all posizione indicata
void TWindow::printat(
  short x,         // @parm Coordinata x della finestra in cui scrivere il testo
  short y,         // @parm Coordinata y della finestra in cui scrivere il testo
  const char* fmt, // @parm Formato che deve essere dato al testo
  ...)             // @parmvar Uno o piu' parametri corrispondenti ai codici in <p fmt>
{
  va_list argptr;
  va_start(argptr, fmt);
  vsprintf(__tmp_string, fmt, argptr);
  va_end(argptr);
  stringat(x, y, __tmp_string);
}


void TWindow::line(short x0, short y0, short x1, short y1)
{
  PNT f = log2dev(x0,y0);
  PNT t = log2dev(x1,y1);

#if XVTWS != WMWS
  if (f.h == 0) f.h = -CHARX; else f.h += CHARX>>1;
  if (f.v == 0) f.v = -CHARY; else f.v += CHARY>>1;
  if (t.h == 0) t.h = -CHARX; else t.h += CHARX>>1;
  if (t.v == 0) t.v = -CHARY; else t.v += CHARY>>1;
#endif

  xvt_dwin_draw_set_pos(win(), f);
  xvt_dwin_draw_line(win(), t);
}

void TWindow::icon(short x0, short y0, int iconid)
{
#if XVTWS == WMWS
  bar(x0, y0, x0+1, y0+1);
#else
  PNT f = log2dev(x0,y0);
  if (iconid < 0) iconid = ICON_RSRC;
  xvt_dwin_draw_icon(win(), f.h, f.v, iconid);
#endif
}

void TWindow::clear(COLOR color)
{ xvt_dwin_clear(win(), color); }

void TWindow::set_mode(DRAW_MODE mode)
{ xvt_dwin_set_draw_mode(win(), mode); }

// @mfunc Aggiunge voci di menu' durante l'esecuzione di una finestra
//
// @rdesc Ritorna se e' riuscito ad aggiungere la voce al menu':
//
// @flag TRUE | Se ha ggiunto la voce
// @flag FALSE | Se non ha ggiunto la voce
bool TWindow::add_menu(
  TString_array& menu, // @parm Array da aggiungere al menu'
  MENU_TAG id,         // @parm Identificatore del menu' a cui aggiungere le voci
  bool force)          // @parm Mai usato
  
  // @comm Nel caso <p id> sia uguale a 0 la voce veine aggiunte nella barra dei menu'
{ 
  CHECK(menu.items() > 0, "TWindow::add_menu: no menus to add");

  // get menu tree
  MENU_ITEM* menubar = xvt_menu_get_tree(win());
  
  if (id == 0)  // add to menubar
  {
    // count menus
    for (int nmen = 0; menubar[nmen].tag != 0; nmen++);           
    menubar = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)menubar, 
                                          sizeof(MENU_ITEM)*(nmen+menu.items()+1));
    // zero new
    xvt_mem_rep((DATA_PTR)&menubar[nmen], "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1));

    // add new menus
    for (int i = 0; i < menu.items(); i++)
      set_menu_item(menubar[nmen+i], menu.row(i));
  }   
  else  // add to menu
  {
    MENU_ITEM* father = find_menu_item(menubar, id, TRUE);
    CHECK(father != NULL, "TWindow::add_menu: you're adding to a NULL menu item");
    
    // count children
    for (int nmen = 0; father->child != NULL && father->child[nmen].tag != 0; nmen++);           
    father->child = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)father->child, 
                                                sizeof(MENU_ITEM)*(nmen+menu.items()+1));
    // zero new
    xvt_mem_rep((DATA_PTR)&(father->child[nmen]), "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1));

    // add new menus
    for (int i = 0; i < menu.items(); i++)
      set_menu_item(father->child[nmen+i], menu.row(i));    
  }
  
  xvt_menu_set_tree(win(), menubar);              
  xvt_res_free_menu_tree(menubar);
  
  return TRUE;
}

bool TWindow::remove_menu(MENU_TAG id)
{
  MENU_ITEM* menubar = xvt_menu_get_tree(win());
  if (remove_menu_item(menubar, id, TRUE))
  {
    xvt_menu_set_tree(win(),menubar);
    xvt_res_free_menu_tree(menubar);
  }                                 
  return TRUE;
}                                                          


///////////////////////////////////////////////////////////
// TTemp_window
///////////////////////////////////////////////////////////

TTemp_window::TTemp_window(WINDOW w)
{
  set_win(w);
}

TTemp_window::~TTemp_window()
{
  set_win(NULL_WIN);    // I don't want to be closed!
}

///////////////////////////////////////////////////////////
// TScroll_window
///////////////////////////////////////////////////////////

TScroll_window::TScroll_window()
: _origin(0, 0), _max(0, 0), _shift(0), _autoscroll(TRUE),
  _has_hscroll(TRUE), _has_vscroll(TRUE)
{
}

WINDOW TScroll_window::create(short x, short y, short dx, short dy,
                              const char* title, long flags, WIN_TYPE wt, WINDOW parent, int menu)
{
  _has_hscroll = (flags & WSF_HSCROLL) != 0;
  _has_vscroll = (flags & WSF_VSCROLL) != 0 ;
  return TWindow::create(x, y, dx, dy, title, flags, wt, parent, menu);
}

PNT TScroll_window::log2dev(long x, long y) const
{
  if (_autoscroll)
  {
    x -= _origin.x;
    y -= _origin.y >> _shift;
  }
  return TWindow::log2dev(x,y);
}


void TScroll_window::set_scroll_max(long maxx, long maxy)
{
  if (_has_hscroll && maxx >= 0)
  {
    _max.x = maxx;
    xvt_sbar_set_range(win(), HSCROLL, 0, int(maxx));
  }
  if (_has_vscroll && maxy >= 0)
  {
    _shift = 0;
    while ((maxy >> _shift) > 0x7FFF) _shift++;
    _max.y = maxy;
    xvt_sbar_set_range(win(), VSCROLL, 0, int(maxy >> _shift));
  }
}

// @mfunc Aggiorna la scrollbar
void TScroll_window::update_thumb(
  long x, // @parm Indica la posizione in x in cui spostare la finestra (default -1)
  long y) // @parm Indica la posizione in x in cui spostare la finestra (default -1)
  
  // @comm Nel caso si voglia aggiornare solamente una coordinata l'altra dovra' essere
  //                     settata a -1     
{
  if (x >= 0 && x <= _max.x) _origin.x = x;
  if (y >= 0 && y <= _max.y) _origin.y = y;

  if (_has_hscroll)
    xvt_sbar_set_pos(win(), HSCROLL, int(_origin.x));
  if (_has_vscroll)
    xvt_sbar_set_pos(win(), VSCROLL, int(_origin.y >> _shift));
}

void TScroll_window::handler(WINDOW win, EVENT* ep)
{
  bool up = FALSE;

  switch (ep->type)
  {
  case E_HSCROLL:
  case E_VSCROLL:
  {
    long& pos = (ep->type == E_HSCROLL) ? _origin.x : _origin.y;
    long& max = (ep->type == E_HSCROLL) ? _max.x : _max.y;
    short pag = (ep->type == E_HSCROLL) ? columns()/2+1 : rows()/2+1;
    switch(ep->v.scroll.what)
    {
    case SC_PAGE_UP:
      pos -= pag;
      up = TRUE;
      break;
    case SC_LINE_UP:
      pos--;
      up = TRUE;
      break;
    case SC_PAGE_DOWN:
      pos += pag;
      up = TRUE;
      break;
    case SC_LINE_DOWN:
      pos++;
      up = TRUE;
      break;
    case SC_THUMB:
      pos = ep->v.scroll.pos;
      up = TRUE;
      break;
    default:
      break;
    }
    if (pos < 0) pos = 0;
    if (pos > max) pos = max;
  }
  break;
default:
  break;
}

  if (up)
  {
    update_thumb();
    force_update();
  }

  TWindow::handler(win, ep);
}

bool TScroll_window::on_key(KEY key)
{
  switch(key)
  {
  case K_LHOME:
    update_thumb(0,0);
    force_update();
    break;
  case K_LEND:
    update_thumb(0,range().y);
    force_update();
    break;
  case K_TAB:
    update_thumb(origin().x+8);
    force_update();
    break;
  case K_BTAB:
  {
    long x = origin().x-8;
    if (x < 0) x = 0;
    update_thumb(x);
    force_update();
  }
    break;
  case K_UP:
  case K_DOWN:
  case K_PREV:
  case K_NEXT:
  case K_LEFT:
  case K_RIGHT:
    dispatch_e_scroll(win(), key);
    break;
  default:
    break;
  }

  return TWindow::on_key(key);
}

