/*------------------------------------------------------------------------------
 src/lib/djWidget/buttonWidget.cxx

 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djButtonWidget.h"
#include <djIcon.h>
#include <FL/Fl.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/fl_draw.H>

namespace dj {

PushButtonWidget::PushButtonWidget(const Str & in) :
  _label(in), _labelAscii(0)
{
  _labelAscii = toAscii(_label);
  
  _widget = new Fl_Button(0, 0, 0, 0);
  _widget->labelfont(dj::font());
  _widget->labelsize(dj::fontSize());
  _widget->labelcolor(FL_FOREGROUND_COLOR);
  _widget->label(_labelAscii);
  _widget->box(FL_THIN_UP_BOX);
  add(_widget);
  
  sizePolicy(FIXED, FIXED);
  
  _widget->callback(widgetCallback, this);
}

PushButtonWidget::~PushButtonWidget()
{
  if (_labelAscii) delete [] _labelAscii;
}

void PushButtonWidget::label(const Str & in)
{
  _label = in;
  if (_labelAscii) delete [] _labelAscii;
  _labelAscii = toAscii(_label);
  _widget->label(_labelAscii);
}
  
const Vec2i PushButtonWidget::sizeHint() const
{
  fl_font(labelfont(), labelsize());
  return
    Vec2i(max(ceil(fl_width(_labelAscii)), layoutButtonSize()), labelsize()) +
    layoutWidgetMargin() * 2 +
    Vec2i(Fl::box_dw(_widget->box()), Fl::box_dh(_widget->box()));
}

void PushButtonWidget::resize(int x, int y, int w, int h)
{
  Widget::resize(x, y, w, h);
  _widget->resize(x, y, w, h);
}

void PushButtonWidget::widgetCallback()
{
  _sig.emit(true);
}

CheckButtonWidget::CheckButtonWidget(const Str & in) :
  _value(false),
  _labelAscii(0),
  _callbacks(true)
{
  _widget = new Fl_Check_Button(0, 0, 0, 0);
  _widget->labelfont(dj::font());
  _widget->labelsize(dj::fontSize());
  _widget->labelcolor(FL_FOREGROUND_COLOR);
  add(_widget);
  
  sizePolicy(FIXED, FIXED);
  
  widgetUpdate();
  label(in);
  
  _widget->callback(widgetCallback, this);
}

CheckButtonWidget::~CheckButtonWidget()
{
  if (_labelAscii) delete [] _labelAscii;
}
  
void CheckButtonWidget::set(bool in)
{
  if (in == _value) return;
  _value = in;
  _sig.emit(_value);
  widgetUpdate();
}

void CheckButtonWidget::label(const Str & in)
{
  // XXX Add space between checkbox and label.
  //
  _label = Str(" ") + in;
  if (_labelAscii) delete [] _labelAscii;
  _labelAscii = toAscii(_label);
  _widget->label(_labelAscii);
}

const Vec2i CheckButtonWidget::sizeHint() const
{
  fl_font(labelfont(), labelsize());
  return
    Vec2i(labelsize() + 2 + ceil(fl_width(_labelAscii)), labelsize()) +
    layoutWidgetMargin() * 2;
}

void CheckButtonWidget::resize(int x, int y, int w, int h)
{
  Widget::resize(x, y, w, h);
  _widget->resize(x, y, w, h);
}

void CheckButtonWidget::widgetCallback()
{
  if (! _callbacks) return;
  set(_widget->value());
}

void CheckButtonWidget::widgetUpdate()
{
  _callbacks = false;
  _widget->value(_value);
  _callbacks = true;
}

ToolButtonWidget::ToolButtonWidget(const Str & icon, int iconMargin) :
  _value(false),
  _type(PUSH),
  _repeatActive(false),
  _icon(dj::icon(icon)), _iconMargin(iconMargin),
  _shortcut(0),
  _pressed(false), _inside(false)
{
  //box(FL_THIN_UP_BOX);
  box(FL_FLAT_BOX);

  sizePolicy(FIXED, FIXED);
}

ToolButtonWidget::~ToolButtonWidget()
{
  die();
}

void ToolButtonWidget::die()
{
  Fl::remove_timeout(repeatCallback, this);
}

void ToolButtonWidget::set(bool in)
{
  if (in == _value) return;
  _value = in;
  _sig.emit(_value);
  redraw();
}

void ToolButtonWidget::type(TYPE in)
{
  _type = in;
}

void ToolButtonWidget::icon(Fl_Image * in)
{
  _icon = in;
  redraw();
}

void ToolButtonWidget::shortcut(int in)
{
  _shortcut = in;
}

const Vec2i ToolButtonWidget::sizeHint() const
{
  return
    (_icon ? Vec2i(_icon->w(), _icon->h()) : layoutIconSize) +
    _iconMargin * 2 +
    frameWidth() * 2;
}

void ToolButtonWidget::draw()
{
  WidgetBase::draw();
  //Widget::draw();
  
  const Fl_Boxtype boxUp = this->box();
  const Fl_Boxtype boxDown = FL_THIN_DOWN_BOX;
  Fl_Boxtype box = boxUp;
  if (_value) {
    if (_pressed && _inside) ;
    else box = boxDown;
  } else {
    if (_pressed && _inside) box = boxDown;
    else ;
  }
  
  fl_draw_box(box, x(), y(), w(), h(), color());
  if (_icon)
    _icon->draw(x() + (w() - _icon->w()) / 2, y() + (h() - _icon->h()) / 2);
}

int ToolButtonWidget::handle(int in)
{
  const Vec2i mouse(Fl::event_x(), Fl::event_y());
  
  switch (in) {
  
    case FL_PUSH: {
    
      DJ_DBG_FNC("dj::ToolButtonWidget::handle");
      DJ_DBG_PRN("push");
      
      _pressed = true;
      _inside = true;
      if (REPEAT == _type)
        Fl::add_timeout(0.5, repeatCallback, this);
      redraw();
      
    } return 1;
      
    case FL_RELEASE: {
    
      DJ_DBG_FNC("dj::ToolButtonWidget::handle");
      DJ_DBG_PRN("release");
      
      if (
        ! _repeatActive &&
        intersect(Box2i(x(), y(), w(), h()), mouse)
      )
        action();
      _repeatActive = false;
      _pressed = false;
      _inside = false;
      Fl::remove_timeout(repeatCallback, this);
      redraw();
      
    } return 1;
    
    case FL_DRAG:
      if (intersect(Box2i(x(), y(), w(), h()), mouse)) {
        if (! _inside) {
          _inside = true;
          if (REPEAT == _type)
            Fl::add_timeout(0.5, repeatCallback, this);
          redraw();
        }
      } else {
        if (_inside) {
          _inside = false;
          Fl::remove_timeout(repeatCallback, this);
          redraw();
        }
      }
      return 1;
    
    case FL_SHORTCUT:
      if (Fl::event_key() == _shortcut) {
        action();
        redraw();
        return 1;
      }
      break;
  }
  
  return Widget::handle(in);
}

void ToolButtonWidget::repeatCallback()
{
  if (_inside) {
    _repeatActive = true;
    _sig.emit(true);
    Fl::repeat_timeout(0.1, repeatCallback, this);
  } else {
    _repeatActive = false;
    Fl::remove_timeout(repeatCallback, this);
  }
}

void ToolButtonWidget::action()
{
  switch (_type) {
    
    case PUSH:
    case REPEAT:
      _sig.emit(true);
      break;
        
    case TOGGLE:
      _value = ! _value;
      _sig.emit(_value);
      break;
        
    case RADIO:
      if (! _value) {
        _value = true;
        _sig.emit(_value);
      }
      break;
  }
}

}
