/***************************************************************************
                          kmymoneydateinput.cpp
                             -------------------
    copyright            : (C) 2000 by Michael Edwardes
    email                : mte@users.sourceforge.net
                           ipwizard@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// ----------------------------------------------------------------------------
// QT Includes

#include <tqpainter.h>
#include <tqdrawutil.h>
#include <tqpoint.h>
#include <tqvalidator.h>
#include <tqtimer.h>
#include <tqstyle.h>
#include <tqlayout.h>
#include <tqapplication.h>
#include <tqdesktopwidget.h>
#include <tqpixmap.h>
#include <tqtimer.h>
#include <tqlabel.h>

// ----------------------------------------------------------------------------
// KDE Includes
#include <tdeversion.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <kpushbutton.h>
#include <tdeshortcut.h>
#include <kpassivepopup.h>

// ----------------------------------------------------------------------------
// Project Includes

#include "kmymoneydateinput.h"

namespace {
  const int DATE_POPUP_TIMEOUT = 1500;
}

bool KMyMoneyDateEdit::event(TQEvent* e)
{
  // make sure that we keep the current date setting of a kMyMoneyDateInput object
  // across the TQDateEdit::event(FocusOutEvent)
  bool rc;

  kMyMoneyDateInput* p = dynamic_cast<kMyMoneyDateInput*>(parentWidget());
  if(e->type() == TQEvent::FocusOut && p) {
    TQDate d = p->date();
    rc = TQDateEdit::event(e);
    if(d.isValid())
      d = p->date();
    p->loadDate(d);
  } else {
    rc = TQDateEdit::event(e);
  }
  return rc;
}

kMyMoneyDateInput::kMyMoneyDateInput(TQWidget *parent, const char *name, TQt::AlignmentFlags flags)
 : TQHBox(parent,name)
{
  m_qtalignment = flags;
  m_date = TQDate::currentDate();

  dateEdit = new KMyMoneyDateEdit(m_date, this, "dateEdit");
  setFocusProxy(dateEdit);
  focusWidget()->installEventFilter(this); // To get dateEdit's FocusIn/Out and some KeyPress events
  dateEdit->installEventFilter(this); // To get dateEdit's FocusIn/Out and some KeyPress events

  m_datePopup = new KPassivePopup(dateEdit, "datePopup");
  m_datePopup->setTimeout(DATE_POPUP_TIMEOUT);
  m_datePopup->setView(new TQLabel(TDEGlobal::locale()->formatDate(m_date), m_datePopup, "datePopupLabel"));

  m_dateFrame = new TQVBox(this, 0, WType_Popup);
  m_dateFrame->setFrameStyle(TQFrame::PopupPanel | TQFrame::Raised);
  m_dateFrame->setLineWidth(3);
  m_dateFrame->hide();

  TQString dateFormat = TDEGlobal::locale()->dateFormatShort().lower();
  TQString order, separator;
  for(unsigned i = 0; i < dateFormat.length(); ++i) {
    // DD.MM.YYYY is %d.%m.%y
    // dD.mM.YYYY is %e.%n.%y
    // SHORTWEEKDAY, dD SHORTMONTH YYYY is %a, %e %b %Y
    if(dateFormat[i] == 'y' || dateFormat[i] == 'm' || dateFormat[i] == 'n' || dateFormat[i] == 'd' || dateFormat[i] == 'e') {
      if(dateFormat[i] == 'n')
        dateFormat[i] = 'm';
      if(dateFormat[i] == 'e')
        dateFormat[i] = 'd';
      order += dateFormat[i];
    } else if(dateFormat[i] != '%' && separator.isEmpty())
      separator = dateFormat[i];
    if(order.length() == 3)
      break;
  }

  // see if we find a known format. If it's unknown, then we use YMD (international)
  // set m_focusDatePart to the day position (0-2)
  if(order == "mdy") {
    dateEdit->setOrder(TQDateEdit::MDY);
    m_focusDatePart = 1;
  } else if(order == "dmy") {
    dateEdit->setOrder(TQDateEdit::DMY);
    m_focusDatePart = 0;
  } else if(order == "ydm") {
    dateEdit->setOrder(TQDateEdit::YDM);
    m_focusDatePart = 1;
  } else {
    dateEdit->setOrder(TQDateEdit::YMD);
    m_focusDatePart = 2;
    separator = '-';
  }
  dateEdit->setSeparator(separator);

  m_datePicker = new KDatePicker(m_dateFrame, m_date);
#if KDE_IS_VERSION(3,1,0)
  // Let the date picker have a close button (Added in 3.1)
  m_datePicker->setCloseButton(true);
#endif

  // the next line is a try to add an icon to the button
  m_dateButton = new KPushButton(TQIconSet(TQPixmap(TDEGlobal::iconLoader()->iconPath("date", -TDEIcon::SizeSmall))), TQString(""), this);
  m_dateButton->setMinimumWidth(30);

  connect(m_dateButton,TQT_SIGNAL(clicked()),TQT_SLOT(toggleDatePicker()));
  connect(dateEdit, TQT_SIGNAL(valueChanged(const TQDate&)), this, TQT_SLOT(slotDateChosenRef(const TQDate&)));
  connect(m_datePicker, TQT_SIGNAL(dateSelected(TQDate)), this, TQT_SLOT(slotDateChosen(TQDate)));
  connect(m_datePicker, TQT_SIGNAL(dateEntered(TQDate)), this, TQT_SLOT(slotDateChosen(TQDate)));
  connect(m_datePicker, TQT_SIGNAL(dateSelected(TQDate)), m_dateFrame, TQT_SLOT(hide()));
}

void kMyMoneyDateInput::markAsBadDate(bool bad, const TQColor& color)
{
  if(dateEdit->focusProxy()) {
    TQT_TQWIDGET(dateEdit->focusProxy())->setPaletteForegroundColor(paletteForegroundColor());
    if(bad)
      TQT_TQWIDGET(dateEdit->focusProxy())->setPaletteForegroundColor(color);
  }
}

void kMyMoneyDateInput::show(void)
{
  // don't forget the standard behaviour  ;-)
  TQHBox::show();

  // If the widget is shown, the size must be fixed a little later
  // to be appropriate. I saw this in some other places and the only
  // way to solve this problem is to postpone the setup of the size
  // to the time when the widget is on the screen.
  TQTimer::singleShot(50, this, TQT_SLOT(fixSize()));
}

void kMyMoneyDateInput::fixSize(void)
{
  // According to a hint in the documentation of KDatePicker::sizeHint()
  // 28 pixels should be added in each direction to obtain a better
  // display of the month button. I decided, (22,14) is good
  // enough and save some space on the screen (ipwizard)
  m_dateFrame->setFixedSize(m_datePicker->sizeHint() + TQSize(22, 14));

  dateEdit->setMinimumWidth(dateEdit->minimumSizeHint().width() + 6);
}

kMyMoneyDateInput::~kMyMoneyDateInput()
{
  delete m_dateFrame;
  delete m_datePopup;
}

void kMyMoneyDateInput::toggleDatePicker()
{
  int w = m_dateFrame->width();
  int h = m_dateFrame->height();

  if(m_dateFrame->isVisible())
  {
    m_dateFrame->hide();
  }
  else
  {
    TQPoint tmpPoint = mapToGlobal(m_dateButton->geometry().bottomRight());

    // usually, the datepicker widget is shown underneath the dateEdit widget
    // if it does not fit on the screen, we show it above this widget

    if(tmpPoint.y() + h > TQApplication::desktop()->height()) {
      tmpPoint.setY(tmpPoint.y() - h - m_dateButton->height());
    }

    if((m_qtalignment == TQt::AlignRight && tmpPoint.x()+w <= TQApplication::desktop()->width())
    || (tmpPoint.x()-w < 0)  )
    {
      m_dateFrame->setGeometry(tmpPoint.x()-width(), tmpPoint.y(), w, h);
    }
    else
    {
      tmpPoint.setX(tmpPoint.x() - w);
      m_dateFrame->setGeometry(tmpPoint.x(), tmpPoint.y(), w, h);
    }

    if(m_date.isValid())
    {
      m_datePicker->setDate(m_date);
    }
    else
    {
      m_datePicker->setDate(TQDate::currentDate());
    }
    m_dateFrame->show();
  }
}


void kMyMoneyDateInput::resizeEvent(TQResizeEvent* ev)
{
  m_dateButton->setMaximumHeight(ev->size().height());
  m_dateButton->setMaximumWidth(ev->size().height());
  dateEdit->setMaximumHeight(ev->size().height());

  // tqDebug("Received resize-event %d,%d", ev->size().width(), ev->size().height());
}


/** Overriding TQWidget::keyPressEvent
  *
  * increments/decrements the date upon +/- or Up/Down key input
  * sets the date to current date when the 'T' key is pressed
  */
void kMyMoneyDateInput::keyPressEvent(TQKeyEvent * k)
{
  TDEShortcut today(i18n("Enter todays date into date input widget", "T"));

  switch(k->key()) {
    case Key_Equal:
    case Key_Plus:
      slotDateChosen(m_date.addDays(1));
      break;

    case Key_Minus:
      slotDateChosen(m_date.addDays(-1));
      break;

    default:
      if(today.contains(KKey(k)) || k->key() == Key_T) {
        slotDateChosen(TQDate::currentDate());
      }
      break;
  }
}

/**
  * This function receives all events that are sent to focusWidget().
  * Some KeyPress events are intercepted and passed to keyPressEvent.
  * Otherwise they would be consumed by TQDateEdit.
  */
bool kMyMoneyDateInput::eventFilter(TQObject *, TQEvent *e)
{
  if (e->type() == TQEvent::FocusIn) {
    m_datePopup->show();
    // The cast to the base class is needed since setFocusSection
    // is protected in TQDateEdit. This causes some logic in
    // TQDateEdit::setFocusSection not to be executed but this does
    // not hurt here, because the widget just receives focus.
    static_cast<TQDateTimeEditBase *>(dateEdit)->setFocusSection(m_focusDatePart);
  }
  else if (e->type() == TQEvent::FocusOut)
    m_datePopup->hide();
  else if (e->type() == TQEvent::KeyPress) {
    if (TQKeyEvent *k = dynamic_cast<TQKeyEvent*>(e)) {
      if (k->key() == Key_Minus) {
        keyPressEvent(k);
        return true;
      }
    }
  }

  return false; // Don't filter the event
}

void kMyMoneyDateInput::slotDateChosenRef(const TQDate& date)
{
  if(date.isValid()) {
    emit dateChanged(date);
    m_date = date;

    TQLabel *lbl = static_cast<TQLabel*>(m_datePopup->view());
    lbl->setText(TDEGlobal::locale()->formatDate(date));
    lbl->adjustSize();
    if(m_datePopup->isVisible() || hasFocus())
      m_datePopup->show(); // Repaint
  }
}

void kMyMoneyDateInput::slotDateChosen(TQDate date)
{
  if(date.isValid()) {
    // the next line implies a call to slotDateChosenRef() above
    dateEdit->setDate(date);
  }
}

TQDate kMyMoneyDateInput::date(void) const
{
  return dateEdit->date();
}

void kMyMoneyDateInput::setDate(TQDate date)
{
  slotDateChosen(date);
}

void kMyMoneyDateInput::loadDate(const TQDate& date)
{
  m_date = m_prevDate = date;

  blockSignals(true);
  dateEdit->setDate(date);
  m_date = date;
  blockSignals(false);
}

void kMyMoneyDateInput::resetDate(void)
{
  setDate(m_prevDate);
}

TQWidget* kMyMoneyDateInput::focusWidget(void) const
{
  TQWidget* w = dateEdit;
  while(w->focusProxy())
    w = TQT_TQWIDGET(w->focusProxy());
  return w;
}

#include "kmymoneydateinput.moc"
