#include "wxinc.h"
#include "xvt.h"
#include "xvtart.h"
#include "xvtwin.h"
#include "statbar.h"
#include "treelistctrl.h"

#include <wx/artprov.h>
#include <wx/filename.h>
#include <wx/tokenzr.h>
#include <wx/treectrl.h>
#include <wx/vlbox.h>
#include <wx/aui/aui.h>
#include <wx/propgrid/propgrid.h>

///////////////////////////////////////////////////////////
// Utility functions
///////////////////////////////////////////////////////////

static wxBitmap Image2Bitmap(XVT_IMAGE image, int maxx, int maxy, BOOLEAN trans)
{
  if (image == NULL || !((wxImage*)image)->IsOk())
    return wxNullBitmap;

  wxImage img = *(wxImage*)image;

  int w = img.GetWidth(), h = img.GetHeight();
  if (w > maxx || h > maxy)
  {
    const double mx = (maxx / 2) * 2, my = (maxy / 2) * 2;
    const double rx = mx / w, ry = my / h;
    const double r = rx < ry ? rx : ry;
    w = int(w * r); h = int(h * r);
    img.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
  }

  if (trans && !img.HasMask())
  {
    const int r = img.GetRed(0,0);
    const int g = img.GetGreen(0,0);
    const int b = img.GetBlue(0,0);
    img.SetMask();
    img.SetMaskColour(r, g, b);
  }
  return wxBitmap(img);
}

static int RoundToIcon(int nSize)
{
  nSize = ((nSize+3) / 8) * 8;
  if (nSize <  16) nSize =  16;
  if (nSize > 128) nSize = 128;
  return nSize;
}

void Image2Colors(const wxImage& img, wxColour& mean, wxColour& dark, wxColour& light)
{
  dark = *wxWHITE;
  mean = *wxLIGHT_GREY;
  light = *wxBLACK;
  double r=0, g=0, b=0;

  const int h = img.GetHeight();
  for (int i = 0; i < h; i++)
  {
    const wxColourBase::ChannelType cr = img.GetRed(i,i);
    const wxColourBase::ChannelType cg = img.GetGreen(i,i);
    const wxColourBase::ChannelType cb = img.GetBlue(i,i);
    r += cr; g += cg; b += cb;
  }
  r/=h; g/=h; b/=h;
  mean = wxColour(r, g, b);
  dark = wxColour(r*0.8, g*0.8, b*0.8);
  light = wxColour(min(r*1.2,255), min(g*1.2,255), min(b*1.2,255));
}


static wxAuiDockArt* FindArtist(wxWindow* pWindow)
{
  wxAuiDockArt* pArtist = NULL;
  const wxAuiManager* pManager = wxAuiManager::GetManager(pWindow);
  if (pManager != NULL)
    pArtist = pManager->GetArtProvider();
  return pArtist;
}

///////////////////////////////////////////////////////////
// Controls functions
///////////////////////////////////////////////////////////

class TwxScrollBar : public wxScrollBar
{
protected:
  virtual bool AcceptsFocus() const { return false; } // Altrimenti mette il flag wxTAB_TRAVERSAL

public:
  TwxScrollBar(wxWindow *parent, wxWindowID id,
               const wxPoint& pos, const wxSize& size, long style)
  { Create(parent, id, pos, size, style); }
};

class TwxNoteBook : public wxAuiNotebook
{
  enum { BOOK_ICO_SIZE = 16 };
  bool m_bSuspended;

  DECLARE_EVENT_TABLE()
  DECLARE_DYNAMIC_CLASS(TwxNoteBook)
  
protected:
  virtual bool SetBackgroundColour(const wxColour& col);

  void OnChar(wxKeyEvent& evt);
  void OnPageChanging(wxAuiNotebookEvent& e);
  void OnPageChanged(wxAuiNotebookEvent& e);
  long Flags2Style(long flags) const;

  TwxNoteBook() {}

public:
  int ChangeSelection(size_t tab_no); // wxNotebook had it!
  void SetTabImage(size_t tab_no, XVT_IMAGE img);
  
  short AddTab(wxWindow* pPage, const wxString text, XVT_IMAGE img = NULL, short idx = -1);
  TwxNoteBook(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style);
  ~TwxNoteBook();
};

WX_DECLARE_VOIDPTR_HASH_MAP(int, XVT_IMAGE_Map);

class TwxTreeCtrl : public wxTreeCtrl
{
	XVT_IMAGE_Map m_img;
  wxColour m_clrSelFore, m_clrSelBack, m_clrDisFore;
  int m_nFrozen;

private:
  int img2int(XVT_IMAGE img); // Store img into internal image list
  void OnClick(wxTreeEvent& evt, bool bDouble);

protected:
  DECLARE_EVENT_TABLE();
  void OnExpanding(wxTreeEvent& e);  // Called when node in about to be expanded
  void OnCollapsed(wxTreeEvent& e);  // Called when node is collapsed
  void OnSelected(wxTreeEvent& e);   // Calls OnClick(e, false)
  void OnActivated(wxTreeEvent& e);  // Calls OnClick(e, true)
  void OnRightDown(wxMouseEvent& e);

  virtual wxColour GetItemTextColour(const wxTreeItemId& id) const;
  virtual wxColour GetItemBackgroundColour(const wxTreeItemId& id) const;

public:
  void SetNodeImages(const wxTreeItemId& id, XVT_IMAGE item_image, 
                     XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image);
  void SetColors(const XVT_COLOR_COMPONENT* colors);
  void Suspend();
  void Resume();
  void Enable(const wxTreeItemId& id, bool on);
  TwxTreeCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size);
};

class TwxTreeListCtrl : public wxTreeListCtrl
{
	XVT_IMAGE_Map m_img;
  wxColour m_clrSelFore, m_clrSelBack, m_clrDisFore;
  int m_nFrozen;

private:
  int img2int(XVT_IMAGE img); // Store img into internal image list

protected:
  DECLARE_EVENT_TABLE();
  void OnExpanding(wxTreeEvent& e);  // Called when node is about to be expanded
  void OnExpanded(wxTreeEvent& e);   // Called when node has been expanded
  void OnCollapsed(wxTreeEvent& e);  // Called when node has been collapsed
  void OnSelChanged(wxTreeEvent& e); // Called when node has been selected

public:
  void SetNodeImages(const wxTreeItemId& id, XVT_IMAGE item_image, 
                     XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image);
  void SetColors(const XVT_COLOR_COMPONENT* colors);
  void Suspend();
  void Resume();
  void Enable(const wxTreeItemId& id, bool on);
  TwxTreeListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size);
};

struct TwxOutlookItem
{
  wxString m_strText;
  short m_nIconId;
  int m_nFlags;
};

class TwxOutlookBar : public wxVListBox
{
  enum { MAX_ITEMS = 32 };
  TwxOutlookItem m_item[MAX_ITEMS];
  int m_nHovering;

  DECLARE_EVENT_TABLE()

protected:
  virtual void OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const;
  virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const;
  virtual wxCoord OnMeasureItem(size_t n) const;
  virtual void OnMouseMove(wxMouseEvent& e);
  virtual void OnMouseLeave(wxMouseEvent& e);
  virtual void OnSelected(wxCommandEvent& e);

public:
  int Add(short nIconId, const wxString strText, int nFlags);

  TwxOutlookBar(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style);
	~TwxOutlookBar();
};

class TwxPopUp : public wxVListBox
{
  DECLARE_DYNAMIC_CLASS(TwxPopUp);

  wxArrayString m_menu;
  int m_nHovering;
  wxCoord m_nRowHeight;
  wxColour m_clrBack, m_clrFore;

  DECLARE_EVENT_TABLE()
  void NotifySelection();
  TwxPopUp() { wxFAIL; }

protected:
  virtual void OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const;
  virtual void OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const;
  virtual wxCoord OnMeasureItem(size_t n) const;
  virtual void OnMouseMove(wxMouseEvent& e);
  virtual void OnKillFocus(wxFocusEvent& e);
  virtual void OnSelected(wxCommandEvent& e);
  virtual void OnKeyDown(wxKeyEvent& e);

public:
  int Add(const wxString str);
  void SetSelectForeColor(const wxColour& rgb) { m_clrFore = rgb; } 
  void SetSelectBackColor(const wxColour& rgb) { m_clrBack= rgb; } 
  TwxPopUp(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size);
};

class TwxPropertyGrid : public wxPropertyGrid
{
  DECLARE_EVENT_TABLE()
protected:
  void OnPropertyChanged(wxPropertyGridEvent& evt);
public:
  void SetColors(const XVT_COLOR_COMPONENT* colors);
  TwxPropertyGrid(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style);
};

WINDOW xvt_ctl_create_def(WIN_DEF* win_def_p, WINDOW parent_win, long app_data)
{
  wxASSERT(win_def_p != NULL);
	const wxRect rct = RCT2Rect(&win_def_p->rct);
  wxWindow* pParent = wxStaticCast((wxObject*)parent_win, wxWindow);
  const wxWindowID id = win_def_p->v.ctl.ctrl_id;

  WINDOW win = NULL_WIN;
  switch (win_def_p->wtype)
	{
  case WC_HSCROLL:	/* horizontal scrollbar control */
  case WC_VSCROLL:	/* vertical scrollbar control */
		{
      long style = win_def_p->wtype == WC_HSCROLL ? wxSB_HORIZONTAL : wxSB_VERTICAL;
			style |= wxCLIP_SIBLINGS;
		  TwxScrollBar* sb = new TwxScrollBar(pParent, id, rct.GetPosition(), rct.GetSize(), style);
			win = (WINDOW)sb;
		}
		break;
  case WC_HGAUGE:	/* horizontal progress bar control */
  case WC_VGAUGE:	/* vertical progress bar control */
    {
      const long style = (win_def_p->wtype == WC_HGAUGE) ? wxGA_HORIZONTAL : wxGA_VERTICAL;
      wxGauge* pg = new wxGauge(pParent, id, app_data, rct.GetPosition(), rct.GetSize(), style);
      win = (WINDOW)pg;
    }
    break;
  case WC_HSLIDER:	/* horizontal slider control */
  case WC_VSLIDER:	/* vertical slider control */
		{
			const long style = win_def_p->wtype == WC_HSLIDER ? wxSL_HORIZONTAL : wxSL_VERTICAL;
		  wxSlider* sc = new wxSlider(pParent, id, 0, 0, app_data, rct.GetPosition(), rct.GetSize(), style);
			win = (WINDOW)sc;
		}
		break;
  case WC_PUSHBUTTON: /* bottone normale */
    {
      wxButton* pb = NULL;
      if (win_def_p->text && *win_def_p->text) // Bottone normale con label
        pb = new wxButton(pParent, id, win_def_p->text, rct.GetPosition(), rct.GetSize()); 
      else
        pb = new wxBitmapButton(pParent, id, wxNullBitmap, rct.GetPosition(), rct.GetSize()); 
      win = (WINDOW)pb;
    }
    break;
  case WC_CHECKBOX: /* check box */
    {
      long style = wxCHK_2STATE | wxCLIP_SIBLINGS;
      if (win_def_p->wtype == CTL_FLAG_RIGHT_JUST)
        style |= wxALIGN_RIGHT;
      wxCheckBox* cb = new wxCheckBox(pParent, id, win_def_p->text,
			                                rct.GetPosition(), rct.GetSize(), style); 
      win = (WINDOW)cb;
    }
    break;
  case WC_RADIOBUTTON: /* radio button */
    {
      const long style = wxRB_SINGLE | wxCLIP_SIBLINGS;
      wxRadioButton* rb = new wxRadioButton(pParent, id, win_def_p->text,
			                                      rct.GetPosition(), rct.GetSize(), style); 
      win = (WINDOW)rb;
    }
    break;
  case WC_NOTEBK:
    {
      TwxNoteBook* nb = new TwxNoteBook(pParent, id, rct.GetPosition(), rct.GetSize(), win_def_p->v.ctl.flags);
      win = (WINDOW)nb;
    }
    break;
  case WC_TREE:
    {
      TwxTreeCtrl* tv = new TwxTreeCtrl(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)tv;
    }
    break;
  case WC_LBOX:
    {
      wxListBox* tlb = new wxListBox(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)tlb;
    }
    break;
  case WC_OUTLOOKBAR:
    {
      long style = 0;
      TwxOutlookBar* tob = new TwxOutlookBar(pParent, id, rct.GetPosition(), rct.GetSize(), style);
      win = (WINDOW)tob;
    }
    break;
  case WC_POPUP:
    {
      TwxPopUp* tpu = new TwxPopUp(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)tpu;
    }
    break;
  case WC_PROPGRID:
    {
      long flags = wxPG_BOLD_MODIFIED | wxPG_SPLITTER_AUTO_CENTER | wxPG_DESCRIPTION | wxPG_TOOLTIPS;
      TwxPropertyGrid* pg = new TwxPropertyGrid(pParent, id, rct.GetPosition(), rct.GetSize(), flags);
      win = (WINDOW)pg;
    }
    break;
  case WC_TREELIST:
    {
      TwxTreeListCtrl* tv = new TwxTreeListCtrl(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)tv;
    }
    break;
	default:
		SORRY_BOX(); break;
	}

  if (win != NULL_WIN)
  {
    wxWindow& w = *wxStaticCast((wxObject*)win, wxWindow);
    const long flags = win_def_p->v.ctl.flags;
    if (flags & CTL_FLAG_INVISIBLE) w.Hide();
    if (flags & CTL_FLAG_DISABLED) w.Disable();

    XVT_FNTID font_id = win_def_p->v.ctl.font_id;
    const bool bDestroyFont = font_id == NULL;
    if (bDestroyFont)
      font_id = xvt_dwin_get_font(parent_win);
    if (font_id != NULL)
    {
      const	wxFont& font = wxStaticCast(font_id, TFontId)->Font(NULL, win);
      w.SetFont(font);
      if (bDestroyFont)
        xvt_font_destroy(font_id);
    }
    xvt_ctl_set_colors(win, win_def_p->ctlcolors, XVT_COLOR_ACTION_SET);
  }

	return win;
}

void xvt_ctl_check_radio_button(WINDOW win, WINDOW* wins, int NbrWindows)
{ 
  wxASSERT(wins != NULL_WIN && NbrWindows >= 2);
  for (int i = 0; i < NbrWindows; i++)
  {
    wxRadioButton* rb = wxDynamicCast((wxObject*)wins[i], wxRadioButton);
    if (rb != NULL)
      rb->SetValue(win == wins[i]);
  }
}

void xvt_ctl_set_checked(WINDOW win, BOOLEAN bCheck)
{ 
  wxCheckBox* cb = wxDynamicCast((wxObject*)win, wxCheckBox);
  if (cb != NULL)
    cb->SetValue(bCheck != 0);
}

void xvt_ctl_set_colors(WINDOW win, const XVT_COLOR_COMPONENT* colors, XVT_COLOR_ACTION action)
{
  // Non posso usare wxControl in quanto certi controlli derivano da wxWindow
  wxWindow* w = wxDynamicCast((wxObject*)win, wxWindow);
  if (w != NULL && colors != NULL)
  {
    if (action == XVT_COLOR_ACTION_SET)
    {
      switch (xvt_vobj_get_type(win))
      {
      case WC_TREE    : wxStaticCast(w, TwxTreeCtrl)->SetColors(colors);     return;
      case WC_PROPGRID: wxStaticCast(w, TwxPropertyGrid)->SetColors(colors); return;
      case WC_TREELIST: wxStaticCast(w, TwxTreeListCtrl)->SetColors(colors); return;
      default: break;;
      }
      for (int i = 0; colors[i].type; i++)
      {
        CAST_COLOR(colors[i].color, rgb);
        switch(colors[i].type)
        {
        case XVT_COLOR_BACKGROUND: w->SetOwnBackgroundColour(rgb); break;
        case XVT_COLOR_FOREGROUND: w->SetOwnForegroundColour(rgb); break;
        case XVT_COLOR_HIGHLIGHT:
          {
            TwxPopUp* tpu = wxDynamicCast(w, TwxPopUp);
            if (tpu != NULL)
              tpu->SetSelectForeColor(rgb);
          }
          break;
        case XVT_COLOR_SELECT: 
          {
            TwxPopUp* tpu = wxDynamicCast(w, TwxPopUp);
            if (tpu != NULL)
              tpu->SetSelectBackColor(rgb);
          }
          break;
        case XVT_COLOR_BLEND: 
          if (!w->IsKindOf(CLASSINFO(wxButton)))
            w->SetOwnBackgroundColour(rgb); 
          break;
        default:
          break;
        }
      }
    }
    else
    {
      // ???
    }
  }
}

///////////////////////////////////////////////////////////
// Buttons
///////////////////////////////////////////////////////////

void xvt_btn_set_images(WINDOW win, XVT_IMAGE up, XVT_IMAGE down)
{
  if (win != NULL_WIN && up != NULL)
  {
    wxBitmapButton* pb = wxDynamicCast((wxObject*)win, wxBitmapButton);
    if (pb != NULL)
    {
      int mx, my; pb->GetSize(&mx, &my);
      wxBitmap bmpUp = Image2Bitmap(up, mx, my, TRUE);
      if (bmpUp.Ok())
      {
        pb->SetBitmapLabel(bmpUp);
        const wxImage imgGay = ((wxImage*)up)->ConvertToGreyscale();
        wxBitmap bmpGay(imgGay);
        pb->SetBitmapDisabled(bmpGay);
      } 
      if (down != NULL)
      {
        wxBitmap bmpDown = Image2Bitmap(down, mx, my, TRUE);
        if (bmpDown.Ok())
          pb->SetBitmapSelected(bmpDown);
      }
      else
      {
        if (bmpUp.Ok())
          pb->SetBitmapSelected(bmpUp);
      }
    }
  }
}

///////////////////////////////////////////////////////////
// Pane interface
///////////////////////////////////////////////////////////

static wxAuiManager* FindPaneManager(WINDOW win)
{
  wxAuiManager* pManager = NULL;
  if (win != NULL_WIN)
  {
    wxWindow* pwin = wxStaticCast((wxObject*)win, wxWindow);
    pManager = wxAuiManager::GetManager(pwin);
  }
  return pManager;
}

static wxAuiPaneInfo* LockPane(WINDOW win)
{
  wxAuiManager* pManager = FindPaneManager(win);
  if (pManager != NULL)
  {
    wxAuiPaneInfo& pane = pManager->GetPane((wxWindow*)win);
    if (pane.IsOk())
      return &pane;
  }
  return NULL;
}

static void UnlockPane(WINDOW win)
{
  wxAuiManager* pManager = FindPaneManager(win);
  if (pManager != NULL)
    pManager->Update();
}

BOOLEAN xvt_pane_add(WINDOW win, WINDOW pane, const char* name, int dock, int flags)
{
  BOOLEAN done = FALSE;
  if (win != NULL_WIN && pane != NULL_WIN && name && *name)
  {
    TwxWindow* owner = wxStaticCast((wxObject*)win, TwxWindow);
    wxWindow*  child = wxStaticCast((wxObject*)pane, wxWindow);
    done = owner->AddPane(child, name, dock, flags);
  }
  return done;
}

BOOLEAN xvt_pane_set_title(WINDOW win, const char* title)
{
  wxAuiPaneInfo* pane = LockPane(win);
  if (pane != NULL)
  {
  	pane->Caption(title);
    UnlockPane(win);
  }
  return pane != NULL;
}

XVTDLL BOOLEAN xvt_pane_change_flags(WINDOW win, int set, int rst)
{
  wxAuiPaneInfo* pane = LockPane(win);
  if (pane != NULL && (set || rst))
  {
    if (set) pane->SetFlag(set, true);
    if (rst) pane->SetFlag(rst, false);
    UnlockPane(win);
  }
  return pane != NULL;
}

XVTDLL BOOLEAN xvt_pane_detach(WINDOW win)
{
  BOOLEAN ok = FALSE;
  wxAuiManager* pManager = FindPaneManager(win);
  if (pManager != NULL)
  {
    ok = pManager->DetachPane((wxWindow*)win);
    pManager->Update();
  }
  return ok;
}

XVTDLL BOOLEAN xvt_pane_manager_load_perspective(WINDOW win, const char* perspective)
{
  BOOLEAN ok = FALSE;
  if (perspective && *perspective)
  {
    wxAuiManager* pManager = FindPaneManager(win);
    if (pManager != NULL)
    {
      const wxString str = perspective;
      ok = pManager->LoadPerspective(str, true);
    }
  }
  return ok;
}

XVTDLL int xvt_pane_manager_save_perspective(WINDOW win, char* perspective, int max_size)
{
  int nSize = 0;
  wxAuiManager* pManager = FindPaneManager(win);
  if (pManager != NULL)
  {
    const wxString str = pManager->SavePerspective();
    nSize = str.Len()+1;
    if (perspective != NULL && max_size > 0)
      wxStrncpy(perspective, str, max_size);
  }
  return nSize;
}

XVTDLL BOOLEAN xvt_pane_set_size_range(WINDOW win, int min_size, int best_size, int max_size)
{
  BOOLEAN ok = FALSE;
  wxAuiPaneInfo* pane = LockPane(win);
  if (pane != NULL)
  {
    if (min_size > 0 || max_size > 0)
    {
      if (best_size <= 0)
      {
        if (min_size > 0)
          best_size = max_size > 0 ? (min_size+max_size) / 2 : min_size;
        else
          best_size = max_size;
      }
      wxSize szMin(-1, -1), szBst(-1, -1), szMax(-1, -1);
      if (pane->IsTopDockable() || pane->IsBottomDockable())
      {
        szMin.y = min_size;
        szBst.y = best_size;
        szMax.y = max_size;
      }
      else
      {
        szMin.x = min_size;
        szBst.x = best_size;
        szMax.x = max_size;
      }
      pane->MinSize(szMin);
      pane->BestSize(szBst);
      pane->MaxSize(szMax);
    }
    pane->Resizable(min_size != max_size);
    pane->DockFixed(min_size == max_size);
    UnlockPane(win);
  }
  return ok;
}

///////////////////////////////////////////////////////////
// Notebook interface
///////////////////////////////////////////////////////////

class TwxAuiDefaultTabArt : public wxAuiDefaultTabArt
{
public:
  void SetBackgroundColour(const wxColor& colour) { m_base_colour = colour; }
  virtual wxAuiTabArt* Clone();
};

wxAuiTabArt* TwxAuiDefaultTabArt::Clone()
{
  TwxAuiDefaultTabArt* art = new TwxAuiDefaultTabArt();

  // Copy'n'paste from aui/auibook.cpp
  art->SetNormalFont(m_normal_font);
  art->SetSelectedFont(m_selected_font);
  art->SetMeasuringFont(m_measuring_font);

  // My own addition
  art->m_base_colour = m_base_colour;
  
  return art;
}

IMPLEMENT_DYNAMIC_CLASS(TwxNoteBook, wxAuiNotebook)

#define CAST_NOTEBOOK(win, nb) TwxNoteBook& nb = *wxStaticCast((wxObject*)win, TwxNoteBook);

inline bool VALID_NOTEBOOK(WINDOW notebk, short page_no)
{ return page_no >= 0 && wxDynamicCast((wxObject*)notebk, TwxNoteBook)!=NULL; }

BEGIN_EVENT_TABLE(TwxNoteBook, wxAuiNotebook)
  EVT_AUINOTEBOOK_PAGE_CHANGING(wxID_ANY, TwxNoteBook::OnPageChanging)
  EVT_AUINOTEBOOK_PAGE_CHANGED(wxID_ANY, TwxNoteBook::OnPageChanged)
  EVT_CHAR(TwxNoteBook::OnChar)
END_EVENT_TABLE();

bool TwxNoteBook::SetBackgroundColour(const wxColour& col)
{
  const bool ok = wxAuiNotebook::SetBackgroundColour(col);
  if (ok) // Se cambio lo sfondo del tab control devo notificarlo all'art provider
  {
    TwxAuiDefaultTabArt* pArtist = (TwxAuiDefaultTabArt*)GetArtProvider();
    if (pArtist != NULL) 
      pArtist->SetBackgroundColour(col);
  }
  return ok;
}

void TwxNoteBook::OnChar(wxKeyEvent& evt)
{
  // Ridirige i tasti che non sono certamente di navigazione alla finestra nonna
  const int kc = evt.GetKeyCode();
  if (kc >= WXK_F1 && kc <= WXK_F24 || kc == WXK_ESCAPE || kc == WXK_RETURN)
  {
    TwxWindow* gp = wxDynamicCast(GetGrandParent(), TwxWindow);
    if (gp != NULL)
      gp->ProcessEvent(evt);
    else
      evt.Skip();
  }
}

void TwxNoteBook::OnPageChanging(wxAuiNotebookEvent& evt)
{
  if (!m_bSuspended)
  {
    m_bSuspended = true;
    XVT_EVENT e(E_CONTROL);
    CONTROL_INFO& ci = e.v.ctl.ci;
    e.v.ctl.id = evt.GetId();
    ci.type = WC_NOTEBK;
    ci.win = WINDOW(this);
    // page == NULL_WIN -> changing page; page != NULL_WIN -> page changed.
    ci.v.notebk.page = NULL_WIN;
    ci.v.notebk.page_new = evt.GetSelection();
    ci.v.notebk.page_old = evt.GetOldSelection();

    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    const bool refused = win->DoXvtEvent(e) != 0;
    if (refused)
      evt.Veto(); // Vieta il passaggio alla pagina nuova
    else
      evt.Skip(); // Permette la notifica dell'evento PageChanged

    m_bSuspended = false;
  }
}

void TwxNoteBook::OnPageChanged(wxAuiNotebookEvent& evt)
{
  // Mando la notifica solo al book in basso di ba0
  //if (m_flags & wxAUI_NB_BOTTOM) // Perch� mai solo a ba0?
  
  if (!m_bSuspended)
  {
    m_bSuspended = true;
    XVT_EVENT e(E_CONTROL);
    CONTROL_INFO& ci = e.v.ctl.ci;
    e.v.ctl.id = evt.GetId();
    ci.type = WC_NOTEBK;
    ci.win = WINDOW(this);
    // page == NULL_WIN -> changing page; page != NULL_WIN -> page changed.
    ci.v.notebk.page = (WINDOW)GetPage(evt.GetSelection()); 
    ci.v.notebk.page_new = evt.GetSelection();
    ci.v.notebk.page_old = evt.GetOldSelection();

    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    win->DoXvtEvent(e);
    m_bSuspended = false;
  }
}

short TwxNoteBook::AddTab(wxWindow* pPage, const wxString text, XVT_IMAGE xvt_img, short idx)
{
  wxBitmap bmp = Image2Bitmap(xvt_img, BOOK_ICO_SIZE, BOOK_ICO_SIZE, TRUE);

  if (idx < 0 || idx >= (int)GetPageCount())
  {
    AddPage(pPage, text, false, bmp);
    idx = GetPageCount()-1;
  }
  else
    InsertPage(idx, pPage, text, false, bmp);

  return idx;
}

void TwxNoteBook::SetTabImage(size_t idx, XVT_IMAGE img)
{
  wxBitmap bmp = Image2Bitmap(img, BOOK_ICO_SIZE, BOOK_ICO_SIZE, TRUE);
  SetPageBitmap(idx, bmp);
}

int TwxNoteBook::ChangeSelection(size_t tab_no)
{
  const size_t nSel = GetSelection();
  if (!m_bSuspended && tab_no != nSel)
  {
    m_bSuspended = true;
    SetSelection(tab_no);
    m_bSuspended = false;
  }
  return nSel;
}

long TwxNoteBook::Flags2Style(long flags) const
{
  long style = wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS;
  bool bottom = false;

#if wxCHECK_VERSION(2,8,9)
  if (flags & (CTL_FLAG_TAB_TOP|CTL_FLAG_TAB_BOTTOM|CTL_FLAG_TAB_LEFT|CTL_FLAG_TAB_RIGHT))
  {
    bottom = (flags & CTL_FLAG_TAB_BOTTOM) != 0;
  }
  else
#endif
  {
    bottom = (flags & CTL_FLAG_CENTER_JUST) != 0;
  }

  if (bottom)
    style |= wxAUI_NB_BOTTOM;
  else
    style |= wxAUI_NB_TOP;

  return style;
}

TwxNoteBook::TwxNoteBook(wxWindow *parent, wxWindowID id, 
                         const wxPoint& pos, const wxSize& size, long flags)
           : wxAuiNotebook(parent, id, pos, size, Flags2Style(flags)), m_bSuspended(false)
{ 
  SetArtProvider(new TwxAuiDefaultTabArt);
  _nice_windows.Put((WINDOW)this, this); // Serve per poter fare la xvt_vobj_destroy

  wxAuiTabCtrl* atc = GetActiveTabCtrl();
  if (atc != NULL)
#if wxCHECK_VERSION(2,9,0)    
    atc->GetEventHandler().Bind(wxEVT_CHAR, (functor)&TwxNoteBook::OnChar);
#else
    atc->Connect(wxEVT_CHAR, (wxObjectEventFunction)&TwxNoteBook::OnChar);
#endif
}

TwxNoteBook::~TwxNoteBook()
{ 
  m_bSuspended = true;
  _nice_windows.Delete((WINDOW)this); 
}

short xvt_notebk_add_page(WINDOW notebk, WINDOW page, const char* title, XVT_IMAGE image, short tab_no)
{
  short idx = -1;
  if (notebk != NULL_WIN)
  {
    CAST_NOTEBOOK(notebk, nb);
    wxString strTitle = title;
    if (strTitle.IsEmpty() && page != NULL_WIN)
    {
      wxWindow* pg = wxStaticCast((wxObject*)page, wxWindow);
      strTitle = pg->GetLabel();
    }
    idx = nb.AddTab((wxWindow*)page, strTitle, image, tab_no);
  }
  return idx;
}

WINDOW xvt_notebk_get_page(WINDOW notebk, short tab_no)
{
  WINDOW page = NULL_WIN;
  if (VALID_NOTEBOOK(notebk, tab_no))
  {
    CAST_NOTEBOOK(notebk, nb);
    page = (WINDOW)nb.GetPage(tab_no);
  }
  return page;
}

short xvt_notebk_get_num_tabs(WINDOW notebk)
{
  short pg = 0;
  if (notebk != NULL_WIN)
  {
    CAST_NOTEBOOK(notebk, nb);
    pg = nb.GetPageCount();
  }
  return pg;
}

void xvt_notebk_rem_page(WINDOW notebk, short page_no)
{
  WINDOW win = xvt_notebk_get_page(notebk, page_no);
  if (win != NULL_WIN)
  {
    xvt_notebk_rem_tab(notebk, page_no);
    xvt_vobj_destroy(win);
  }
}

void xvt_notebk_rem_tab(WINDOW notebk, short tab_no)
{
  if (VALID_NOTEBOOK(notebk, tab_no))
  {
    CAST_NOTEBOOK(notebk, nb);
    nb.RemovePage(tab_no);
  }
}

void xvt_notebk_set_front_page(WINDOW notebk, short tab_no)
{
  if (VALID_NOTEBOOK(notebk, tab_no))
  {
    CAST_NOTEBOOK(notebk, nb);
    wxWindow* w = nb.GetPage(tab_no);
    if (w != NULL)
    {
      nb.ChangeSelection(tab_no);  // Non genera evento di cambio pagina!
      if (!w->IsShown())           // A volte succede che la prima pagina sia nascosta!
        w->Show(true);
    }
  }
}

short xvt_notebk_get_front_page(WINDOW notebk)
{
  short idx = -1;
  if (notebk != NULL_WIN)
  {
    CAST_NOTEBOOK(notebk, nb);
    idx = nb.GetSelection();
  }
  return idx;
}


char* xvt_notebk_get_tab_title(WINDOW notebk, short tab_no, char* title, int sz_title)
{
  if (VALID_NOTEBOOK(notebk, tab_no))
  {
    CAST_NOTEBOOK(notebk, nb);
	  wxStrncpy(title, nb.GetPageText(tab_no), sz_title);
	  title[sz_title-1] = '\0';
  }
  else
    *title = '\0';
  return title;
}

void xvt_notebk_set_page_title(WINDOW notebk, short tab_no, const char* title)
{
  WINDOW win = xvt_notebk_get_page(notebk, tab_no);
  if (win != NULL_WIN)
    xvt_vobj_set_title(win, title);
}

void xvt_notebk_set_tab_image(WINDOW notebk, short tab_no, XVT_IMAGE img)
{
  if (notebk != NULL_WIN && tab_no >= 0)
  {
    CAST_NOTEBOOK(notebk, nb);
    nb.SetTabImage(tab_no, img); // Se img=NULL toglie l'immagine
  }
}
     
void xvt_notebk_set_tab_icon(WINDOW notebk, short tab_no, int rid)
{
  const wxString strName = xvtart_GetResourceName("Icon", rid);  
  XVT_IMAGE img = xvt_image_read(strName);
  xvt_notebk_set_tab_image(notebk, tab_no, img);
  xvt_image_destroy(img);
}

void xvt_notebk_set_tab_title(WINDOW notebk, short tab_no, const char* title)
{
  if (VALID_NOTEBOOK(notebk, tab_no))
  {
    CAST_NOTEBOOK(notebk, nb);
    const short pc = nb.GetPageCount();
    if (tab_no >= pc)
      nb.AddTab(nb.GetPage(pc-1), title, NULL, pc);
    else
      nb.SetPageText(tab_no, title);
  }
}

///////////////////////////////////////////////////////////
// TreeCtrl interface
///////////////////////////////////////////////////////////

BEGIN_EVENT_TABLE(TwxTreeCtrl, wxTreeCtrl)
  EVT_TREE_ITEM_EXPANDING(wxID_ANY, TwxTreeCtrl::OnExpanding)
  EVT_TREE_ITEM_COLLAPSED(wxID_ANY, TwxTreeCtrl::OnCollapsed)
  EVT_TREE_SEL_CHANGED(wxID_ANY,    TwxTreeCtrl::OnSelected)
  EVT_TREE_ITEM_ACTIVATED(wxID_ANY, TwxTreeCtrl::OnActivated)
  EVT_RIGHT_DOWN(TwxTreeCtrl::OnRightDown)
END_EVENT_TABLE();

#define CAST_TREEVIEW(win, tv) TwxTreeCtrl& tv = *wxStaticCast((wxObject*)win, TwxTreeCtrl);

struct TwxTreeItemData : public wxTreeItemData
{
  wxString m_strData; // Assumo sempre una stringa come dati
};

void TwxTreeCtrl::OnExpanding(wxTreeEvent& evt)
{
  if (!m_nFrozen)
  {
    const wxTreeItemId id = evt.GetItem();
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.type = WC_TREE;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.treeview.node = id.m_pItem;
    e.v.ctl.ci.v.treeview.expanded  = TRUE;   
    if (GetChildrenCount(id) == 0) // Trucco perfido ...
      e.v.ctl.ci.v.treeview.collapsed = TRUE; // ... stato indeterminato = EXPANDING
    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    win->DoXvtEvent(e);
    if (GetChildrenCount(id) == 0) // Allora e' proprio vero ...
      SetItemHasChildren(id, false);
  }
}

void TwxTreeCtrl::OnCollapsed(wxTreeEvent& evt)
{
  if (!m_nFrozen)
  {
    Suspend();
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.type = WC_TREE;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.treeview.node = evt.GetItem().m_pItem;
    e.v.ctl.ci.v.treeview.collapsed = TRUE;
    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    win->DoXvtEvent(e);
    Resume();
  }
}

void TwxTreeCtrl::OnClick(wxTreeEvent& evt, bool bDouble)
{
  if (!m_nFrozen)
  {
    Suspend();
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.type = WC_TREE;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.treeview.node = evt.GetItem().m_pItem;
    if (bDouble)
      e.v.ctl.ci.v.treeview.dbl_click = TRUE;
    else
      e.v.ctl.ci.v.treeview.sgl_click = TRUE;
    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    win->DoXvtEvent(e);
    Resume();
  }
}

wxColour TwxTreeCtrl::GetItemTextColour(const wxTreeItemId& id) const
{
  if (IsSelected(id))
    return m_clrSelFore;
  return wxTreeCtrl::GetItemTextColour(id);
}

wxColour TwxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& id) const
{
  if (IsSelected(id))
    return m_clrSelBack;
  return wxTreeCtrl::GetItemBackgroundColour(id);
}

void TwxTreeCtrl::OnSelected(wxTreeEvent& evt)
{ 
  OnClick(evt, false); 
}

void TwxTreeCtrl::OnActivated(wxTreeEvent& evt)
{ 
#if wxCHECK_VERSION(2,8,10)
  const wxTreeItemId id = evt.GetItem();
  if (ItemHasChildren(id))
  {
    if (IsExpanded(id))
      Collapse(id);
    else
      Expand(id);
  }
#endif
  OnClick(evt, true); 
}

void TwxTreeCtrl::OnRightDown(wxMouseEvent& evt)
{
  TwxWindow* pParent = wxDynamicCast(GetParent(), TwxWindow);
  if (pParent != NULL)
  {
    XVT_EVENT e(E_MOUSE_DOWN);
	  e.v.mouse.button = 1;
	  e.v.mouse.control = evt.ControlDown();
	  e.v.mouse.shift = evt.ShiftDown();
	  e.v.mouse.where.h = evt.GetX();
	  e.v.mouse.where.v = evt.GetY();
	  pParent->DoXvtEvent(e);
  }
}

int TwxTreeCtrl::img2int(XVT_IMAGE xvt_img)
{
  int i = -1;
  if (xvt_img != NULL)
  {
    i = m_img[xvt_img] - 1;       // Ho memorizzato indice+1
    if (i < 0)                    // Immagine sconosciuta
    {
      const wxImage& img = *(wxImage*)xvt_img;
      wxImageList* il = GetImageList();
      if (il == NULL)             // Lista non ancora creata
      { 
        il = new wxImageList;
        il->Create(img.GetWidth(), img.GetHeight(), true, 3);
        AssignImageList(il);      // DON'T CALL SetImageList!
      }
      else
      {
        int old_w, old_h; il->GetSize(0, old_w, old_h); 
        const int new_w = img.GetWidth(), new_h = img.GetHeight();
        if (new_w > old_w)  // L'immagine nuova e' troppo grande?
        {
          const int old_ratio = old_w * 100 / old_h;
          const int new_ratio = new_w * 100 / new_h;
          const int old_count = il->GetImageCount();
          wxImageList* nil = new wxImageList;
          nil->Create(new_w, new_h, true, 3*old_count/2);
          for (int k = 0; k < old_count; k++)
          {
            wxImage old = il->GetBitmap(k).ConvertToImage();
            if (old_ratio == new_ratio)
              old.Rescale(new_w, new_h, wxIMAGE_QUALITY_HIGH);
            else
              old.Resize(wxSize(new_w, new_h), wxPoint((new_w-old_w)/2, (new_h-old_h)/2));
            nil->Add(old);
          }
          AssignImageList(il = nil);
        }
      }

      if (!img.HasMask())
      {
        wxImage& trans = (wxImage&)img;
        const int r = img.GetRed(0,0);
        const int g = img.GetGreen(0,0);
        const int b = img.GetBlue(0,0);
        trans.SetMask();
        trans.SetMaskColour(r, g, b);
      }
      const wxBitmap bmp(img);
      i = il->Add(bmp);
      m_img[xvt_img] = i+1;       // Memorizzo indice+1
    }
    if (i < 0)
      SORRY_BOX();   
  }
  return i;
}

void TwxTreeCtrl::SetNodeImages(const wxTreeItemId& id, XVT_IMAGE item_image, 
                                XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image)
{
  const int ii = img2int(item_image);
  if (ii >= 0)
    SetItemImage(id, ii);
  else
  {
    const int ic = img2int(collapsed_image);
    if (ic >= 0)
    {
      SetItemImage(id, ic);
      const int ie = img2int(expanded_image);
      if (ie >= 0)
        SetItemImage(id, ie, wxTreeItemIcon_Selected);
    }
  }
}

void TwxTreeCtrl::Enable(const wxTreeItemId& id, bool on)
{
  SetItemTextColour(id, on ? m_clrSelFore : m_clrDisFore); 
}

void TwxTreeCtrl::Suspend()
{ m_nFrozen++; }

void TwxTreeCtrl::Resume()
{
  wxASSERT(m_nFrozen > 0);
  if (m_nFrozen > 0)
    m_nFrozen--;
}

void TwxTreeCtrl::SetColors(const XVT_COLOR_COMPONENT* colors)
{
  for (int i = 0; colors[i].type; i++)
  {
    CAST_COLOR(colors[i].color, rgb);
    switch(colors[i].type)
    {
    case XVT_COLOR_BACKGROUND: SetOwnBackgroundColour(rgb); break;
    case XVT_COLOR_FOREGROUND: SetOwnForegroundColour(rgb); break;
    case XVT_COLOR_HIGHLIGHT : m_clrSelBack = rgb; break;
    case XVT_COLOR_SELECT    : m_clrSelFore = rgb; break;
    case XVT_COLOR_TROUGH    : m_clrDisFore = rgb; break;
    default                  : break;
    }
  }
}

TwxTreeCtrl::TwxTreeCtrl(wxWindow *parent, wxWindowID id, 
                         const wxPoint& pos, const wxSize& size)
           : wxTreeCtrl(parent, id, pos, size, wxTR_HAS_BUTTONS | wxTR_HIDE_ROOT),
             m_nFrozen(0)
{ 
  AddRoot("Root");
}

WINDOW xvt_treeview_create(WINDOW parent_win,
  RCT * rct_p, char * title, long ctl_flags,
  long app_data, int ctl_id, XVT_IMAGE WXUNUSED(item_image),
  XVT_IMAGE WXUNUSED(collapsed_image), XVT_IMAGE WXUNUSED(expanded_image),
  long WXUNUSED(attrs), int WXUNUSED(line_height))
{
  WIN_DEF win_def; memset(&win_def, 0, sizeof(WIN_DEF));
  win_def.wtype = WC_TREE;
  win_def.rct = *rct_p;
  win_def.text = title;
  win_def.v.ctl.ctrl_id = ctl_id;
  win_def.v.ctl.flags = ctl_flags;
  WINDOW win = xvt_ctl_create_def(&win_def, parent_win, app_data);
  return win;
}

XVT_TREEVIEW_NODE xvt_treeview_add_child_node(WINDOW win,
  XVT_TREEVIEW_NODE parent, XVT_TREEVIEW_NODE_TYPE type,
  XVT_IMAGE item_image, XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image,
  const char* string, XVT_TREEVIEW_CALLBACK WXUNUSED(callback), const char* data)
{
  XVT_TREEVIEW_NODE node = NULL;
  if (win != NULL_WIN)
  {
    CAST_TREEVIEW(win, tv);
    TwxTreeItemData* pData = new TwxTreeItemData;
    pData->m_strData = data;
    wxTreeItemId pa(parent);
    if (!pa.IsOk())
      pa = tv.GetRootItem();
    wxTreeItemId id = tv.AppendItem(pa, string, -1, -1, pData);
    if (id.IsOk())
    {
      tv.SetItemHasChildren(pa, true);
      tv.SetItemHasChildren(id, type == XVT_TREEVIEW_NODE_NONTERMINAL);
      tv.SetNodeImages(id, item_image, collapsed_image, expanded_image);
      tv.SetItemFont(id, tv.GetFont());
      node = id.m_pItem;
    }
  }
  return node;
}

XVT_TREEVIEW_NODE xvt_treeview_get_child_node(WINDOW win, XVT_TREEVIEW_NODE parent_node, 
                                              int position)
{
  XVT_TREEVIEW_NODE child_node = NULL;
  if (win != NULL_WIN && position >= 0)
  {
    CAST_TREEVIEW(win, tv);
    wxTreeItemId parent(parent_node);
    if (!parent.IsOk())
      parent = tv.GetRootItem();

    if (parent.IsOk() && position < (int)tv.GetChildrenCount(parent))
    {
      wxTreeItemIdValue cookie;
      wxTreeItemId id;
      int i = -1;
      for (id = tv.GetFirstChild(parent, cookie), i = -1; 
           i < position && id.IsOk(); id = tv.GetNextChild(parent, cookie), i++);
      child_node = id.m_pItem;
    }
  }
  return child_node;
}

const char* xvt_treeview_get_node_data(WINDOW win, XVT_TREEVIEW_NODE node)
{
  const char* data = NULL;
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    TwxTreeItemData* pData = (TwxTreeItemData*)tv.GetItemData(id);
    if (pData != NULL)
      data = (const char*)pData->m_strData;
  }
  return data;
}

void xvt_treeview_destroy_node(WINDOW win, XVT_TREEVIEW_NODE node)
{ 
  if (win != NULL_WIN && node != NULL)
  {  
    CAST_TREEVIEW(win, tv);
    wxTreeItemId id(node);
    tv.Delete(id);
  }
}

BOOLEAN xvt_treeview_enable_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN on)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREEVIEW(win, tv);
    wxTreeItemId id(node);
    tv.Enable(id, on != FALSE);
  }
  return ok;
}

BOOLEAN xvt_treeview_expand_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN recurse)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREEVIEW(win, tv);
    tv.Suspend();
    const wxTreeItemId id(node);
    if (recurse)
      tv.ExpandAllChildren(id);
    else
      tv.Expand(id);
    tv.Resume();
  }
  return ok;
}

XVT_TREEVIEW_NODE xvt_treeview_get_root_node(WINDOW win)
{
  XVT_TREEVIEW_NODE pRoot = NULL;
  if (win != NULL_WIN)
  {  
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id = tv.GetRootItem();
    pRoot = id.m_pItem;
  }
  return pRoot;
}

XVT_TREEVIEW_NODE xvt_treeview_get_selected_node(WINDOW win)
{
  CAST_TREEVIEW(win, tv);
  const wxTreeItemId id = tv.GetSelection();
  return id.m_pItem;
}

BOOLEAN xvt_treeview_remove_child_node(WINDOW win, XVT_TREEVIEW_NODE node)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    if (id == tv.GetRootItem())
      tv.DeleteAllItems();
    else
    {
      tv.Suspend();
      tv.Delete(id);
      tv.Resume();
    }
  }
  return ok;
}

BOOLEAN xvt_treeview_remove_node_children(WINDOW win, XVT_TREEVIEW_NODE node)
{
  BOOLEAN ok = FALSE;
  if (win != NULL_WIN)
  {  
    CAST_TREEVIEW(win, tv);
    tv.Suspend();
    wxTreeItemId id(node);
    if (!id.IsOk()) 
      id = tv.GetRootItem();
    tv.DeleteChildren(id);
    tv.Resume();
    ok = TRUE;
  }
  return ok;
}

void xvt_treeview_resume(WINDOW win)
{
  CAST_TREEVIEW(win, tv);
  tv.Resume();
}

void xvt_treeview_select_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN sel)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    if (sel)
    {
      tv.Suspend();
      tv.SelectItem(id, true);
      tv.EnsureVisible(id);
      tv.Resume();
    }
    else
      tv.SelectItem(id, false);
  }
}

void xvt_treeview_set_node_images(WINDOW win, XVT_TREEVIEW_NODE node, XVT_IMAGE item_image, 
                                  XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    tv.SetNodeImages(id, item_image, collapsed_image, expanded_image);
  }
}

void  xvt_treeview_set_node_bold(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN bold)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    tv.SetItemBold(id, bold != FALSE);
  }
}

void xvt_treeview_set_node_string(WINDOW win, XVT_TREEVIEW_NODE node, const char* text)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
    tv.SetItemText(id, text);
  }
}

void xvt_treeview_suspend(WINDOW win)
{
  CAST_TREEVIEW(win, tv);
  tv.Suspend();
}

static XVT_TREEVIEW_NODE FindTreeNodeString(wxTreeCtrl& tv, const wxTreeItemId& parent, 
                                            const char*text)
{
  if (parent.IsOk())
  {
    TwxTreeItemData* pData = (TwxTreeItemData*)tv.GetItemData(parent);
    if (pData != NULL && pData->m_strData == text)
      return parent.m_pItem;
    
    wxTreeItemIdValue cookie;
    for (wxTreeItemId id = tv.GetFirstChild(parent, cookie); id.IsOk(); 
         id = tv.GetNextChild(parent, cookie))
    {
      XVT_TREEVIEW_NODE node = FindTreeNodeString(tv, id, text);
      if (node != NULL)
        return node;
    }
  }
  return NULL;
}

XVT_TREEVIEW_NODE xvt_treeview_find_node_string(WINDOW win, const char* text)
{
  XVT_TREEVIEW_NODE node = NULL;
  if (win != NULL_WIN && text && *text)
  {
    CAST_TREEVIEW(win, tv);
    node = FindTreeNodeString(tv, tv.GetSelection(), text);
    if (node == NULL)
      node = FindTreeNodeString(tv, tv.GetRootItem(), text);
  }
  return node;
}

///////////////////////////////////////////////////////////
// TwxOutlookBar
///////////////////////////////////////////////////////////

BEGIN_EVENT_TABLE(TwxOutlookBar, wxVListBox)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_LISTBOX_SELECTED,      TwxOutlookBar::OnSelected)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, TwxOutlookBar::OnSelected)
  EVT_MOTION(TwxOutlookBar::OnMouseMove)
  EVT_LEAVE_WINDOW(TwxOutlookBar::OnMouseLeave)
END_EVENT_TABLE()

static const wxColour ModulateColour(const wxColour& col, int percent)
{
  int k = 0;
  if (percent > 0)
    k = 255;
  else
    percent = -percent;
  const int inverse = 100-percent;
  int r = ((k * percent) + (col.Red()   * inverse)) / 100;
  int g = ((k * percent) + (col.Green() * inverse)) / 100;
  int b = ((k * percent) + (col.Blue()  * inverse)) / 100;
  return wxColour(r, g, b);
}

/*
static const wxColour MeanColour(const wxColour& c1, const wxColour& c2, int percent = 50)
{
  const double p = percent / 100.0;
  const double i = 1.0-p;
  return wxColour(c1.Red()*i+c2.Red()*p, c1.Green()*i+c2.Green()*p, c1.Blue()*i+c2.Blue()*p);
}
*/

void TwxOutlookBar::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t u) const
{
  const int n = u; // Anti warning
  wxColour color1, color2;
  if (n == m_nHovering)
  {
    if (n == GetSelection())
    {
      color1 = wxColour(232,127,8);
      color2 = wxColour(247,218,124);
    }
    else
    {
      color1 = wxColour(255,255,220);
      color2 = wxColour(247,192,91);
    }
  }
  else
  {
    if (n == GetSelection())
    {
      color1 = wxColour(251,230,148); // Colori predefiniti di Outlook
      color2 = wxColour(238,149, 21);
    }
    else
    {
      if (InheritsBackgroundColour())
      {
        color1 = ModulateColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTION), +20);
        color2 = ModulateColour(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION), -20);
      }
      else
      {
        const wxColour bkg = GetBackgroundColour();
        color1 = ModulateColour(bkg, +20);
        color2 = ModulateColour(bkg, -20);
      }
    }
  }

  if (xvt_sys_get_oem_int("OEM", -1) == 0)
  {
    const wxColour color0 = ModulateColour(color2, +20);
    const int delta = 2*rect.height/5;
    wxRect r1 = rect, r2 = rect;   
    r1.height = delta;
    r2.y += delta; r2.height -= delta;
    dc.GradientFillLinear(r1, color0, color0, wxDOWN);
    dc.GradientFillLinear(r2, color2, color1, wxDOWN);

    wxPen pen1(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
    dc.SetPen(pen1);
    dc.DrawLine(rect.x, rect.y, rect.GetRight(), rect.y);

    wxPen pen2(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW));
    dc.SetPen(pen2);
    dc.DrawLine(rect.x, rect.GetBottom(), rect.GetRight(), rect.GetBottom());
  }
  else
    dc.GradientFillLinear(rect, color1, color2, wxDOWN);
}

void TwxOutlookBar::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
{
  const int nSide = rect.height;
  const TwxOutlookItem& oi = m_item[n];
  int nTextOffset = 4;
  if (oi.m_nIconId > 0)
  {
    const int sz = nSide > 16 ? (nSide < 48 ? (nSide/16*16) : 48) : 16;
    const wxIcon ico = xvtart_GetIconResource(oi.m_nIconId, wxART_TOOLBAR, sz);
    if (ico.IsOk())
    {
      const wxSize szIco(ico.GetWidth(), ico.GetHeight());
      dc.DrawIcon(ico, rect.x+nTextOffset, rect.y+(nSide-szIco.y)/2);
      nTextOffset += nTextOffset+szIco.x;
    }
    else
      nTextOffset += nTextOffset+sz;
  }

  dc.SetFont(GetFont()); // Imposta il font predefinito per questo controllo

  wxColour color = GetForegroundColour();
  if (InheritsBackgroundColour())
    color = wxSystemSettings::GetColour(wxSYS_COLOUR_CAPTIONTEXT);
  dc.SetTextForeground(color);

  const wxString& str = oi.m_strText;
  const int nMaxX = rect.width - nTextOffset;
  const wxSize szText = dc.GetTextExtent(str);
  if (szText.x > nMaxX && szText.y*2 < nSide)
  {
    const int nMid = str.Len() / 2;
    int nBest = nMid, nDist = nMid;
    for (int i = 0; i < 2*nMid; i++) if (str[i] <= wxChar(' '))
    {
      const int d = abs(nMid-i);
      if (d < nDist)
      {
        nBest = i;
        nDist = d;
      }
    }
    dc.DrawText(str.Left(nBest+1), rect.x+nTextOffset, rect.y+nSide/2-szText.y);
    dc.DrawText(str.Mid(nBest+1), rect.x+nTextOffset, rect.y+nSide/2);
  }
  else
    dc.DrawText(oi.m_strText, rect.x+nTextOffset, rect.y+(nSide-szText.y)/2);
}

wxCoord TwxOutlookBar::OnMeasureItem(size_t WXUNUSED(n)) const
{
  const int nItems = GetItemCount();
  wxCoord nHeight = 32 + 4; // Icon size + gap
  if (nItems > 1)
  {
    const wxSize sz = GetSize();
    nHeight = max(sz.y / nItems, nHeight);
  }
  return nHeight;
}

void TwxOutlookBar::OnMouseMove(wxMouseEvent& evt)
{
  const int nWasHovering = m_nHovering;
  m_nHovering = HitTest(evt.GetPosition());
  if (m_nHovering != nWasHovering)
  {
    if (nWasHovering != wxNOT_FOUND)
      RefreshLine(nWasHovering);
    if (m_nHovering != wxNOT_FOUND)
      RefreshLine(m_nHovering);
  }
}

void TwxOutlookBar::OnMouseLeave(wxMouseEvent& WXUNUSED(e))
{
  if (m_nHovering != wxNOT_FOUND)
  {
    const int nWasHovering = m_nHovering;
    m_nHovering = wxNOT_FOUND;
    RefreshLine(nWasHovering);
  }
}

void TwxOutlookBar::OnSelected(wxCommandEvent& evt)
{
  TwxWindow* win = wxDynamicCast(GetParent(), TwxWindow);
  if (win != NULL)
  {
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.type = WC_OUTLOOKBAR;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.lbox.dbl_click = evt.GetEventType() == wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
    win->DoXvtEvent(e);
  }
}

int TwxOutlookBar::Add(short nIconId, const wxString strText, int nFlags)
{
  int i = GetItemCount();
  const bool ok = i < MAX_ITEMS-1;
  if (ok)
  {
    m_item[i].m_nIconId = nIconId;
    m_item[i].m_strText = strText;
    m_item[i].m_nFlags  = nFlags;
    SetItemCount(i+1);
  }
  else
    i = -1;
  return i;
}

TwxOutlookBar::TwxOutlookBar(wxWindow *parent, wxWindowID id, 
                             const wxPoint& pos, const wxSize& size, long style)
             : wxVListBox(parent, id, pos, size, style), m_nHovering(wxNOT_FOUND)
{
  SetItemCount(0);
}

TwxOutlookBar::~TwxOutlookBar()
{ }

BOOLEAN xvt_list_add(WINDOW win, int index, const char* text)
{
  wxListBox* lb = wxDynamicCast((wxObject*)win, wxListBox);
  BOOLEAN ok = lb != NULL;
  if (ok)
  {
    const wxString str = text;
    if (index < 0 || index >= (int)lb->GetCount())
      lb->AppendString(str);
    else
      lb->Insert(str, index);
  }
  return ok;
}

int xvt_list_add_item(WINDOW win, short icon, const char* text, int flags)
{
  int n = -1;
  if (win != NULL_WIN)
  {
    TwxOutlookBar* olb = wxDynamicCast((wxObject*)win, TwxOutlookBar);
    if (olb != NULL)
      n = olb->Add(icon, text, flags);
  }
  return n;
}

BOOLEAN xvt_list_clear(WINDOW win)
{
  BOOLEAN ok = win != NULL_WIN;
  if (ok)
  {
    wxVListBox* olb = wxDynamicCast((wxObject*)win, wxVListBox);
    if (olb != NULL)
      olb->Clear();
    else
    {
      wxListBox* lb = wxDynamicCast((wxObject*)win, wxListBox);
      if (lb != NULL)
        lb->Clear();
    }
  }
  return ok;
}

int xvt_list_get_sel_index(WINDOW win)
{
  int sel = -1;
  if (win != NULL_WIN)
  {
    wxVListBox* olb = wxDynamicCast((wxObject*)win, wxVListBox);
    if (olb != NULL)
      sel = olb->GetSelection();
    else
    {
      wxListBox* lb = wxDynamicCast((wxObject*)win, wxListBox);
      if (lb != NULL)
        sel = lb->GetSelection();
    }
  }
  return sel;
}

BOOLEAN xvt_list_set_sel(WINDOW win, int index, BOOLEAN select)
{
  BOOLEAN ok = win != NULL_WIN;
  if (ok)
  {
    wxVListBox* olb = wxDynamicCast((wxObject*)win, wxVListBox);
    if (olb != NULL)
    {
      if (select)
        olb->SetSelection(index);
    }
    else
    {
      wxListBox* lb = wxDynamicCast((wxObject*)win, wxListBox);
      if (lb != NULL)
        lb->SetSelection(index, select != 0);
    }
  }
  return ok;
}

int xvt_list_count(WINDOW win)
{
  int n = 0;
  if (win != NULL_WIN)
  {
    wxVListBox* olb = wxDynamicCast((wxObject*)win, wxVListBox);
    if (olb != NULL)
      n = olb->GetItemCount();
    else
    {
      wxListBox* lb = wxDynamicCast((wxObject*)win, wxListBox);
      if (lb != NULL)
        n = lb->GetCount();
    }
  }
  return n;
}

///////////////////////////////////////////////////////////
// TwxPopUp
///////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS(TwxPopUp, wxVListBox)
  
BEGIN_EVENT_TABLE(TwxPopUp, wxVListBox)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_LISTBOX_SELECTED,      TwxPopUp::OnSelected)
  EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, TwxPopUp::OnSelected)
  EVT_MOTION(TwxPopUp::OnMouseMove)
  EVT_KILL_FOCUS(TwxPopUp::OnKillFocus)
  EVT_KEY_DOWN(TwxPopUp::OnKeyDown)
END_EVENT_TABLE()


void TwxPopUp::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t u) const
{
  const int n = u; // Anti warning
  if (n == m_nHovering || (m_nHovering == wxNOT_FOUND && IsCurrent(u)))
  {
    wxBrush brush(m_clrBack);
    dc.SetBrush(brush);
    dc.SetPen(*wxTRANSPARENT_PEN);
    dc.DrawRectangle(rect);
  }
}

void TwxPopUp::OnDrawItem(wxDC& dc, const wxRect& rect, size_t u) const
{
  const int n = u; // Anti warning
  wxColour color;
  if (n == m_nHovering || (m_nHovering == wxNOT_FOUND && IsCurrent(u)))
    color = m_clrFore;
  else
    color = GetForegroundColour();  
  dc.SetTextForeground(color);
  dc.SetFont(GetFont());
  dc.DrawText(m_menu[n], rect.x, rect.y);
}

wxCoord TwxPopUp::OnMeasureItem(size_t WXUNUSED(n)) const
{
  return m_nRowHeight;
}

void TwxPopUp::OnMouseMove(wxMouseEvent& evt)
{
  int nHover = wxNOT_FOUND;
    
  const wxRect rect = GetClientRect();
  if (rect.Contains(evt.GetPosition()))
    nHover = HitTest(evt.GetPosition());

  if (nHover != m_nHovering)
  {
    const int nWasHovering = m_nHovering;
    m_nHovering = nHover;
    if (nWasHovering != wxNOT_FOUND)
      RefreshLine(nWasHovering);
    if (m_nHovering != wxNOT_FOUND)
      RefreshLine(m_nHovering);
  }
}

void TwxPopUp::OnKillFocus(wxFocusEvent& WXUNUSED(e))
{
  Hide();
}

void TwxPopUp::NotifySelection()
{
  TwxWindow* win = wxDynamicCast(GetParent(), TwxWindow);
  if (win != NULL)
  {
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = GetId();
    e.v.ctl.ci.type = WC_LISTEDIT;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.listedit.active = GetSelection();
    win->DoXvtEvent(e);
  }
  Hide();
}

void TwxPopUp::OnSelected(wxCommandEvent& WXUNUSED(evt))
{
  if (m_nHovering >= 0)
    NotifySelection();
}

int TwxPopUp::Add(const wxString str)
{
  m_menu.Add(str);
  const int i = m_menu.GetCount();
  SetItemCount(i);
  return i;
}

void TwxPopUp::OnKeyDown(wxKeyEvent& evt)
{
  m_nHovering = wxNOT_FOUND; // Evita chiusura involontaria della lista 
  int key = evt.GetKeyCode();
  switch (key)
  {
  case WXK_RETURN: 
    NotifySelection();
    break;
  default:
    if (key > ' ' && key <= 'z')
    {
      key = toupper(key);
      const int curr = max(GetSelection(), 0);
      const int tot = m_menu.GetCount();
      int i = curr;
      for (i = (i+1)%tot; i != curr; i = (i+1)%tot)
      {
        if (toupper(m_menu[i][0]) == key)
          break;
      }
      SetSelection(i);
    }
    else
      evt.Skip();
    break;
  }
}

TwxPopUp::TwxPopUp(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size)
        : wxVListBox(parent, id, pos, size, wxBORDER|wxCLIP_SIBLINGS), 
          m_nHovering(wxNOT_FOUND)
{ 
  m_clrFore = wxSystemSettings::GetColour(wxSYS_COLOUR_HOTLIGHT);
  m_clrBack = GetSelectionBackground();

  const wxFont font = parent->GetFont();
  m_nRowHeight = abs(font.GetPixelSize().y) + 4;
}

static int RoundPopupHeight(int list_h, int row_h)
{
  const int rem = list_h % (row_h+1);
  if (rem > 0)
    list_h -= rem;
  return list_h;
}

MENU_TAG xvt_list_popup(WINDOW parent_win, const RCT* ownrct, const MENU_ITEM* menu, 
                        const XVT_COLOR_COMPONENT* colors, MENU_TAG first)
{
  int sel = -1;
  int items = 0;
  if (parent_win != NULL_WIN && ownrct != NULL && menu != NULL)
  {
    wxWindow* parent = wxStaticCast((wxObject*)parent_win, wxWindow);
    int width = ownrct->right - ownrct->left;
    for (items = 0; menu[items].tag != 0; items++) if (menu[items].tag > 0 && menu[items].text)
    {
      const wxString str = menu[items].text;
      int w = 0, h = 0; parent->GetTextExtent(str, &w, &h); 
      w += 24;
      if (w > width)
        width = w;
    }

    const wxFont font = parent->GetFont();
    const int nRowHeight = abs(font.GetPixelSize().y)+4;
    const wxRect rctClient = parent->GetClientRect();
    const int nBottom = rctClient.GetBottom();

    wxPoint pos(ownrct->right-width, ownrct->bottom);
    wxSize size(width, items*nRowHeight+2);

    if (pos.y + size.y > nBottom)               // La lista deborda di sotto?
    {
      if (ownrct->top > nBottom-ownrct->bottom) // Ho piu' spazio sopra che sotto?
      {
        pos.y = ownrct->top - size.y;           // Sposto la lista sopra al campo di testo
        if (pos.y < 0)
        {
          size.y = RoundPopupHeight(size.y + pos.y, nRowHeight);
          pos.y = ownrct->top - size.y;
        }
      }
      else
      {
        // Accorcio la lista in basso
        size.y = RoundPopupHeight(nBottom-pos.y, nRowHeight);                 
      }
    }
    if (pos.x < 0)
      pos.x = 0;

    WIN_DEF wd; memset(&wd, 0, sizeof(wd));
    wd.ctlcolors = (XVT_COLOR_COMPONENT*)colors;
    xvt_rect_set(&wd.rct, pos.x, pos.y, pos.x+size.x, pos.y+size.y);
    wd.v.ctl.ctrl_id = wxID_ANY;
    wd.v.ctl.flags = CTL_FLAG_INVISIBLE;
    wd.wtype = WC_POPUP;
    WINDOW win = xvt_ctl_create_def(&wd, parent_win, 0);
    
    TwxPopUp* lb = wxDynamicCast((wxObject*)win, TwxPopUp);
    if (lb != NULL)
    {
      for (int i = 0; menu[i].tag != 0; i++)
      {
        const MENU_ITEM& mi = menu[i];
        if (mi.tag > 0)
        {
          lb->Add(mi.text);
          if (mi.tag == first)
            sel = i;
        }
      }
      if (sel >= 0)
        lb->SetSelection(sel);
      lb->Show(); 
      lb->SetFocus();
      while (lb->IsShown())
      {
        wxApp* a = wxTheApp;  // Memorizzo il risultato di wxGetInstance
        while (a->Pending())
          a->Dispatch();
        lb->Raise();
        wxMilliSleep(50);
      }
      sel = lb->GetSelection();
      delete lb;
    }
  }
  return sel >= 0 && sel < items ? menu[sel].tag : 0;
}

///////////////////////////////////////////////////////////
// ToolBar
///////////////////////////////////////////////////////////

#if wxCHECK_VERSION(3,8,9)
#include <wx/aui/auibar.h>
#define TwxToolBarBase wxAuiToolBar
#else
#define TwxToolBarBase wxToolBar
#endif

class TwxToolBar : public TwxToolBarBase
{
  DECLARE_DYNAMIC_CLASS(TwxToolBar)
  wxBitmap m_texture;

protected:
  DECLARE_EVENT_TABLE()
  void OnTool(wxCommandEvent& evt);
  void OnEraseBackground(wxEraseEvent& evt);
  virtual bool SetBackgroundColour(const wxColour& colour);
  TwxToolBar() : TwxToolBarBase(NULL, wxID_ANY) { wxASSERT(false); }

public:
  void ShowTool(int id, bool on);
  void SetBackgroundTexture(XVT_IMAGE img);
  TwxToolBar(wxWindow* parent, wxWindowID id, const wxPoint& pos, 
             const wxSize& size, long style);
};

IMPLEMENT_DYNAMIC_CLASS(TwxToolBar, TwxToolBarBase)

BEGIN_EVENT_TABLE(TwxToolBar, TwxToolBarBase)
  EVT_TOOL(wxID_ANY, TwxToolBar::OnTool)
  EVT_ERASE_BACKGROUND(TwxToolBar::OnEraseBackground)
END_EVENT_TABLE();

void TwxToolBar::OnTool(wxCommandEvent& evt)
{
  XVT_EVENT e(E_CONTROL);
  e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_ICON; // WC_PUSHBUTTON entra in conflitto coi bottoni
  e.v.ctl.ci.win = WINDOW(this);
  TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
  win->DoXvtEvent(e);
}

void TwxToolBar::OnEraseBackground(wxEraseEvent& evt)
{
  wxDC& dc = *evt.GetDC();
  if (m_texture.IsOk())
  {
    const wxCoord tw = m_texture.GetWidth();
    const wxCoord th = m_texture.GetHeight();
    wxCoord cw, ch; dc.GetSize(&cw, &ch);
    for (wxCoord y = 0; y < ch; y += th)
      for (wxCoord x = 0; x < cw; x += tw)
        dc.DrawBitmap(m_texture, x, y);
  }
  else
  {
    if (xvt_sys_get_oem_int("OEM", -1) == 0)
    {
      wxCoord cw, ch; dc.GetSize(&cw, &ch);
      const wxColour b0 = GetBackgroundColour();
      const wxColour b1 = ModulateColour(b0, -10);
      const wxColour b2 = ModulateColour(b0, +70);
      dc.GradientFillLinear(wxRect(0,0,cw,ch),b1,b2,wxSOUTH);
    }
    else
    {
      wxBrush brush(GetBackgroundColour());
      dc.SetBackground(brush);
      dc.Clear();
    }
  }
}

bool TwxToolBar::SetBackgroundColour(const wxColour& colour)
{
  const bool ok = TwxToolBarBase::SetBackgroundColour(colour);
  if (ok) // Se cambio lo sfondo della toolbar devo aggiornare anche quello del gripper
  {
    wxAuiDockArt* pArtist = FindArtist(this);
    if (pArtist != NULL)
      pArtist->SetColor(wxAUI_DOCKART_GRIPPER_COLOUR, colour);
  }
  return ok;
}

void TwxToolBar::SetBackgroundTexture(XVT_IMAGE xvt_img)
{
  if (xvt_img != NULL)
  {
    const wxImage& img = *(wxImage*)xvt_img;
    wxColour mean, dark, light;
    Image2Colors(img, mean, dark, light);
    SetBackgroundColour(mean);

    m_texture = wxBitmap(img);
  }
  else
    m_texture = wxNullBitmap;
}

TwxToolBar::TwxToolBar(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
          : TwxToolBarBase(parent, id, pos, size, style)
{ }

static TwxToolBar* Win2Bar(WINDOW win)
{
  wxASSERT(win != NULL_WIN);
  return wxDynamicCast((wxObject*)win, TwxToolBar);
}

BOOLEAN xvt_toolbar_add_control(WINDOW win, int cid, TOOL_TYPE type, const char *title, 
                                int ico, int WXUNUSED(cust_width), int idx)
{ 
  BOOLEAN ok = FALSE;
  TwxToolBar* ptb = Win2Bar(win);
  if (ptb != NULL)
  {
    TwxToolBar& tb = *ptb;
    switch (type)
    {
    case TOOL_SEPARATOR:
#ifdef wxAuiToolBar
      tb.AddSeparator();
      ok = idx < 0;
#else
      if (idx < 0)
        ok = tb.AddSeparator() != NULL;
      else
        ok = tb.InsertSeparator(idx) != NULL;
#endif
      break;
    default:
      {
        const wxBitmap bmp = xvtart_GetToolResource(ico, tb.GetToolBitmapSize().y);
        wxString cap, tip;
        wxChar acc = 0;
        for (const char* t = title; *t; t++) 
        {
          if (*t == '~' || *t == '&') 
          {
            cap << '&';
            acc = toupper(*(t+1));
          }
          else
          {
            cap << *t;
            tip << *t;
          }
        }
        if (acc > '\0')
        {
          if (acc >= 'A' && acc <= 'Z')
            tip << "\n(Alt+" << acc << ")";
        }
        else
        {
          switch (ico) //  Gestione bottoni speciali
          {
          case 102: tip << "\n(Esc)"; break;
          case 114: tip << "\n(Alt+F4)"; break;
          case 162: tip << "\n(F2)"; break;
          case 163: tip << "\n(F1)"; break;
          default: break;
          }
        }

#ifdef wxAuiToolBar
        tb.AddTool(cid, cap, bmp, wxNullBitmap, wxItemKind(type), tip, tip, NULL);
        ok = idx < 0;
#else
        if (idx < 0)
          ok = tb.AddTool(cid, cap, bmp, wxNullBitmap, wxItemKind(type), tip) != NULL;
        else
          ok = tb.InsertTool(idx, cid, cap, bmp, wxNullBitmap, wxItemKind(type), tip) != NULL;
#endif
      }
      break;
    }
  }
  return ok;
}

WINDOW xvt_toolbar_create(int cid, int left, int top, int right, int bottom, long nFlags, WINDOW parent)
{
#ifdef wxAuiToolBar
  long nStyle = wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_GRIPPER;
  if (nFlags & CTL_FLAG_PASSWORD)
    nStyle |= wxAUI_TB_TEXT;

  const wxPoint ptPos(left, top);
  wxSize szSize(right-left, bottom-top);

  int nIcoSize = 24;
  if (bottom > 0) 
  {
    nIcoSize = RoundToIcon(szSize.y);
  }
  else
  {
    nStyle |= wxAUI_TB_VERTICAL;
    nIcoSize = RoundToIcon(szSize.x);
  }
#else
  long nStyle = wxNO_BORDER | wxTB_NODIVIDER;
  if (nFlags & CTL_FLAG_PASSWORD)
    nStyle |= wxTB_TEXT | wxTB_FLAT;

  const wxPoint ptPos(left, top);
  wxSize szSize(right-left, bottom-top);

  int nIcoSize = 24;
  if (bottom > 0) 
  {
    nStyle |= wxTB_HORIZONTAL;
    nIcoSize = RoundToIcon(szSize.y);
  }
  else
  {
    nStyle |= wxTB_VERTICAL;
    nIcoSize = RoundToIcon(szSize.x);
  }
#endif

  wxWindow* pParent = wxStaticCast((wxObject*)parent, wxWindow);
  TwxToolBar* tb = new TwxToolBar(pParent, cid, ptPos, wxDefaultSize, nStyle);
  tb->SetToolBitmapSize(wxSize(nIcoSize, nIcoSize));
  return (WINDOW)tb;
}

void xvt_toolbar_enable_control(WINDOW win, int cid, BOOLEAN on)
{
  TwxToolBar* ptb = Win2Bar(win);
  if (ptb != NULL && cid > 0)
    ptb->EnableTool(cid, on != 0);
}

BOOLEAN xvt_toolbar_set_last_tool(WINDOW win, int id)
{
  BOOLEAN bMoved = FALSE;
  TwxToolBar* ptb = Win2Bar(win);
  if (ptb != NULL)  // Is a valid toolbar?
  {
    const int pos = ptb->GetToolPos(id);
    if (pos >= 0)
    {
#ifdef wxAuiToolBar
      // TBI 
#else
      const int nCount = ptb->GetToolsCount();
      if (pos < nCount-1)
      {
        wxToolBarToolBase* tool = ptb->RemoveTool(id);
        ptb->InsertTool(nCount-1, tool);
      }
      bMoved = TRUE;
#endif
    }
  }
  return bMoved;
}

void xvt_toolbar_realize(WINDOW win)
{
  TwxToolBar* ptb = Win2Bar(win);
  if (ptb != NULL)  // Is a valid toolbar?
  {
    ptb->Realize(); // Update tools
    wxAuiPaneInfo* pi = LockPane(win);
    if (pi != NULL)
    {
      const wxSize szBar = ptb->GetSize();
      if (pi->min_size.x < szBar.x || pi->min_size.y < szBar.y)
      {
        pi->MinSize(szBar); 
        pi->BestSize(szBar);
        UnlockPane(win);
      }
    }
    
    // Iucunde repetita juvant: forzo il colore del gripper che viene spesso dimenticato
    wxAuiDockArt* pArtist = FindArtist(ptb);
    if (pArtist != NULL)
      pArtist->SetColor(wxAUI_DOCKART_GRIPPER_COLOUR, ptb->GetBackgroundColour());
  }
}

void xvt_toolbar_show_control(WINDOW win, int cid, BOOLEAN on)
{
  if (win != NULL_WIN && cid > 0) 
  {
    wxASSERT(on);// Per ora non so come si faccia
    /*
    TwxToolBar* ptb = Win2Bar(win);
    if (ptb != NULL && cid > 0)
    {
    // ????
    }
    */
  }
}

void xvt_dwin_draw_tool(WINDOW win, int x, int y, int rid, int size)
{
  const wxBitmap bmp = xvtart_GetToolResource(rid, size);
  if (bmp.IsOk())
  {
  	wxDC& dc = GetTDCMapper().GetDC(win);
    dc.DrawBitmap(bmp, x, y);
  }
}

void xvt_ctl_set_texture(WINDOW win, XVT_IMAGE xvt_img)
{
  TwxToolBar* w = wxDynamicCast((wxObject*)win, TwxToolBar);
  if (w != NULL)
    w->SetBackgroundTexture(xvt_img);
}

///////////////////////////////////////////////////////////
// wxPropertyGrid
///////////////////////////////////////////////////////////

#include <wx/propgrid/advprops.h>

BEGIN_EVENT_TABLE(TwxPropertyGrid, wxPropertyGrid)
  EVT_PG_CHANGED(wxID_ANY, TwxPropertyGrid::OnPropertyChanged)
END_EVENT_TABLE();

void TwxPropertyGrid::SetColors(const XVT_COLOR_COMPONENT* colors)
{
  for (int i = 0; colors[i].type; i++)
  {
    CAST_COLOR(colors[i].color, rgb);
    switch(colors[i].type)
    {
    case XVT_COLOR_BACKGROUND : SetCellBackgroundColour(rgb); break;
    case XVT_COLOR_FOREGROUND : SetCellTextColour(rgb); break;
    case XVT_COLOR_HIGHLIGHT  : SetSelectionBackground(rgb); break;
    case XVT_COLOR_SELECT     : SetSelectionForeground(rgb); break;
    case XVT_COLOR_BLEND      : SetCaptionBackgroundColour(rgb); SetMarginColour(rgb); break;
    case XVT_COLOR_TROUGH     : SetEmptySpaceColour(rgb); break; 
    case XVT_COLOR_CAPTIONTEXT: SetCaptionForegroundColour(rgb); break;
    default                   : break;
    }
  }
}

void TwxPropertyGrid::OnPropertyChanged(wxPropertyGridEvent& evt)
{
  TwxWindow* win = wxDynamicCast(GetParent(), TwxWindow);
  if (win != NULL && !IsFrozen())
  {
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.v.treeview.sgl_click = TRUE;
    e.v.ctl.ci.v.treeview.node = evt.GetProperty();
    e.v.ctl.ci.type = WC_PROPGRID;
    e.v.ctl.ci.win = WINDOW(this);
    win->DoXvtEvent(e);
  }
}

TwxPropertyGrid::TwxPropertyGrid(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
               : wxPropertyGrid(parent, id, pos, size, style)
{ }

static BOOLEAN xvt_prop_freeze(WINDOW win, BOOLEAN on)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  const BOOLEAN ok = pg != NULL && on != BOOLEAN(pg->IsFrozen());
  if (ok)
  {
    if (on)
      pg->Freeze();
    else
    {
      pg->Thaw();
      pg->Refresh();
    }
  }
  return ok;
}

BOOLEAN xvt_prop_restart(WINDOW win)
{ return xvt_prop_freeze(win, FALSE); }

BOOLEAN xvt_prop_suspend(WINDOW win)
{ return xvt_prop_freeze(win, TRUE); }

static wxColour STR2COLOUR(const char* value)
{
  wxColour col;

  if (value && *value)
  {
    if (*value == '(')
      value++;
    if (isdigit(*value))
    {
      int r, g, b;
      const int n = sscanf(value, "%d,%d,%d", &r, &g, &b);
      if (n == 3)
        col = wxColour(r, g, b);
      else
      {
        CAST_COLOR(r, w); // NON usare wxColour(r) in quanto si aspetta un numero in formato BGR
        col = w;
      }
    }
    else
      col = wxColour(value); // Black, White, Yellow, ...
  }
  return col;
}

BOOLEAN xvt_prop_set_data(WINDOW win, XVT_TREEVIEW_NODE node, const char* value)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp != NULL)
    {
      const wxString strType = pgp->GetType();
      if (strType == "wxColour")
      {
        wxColourPropertyValue val(STR2COLOUR(value));
        wxVariant& var = pgp->GetValueRef();
        var = ((wxColourProperty*)pgp)->DoTranslateVal(val);
      }
      else
      {
        if (strType == "long" || strType == "int")
          pgp->SetValue(atol(value)); else
        if (strType == "bool")
          pgp->SetValue(*value > '0' && strchr("1TXY", *value) != NULL); 
        else
          pgp->SetValue(value);
      }
      return TRUE;
    }
  }
  return FALSE;
}

XVT_TREEVIEW_NODE xvt_prop_add(WINDOW win, const char* type, const char* name, const char* value, const char* label)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    wxPGProperty* pgp = pg->GetPropertyByName(name);
    if (pgp == NULL)
    {
      const wxString strLabel = (label && *label) ? label : name;
      if (type && *type > ' ')
      {
        switch (toupper(*type))
        {
        case 'B': 
          pgp = new wxBoolProperty(strLabel, name, *value > '0' && strchr("1TXY", *value) != NULL); 
          pgp->SetAttribute(wxString("UseCheckbox"), true);
          break;
        case 'C': 
          pgp = new wxColourProperty(strLabel, name, STR2COLOUR(value)); 
          break;
        case 'I':
        case 'L': 
          pgp = new wxIntProperty(strLabel, name, atol(value)); 
          break;
        default : 
          pgp = new wxStringProperty(strLabel, name, value); 
          break;
        }
      }
      else
        pgp = new wxPropertyCategory(strLabel, name);
      pg->Append(pgp);
    }
    else
      xvt_prop_set_data(win, pgp, value);
    return pgp;
  }
  return NULL;
}

XVT_TREEVIEW_NODE xvt_prop_current(WINDOW win)
{
  XVT_TREEVIEW_NODE node = NULL;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
    node = pg->GetSelection();
  return node;
}

XVT_TREEVIEW_NODE xvt_prop_find(WINDOW win, const char* name)
{
  XVT_TREEVIEW_NODE node = NULL;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
    node = pg->GetPropertyByName(name);
  return node;
}

void xvt_prop_fit_columns(WINDOW win)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
    pg->FitColumns();
}

BOOLEAN xvt_prop_remove(WINDOW win, XVT_TREEVIEW_NODE node)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp != NULL)
    {
      pg->DeleteProperty(pgp->GetId());
      return TRUE;
    }
  }
  return FALSE;
}

int xvt_prop_get_string(WINDOW win, XVT_TREEVIEW_NODE node, char* label, int maxlen)
{
  int len = -1;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    const wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp != NULL)
    {
      const wxString& str = pgp->GetLabel();
      if (label && maxlen > 0)
      {
        wxStrncpy(label, str, maxlen);
        label[maxlen-1] = '\0';
      }
      len = str.Len();
    }
  }
  return len;
}

int xvt_prop_get_type(WINDOW win, XVT_TREEVIEW_NODE node, char* type, int maxlen)
{
  int len = 0;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    const wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp != NULL)
    {
      wxString strType = pgp->GetType();
      if (strType == "wxColour")
        strType = "color";
      wxStrncpy(type, strType, maxlen);
      len = strType.Len();
    }
  }
  return len;
}

int xvt_prop_get_data(WINDOW win, XVT_TREEVIEW_NODE node, char* value, int maxlen)
{
  int len = -1;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    const wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp != NULL)
    {
      wxString str = pgp->GetValueAsString();
      wxString strType = pgp->GetType();

      if (strType == "wxColour")
      {
        strType = "color";
        str.RemoveLast(1); // Toglie la )
        str.Remove(0, 1);  // Toglie la (
      }
      len = str.Len();
      if (value != NULL && maxlen > 0)
      {
        wxStrncpy(value, str, maxlen);
        value[maxlen-1] = '\0';
      }
    }
  }
  return len;
}

static BOOLEAN xvt_for_each_property(WINDOW pg, const wxPGProperty* prop, PROP_CALLBACK pcb, void* jolly)
{
  BOOLEAN ok = prop != NULL && pcb != NULL;
  if (ok && !prop->IsRoot())
    ok = pcb(pg, (XVT_TREEVIEW_NODE)prop, jolly);
  if (ok)
  {
    const int nc = prop->GetChildCount();
    for (int c = 0; c < nc && ok; c++)
      ok = xvt_for_each_property(pg, prop->Item(c), pcb, jolly);
  }
  return ok;
}

BOOLEAN xvt_prop_for_each(WINDOW win, PROP_CALLBACK pcb, void* jolly)
{
  BOOLEAN ok = FALSE;
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
    ok = xvt_for_each_property(win, pg->GetRoot(), pcb, jolly);
  return ok;
}

BOOLEAN xvt_prop_set_read_only(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN ro)
{
  wxPropertyGrid* pg = wxDynamicCast((wxObject*)win, wxPropertyGrid);
  if (pg != NULL)
  {
    wxPGProperty* pgp = wxDynamicCast((wxObject*)node, wxPGProperty);
    if (pgp == NULL)
      pgp = pg->GetRoot();
    pgp->SetFlagRecursively(wxPG_PROP_DISABLED, ro != 0);
  }
  return pg != NULL;
}

///////////////////////////////////////////////////////////
// TwxTreeListCtrl
///////////////////////////////////////////////////////////

BEGIN_EVENT_TABLE(TwxTreeListCtrl, wxTreeListCtrl)
  EVT_TREE_ITEM_EXPANDING(wxID_ANY, TwxTreeListCtrl::OnExpanding)
  EVT_TREE_ITEM_EXPANDED (wxID_ANY, TwxTreeListCtrl::OnExpanded)
  EVT_TREE_ITEM_COLLAPSED(wxID_ANY, TwxTreeListCtrl::OnCollapsed)
  EVT_TREE_SEL_CHANGED   (wxID_ANY, TwxTreeListCtrl::OnSelChanged)
END_EVENT_TABLE();

int TwxTreeListCtrl::img2int(XVT_IMAGE xvt_img)
{
  int i = -1;
  if (xvt_img != NULL)
  {
    i = m_img[xvt_img] - 1;       // Ho memorizzato indice+1
    if (i < 0)                    // Immagine sconosciuta
    {
      const wxImage& img = *(wxImage*)xvt_img;
      wxImageList* il = GetImageList();
      if (il == NULL)             // Lista non ancora creata
      { 
        il = new wxImageList;
        il->Create(img.GetWidth(), img.GetHeight(), true, 3);
        AssignImageList(il);      // DON'T CALL SetImageList!
      }
      else
      {
        int old_w, old_h; il->GetSize(0, old_w, old_h); 
        const int new_w = img.GetWidth(), new_h = img.GetHeight();
        if (new_w > old_w)  // L'immagine nuova e' troppo grande?
        {
          const int old_ratio = old_w * 100 / old_h;
          const int new_ratio = new_w * 100 / new_h;
          const int old_count = il->GetImageCount();
          wxImageList* nil = new wxImageList;
          nil->Create(new_w, new_h, true, 3*old_count/2);
          for (int k = 0; k < old_count; k++)
          {
            wxImage old = il->GetBitmap(k).ConvertToImage();
            if (old_ratio == new_ratio)
              old.Rescale(new_w, new_h, wxIMAGE_QUALITY_HIGH);
            else
              old.Resize(wxSize(new_w, new_h), wxPoint((new_w-old_w)/2, (new_h-old_h)/2));
            nil->Add(old);
          }
          AssignImageList(il = nil);
        }
      }

      if (!img.HasMask())
      {
        wxImage& trans = (wxImage&)img;
        const int r = img.GetRed(0,0);
        const int g = img.GetGreen(0,0);
        const int b = img.GetBlue(0,0);
        trans.SetMask();
        trans.SetMaskColour(r, g, b);
      }
      const wxBitmap bmp(img);
      i = il->Add(bmp);
      m_img[xvt_img] = i+1;       // Memorizzo indice+1
    }
    if (i < 0)
      SORRY_BOX();   
  }
  return i;
}

void TwxTreeListCtrl::OnExpanding(wxTreeEvent& evt)
{
  const wxTreeItemId id = evt.GetItem();
  XVT_EVENT e(E_CONTROL);
  e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_TREELIST;
  e.v.ctl.ci.win = WINDOW(this);
  e.v.ctl.ci.v.treeview.node = id.m_pItem;
  e.v.ctl.ci.v.treeview.expanded  = TRUE;   
  if (GetChildrenCount(id) == 0) // Trucco perfido ...
    e.v.ctl.ci.v.treeview.collapsed = TRUE; // ... stato indeterminato = EXPANDING
  TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
  win->DoXvtEvent(e);
  if (GetChildrenCount(id) == 0) // Allora e' proprio vero ...
    SetItemHasChildren(id, false);
}

void TwxTreeListCtrl::OnExpanded(wxTreeEvent& WXUNUSED(evt))
{ Refresh(false); } // Non dovrebbe servire ma ...

void TwxTreeListCtrl::OnCollapsed(wxTreeEvent& WXUNUSED(evt))
{ Refresh(false); } // Non dovrebbe servire ma ...

void TwxTreeListCtrl::OnSelChanged(wxTreeEvent& evt)
{ 
  if (!m_nFrozen)
  {
    Suspend();
    XVT_EVENT e(E_CONTROL);
    e.v.ctl.id = evt.GetId();
    e.v.ctl.ci.type = WC_TREELIST;
    e.v.ctl.ci.win = WINDOW(this);
    e.v.ctl.ci.v.treeview.node = evt.GetItem().m_pItem;
    e.v.ctl.ci.v.treeview.sgl_click = TRUE;
    TwxWindow* win = wxStaticCast(GetParent(), TwxWindow);
    win->DoXvtEvent(e);
    Resume();
  }
  Refresh(false); // Non dovrebbe servire ma ...
} 

void TwxTreeListCtrl::Enable(const wxTreeItemId& id, bool on)
{ SetItemTextColour(id, on ? m_clrSelFore : m_clrDisFore); }

void TwxTreeListCtrl::Suspend()
{ m_nFrozen++; }

void TwxTreeListCtrl::Resume()
{
  wxASSERT(m_nFrozen > 0);
  if (m_nFrozen > 0)
    m_nFrozen--;
}

void TwxTreeListCtrl::SetColors(const XVT_COLOR_COMPONENT* colors)
{
  for (int i = 0; colors[i].type; i++)
  {
    CAST_COLOR(colors[i].color, rgb);
    switch(colors[i].type)
    {
    case XVT_COLOR_BACKGROUND: SetOwnBackgroundColour(rgb); break;
    case XVT_COLOR_FOREGROUND: SetOwnForegroundColour(rgb); break;
    case XVT_COLOR_HIGHLIGHT : m_clrSelBack = rgb; break;
    case XVT_COLOR_SELECT    : m_clrSelFore = rgb; break;
    case XVT_COLOR_TROUGH    : m_clrDisFore = rgb; break;
    default                  : break;
    }
  }
}

void TwxTreeListCtrl::SetNodeImages(const wxTreeItemId& id, XVT_IMAGE item_image, 
                                    XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image)
{
  const int ii = img2int(item_image);
  if (ii >= 0)
    SetItemImage(id, ii);
  else
  {
    const int ic = img2int(collapsed_image);
    if (ic >= 0)
    {
      SetItemImage(id, ic);
      const int ie = img2int(expanded_image);
      if (ie >= 0)
        SetItemImage(id, ie, wxTreeItemIcon_Selected);
    }
  }
}

TwxTreeListCtrl::TwxTreeListCtrl(wxWindow *parent, wxWindowID id, 
                         const wxPoint& pos, const wxSize& size)
               : wxTreeListCtrl(parent, id, pos, size, 
                 wxTR_HAS_BUTTONS|wxTR_HIDE_ROOT|wxTR_ROW_LINES|wxTR_COLUMN_LINES|wxTR_SINGLE), 
                 m_nFrozen(0)
{ 
  AddColumn(wxEmptyString, size.x);
  AddRoot("Root");
  SetIndent(GetIndent()/2);
}

#define CAST_TREELIST(win, tv) TwxTreeListCtrl& tv = *wxStaticCast((wxObject*)win, TwxTreeListCtrl);

WINDOW xvt_treelist_create(WINDOW parent_win,
  RCT * rct_p, char * title, long ctl_flags,
  long app_data, int ctl_id, XVT_IMAGE WXUNUSED(item_image),
  XVT_IMAGE WXUNUSED(collapsed_image), XVT_IMAGE WXUNUSED(expanded_image),
  long WXUNUSED(attrs), int line_height)
{
  wxASSERT(parent_win != NULL_WIN);
  // La xvt_ctl_create_def accetta un array di controlli da inizializzare, 
  // per cui le passero' un elemento valido ed uno nullo che funga da terminatore
  WIN_DEF win_def[2]; memset(win_def, 0, sizeof(WIN_DEF));
  win_def->wtype = WC_TREELIST;
  if (xvt_rect_is_empty(rct_p))
    xvt_vobj_get_client_rect(parent_win, &win_def->rct);
  else
    win_def->rct = *rct_p;
  win_def->text = title;
  win_def->v.ctl.ctrl_id = ctl_id;
  win_def->v.ctl.flags = ctl_flags;
  WINDOW win = xvt_ctl_create_def(win_def, parent_win, app_data);
  
  if (win != NULL_WIN && line_height > 12)
  {
    CAST_TREELIST(win, tv);
    tv.SetLineSpacing(line_height);
  }
  return win;
}

XVT_TREEVIEW_NODE xvt_treelist_add_child_node(WINDOW win,
  XVT_TREEVIEW_NODE parent, XVT_TREEVIEW_NODE_TYPE type,
  XVT_IMAGE item_image, XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image,
  const char* string, XVT_TREEVIEW_CALLBACK WXUNUSED(callback), const char* data)
{
  XVT_TREEVIEW_NODE node = NULL;
  if (win != NULL_WIN)
  {
    CAST_TREELIST(win, tv);
    TwxTreeItemData* pData = new TwxTreeItemData;
    pData->m_strData = data;
    wxTreeItemId pa(parent);
    if (!pa.IsOk())
      pa = tv.GetRootItem();

    wxStringTokenizer tok(string, "\t", wxTOKEN_RET_EMPTY);

    wxTreeItemId id = tv.AppendItem(pa, tok.GetNextToken(), -1, -1, pData);
    if (id.IsOk())
    {
      const int nColumns = tv.GetColumnCount();
      for (int c = 1; c < nColumns && tok.HasMoreTokens(); c++)
        tv.SetItemText(id, c, tok.GetNextToken());
      tv.SetItemHasChildren(pa, true);
      tv.SetItemHasChildren(id, type == XVT_TREEVIEW_NODE_NONTERMINAL);
      tv.SetNodeImages(id, item_image, collapsed_image, expanded_image);
      tv.SetItemFont(id, tv.GetFont());
      node = id.m_pItem;
    }
  }
  return node;
}

XVT_TREEVIEW_NODE xvt_treelist_get_child_node(WINDOW win, XVT_TREEVIEW_NODE parent_node, 
                                              int position)
{
  XVT_TREEVIEW_NODE child_node = NULL;
  if (win != NULL_WIN && position >= 0)
  {
    CAST_TREELIST(win, tv);
    wxTreeItemId parent(parent_node);
    if (!parent.IsOk())
      parent = tv.GetRootItem();

    if (parent.IsOk() && position < (int)tv.GetChildrenCount(parent))
    {
      wxTreeItemIdValue cookie;
      wxTreeItemId id;
      int i = -1;
      for (id = tv.GetFirstChild(parent, cookie), i = -1; 
           i < position && id.IsOk(); id = tv.GetNextChild(parent, cookie), i++);
      child_node = id.m_pItem;
    }
  }
  return child_node;
}

const char* xvt_treelist_get_node_data(WINDOW win, XVT_TREEVIEW_NODE node)
{
  const char* data = NULL;
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREELIST(win, tv);
    const wxTreeItemId id(node);
    TwxTreeItemData* pData = (TwxTreeItemData*)tv.GetItemData(id);
    if (pData != NULL)
      data = (const char*)pData->m_strData;
  }
  return data;
}

void xvt_treelist_destroy_node(WINDOW win, XVT_TREEVIEW_NODE node)
{ 
  if (win != NULL_WIN && node != NULL)
  {  
    CAST_TREELIST(win, tv);
    wxTreeItemId id(node);
    tv.Delete(id);
  }
}

BOOLEAN xvt_treelist_enable_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN on)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREELIST(win, tv);
    wxTreeItemId id(node);
    tv.Enable(id, on != FALSE);
  }
  return ok;
}

BOOLEAN xvt_treelist_expand_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN recurse)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREELIST(win, tv);
    tv.Suspend();
    const wxTreeItemId id(node);
    if (recurse)
      tv.ExpandAll(id);
    else
      tv.Expand(id);
    tv.Resume();
  }
  return ok;
}

XVT_TREEVIEW_NODE xvt_treelist_get_root_node(WINDOW win)
{
  XVT_TREEVIEW_NODE pRoot = NULL;
  if (win != NULL_WIN)
  {  
    CAST_TREELIST(win, tv);
    const wxTreeItemId id = tv.GetRootItem();
    pRoot = id.m_pItem;
  }
  return pRoot;
}

XVT_TREEVIEW_NODE xvt_treelist_get_selected_node(WINDOW win)
{
  CAST_TREELIST(win, tv);
  const wxTreeItemId id = tv.GetSelection();
  return id.m_pItem;
}

BOOLEAN xvt_treelist_remove_child_node(WINDOW win, XVT_TREEVIEW_NODE node)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREELIST(win, tv);
    const wxTreeItemId id(node);
    if (id == tv.GetRootItem())
      tv.DeleteChildren(id);
    else
    {
      tv.Suspend();
      tv.Delete(id);
      tv.Resume();
    }
  }
  return ok;
}

BOOLEAN xvt_treelist_remove_node_children(WINDOW win, XVT_TREEVIEW_NODE node)
{
  BOOLEAN ok = FALSE;
  if (win != NULL_WIN)
  {  
    CAST_TREELIST(win, tv);
    tv.Suspend();
    wxTreeItemId id(node);
    if (!id.IsOk()) 
      id = tv.GetRootItem();
    tv.DeleteChildren(id);
    tv.Resume();
    ok = TRUE;
  }
  return ok;
}

void xvt_treelist_resume(WINDOW win)
{
  CAST_TREELIST(win, tv);
  tv.Resume();
}

void xvt_treelist_select_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN sel)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREELIST(win, tv);
    const wxTreeItemId id(node);
    if (sel)
    {
      tv.Suspend();
      tv.SelectItem(id, id, true);
      tv.EnsureVisible(id);
      tv.Resume();
    }
    else
      tv.UnselectAll();
  }
}

void xvt_treelist_set_node_images(WINDOW win, XVT_TREEVIEW_NODE node, XVT_IMAGE item_image, 
                                  XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREELIST(win, tv);
    const wxTreeItemId id(node);
    tv.SetNodeImages(id, item_image, collapsed_image, expanded_image);
  }
}

void xvt_treelist_set_node_string(WINDOW win, XVT_TREEVIEW_NODE node, const char* text)
{
  if (win != NULL_WIN)
  {
    CAST_TREELIST(win, tv);
    const int cc = tv.GetColumnCount();
    wxStringTokenizer tok(text, "\t", wxTOKEN_RET_EMPTY);
    if (node != NULL)
    {
      const wxTreeItemId id(node);
      for (int c = 0; c < cc && tok.HasMoreTokens(); c++)
        tv.SetItemText(id, c, tok.GetNextToken());
    }
    else
    {
      for (int c = 0; tok.HasMoreTokens(); c++)
      {
        wxString str = tok.GetNextToken();
        int width = 0;
        bool bRightAlign = false;
        const int a = str.Find('@');
        if (a > 0)
        {
          width = 10*wxAtoi(str.Mid(a+1));
          bRightAlign = str.Find('R', true) > a;
          str.Truncate(a);
        }
        else
          width = 10*str.Len();
        if (c >= cc)
          tv.AddColumn(str, width, bRightAlign ? wxALIGN_RIGHT : wxALIGN_LEFT);
        else
        {
          tv.SetColumnText(c, str);
          const int oldw = tv.GetColumnWidth(c);
          if (width < 2*oldw/3 || width > 3*oldw/2)
            tv.SetColumnWidth(c, width);
        }
      }
    }
  }
}

void xvt_treelist_suspend(WINDOW win)
{
  CAST_TREELIST(win, tv);
  tv.Suspend();
}

void  xvt_treelist_set_node_bold(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN bold)
{
  if (win != NULL_WIN && node != NULL)
  {
    CAST_TREELIST(win, tv);
    const wxTreeItemId id(node);
    tv.SetItemBold(id, bold != FALSE);
  }
}

static XVT_TREEVIEW_NODE FindTreeListNodeString(wxTreeListCtrl& tv, const wxTreeItemId& parent, const char* text)
{
  if (parent.IsOk())
  {
    TwxTreeItemData* pData = (TwxTreeItemData*)tv.GetItemData(parent);
    if (pData != NULL && pData->m_strData == text)
      return parent.m_pItem;
    
    wxTreeItemIdValue cookie;
    for (wxTreeItemId id = tv.GetFirstChild(parent, cookie); id.IsOk(); 
         id = tv.GetNextChild(parent, cookie))
    {
      XVT_TREEVIEW_NODE node = FindTreeListNodeString(tv, id, text);
      if (node != NULL)
        return node;
    }
  }
  return NULL;
}

XVT_TREEVIEW_NODE xvt_treelist_find_node_string(WINDOW win, const char* text)
{
  XVT_TREEVIEW_NODE node = NULL;
  if (win != NULL_WIN && text && *text)
  {
    CAST_TREELIST(win, tv);
    node = FindTreeListNodeString(tv, tv.GetSelection(), text);
    if (node == NULL)
      node = FindTreeListNodeString(tv, tv.GetRootItem(), text);
  }
  return node;
}

///////////////////////////////////////////////////////////
// Sad but needed migration from xvaga.cpp
///////////////////////////////////////////////////////////

WIN_TYPE xvt_vobj_get_type(WINDOW win)
{
  if (win == NULL_WIN)
    return W_NONE;
  if (win == TASK_WIN)
    return W_TASK;
  if (win == SCREEN_WIN)
    return W_SCREEN;
  if (win == PRINTER_WIN)
    return W_PRINT;

  const TwxWindow* w = wxDynamicCast((wxObject*)win, TwxWindow);
  if (w != NULL)
    return w->_type;

  const wxControl* ctl = wxDynamicCast((wxObject*)win, wxControl);
  if (ctl != NULL)
  {
    if (ctl->IsKindOf(CLASSINFO(wxTreeCtrl)))     return WC_TREE;
    if (ctl->IsKindOf(CLASSINFO(wxPropertyGrid))) return WC_PROPGRID;
    if (ctl->IsKindOf(CLASSINFO(wxTreeListCtrl))) return WC_TREELIST;
  }
  return WO_TE; // Unknown custom control
}