extern "C"
{
#include <xvt.h>
#include <xvtcm.h>
}

#if XVT_OS != XVT_OS_WIN
#error "This file should be compiled for Windows only"
#endif

#include <colors.h>
#include <controls.h>
#include <urldefid.h>
#include <utility.h>
#include <window.h>

#define STRICT
#include <windows.h>

/////////////////////////////////////////////////////////// 
// TPicture_array
/////////////////////////////////////////////////////////// 

class TPicture_array : public TArray
{
public:
  TImage& add(short id, bool convert = FALSE);

  //TImage& image(short id) { return (TImage&)operator[](id); }
  const TImage& image(short id) const { return (const TImage&)operator[](id); }
  bool exist(short id) const { return objptr(id) != NULL; } 
  
  TPicture_array() : TArray(128) {}
  ~TPicture_array() {}
};

TImage& TPicture_array::add(short id, bool convert)
{           
  TImage* i = (TImage*)objptr(id);

  if (i == NULL)
  {
    i = new TImage(id);
    TArray::add(i, id);
    if (convert)
      i->convert_to_default_colors();  
  }  
  
  return *i;  
}

///////////////////////////////////////////////////////////
// Static data and functions
///////////////////////////////////////////////////////////

HIDDEN TControl* creating = NULL;
HIDDEN long ctl_flags;
HIDDEN WINDOW _hdc;
HIDDEN RCT _client; 
HIDDEN TPicture_array cpb;

HIDDEN void get_geometry(WINDOW win)
{
  xvt_vobj_get_client_rect(win, &_client);
  _client.right--; _client.bottom--;
  _hdc = win;
}

HIDDEN void set_creation_args(WIN_CREATION_ARGS *a)
{
  long& flags = a->win_flags;
  if (ctl_flags & CTL_FLAG_DISABLED)  flags |= WSF_DISABLED;
  if (ctl_flags & CTL_FLAG_INVISIBLE) flags |= WSF_INVISIBLE;

  switch(creating->id())
  {
  case -2:
    flags |= WSF_DISABLED;
    break;
  default:break;
  }
}

void xvt_draw_rect(WINDOW win, const RCT& rect, COLOR lt, COLOR rb, short depth)
{
  RCT r = rect;

  CPEN pen;
  pen.width = 1;
  pen.pat = PAT_SOLID;
  pen.style = P_SOLID;
  pen.color = lt;

  for (short d = 0; d < depth;)
  {
    xvt_dwin_set_cpen(win, &pen);

    PNT p;      // Current vertex of the rectangle

    bool drawed = FALSE;
    if (lt != COLOR_LTGRAY)
    {
      p.h = r.left; p.v = r.bottom;
      xvt_dwin_draw_set_pos(win, p);

      p.v = r.top;
      xvt_dwin_draw_line(win, p);
      p.h = r.right;
      xvt_dwin_draw_line(win, p);
      drawed = TRUE;
    }

    if (rb != COLOR_LTGRAY)
    {
      if (pen.color != rb)
      {
        pen.color = rb;
        xvt_dwin_set_cpen(win, &pen);
      }
      if (!drawed)
      {
        p.h = r.right; p.v = r.top;
        xvt_dwin_draw_set_pos(win, p);
      }
      p.v = r.bottom;
      xvt_dwin_draw_line(win, p);
      p.h = r.left;
      xvt_dwin_draw_line(win, p);
    }

    if (++d < depth)
    {
      r.left++; r.top++;
      r.right--; r.bottom--;
    }
  }
}


void TControl::create(
  short left, short top, short right, short bottom, const char* title,
  WINDOW parent, long flags, long app_data, short id)
{
  bool bold  = FALSE;
  _color     = NORMAL_COLOR;
  _backcolor = MASK_BACK_COLOR;
  
  while (*title == '@' || *title == '$')
  { 
    switch (*title++)
    {
    case '@':
      if (toupper(*title) == 'B') bold = TRUE;
      break;
    case '$':
      title++;                               // Skip [
      if (isalpha(*title))
        _color = trans_color(*title);
      else
      {                                       
        int r = 0, g = 0, b = 0;
        sscanf(title, "%d,%d,%d", &r, &g, &b);
        _color = MAKE_COLOR(r, g, b);
      }
      while (*title != ']')                  // Find ]
      {
        CHECK(*title, "Bad prompt color format");
        title++;  
      }  
      break;
    default:
      break;  
    }  
    title++;
  }  

  const int prop_count = 1;
  const char* prop_list[prop_count+1] = { title, NULL };

  _id = id;
  _disabled = (flags & CTL_FLAG_DISABLED) != 0;
  _checked  = (flags & CTL_FLAG_CHECKED)  != 0;
  _multiple = (flags & CTL_FLAG_MULTIPLE) != 0;
  _focused  = FALSE;
  _caption = title; _caption.strip("~");

  creating = this;
  ctl_flags = flags;
  _win = xvtcm_create(id, left, top, right, bottom,
                      prop_count, (char**)prop_list, parent,
                      0, 0, NULL, handler, set_creation_args);
  CHECKD(_win, "Can't create control ", id);

  creating = NULL;
  xvt_vobj_set_data(_win, app_data);
  xvt_set_font(_win, "", int(bold ? XVT_FS_BOLD : XVT_FS_NONE));
}


// Virtual destructor needed to make derived descrutors live!
TControl::~TControl()
{}


long XVT_CALLCONV1 TControl::handler(WINDOW win, EVENT* ep)
{
  HIDDEN bool tracking = FALSE;
  HIDDEN bool pressed = FALSE;

  if (ep->type == E_CREATE)
    xvtcm_eh_start(win, ep);

  TControl** model = (TControl**)xvtcm_get_model(win, sizeof(TControl*));
  CHECK(model, "Can't get the model");

  TControl*& cc = *model;
  if (ep->type == E_CREATE)
  {
    CHECK(creating, "Can't create a NULL control");
    cc = creating;
  }
  CHECK(cc, "Can't handle a NULL control");

  if (creating == NULL) switch(ep->type)
  {
  case E_FOCUS:
    cc->focus((bool)ep->v.active);
  case E_UPDATE:
    cc->update();
    break;
  case E_MOUSE_DOWN:
    xvt_win_trap_pointer(win);
    tracking = pressed = TRUE;
    cc->mouse_down(ep->v.mouse.where);
    break;
  case E_CHAR:
  {
    const KEY key = e_char_to_key(ep);
    switch(key)
    {
    case K_SPACE:
      cc->mouse_up();
      break;
    default:
      dispatch_e_char(xvt_vobj_get_parent(win), key);
      break;
    }
  }
    break;
  case E_MOUSE_MOVE:
    if (tracking)
    {
      RCT r;
      xvt_vobj_get_client_rect(win, &r);
      if (xvt_rect_has_point(&r, ep->v.mouse.where))
      {
        if (!pressed)
        {
          cc->mouse_down(ep->v.mouse.where);
          pressed = TRUE;
        }
      }
      else
      {
        if (pressed)
        {
          cc->update();
          pressed = FALSE;
        }
      }
    }
    break;
  case E_MOUSE_UP:
    if (tracking)
    {
      xvt_win_release_pointer();
      tracking = FALSE;
      if (pressed)
      {
        cc->mouse_up();
        pressed = FALSE;
      }
    }
    break;
  case E_DESTROY:
    delete *model;
    *model = NULL;
    xvtcm_eh_end(win, ep);
    break;
  default:
    break;
  }

  return 0L;
}

void TControl::enable(bool on)
{
  if (on == disabled())
  {
    _disabled = !on;
    update();
    xvt_vobj_set_enabled(win(), on);
  }
}

void TControl::check(bool on)
{
  _checked = on;
}

void TControl::update() const
{
  if (_win != _hdc)
    get_geometry(_win);
  xvt_dwin_clear(_hdc, _backcolor);
  xvt_dwin_set_fore_color(_hdc, disabled() ? DISABLED_COLOR : color());
}

void TControl::set_caption(const char* t)
{ 
  _caption = t;
  update();
}

///////////////////////////////////////////////////////////
// TText
///////////////////////////////////////////////////////////

class TText : public TControl
{
protected:
  virtual WIN_TYPE type() const { return WC_TEXT; }
  virtual void update() const;

public:
  TText(short left, short top, short right, short bottom,
        const char* caption,    WINDOW parent,
        long flags, long app_data, short id);
};

TText::TText(short left, short top, short right, short bottom,
             const char* caption, WINDOW parent,
             long flags, long app_data, short id)
{
  create(left, top+1, right, bottom-1, caption, parent, flags, app_data, id);
}

void TText::update() const
{
  TControl::update();
  xvt_dwin_set_fore_color(_hdc, color());
  xvt_dwin_draw_text(_hdc, _client.left, _client.top+BASEY, (char*)caption(), -1);
}

///////////////////////////////////////////////////////////
// TGroup
///////////////////////////////////////////////////////////

class TGroup : public TText
{
protected:
  virtual WIN_TYPE type() const { return WC_GROUPBOX; }
  virtual void update() const;
  void draw_round_rect(const RCT& r, COLOR c) const;

public:
  TGroup(short left, short top, short right, short bottom,
         const char* caption,   WINDOW parent,
         long flags, long app_data, short id);
};

TGroup::TGroup(short left, short top, short right, short bottom,
               const char* caption,     WINDOW parent,
               long flags, long app_data, short id)
: TText(left, top, right, bottom, caption, parent, flags | CTL_FLAG_DISABLED, app_data,
        (id < 0) ? -2 : id)
{}

void TGroup::draw_round_rect(const RCT& r, COLOR c) const
{
  CPEN pen;
  pen.width = 2;
  pen.pat = PAT_SOLID;
  pen.style = P_SOLID;
  pen.color = c;
  xvt_dwin_set_cpen(_hdc, &pen);

  CBRUSH brush = { PAT_HOLLOW, MASK_BACK_COLOR };
  xvt_dwin_set_cbrush(_hdc, &brush);
  xvt_dwin_draw_roundrect(_hdc, (RCT*)&r, ROWY, ROWY);
}

void TGroup::update() const
{
  TText::update();

  RCT r = _client;
  r.top += CHARY;
  r.right-=4; r.bottom-=ROWY/2;
  if (multiple())
  {
    r.left++; r.top++; r.right--; r.bottom--;
    draw_round_rect(r, MASK_LIGHT_COLOR);
  }
  else
  {
    xvt_draw_rect(_hdc, r, MASK_DARK_COLOR, MASK_LIGHT_COLOR);
  }
}

///////////////////////////////////////////////////////////
// Button
///////////////////////////////////////////////////////////

class TButton : public TControl
{
protected:
  enum { DEPTH = 2 };

  virtual WIN_TYPE type() const { return WC_PUSHBUTTON; }
  virtual void update() const;
  virtual void mouse_down(PNT where);
  virtual void mouse_up();
  virtual void check(bool on);
  virtual void draw_pressed(bool pressed) const;

public:
  TButton(short left, short top, short right, short bottom,
          const char* caption,WINDOW parent,
          long flags, long app_data, short id);
};


TButton::TButton(short left, short top, short right, short bottom,
                 const char* caption, WINDOW parent,
                 long flags, long app_data, short id)
{
  create(left, top, right, bottom, caption, parent, flags, app_data, id);
}


void TButton::draw_pressed(bool pressed) const
{
  get_geometry(win());

  xvt_dwin_clear(_hdc, COLOR_LTGRAY);
  RCT r = _client;
  xvt_draw_rect(_hdc, r, COLOR_BLACK, COLOR_BLACK);

  COLOR lt = pressed ? COLOR_GRAY : COLOR_WHITE;
  COLOR rb = pressed ? COLOR_LTGRAY : COLOR_GRAY;

  for (int i = DEPTH; i--;)
  {
    r.left++; r.top++; r.right--; r.bottom--;
    xvt_draw_rect(_hdc, r, lt, rb);
    if (i == 1) lt = COLOR_LTGRAY;
  }
}


void TButton::update() const
{
  TControl::update();
  draw_pressed(checked());
}

void TButton::mouse_down(PNT)
{
  draw_pressed(!checked());
}

void TButton::mouse_up()
{
  draw_pressed(checked());

  EVENT e;                      // Notification message
  e.type = E_CONTROL;
  e.v.ctl.id = id();
  e.v.ctl.ci.type = type();
  e.v.ctl.ci.win = win();
  xvt_win_dispatch_event(xvt_vobj_get_parent(win()), &e);
}

void TButton::check(bool on)
{
  TControl::check(on);
  draw_pressed(on);
}

///////////////////////////////////////////////////////////
// TPush button
///////////////////////////////////////////////////////////

class TPush_button : public TButton
{
  short _picup, _picdn;
  short _accel, _dx, _dy;

protected:
  void draw_pressed(bool pressed) const;
  void update() const;

public:
  TPush_button(short left, short top, short right, short bottom,
               const char* caption,     WINDOW parent,
               long flags, long app_data, short id);
  virtual ~TPush_button();
};

TPush_button::TPush_button(short left, short top, short right, short bottom,
                           const char* capt, WINDOW parent,
                           long flags, long app_data, short id)
: TButton(left-(id == DLG_F9), top, right, bottom,
          capt, parent, flags, app_data, id),
          _picup(0), _picdn(0)
{
  switch(id)
  {
    
  case DLG_OK:
    if (strcmp("Conferma", caption()) == 0)
      capt = format("#%d", BMP_OK);
    break;
  case DLG_CANCEL:
    if (strcmp("Annulla", caption()) == 0)
      capt = format("#%d", BMP_CANCEL);
    break;
  case DLG_QUIT:
    capt = format("#%d#%d", BMP_QUIT, BMP_QUITDN);
    break;
  case DLG_SELECT:
    capt = format("#%d", BMP_SELECT);
    break;
  case DLG_DELREC:
    capt = format("#%d#%d", BMP_DELREC, BMP_DELRECDN);
    break;
  case DLG_NEWREC:
    if (strcmp("Nuovo", caption()) == 0)       // Puo' essere Gestione
      capt = format("#%d#%d", BMP_NEWREC, BMP_NEWRECDN);
    break;
  case DLG_STOPREC:
    capt = format("#%d", BMP_STOPREC);
    break;
    /*    
       case DLG_FIRSTREC:
       capt = format("#%d", BMP_FIRSTREC);
       break;
       case DLG_PREVREC:
       capt = format("#%d", BMP_PREVREC);
       break;
       case DLG_NEXTREC:
       capt = format("#%d", BMP_NEXTREC);
       break;
       case DLG_LASTREC:
       capt = format("#%d", BMP_LASTREC);
       break;
       */    
  case DLG_SAVEREC:
    capt = format("#%d#%d", BMP_SAVEREC, BMP_SAVERECDN);
    break;
  case DLG_FINDREC:
    capt = format("#%d", BMP_FINDREC);
    break;
  case DLG_F9:
    if (*capt != '#')
      capt = format("#%d", BMP_SEARCH);
    break;
  case DLG_F8:
    if (*capt != '#')
      capt = format("#%d", BMP_LENTE);
    break;
  case DLG_LINK:
    if (strcmp("~Collega", caption()) == 0)
      capt = format("#%d", BMP_LINK);
    break;
  case DLG_EDIT:
    capt = format("#%d", BMP_EDIT);
    break;
  case DLG_PRINT:
    capt = format("#%d", BMP_PRINT);
    break;
  case DLG_SETPRINT:
    capt = format("#%d", BMP_SETPRINT);
    break;
  case DLG_RECALC:
    capt = format("#%d", BMP_RECALC);
    break;
  default:
    break;
  }

  const int height = bottom-top;
  const int width = right-left;
  
  const char* diesis = strchr(capt, '#');                
  if (diesis != NULL)
  {
    _picup = atoi(++diesis);
    const TImage& pic = cpb.add(_picup);
    diesis = strchr(diesis, '#');
    if (diesis !=  NULL)
    {
      _picdn = atoi(++diesis);
      cpb.add(_picdn);
    }                       
    
    _dx = (width - pic.width()) >> 1;
    _dy = (height - pic.height()) >> 1;

    _accel = -1;
  }
  else
  {
    TFixed_string c(capt);
    _accel = c.find('~');
    if (_accel > 0) 
      _accel = xvt_dwin_get_text_width(win(), (char*)capt, _accel);
    
    _dx = (width - xvt_dwin_get_text_width(win(), (char*)caption(), -1)) >> 1;
    _dy = (height- CHARY)/2 + BASEY - DEPTH;
  }
}

TPush_button::~TPush_button()
{
}

void TPush_button::draw_pressed(bool pressed) const
{
  TButton::draw_pressed(pressed);

  if (_picup)
  {    
    const short pic = (pressed && _picdn) ? _picdn : _picup;
    if (cpb.exist(pic))
    {
      const TImage& i = cpb.image(pic);
      RCT dst = i.rect();    
      if (pressed)
        xvt_rect_offset(&dst, _dx+DEPTH, _dy+DEPTH);
      else
        xvt_rect_offset(&dst, _dx, _dy);
      i.draw(_hdc, dst);
    }
  }
  else
  {
    const char* t = caption();
    
    const short x = _dx + DEPTH*pressed;
    const short y = _dy + DEPTH*pressed;

    xvt_dwin_set_fore_color(_hdc, COLOR_WHITE);
    xvt_dwin_draw_text(_hdc, x+1, y+1, (char*)t, -1);
    if (_accel >= 0)
      xvt_dwin_draw_text(_hdc, x+_accel+1, y+1, "_", 1);
    
    const COLOR c = disabled() ? DISABLED_COLOR : color();
    xvt_dwin_set_fore_color(_hdc, c);
    xvt_dwin_draw_text(_hdc, x, y, (char*)t, -1);
    if (_accel >= 0)
      xvt_dwin_draw_text(_hdc, x+_accel, y+0, "_", 1);
  }
}

void TPush_button::update() const
{
  draw_pressed(FALSE);
  if (focused() && id() != DLG_F9)
  {
    CPEN pen;
    pen.width = 1;
    pen.pat = PAT_SOLID;
    pen.style = P_SOLID;
    pen.color = COLOR_RED;
    xvt_dwin_set_cpen(_hdc, &pen);
    CBRUSH brush = { PAT_HOLLOW, COLOR_WHITE };
    xvt_dwin_set_cbrush(_hdc, &brush);
    xvt_dwin_draw_rect(_hdc, &_client);
  } else
    if (disabled() && _picup)
    {
      CPEN pen;
      pen.width = 1;
      pen.pat = PAT_SOLID;
      pen.style = P_SOLID;
      pen.color = COLOR_LTGRAY;
      xvt_dwin_set_cpen(_hdc, &pen);

      const int sx = _client.left+DEPTH+1;
      const int sy = _client.top+DEPTH+1;
      const int ex = _client.right-DEPTH-1;
      const int ey = _client.bottom-DEPTH-1;

      for (int i = sx; i < ex; i += 2)
      {
        PNT p = { sy, i };
        xvt_dwin_draw_set_pos(_hdc, p);
        p.v = ey;
        xvt_dwin_draw_line(_hdc, p);
      }
      for (i = sy; i < ey; i += 2)
      {
        PNT p = { i, sx };
        xvt_dwin_draw_set_pos(_hdc, p);
        p.h = ex;
        xvt_dwin_draw_line(_hdc, p);
      }
    }
}

///////////////////////////////////////////////////////////
// TPage_button
///////////////////////////////////////////////////////////

class TPage_button : public TControl
{
  enum { height = 19, width2 = 16, width = 32 };
  byte _flag;

protected:
  virtual void update() const;
  virtual void mouse_down(PNT where);
  virtual void mouse_up();

public:
  TPage_button(WINDOW parent, byte flag);
  byte get_flag() const { return _flag; }
  void set_flag(byte f);
};

TPage_button::TPage_button(WINDOW parent, byte flag) : _flag(flag)
{
  RCT r; xvt_vobj_get_client_rect(parent, &r);
  const int w = (flag == 3) ? width : width2;
  if (flag == 2) r.right -= w;
  
  create(r.right-w, r.bottom-height, r.right, r.bottom,
         "", parent, 0L, 0L, DLG_PAGE);
  cpb.add(BMP_BOOK1 + flag -1, flag == 3);
}

void TPage_button::mouse_down(PNT where)
{             
  bool p;           
  switch (_flag)
  {
  case 1:
    p = FALSE; break;
  case 2:
    p = TRUE;  
  default:
    p = where.h < (width2); break;
  }
  check(p);  
}

void TPage_button::mouse_up()
{
  dispatch_e_char(xvt_vobj_get_parent(win()), checked() ? K_PREV : K_NEXT);
}

void TPage_button::update() const
{
  TControl::update();    
  if (_flag)
    cpb.image(BMP_BOOK1 + _flag -1).draw(_hdc);
}

void TPage_button::set_flag(byte f)
{           
  _flag = f;
  update();
}

///////////////////////////////////////////////////////////
// TTag_button
///////////////////////////////////////////////////////////

class TTag_button : public TControl
{
  enum { height = 12, width = 32 };
  byte _curr;
  byte _page;
  byte _pages;

  WINDOW _parent;

protected:
  virtual void update() const;
  virtual void mouse_down(PNT where);
  virtual void mouse_up();

public:
  TTag_button(WINDOW parent, byte p, byte tot);
  byte get_pages() const { return _pages; }
  void set_pages(byte p);
};

TTag_button::TTag_button(WINDOW parent, byte p, byte tot)
: _parent(parent), _page(p), _pages(tot), _curr(p)
{
  RCT r; xvt_vobj_get_client_rect(parent, &r);
  create(0, 0, r.right, CHARY, "", parent,0L,0L, DLG_PAGETAGS);
}

void TTag_button::update() const
{
  get_geometry(win());
  xvt_dwin_clear(_hdc, MASK_DARK_COLOR);

  for (int i = 0; i < _pages; i++)
  {
    RCT r; xvt_rect_set(&r, width*i, 0, width*(i+1), _client.bottom+4);
    CBRUSH b = { PAT_SOLID, (i == _page) ? MASK_BACK_COLOR : MASK_DARK_COLOR};
    xvt_dwin_set_cbrush(_hdc, &b);
    xvt_dwin_set_std_cpen(_hdc, TL_PEN_BLACK);
    xvt_dwin_draw_rect(_hdc, &r);

    if (i == _page)
      xvt_draw_rect(_hdc, r, MASK_LIGHT_COLOR, MASK_BACK_COLOR);

    char n[4]; sprintf(n, "%d", i+1);
    xvt_dwin_set_fore_color(_hdc, color());
    xvt_dwin_draw_text(_hdc, (width-CHARX)/2 + i*width, BASEY, n, -1);
  }

  CPEN pen;
  pen.width = 1;
  pen.pat = PAT_SOLID;
  pen.style = P_SOLID;
  pen.color = MASK_LIGHT_COLOR;
  xvt_dwin_set_cpen(_hdc, &pen);

  PNT p = { _client.bottom, 0 };
  xvt_dwin_draw_set_pos(_hdc, p);
  p.h = width*_page;
  xvt_dwin_draw_line(_hdc, p);
  p.h += width+1;
  xvt_dwin_draw_set_pos(_hdc, p);
  p.h = _client.right;
  xvt_dwin_draw_line(_hdc, p);
}

void TTag_button::mouse_down(PNT where)
{
  _curr = where.h / width;
  if (_curr >= _pages) _curr = _pages-1; else
    if (_curr < 0) _curr = 0;
}

void TTag_button::mouse_up()
{
  if (_curr != _page)
    dispatch_e_char(_parent, K_CTRL + K_F1 + _curr);
}

void TTag_button::set_pages(byte p)
{
  _pages = p;
  update();
}

///////////////////////////////////////////////////////////
// Checkbox
///////////////////////////////////////////////////////////

class TCheckbox : public TButton
{
protected:
  virtual WIN_TYPE type() const;
  virtual void draw_pressed(bool pressed) const;
  virtual void update() const;

  int radio() const { return multiple() ? 1 : 0; }

public:
  TCheckbox(short left, short top, short right, short bottom,
            const char* caption,        WINDOW parent,
            long flags, long app_data, short id);
  virtual ~TCheckbox();
};

TCheckbox::TCheckbox(
  short left, short top, short right, short bottom,
  const char* caption,  WINDOW parent,
  long flags, long app_data, short id)
: TButton(left, top, right, bottom,
          caption, parent, flags, app_data, id)
{
  if (!cpb.exist(BMP_CHECK_ON))
  {              
    TImage& i = cpb.add(BMP_CHECK_ON, TRUE);     

    const short dx = 0;
    const short dy = BASEY - i.height() + 2;
    i.set_pos(dx, dy);
    
    cpb.add(BMP_CHECK_OFF, TRUE).set_pos(dx, dy);
    cpb.add(BMP_RADIO_ON, TRUE).set_pos(dx, dy);
    cpb.add(BMP_RADIO_OFF, TRUE).set_pos(dx, dy);
  }  
}


TCheckbox::~TCheckbox()
{
}


WIN_TYPE TCheckbox::type() const
{
  return multiple() ? WC_RADIOBUTTON : WC_CHECKBOX;
}


void TCheckbox::draw_pressed(bool pressed) const
{
  get_geometry(win());
  short pic;
  if (radio())
    pic = pressed ? BMP_RADIO_ON : BMP_RADIO_OFF;
  else  
    pic = pressed ? BMP_CHECK_ON : BMP_CHECK_OFF;
  
  if (cpb.exist(pic))
    cpb.image(pic).draw(_hdc);
}


void TCheckbox::update() const
{
  TButton::update();

  const int x = _client.left+20;
  const int y = _client.top + BASEY;

  if (focused())
  {
#if XVT_OS == XVT_OS_WIN
    RECT r;
    r.left = x-2; r.top = _client.top;
    r.right = _client.right-CHARX; r.bottom = y+3;

    HWND hwnd = (HWND)xvt_vobj_get_attr(_hdc, ATTR_NATIVE_WINDOW);
    HDC hdc = GetDC(hwnd);
    DrawFocusRect(hdc, &r);
    ReleaseDC(hwnd, hdc);
#endif
  }

  xvt_dwin_draw_text(_hdc, x, y, (char*)caption(), -1);
}

///////////////////////////////////////////////////////////
// User functions
///////////////////////////////////////////////////////////

WINDOW xvt_create_checkbox(
  short left, short top, short right, short bottom,
  const char* caption,
  WINDOW parent,
  long flags,
  long app_data,
  int id)
{
  TCheckbox* cb = new TCheckbox(left, top, right, bottom,
                                caption, parent,
                                flags, app_data, id);

  // It'll destroy itself automagically :-)

  return cb->win();
}

WINDOW xvt_create_radiobutton(
  short left, short top, short right, short bottom,
  const char* caption,
  WINDOW parent,
  long flags,
  long app_data,
  int id)
{
  flags |= CTL_FLAG_MULTIPLE;
  TCheckbox* cb = new TCheckbox(left, top, right, bottom,
                                caption, parent,
                                flags, app_data, id);

  // It'll destroy itself automagically :-)
  return cb->win();
}


WINDOW xvt_create_pushbutton(
  short left, short top, short right, short bottom,
  const char* caption,
  WINDOW parent,
  long flags,
  long app_data,
  int id)
{
  TControl* pb;

  switch (id)
  {
  case DLG_PAGE:
    pb = new TPage_button(parent, (byte)flags);
    break;
  case DLG_PAGETAGS:
    pb = new TTag_button(parent, (byte)flags, (byte)app_data);
    break;
  default:
    if (bottom-top > (CHARY<<1))
    {
      top += BASEY>>1;
      bottom -= BASEY>>1;
    }
    pb = new TPush_button(left, top, right, bottom,
                          caption, parent,
                          flags, app_data, id);
  }

  // It'll destroy itself automagically :-)
  return pb->win();
}


WINDOW xvt_create_text(
  short left, short top, short right, short bottom,
  const char* caption,
  WINDOW parent,
  long flags,
  long app_data,
  int id)
{
  TText* cb = new TText(left, top, right, bottom,
                        caption, parent,
                        flags, app_data, id);
  // It'll destroy itself automagically :-)
  return cb->win();
}


WINDOW xvt_create_groupbox(
  short left, short top, short right, short bottom,
  const char* caption,
  WINDOW parent,
  long flags,
  long app_data,
  int id)
{
  TGroup* cb = new TGroup(left, top, right, bottom,
                          caption, parent,
                          flags, app_data, id);
  // It'll destroy itself automagically :-)
  return cb->win();
}


void free_controls_bmp()
{
  cpb.destroy();
}


TControl* TControl::WINDOW2TControl(WINDOW win)
{
  CHECK(win, "Can't get the control model from a NULL window");
  TControl** model = (TControl**)xvtcm_get_model(win, 0);
  CHECK(model && *model, "Can't get the control model from a window");
  return *model;
}

void xvt_change_page_tags(WINDOW pag, bool on, WINDOW tag, byte p)
{
  if (pag != NULL_WIN)
  {
    TPage_button* pb = (TPage_button*)TControl::WINDOW2TControl(pag);
    byte f = pb->get_flag();
    if (on) f |= 0x1;
    else    f &= 0x2;
    pb->set_flag(f);
  }  
  
  if (tag != NULL_WIN)
  {
    TTag_button* pt = (TTag_button*)TControl::WINDOW2TControl(tag);
    pt->set_pages(p);
  }  
}


