#include <colors.h>            
#include <tree.h>
#include <urldefid.h>
#include <image.h>

///////////////////////////////////////////////////////////
// Callbacks
///////////////////////////////////////////////////////////

HIDDEN bool callback_compare_node(TTree& node, void* jolly, word when)
{
  const TString& id = *((TString*)jolly);
  TString cur_id; node.curr_id(cur_id);
  return id == cur_id;
}

HIDDEN bool callback_expand_node(TTree& node, void* jolly, word when)
{             
  if (when == SCAN_PRE_ORDER)
    node.expand();
  return false;
}

HIDDEN bool callback_find_father(TTree& node, void* jolly, word when)
{             
  TString_array& father_and_son = *(TString_array*)jolly;
  TString myself; node.curr_id(myself);
  
  if (when == SCAN_PRE_ORDER)
  {
    if (myself == father_and_son.row(1))
      father_and_son.add(myself, 0);
  }    
  else
  {
    if (father_and_son.objptr(0))
    {
      father_and_son.add(myself, 0);
      return true;
    }
  }
  
  return false;
}

HIDDEN bool callback_find_brother(TTree& node, void* jolly, word when)
{             
  TString_array& brother_and_sister = *(TString_array*)jolly;
  TString myself; node.curr_id(myself);
  
  if (when == SCAN_PRE_ORDER)
  {
    if (myself == brother_and_sister.row(1))
      brother_and_sister.add(myself, 0);
  }    
  else
  {
    if (brother_and_sister.objptr(0))
    {
      brother_and_sister.add(myself, 0);
      return true;
    }
  }
  return false;
}

///////////////////////////////////////////////////////////
//  TTtree
///////////////////////////////////////////////////////////

bool TTree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags)
{ 
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;
                       
  bool test_myself = true;                     
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;

  TString myself;
  curr_id(myself);

  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))  
      return true;
    
    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_depth_first(nh, jolly, flags))
        return true;
      goto_node(myself);  
    }

    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))  
      return true;
  }

  if (goto_rbrother())
  {
    if (scan_depth_first(nh, jolly, flags))
      return true;
    goto_node(myself);  
  }
  
  if (test_myself)
  {
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))  
      return true;
  }
  
  return false;
}

bool TTree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags)
{          
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;                     
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;
  
  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))  
      return true;
  }

  TString myself;
  curr_id(myself);

  if (goto_rbrother())
  {
    if (scan_breadth_first(nh, jolly, flags))
      return true;
    goto_node(myself);  
  }
  
  if (test_myself)
  {
    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))  
      return true;
  
    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_breadth_first(nh, jolly, flags))
        return true;
      goto_node(myself);  
    }    
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))  
      return true;
  }

  return false;
}

bool TTree::has_root() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_root();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::has_father() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_father();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::goto_father()
{
  TString myself; curr_id(myself);
  bool ok = goto_root();
  if (ok)
  {
    TString_array father_and_son;
    father_and_son.add(myself, 1);
    ok = scan_breadth_first(callback_find_father, &father_and_son, 
                            SCAN_PRE_ORDER | SCAN_IN_ORDER);
  }                     
  if (!ok)
    goto_node(myself);
  return ok;
}

bool TTree::has_lbrother() const
{
  TString myself; curr_id(myself);
  bool ok = ((TTree*)this)->goto_lbrother();
  if (ok)
    ((TTree*)this)->goto_node(myself);
  return ok;
}

bool TTree::goto_lbrother()
{
  TString myself; curr_id(myself);
  bool ok = goto_root();
  if (ok)
  {
    TString_array brother_and_sister;
    brother_and_sister.add(myself, 1);
    ok = scan_depth_first(callback_find_brother, &brother_and_sister, 
                          SCAN_PRE_ORDER | SCAN_POST_ORDER);
  }
  if (!ok)
    goto_node(myself);
  return ok;
}

bool TTree::expanded() const
{ 
  TString str;                
  curr_id(str);
  bool yes = _expanded.is_key(str);
  return yes;
}

bool TTree::expand()
{          
  bool ok = enabled() && has_son();
  if (ok)
  {
    TString str; curr_id(str);
    ok = !_expanded.is_key(str);
    if (ok)
      _expanded.add(str);
  }
  return ok;
}

bool TTree::shrink()
{         
  TString str;
  curr_id(str);
  bool ok = _expanded.is_key(str);
  if (ok)
    _expanded.remove(str);
  return ok;
}

bool TTree::expand_all()
{                     
  bool ok = goto_root();
  if (ok)
    scan_breadth_first(callback_expand_node, NULL);
  return ok;  
}

bool TTree::shrink_all()
{        
  _expanded.destroy();  
  return goto_root();
}


TImage* TTree::get_res_image(short bmp_id) const
{
  TImage* bmp = (TImage*)_image.objptr(bmp_id);
  if (bmp == NULL)
  {                         
    bmp = new TImage(bmp_id);
    if (bmp->ok())
    {
      bmp->convert_transparent_color(NORMAL_BACK_COLOR);
      ((TTree*)this)->_image.add(bmp, bmp_id);
    }
    else
    {
      delete bmp;
      bmp = NULL;
    }  
  }
  return bmp;
}

TImage* TTree::image(bool selected) const
{                
  short bmp_id = BMP_FILE;
  if (has_son()) 
    bmp_id = selected ? BMP_DIRDN : BMP_DIR;
  return get_res_image(bmp_id);
}


///////////////////////////////////////////////////////////
//  TBidirectional_tree
///////////////////////////////////////////////////////////

bool TBidirectional_tree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags)
{                             
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;
                                                     
  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))  
      return true;
    
    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_depth_first(nh, jolly, flags))
        return true;
      goto_father();  
    }    

    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))  
      return true;
  }

  if (goto_rbrother())
  {
    if (scan_depth_first(nh, jolly, flags))
      return true;
    goto_lbrother();  
  }
  
  if (test_myself)
  {
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))  
      return true;
  }
  
  return false;
}

bool TBidirectional_tree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags)
{                             
  if ((flags & 0x7) == 0)
    flags |= SCAN_PRE_ORDER;

  bool test_myself = true;
  if ((flags & SCAN_IGNORING_LEAVES) && !has_son())
    test_myself = false;
  
  if (test_myself)
  {
    if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER))  
      return true;
  }
  
  if (goto_rbrother())
  {
    if (scan_breadth_first(nh, jolly, flags))
      return true;
    goto_lbrother();  
  }
  
  if (test_myself)
  {
    if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER))  
      return true;
    const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded();
    if (!stop && goto_firstson())
    {
      if (scan_breadth_first(nh, jolly, flags))
        return true;
      goto_father();  
    }    
    if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER))  
      return true;
  }
  
  return false;
}

bool TBidirectional_tree::goto_node(const TString &id)
{         
  bool ok = goto_root();
  if (ok && id.not_empty())
    ok = scan_breadth_first(callback_compare_node, (void *)&id);
  return ok;  
}

///////////////////////////////////////////////////////////
// TObject tree
///////////////////////////////////////////////////////////

bool TObject_tree::expanded() const
{
  bool ok = _current && _current->_expanded;
  return ok;
}

bool TObject_tree::expand()
{
  bool ok = _current && _current->_son && !_current->_expanded && enabled();
  if (ok)
    _current->_expanded = true;
  return ok;
}

bool TObject_tree::shrink()
{
  bool ok = _current && _current->_expanded;
  if (ok)
    _current->_expanded = false;
  return ok;
}

void TObject_tree::node2id(const TObject* node, TString& id) const
{
  id.format("%p", (const void*)node);
}

bool TObject_tree::goto_node(const TString& node)
{          
  void* p = NULL;
  if (node.not_empty())
  {
#ifdef DBG_TREE  
    if (!_expanded.is_key(node)) // Usa l'assoc array per testare i nodi validi!
    {
      NFCHECK("Invalid node: %s", (const char*)node);
      return goto_root();
    }
#endif
    sscanf(node, "%p", &p);
  }

  bool ok = true;
  if (p == NULL)
    ok = goto_root();
  else
    _current = (TTree_node*)p;
  return ok;
}

bool TObject_tree::goto_root()
{
  _current = _root;
  return _root != NULL;
}

bool TObject_tree::goto_firstson()
{
  TTree_node* n = _current ? _current->_son : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_rbrother()
{
  TTree_node* n = _current ? _current->_rbrother : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_father()
{
  TTree_node* n = _current ? _current->_father : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::goto_lbrother()
{
  TTree_node* n = _current ? _current->_lbrother : NULL;
  if (n) _current = n;
  return n != NULL;
}

bool TObject_tree::set_object(TObject* obj)
{
  if (_current)
  {
#ifdef DBG_TREE        
    TString str; curr_id(str);
    _expanded.add(str); // Usa l'assoc array per memorizzare i nodi validi!
#endif  
    if (_current->_obj)
      delete _current->_obj;
    _current->_obj = obj;  
  }
  return _current != NULL;
}

bool TObject_tree::set_object(const TObject& obj)
{              
  bool ok = false;
  if (_current)
  {
    TObject* ptr = obj.dup();
    ok = set_object(ptr);
    if (!ok)
      delete ptr;
  }
  return ok;  
}

bool TObject_tree::create_root()
{
  if (!has_root())
    _root = _current = new TTree_node;
  return goto_root();  
}

bool TObject_tree::add_son(TObject* obj)
{
  bool ok = false;
  if (_root)
  {                   
    TTree_node*& curson = _current->_son;
    TTree_node* newson = new TTree_node;
    newson->_father = _current;
    if (curson != NULL)
    {
      curson->_lbrother = newson;
      newson->_rbrother = curson;
    }
    curson = newson;
    ok = goto_firstson();
  }
  else
    ok = create_root();

  if (ok)
    ok = set_object(obj);
  return ok;
}

bool TObject_tree::add_son(const TObject& obj)
{   
  TObject* ptr = obj.dup();
  bool ok = add_son(ptr);
  if (!ok) delete ptr;
  return ok;
} 

bool TObject_tree::add_brother(TObject* obj)
{   
  bool ok = false;
  if (goto_father())
    ok = add_son(obj);
  else
    ok = create_root() && set_object(obj);
  return ok;
}

bool TObject_tree::add_brother(const TObject& obj)
{   
  TObject* ptr = obj.dup();
  bool ok = add_brother(ptr);
  if (!ok) delete ptr;
  return ok;
} 

bool TObject_tree::add_rbrother(TObject* obj)
{     
  bool ok = false;
  if (has_father())
  { 
    TTree_node* newbrother = new TTree_node;
    newbrother->_father    = _current->_father;
    newbrother->_lbrother  = _current;
    newbrother->_rbrother  = _current->_rbrother;
    _current->_rbrother    = newbrother;
    ok = goto_rbrother();
  }
  else
    ok = create_root();

  if (ok)
    set_object(obj);

  return ok;
}

bool TObject_tree::add_rbrother(const TObject& obj)
{   
  TObject* ptr = obj.dup();
  bool ok = add_rbrother(ptr);
  if (!ok) delete ptr;
  return ok;
} 

bool TObject_tree::add_lbrother(TObject* obj)
{     
  bool ok = false;
  if (has_father())
  { 
    if (_current->_lbrother == NULL)
      ok = add_brother(obj);
    else
    {  
      TTree_node* newbrother = new TTree_node;
      newbrother->_father    = _current->_father;
      newbrother->_rbrother  = _current;
      newbrother->_lbrother  = _current->_lbrother;
      _current->_lbrother    = newbrother;
      ok = goto_lbrother();
    }    
  }
  else
    ok = create_root();
  if (ok)
    set_object(obj);
  return ok;
}

bool TObject_tree::add_lbrother(const TObject& obj)
{   
  TObject* ptr = obj.dup();
  bool ok = add_lbrother(ptr);
  if (!ok) delete ptr;
  return ok;
} 

bool TObject_tree::kill_node()
{
  bool ok = false;
  if (_current)
  { 
    TTree_node* cur = _current;
    while (goto_firstson())
    {
      kill_node();
      _current = cur;
    }
    if (_current->_lbrother)
    {
      _current->_lbrother->_rbrother = _current->_rbrother;
    }  
    else
    {
      if (_current->_father)
        _current->_father->_son = _current->_rbrother;
    }  
    if (_current->_rbrother)
      _current->_rbrother->_lbrother = _current->_lbrother;

#ifdef DBG_TREE
    TString id; curr_id(id);
    _expanded.remove(id);
#endif    

    TTree_node* to_be_killed = _current;

    if (_current == _root)
       _root = NULL;
    
    if (_current->_rbrother)
      _current = _current->_rbrother; 
    else  
    {
      if (_current->_lbrother)
        _current = _current->_lbrother; 
      else
        _current = _current->_father;  
    }

    delete to_be_killed;

    ok = true;  
  }
  return ok;
}

bool TObject_tree::get_description(TString& str) const
{
  TObject* obj = curr_node();
  if (obj)
    str << *obj;
  return obj != NULL;
}

TObject_tree::TObject_tree()
{
  _root = _current = NULL;
}

TObject_tree::~TObject_tree()
{
  if (goto_root())
    kill_node();
}         

///////////////////////////////////////////////////////////
// TString_tree
///////////////////////////////////////////////////////////

bool TString_tree::get_description(TString& str) const
{
  const TString* obj = (const TString*)curr_node();
  if (obj) 
    str = *obj;
  else
    str.cut(0);
  return obj != NULL;  
}

///////////////////////////////////////////////////////////
// TNode_info
///////////////////////////////////////////////////////////

class TNode_info : public TSortable
{
public:
  bool _valid      : 1;
  bool _enabled    : 1;
  bool _expandable : 1;
  bool _expanded   : 1;
  TString _id;
  long _plusx;
  long _startx;
  long _endx;
  
  const TNode_info& operator =(const TNode_info& ni)
  { 
    _valid = ni._valid;
    _enabled = ni._enabled;
    _expanded = ni._expanded;
    _expandable = ni._expandable;
    _id = ni._id; 
    _plusx = ni._plusx; 
    _startx = ni._startx; 
    _endx = ni._endx; 
    return ni;
  }
  
  virtual int compare(const TSortable& s) const
  {                  
    const TNode_info& ni = (const TNode_info&)s;
    return _id.compare(ni._id);
  }
  
  virtual bool ok() const { return _valid; }
  void reset() { _valid = false; }
  
  TNode_info() : _valid(false) { }
  virtual ~TNode_info() { }
};

class TNode_info_array : public TObject
{
  TArray _data;

public:
  int last() const;
  int find(const TNode_info& ni) const;
  TNode_info& operator[](int index);
  const TNode_info& operator[](int index) const 
  { return (const TNode_info&)_data[index]; }
  void reset();
};

TNode_info& TNode_info_array::operator[](int index)
{      
  CHECKD(index >= 0, "Bad index ", index);
  TNode_info* ni = (TNode_info*)_data.objptr(index);
  if (ni == NULL)
  {
    ni = new TNode_info;
    _data.add(ni, index);
  }
  return *ni;
}

void TNode_info_array::reset()
{
  for (int i = _data.last(); i >= 0; i = _data.pred(i))
    ((TNode_info*)_data.objptr(i))->reset();
}

int TNode_info_array::last() const
{
	int i;
  for (i = _data.last(); i >= 0; i = _data.pred(i))
    if (_data[i].ok())
      break;
  return i;
}

int TNode_info_array::find(const TNode_info& ni) const
{
	int i;
	for (i = last(); i >= 0; i = _data.pred(i))
  {   
    if (((TNode_info*)_data.objptr(i))->compare(ni) == 0)
      break;
  }
  return i;
}


///////////////////////////////////////////////////////////
// TTree_window
///////////////////////////////////////////////////////////

#include <maskfld.h>

class TTree_window : public TField_window
{   
  TTree* _tree;
  bool _hide_leaves;
  long _first_line;        // Prima riga disegnata
  TNode_info_array _node_info;
  TNode_info _curr_info;   // Nodo attualmente selezionato
  TAuto_token_string _header;
  int _headlines;

  /*static*/ int _row_height;
  static bool _tree_locked;

protected: // TWindow
  virtual void update();
  virtual bool on_key(KEY key);
  virtual void handler(WINDOW win, EVENT* ep);

	int row_height() const { return _row_height; }
  PNT log2dev(long x, long y) const;
  TPoint dev2log(const PNT& p) const;

protected: // Internal use
  static bool callback_draw_node(TTree& node, void* jolly, word);
  void update_header();
  void draw_plus_minus();
  
  int info2index(const TNode_info& ni) const;
  bool index2info(long index, TNode_info& ni);
  
public:        
  TTree* tree() const { return _tree; }
  void set_tree(TTree* tree) { _tree = tree; _first_line = -1; }
  void hide_leaves(bool yes) { _hide_leaves = yes; }
  bool select_current();
  bool goto_selected();
  void set_header(const char* head);
  void set_row_height(int rh);



  TTree_window(int x, int y, int dx, int dy, 
               WINDOW parent, TTree_field* owner);
  virtual ~TTree_window() { }
};

/* int TTree_window::_row_height = CHARY+2; */
bool TTree_window::_tree_locked = false;

const int TABX = 3;

struct TUpdate_info
{                  
  TTree_window* _win;
  TToken_string _str;
  long _x, _y;
  long _firsty, _lasty;
  long _jolly;
  int _headlines;
  TNode_info_array* _node_info;
  TNode_info* _curr_info;
};


PNT TTree_window::log2dev(long x, long y) const
{
  if (_headlines > 0)
    y += _headlines;

  if (autoscrolling())
  {      
    if (_pixmap)
    {
      x -= origin().x * CHARX;
      y -= origin().y * _row_height;
    }
    else
    {
      x -= origin().x;
      y -= origin().y;
    } 
  }

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

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

TPoint TTree_window::dev2log(const PNT& dp) const
{
  PNT p = dp;
  
  if (_headlines > 0)
    p.v -= _headlines * _row_height;
    
  TPoint pnt(_pixmap ? p.h : p.h/CHARX, _pixmap ? p.v : p.v/_row_height);
  return pnt;
}


bool TTree_window::callback_draw_node(TTree& node, void* jolly, word when)
{
  TUpdate_info* ui = (TUpdate_info*)jolly;
  
  if (when == SCAN_PRE_ORDER)
  {
    if (ui->_y >= ui->_firsty && ui->_y <= ui->_lasty)         
    { 
      node.curr_id(ui->_str);
      const bool is_selected = ui->_str == ui->_curr_info->_id;
      const bool is_enabled = node.enabled();
      int text_len = 0;
      if (node.get_description(ui->_str))
      {
        text_len = ui->_str.len();
        if (is_selected)
          ui->_win->set_color(FOCUS_COLOR, FOCUS_BACK_COLOR);
        else
          ui->_win->set_color(is_enabled ? NORMAL_COLOR : DISABLED_COLOR, NORMAL_BACK_COLOR);
        ui->_win->stringat(int(ui->_x+2), int(ui->_y), ui->_str);
      }
      
      const int ry = int(ui->_y - ui->_firsty);
      int by;
      
      for (by = ry-1; by >= 0; by--)
      {
        long rx = (*ui->_node_info)[by]._startx;
        if (rx < ui->_x)
          break;
      } 
  
      const PNT p = ui->_win->log2dev(ui->_x, ui->_y);
      const WINDOW win = ui->_win->win();
      ui->_win->set_pen(DISABLED_COLOR);
      
			const int rh = ui->_win->row_height();

      PNT q;
      q.h = p.h; q.v = p.v + rh/2;
      xvt_dwin_draw_set_pos(win, q);
      q.h -= TABX*CHARX - 3*CHARX/2;
      xvt_dwin_draw_line(win, q);
      q.v = (by+1+ui->_headlines)*rh;
     
      xvt_dwin_draw_line(win, q);
      
      TImage* bmp = node.image(is_selected); 
      if (bmp)
      {
        const int x = p.h;
        const int y = p.v + (rh - bmp->height()) / 2;
        if (is_enabled)
          bmp->draw(win, x, y);
        else
        {   
          TImage dis(*bmp);
          for (int j = dis.height()-1; j >= 0; j--)
            for (int i = dis.width() - (j&1 ? 2 : 1); i >= 0; i-=2)
              dis.set_pixel(i, j, NORMAL_BACK_COLOR);
          dis.draw(win, x, y);
        }
      }
      
      TNode_info& ni = (*ui->_node_info)[ry];
      node.curr_id(ni._id);
      ni._valid      = true;
      ni._startx     = ui->_x;
      ni._endx       = ui->_x + text_len + 2;
      ni._enabled    = is_enabled;
      ni._expandable = is_enabled && node.has_son();
      if (ni._expandable)
      {
        ni._plusx    = ui->_x - TABX + 1;
        ni._expanded = node.expanded();
      }
      else
      {
        ni._plusx    = 0;
        ni._expanded = false;
      }  
    }  
    ui->_y++;
    
    ui->_x += TABX;
    if (ui->_x > ui->_jolly)
      ui->_jolly = ui->_x;
  } else
  if (when == SCAN_IN_ORDER)
  {   
    ui->_x -= TABX;
    if (ui->_y > ui->_lasty)
      return true;
  }    
  
  return false;
}

void TTree_window::draw_plus_minus()
{     
  const long firsty = origin().y;
  const int last_drawn = _node_info.last();
  for (int i = last_drawn; i >= 0; i--)
  {
    TNode_info& ni = _node_info[i];
    if (ni._plusx > 0)
    {   
      if (ni._expanded)
      {                                 
        bool spudorato = i == last_drawn; // Falso espandibile
        if (!spudorato)
        {
          const TNode_info& nni = _node_info[i+1];
          spudorato = nni._startx <= ni._startx;
        }
        if (spudorato)
        { 
          ni._expandable = false;
          ni._expanded = false;
          ni._plusx = 0;
          continue;
        }
      }
    
      WINDOW w = win();
      PNT r = log2dev(ni._plusx, firsty + i);
      r.v += _row_height/2;
      r.h += CHARX/2;
      
      RCT rct; 
      rct.left   = r.h - 4;
      rct.top    = r.v - 4;
      rct.right  = r.h + 5;
      rct.bottom = r.v + 5;
      set_pen(NORMAL_COLOR);
      set_brush(NORMAL_BACK_COLOR);
      xvt_dwin_draw_rect(w, &rct);
      
      PNT f, t; 
      f.h = rct.left+2; f.v = r.v;
      t.h = rct.right+-2; t.v = r.v;  
      xvt_dwin_draw_set_pos(w, f);
      xvt_dwin_draw_line(w, t);
      
      if (!ni._expanded)
      {
        f.h = r.h; f.v = rct.top+2;
        t.h = r.h; t.v = rct.bottom-2;  
        xvt_dwin_draw_set_pos(w, f);
        xvt_dwin_draw_line(w, t);
      }
    }  
  }
}

void TTree_window::set_header(const char* head) 
{ 
  _header = head; 
  _headlines = _header.items();
}

void TTree_window::update_header()
{
  if (_headlines > 0)
  {
    autoscroll(false);
    set_brush(MASK_BACK_COLOR);
    bar(0,-_headlines,columns(),0);
    autoscroll(true);
    set_opaque_text(false);
    for (int i = 0; i < _headlines; i++)
      stringat(2, int(origin().y+i-_headlines), _header.get(i));
  }
}

void TTree_window::update()
{          
  TField_window::update();

  if (_tree == NULL)
    return;
    
  update_header();
    
  TUpdate_info ui; 
  ui._win = this;
  ui._x = 0;
  ui._y = 0;
  ui._firsty = origin().y;
  ui._lasty = ui._firsty + rows();
  ui._jolly = 0;
  ui._headlines = _headlines;
  ui._node_info = &_node_info;
  ui._curr_info = &_curr_info;
  
  bool ok = false;
  if (_first_line > 0)
  {
    const long index = origin().y - _first_line;
    TNode_info ni;
    if (index2info(index, ni))
    {
      ok = _tree->goto_node(ni._id);                        
      ui._x = ni._startx;
      ui._y = ui._firsty;
    }  
  }  
  if (!ok)
  {
    ok = _tree->goto_root();
    if (!ok) 
      return;
  }
  
  _node_info.reset();
  set_opaque_text(true);
  
  word flags = SCAN_PRE_ORDER | SCAN_IN_ORDER | SCAN_IGNORING_UNEXPANDED;
  if (_hide_leaves) flags |= SCAN_IGNORING_LEAVES;
  
  _tree->scan_depth_first(callback_draw_node, &ui, flags);
  while (ui._y < ui._lasty)
  {
    if (_tree->goto_father())
    {         
      ui._x -= TABX;
      if (_tree->goto_rbrother())
        _tree->scan_depth_first(callback_draw_node, &ui, flags);
    }    
    else
      break;  
  }    
  draw_plus_minus();
  
  set_scroll_max(ui._jolly+columns(), ui._y);
  _first_line = ui._firsty;
}

// Fa diventare il nodo corrente selezionato
bool TTree_window::select_current()
{
  if (_tree)
    _tree->curr_id(_curr_info._id);
  return _tree != NULL;
}

// Salta al nodo correntemente selezionato
bool TTree_window::goto_selected()
{
  bool ok = false;
  if (_tree)
    ok = _tree->goto_node(_curr_info._id);
  return ok;
}

void TTree_window::set_row_height(int rh) 
{ 
	if (rh <= 0)
		rh = CHARY+2;
	_row_height = rh; 
}

int TTree_window::info2index(const TNode_info& info) const
{   
  return _node_info.find(info);
}

bool TTree_window::index2info(long index, TNode_info& ni)
{                 
  bool ok = false;
  const int last = _node_info.last();

  if (index == -1)
  {          
    if (index2info(0, ni) && _tree->goto_node(ni._id))
    {             
      ok = _tree->goto_lbrother();
      if (!ok)
      {
        ok = _tree->goto_father();
        ni._startx -= TABX;
      }  
      if (ok)
        _tree->curr_id(ni._id);
    }
  } else
  if (index == last+1 && last >= 0) 
  {
    if (index2info(last, ni) && _tree->goto_node(ni._id))
    {                  
      ok = _tree->expanded() && _tree->goto_firstson();
      if (ok && _hide_leaves && !_tree->has_son())
      {      
        _tree->goto_node(ni._id);  // Ritorno al padre perche' ignoro le foglie
        ok = false;
      }  
      if (!ok)
      {
        ok = _tree->goto_rbrother();
        ni._startx += TABX;
      }
      if (ok)
      {
        _tree->curr_id(ni._id);
      }  
    }  
  } else
  if (index >= 0 && index <= last)
  {
    TNode_info& info = _node_info[int(index)];
    ok = info.ok();
    if (ok)
      ni = info;
  }  
  
  return ok;
}

bool TTree_window::on_key(KEY key)
{
  if (_tree == NULL || _tree_locked)
    return false;

  if (key == K_RIGHT)
  {
    if (_tree->goto_node(_curr_info._id) &&
        !_tree->expanded() && _tree->has_son())
      key = K_ENTER;    
    else  
      key = K_DOWN;
  }

  if (key == K_LEFT)
  {
    if (_tree->goto_node(_curr_info._id) && 
        _tree->expanded())
      key = K_ENTER;    
    else  
      key = K_UP;
  }

  if (key == K_UP || key == K_DOWN)
  { 
    _tree_locked = true;   
    int index = info2index(_curr_info);
    if (key == K_UP) 
      index--;
    else  
      index++;
    
    bool ok = false;
    bool scroll = false;        
    
    TNode_info ni;
    if (index2info(index, ni))
    {
      ok = _tree->goto_node(ni._id);
      scroll = ok && (index < 0 || index > _node_info.last());
    }
    else
    {
      const int index = key == K_UP ? 0 : _node_info.last();
      ok = index2info(index, ni) && _tree->goto_node(ni._id);
    }
    
    if (ok && _curr_info != ni) 
    {
      _tree->curr_id(_curr_info._id);
      owner().on_key(K_SPACE);
      if (!scroll)
      {
        force_update();
        do_events();
      }  
    }  
    
    _tree_locked = false;
    if (!scroll)
      return true;
  }
  
  if (key == K_ENTER)
  {     
    _tree_locked = true;
    if (_tree->goto_node(_curr_info._id))
    {         
      if (_tree->has_son())
      {
        bool ok;
        if (_tree->expanded())
           ok = _tree->shrink();
        else  
           ok = _tree->expand();
        if (ok)         
        {
          force_update();
          do_events();
          // Make sure of being well positioned after the redraw!!!
          _tree->goto_node(_curr_info._id);  
        }
      }
      owner().on_key(K_CTRL + K_SPACE);
    }
    _tree_locked = false;
  }
  
  return TField_window::on_key(key);
}

void TTree_window::handler(WINDOW win, EVENT* ep)
{ 
  switch(ep->type)  
  {
  case E_MOUSE_DBL:
  case E_MOUSE_DOWN:
    if (_tree && !_tree_locked)
    {                     
      _tree_locked = true;
      const TPoint lp = dev2log(ep->v.mouse.where);
      const int c = (int)lp.x;
      const int r = (int)lp.y;
      
      TNode_info ni;
      bool ok = index2info(r, ni);
      if (ok && (c == ni._plusx || (c >= ni._startx && c < ni._endx)) && 
          _tree->goto_node(ni._id))
      {             
        if (c == ni._plusx || (ni._expandable && ep->type == E_MOUSE_DBL))
        {     
          if (_tree->expanded())
            ok = _tree->shrink();
          else  
            ok = _tree->expand();
          if (ok)
            owner().on_key(K_CTRL + K_SPACE);
        }
        else
        {
          KEY key = K_SPACE;
          if (ep->type == E_MOUSE_DBL)
            key += K_CTRL;
          owner().on_key(key);
        }  
        if (ok)
        {
          _curr_info = ni;
          force_update();
        }  
      }  
      _tree_locked = false;
    }  
    break;
  default:
    TField_window::handler(win, ep);
    break;
  }
}

TTree_window::TTree_window(int x, int y, int dx, int dy, 
                           WINDOW parent, TTree_field* owner)          
            : TField_window(x, y, dx, dy, parent, owner), _tree(NULL), 
              _hide_leaves(false), _headlines(0)
{                               
  set_row_height(-1);  // Compute default row height
}

///////////////////////////////////////////////////////////
// TTree_field
///////////////////////////////////////////////////////////

word TTree_field::class_id() const
{
  return CLASS_TREE_FIELD;
}

bool TTree_field::is_kind_of(word cid) const
{
  return cid == CLASS_TREE_FIELD || TWindowed_field::is_kind_of(cid);
}

#define tree_win() ((TTree_window&)win())

TTree* TTree_field::tree() const 
{ 
  return tree_win().tree();
}

void TTree_field::set_tree(TTree* tree) 
{ 
  tree_win().set_tree(tree);
  tree_win().select_current();
}

void TTree_field::hide_leaves(bool yes)
{
  tree_win().hide_leaves(yes);
}

bool TTree_field::select_current()
{
  return tree_win().select_current();
}

bool TTree_field::goto_selected()
{
  return tree_win().goto_selected();
}

void TTree_field::set_header(const char* head)
{
  tree_win().set_header(head);
}

void TTree_field::set_row_height(int rh)
{
  tree_win().set_row_height(rh);
}

TField_window* TTree_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
{
  return new TTree_window(x, y, dx, dy, parent, this);
}