/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          ccontrol.cpp  -  description
                             -------------------
    begin                : Tue Apr 4 2000
    copyright            : (C) 2000-2005 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>
#include <FL/fl_draw.H>
#include <ctype.h>
#include <stdio.h>

#include <sptk3/CEvent.h>
#include <sptk3/CControl.h>
#include <sptk3/CException.h>
#include <sptk3/string_ext.h>

using namespace std;

const Fl_Menu_Item CControl::defaultControlMenu[] = {
   {"Copy", FL_CTRL+FL_Insert,   CControl::defaultControlMenuCopy, 0, 0, 0, 0, 12, 0},
   {"Cut", FL_SHIFT+FL_Delete,   CControl::defaultControlMenuCut, 0, 0, 0, 0, 12, 0},
   {"Paste", FL_SHIFT+FL_Insert, CControl::defaultControlMenuPaste,  0, 0, 0, 0, 12, 0},
   {"Clear", 0,                  CControl::defaultControlMenuClear,  0, 0, 0, 0, 12, 0},
   { 0 }
};


void CControl::defaultControlMenuCopy(Fl_Widget *w,void *) {
   CControl *control = dynamic_cast<CControl *>(w->parent());
   if (control) {
      std::string text = control->data().asString();
      Fl::copy(text.c_str(),text.length(),1);
   }
}

void CControl::defaultControlMenuCut(Fl_Widget *w,void *) {
   CControl *control = dynamic_cast<CControl *>(w->parent());
   if (control) {
      std::string text = control->data().asString();
      Fl::copy(text.c_str(),text.length(),1);
      control->data("");
   }
}

void CControl::defaultControlMenuPaste(Fl_Widget *w,void *) {
   CControl *control = dynamic_cast<CControl *>(w->parent());
   if (control) {
      control->data("");
      Fl::paste(*control->m_control,1);
   }
}

void CControl::defaultControlMenuClear(Fl_Widget *w,void *) {
   CControl *control = dynamic_cast<CControl *>(w->parent());
   if (control) 
      control->data("");
}

void CControl::ctor_init(const char *label) {
   if (label) m_label = label;
   m_limited = false;
   m_controlFlags = FGE_SINGLELINEENTRY;
   m_textFont = FL_HELVETICA;
   m_textSize = FL_NORMAL_SIZE;
   m_textColor = FL_BLACK;
   m_control = 0;
   m_tag = 0;
   m_labelWidth = 100;
   m_labelColor = FL_BLACK;
   m_menuButton = 0L;
   m_hasFocus = false;
   box(FL_NO_BOX);
}

CControl::CControl(const char * label,int layoutSize,CLayoutAlign layoutAlignment) 
: Fl_Group(0,0,layoutSize,layoutSize),CLayoutClient(layoutSize,layoutAlignment) {
   ctor_init(label);
}

#ifdef __COMPATIBILITY_MODE__    
CControl::CControl(int x,int y,int w,int h,const char * label)
: Fl_Group(x,y,w,h,""), CLayoutClient(w,SP_ALIGN_NONE) {
   ctor_init(label);
}
#endif

void CControl::flags(unsigned flags) 
{
   // Only allow to change user flags
   m_controlFlags &= 0xFF00;
   m_controlFlags |= flags & 0xFF;
}

unsigned CControl::labelHeight() const {
   fl_font(labelfont(),labelsize());
   int cw = m_labelWidth - 3, ch = 0;
   if (m_labelWidth)
      fl_measure(m_label.c_str(), cw, ch);
   return ch;
}

void CControl::resize(int x,int y,int w,int h) {
   Fl_Group::resize(x,y,w,h);
   if (m_control) {
      int hh = h;
      if (!(m_controlFlags & FGE_MULTILINEENTRY)) hh = textSize() + 8;
      m_control->resize(x+m_labelWidth,y,w-m_labelWidth,hh);
   }
   if (m_menuButton)
      m_menuButton->resize(x+m_labelWidth,y,w-m_labelWidth,h);
}

Fl_Color CControl::labelColor() const {
   return m_labelColor;
}

void CControl::labelColor(Fl_Color clr) {
   m_labelColor = clr;
}

void CControl::draw() 
{
   Fl_Group::draw();
   if (m_labelWidth > 5) {
      int hh = labelHeight();
      if (hh) {
         fl_font(labelfont(),labelsize());
         fl_color(m_labelColor);
         fl_draw(m_label.c_str(),x()+1,y()+1,m_labelWidth-3,hh,Fl_Align(FL_ALIGN_TOP|FL_ALIGN_RIGHT|FL_ALIGN_WRAP));
      }
   }
}

void CControl::label(const std::string label) {
   if ( m_label != label ) {
      m_label = label;
      damage(FL_DAMAGE_ALL);
      draw();
   }
}

void CControl::labelWidth(unsigned newWidth) 
{
   if (m_labelWidth != newWidth) {
      m_labelWidth = newWidth;
      resize(x(),y(),w(),h());
   }
}

Fl_Font CControl::textFont() const {
   return m_textFont;
}

void CControl::textFont(Fl_Font f) {
   m_textFont = f;
}

uchar CControl::textSize() const {
   return m_textSize;
}

void CControl::textSize(uchar s) {
   m_textSize = s;
}

Fl_Color CControl::textColor() const {
   return m_textColor;
}

void CControl::textColor(Fl_Color n) {
   m_textColor = n;
}

Fl_Color CControl::color() const {
   return m_control->color();
}

void CControl::color(Fl_Color clr) {
   m_control->color(clr);
}

uchar CControl::labelSize() const {
   return labelsize();
}

void CControl::labelSize(uchar sz) {
   labelsize(sz);
}

void CControl::onEnter() {
}

void CControl::onExit() {
}

bool checkFieldName(std::string fieldName) {
   unsigned len = fieldName.length();
   if (len > 80) return false;
   if (!len) return true;

   for (unsigned i = 0; i < len; i++) {
      if (!isalnum( fieldName[i] ))
         if (fieldName[i] != '_')
            return false;
   }
   if (!isalpha( fieldName[0] ))
      return false;
   return true;
}

bool CControl::containsFocus() const {
   return contains(Fl::focus()) != 0;
}

void CControl::notifyFocus(bool gotFocus) {
   if (gotFocus) {
      if (!m_hasFocus) {
         m_hasFocus = true;
         fireEvent(CE_FOCUS,0);
      }
   } else {
      if (m_hasFocus && !contains(Fl::focus())) {
         m_hasFocus = false;
         fireEvent(CE_UNFOCUS,0);
      }
   }
}

int CControl::handle(int event) {
   bool newFocus = false;
   switch (event) {
      case FL_SHOW:
         fireEvent(CE_SHOW,0);
         break;
      case FL_HIDE:
         fireEvent(CE_HIDE,0);
         break;
   }
   int rc = Fl_Group::handle(event);
   if (event == FL_UNFOCUS) {
      newFocus = containsFocus();
      if (!newFocus) {
         m_hasFocus = false;
         fireEvent(CE_UNFOCUS,0);
      }
   }
   return rc;
}

void CControl::fieldName(const std::string& s) {
   m_fieldName = s;
}

CControlKind CControl::controlNameToType(std::string typeName,int& maxLength,std::string values) {
   CControlKind controlType = DCV_UNKNOWN;
   char c1 = toupper(typeName[1]);
   maxLength = 0; 
   switch (toupper(typeName[0])) {
      case 'A':   // area code
         maxLength = 3;
         return DCV_INTEGER;

      case 'C':   // CURRENCY, CREDIT CARD, COMBO BOX, CHOICE, CHECK BUTTONS
         switch (c1) {
            case 'U':   
               controlType = DCV_FLOAT;
               break;
            case 'R':   
               controlType = DCV_STRING;
               maxLength = 16;
               break;
            case 'H':
               controlType = DCV_COMBO;
               if (toupper(typeName[2]) == 'E')
                  controlType = DCV_CHECKBUTTONS;
               break;
            case 'O':   
               controlType = DCV_COMBO;
               break;
         }
         break;

      case 'D':   // DATE, DATE AND TIME
         if (typeName.length() > 4)
            controlType = DCV_DATETIME;
         else  controlType = DCV_DATE;
         break;

      case 'F':   // float #
         controlType = DCV_FLOAT;
         break;

      case 'H':  // html
         controlType = DCV_HTMLBOX;
         break;

      case 'I':   // INTEGER
         if (trim(values).length())
            controlType = DCV_INTVALUECOMBO;
         else  controlType = DCV_INTEGER;
         break;

      case 'L':   // LABEL, LONG ZIP CODE
         switch (c1) {
            case 'A':
               controlType = DCV_BOX;
               break;
            case 'O':
               controlType = DCV_STRING;
               maxLength = 10;
               break;
         }
         break;

      case 'M':   // MASK, MULTIPLE CHOICE, money
         switch (c1) {
            case 'A': controlType = DCV_STRING; break;
            case 'U': controlType = DCV_CHECKBUTTONS; break;
            case 'O': controlType = DCV_FLOAT; break;
         }
         break;


      case 'P':   // PHONE, PHONE EXT, PLAIN TEXT
         if (c1 == 'H') {
            if (typeName.find("ext") == string::npos)
               controlType = DCV_PHONE; // phone number
            else {
               maxLength = 4;
               controlType = DCV_INTEGER;  // phone extension
            }
         } else {

            controlType = DCV_BOX;
         }
         break;

      case 'S':   // SIMPLE, STRING, SSN
         controlType = DCV_STRING;
         break;

      case 'T':   // TIME
         controlType = DCV_TIME;
         break;

      case 'Z':   // Zip code
         maxLength = 10;
         controlType = DCV_STRING;

         break;
   }
   return controlType;
}

void CControl::menu(const Fl_Menu_Item *newMenu) {
   if (!newMenu) { 
      if (m_menuButton) {
         remove(m_menuButton);
         delete m_menuButton;
         m_menuButton = 0L;
      }
      return;
   }
   if (!m_menuButton) {
      Fl_Group *currentGroup = Fl_Group::current();
      begin();
      m_menuButton = new Fl_Menu_Button(m_labelWidth,0,w()-m_labelWidth,h());
      m_menuButton->type(Fl_Menu_Button::POPUP23);
      m_menuButton->menu(newMenu);
      m_menuButton->visible_focus(false);
      end();
      Fl_Group::current(currentGroup);
   } else {
      m_menuButton->menu(newMenu);
   }
}

Fl_Menu_ *CControl::menu() const {
   return m_menuButton;
}

void CControl::internalCallback(Fl_Widget *internalWidget,void *data) {
   for (Fl_Widget *parentWidget = internalWidget->parent(); parentWidget; parentWidget = parentWidget->parent()) {
      CControl *control = dynamic_cast<CControl *>(parentWidget);
      if (control) {
         control->fireEvent(CE_DATA_CHANGED,long(data));
         break;
      }
   }
}

void CControl::fireEvent(CEvent ev,long arg) { 
   if (callback()) {
      m_event = CEventInfo(ev,arg);
      do_callback(); 
      m_event = CEventInfo(CE_NONE,0); 
   }
}
