diff options
Diffstat (limited to 'src/widgets/kdateedit.cpp')
-rw-r--r-- | src/widgets/kdateedit.cpp | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/src/widgets/kdateedit.cpp b/src/widgets/kdateedit.cpp new file mode 100644 index 0000000..8becc83 --- /dev/null +++ b/src/widgets/kdateedit.cpp @@ -0,0 +1,388 @@ +/* + This file is part of libtdepim. + + Copyright (c) 2002 Cornelius Schumacher <[email protected]> + Copyright (c) 2003-2004 Reinhold Kainhofer <[email protected]> + Copyright (c) 2004 Tobias Koenig <[email protected]> + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#include <tqapplication.h> +#include <tqlineedit.h> +#include <tqlistbox.h> +#include <tqvalidator.h> + +//#include <kcalendarsystem.h> +#include <tdeglobal.h> +#include <tdeglobalsettings.h> +#include <tdelocale.h> +#include <tdeconfig.h> + +#include "kdateedit.h" + +TQRect desktopGeometry(TQWidget* w) +{ + TQDesktopWidget *dw = TQApplication::desktop(); + if (dw->isVirtualDesktop()) { + TDEConfigGroup group(TDEGlobal::config(), "Windows"); + if (group.readBoolEntry("XineramaEnabled", true) && + group.readBoolEntry("XineramaPlacementEnabled", true)) { + if (w) + return dw->screenGeometry(dw->screenNumber(w)); + else return dw->screenGeometry(-1); + } else { + return dw->geometry(); + } + } else { + return dw->geometry(); + } +} + +class DateValidator : public TQValidator +{ + public: + DateValidator( const TQStringList &keywords, TQWidget* parent, const char* name = 0 ) + : TQValidator( parent, name ), mKeywords( keywords ) + {} + + virtual State validate( TQString &str, int& ) const + { + int length = str.length(); + + // empty string is intermediate so one can clear the edit line and start from scratch + if ( length <= 0 ) + return Intermediate; + + if ( mKeywords.contains( str.lower() ) ) + return Acceptable; + + bool ok = false; + TDEGlobal::locale()->readDate( str, &ok ); + if ( ok ) + return Acceptable; + else + return Intermediate; + } + + private: + TQStringList mKeywords; +}; + +KDateEdit::KDateEdit( TQWidget *parent, const char *name ) + : TQComboBox( true, parent, name ), + mReadOnly( false ), + mDiscardNextMousePress( false ) +{ + // need at least one entry for popup to work + setMaxCount( 1 ); + + mDate = TQDate::currentDate(); + TQString today = TDEGlobal::locale()->formatDate( mDate, true ); + + insertItem( today ); + setCurrentItem( 0 ); + changeItem( today, 0 ); + setMinimumSize( sizeHint() ); + + connect( lineEdit(), TQ_SIGNAL( returnPressed() ), + this, TQ_SLOT( lineEnterPressed() ) ); + connect( this, TQ_SIGNAL( textChanged( const TQString& ) ), + TQ_SLOT( slotTextChanged( const TQString& ) ) ); + + mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words ); + mPopup->hide(); + mPopup->installEventFilter( this ); + + connect( mPopup, TQ_SIGNAL( dateChanged( TQDate ) ), + TQ_SLOT( dateSelected( TQDate ) ) ); + + // handle keyword entry + setupKeywords(); + lineEdit()->installEventFilter( this ); + + setValidator( new DateValidator( mKeywordMap.keys(), this ) ); + + mTextChanged = false; +} + +KDateEdit::~KDateEdit() +{ + delete mPopup; + mPopup = 0; +} + +void KDateEdit::setDate( const TQDate& date ) +{ + assignDate( date ); + updateView(); +} + +TQDate KDateEdit::date() const +{ + return mDate; +} + +void KDateEdit::setReadOnly( bool readOnly ) +{ + mReadOnly = readOnly; + lineEdit()->setReadOnly( readOnly ); +} + +bool KDateEdit::isReadOnly() const +{ + return mReadOnly; +} + +void KDateEdit::popup() +{ + if ( mReadOnly ) + return; + + TQRect desk = desktopGeometry( this ); + + TQPoint popupPoint = mapToGlobal( TQPoint( 0,0 ) ); + + int dateFrameHeight = mPopup->sizeHint().height(); + if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() ) + popupPoint.setY( popupPoint.y() - dateFrameHeight ); + else + popupPoint.setY( popupPoint.y() + height() ); + + int dateFrameWidth = mPopup->sizeHint().width(); + if ( popupPoint.x() + dateFrameWidth > desk.right() ) + popupPoint.setX( desk.right() - dateFrameWidth ); + + if ( popupPoint.x() < desk.left() ) + popupPoint.setX( desk.left() ); + + if ( popupPoint.y() < desk.top() ) + popupPoint.setY( desk.top() ); + + if ( mDate.isValid() ) + mPopup->setDate( mDate ); + else + mPopup->setDate( TQDate::currentDate() ); + + mPopup->popup( popupPoint ); + + // The combo box is now shown pressed. Make it show not pressed again + // by causing its (invisible) list box to emit a 'selected' signal. + // First, ensure that the list box contains the date currently displayed. + TQDate date = parseDate(); + assignDate( date ); + updateView(); + // Now, simulate an Enter to unpress it + TQListBox *lb = listBox(); + if (lb) { + lb->setCurrentItem(0); + TQKeyEvent* keyEvent = new TQKeyEvent(TQEvent::KeyPress, TQt::Key_Enter, 0, 0); + TQApplication::postEvent(lb, keyEvent); + } +} + +void KDateEdit::dateSelected( TQDate date ) +{ + if (assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + + if ( date.isValid() ) { + mPopup->hide(); + } + } +} + +void KDateEdit::dateEntered( TQDate date ) +{ + if (assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + } +} + +void KDateEdit::lineEnterPressed() +{ + bool replaced = false; + + TQDate date = parseDate( &replaced ); + + if (assignDate( date ) ) { + if ( replaced ) + updateView(); + + emit dateChanged( date ); + } +} + +TQDate KDateEdit::parseDate( bool *replaced ) const +{ + TQString text = currentText(); + TQDate result; + + if ( replaced ) + (*replaced) = false; + + if ( text.isEmpty() ) + result = TQDate(); + else if ( mKeywordMap.contains( text.lower() ) ) { + TQDate today = TQDate::currentDate(); + int i = mKeywordMap[ text.lower() ]; + if ( i >= 100 ) { + /* A day name has been entered. Convert to offset from today. + * This 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 occurred 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). + */ + i -= 100; + int currentDay = today.dayOfWeek(); + if ( i >= currentDay ) + i -= currentDay; + else + i += 7 - currentDay; + } + + result = today.addDays( i ); + if ( replaced ) + (*replaced) = true; + } else { + result = TDEGlobal::locale()->readDate( text ); + } + + return result; +} + +bool KDateEdit::eventFilter( TQObject *object, TQEvent *event ) +{ + if ( object == lineEdit() ) { + // We only process the focus out event if the text has changed + // since we got focus + if ( (event->type() == TQEvent::FocusOut) && mTextChanged ) { + lineEnterPressed(); + mTextChanged = false; + } else if ( event->type() == TQEvent::KeyPress ) { + // Up and down arrow keys step the date + TQKeyEvent* keyEvent = (TQKeyEvent*)event; + + if ( keyEvent->key() == TQt::Key_Return ) { + lineEnterPressed(); + return true; + } + + int step = 0; + if ( keyEvent->key() == TQt::Key_Up ) + step = 1; + else if ( keyEvent->key() == TQt::Key_Down ) + step = -1; + if ( step && !mReadOnly ) { + TQDate date = parseDate(); + if ( date.isValid() ) { + date = date.addDays( step ); + if ( assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + return true; + } + } + } + } + } else { + // It's a date picker event + switch ( event->type() ) { + case TQEvent::MouseButtonDblClick: + case TQEvent::MouseButtonPress: { + TQMouseEvent *mouseEvent = (TQMouseEvent*)event; + if ( !mPopup->rect().contains( mouseEvent->pos() ) ) { + TQPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() ); + if ( TQApplication::widgetAt( globalPos, true ) == this ) { + // The date picker is being closed by a click on the + // KDateEdit widget. Avoid popping it up again immediately. + mDiscardNextMousePress = true; + } + } + + break; + } + default: + break; + } + } + + return false; +} + +void KDateEdit::mousePressEvent( TQMouseEvent *event ) +{ + if ( event->button() == TQt::LeftButton && mDiscardNextMousePress ) { + mDiscardNextMousePress = false; + return; + } + + TQComboBox::mousePressEvent( event ); +} + +void KDateEdit::slotTextChanged( const TQString& ) +{ + TQDate date = parseDate(); + + if ( assignDate( date ) ) + emit dateChanged( date ); + + mTextChanged = true; +} + +void KDateEdit::setupKeywords() +{ + // Create the keyword list. This will be used to match against when the user + // enters information. + mKeywordMap.insert( i18n( "tomorrow" ), 1 ); + mKeywordMap.insert( i18n( "today" ), 0 ); + mKeywordMap.insert( i18n( "yesterday" ), -1 ); + + #if 0 //depends on KDE 3.2 + TQString dayName; + for ( int i = 1; i <= 7; ++i ) { + dayName = TDEGlobal::locale()->calendar()->weekDayName( i ).lower(); + mKeywordMap.insert( dayName, i + 100 ); + } + #endif +} + +bool KDateEdit::assignDate( const TQDate& date ) +{ + mDate = date; + mTextChanged = false; + return true; +} + +void KDateEdit::updateView() +{ + TQString dateString; + if ( mDate.isValid() ) + dateString = TDEGlobal::locale()->formatDate( mDate, true ); + + // We do not want to generate a signal here, + // since we explicitly setting the date + bool blocked = signalsBlocked(); + blockSignals( true ); + changeItem( dateString, 0 ); + blockSignals( blocked ); +} + +#include "kdateedit.moc" |