/* 
 * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */
#include "../lf_mforms.h"

#include "../lf_drawbox.h"

namespace mforms {
namespace gtk {

struct DrawBoxImplPrivateData
{
  DrawBoxImplPrivateData() : _drawbox(0) {}
  DrawBoxImpl   *_drawbox;
};

DrawBoxImpl::DrawBoxImpl(::mforms::DrawBox *self)
  : ViewImpl(self)
  , _fixed_width(-1)
  , _fixed_height(-1)
{
  _darea.signal_expose_event().connect(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::repaint), self));
  _darea.signal_button_press_event().connect_notify(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::mouse_button_event), self));
  _darea.signal_button_release_event().connect_notify(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::mouse_button_event), self));
  _darea.signal_enter_notify_event().connect_notify(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::mouse_cross_event), self));
  _darea.signal_leave_notify_event().connect_notify(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::mouse_cross_event), self));
  _darea.signal_motion_notify_event().connect_notify(sigc::bind(sigc::mem_fun(this, &DrawBoxImpl::mouse_move_event), self));

  _darea.set_size_request(10, 10); // set initial size to allow a repaint event to arrive

  // request mouse moved events
  _darea.add_events(Gdk::POINTER_MOTION_MASK);
  _darea.show(); 
  setup();
}

DrawBoxImpl::~DrawBoxImpl()
{}

void* DrawBoxImpl::on_repaint()
{
  _darea.queue_draw();
  return 0;
}

void DrawBoxImpl::set_size(int width, int height)
{
  _darea.set_size_request(width, height);
  ViewImpl::set_size(width, height);
  
  _fixed_width = width;
  _fixed_height = height;
}


bool DrawBoxImpl::repaint(GdkEventExpose *event, ::mforms::DrawBox *self)
{
  //This vv needs improvment on linux. Maybe setup an event listener which is bound to resize
  int w  = -1;
  int h = -1;
  self->get_layout_size(&w, &h);
  if (_fixed_height >= 0)
    h = _fixed_height;
  if (_fixed_width >= 0)
    w = _fixed_width;
  _darea.set_size_request(w, h);

  mforms::gtk::expose_event_slot(event, &_darea);

  Cairo::RefPtr<Cairo::Context> context(_darea.get_window()->create_cairo_context());
  self->repaint(context->cobj(), event->area.x, event->area.y, event->area.width, event->area.height);
  return true;
}

void DrawBoxImpl::mouse_cross_event(GdkEventCrossing *event, ::mforms::DrawBox *self)
{
  if (event->type == GDK_ENTER_NOTIFY)
    self->mouse_enter();
  else
    self->mouse_leave();
}

void DrawBoxImpl::mouse_button_event(GdkEventButton *event, ::mforms::DrawBox *self)
{
  int b = 0;
  switch (event->button) // button number assumptions from starter icon code
  {
  case 1: b = 0; break;
  case 2: b = 2; break;
  case 3: b = 1; break;
  }
  
  if (event->type == GDK_BUTTON_PRESS)
  {
    self->mouse_down(b, (int)event->x, (int)event->y);
  }
  else if (event->type == GDK_BUTTON_RELEASE)
  {
    // click before up
    self->mouse_click(b, (int)event->x, (int)event->y);
    self->mouse_up(b, (int)event->x, (int)event->y);
  }
  else if (event->type == GDK_2BUTTON_PRESS)
    self->mouse_double_click(b, (int)event->x, (int)event->y);
}
  
void DrawBoxImpl::mouse_move_event(GdkEventMotion *event, ::mforms::DrawBox *self)
{
  self->mouse_move((int)event->x, (int)event->y);
}

bool DrawBoxImpl::create(::mforms::DrawBox *self)
{
  return new DrawBoxImpl(self) != 0;
}

void DrawBoxImpl::set_needs_repaint(::mforms::DrawBox *self)
{
  // request a repaint so that this can be called from any thread
  DrawBoxImpl *impl = self->get_data<DrawBoxImpl>();

  mforms::Utilities::perform_from_main_thread(sigc::mem_fun(impl, &DrawBoxImpl::on_repaint), false);
}

//------------------------------------------------------------------------------
void DrawBoxImpl::set_back_color(const std::string& s)
{
  mforms::gtk::set_bgcolor(&_darea, s);
}

void DrawBoxImpl::init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();

  f->_drawbox_impl.create         = &DrawBoxImpl::create;
  f->_drawbox_impl.set_needs_repaint = &DrawBoxImpl::set_needs_repaint;
}

};
};
