/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CButton.cpp  -  description
                             -------------------
    begin                : Jan 2, 2003
    copyright            : (C) 2003 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at
   your option) any later version.

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#include <FL/Fl.H>

#if FL_MAJOR_VERSION < 2
#include <FL/fl_draw.h>
#include <FL/Fl_Pixmap.h>
#else
#include <fltk/draw.h>
#include <fltk/xpmImage.h>
#define Fl_Pixmap fltk::xpmImage
#define Fl_Image  fltk::Image
#endif

#include <sptk3/CButton.h>
#include <sptk3/CControl.h>
#include <sptk3/CThemes.h>

#include <buttons/calendar.xpm>
#include <buttons/cancel.xpm>
#include <buttons/delete.xpm>
#include <buttons/edit.xpm>
#include <buttons/help.xpm>
#include <buttons/insert.xpm>
#include <buttons/copy.xpm>
#include <buttons/left.xpm>
#include <buttons/new.xpm>
#include <buttons/next.xpm>
#include <buttons/no.xpm>
#include <buttons/ok.xpm>

#include <buttons/open.xpm>
#include <buttons/printer.xpm>
#include <buttons/print.xpm>
#include <buttons/prior.xpm>
#include <buttons/refresh.xpm>
#include <buttons/right.xpm>
#include <buttons/save.xpm>
#include <buttons/search.xpm>
#include <buttons/send.xpm>

#include <buttons/home.xpm>
#include <buttons/exec.xpm>
#include <buttons/stop.xpm>
#include <buttons/exit.xpm>
#include <buttons/saveas.xpm>
#include <buttons/configure.xpm>

#include <buttons/small_add.xpm>
#include <buttons/small_browse.xpm>
#include <buttons/small_calendar.xpm>
#include <buttons/small_delete.xpm>
#include <buttons/small_edit.xpm>
#include <buttons/small_refresh.xpm>
#include <buttons/stepleft.xpm>
#include <buttons/stepright.xpm>
#include <buttons/view.xpm>

static Fl_Pixmap pixmap_calendar(calendar_xpm);
static Fl_Pixmap pixmap_cancel(cancel_xpm);
static Fl_Pixmap pixmap_insert(insert_xpm);
static Fl_Pixmap pixmap_delete(delete_xpm);
static Fl_Pixmap pixmap_edit(edit_xpm);
static Fl_Pixmap pixmap_help(help_xpm);
static Fl_Pixmap pixmap_copy(copy_xpm);
static Fl_Pixmap pixmap_left(left_xpm);
static Fl_Pixmap pixmap_new(new_xpm);
static Fl_Pixmap pixmap_next(next_xpm);
static Fl_Pixmap pixmap_no(no_xpm);
static Fl_Pixmap pixmap_ok(ok_xpm);
static Fl_Pixmap pixmap_open(open_xpm);
static Fl_Pixmap pixmap_printer(printer_xpm);
static Fl_Pixmap pixmap_print(print_xpm);
static Fl_Pixmap pixmap_prior(prior_xpm);
static Fl_Pixmap pixmap_refresh(refresh_xpm);
static Fl_Pixmap pixmap_right(right_xpm);
static Fl_Pixmap pixmap_save(save_xpm);
static Fl_Pixmap pixmap_search(search_xpm);
static Fl_Pixmap pixmap_send(send_xpm);
static Fl_Pixmap pixmap_small_add(small_add_xpm);
static Fl_Pixmap pixmap_small_browse(small_browse_xpm);
static Fl_Pixmap pixmap_small_calendar(small_calendar_xpm);
static Fl_Pixmap pixmap_small_delete(small_delete_xpm);
static Fl_Pixmap pixmap_small_edit(small_edit_xpm);
static Fl_Pixmap pixmap_small_refresh(small_refresh_xpm);
static Fl_Pixmap pixmap_stepleft(stepleft_xpm);
static Fl_Pixmap pixmap_stepright(stepright_xpm);
static Fl_Pixmap pixmap_view(view_xpm);
static Fl_Pixmap pixmap_home(home_xpm);
static Fl_Pixmap pixmap_exec(exec_xpm);
static Fl_Pixmap pixmap_stop(stop_xpm);
static Fl_Pixmap pixmap_exit(exit_xpm);
static Fl_Pixmap pixmap_saveas(saveas_xpm);
static Fl_Pixmap pixmap_configure(configure_xpm);

void CBaseButton::smallPixmap(int buttonKind) {
   Fl_Pixmap  *pixmap = 0;
   switch (buttonKind) {
      case SP_ADD_BUTTON:      pixmap = &pixmap_small_add;      break;
      case SP_DELETE_BUTTON:   pixmap = &pixmap_small_delete;   break;
      case SP_EDIT_BUTTON:     pixmap = &pixmap_small_edit;     break;
      case SP_BROWSE_BUTTON:   pixmap = &pixmap_small_browse;   break;
      case SP_CALENDAR_BUTTON: pixmap = &pixmap_small_calendar; break;
      case SP_REFRESH_BUTTON:  pixmap = &pixmap_small_refresh;  break;
   }
   m_image = pixmap;
}

void CBaseButton::largePixmap(int buttonKind,const char *label) {
   Fl_Pixmap  * pixmap = 0;
   const char *l = 0L;
   switch (buttonKind) {
      case SP_ADD_BUTTON:      pixmap = &pixmap_insert;   l = "Add";     break;
      case SP_DELETE_BUTTON:   pixmap = &pixmap_delete;   l = "Delete";  break;
      case SP_EDIT_BUTTON:     pixmap = &pixmap_edit;     l = "Edit";    break;
      case SP_BROWSE_BUTTON:   pixmap = &pixmap_edit;     l = "Browse";  break;
      case SP_CALENDAR_BUTTON: pixmap = &pixmap_calendar; l = "Date"; break;
      case SP_REFRESH_BUTTON:  pixmap = &pixmap_refresh;  l = "Refresh"; break;
      case SP_CANCEL_BUTTON:   pixmap = &pixmap_cancel;   l = "Cancel";  break;
      case SP_HELP_BUTTON:     pixmap = &pixmap_help;     l = "Help";    break;
      case SP_NO_BUTTON:       pixmap = &pixmap_no;       l = "No";      break;
      case SP_OK_BUTTON:       pixmap = &pixmap_ok;       l = "Ok";      break;
      case SP_OPEN_BUTTON:     pixmap = &pixmap_open;     l = "Open";    break;
      case SP_PRINT_BUTTON:    pixmap = &pixmap_print;    l = "Print";   break;
      case SP_SAVE_BUTTON:     pixmap = &pixmap_save;     l = "Save";    break;
      case SP_COPY_BUTTON:     pixmap = &pixmap_copy;     l = "Insert";  break;
      case SP_LEFT_BUTTON:     pixmap = &pixmap_left;     l = "Left";    break;
      case SP_NEW_BUTTON:      pixmap = &pixmap_new;      l = "New";     break;
      case SP_NEXT_BUTTON:     pixmap = &pixmap_next;     l = "Next";    break;
      case SP_PRINTER_BUTTON:  pixmap = &pixmap_printer;  l = "Printer"; break;
      case SP_PRIOR_BUTTON:    pixmap = &pixmap_prior;    l = "Prior";   break;
      case SP_RIGHT_BUTTON:    pixmap = &pixmap_right;    l = "Right";   break;
      case SP_SEARCH_BUTTON:   pixmap = &pixmap_search;   l = "Search";  break;
      case SP_SEND_BUTTON:     pixmap = &pixmap_send;     l = "Send";    break;
      case SP_STEPLEFT_BUTTON: pixmap = &pixmap_stepleft;  l = "Step Left";  break;
      case SP_STEPRIGHT_BUTTON:pixmap = &pixmap_stepright; l = "Step Right"; break;
      case SP_VIEW_BUTTON:     pixmap = &pixmap_view;     l = "View";    break;
      case SP_HOME_BUTTON:     pixmap = &pixmap_home;     l = "Home";    break;
      case SP_EXEC_BUTTON:     pixmap = &pixmap_exec;     l = "Exec";    break;
      case SP_STOP_BUTTON:     pixmap = &pixmap_stop;     l = "Stop";    break;
      case SP_EXIT_BUTTON:     pixmap = &pixmap_exit;     l = "Exit";    break;
      case SP_SAVE_AS_BUTTON:  pixmap = &pixmap_saveas;   l = "Save As.."; break;
      case SP_CONFIGURE_BUTTON:pixmap = &pixmap_configure;l = "Settings"; break;
   }
   if (!label)
      label = l;
   m_image = pixmap;
   this->label(label);
}

void CBaseButton::label(const char *lbl) {
   m_label = lbl;
   Fl_Button::label(m_label.c_str());
}

#ifdef __COMPATIBILITY_MODE__
CBaseButton::CBaseButton(CButtonKind kind,int x,int y,int w,const char *l,CThemeButtonSize tbt)
: Fl_Button(x,y,w,30), CLayoutClient(w,SP_ALIGN_NONE) {
   m_default = false;
   m_type = tbt;
   largePixmap(kind,l);
   box(FL_THIN_UP_BOX);
   color(FL_LIGHT3);
}
#endif

CBaseButton::CBaseButton(CButtonKind kind,CLayoutAlign layoutAlign,bool is_small,const char *l,CThemeButtonSize tbt)
: Fl_Button(0,0,20,20), CLayoutClient(20,layoutAlign) {
   m_default = false;
   m_type = tbt;
   if (is_small)
      smallPixmap(kind);
   else largePixmap(kind,l);
   box(FL_THIN_UP_BOX);
}

CBaseButton::CBaseButton(const char *l,CLayoutAlign layoutAlign,CThemeButtonSize tbt)
: Fl_Button(0,0,20,20), CLayoutClient(20,layoutAlign) {
   m_default = false;
   m_type = tbt;
   m_image = 0L;
   label(l);
   box(FL_THIN_UP_BOX);
}

void CBaseButton::defaultButton(bool isDefault) {
   m_default = isDefault;
   if (isDefault)
      box(FL_UP_BOX);
   else box(FL_THIN_UP_BOX);
   redraw();
}

void CBaseButton::drawFocusLine(int xs,int ys,int xe,int ye) {
    // Since FLTK under Win32 doesn't support the dotted line,
    // draw it with dots.
   int xp = xs;
   int yp = ys;
   int xsign = ((xs < xe) - (xs > xe));
   int ysign = ((ys < ye) - (ys > ye));
   if (xsign == 0 && ysign == 0) return;
   int dx = xsign * 2;
   int dy = ysign * 2;
   for ( ; xp * xsign <= xe * xsign && yp * ysign <= ye * ysign; xp += dx, yp += dy ) {
      fl_point(xp,yp);
   }
}

void CBaseButton::drawFocus(bool usingTheme) {
   fl_color(FL_BLACK);
   int r = 0;
   if (usingTheme) r = CThemes::buttonFocusRadius();
   if (r > h() / 2) r = h() / 2;
   int xx = x()+3;
   int yy = y()+3;
   int ww = w()-7;
   int hh = h()-7;
   if (m_default) {
      xx += 2;
      yy += 2;
      ww -= 6;
      hh -= 5;
   }
   drawFocusLine(xx+r,yy,xx+ww-r,yy);
   drawFocusLine(xx+ww-r,yy,xx+ww,yy+r);
   drawFocusLine(xx+ww,yy+r,xx+ww,yy+hh-r);
   drawFocusLine(xx+ww,yy+hh-r,xx+ww-r,yy+hh);
   drawFocusLine(xx+ww-r,yy+hh,xx+r,yy+hh);
   drawFocusLine(xx+r,yy+hh,xx,yy+hh-r);
   drawFocusLine(xx,yy+hh-r,xx,yy+r);
   drawFocusLine(xx,yy+r,xx+r,yy);
}

void CBaseButton::draw() {
#if FL_MAJOR_VERSION > 1
   align(FL_ALIGN_INSIDE);
   image(m_image);
   Fl_Button::draw();
#else
   align(Fl_Align(align()|FL_ALIGN_INSIDE));
   if (type() == FL_HIDDEN_BUTTON) return;

   int xx = x();
   int yy = y();
   int offset = 0;
   if (m_default) offset = 2;
   int X = xx  + offset;
   int Y = yy  + offset;
   int W = w() - offset*2;
   int H = h() - offset*2;

   bool highlited = (Fl::belowmouse() == this) && active_r();

   bool usingTheme = CThemes::drawButton(m_type, X, Y, W, H, highlited, value()!=0, m_default);
   if (!usingTheme) {
      if (m_default) {
         draw_box(FL_THIN_DOWN_BOX,FL_BLACK);
         draw_box(value()?FL_FLAT_BOX:box(), X, Y, W, H, color());
      } else {
         draw_box(value()?FL_THIN_DOWN_BOX:box(), X, Y, W, H, color());
      }
   }

   fl_push_clip(X+1,Y+1,W-2,H-2);
   if (!m_image)
      draw_label();
   else {
      int pressedOffset = value()?1:0;
      int xx = X + 4 + pressedOffset;
      int yy = Y + (H - m_image->h()) / 2 + pressedOffset;
      int ww = W - 6;
      int hh = H - 6;
      int tx = xx + m_image->w() + 2;
      int tw = ww - (tx - xx) - 2;

      if (!label()) { // center image
         xx = x() + (w() - m_image->w()) / 2 + pressedOffset;
         m_image->draw(xx,yy,m_image->w(),m_image->h(),0,0);
      } else {
         m_image->draw(xx,yy,ww,hh,0,0);
      }
      fl_color(FL_BLACK);
      fl_font(FL_HELVETICA,FL_NORMAL_SIZE);
      int wt = tw;
      int ht = 0;
      fl_measure(label(),wt,ht);
      int ty = Y + H / 2 - ht / 2;
      fl_draw(label(), tx, ty, wt, ht, Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_TOP));
      if (Fl::focus() == this) {
         //draw_focus(FL_FLAT_BOX, x() + 2, y() + 2, w() - 4, h() - 4);
         drawFocus(usingTheme);
      }
   }
   fl_pop_clip();
#endif
}

int CBaseButton::handle(int event) {
   switch (event) {
      case FL_FOCUS:
         {
            CControl *control = 0L;
            try {
               control = dynamic_cast<CControl *>(parent());
            }
            catch (...) {}

            if (!control && parent())
               try {
               control = dynamic_cast<CControl *>(parent()->parent());
               if (control)
                  control->notifyFocus();
            }
            catch (...) {}
         }
         break;

      case FL_ENTER:
      case FL_LEAVE:
         redraw();
         break;
   }
   return Fl_Button::handle(event);
}

void CBaseButton::preferredSize(int& w,int& h) {
   h = 0;

   int bh = 0;
   int bw = 0;
   int br = 0;
   CThemes::sizeButton(m_type,bw,bh);
   if (bw) {
      br = CThemes::buttonFocusRadius();
      if (m_default) {
         bh += 4;
         bw += 4;
      }
   }

   if (m_image)
      h = m_image->h() + 6;

   fl_font(labelfont(),labelsize());
   int lw = 0, lh = 0;
   fl_measure(label(),lw,lh);
   if (lh + 4 > h) h = lh + 4;
   if (lw) lw += 8;
   int ww = lw + 6 + br;
   if (m_image)
      ww += m_image->w();
   if (m_default) {
      h += 4;
      ww += 4;
   }
   if (bh) h = bh;
   if (w < ww) w = ww;
   if (w < bw) w = bw;
}
