/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CDataControl.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_Round_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Help_View.H>
#include <ctype.h>

#include <stdio.h>
#include <stdlib.h>
#include <sptk3/CStrings.h>
#include <sptk3/CDataControl.h>
#include <sptk3/CButton.h>
#include <sptk3/CCalendar.h>
#include <sptk3/CException.h>
#include <sptk3/CQuery.h>

#include <sptk3/CEditor.h>
#include <sptk3/CInput.h>
#include <sptk3/CDateControl.h>
#include <sptk3/CRadioButtons.h>
#include <sptk3/CCheckButtons.h>
#include <sptk3/CComboBox.h>

void CInput::ctor_init(bool autoCreate) {
   if (autoCreate) {
      m_control = new CInput_("");
      m_control->align(FL_ALIGN_LEFT);
      m_control->callback(CControl::internalCallback);
      m_control->when(FL_WHEN_CHANGED);
      m_control->color(FL_LIGHT3);
      menu(CControl::defaultControlMenu);
   } else {
      m_control = 0L;
   }
   end();
}

CInput::CInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CControl(label,layoutSize,layoutAlignment) {
   ctor_init(true);
}

CInput::CInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment,bool autoCreate)
: CControl(label,layoutSize,layoutAlignment) {
   ctor_init(autoCreate);
}

#ifdef __COMPATIBILITY_MODE__    
CInput::CInput(int x,int y,int w,int h,const char *label,bool autoCreate)
: CControl(x,y,w,h,label) {
   ctor_init(autoCreate);
}
#endif

CInput::~CInput() {
}

int CInput::maxLength() const {
   CInput_ *input = dynamic_cast<CInput_ *>(m_control);
   if (input)
      return input->maxLength();
   return 0;
}

void CInput::maxLength(int ml) {
   CInput_ *input = dynamic_cast<CInput_ *>(m_control);
   if (input)
      input->maxLength(ml);
}

int CInput::controlType() const {
   CInput_ *input = dynamic_cast<CInput_ *>(m_control);
   if (input)
      return input->type();
   return 0;
}

void CInput::controlType(int type) {
   CInput_ *input = dynamic_cast<CInput_ *>(m_control);
   if (input)
      input->type((unsigned char)(type));
}

Fl_Font CInput::textFont() const {
   return ((CInput_ *)m_control)->textfont();
}

void CInput::textFont(Fl_Font f) {
   ((CInput_ *)m_control)->textfont(f);
}

uchar CInput::textSize() const {
   return ((CInput_ *)m_control)->textsize();
}

void CInput::textSize(uchar s) {
   ((CInput_ *)m_control)->textsize(s);
}

void CInput::load(CQuery *loadQuery) {
   if (!m_fieldName.length()) return;
   CField& fld = (*loadQuery)[m_fieldName.c_str()];
   data( fld );
}

void CInput::save(CQuery *updateQuery) {
   if (!m_fieldName.length()) return;
   CParam& param = updateQuery->param(m_fieldName.c_str());
   param = data();
}

CVariant CInput::data() const {
   return ((CInput_ *)m_control)->value();
}

void CInput::data(const CVariant s) {
   std::string strValue = s.asString();
   ((CInput_ *)m_control)->value(strValue.c_str());
}

void CInput::preferredWidth(int& w) const {
   int ml = maxLength();
   if (ml > 0 && ml <= 80) {
      w = (int) (ml * fl_width('W')) + m_labelWidth + 6;
   } else {
      if (w < (int)m_labelWidth + 16) w = m_labelWidth + 16;
   }
}

void CInput::preferredHeight(int& h) const {
   int hh = textSize() + 8;
   if (hh < (int)labelHeight())
      hh = labelHeight();
   if (m_controlFlags & FGE_MULTILINEENTRY) {
      if (h < hh) h = hh;
   } else {
      h = hh;
   }
}

void CInput::preferredSize(int& w,int& h) {
   fl_font(textFont(),textSize());
   preferredWidth(w);
   preferredHeight(h);
}
//===========================================================================
void CMemoInput::ctor_init() {
   m_controlFlags = FGE_MULTILINEENTRY;
   begin();
   CEditor *e = new CEditor(50,SP_ALIGN_NONE);
   e->wrap_mode(true,0);
   m_control = e;
   m_control->callback(CControl::internalCallback);
   m_control->when(FL_WHEN_CHANGED);
   end();
}

CMemoInput::CMemoInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment,false) {
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CMemoInput::CMemoInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label,false) {
   ctor_init();
}
#endif

CVariant CMemoInput::data() const {
   return ((CEditor *)m_control)->textBuffer()->text();
}

void CMemoInput::data(const CVariant s) {
   std::string strValue = replaceAll(s.asString(),"\r","");
   ((CEditor *)m_control)->textBuffer()->text(strValue.c_str());
}

Fl_Font CMemoInput::textFont() const {
   return ((CEditor *)m_control)->textfont();
}

void CMemoInput::textFont(Fl_Font f) {
   ((CEditor *)m_control)->textfont(f);
}

uchar CMemoInput::textSize() const {
   return ((CEditor *)m_control)->textsize();
}

void CMemoInput::textSize(uchar s) {
   ((CEditor *)m_control)->textsize(s);
}

void CMemoInput::save(CQuery *updateQuery) {
   if (!m_fieldName.length()) return;
   CParam& param = updateQuery->param(m_fieldName.c_str());
   param.setText(data().getString());
}

void CMemoInput::preferredSize(int& w,int& h) {
   if (h < 30) h = 30;
   if (w < 30) w = 30;
}
//===========================================================================
void CBox::ctor_init(const char *label) {
   m_controlFlags = FGE_MULTILINEENTRY;
   begin();
   m_control = new Fl_Box(x(),y(),w(),h());
   end();
   m_control->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
   m_control->callback(CControl::internalCallback);
   m_control->when(FL_WHEN_CHANGED);
   labelWidth(0);
   if (label)
      data(label);
}

CBox::CBox(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput("",layoutSize,layoutAlignment,false) {
   ctor_init(label);
}

#ifdef __COMPATIBILITY_MODE__    
CBox::CBox(int x,int y,int w,int h,const char *label)
: CInput(x,y,w,h,"",false) {
   ctor_init(label);
}
#endif

CVariant CBox::data() const {
   return m_text.c_str();
}

void CBox::data(const CVariant v) {
   std::string text = v.asString();
   m_text.resize(text.length());
   if (text.length()) {
      unsigned i = 0;
      for (const char *c = text.c_str(); *c; c++) {
         if (*c == '\r') continue;
         m_text[i] = *c;
         i++;
      }
      m_text[i] = 0;

      m_text = replaceAll(m_text,"\x0B","  ");
   }
   m_control->label( m_text.c_str() );
}

Fl_Font CBox::textFont() const {
   return m_control->labelfont();
}

void CBox::textFont(Fl_Font f) {
   m_control->labelfont(f);
}

uchar CBox::textSize() const {
   return m_control->labelsize();
}

void CBox::textSize(uchar s) {
   m_control->labelsize(s);
}

int CBox::totalHeight(int ww) const {
   fl_font(m_textFont,m_textSize);
   if (ww < 1) ww = w();
   int cw = ww, ch = 0;
   fl_measure(m_text.c_str(), cw, ch);
   return ch;
}

void CBox::resize(int x,int y,int w,int h) {
   m_labelWidth = 0;
   Fl_Group::resize(x,y,w,h);
   m_control->resize(x,y,w,h);
}

void CBox::preferredSize(int& w,int& h) {
   int cw = 0, ch = 0;
   int dh = Fl::box_dh(box()) * 2;
   int dw = Fl::box_dw(box()) * 2;

   switch (m_layoutAlign) {
      case SP_ALIGN_TOP:
      case SP_ALIGN_BOTTOM:
         cw = w - dw;
         break;
      case SP_ALIGN_LEFT:
      case SP_ALIGN_RIGHT:
         ch = h - dh;
         break;
      default:
         break;
   }

   fl_font(m_textFont,m_textSize);
   fl_measure(m_text.c_str(), cw, ch);
   int hh = ch + dh;
   int ww = cw + dw;
   if (h < hh) h = hh;
   if (w < ww) w = ww;
}
//===========================================================================
void CHtmlBox::ctor_init(const char *label) {
   m_controlFlags = FGE_MULTILINEENTRY;
   m_control = new Fl_Help_View(x(),y(),w(),h(),"");
   add(m_control);
   m_control->box(FL_FLAT_BOX);
   m_control->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
   m_control->callback(CControl::internalCallback);
   m_control->when(FL_WHEN_CHANGED);
   labelWidth(0);
   if (label)
      data(label);
}

CHtmlBox::CHtmlBox(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput("",layoutSize,layoutAlignment,false) {
   ctor_init(label);
}

#ifdef __COMPATIBILITY_MODE__    
CHtmlBox::CHtmlBox(int x,int y,int w,int h,const char *label)
: CInput(x,y,w,h,"",false) {
   ctor_init(label);
}
#endif

CVariant CHtmlBox::data() const {
   return ((Fl_Help_View *)m_control)->value();
}

void CHtmlBox::data(const CVariant v) {
   ((Fl_Help_View *)m_control)->value(v.getString());
}

Fl_Font CHtmlBox::textFont() const {
   return Fl_Font(((Fl_Help_View *)m_control)->textfont());
}

void  CHtmlBox::textFont(Fl_Font f) {
   ((Fl_Help_View *)m_control)->textfont(f);
}

uchar CHtmlBox::textSize() const {
   return ((Fl_Help_View *)m_control)->textsize();
}

void  CHtmlBox::textSize(uchar s) {
   ((Fl_Help_View *)m_control)->textsize(s);
}

int CHtmlBox::totalHeight() const {
   return ((Fl_Help_View *)m_control)->size();
}

void CHtmlBox::preferredSize(int& w,int& h) {
   Fl_Help_View *hv = (Fl_Help_View *)m_control;
   hv->resize(hv->x(),hv->y(),w,h);
   int totalHeight = hv->size();

   int hh = totalHeight + 10;
   if (hh < (int)labelHeight())
      hh = labelHeight();
   if (h < hh) h = hh;
   if (w < (int)m_labelWidth + 16)
      w = m_labelWidth + 16;
}
//===========================================================================
void CDateIntervalInput::intervalCalendarButtonPressed(Fl_Widget *btn,void *data) {
   CDateIntervalInput *intervalInput = (CDateIntervalInput *)btn->parent()->parent();
   intervalInput->showCalendar(btn);
}

void CDateIntervalInput::ctor_init() {
   m_calendarWindow = new CPopupCalendar(0L);
   m_separator = "..";

   begin();

   m_firstDateInput = new CDateControl(0,0,10,10);
   m_firstDateInput->callback(CControl::internalCallback);
   m_firstDateInput->when(FL_WHEN_CHANGED);
   m_firstDateInput->button()->callback(intervalCalendarButtonPressed);

   m_control = m_firstDateInput->input();

   m_secondDateInput = new CDateControl(0,0,10,10,"to");
   m_secondDateInput->align(FL_ALIGN_LEFT);
   m_secondDateInput->callback(CControl::internalCallback);
   m_secondDateInput->when(FL_WHEN_CHANGED);
   m_secondDateInput->button()->callback(intervalCalendarButtonPressed);

   end();
}

CDateIntervalInput::CDateIntervalInput(const char *label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment,false) {
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CDateIntervalInput::CDateIntervalInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label,false) {
   ctor_init();
}
#endif

void CDateIntervalInput::showCalendar(Fl_Widget *btn) {
   CDateTime originalDate;
   CDateControl *dateControl;
   if (btn == m_firstDateInput->button()) {
      originalDate = beginOfInterval();
      dateControl = m_firstDateInput;
   } else {
      originalDate = endOfInterval();
      dateControl = m_secondDateInput;
   }
   m_calendarWindow->attachTo(dateControl);
   m_calendarWindow->date(originalDate);
   bool rc = m_calendarWindow->showModal();
   if (rc && m_calendarWindow->date() != originalDate) {
      if (btn == m_firstDateInput->button()) {
         beginOfInterval(m_calendarWindow->date());
      } else {
         endOfInterval(m_calendarWindow->date());
      }
      dateControl->do_callback();
   }
}

void CDateIntervalInput::separator(std::string s) {
   m_separator = s;
}

void CDateIntervalInput::resize(int x,int y,int w,int h) {
   int twidth = 16;
   fl_font(textFont(),textSize());
   int editorWidth = (int) fl_width("00/00/00000") + h;
   int maxWidth = 2 * editorWidth + twidth + m_labelWidth;

   if (w > maxWidth) w = maxWidth;

   CControl::resize(x,y,w,h);
   h = this->h();

   w -= m_labelWidth;
   x += m_labelWidth;
   if (m_menuButton)
      m_menuButton->resize(x,y,w,h);

   m_firstDateInput->resize(x,y,editorWidth,h);
   x += editorWidth + twidth;
   m_secondDateInput->resize(x,y,editorWidth,h);
}

CVariant CDateIntervalInput::data() const {
   CDateTime   dt1 = beginOfInterval();
   CDateTime   dt2 = endOfInterval();

   if ((double)dt1 == 0.0 && (double)dt2 == 0.0)
      return "";

   std::string result = m_firstDateInput->input()->value();
   result += m_separator;
   result += m_secondDateInput->input()->value();
   return result;
}

void CDateIntervalInput::data(const CVariant s) {
   std::string     firstStringData(s.asString().c_str());
   std::string     secondStringData;

   int pos = firstStringData.find(m_separator.c_str());
   int separatorLength = m_separator.length();

   if (!separatorLength)
      throw CException("Can't work with an empty dates separator");
   if (pos > 0) {
      firstStringData[pos] = 0;
      secondStringData = firstStringData.substr(pos+separatorLength,20);
   }
   m_firstDateInput->input()->value(firstStringData.c_str());
   m_secondDateInput->input()->value(secondStringData.c_str());
}

bool CDateIntervalInput::valid() const {
   CDateTime   dt1 = beginOfInterval();
   CDateTime   dt2 = endOfInterval();

   if ((double)dt1 != 0 && (double)dt2 != 0 && dt1 > dt2)
      return false;

   return true;
}

CDateTime CDateIntervalInput::beginOfInterval() const {
   return CDateTime(m_firstDateInput->input()->value());
}

void CDateIntervalInput::beginOfInterval(const CDateTime bi) {
   m_firstDateInput->input()->value(bi.dateString().c_str());
}

CDateTime CDateIntervalInput::endOfInterval() const {
   return CDateTime(m_secondDateInput->input()->value());
}

void CDateIntervalInput::endOfInterval(const CDateTime ei) {
   m_secondDateInput->input()->value(ei.dateString().c_str());
}
//===========================================================================
void CPhoneNumberInput::ctor_init() {
   ((CInput_ *)m_control)->mask("(999)-999-9999");
   maxLength(14);
}

CPhoneNumberInput::CPhoneNumberInput(const char *label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment,true) {
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CPhoneNumberInput::CPhoneNumberInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label) {
   ctor_init();
}
#endif

void CPhoneNumberInput::preferredWidth(int& w) const {
   w = (int) fl_width("(000)-000-00000") + labelWidth();
}

CVariant CPhoneNumberInput::data() const {
   return CInput::data();
}

void CPhoneNumberInput::data(const CVariant ss) {
   char *s1 = strdup(ss.asString().c_str());
   char *p1 = new char[15];

   int pos = 0;
   char *s2 = s1;
   char *p2 = p1;
   while (*s2 && pos < 14) {
      switch (pos) {
         case 0: *p2 = '('; p2++; break;
         case 4: *p2 = ')'; p2++; break;
         case 5:
         case 9: *p2 = '-'; p2++; break;
         default:
            while (*s2 && !isdigit(*s2)) s2++;
            if (isdigit(*s2)) {
               *p2 = *s2;
               p2++;
               s2++;
            }
      }
      pos++;
   }
   *p2 = 0;
   CInput::data(p1);
   free(p1);
   free(s1);
}

bool CPhoneNumberInput::valid() const {
   return true;
}
//===========================================================================
CIntegerInput::CIntegerInput(const char *label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment) {
   controlType(FL_INT_INPUT);
   m_minValue = m_maxValue = 0;
   maxLength(10);
}

#ifdef __COMPATIBILITY_MODE__    
CIntegerInput::CIntegerInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label) {
   controlType(FL_INT_INPUT);
   m_minValue = m_maxValue = 0;
}
#endif

void CIntegerInput::save(CQuery *updateQuery) {
   if (!m_fieldName.length()) return;
   CParam& param = updateQuery->param(m_fieldName.c_str());
   param.setInteger( data() );
}

bool CIntegerInput::valid() const {
   if (m_limited) {
      int val = data();
      return val >= m_minValue && val <= m_maxValue;
   }
   return true;
}

void CIntegerInput::setLimits(bool limited,int min,int max) {
   m_limited = limited;
   m_minValue = min;
   m_maxValue = max;
}
//===========================================================================
CFloatInput::CFloatInput(const char *label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment) {
   controlType(FL_FLOAT_INPUT);
   m_minValue = m_maxValue = 0;
   m_decimalPlaces = 4;
}

#ifdef __COMPATIBILITY_MODE__    
CFloatInput::CFloatInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label) {
   controlType(FL_FLOAT_INPUT);
   m_minValue = m_maxValue = 0;
   m_decimalPlaces = 4;
}
#endif

void CFloatInput::save(CQuery *updateQuery) {
   if (!m_fieldName.length()) return;
   CParam& param = updateQuery->param(m_fieldName.c_str());
   param.setFloat( data() );
}

bool CFloatInput::valid() const {
   if (m_limited) {
      double val = data();
      return val >= m_minValue && val <= m_maxValue;
   }
   return true;
}

void CFloatInput::setLimits(bool limited,double min,double max) {
   m_limited = limited;

   m_minValue = min;
   m_maxValue = max;
}
//===========================================================================
CPasswordInput::CPasswordInput(const char *label,int layoutSize,CLayoutAlign layoutAlignment)
: CInput(label,layoutSize,layoutAlignment) {
   controlType(FL_SECRET_INPUT);
}

#ifdef __COMPATIBILITY_MODE__    
CPasswordInput::CPasswordInput(int x,int y,int w,int h,const char * label)
: CInput(x,y,w,h,label) {
   controlType(FL_SECRET_INPUT);
}
#endif
//===========================================================================
void CDateTimeBaseInput::calendarButtonPressed(Fl_Widget *btn,void *data) {
   CDateTimeBaseInput *dateInput = (CDateTimeBaseInput *)btn->parent()->parent();
   if (!dateInput) return;
   CDateTime dt = dateInput->dateTimeValue();
   dateInput->showCalendar(btn);
   if (dt != dateInput->dateTimeValue())
      dateInput->m_dateInput->do_callback();
}

void CDateTimeBaseInput::ctor_init() {
   m_dateInput = 0L;
   m_timeInput = 0L;
   m_minValue = double(0);
   m_maxValue = double(0);
   m_calendarWindow = 0L;
   m_timeInput = (CInput_ *)m_control;
}

CDateTimeBaseInput::CDateTimeBaseInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment,bool autoCreate)
: CInput(label,layoutSize,layoutAlignment,autoCreate) {
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CDateTimeBaseInput::CDateTimeBaseInput(int x,int y,int w,int h,const char * label,bool autoCreate)
: CInput(x,y,w,h,label,autoCreate) {
   ctor_init();
}
#endif

void CDateTimeBaseInput::setLimits(bool limited,CDateTime min,CDateTime max) {
   m_limited = limited;
   m_minValue = min;
   m_maxValue = max;
}

void CDateTimeBaseInput::load(CQuery *loadQuery) {
   if (!m_fieldName.length())
      return; // no field name - no data loaded
   if (!m_fieldName.length()) return;
   CField& fld = (*loadQuery)[m_fieldName.c_str()];
   dateTimeValue(fld.asDateTime());
}

void CDateTimeBaseInput::save(CQuery *updateQuery) {
   if (!m_fieldName.length())
      return; // no field name - no data saved
   CParam& param = updateQuery->param(m_fieldName.c_str());
   CDateTime dt = dateTimeValue();
   param.setDateTime( dt );
}

bool CDateTimeBaseInput::valid() const {
   try {
      double val = dateTimeValue();
      if (m_limited)
         return val >= double(m_minValue) && val <= double(m_maxValue);
      return true;
   }
   catch (...) {
      return false;
   }
}

CDateTime CDateTimeBaseInput::dateTimeValue() const {
   return data();
}

void CDateTimeBaseInput::dateTimeValue(CDateTime dt) {
   if (kind() == DCV_DATE)
      data(dt.dateString());
   else  data(dt.timeString());
}

bool CDateTimeBaseInput::showCalendar(Fl_Widget *btn) {
   if (!m_calendarWindow) return false;
   m_calendarWindow->attachTo(btn->parent());
   m_calendarWindow->date(dateTimeValue());
   bool rc = m_calendarWindow->showModal();
   if (rc) {
      if (m_calendarWindow->date() != dateTimeValue()) {
         dateTimeValue(m_calendarWindow->date());
         do_callback();
      }
   }
   return rc;
}
//===========================================================================
void CDateInput::ctor_init() {
   begin();
   m_dateInput = new CDateControl(0,0,10,10);
   m_control = m_dateInput->input();
   m_dateInput->callback(CControl::internalCallback);
   m_dateInput->when(FL_WHEN_CHANGED);
   m_dateInput->button()->callback(calendarButtonPressed);
   m_calendarWindow = new CPopupCalendar(m_control);
   end();
   m_control->color(FL_LIGHT3);
}

CDateInput::CDateInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CDateTimeBaseInput(label,layoutSize,layoutAlignment,false)
{
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CDateInput::CDateInput(int x,int y,int w,int h,const char * label)
: CDateTimeBaseInput(x,y,w,h,label,false)
{
   ctor_init();
}
#endif

CDateInput::~CDateInput() {
   delete m_calendarWindow;
}

void CDateInput::resize(int xx,int yy,int ww,int hh) {
   CControl::resize(xx,yy,ww,hh);
/*
   hh = textSize() + 8;
   int bw = 0;
   int bh = 0;
   CThemes::sizeButton(THM_BUTTON_COMBO,bw,bh);
   bh += Fl::box_dy(m_dateInput->box());
   if (bh > hh) hh = bh;
*/
   m_dateInput->resize(xx+m_labelWidth,yy,ww-m_labelWidth,hh);
}

CVariant CDateInput::data() const {
   CDateTime   dt(m_dateInput->input()->value());
   CVariant    rc;
   rc.setDate(dt);
   return rc;
}

void CDateInput::data(const CVariant s) {
   CDateTime dt = s;
   m_dateInput->input()->value(dt.dateString().c_str());
}

void CDateInput::preferredHeight(int& h) const {
   CDateTimeBaseInput::preferredHeight(h);
   m_dateInput->preferredHeight(h);
}
//===========================================================================
CTimeInput::CTimeInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CDateTimeBaseInput(label,layoutSize,layoutAlignment,true)
{
   m_timeInput = (CInput_ *)m_control;
   m_timeInput->mask(CDateTime::shortTimeFormat);
}

#ifdef __COMPATIBILITY_MODE__
CTimeInput::CTimeInput(int x,int y,int w,int h,const char * label)
: CDateTimeBaseInput(x,y,w,h,label,true)
{
   m_timeInput = (CInput_ *)m_control;
   m_timeInput->mask(CDateTime::shortTimeFormat);
}
#endif

void CTimeInput::preferredWidth(int& w) const {
   w = int(7 * fl_width('W')) + m_labelWidth + 6;
}
//===========================================================================
void CDateTimeInput::ctor_init() {
   begin();
   m_dateInput = new CDateControl(0,0,10,10);
   m_dateInput->callback(CControl::internalCallback);
   m_dateInput->when(FL_WHEN_CHANGED);
   m_dateInput->button()->callback(calendarButtonPressed);
   m_dateInput->color(FL_LIGHT3);

   m_control = m_dateInput->input();

   m_timeInput = new CInput_("");
   m_timeInput->callback(CControl::internalCallback);
   m_timeInput->when(FL_WHEN_CHANGED);
   m_timeInput->color(FL_LIGHT3);

   m_calendarWindow = new CPopupCalendar(m_dateInput);
   end();
}

CDateTimeInput::CDateTimeInput(const char * label,int layoutSize,CLayoutAlign layoutAlignment)
: CDateTimeBaseInput(label,layoutSize,layoutAlignment,false)
{
   ctor_init();
}

#ifdef __COMPATIBILITY_MODE__    
CDateTimeInput::CDateTimeInput(int x,int y,int w,int h,const char * label)
: CDateTimeBaseInput(x,y,w,h,label,false) 
{
   ctor_init();
}
#endif

void CDateTimeInput::resize(int x,int y,int w,int h) 
{
   CControl::resize(x,y,w,h);
   w -= m_labelWidth;
   x += m_labelWidth;

   int hh = textSize() + 6;

   if (m_menuButton)
      m_menuButton->resize(x,y,w,hh);

   m_dateInput->resize(x,y,w/2-2,hh);
   x += m_dateInput->w() + 4;

   fl_font(textFont(),textSize());
   int timeWidth = (int) fl_width("00:00AM") + 6;
   m_timeInput->resize(x,y,timeWidth,m_dateInput->h());
}

void CDateTimeInput::dateTimeValue(CDateTime dt) 
{
   m_dateInput->input()->value(dt.dateString().c_str());
   m_timeInput->value(dt.timeString().c_str());
}

CDateTime CDateTimeInput::dateTimeValue() const {
   CDateTime   dt = CDateTime( m_dateInput->input()->value() );
   CDateTime   tm = CDateTime( m_timeInput->value() );
   return dt + tm;
}

CVariant CDateTimeInput::data() const {
   return std::string(m_dateInput->input()->value()) + " " + std::string(m_timeInput->value());
}

void CDateTimeInput::data(const CVariant s) {
   dateTimeValue(s.asDateTime());
}
//===========================================================================
CControl *createControl(int controlKind,std::string label,std::string fieldName,int size) {
   CControl   *control = NULL;

   const char *lbl = label.c_str();

   switch (controlKind) {
      case DCV_BOX:
         control = new CBox(lbl,size);
         break;
      case DCV_HTMLBOX:
         control = new CHtmlBox(lbl,size);
         break;
      case DCV_RADIOBUTTONS:
         control = new CRadioButtons(lbl,size);
         break;
      case DCV_STRING:
         control = new CInput(lbl,size);
         break;
      case DCV_MEMO:
         control = new CMemoInput(lbl,size);
         break;
      case DCV_INTEGER:
         control = new CIntegerInput(lbl);
         break;
      case DCV_FLOAT:
         control = new CFloatInput(lbl);
         break;
      case DCV_DATETIME:      
         control = new CDateTimeInput(lbl);
         break;
      case DCV_DATEINTERVAL:
         control = new CDateIntervalInput(lbl);
         break;
      case DCV_DATE:
         control = new CDateInput(lbl);
         break;
      case DCV_TIME:
         control = new CTimeInput(lbl);
         break;
      case DCV_PHONE:
         control = new CPhoneNumberInput(lbl);
         break;
      case DCV_LISTBOX:
         control = new CListBox(lbl,size);
         break;
      case DCV_CHECKBUTTONS:
         control = new CCheckButtons(lbl);
         break;
      case DCV_COMBO:
         control = new CComboBox(lbl,size);
         break;
      case DCV_INTVALUECOMBO:
         control = new CComboBox(lbl,size);
         break;
      default:
         throw CException("Unsupported control type, field name '"+fieldName+"'.");
   }

   control->fieldName(fieldName.c_str());

   return control;
}
