#include "wxinc.h"

#define XTWIN_CPP 1
#include "xvt.h"
#include "xvtwin.h"

#include "wx/image.h"
#include "wx/notebook.h"
#include "wx/treectrl.h"
#if wxCHECK_VERSION(2,8,7)
#include "wx/aui/aui.h"
#endif
///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////

wxHashTable _nice_windows;
wxWindow*   _task_win = NULL;
EVENT_HANDLER _task_win_handler = NULL;

wxRect NormalizeRCT(const RCT* prct)
{
	wxRect rct;
	if (prct != NULL)
  {
	  rct.x = min(prct->left, prct->right);
	  rct.y = min(prct->top, prct->bottom);
	  rct.width = abs(prct->right - prct->left);
	  rct.height = abs(prct->bottom - prct->top);
  }
	return rct;
}

///////////////////////////////////////////////////////////
// Caret emulation
///////////////////////////////////////////////////////////

class TwxCaret : private wxTimer
{
	WINDOW _owner;
  PNT _pos;
	wxSize _size;
  bool _visible;
	bool _drawn;

protected:
	void Toggle();
  virtual void Notify() { Toggle(); }

public:
	void SetPos(int x, int y);
	void SetSize(int x, int y) { _size.x = x; _size.y = y; }
	void Show(WINDOW w, bool on = true);
	void Hide() { Show(NULL_WIN, false); }
	bool IsVisible() const { return _visible; }
	WINDOW Owner() const { return _owner; }
	void Kill();

	TwxCaret() :  _owner(NULL_WIN), _visible(false) { }
	virtual ~TwxCaret() { Kill(); }
} _TheCaret;

void TwxCaret::Kill()
{ _owner = NULL_WIN; }

void TwxCaret::SetPos(int x, int y) 
{ 
  if (_visible && _drawn) // Lo cancella se necessario
	  Toggle();  
  _pos.h = x; _pos.v = y; 
}

void TwxCaret::Show(WINDOW w, bool on)
{
  if (_visible && _drawn)
		Toggle();  // Lo cancella

	_visible = on;
	if (on)
	{
		_owner = w;
		Toggle();
		wxTimer::Start(500); // Lampeggia ogni mezzo secondo
	}
	else
	{
    if (w == _owner || w == NULL_WIN)
			Kill();
	}
}

void TwxCaret::Toggle()
{
	if (!_visible || _owner == NULL_WIN)
		return;
		
	_drawn = !_drawn;

	DRAW_CTOOLS dct;
	xvt_dwin_get_draw_ctools(_owner, &dct);

	CPEN pen;
	pen.width = _size.x;
	pen.pat = PAT_SOLID;
	pen.style = P_SOLID;
  pen.color = dct.fore_color;
	xvt_dwin_set_draw_mode(_owner, M_NOT_XOR);
	
  xvt_dwin_set_cpen(_owner, &pen);
	xvt_dwin_draw_set_pos(_owner, _pos);
	PNT p = _pos;	p.v -= _size.y-1;

	xvt_dwin_set_clip(_owner, NULL);  // Non si sa mai!
	xvt_dwin_draw_line(_owner, p);
	xvt_dwin_set_draw_ctools(_owner, &dct);
}

void xvt_win_set_caret_size(WINDOW win, int width, int height)
{
	_TheCaret.SetSize(width, height);
}

void xvt_win_set_caret_pos(WINDOW win, PNT p)
{
	_TheCaret.SetPos(p.h, p.v-1);
}

void xvt_win_set_caret_visible(WINDOW win, BOOLEAN on)
{
	_TheCaret.Show(win, on != 0);
}

///////////////////////////////////////////////////////////
// Generic Display context
///////////////////////////////////////////////////////////

TDC::TDC(wxWindow* owner) : _dc(NULL)
{
  _owner = owner;

	memset(&_dct, 0, sizeof(_dct));
	_dct.pen.width = 0;
	_dct.pen.pat = PAT_SOLID;
  _dct.pen.style = P_SOLID;
  _dct.pen.color = COLOR_BLACK;
	_dct.brush.pat = PAT_HOLLOW;
  _dct.brush.color = COLOR_WHITE;
	_dct.mode = M_COPY;
	_dct.fore_color = COLOR_BLACK;
	_dct.back_color = COLOR_WHITE;
	_dct.opaque_text = FALSE;

  _font.SetPointSize(9);  // Default font
  _deltaf = 0;

  // Reset clip area
  SetClippingBox(NULL);
  _real_clip = _clip;

	_dirty = -1;  // Absolutely force setting
}

TDC::~TDC()
{
	KillDC();
}

void TDC::SetDirty(int d)
{
	if (_dirty >= 0)
		_dirty = d;
}

static int PatternToStyle(PAT_STYLE pat)
{
	int style = wxSOLID;
	switch (pat)
	{
  case PAT_NONE:
  case PAT_HOLLOW:    style = wxTRANSPARENT; break;
  case PAT_SOLID:	    style = wxSOLID; break;
  case PAT_HORZ:      style = wxHORIZONTAL_HATCH; break;
  case PAT_VERT:      style = wxVERTICAL_HATCH; break;
  case PAT_FDIAG:	    style = wxFDIAGONAL_HATCH; break;
  case PAT_BDIAG:     style = wxBDIAGONAL_HATCH; break;
  case PAT_CROSS:     style = wxCROSS_HATCH; break;
  case PAT_DIAGCROSS: style = wxCROSSDIAG_HATCH; break;
  case PAT_RUBBER:	
  case PAT_SPECIAL:		
  default: SORRY_BOX(); break;
	}
	return style;
}

static int PenStyleToStyle(PEN_STYLE s, PAT_STYLE p)
{
	int style = wxSOLID;
	if (p != PAT_HOLLOW)
	{
		switch (s)
		{
		case P_DOT : style = wxDOT;        break;
		case P_DASH: style = wxSHORT_DASH; break;
		default: break;
		}
	}
	else
		style = wxTRANSPARENT;

	return style;
}

bool TDC::PenChanged() const
{
	const int diff = memcmp(&_dct.pen, &_real_dct.pen, sizeof(_dct.pen));
  return diff != 0;
}

bool TDC::BrushChanged() const
{
	const int diff = memcmp(&_dct.brush, &_real_dct.brush, sizeof(_dct.brush));
  return diff != 0;
}

bool TDC::FontChanged() const
{
  return _font != _real_font;
}

bool TDC::ClipChanged() const
{
	const int diff = memcmp(&_clip, &_real_clip, sizeof(_clip));
  return diff != 0;
}

#ifdef LINUX
bool is_printer_dc(wxDC * dc)
{
	return dc->IsKindOf(CLASSINFO(wxPostScriptDC));
}
#endif

wxDC& TDC::GetDC(bool bPaint)
{
  if (bPaint)
	{
		KillDC();
	  _dc = new wxPaintDC(_owner);
  	_dirty = -1;
	}
	else
	{
		if (_dc == NULL)
		{
      if (_owner == NULL || (unsigned long)_owner == SCREEN_WIN)
        _dc = new wxScreenDC();
      else  
		    _dc = new wxClientDC(_owner);
    	_dirty = -1;
		}
	}

	if (_dirty)
	{
		if (_dirty < 0 || PenChanged())
		{
		  CAST_COLOR(_dct.pen.color, pen_color);
  		wxPen* pen = wxThePenList->FindOrCreatePen(pen_color, _dct.pen.width, PenStyleToStyle(_dct.pen.style, _dct.pen.pat));
	  	_dc->SetPen(*pen);
			_real_dct.pen = _dct.pen;
		}

		if (_dirty < 0 || BrushChanged())
		{
 		  CAST_COLOR(_dct.brush.color, brush_color);
		  wxBrush* brush = wxTheBrushList->FindOrCreateBrush(brush_color, PatternToStyle(_dct.brush.pat));
		  _dc->SetBrush(*brush);
			_real_dct.brush = _dct.brush;
		}

		if (_dirty < 0 || _dct.mode != _real_dct.mode)
		{
#ifdef LINUX
			if(!is_printer_dc(_dc))
			{ 
#endif
			switch(_dct.mode)
			{
			case M_COPY:     _dc->SetLogicalFunction(wxCOPY); break;
			case M_OR:       _dc->SetLogicalFunction(wxOR); break;
			case M_XOR:      _dc->SetLogicalFunction(wxXOR); break;
			case M_CLEAR:    _dc->SetLogicalFunction(wxCLEAR); break;
			case M_NOT_COPY: _dc->SetLogicalFunction(wxSRC_INVERT); break;
			case M_NOT_OR:   _dc->SetLogicalFunction(wxNOR); break;
			case M_NOT_XOR:  _dc->SetLogicalFunction(wxEQUIV); break;
			case M_NOT_CLEAR:_dc->SetLogicalFunction(wxSET); break;
			default: SORRY_BOX();
			}
#ifdef LINUX
			}
#endif
			_real_dct.mode = _dct.mode;
		}

		if (_dirty < 0 || _dct.fore_color != _real_dct.fore_color)
		{
 		  CAST_COLOR(_dct.fore_color, fore_color);
		  _dc->SetTextForeground(fore_color);
			_real_dct.fore_color = _dct.fore_color;
		}

		if (_dirty < 0 || _dct.back_color != _real_dct.back_color)
		{
 		  CAST_COLOR(_dct.back_color, back_color);
		  _dc->SetTextBackground(back_color);
			_real_dct.back_color = _dct.back_color;
		}

		if (_dirty < 0 || _dct.opaque_text != _real_dct.opaque_text)
		{
		  _dc->SetBackgroundMode(_dct.opaque_text ? wxSOLID : wxTRANSPARENT);
			_real_dct.opaque_text = _dct.opaque_text;
		}

		if (_dirty < 0 || FontChanged())
		{
      const wxFont& f = _font.Font(_dc, (WINDOW)_owner);
 		  _dc->SetFont(f);
			_real_font = _font;

		  int height, desc, lead; 	
		  _dc->GetTextExtent("Campo", NULL, &height, &desc, &lead);
		  _deltaf = height-desc;
		}

    if (_dirty < 0 || ClipChanged())
    {
      _dc->DestroyClippingRegion();
      if (_clip.bottom < 4096)
        _dc->SetClippingRegion(NormalizeRCT(&_clip));
      _real_clip = _clip;
    }
		
		_dirty = false;
	}
	return *_dc;
}

void TDC::KillDC()
{
	if (_dc != NULL)
	{
  	SetClippingBox(NULL);
    _real_clip = _clip;
		delete _dc;
  	_dc = NULL;
	}
}

void TDC::SetClippingBox(const RCT* pRct)
{
	if (pRct != NULL)
	{
		const wxRect rct = NormalizeRCT(pRct);
  	_clip.left = rct.x;	_clip.top = rct.y;
    _clip.right = rct.GetRight(); _clip.bottom = rct.GetBottom();
	}
	else
	{
		_clip.left = _clip.top = 0;
		_clip.right = _clip.bottom = 4096;
	}
}

bool TDC::GetClippingBox(RCT* pRct) const
{
	*pRct = _clip;
	return _clip.right > _clip.left;
}

TDCMapper& GetTDCMapper()
{
  static TDCMapper* _dc_map = NULL;
  if (_dc_map == NULL)
    _dc_map = new TDCMapper;
  return *_dc_map;
}

void TDCMapper::DestroyDC(WINDOW owner)
{
	if (owner)
	{
	  TDC* pTDC = (*this)[owner];
	  if (pTDC)
		  pTDC->KillDC();
	}
	else
	{
		for (TDCMapper::iterator it = begin(); it != end(); ++it)
		{
  	  TDC* pTDC = it->second;
	  	if (pTDC)
   		  pTDC->KillDC();
		}
  }
}

void TDCMapper::DestroyTDC(WINDOW owner)
{
	if (owner != NULL_WIN)
	{
	  TDC* pTDC = (*this)[owner];
	  if (pTDC)
		  delete pTDC;
    erase(owner);
	}
	else
	{
		TDCMapper::iterator it;
		for (it = begin(); it != end(); ++it)
		{
  	  TDC* pTDC = it->second;
	  	if (pTDC)
				delete pTDC;
		}
		clear();
	}
	_pLastOwner = NULL_WIN;
}

TDC& TDCMapper::GetTDC(WINDOW owner)
{
	if (owner != _pLastOwner)
  {
	  TDC* pTDC = (*this)[owner];
	  if (pTDC == NULL)
	  {
		  if (owner == PRINTER_WIN)
			  pTDC = new TPrintDC((wxWindow*)owner);
		  else
		    pTDC = new TDC((wxWindow*)owner);
		  (*this)[owner] = pTDC;
	  }
	  _pLastOwner = owner;
	  _pLastTDC = pTDC;
  }
	return *_pLastTDC;
}

bool TDCMapper::HasValidDC(WINDOW owner) const
{
  if (owner == NULL_WIN)
		return false;

	if (owner == (WINDOW)_pLastOwner)
		return true;
	
	TDC* pTDC = (*((TDCMapper *) this))[owner];
	return pTDC != NULL;
}

///////////////////////////////////////////////////////////
// Generic window class
///////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS(TwxWindowBase, wxWindow)

bool TwxWindowBase::CreateBase(wxWindow *parent, wxWindowID id, const wxString &title,
		    								       const wxPoint &pos, const wxSize &size, long style)
{
  // Evita inutili sfarfallamenti in quanto wxWidgets crea le finestre visibili per default
  wxWindowBase::Show(false);  
  bool ok = Create(parent, id, pos, size, style, title);
  if (ok)
  {
#if wxCHECK_VERSION(2,8,7)
#else
    SetTitle(title); // Triste necessita', la Create sembra ignorare il titolo
#endif
  }

  return ok;
}

TwxWindowBase::TwxWindowBase(wxWindow *parent, wxWindowID id, const wxString &title,
													   const wxPoint &pos, const wxSize &size, long style)
{
  CreateBase(parent, id, title, pos, size, style);
}

IMPLEMENT_DYNAMIC_CLASS(TwxWindow, TwxWindowBase)

BEGIN_EVENT_TABLE(TwxWindow, TwxWindowBase)
  EVT_CHAR(TwxWindow::OnChar)
  EVT_KEY_DOWN(TwxWindow::OnKeyDown)
	EVT_CLOSE(TwxWindow::OnClose)
	EVT_KILL_FOCUS(TwxWindow::OnKillFocus)
  EVT_LEFT_DCLICK(TwxWindow::OnMouseDouble)
  EVT_LEFT_DOWN(TwxWindow::OnMouseDown)
  EVT_LEFT_UP(TwxWindow::OnMouseUp)
  EVT_MENU_RANGE(1000, 32766, TwxWindow::OnMenu)
  EVT_MIDDLE_DOWN(TwxWindow::OnMouseDown)
  EVT_MIDDLE_UP(TwxWindow::OnMouseUp)
	EVT_MOTION(TwxWindow::OnMouseMove)
#if wxCHECK_VERSION(2,8,7)
  EVT_MOUSE_CAPTURE_LOST(TwxWindow::OnMouseCaptureLost)
#endif
  EVT_MOUSEWHEEL(TwxWindow::OnMouseWheel)
  EVT_PAINT(TwxWindow::OnPaint)
  EVT_RIGHT_DOWN(TwxWindow::OnMouseDown)
  EVT_RIGHT_UP(TwxWindow::OnMouseUp)
	EVT_SCROLL(TwxWindow::OnScroll)
	EVT_SCROLLWIN(TwxWindow::OnScrollWin)
	EVT_SET_FOCUS(TwxWindow::OnSetFocus)
	EVT_SIZE(TwxWindow::OnSize)
	EVT_TIMER(TIMER_ID, TwxWindow::OnTimer)
  EVT_COMMAND_RANGE(1, 9999, wxEVT_COMMAND_BUTTON_CLICKED, TwxWindow::OnButton)
  EVT_COMMAND_RANGE(1, 9999, wxEVT_COMMAND_CHECKBOX_CLICKED, TwxWindow::OnCheckBox)
  EVT_COMMAND_RANGE(1, 9999, wxEVT_COMMAND_RADIOBUTTON_SELECTED, TwxWindow::OnRadioButton)
END_EVENT_TABLE()

void TwxWindow::DoXvtEvent(EVENT& e)
{
	if (this != NULL && _eh != NULL)
  	_eh((WINDOW)this, &e);
}

void TwxWindow::OnChar(wxKeyEvent& evt)
{
  static int nSkipNextDotKey = -883; // Valore indefinito
  if (nSkipNextDotKey == -883)       // Devo stabilire se attivare la gestione o no
  {
    const char* campoini = xvt_fsys_get_campo_ini();
    char str[2];
    xvt_sys_get_profile_string(campoini, "Main", "Point2Comma", "1", str, sizeof(str));
    nSkipNextDotKey = strchr("1XY", *str) != NULL ? 0 : -1; // Dis/Abilita conversione punto in virgola
  }

  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_CHAR;
	int k = evt.GetKeyCode();

	if (nSkipNextDotKey == 1)
	{
    nSkipNextDotKey = 0;
		if (k == '.')
		{
		  evt.Skip();
		  return;
		}
	}

	switch (k)
	{
	case WXK_ALT:
	case WXK_MENU:
  case WXK_NUMPAD0:
  case WXK_NUMPAD1:
  case WXK_NUMPAD2:
  case WXK_NUMPAD3:
  case WXK_NUMPAD4:
  case WXK_NUMPAD5:
  case WXK_NUMPAD6:
  case WXK_NUMPAD7:
  case WXK_NUMPAD8:
  case WXK_NUMPAD9:
		evt.Skip();
		return;
  case WXK_NUMPAD_DECIMAL:   // Arriva solo dalla 3.6.3 in poi
	case WXK_DECIMAL:          // ??? Arriva sia '.' sia WXK_DECIMAL=340
    if (nSkipNextDotKey == 0)
    {
		  k = ','; // Trasformo il punto in virgola
		  nSkipNextDotKey = 1;
    }
		break;
  case WXK_NUMPAD_ADD: k = '+';break;
	case WXK_DOWN : k = K_DOWN;  break;
  case WXK_END  : k = K_LEND;  break;
  case WXK_HOME : k = K_LHOME; break;
	case WXK_LEFT : k = K_LEFT;  break;
	case WXK_NEXT : k = K_NEXT;  break;
	case WXK_PRIOR: k = K_PREV;  break;
	case WXK_RIGHT: k = K_RIGHT; break;
	case WXK_UP   : k = K_UP;	   break;
	case WXK_TAB:
    if (evt.ShiftDown())
			k = K_BTAB;
		break;
	default:
	  if (k >= WXK_F1 && k <= WXK_F24)
		  k = K_F1 + k - WXK_F1;
		break;
	}
	
	e.v.chr.shift = evt.ShiftDown();
	e.v.chr.control = evt.ControlDown();
	if (evt.AltDown())
	{
		e.v.chr.control = TRUE;
    if (xvt_chr_is_alnum(k))
			k = toupper(k);
		else
		{
      if (strchr("+-", k) == NULL) // Aggiungere qui vari testi eventuali
      {
			  evt.Skip();
			  return;
      }
		}
	}
	e.v.chr.ch = k;
	
	DoXvtEvent(e);
}

void TwxWindow::OnKeyDown(wxKeyEvent& e)
{
#ifdef WIN32
  // Triste necessita' per gestire corretamente Alt+'+' del tasterino
	const int k = e.GetKeyCode();
  if (k == WXK_NUMPAD_ADD)
  {
    if (e.AltDown())
    {
      OnChar(e);
      return;
    }
  }
#else
  if (e.AltDown() || e.ControlDown())
  {
    OnChar(event);
    return;
  }
#endif
  e.Skip();
}

void TwxWindow::OnClose(wxCloseEvent& WXUNUSED(e))
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_DESTROY;
	DoXvtEvent(e);
  Destroy();
}

void TwxWindow::OnKillFocus(wxFocusEvent& WXUNUSED(e))
{
  if (_TheCaret.Owner() == (WINDOW)this)
    _TheCaret.Hide();

  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_FOCUS;
	e.v.active = 0;
	DoXvtEvent(e);
}

void TwxWindow::OnMenu(wxCommandEvent& evt)
{
	EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_COMMAND;
	e.v.cmd.control = 0; e.v.cmd.shift = 0;
	e.v.cmd.tag = evt.GetId();
	DoXvtEvent(e);
}

#if wxCHECK_VERSION(2,8,7)
void TwxWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(e))
{
  xvt_win_release_pointer();
}
#endif

void TwxWindow::OnMouseDouble(wxMouseEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_MOUSE_DBL;
	e.v.mouse.button = (evt.RightDown() ? 1 : 0) + (evt.MiddleDown() ? 2 : 0);
	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();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseDown(wxMouseEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
  e.type = E_MOUSE_DOWN;
  e.v.mouse.button = (evt.RightDown() ? 1 : 0) + (evt.MiddleDown() ? 2 : 0);
  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();
  DoXvtEvent(e);
  SetFocus();   // Triste necessita'
}

void TwxWindow::OnMouseMove(wxMouseEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_MOUSE_MOVE;
	e.v.mouse.button = (evt.RightIsDown() ? 1 : 0) + (evt.MiddleIsDown() ? 2 : 0);
	e.v.mouse.control = evt.ControlDown();
	e.v.mouse.shift = evt.m_shiftDown;
	e.v.mouse.where.h = evt.GetX();
	e.v.mouse.where.v = evt.GetY();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseUp(wxMouseEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_MOUSE_UP;
	e.v.mouse.button = (evt.RightUp() ? 1 : 0) + (evt.MiddleUp() ? 2 : 0);
	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();
	DoXvtEvent(e);
}

void TwxWindow::OnMouseWheel(wxMouseEvent& evt)
{
  const int nRot = evt.GetWheelRotation();
  if (nRot != 0)
  {
    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = E_VSCROLL;
	  e.v.scroll.pos = evt.GetY();
    e.v.scroll.what = nRot > 0 ? SC_LINE_UP : SC_LINE_DOWN;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_UPDATE;

	RCT& rct = e.v.update.rct;

  wxRect rctDamaged = GetUpdateRegion().GetBox();
  rct.left = rctDamaged.x;
	rct.top = rctDamaged.y;
	rct.right = rctDamaged.GetRight()+1;
	rct.bottom = rctDamaged.GetBottom()+1;

	TDC& tdc = GetTDCMapper().GetTDC((WINDOW)this);
	tdc.GetDC(true); // Forza la creazione di un wxPaintDC
	DoXvtEvent(e);
	tdc.KillDC();    // Distrugge il wxPaintDC
	GetTDCMapper().DestroyDC(NULL_WIN); // Distrugge davvero tutti i wxClientDC residui (risolve molte "porcate" del video)
}

static SCROLL_CONTROL ConvertScrollToXVT(wxEventType et)
{
  if (et == wxEVT_SCROLL_TOP)
		return SC_THUMB; // Meglio di niente
  if (et == wxEVT_SCROLL_BOTTOM)
		return SC_THUMB; // Meglio di niente
  if (et == wxEVT_SCROLL_LINEUP)
		return SC_LINE_UP;
  if (et == wxEVT_SCROLL_LINEDOWN)
		return SC_LINE_DOWN;
  if (et == wxEVT_SCROLL_PAGEUP)
		return SC_PAGE_UP;
  if (et == wxEVT_SCROLL_PAGEDOWN)
		return SC_PAGE_DOWN;
  if (et == wxEVT_SCROLL_THUMBTRACK)
		return SC_THUMBTRACK;
  if (et == wxEVT_SCROLL_THUMBRELEASE)
		return SC_THUMB;
	return SC_NONE;
}

void TwxWindow::OnScroll(wxScrollEvent& evt)
{
  SCROLL_CONTROL sc = ConvertScrollToXVT(evt.GetEventType());
  if (sc != SC_NONE)
  {
    const wxScrollBar* sb = (wxScrollBar*)evt.GetEventObject();
	  const wxSize sz = sb->GetSize();

    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = E_CONTROL;
	  e.v.ctl.id = evt.GetId();
	  e.v.ctl.ci.type = sz.x > sz.y ? WC_HSCROLL : WC_VSCROLL;
	  e.v.ctl.ci.win = (WINDOW)sb;
	  e.v.ctl.ci.v.scroll.pos = evt.GetPosition();
    e.v.ctl.ci.v.scroll.what = sc;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnScrollWin(wxScrollWinEvent& evt)
{
	wxEventType et = evt.GetEventType();
  et -= (wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP);
  const SCROLL_CONTROL sc = ConvertScrollToXVT(et);
  if (sc != SC_NONE)
  {
    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = evt.GetOrientation() == wxHORIZONTAL ? E_HSCROLL : E_VSCROLL;
	  e.v.scroll.pos = evt.GetPosition();
	  e.v.scroll.what = sc;
	  DoXvtEvent(e);
  }
}

void TwxWindow::OnSetFocus(wxFocusEvent& WXUNUSED(e))
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_FOCUS;
	e.v.active = 1;
	DoXvtEvent(e);
}

void TwxWindow::OnSize(wxSizeEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_SIZE;
	e.v.size.width = evt.GetSize().x;
	e.v.size.height = evt.GetSize().y;
	DoXvtEvent(e);
}

void TwxWindow::OnTimer(wxTimerEvent& WXUNUSED(evt))
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_TIMER;
	e.v.timer.id = (WINDOW)this;
	DoXvtEvent(e);
}

void TwxWindow::OnButton(wxCommandEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_CONTROL;
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_PUSHBUTTON;
	DoXvtEvent(e);
}

void TwxWindow::OnCheckBox(wxCommandEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_CONTROL;
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_CHECKBOX;
	DoXvtEvent(e);
}

void TwxWindow::OnRadioButton(wxCommandEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_CONTROL;
	e.v.ctl.id = evt.GetId();
  e.v.ctl.ci.type = WC_RADIOBUTTON;
	DoXvtEvent(e);
}

void TwxWindow::SetMenuTree(const MENU_ITEM* tree)
{
	wxASSERT(tree != NULL);
	if (tree != NULL)
	{
		if (m_menu)
			xvt_res_free_menu_tree(m_menu);
		m_menu = xvt_menu_duplicate_tree(tree);
		((TTaskWin*)_task_win)->PushMenuTree(tree, this);
	}
}

BOOLEAN TwxWindow::AddPane(wxWindow* wnd, const char* caption, int nDock, int nFlags)
{
  BOOLEAN ok = wnd != NULL;
#if wxCHECK_VERSION(2,8,7)
  if (ok)
  {
    if (m_pManager == NULL)
      m_pManager = new wxAuiManager(this);
    wxAuiPaneInfo pane;
    pane.DefaultPane(); pane.Dockable(false); 
    if (caption && *caption)
      pane.Caption(caption);
    const wxSize sz = wnd->GetSize();
    switch (nDock)
    {
    case  1: 
      pane.Left().LeftDockable().RightDockable().MinSize(sz.x/2, -1); 
      break;
    case  2: 
      pane.Top().TopDockable().BottomDockable().MinSize(-1, sz.y/2); 
      break;
    case  3: 
      pane.Right().LeftDockable().RightDockable().MinSize(sz.x/2, -1); 
      break;
    case  4: 
      pane.Bottom().TopDockable().BottomDockable().MinSize(-1, sz.y/2); 
      break;
    default: 
      pane.CentrePane().Floatable(false); 
      break;
    }
    ok = m_pManager->AddPane(wnd, pane);
    if (ok)
      m_pManager->Update();
  }
#endif
  return ok;
}

TwxWindow::TwxWindow() 
         :  m_menu(NULL), _type(W_DOC), _eh(NULL), _app_data(0L), 
            _timer(NULL), m_pManager(NULL)
{ }

TwxWindow::TwxWindow(wxWindow *parent, wxWindowID id, const wxString& title,
										 const wxPoint& pos, const wxSize& size, long style)
  			 : TwxWindowBase(parent, id, title, pos, size, style),
           m_menu(NULL), _eh(NULL), _timer(NULL), m_pManager(NULL)
{
  _nice_windows.Put((WINDOW)this, this);
}

TwxWindow::~TwxWindow()
{
  if (_timer)
		delete _timer;
#if wxCHECK_VERSION(2,8,7)
  if (m_pManager)
  {
    m_pManager->UnInit(); // Obbligatorio ma, chissa' perche', non gestito dal distruttore!
    delete m_pManager;
  }
#endif
	if (m_menu)
	{
		xvt_res_free_menu_tree(m_menu);
		((TTaskWin*)_task_win)->PopMenuTree();
	}

  _nice_windows.Delete((WINDOW)this);
}


///////////////////////////////////////////////////////////
// Main application = TASK_WIN functions
///////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS(TTaskWin, wxFrame)

BEGIN_EVENT_TABLE(TTaskWin, wxFrame)
  EVT_CLOSE(TTaskWin::OnClose)
  EVT_MENU_RANGE(1000, 32766, TTaskWin::OnMenu)
	EVT_PAINT(TTaskWin::OnPaint)
	EVT_SIZE(TTaskWin::OnSize)
END_EVENT_TABLE()

void TTaskWin::OnClose(wxCloseEvent& evt)
{
	if (evt.CanVeto())
	{
	  EVENT e; memset(&e, 0, sizeof(EVENT));
	  e.type = E_CLOSE;
	  int veto = _task_win_handler((WINDOW)this, &e);
		evt.Veto(veto != 0);
	}
	else
		evt.Skip();
}

void TTaskWin::OnMenu(wxCommandEvent& evt)
{
	EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_COMMAND;
	e.v.cmd.control = 0; e.v.cmd.shift = 0;
	e.v.cmd.tag = evt.GetId();

	if (m_MenuOwner == NULL || m_MenuOwner == this)
  	_task_win_handler((WINDOW)this, &e);
	else
		((TwxWindow*)m_MenuOwner)->_eh((WINDOW)m_MenuOwner, &e);
}

void TTaskWin::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_UPDATE;
	RCT& rct = e.v.update.rct;

  wxRect rctDamaged = GetUpdateRegion().GetBox();
  rct.left = rctDamaged.x;
	rct.top = rctDamaged.y;
	rct.right = rctDamaged.GetRight()+1;
	rct.bottom = rctDamaged.GetBottom()+1;

	TDC& dc = GetTDCMapper().GetTDC((WINDOW)this);
	dc.GetDC(true); // Forza la creazione di un wxPaintDC
	_task_win_handler((WINDOW)this, &e);
	dc.KillDC();
}

void TTaskWin::OnSize(wxSizeEvent& evt)
{
  EVENT e; memset(&e, 0, sizeof(EVENT));
	e.type = E_SIZE;
	e.v.size.width = evt.GetSize().x;
	e.v.size.height = evt.GetSize().y;
	_task_win_handler((WINDOW)this, &e);
}

void TTaskWin::SetMenuTree(const MENU_ITEM* tree)
{
	if (m_menu)
		xvt_res_free_menu_tree(m_menu);
	m_menu = xvt_menu_duplicate_tree(tree);

	wxMenuBar* bar = GetMenuBar();
	for ( ; tree != NULL && tree->tag != 0; tree++)
	{
		wxMenu* pMenu = new wxMenu;
		for (MENU_ITEM* mi = tree->child; mi != NULL && mi->tag != 0; mi++)
		{
			wxMenuItem* item = NULL;
			if (mi->separator)
				item = new wxMenuItem(pMenu, wxID_SEPARATOR);
			else
				item = new wxMenuItem(pMenu, mi->tag, mi->text, wxEmptyString, mi->checkable);
      pMenu->Append(item);
		}
		const int nLast = bar->GetMenuCount()-1;
		int m;
		for (m = 2; m < nLast; m++)
		{
			wxMenu* pMenu = bar->GetMenu(m);
			if (pMenu->FindItem(tree->child->tag))
			{
				bar->Remove(m);
//				delete pMenu;
				break;
			}
		}
		bar->Insert(m, pMenu, tree->text);
	}
}

void TTaskWin::PushMenuTree(const MENU_ITEM* tree, wxWindow* owner)
{
	if(m_pOldBar != NULL)
    PopMenuTree();
  m_pOldBar = GetMenuBar();

	wxMenuBar* pBar = new wxMenuBar;
	for (; tree && tree->tag != 0; tree++)
	{
		wxMenu* pMenu = new wxMenu;
		for (MENU_ITEM* mi = tree->child; mi != NULL && mi->tag != 0; mi++)
		{
			wxMenuItem* item = NULL;
			if (mi->separator)
				item = new wxMenuItem(pMenu, wxID_SEPARATOR);
			else
				item = new wxMenuItem(pMenu, mi->tag, mi->text, wxEmptyString, mi->checkable);
      pMenu->Append(item);
		}
		pBar->Append(pMenu, tree->text);
	}
	SetMenuBar(pBar);
	m_MenuOwner = owner;
}

void TTaskWin::PopMenuTree()
{
	wxASSERT(m_pOldBar != NULL);
  wxMenuBar* pBar = GetMenuBar();
	SetMenuBar(m_pOldBar);
	delete pBar;
	m_pOldBar = NULL;
	m_MenuOwner = NULL;  // = this;
}

TTaskWin::TTaskWin(wxWindowID id,
           const wxString& title, const wxPoint& pos,
           const wxSize& size, long style)
				: wxFrame(NULL, id, title, pos, size, style), m_menu(NULL), m_pOldBar(NULL), m_MenuOwner(NULL)
{
	wxIcon* ico = _GetIconResource(ICON_RSRC);
	if (ico)
		SetIcon(*ico);
  _nice_windows.Put((WINDOW)this, this);
}

TTaskWin::~TTaskWin()
{
	if (m_menu)
	{
		xvt_res_free_menu_tree(m_menu);
		m_menu = NULL;
	}
  _nice_windows.Delete((WINDOW)this);
}

///////////////////////////////////////////////////////////
// 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 wxNotebook
{
public:
  wxNotebookPage* AddTab(int idx, const wxString text, wxBitmap* bmp);
  TwxNoteBook(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size);
};

class TwxTreeCtrl : public wxTreeCtrl
{
#if wxCHECK_VERSION(2,8,7)
  WX_DECLARE_VOIDPTR_HASH_MAP(int, XVT_IMAGE_Map);
	XVT_IMAGE_Map m_img;
#else
  wxHashTable m_img;
#endif
  wxFont m_font;
  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)

public:
  void SetNodeImages(const wxTreeItemId& id, XVT_IMAGE item_image, 
                     XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image);
#if wxCHECK_VERSION(2,8,7)
  virtual bool SetFont(const wxFont& font) { m_font = font; return font.IsOk(); }
#else
	virtual bool SetFont(const wxFont& font) { m_font = font; return true; }
#endif
  virtual wxFont GetFont() const;
  
  void Suspend();
  void Resume();
  TwxTreeCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size);
};

WINDOW xvt_ctl_create_def(WIN_DEF *win_def_p, WINDOW parent_win, long app_data)
{
  wxASSERT(win_def_p != NULL);
	const wxRect rct = NormalizeRCT(&win_def_p->rct);
  wxWindow* pParent = (wxWindow*)parent_win;
  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 */
		{
			const long style = win_def_p->wtype == WC_HSCROLL ? wxSB_HORIZONTAL : wxSB_VERTICAL;
		  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_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
      {                                        // Bottone figo con immagini
        wxBitmap bmp;
        pb = new wxBitmapButton(pParent, id, bmp, rct.GetPosition(), rct.GetSize()); 
      }
      win = (WINDOW)pb;
    }
    break;
  case WC_CHECKBOX: /* check box */
    {
      long style = wxCHK_2STATE | wxTRANSPARENT_WINDOW;
      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 */
    {
      wxRadioButton* rb = new wxRadioButton(pParent, id, win_def_p->text,
			                                      rct.GetPosition(), rct.GetSize()); 
      win = (WINDOW)rb;
    }
    break;
  case WC_NOTEBK:
    {
      TwxNoteBook* nb = new TwxNoteBook(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)nb;
    }
    break;
  case WC_TREE:
    {
      TwxTreeCtrl* tv = new TwxTreeCtrl(pParent, id, rct.GetPosition(), rct.GetSize());
      win = (WINDOW)tv;
      XVT_FNTID font_id = win_def_p->v.ctl.font_id;
      if (font_id == NULL)
        font_id = xvt_dwin_get_font(parent_win);
      if (font_id != NULL)
      {
        const	wxFont& font = ((TFontId*)font_id)->Font(NULL, win);
        tv->SetFont(font);
      }
    }
    break;
	default:
		SORRY_BOX(); break;
	}

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

	return win;
}

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

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

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

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

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

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

wxNotebookPage* TwxNoteBook::AddTab(int idx, const wxString text, wxBitmap* bmp)
{
  int imageId = -1;
  if (bmp != NULL)
  {
    wxImageList* il = GetImageList();
    if (il == NULL)
      AssignImageList(il = new wxImageList(16,16));
    imageId = il->Add(*bmp);
  }

  wxNotebookPage* nbp = new TwxWindow(this, wxID_ANY, text, 
                                      wxDefaultPosition, wxDefaultSize, 0);
  if (idx < 0 || idx >= (int)GetPageCount())
    AddPage(nbp, text, false, imageId);
  else
    InsertPage(idx, nbp, text, false, imageId);

  return nbp;
}

TwxNoteBook::TwxNoteBook(wxWindow *parent, wxWindowID id, 
                         const wxPoint& pos, const wxSize& size)
           : wxNotebook(parent, id, pos, size)
{}

void xvt_notebk_add_page(WINDOW notebk, short tab_no, short page_no, 
                         const char* title, long page_data)
{
  wxASSERT(page_no == 0);
  CAST_NOTEBOOK(notebk, nb);
  if (tab_no < 0 || tab_no >= (int)nb.GetPageCount())
  {
    tab_no = nb.GetPageCount();
    xvt_notebk_add_tab(notebk, tab_no, title, NULL);
  }
  
  TwxWindow* nbp = (TwxWindow*)nb.GetPage(tab_no);
  nbp->SetLabel(title);
	nbp->_app_data = page_data;
  nbp->SetBackgroundStyle(wxBG_STYLE_CUSTOM); // Lo sfondo viene disegnato nella OnPaint
}

void xvt_notebk_add_tab(WINDOW notebk, short tab_no, const char* title, XVT_IMAGE image)
{
  CAST_NOTEBOOK(notebk, nb);
  nb.AddTab(tab_no, title, NULL);
}

WINDOW xvt_notebk_create_face(WINDOW notebk, short tab_no, short page_no,
                              EVENT_MASK mask, EVENT_HANDLER face_eh, long app_data)
{
  return xvt_notebk_create_face_def(notebk, tab_no, page_no, NULL, mask, face_eh, app_data);
}

WINDOW xvt_notebk_create_face_def(WINDOW notebk, short tab_no, short WXUNUSED(page_no),
                                  WIN_DEF* win_def_p, EVENT_MASK WXUNUSED(mask), 
                                  EVENT_HANDLER face_eh, long app_data)
{
  wxASSERT(tab_no >= 0 && tab_no < xvt_notebk_get_num_tabs(notebk));
  CAST_NOTEBOOK(notebk, nb);
  TwxWindow* nbp = (TwxWindow*)nb.GetPage(tab_no);

  wxWindowID id = 10001+tab_no;
  wxString text;
  long style = 0;
  if (win_def_p != NULL)
  {
    id = win_def_p->v.ctl.ctrl_id;
    text = win_def_p->text;
    style = win_def_p->v.ctl.flags;
  }
  if (!text.IsEmpty()) 
    nbp->SetLabel(text);
  if (id > 0)
    nbp->SetId(id);
  nbp->_eh = face_eh;
  nbp->_app_data = app_data;

  return (WINDOW)nbp;
}

WINDOW xvt_notebk_get_face(WINDOW notebk, short tab_no, short page_no)
{
  wxASSERT(tab_no >= 0 && tab_no < xvt_notebk_get_num_tabs(notebk));
  wxASSERT(page_no == 0);
  CAST_NOTEBOOK(notebk, nb);
  return (WINDOW)nb.GetPage(tab_no);
}

short xvt_notebk_get_num_tabs(WINDOW notebk)
{
  CAST_NOTEBOOK(notebk, nb);
  return nb.GetPageCount();
}

void xvt_notebk_set_front_page(WINDOW notebk, short tab_no, short page_no)
{
#if wxCHECK_VERSION(2,8,7)
  CAST_NOTEBOOK(notebk, nb);
  nb.ChangeSelection(tab_no);  // Non generare eventi di cambio pagina!
#endif
}

void xvt_notebk_set_tab_title(WINDOW notebk, short tab_no, const char* title)
{
  CAST_NOTEBOOK(notebk, nb);
  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)
END_EVENT_TABLE();

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

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();
    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = 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 = (TwxWindow*)GetParent();
    win->DoXvtEvent(e);
    if (GetChildrenCount(id) == 0) // Allora e' proprio vero ...
      SetItemHasChildren(id, false);
  }
}

void TwxTreeCtrl::OnCollapsed(wxTreeEvent& evt)
{
  if (!m_nFrozen)
  {
    Suspend();
    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = 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 = (TwxWindow*)GetParent();
    win->DoXvtEvent(e);
    Resume();
  }
}

void TwxTreeCtrl::OnClick(wxTreeEvent& evt, bool bDouble)
{
  if (!m_nFrozen)
  {
    Suspend();
    EVENT e; memset(&e, 0, sizeof(EVENT));
    e.type = 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 = (TwxWindow*)GetParent();
    win->DoXvtEvent(e);
    Resume();
  }
}

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

void TwxTreeCtrl::OnActivated(wxTreeEvent& evt)
{ OnClick(evt, true); }

int TwxTreeCtrl::img2int(XVT_IMAGE xvt_img)
{
  int i = -1;
  if (xvt_img != NULL)
  {
#if wxCHECK_VERSION(2,8,7)
    i = m_img[xvt_img] - 1;       // Ho memorizzato indice+1
#else
		i = (int)m_img.Get((long)xvt_img) - 1;
#endif
    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!
      }
      i = il->Add(wxBitmap(img));
#if wxCHECK_VERSION(2,8,7)
      m_img[xvt_img] = i+1;       // Memorizzo indice+1
#else
      m_img.Put((long)xvt_img, (wxObject*)(i+1));       // Memorizzo indice+1
#endif
    }
    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);
    }
  }
}

wxFont TwxTreeCtrl::GetFont() const 
{ 
#if wxCHECK_VERSION(2,8,7)
	return m_font.IsOk() ? m_font : wxTreeCtrl::GetFont(); 
#else
	return wxTreeCtrl::GetFont(); 
#endif
}

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

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

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 item_image,
  XVT_IMAGE collapsed_image, XVT_IMAGE expanded_image,
  long attrs, int 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 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 (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_expand_node(WINDOW win, XVT_TREEVIEW_NODE node, BOOLEAN recurse)
{
  BOOLEAN ok = (win != NULL_WIN) && (node != NULL);
  if (ok)
  {  
    CAST_TREEVIEW(win, tv);
    const wxTreeItemId id(node);
#if wxCHECK_VERSION(2,8,7)
    if (recurse)
      tv.ExpandAllChildren(id);
    else
#endif
      tv.Expand(id);
  }
  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);
    tv.Delete(id);
  }
  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);
    wxTreeItemId id(node);
    if (!id.IsOk())
      id = tv.GetRootItem();
    tv.DeleteChildren(id);
    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);
    tv.SelectItem(id, sel != 0);
    if (sel)
      tv.EnsureVisible(id);
  }
}

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