#define XI_INTERNAL           
#include <xinclude.h>

#include <colors.h>
#include <controls.h>
#include <diction.h>
#include <image.h>
#include <mask.h>
#include <tree.h>
#include <treectrl.h>

///////////////////////////////////////////////////////////
// TField_window
///////////////////////////////////////////////////////////

long TField_window::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {                
  case E_FOCUS:
    if (ep->v.active)
    {
      WINDOW parent = xvt_vobj_get_parent(win);
      XI_OBJ* itf = xi_get_itf((XinWindow)parent);
      xi_set_focus(itf);
    }
    break;
  case E_MOUSE_DOWN:
    if (ep->v.mouse.button == 1 && _owner != NULL)
    {
      _owner->on_key(K_F11);
      return 0L;
    }
    break;
  default:
    break;  
  }
  return TScroll_window::handler(win, ep);
}

bool TField_window::on_key(KEY k)
{             
  if (k == K_TAB || k == K_BTAB || k == K_F3 || k == K_F4)
  { 
    const TMask& m = _owner->mask();
    const short id = _owner->dlg();
    const int start = m.id2pos(id);
    const int dir = (k == K_TAB || k == K_F3) ? +1 : -1;
    
    int pos = start;
    while (TRUE)
    { 
      pos += dir;
      if (pos < 0) pos = m.fields()-1;
      if (pos >= m.fields()) pos = 0;
      if (pos == start)
        break;
      TMask_field& f = m.fld(pos);
      if (f.is_operable())
      {
        f.set_focus();
        return TRUE;
      }  
    }  
  }
  return TScroll_window::on_key(k);
}

void TField_window::update()
{  
  const WINDOW me = win();
  if (CAMPI_SCAVATI)
  { 
    const WINDOW pa = parent();
    RCT rct; xvt_vobj_get_outer_rect(me, &rct);
    xvt_rect_inflate(&rct, 2, 2);
    xi_draw_3d_rect((XinWindow)pa, (XinRect*)&rct, TRUE, 2,
                    MASK_LIGHT_COLOR, MASK_BACK_COLOR, MASK_DARK_COLOR);
  }
  xvt_dwin_clear(me, NORMAL_BACK_COLOR);
  set_font();
}

// Serve quando si chiama il costruttore senza owner
void TField_window::set_owner(TWindowed_field* o) 
{ _owner = o; }

TField_window::TField_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner) 
             : _owner(owner)
{ 
  if (owner != NULL)
  {
    CHECK(parent, "Null control parent");
    create(x, y, dx, dy, "", WSF_HSCROLL | WSF_VSCROLL, W_PLAIN, parent);
    activate(owner->enabled());
    if (owner->shown()) 
      open();
  }    
}

///////////////////////////////////////////////////////////
// TWindowed field
///////////////////////////////////////////////////////////

void TWindowed_field::enable(bool on)
{
  TOperable_field::enable(on);
  if (_win) 
    _win->activate(on);
}

void TWindowed_field::show(bool on)
{
  TOperable_field::show(on);  
  if (_win && _win->is_open() != on)
  {
    if (on) 
      _win->open();
    else               
      _win->close();
  }
}

void TWindowed_field::highlight() const
{
  if (_win && active())
    _win->set_focus();
}

RCT& TWindowed_field::get_rect(RCT& r) const
{
  if (_win)
    xvt_vobj_get_outer_rect(_win->win(), &r);
  else
    xvt_rect_set_empty(&r);
  return r;
}

void TWindowed_field::set_rect(const RCT& r)
{
  if (_win)
    xvt_vobj_move(_win->win(), (RCT*)&r);
}

word TWindowed_field::class_id() const 
{ return CLASS_WINDOWED_FIELD; }

bool TWindowed_field::is_kind_of(word id) const
{ return id == CLASS_WINDOWED_FIELD || TOperable_field::is_kind_of(id); }

const char* TWindowed_field::class_name() const 
{ return "WINDOWED"; }

short TWindowed_field::dlg() const 
{ return _dlg ? _dlg : _ctl_data._dlg; }

void TWindowed_field::parse_head(TScanner& scanner)
{
  _ctl_data._width  = scanner.integer();
  _ctl_data._height = scanner.integer();
}

TField_window* TWindowed_field::create_window(int x, int y, int dx, int dy, WINDOW parent) 
{                                                       
  // Must be always overridden and look like this
  return new TField_window(x, y, dx, dy, parent, this);
}

void TWindowed_field::create(short id, int x, int y, int dx, int dy, WINDOW parent)
{
  if (parent == NULL_WIN)
    parent = mask().win();
  _dlg = id;
  _win = create_window(x, y, dx, dy, parent);
}

void TWindowed_field::create(WINDOW parent)
{        
  create(_ctl_data._dlg, _ctl_data._x, _ctl_data._y, _ctl_data._width, _ctl_data._height, parent);
}

TWindowed_field::TWindowed_field(TMask* m) 
               : TOperable_field(m), _dlg(0), _win(NULL) 
{ }

TWindowed_field::~TWindowed_field() 
{ 
  if (_win)
    delete _win; 
}

///////////////////////////////////////////////////////////
// TControl_host_window
///////////////////////////////////////////////////////////

long TControl_host_window::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_SIZE:
    if (_ctrl != NULL_WIN)
    {
      RCT rct;
      xvt_rect_set(&rct, 0, 0, ep->v.size.width, ep->v.size.height);
      xvt_vobj_move(_ctrl, &rct);
    }
    break;
  case E_UPDATE:
    if (_ctrl != NULL_WIN)
      return 0L;  // Inutile disegnare: _ctrl occupa tutta la client area
    break;
  default: 
    break;
  }
  return TField_window::handler(win, ep); 
}

TControl_host_window::TControl_host_window(int x, int y, int dx, int dy,  
                                           WINDOW parent, TWindowed_field* owner)
                    : TField_window(x, y, dx, dy, parent, owner), _ctrl(NULL_WIN)
{
	set_scroll_max(0, 0);  // Get rid of that useless scrollbars
}

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

class TTree_window : public TControl_host_window
{
  TTree* _tree;
  bool _hide_leaves;
  TAuto_token_string _header;

private:
  void create_children(XVT_TREEVIEW_NODE node);
  void handle_tree_event(EVENT* ep);
  bool add_child(XVT_TREEVIEW_NODE parent);

protected: // TWindow
  virtual void update();  
  virtual long handler(WINDOW win, EVENT* ep);
  virtual void force_update();

public:
  TTree* tree() const { return _tree; }
  void set_tree(TTree* tree);
  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() { }
};

bool TTree_window::add_child(XVT_TREEVIEW_NODE parent)
{
  XVT_TREEVIEW_NODE_TYPE type = _tree->has_son() ? XVT_TREEVIEW_NODE_NONTERMINAL
                                                 : XVT_TREEVIEW_NODE_TERMINAL;
  if (_hide_leaves && type == XVT_TREEVIEW_NODE_TERMINAL)
    return false;
  
  TString id;   _tree->curr_id(id);
  TString desc; _tree->get_description(desc);

  XVT_IMAGE ii = NULL, ic = NULL, ie = NULL;
  TImage* im_nor = _tree->image(false);
  if (im_nor != NULL)
  {
    if (type == XVT_TREEVIEW_NODE_NONTERMINAL)
    {
      TImage* im_sel = _tree->image(true);
      ic = im_nor->xvt_image();
      ie = im_sel->xvt_image();
    }
    else
      ii = im_nor->xvt_image();
  }
  XVT_TREEVIEW_NODE child = xvt_treeview_add_child_node(_ctrl, parent, type, ii, ic, ie, desc, NULL, id);
  if (child != NULL)
  {
    if (!_tree->enabled())
      xvt_treeview_enable_node(_ctrl, child, FALSE);

    if (type == XVT_TREEVIEW_NODE_NONTERMINAL && _tree->expanded())
    {
      for (bool ok = _tree->goto_firstson(); ok; ok = _tree->goto_rbrother())
        add_child(child);
      xvt_treeview_expand_node(_ctrl, child, FALSE);
    }
  }

  _tree->goto_node(id);
  return true;
}

void TTree_window::create_children(XVT_TREEVIEW_NODE node)
{
  bool ok = false;
  TString id;
  if (_tree != NULL)
  {
    if (node != NULL)
      id = xvt_treeview_get_node_data(_ctrl, node);
    if (id.empty()) // Sono sulla radice
    {
      node = xvt_treeview_get_root_node(_ctrl);
      ok = _tree->goto_root();
    }
    else
    {
      _tree->goto_node(id);
      ok = _tree->goto_firstson();
    }
  }
  xvt_treeview_remove_node_children(_ctrl, node);
  for (; ok; ok = _tree->goto_rbrother())
    add_child(node);

  // Riposiziona per benino l'alberello
  if (id.empty())
    _tree->goto_root();
  else
    _tree->goto_node(id);
}

void TTree_window::set_tree(TTree* tree) 
{ 
  _tree = tree; 
  if (_tree != NULL)
  {
    // Memorizza la posizione dell'albero per dopo ...
    TString curr; tree->curr_id(curr); 
    create_children(NULL); // Rigenera i figli della radice
    tree->goto_node(curr); // Riporta la selezione sul nodo memorizzato
  }
}

void TTree_window::handle_tree_event(EVENT* ep)
{
  XVT_TREEVIEW_NODE node = ep->v.ctl.ci.v.treeview.node;
  const TString id = (const char*)xvt_treeview_get_node_data(_ctrl, node);
  if (_tree->goto_node(id))
  {
    if (ep->v.ctl.ci.v.treeview.sgl_click || ep->v.ctl.ci.v.treeview.dbl_click)
    {
      KEY key = K_SPACE;  // Single click selection               
      if (ep->v.ctl.ci.v.treeview.dbl_click)
        key += K_CTRL;    // Double click selection
      if (owner().on_key(key) && _tree->goto_node(id))
      {
        // Aggiorna testo ed immagini che possono essere cambiate
        XVT_IMAGE ii = NULL, ic = NULL, ie = NULL;
        TImage* im_nor = _tree->image(false);
        if (_tree->has_son())
        {
          TImage* im_sel = _tree->image(true);
          ic = im_nor->xvt_image();
          ie = im_sel->xvt_image();
        }
        else
          ii = im_nor->xvt_image();
        xvt_treeview_set_node_images(_ctrl, node, ii, ic, ie);
        TString desc; _tree->get_description(desc);
        xvt_treeview_set_node_string(_ctrl, node, desc);
      }
    }
    else
    {
      int nWhat = 0;
      if (ep->v.ctl.ci.v.treeview.expanded)
        nWhat |= 0x1;
      if (ep->v.ctl.ci.v.treeview.collapsed)
        nWhat |= 0x2;
      switch (nWhat)
      { 
      case 0x1:               // Expanded
        _tree->expand();
        owner().on_key(K_SHIFT + K_SPACE); 
        break; 
      case 0x2:               // Collapsed
        _tree->shrink();
        break;
      case 0x3:               // Expanding
        _tree->expand();
        create_children(node); 
        owner().on_key(K_SHIFT + K_SPACE);
        owner().on_key(K_SPACE);
        break;
      default : break;
      }
    }
  }
}

void TTree_window::update()
{
  if (_header.full())
  {
    clear(MASK_BACK_COLOR);
    set_color(PROMPT_COLOR, MASK_BACK_COLOR);
    short x = 3, y = 0;
    FOR_EACH_TOKEN(_header, row)
      stringat(x, y++, row);
  }
}

long TTree_window::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_CONTROL:
    if (ep->v.ctl.ci.type == WC_TREE && _tree != NULL)
    {
      handle_tree_event(ep);
      return 0L;
    }
    break;
  case E_UPDATE:
    update(); // TControl_host_window non lo fa
    return 0L;
  default: 
    break;
  }
  return TControl_host_window::handler(win, ep); 
}

bool TTree_window::select_current()
{
  XVT_TREEVIEW_NODE nextsel = NULL; // Nodo da selezionare (se mai lo trovero')

  if (_tree != NULL)
  {
    TString id; _tree->curr_id(id);  // id del nodo corrente dell'albero  

    // Controllo se il tree control e' gia' posizionato bene
    XVT_TREEVIEW_NODE cursel = xvt_treeview_get_selected_node(_ctrl);
    nextsel = xvt_treeview_find_node_string(_ctrl, id);
    if (nextsel != NULL && cursel == nextsel)
      return true;

    if (nextsel == NULL)
    {
      xvt_treeview_suspend(_ctrl);     // Sospendo le notifiche degli eventi
      TString_array a;
      a.add(id);
      // Creo la lista dei progenitori
      while (_tree->goto_father())
      {
        const int i = a.add(EMPTY_STRING);
        _tree->curr_id(a.row(i));
        _tree->expand(); // Nel caso non fosse gia' espanso
      }

      // Scandisco i progenitori partendo dalla radice
      XVT_TREEVIEW_NODE parent = NULL; // was xvt_treeview_get_root_node(_ctrl);
      bool killed = false;             // Ho interrotto "bruscamente" la ricerca?
      FOR_EACH_ARRAY_ROW_BACK(a, r, row)
      {
        if (killed)
          break;
        for (int i = 0; ; i++)
        {
          XVT_TREEVIEW_NODE child = xvt_treeview_get_child_node(_ctrl, parent, i);
          if (child == NULL && i == 0) // Forse non c'e' nessun figlio ...
          {
            create_children(parent);   // ... sara' vero?
            child = xvt_treeview_get_child_node(_ctrl, parent, i);
          }
          if (child == NULL) // Certamente non ci sono piu' figli
          {
            //killed = true;   // Non ho trovato quello che cercavo: esco subito
            break;
          }
          const char* data = xvt_treeview_get_node_data(_ctrl, child);
          if (*row == data)
          {
            nextsel = child;
            if (*row == id) // Ho finito
              killed = true;
            else
            {
              parent = child;
              xvt_treeview_expand_node(_ctrl, child, FALSE);
            }
            break;
          }
        }
      }
      xvt_treeview_resume(_ctrl); // Riattivo le notifiche degli eventi
      _tree->goto_node(id);       // Riposiziono l'albero
    }
    if (nextsel != NULL)
    {
      xvt_treeview_select_node(_ctrl, nextsel, TRUE);
      if (_tree->expanded())
        xvt_treeview_expand_node(_ctrl, nextsel, FALSE);
    }
  }
  return nextsel != NULL;
}

bool TTree_window::goto_selected()
{
  bool ok = false;
  XVT_TREEVIEW_NODE child = xvt_treeview_get_selected_node(_ctrl);
  if (child != NULL)
  {
    const char* data = (const char*)xvt_treeview_get_node_data(_ctrl, child);
    ok = _tree->goto_node(data);
  }
  return ok;
}

void TTree_window::set_header(const char* head)
{
  if (_header != head)
  {
    const int old_headlines = _header.items();
    _header = head;
    const int new_headlines = _header.items();
    if (new_headlines != old_headlines)
    {
      RCT rctree; xvt_vobj_get_client_rect(win(), &rctree);
      rctree.top += new_headlines * CHARY;
      xvt_vobj_move(_ctrl, &rctree);
    }
    force_update();
  }
}

void TTree_window::set_row_height(int rh)
{
}

TTree_window::TTree_window(int x, int y, int dx, int dy, WINDOW parent, TTree_field* owner)
            : TControl_host_window(x, y, dx, dy, parent, owner), _tree(NULL), _hide_leaves(false)
{
  RCT rct; xvt_vobj_get_client_rect(win(), &rct);
  _ctrl = xvt_treeview_create(win(), &rct, "", 0, (long)this, owner->dlg(),
                              NULL, NULL, NULL, 0, CHARY+2);

  XVT_COLOR_COMPONENT xcc[8]; memset(xcc, 0, sizeof(xcc));
  xcc[0].type = XVT_COLOR_BACKGROUND; xcc[0].color = NORMAL_BACK_COLOR;
  xcc[1].type = XVT_COLOR_FOREGROUND; xcc[1].color = NORMAL_COLOR;
  xcc[2].type = XVT_COLOR_HIGHLIGHT;  xcc[2].color = FOCUS_BACK_COLOR;
  xcc[3].type = XVT_COLOR_SELECT;     xcc[3].color = FOCUS_COLOR;
  xcc[4].type = XVT_COLOR_BLEND;      xcc[4].color = MASK_BACK_COLOR;
  xcc[5].type = XVT_COLOR_TROUGH;     xcc[5].color = DISABLED_COLOR;
  xvt_ctl_set_colors(_ctrl, xcc, XVT_COLOR_ACTION_SET);
}

///////////////////////////////////////////////////////////
// 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)
{
  TTree_window& tv = tree_win();
  tv.set_tree(tree);
  tv.select_current();
}

void TTree_window::force_update()
{ set_tree(tree()); }

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); }

///////////////////////////////////////////////////////////
// TOutlook_field
///////////////////////////////////////////////////////////

class TOutlook_window : public TControl_host_window
{
public:
  virtual long handler(WINDOW win, EVENT* ep);

public:
  int add_item(short icon, const char* text, int flags);
  bool select(int item, bool on);
  int selected() const;
  int items() const;
  void clear();
  TOutlook_window(int x, int y, int dx, int dy, WINDOW parent, TOutlook_field* owner);
};

long TOutlook_window::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_CONTROL:
    if (ep->v.ctl.ci.type == WC_OUTLOOKBAR)
    {
      owner().on_key(K_SPACE);
      return 0L;
    }
    break;
  default: 
    break;
  }
  return TControl_host_window::handler(win, ep); 
}

int TOutlook_window::add_item(short icon, const char* text, int flags)
{ return xvt_list_add_item(_ctrl, icon, text, flags); }

bool TOutlook_window::select(int item, bool on)
{ return xvt_list_set_sel(_ctrl, item, on) != FALSE; }

int TOutlook_window::selected() const
{ return xvt_list_get_sel_index(_ctrl); }

int TOutlook_window::items() const
{ return xvt_list_count(_ctrl); }

void TOutlook_window::clear()
{ xvt_list_clear(_ctrl); }

TOutlook_window::TOutlook_window(int x, int y, int dx, int dy, WINDOW parent, TOutlook_field* owner)
               : TControl_host_window(x, y, dx, dy, parent, owner) 
{
  XVT_COLOR_COMPONENT xcc[4]; memset(xcc, 0, sizeof(xcc));
	xcc[0].type = XVT_COLOR_BACKGROUND;	xcc[0].color = BTN_BACK_COLOR;
	xcc[1].type = XVT_COLOR_FOREGROUND;	xcc[1].color = PROMPT_COLOR;

  WIN_DEF wd; memset(&wd, 0, sizeof(wd));
  wd.wtype = WC_OUTLOOKBAR;
  wd.v.ctl.ctrl_id = owner->dlg();
  wd.v.ctl.font_id = xvtil_default_font(true); // Fat font
  wd.rct = resize_rect(x, y, dx, dy, wd.wtype, parent);
  wd.ctlcolors = xcc;
  
  _ctrl = xvt_ctl_create_def(&wd, win(), 0);
}        

word TOutlook_field::class_id() const
{ return CLASS_OUTLOOK_FIELD; }

bool TOutlook_field::is_kind_of(word cid) const
{ return cid == CLASS_OUTLOOK_FIELD || TWindowed_field::is_kind_of(cid); }

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

void TOutlook_field::create(short dlg, int x, int y, int dx, int dy, WINDOW parent)
{ construct(dlg, "", x, y, dy, parent, "", dx); }

void TOutlook_field::clear()
{
  TOutlook_window& ow = (TOutlook_window&)win();
  ow.clear();
}

int TOutlook_field::items() const
{
  TOutlook_window& ow = (TOutlook_window&)win();
  return ow.items();
}

int TOutlook_field::add_item(short icon, const char* text, int flags)
{
  TOutlook_window& ow = (TOutlook_window&)win();
  return ow.add_item(icon, text, flags);
}

void TOutlook_field::set_window_data(const char* data)
{
  TOutlook_window& ow = (TOutlook_window&)win();
  const int sel = atoi(data);
  _str.cut(0) << sel;
  ow.select(sel, true);
}

const TString& TOutlook_field::get_window_data()
{
  TOutlook_window& ow = (TOutlook_window&)win();
  _str.cut(0) << ow.selected();
  return _str;
}

void TOutlook_field::set(const char* data)
{
  set_window_data(data);
  set_dirty();
}

const TString& TOutlook_field::get() const
{ return _str; }

bool TOutlook_field::on_key(KEY key)
{
  if (key == K_SPACE)
  {
    get_window_data();
    set_dirty();
    on_hit();
    return true;
  }
  return TWindowed_field::on_key(key);
}

///////////////////////////////////////////////////////////
// TSlider_window
///////////////////////////////////////////////////////////

class TSlider_window : public TField_window
{
public:
  void set(int pos);
  int get() const;
  void set_range(int mi, int ma);
  TSlider_window(int x, int y, int dx, int dy, WINDOW parent, TSlider_field* owner);
};

void TSlider_window::set_range(int mi, int ma)
{
  if (mi != 0)
  {
    ma -= mi;
    mi = 0;
  }
  if (ma < mi)
    ma = 100;
  xvt_sbar_set_range(win(), HVSLIDER, mi, ma);
}

void TSlider_window::set(int pos)
{ xvt_sbar_set_pos(win(), HVSLIDER, pos); }

int TSlider_window::get() const
{ return xvt_sbar_get_pos(win(), HVSLIDER); }

TSlider_window::TSlider_window(int x, int y, int dx, int dy, WINDOW parent, TSlider_field* owner)
              : TField_window(0, 0, 0, 0, NULL, NULL)
{ 
  XVT_COLOR_COMPONENT xcc[2]; memset(xcc, 0, sizeof(xcc));
  xcc[0].type = XVT_COLOR_BLEND; xcc[0].color = MASK_BACK_COLOR;

  set_owner(owner);
  WIN_DEF wd; memset(&wd, 0, sizeof(wd));
  wd.rct = resize_rect(x, y, dx, dy, wd.wtype, parent);
  wd.wtype = (wd.rct.right-wd.rct.left) > (wd.rct.bottom-wd.rct.top) ? WC_HSLIDER : WC_VSLIDER;
  wd.ctlcolors = xcc;
  wd.v.ctl.ctrl_id = owner->dlg();

  real mi, ma; owner->range(mi, ma);
  const long limit = ma.integer() - mi.integer();
  set_win(xvt_ctl_create_def(&wd, parent, limit));
}

///////////////////////////////////////////////////////////
// TSlider_field
///////////////////////////////////////////////////////////

void TSlider_field::set_window_data(const char* data)
{
  TSlider_window& sw = (TSlider_window&)win();
  const int sel = atoi(data);
  sw.set(sel);
}

const TString& TSlider_field::get_window_data()
{
  TSlider_window& sw = (TSlider_window&)win();
  TString& tmp = get_tmp_string();
  tmp << sw.get();
  return tmp;
}

void TSlider_field::set(const char* data)
{
  if (!_range_min.is_zero())
  {
    real n(data);
    n -= _range_min;
    data = n.string();
  }
  set_window_data(data);
  set_dirty();
}

const TString& TSlider_field::get() const
{ 
  TString& str = (TString&)((TSlider_field*)this)->get_window_data();
  if (!_range_min.is_zero())
  {
    real n(str);
    n += _range_min;
    str = n.string();
  }
  return str;
}

const char* TSlider_field::get_buddy() const
{
  if (_buddy > 0)
  {
    const int pos = mask().id2pos(_buddy);
    if (pos > 0)
    {
      real n = mask().fld(pos).get();
      if (n < _range_min) n = _range_min;
      if (n > _range_max) n = _range_max;
      return n.string();
    }
  }
  return EMPTY_STRING;
}

void TSlider_field::set_buddy(const char* b) const
{
  if (_buddy > 0)
  {
    const int pos = mask().id2pos(_buddy);
    if (pos > 0)
      mask().fld(pos).set(b);
  }
}

bool TSlider_field::on_hit()
{
  if (!mask().is_running())
  {
    set_range(_range_min, _range_max);  // Imposto range dello slider
    const char* b = get_buddy();
    if (b && *b)
      set(b);
  }
  return TWindowed_field::on_hit();
}

word TSlider_field::class_id() const
{ return CLASS_SLIDER_FIELD; }

bool TSlider_field::is_kind_of(word cid) const
{ return cid == CLASS_SLIDER_FIELD || TWindowed_field::is_kind_of(cid); }

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

void TSlider_field::set_range(const real& mi, const real& ma) 
{ 
  _range_min = mi; 
  _range_max = ma; 
  set_range(mi.integer(), ma.integer());
}

void TSlider_field::set_range(int mi, int ma) 
{ 
  _range_min = mi; 
  _range_max = ma;
  if (_win != NULL_WIN)
  {
    TSlider_window& sw = (TSlider_window&)win();
    sw.set_range(mi, ma);
  }
}

void TSlider_field::range(real& mi, real& ma) 
{ mi = _range_min; ma = _range_max; } 

bool TSlider_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "DR")  // DRIVENBY id
  {
    _buddy = scanner.integer();
    return true;
  }
  if (scanner.key() == "RA") // RANGE min max
  {
    const int mi = scanner.integer();
    const int ma = scanner.integer();
    set_range(mi, ma);
    return true;
  }

  return TWindowed_field::parse_item(scanner);
}

TSlider_field::TSlider_field(TMask* m) 
             : TWindowed_field(m), _buddy(0), _range_min(ZERO), _range_max(CENTO) 
{ }


///////////////////////////////////////////////////////////
// TProp_window
///////////////////////////////////////////////////////////

class TProp_window : public TField_window
{
public:
  TProp_window(int x, int y, int dx, int dy, WINDOW parent, TProp_field* owner);
};

TProp_window::TProp_window(int x, int y, int dx, int dy, WINDOW parent, TProp_field* owner)
            : TField_window(0, 0, 0, 0, NULL, NULL)
{
  XVT_COLOR_COMPONENT xcc[8]; memset(xcc, 0, sizeof(xcc));
  xcc[0].type = XVT_COLOR_BACKGROUND; xcc[0].color = NORMAL_BACK_COLOR;
  xcc[1].type = XVT_COLOR_FOREGROUND; xcc[1].color = NORMAL_COLOR;
  xcc[2].type = XVT_COLOR_HIGHLIGHT;  xcc[2].color = FOCUS_BACK_COLOR;
  xcc[3].type = XVT_COLOR_SELECT;     xcc[3].color = FOCUS_COLOR;
  xcc[4].type = XVT_COLOR_BLEND;      xcc[4].color = MASK_BACK_COLOR;
  xcc[5].type = XVT_COLOR_TROUGH;     xcc[5].color = DISABLED_BACK_COLOR;

  set_owner(owner);
  WIN_DEF wd; memset(&wd, 0, sizeof(wd));
  wd.rct = resize_rect(x, y, dx, dy, wd.wtype, parent);
  wd.wtype = WC_PROPGRID;
  wd.ctlcolors = xcc;
  wd.v.ctl.ctrl_id = owner->dlg();

  set_win(xvt_ctl_create_def(&wd, parent, 0L));
}

word TProp_field::class_id() const
{ return CLASS_PROP_FIELD; }

bool TProp_field::is_kind_of(word cid) const
{ return cid == CLASS_PROP_FIELD || TWindowed_field::is_kind_of(cid); }

void TProp_field::freeze(bool on)
{
  WINDOW pg = win().win();
  if (on)
    xvt_prop_suspend(pg);
  else
    xvt_prop_restart(pg);
}

bool TProp_field::set_property(const char* name, const char* value, const char* label)
{ 
  XVT_TREEVIEW_NODE node = NULL;
  WINDOW pg = win().win();
  if (pg != NULL_WIN)
  {
    if (name != NULL && value == NULL && label != NULL)
      node = xvt_prop_add(pg, NULL, name, NULL, label);        // Category!
    else
    {
      if (label && *label)
        node = xvt_prop_add(pg, "string", name, value, label); // Add property
      else
      {
        node = xvt_prop_find(win().win(), name);
        if (node)
          xvt_prop_set_data(pg, node, value);                  // Set property
      }
    }
  }
  return node != NULL;
}

bool TProp_field::set_property(const char* name, long value, const char* label)
{ 
  bool done = false;
  TString16 str; str << value;
  if (label && *label)
    done = xvt_prop_add(win().win(), "long", name, str, label) != NULL; 
  else
    done = set_property(name, str, label); 
  return done;
}

bool TProp_field::set_property(const char* name, COLOR c, const char* label)
{ 
  bool done = false;
  TString16 str; 
  str.format("%d,%d,%d", XVT_COLOR_GET_RED(c), XVT_COLOR_GET_GREEN(c), XVT_COLOR_GET_BLUE(c));
  if (label && *label)
    done = xvt_prop_add(win().win(), "color", name, str, label) != NULL; 
  else
    done = set_property(name, str, label); 
  return done;
}

const TString& TProp_field::get_property(const char* name) const
{
  WINDOW pg = win().win();
  XVT_TREEVIEW_NODE node = xvt_prop_find(pg, name);
  if (node)
  {
    TString& tmp = get_tmp_string();
    const int len = xvt_prop_get_data(pg, node, tmp.get_buffer(), tmp.size());
    if (len > tmp.size())
      xvt_prop_get_data(pg, node, tmp.get_buffer(len), len);
    return tmp;
  }
  return EMPTY_STRING;
}

long TProp_field::get_long_property(const char* name) const
{ return atol(get_property(name)); }

COLOR TProp_field::get_color_property(const char* name) const
{ 
  COLOR col = COLOR_INVALID;
  const TString& tmp = get_property(name);
  if (tmp.full())
  {
    int r = 0, g = 0, b = 0;
    if (sscanf(tmp, "%d,%d,%d", &r, &g, &b) == 3)
      col = RGB2COLOR(r,g,b);
    else
      col = r ? r : COLOR_BLACK;
  }
  return col;
}

bool TProp_field::current_property(TString& name, TVariant& value) const
{
  name.cut(0);
  value.set_null();
  
  WINDOW pg = win().win();
  if (pg != NULL_WIN)
  {
    XVT_TREEVIEW_NODE node = xvt_prop_current(pg);
    if (node != NULL)
    {
      TFilename n;
      xvt_prop_get_string(pg, node, n.get_buffer(), n.size());
      name = n;
      xvt_prop_get_data(pg, node, n.get_buffer(), n.size());
      if (real::is_natural(n))
        value = atol(n);
      else
        value = n;
    }
  }
  return !value.is_null();
}

bool TProp_field::for_each_property(PROP_CALLBACK pcb, void* jolly)
{
  return xvt_prop_for_each(win().win(), pcb, jolly) != 0;
}

static TString_array _items;

bool TProp_field::parse_item(TScanner& scanner)
{
  if (scanner.key() == "IT")
  {
    const TString80 label = dictionary_translate(scanner.string());
    TString16 type = scanner.pop();
    TString80 name, value;
    if (type.starts_with("IT"))
    {
      type.cut(0);
      scanner.push();
    }
    else
    {
      const TString& line = scanner.line();
      int equal = line.find('=');
      if (equal < 0) equal = line.find(' ');
      if (equal > 0)
      {
        name = line.left(equal); name.trim();
        value = line.mid(equal+1); value.trim();
        if (value[0] == '"') { value.rtrim(1); value.ltrim(1); }
      }
      else
      {
        name.trim();
        value.cut(0);
      }
      type.cut(2);
    }

    TToken_string* item = new TToken_string;
    item->add(type); item->add(name); item->add(value); item->add(label,3);
    _items.add(item);
    return true;
  }
  return TWindowed_field::parse_item(scanner);
}

TField_window* TProp_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
{ 
  TProp_window* pw = new TProp_window(x, y, dx, dy, parent, this); 
  if (!_items.empty())
  {
    WINDOW pg = pw->win();
    xvt_prop_suspend(pg); 
    TString8 type;
    TString80 name, value, label;
    FOR_EACH_ARRAY_ROW(_items, i, row)
    {
      row->get(0, type); row->get(1, name); 
      row->get(2, value); row->get(3, label);
      if (type.full())
      {
        if (type == "CO")
          xvt_prop_add(pg, "color", name, value, label); else
        if (type == "NU" || type == "CU")
          xvt_prop_add(pg, "long", name, value, label); 
        else
          xvt_prop_add(pg, "string", name, value, label);
      }
      else
        xvt_prop_add(pg, "", "", "", label); 
    }
    xvt_prop_restart(pg); 
    _items.destroy();
  }
  return pw;
}

TProp_field::TProp_field(TMask* m) : TWindowed_field(m) 
{ }