-rw-r--r-- | libkdepim/kdateedit.cpp | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/libkdepim/kdateedit.cpp b/libkdepim/kdateedit.cpp new file mode 100644 index 0000000..60bd2cf --- a/dev/null +++ b/libkdepim/kdateedit.cpp @@ -0,0 +1,486 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> + + 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. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qapplication.h> +#include <qevent.h> +#include <qlineedit.h> +#include <qpixmap.h> +#include <qpushbutton.h> + +#include <kdatepicker.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <qpalette.h> + +#include "kdateedit.h" +#include "kdateedit.moc" + +KDateEdit::KDateEdit(QWidget *parent, const char *name, bool withoutDP ) + : QHBox(parent, name) +{ + dateFormShort = true; + withoutDp = withoutDP; + mDateEdit = new QLineEdit(this); + mDateEdit->setText(KGlobal::locale()->formatDate(QDate::currentDate(),dateFormShort)); + setFocusProxy(mDateEdit); + mDateEdit->installEventFilter(this); + + // Highlight Background and Textcolor + QPalette palette = QWidget::palette(); + unsigned char red, green, blue; + red = palette.color( QPalette::Normal , QColorGroup::Background ).red() - 10; + green = palette.color( QPalette::Normal , QColorGroup::Background ).green() - 10; + blue = palette.color( QPalette::Normal , QColorGroup::Background ).blue() - 10; + palette.setColor( QColorGroup::Highlight, QColor(red,green,blue) ); + palette.setColor( QColorGroup::HighlightedText, palette.color( QPalette::Normal , QColorGroup::Foreground ) ); + mDateEdit->setPalette( palette ); + + if ( withoutDP ) { + mDateFrame = 0; + mDateButton = 0; + mDatePicker = 0; + } else { + QPixmap pixmap = SmallIcon("smallcal"); + mDateButton = new QPushButton(this); + mDateButton->setPixmap(pixmap); + + mDateFrame = new QVBox(0,0,WType_Popup); + // mDateFrame->setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + mDateFrame->setFrameStyle( QFrame::WinPanel |QFrame::Raised ); + mDateFrame->setLineWidth(3); + mDateFrame->hide(); + + mDatePicker = new KDatePicker(mDateFrame,QDate::currentDate()); + connect(mDatePicker,SIGNAL(dateEntered(QDate)),SLOT(setDate(QDate))); + connect(mDatePicker,SIGNAL(dateEntered(QDate)),SIGNAL(dateChanged(QDate))); + connect(mDatePicker,SIGNAL(dateSelected(QDate)),SLOT(setDate(QDate))); + connect(mDatePicker,SIGNAL(dateSelected(QDate)),SIGNAL(dateChanged(QDate))); + connect(mDatePicker,SIGNAL(dateSelected(QDate)),mDateFrame,SLOT(hide())); + connect(mDateButton,SIGNAL(clicked()),SLOT(toggleDatePicker())); + + //mDateFrame->resize( 400, 300 ); + + } + connect(mDateEdit,SIGNAL(returnPressed()),SLOT(lineEnterPressed())); + connect(mDateEdit,SIGNAL(textChanged(const QString &)), + SLOT(textChanged(const QString &))); + + // Create the keyword list. This will be used to match against when the user + // enters information. + mKeywordMap[i18n("tomorrow")] = 1; + mKeywordMap[i18n("today")] = 0; + mKeywordMap[i18n("yesterday")] = -1; + + /* + * This loop uses some math tricks to figure out the offset in days + * to the next date the given day of the week occurs. There + * are two cases, that the new day is >= the current day, which means + * the new day has not occured yet or that the new day < the current day, + * which means the new day is already passed (so we need to find the + * day in the next week). + */ + QString dayName; + int currentDay = QDate::currentDate().dayOfWeek(); + for (int i = 1; i <= 7; ++i) + { + dayName = KGlobal::locale()->weekDayName(i).lower(); + if (i >= currentDay) + mKeywordMap[dayName] = i - currentDay; + else + mKeywordMap[dayName] = 7 - currentDay + i; + } + + mTextChanged = false; + mHandleInvalid = false; + QWidget::setTabOrder( mDateEdit, mDateButton ); +} + +KDateEdit::~KDateEdit() +{ + delete mDateFrame; +} + +void KDateEdit::setDate(QDate newDate) +{ + if (!newDate.isValid() && !mHandleInvalid) + return; + if ( readDate() == newDate ) + return; + QString dateString = ""; + if(newDate.isValid()) + dateString = KGlobal::locale()->formatDate( newDate, dateFormShort ); + + mTextChanged = false; + + // We do not want to generate a signal here, since we explicity setting + // the date + bool b = mDateEdit->signalsBlocked(); + mDateEdit->blockSignals(true); + mDateEdit->setText(dateString); + mDateEdit->blockSignals(b); +} + +void KDateEdit::setDate( QDate date,int *cpos,const int key ,const bool dateFormShort) +{ + QString dateForm = dateFormShort ? + KGlobal::locale()->dateFormatShort() : + KGlobal::locale()->dateFormat(); + + int begin = dateForm.find("%"); + int space = 0; + int allStrLength = 0; + int strLength = 0; + int repeat = 0; + + // witch? Day, Month or Year switch? + while(1){ + switch ( dateForm.at(begin + 1).latin1() ) + { + case 'd':// 16 (month day) + strLength = 2; //Ok + break; + case 'm':// 01 (month) + strLength = 2; //Ok + break; + case 'a':// Mon (Weekday) + strLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), true).length(); + break; + case 'A':// Monday (Weekday) + strLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), false).length(); + break; + case 'b':// Jan (monthName) + strLength = KGlobal::locale()->monthName(date.month(), true).length(); + break; + case 'B':// January (monthName) + strLength = KGlobal::locale()->monthName(date.month(), false).length(); + break; + case 'y':// 04 (year short) + strLength = 2; //Ok + break; + case 'Y':// 2004 (year) + strLength = 4; //Ok + break; + default: + break; + } + space = begin - (repeat++ * 2); + // all select? then dayswitch + if( (mDateEdit->text().length() == mDateEdit->markedText().length() ) && + ( (dateForm.at(begin + 1).latin1() == 'd') || + (dateForm.at(begin + 1).latin1() == 'a') || + (dateForm.at(begin + 1).latin1() == 'A') ) ) { + break; + } + // mDateEdit-StringPos == CursorPosition(cpos) then break and set date + if( ( (space + allStrLength) <= *cpos && *cpos <= (space + allStrLength + strLength) ) || *cpos < begin ) { + break; + } + allStrLength += strLength; + begin = dateForm.find("%", begin +1); + } + + // set date + switch ( dateForm.at(begin + 1).latin1() ) { + case 'd': + case 'a': + case 'A': + if(key == Key_Up) { + setDate( date.addDays( 1 ) ); + } + else if(key == Key_Down) { + setDate( date.addDays( -1 ) ); + } + maxDay = readDate().day(); + break; + case 'm': + case 'b': + case 'B': + if(key == Key_Up) { + int year = ((date.month()+1)>12)?date.year()+1:date.year(); + int month = ((date.month()+1)>12)?1:date.month()+1; + int day = (QDate(year,month,1).daysInMonth()<maxDay)?QDate(year,month,1).daysInMonth():maxDay; + setDate( QDate( year, month, day ) ); + } else if(key == Key_Down) { + int year = ((date.month()-1)<1)?date.year()-1:date.year(); + int month = ((date.month()-1)<1)?12:date.month()-1; + int day = (QDate(year,month,1).daysInMonth()<maxDay)?QDate(year,month,1).daysInMonth():maxDay; + setDate( QDate( year, month, day ) ); + } + break; + case 'y': + case 'Y': + if(key == Key_Up) { + setDate( QDate( date.year() + 1, date.month() , date.day()) ); + } + else if(key == Key_Down) { + setDate( QDate( date.year() - 1, date.month() , date.day()) ); + } + break; +/* default: + if(key == Key_Up) { + setDate( date.addDays( 1 ) ); + } else if(key == Key_Down) { + setDate( date.addDays( -1 ) ); + } + break;*/ + } + + date = readDate(); + begin = dateForm.find("%"); + int allSelectStrLength = 0; + int selectStrLength = 0; + + // set selection do new date an set cursor at end of selection + for(int i = 0; i < repeat; i++){ + switch ( dateForm.at(begin + 1).latin1() ) + { + case 'd':// 16 (month day) + selectStrLength = 2; //Ok + break; + case 'm':// 01 (month) + selectStrLength = 2; //Ok + break; + case 'a':// Mon (Weekday short) + selectStrLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), true).length(); + break; + case 'A':// Monday (Weekday) + selectStrLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), false).length(); + break; + case 'b':// Jan (monthName short) + selectStrLength = KGlobal::locale()->monthName(date.month(), true).length(); + break; + case 'B':// January (monthName) + selectStrLength = KGlobal::locale()->monthName(date.month(), false).length(); + break; + case 'y':// 04 (year short) + selectStrLength = 2; //Ok + break; + case 'Y':// 2004 (year) + selectStrLength = 4; //Ok + break; + default: + break; + } + space = begin - (i * 2); + allSelectStrLength += selectStrLength; + begin = dateForm.find("%", begin +1); + } + // set selection from begin of date + setSelect( space + allSelectStrLength - selectStrLength , selectStrLength); + *cpos = space + allSelectStrLength; + emit(dateChanged(date)); + + return; +} + +void KDateEdit::setHandleInvalid(bool handleInvalid) +{ + mHandleInvalid = handleInvalid; +} + +void KDateEdit::setEnabled(bool on) +{ + mDateEdit->setEnabled(on); + mDateButton->setEnabled(on); +} + +QDate KDateEdit::date() const +{ + QDate date = readDate(); + + if (date.isValid() || mHandleInvalid) { + return date; + } else { + KNotifyClient::beep(); + return QDate::currentDate(); + } +} + +void KDateEdit::keyPressEvent(QKeyEvent *e) +{ + QDate date = readDate(); + int cpos = mDateEdit->cursorPosition(); + + switch(e->key()) + { + case Key_Escape: + mDateEdit->deselect(); + case Key_Tab: + QHBox::keyPressEvent(e); + break; + case Key_Up: + // when date invalid then set to currend and return + if(!date.isValid()) { + date = QDate::currentDate(); + setDate(date); + mDateEdit->setCursorPosition(cpos); + emit(dateChanged(date)); + QString text = i18n( "You entered an invalid date!\n Date changed to current date." ); + KMessageBox::information( 0, text ); + return; + } + setDate(date, &cpos, Key_Up, dateFormShort); + break; + case Key_Down: + // when date invalid then set to current and return + if(!date.isValid()) { + date = QDate::currentDate(); + setDate(date); + mDateEdit->setCursorPosition(cpos); + emit(dateChanged(date)); + QString text = i18n( "You entered an invalid date!\n Date changed to current date." ); + KMessageBox::information( 0, text ); + return; + } + setDate(date, &cpos, Key_Down, dateFormShort); + break; + default: + QHBox::keyPressEvent(e); + break; + } // switch + mDateEdit->setCursorPosition(cpos); +} + +void KDateEdit::setSelect( int from, int to ) +{ +// return; + mDateEdit->setSelection( from , to ); +} + +void KDateEdit::toggleDatePicker() +{ + if( mDateFrame->isVisible() ) { + mDateFrame->hide(); + } else { + QPoint tmpPoint = mapToGlobal(mDateButton->geometry().bottomRight()); + QSize datepickersize = mDatePicker->sizeHint(); + + if ( tmpPoint.x() < 7+datepickersize.width() ) tmpPoint.setX( 7+datepickersize.width() ); + + int h = QApplication::desktop()->height(); + + if ( tmpPoint.y() + datepickersize.height() > h ) tmpPoint.setY( h - datepickersize.height() ); + + mDateFrame->setGeometry(tmpPoint.x()-datepickersize.width()-7, tmpPoint.y(), + datepickersize.width()+2*mDateFrame->lineWidth(), datepickersize.height()+2*mDateFrame->lineWidth()); + + QDate date = readDate(); + if(date.isValid()) { + mDatePicker->setDate(date); + } else { + mDatePicker->setDate(QDate::currentDate()); + } + mDateFrame->show(); + } +} + + +void KDateEdit::lineEnterPressed() +{ + QDate date = readDate(); + + if(date.isValid()) + { + // Update the edit. This is needed if the user has entered a + // word rather than the actual date. + setDate(date); + emit(dateChanged(date)); + emit returnPressed(); + } + else + { + if ( withoutDp ) { + KNotifyClient::beep(); + } else { + if ( !mDateEdit->text().isEmpty() ) { + mTextChanged = false; + QString text = i18n( "You entered an invalid date!\n Will use current date instead." ); + if ( KMessageBox::warningContinueCancel( 0, text ) == KMessageBox::Continue ) { + setDate( QDate::currentDate() ); + emit dateChanged( QDate::currentDate() ); + } + } + } + } +} + +bool KDateEdit::inputIsValid() +{ + return readDate().isValid(); +} + +QDate KDateEdit::readDate() const +{ + QString text = mDateEdit->text(); + QDate date; + + if (mKeywordMap.contains(text.lower())) + { + date = QDate::currentDate().addDays(mKeywordMap[text.lower()]); + } + else + { + date = KGlobal::locale()->readDate(text); + } + + return date; +} + +bool KDateEdit::eventFilter(QObject *, QEvent *e) +{ + // We only process the focus out event if the text has changed + // since we got focus + if ((e->type() == QEvent::FocusOut) && mTextChanged) + { + lineEnterPressed(); + mTextChanged = false; + } + // switch dateFormShort by double klick with mouse + else if (e->type() == QEvent::MouseButtonDblClick) + { + dateFormShort = dateFormShort?false:true; + mDateEdit->setText(KGlobal::locale()->formatDate(readDate(),dateFormShort)); + } + else if (e->type() == QEvent::FocusIn) + { + maxDay = readDate().day(); + } + + return false; +} + +void KDateEdit::textChanged(const QString &) +{ + if(mHandleInvalid && mDateEdit->text().stripWhiteSpace().isEmpty()) { + QDate date; //invalid date + emit(dateChanged(date)); + } else { + mTextChanged = true; + } + maxDay = readDate().day(); +} |