kalarm Library API Documentation

datetime.cpp

00001 /*
00002  *  datetime.cpp  -  time spinbox, and alarm time entry widget
00003  *  Program:  kalarm
00004  *  (C) 2001 - 2003 by David Jarvie  software@astrojar.org.uk
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  *
00020  *  As a special exception, permission is given to link this program
00021  *  with any edition of Qt, and distribute the resulting executable,
00022  *  without including the source code for Qt in the source distribution.
00023  */
00024 
00025 #include "kalarm.h"
00026 
00027 #include <qlayout.h>
00028 #include <qgroupbox.h>
00029 #include <qhbox.h>
00030 #include <qradiobutton.h>
00031 #include <qcheckbox.h>
00032 #include <qpushbutton.h>
00033 #include <qvalidator.h>
00034 #include <qwhatsthis.h>
00035 
00036 #include <kdialog.h>
00037 #include <kmessagebox.h>
00038 #include <klocale.h>
00039 
00040 #include "dateedit.h"
00041 #include "datetime.moc"
00042 
00043 
00044 /******************************************************************************
00045 *  Construct a widget with a group box and title.
00046 */
00047 AlarmTimeWidget::AlarmTimeWidget(const QString& groupBoxTitle, int mode, QWidget* parent, const char* name)
00048         : ButtonGroup(groupBoxTitle, parent, name)
00049 {
00050         init(mode);
00051 }
00052 
00053 /******************************************************************************
00054 *  Construct a widget without a group box or title.
00055 */
00056 AlarmTimeWidget::AlarmTimeWidget(int mode, QWidget* parent, const char* name)
00057         : ButtonGroup(parent, name)
00058 {
00059         setFrameStyle(QFrame::NoFrame);
00060         init(mode);
00061 }
00062 
00063 void AlarmTimeWidget::init(int mode)
00064 {
00065         connect(this, SIGNAL(buttonSet(int)), SLOT(slotButtonSet(int)));
00066         connect(this, SIGNAL(clicked(int)), SLOT(slotButtonClicked(int)));
00067         QBoxLayout* topLayout = new QVBoxLayout(this, 0, KDialog::spacingHint());
00068         if (!title().isEmpty())
00069                 topLayout->setMargin(marginKDE2 + KDialog::marginHint());
00070         topLayout->addSpacing(fontMetrics().lineSpacing()/2);
00071 
00072         // At time radio button/label
00073         atTimeRadio = new QRadioButton(((mode & DEFER_TIME) ? i18n("&Defer to date/time:") : i18n("At &date/time:")), this, "atTimeRadio");
00074         atTimeRadio->setFixedSize(atTimeRadio->sizeHint());
00075         QWhatsThis::add(atTimeRadio,
00076                         ((mode & DEFER_TIME) ? i18n("Reschedule the alarm to the specified date and time.")
00077                                              : i18n("Schedule the alarm at the specified date and time.")));
00078 
00079         // Date edit box
00080         dateEdit = new DateEdit(this);
00081         dateEdit->setFixedSize(dateEdit->sizeHint());
00082         QWhatsThis::add(dateEdit, i18n("Enter the date to schedule the alarm."));
00083         connect(dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(slotDateChanged(QDate)));
00084 
00085         // Time edit box and Any time checkbox
00086         QHBox* timeBox = new QHBox(this);
00087         timeBox->setSpacing(2*KDialog::spacingHint());
00088         timeEdit = new TimeSpinBox(timeBox);
00089         timeEdit->setValue(1439);
00090         timeEdit->setFixedSize(timeEdit->sizeHint());
00091         QWhatsThis::add(timeEdit, i18n("Enter the time to schedule the alarm."));
00092         connect(timeEdit, SIGNAL(valueChanged(int)), this, SLOT(slotTimeChanged(int)));
00093 
00094         if (mode & DEFER_TIME)
00095         {
00096                 anyTimeAllowed = false;
00097                 anyTimeCheckBox = 0;
00098         }
00099         else
00100         {
00101                 anyTimeAllowed = true;
00102                 anyTimeCheckBox = new QCheckBox(i18n("An&y time"), timeBox);
00103                 anyTimeCheckBox->setFixedSize(anyTimeCheckBox->sizeHint());
00104                 QWhatsThis::add(anyTimeCheckBox, i18n("Schedule the alarm for any time during the day"));
00105                 connect(anyTimeCheckBox, SIGNAL(toggled(bool)), this, SLOT(anyTimeToggled(bool)));
00106         }
00107 
00108         // 'Time from now' radio button/label
00109         afterTimeRadio = new QRadioButton(((mode & DEFER_TIME) ? i18n("Defer for time &interval:") : i18n("Time from no&w:")),
00110                                           this, "afterTimeRadio");
00111         afterTimeRadio->setFixedSize(afterTimeRadio->sizeHint());
00112         QWhatsThis::add(afterTimeRadio,
00113                         ((mode & DEFER_TIME) ? i18n("Reschedule the alarm for the specified time interval after now.")
00114                                              : i18n("Schedule the alarm after the specified time interval from now.")));
00115 
00116         // Delay time spin box
00117         delayTime = new TimeSpinBox(1, 99*60+59, this);
00118         delayTime->setValue(1439);
00119         delayTime->setFixedSize(delayTime->sizeHint());
00120         QWhatsThis::add(delayTime,
00121               i18n("Enter the length of time (in hours and minutes) after the current time to schedule the alarm."));
00122         connect(delayTime, SIGNAL(valueChanged(int)), this, SLOT(delayTimeChanged(int)));
00123 
00124         // Set up the layout, either narrow or wide
00125         if (mode & NARROW)
00126         {
00127                 QGridLayout* grid = new QGridLayout(topLayout, 2, 2, KDialog::spacingHint());
00128                 grid->addWidget(atTimeRadio, 0, 0);
00129                 grid->addWidget(dateEdit, 0, 1, Qt::AlignLeft);
00130                 grid->addWidget(timeBox, 1, 1, Qt::AlignLeft);
00131                 grid->setColStretch(2, 1);
00132                 topLayout->addStretch();
00133                 QBoxLayout* layout = new QHBoxLayout(topLayout, KDialog::spacingHint());
00134                 layout->addWidget(afterTimeRadio);
00135                 layout->addWidget(delayTime);
00136                 layout->addStretch();
00137         }
00138         else
00139         {
00140                 QGridLayout* grid = new QGridLayout(topLayout, 2, 3, KDialog::spacingHint());
00141                 grid->addWidget(atTimeRadio, 0, 0, Qt::AlignLeft);
00142                 grid->addWidget(dateEdit, 0, 1, Qt::AlignLeft);
00143                 grid->addWidget(timeBox, 0, 2, Qt::AlignLeft);
00144                 grid->setRowStretch(0, 1);
00145                 grid->addWidget(afterTimeRadio, 1, 0, Qt::AlignLeft);
00146                 grid->addWidget(delayTime, 1, 1, Qt::AlignLeft);
00147                 grid->setColStretch(3, 1);
00148                 topLayout->addStretch();
00149         }
00150 
00151         // Initialise the radio button statuses
00152         setButton(id(atTimeRadio));
00153 
00154         // Timeout every minute to update alarm time fields.
00155         // But first synchronise to one second after the minute boundary.
00156         int firstInterval = 61 - QTime::currentTime().second();
00157         timer.start(1000 * firstInterval);
00158         timerSyncing = (firstInterval != 60);
00159         connect(&timer, SIGNAL(timeout()), this, SLOT(slotTimer()));
00160 }
00161 
00162 /******************************************************************************
00163 *  Fetch the entered date/time.
00164 *  If <= current time, and 'showErrorMessage' is true, output an error message.
00165 *  Output: 'anyTime' is set true if no time was entered.
00166 *  Reply = widget with the invalid value, if it is after the current time.
00167 */
00168 QWidget* AlarmTimeWidget::getDateTime(QDateTime& dateTime, bool& anyTime, bool showErrorMessage) const
00169 {
00170         QTime nowt = QTime::currentTime();
00171         QDateTime now(QDate::currentDate(), QTime(nowt.hour(), nowt.minute()));
00172         if (atTimeRadio->isOn())
00173         {
00174                 dateTime.setDate(dateEdit->date());
00175                 anyTime = anyTimeAllowed && anyTimeCheckBox && anyTimeCheckBox->isChecked();
00176                 if (anyTime)
00177                 {
00178                         dateTime.setTime(QTime());
00179                         if (dateTime.date() < now.date())
00180                         {
00181                                 if (showErrorMessage)
00182                                         KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18n("Alarm date has already expired"));
00183                                 return dateEdit;
00184                         }
00185                 }
00186                 else
00187                 {
00188                         dateTime.setTime(timeEdit->time());
00189                         if (dateTime <= now.addSecs(1))
00190                         {
00191                                 if (showErrorMessage)
00192                                         KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18n("Alarm time has already expired"));
00193                                 return timeEdit;
00194                         }
00195                 }
00196         }
00197         else
00198         {
00199                 dateTime = now.addSecs(delayTime->value() * 60);
00200                 anyTime = false;
00201         }
00202         return 0;
00203 }
00204 
00205 /******************************************************************************
00206 *  Set the date/time.
00207 */
00208 void AlarmTimeWidget::setDateTime(const QDateTime& dt, bool anyTime)
00209 {
00210         timeEdit->setValue(dt.time().hour()*60 + dt.time().minute());
00211         dateEdit->setDate(dt.date());
00212         dateTimeChanged();     // update the delay time edit box
00213         QDate now = QDate::currentDate();
00214         dateEdit->setMinDate(dt.date() < now ? dt.date() : now);
00215         if (anyTimeCheckBox)
00216         {
00217                 if (anyTime)
00218                         anyTimeAllowed = true;
00219                 anyTimeCheckBox->setChecked(anyTime);
00220         }
00221 }
00222 
00223 /******************************************************************************
00224 *  Enable/disable the "any time" checkbox.
00225 */
00226 void AlarmTimeWidget::enableAnyTime(bool enable)
00227 {
00228         if (anyTimeCheckBox)
00229         {
00230                 anyTimeAllowed = enable;
00231                 bool at = atTimeRadio->isOn();
00232                 anyTimeCheckBox->setEnabled(enable && at);
00233                 if (at)
00234                         timeEdit->setEnabled(!enable || !anyTimeCheckBox->isChecked());
00235         }
00236 }
00237 
00238 /******************************************************************************
00239 *  Called every minute to update the alarm time data entry fields.
00240 */
00241 void AlarmTimeWidget::slotTimer()
00242 {
00243         if (timerSyncing)
00244         {
00245                 // We've synced to the minute boundary. Now set timer to 1 minute intervals.
00246                 timer.changeInterval(1000 * 60);
00247                 timerSyncing = false;
00248         }
00249         if (atTimeRadio->isOn())
00250                 dateTimeChanged();
00251         else
00252                 delayTimeChanged(delayTime->value());
00253 }
00254 
00255 
00256 /******************************************************************************
00257 *  Called when the At or After time radio button states have been set.
00258 *  Updates the appropriate edit box.
00259 */
00260 void AlarmTimeWidget::slotButtonSet(int)
00261 {
00262         bool at = atTimeRadio->isOn();
00263         dateEdit->setEnabled(at);
00264         timeEdit->setEnabled(at && (!anyTimeAllowed || !anyTimeCheckBox || !anyTimeCheckBox->isChecked()));
00265         if (anyTimeCheckBox)
00266                 anyTimeCheckBox->setEnabled(at && anyTimeAllowed);
00267         // Ensure that the value of the delay edit box is > 0.
00268         QDateTime dt(dateEdit->date(), timeEdit->time());
00269         int minutes = (QDateTime::currentDateTime().secsTo(dt) + 59) / 60;
00270         if (minutes <= 0)
00271                 delayTime->setValid(true);
00272         delayTime->setEnabled(!at);
00273 }
00274 
00275 /******************************************************************************
00276 *  Called when the At or After time radio button has been clicked.
00277 *  Moves the focus to the appropriate date or time edit field.
00278 */
00279 void AlarmTimeWidget::slotButtonClicked(int)
00280 {
00281         if (atTimeRadio->isOn())
00282                 dateEdit->setFocus();
00283         else
00284                 delayTime->setFocus();
00285 }
00286 /******************************************************************************
00287 *  Called after the anyTimeCheckBox checkbox has been toggled.
00288 */
00289 void AlarmTimeWidget::anyTimeToggled(bool on)
00290 {
00291         timeEdit->setEnabled((!anyTimeAllowed || !on) && atTimeRadio->isOn());
00292 }
00293 
00294 /******************************************************************************
00295 *  Called when the date or time edit box values have changed.
00296 *  Updates the time delay edit box accordingly.
00297 */
00298 void AlarmTimeWidget::dateTimeChanged()
00299 {
00300         QDateTime dt(dateEdit->date(), timeEdit->time());
00301         int minutes = (QDateTime::currentDateTime().secsTo(dt) + 59) / 60;
00302         bool blocked = delayTime->signalsBlocked();
00303         delayTime->blockSignals(true);     // prevent infinite recursion between here and delayTimeChanged()
00304         if (minutes <= 0  ||  minutes > delayTime->maxValue())
00305                 delayTime->setValid(false);
00306         else
00307                 delayTime->setValue(minutes);
00308         delayTime->blockSignals(blocked);
00309 }
00310 
00311 /******************************************************************************
00312 *  Called when the delay time edit box value has changed.
00313 *  Updates the Date and Time edit boxes accordingly.
00314 */
00315 void AlarmTimeWidget::delayTimeChanged(int minutes)
00316 {
00317         if (delayTime->valid())
00318         {
00319                 QDateTime dt = QDateTime::currentDateTime().addSecs(minutes * 60);
00320                 bool blockedT = timeEdit->signalsBlocked();
00321                 bool blockedD = dateEdit->signalsBlocked();
00322                 timeEdit->blockSignals(true);     // prevent infinite recursion between here and dateTimeChanged()
00323                 dateEdit->blockSignals(true);
00324                 timeEdit->setValue(dt.time().hour()*60 + dt.time().minute());
00325                 dateEdit->setDate(dt.date());
00326                 timeEdit->blockSignals(blockedT);
00327                 dateEdit->blockSignals(blockedD);
00328         }
00329 }
00330 
00331 
00332 /*=============================================================================
00333 =  class TimeSpinBox
00334 =============================================================================*/
00335 class TimeSpinBox::TimeValidator : public QValidator
00336 {
00337         public:
00338                 TimeValidator(int minMin, int maxMin, QWidget* parent, const char* name = 0L)
00339                         : QValidator(parent, name), minMinute(minMin), maxMinute(maxMin) { }
00340                 virtual State validate(QString&, int&) const;
00341                 int  minMinute, maxMinute;
00342 };
00343 
00344 
00345 // Construct a wrapping 00:00 - 23:59 time spin box
00346 TimeSpinBox::TimeSpinBox(QWidget* parent, const char* name)
00347         : SpinBox2(0, 1439, 1, 60, parent, name),
00348           minimumValue(0),
00349           invalid(false),
00350           enteredSetValue(false)
00351 {
00352         validator = new TimeValidator(0, 1439, this, "TimeSpinBox validator");
00353         setValidator(validator);
00354         setWrapping(true);
00355         setShiftSteps(5, 360);    // shift-left button increments 5 min / 6 hours
00356 }
00357 
00358 // Construct a non-wrapping time spin box
00359 TimeSpinBox::TimeSpinBox(int minMinute, int maxMinute, QWidget* parent, const char* name)
00360         : SpinBox2(minMinute, maxMinute, 1, 60, parent, name),
00361           minimumValue(minMinute),
00362           invalid(false),
00363           enteredSetValue(false)
00364 {
00365         validator = new TimeValidator(minMinute, maxMinute, this, "TimeSpinBox validator");
00366         setValidator(validator);
00367         setShiftSteps(5, 360);    // shift-left button increments 5 min / 6 hours
00368 }
00369 
00370 QString TimeSpinBox::shiftWhatsThis()
00371 {
00372         return i18n("\nPress the Shift key while clicking the spin buttons to adjust the time by a larger step (6 hours / 5 minutes).");
00373 }
00374 
00375 QTime TimeSpinBox::time() const
00376 {
00377         return QTime(value() / 60, value() % 60);
00378 }
00379 
00380 QString TimeSpinBox::mapValueToText(int v)
00381 {
00382         QString s;
00383         s.sprintf("%02d:%02d", v/60, v%60);
00384         return s;
00385 }
00386 
00387 /******************************************************************************
00388  * Convert the user-entered text to a value in minutes.
00389  * The allowed format is [hour]:[minute], where hour and
00390  * minute must be non-blank.
00391  */
00392 int TimeSpinBox::mapTextToValue(bool* ok)
00393 {
00394         QString text = cleanText();
00395         int colon = text.find(':');
00396         if (colon >= 0)
00397         {
00398                 QString hour   = text.left(colon).stripWhiteSpace();
00399                 QString minute = text.mid(colon + 1).stripWhiteSpace();
00400                 if (!hour.isEmpty()  &&  !minute.isEmpty())
00401                 {
00402                         bool okhour, okmin;
00403                         int m = minute.toUInt(&okmin);
00404                         int t = hour.toUInt(&okhour) * 60 + m;
00405                         if (okhour  &&  okmin  &&  m < 60  &&  t >= minimumValue  &&  t <= maxValue())
00406                         {
00407                                 *ok = true;
00408                                 return t;
00409                         }
00410                 }
00411         }
00412         *ok = false;
00413         return 0;
00414 }
00415 
00416 /******************************************************************************
00417  * Set the spin box as valid or invalid.
00418  * If newly invalid, the value is displayed as asterisks.
00419  * If newly valid, the value is set to the minimum value.
00420  */
00421 void TimeSpinBox::setValid(bool valid)
00422 {
00423         if (valid  &&  invalid)
00424         {
00425                 invalid = false;
00426                 if (value() < minimumValue)
00427                         SpinBox2::setValue(minimumValue);
00428                 setSpecialValueText(QString());
00429                 setMinValue(minimumValue);
00430         }
00431         else if (!valid  &&  !invalid)
00432         {
00433                 invalid = true;
00434                 setMinValue(minimumValue - 1);
00435                 setSpecialValueText(QString::fromLatin1("**:**"));
00436                 SpinBox2::setValue(minimumValue - 1);
00437         }
00438 }
00439 
00440 /******************************************************************************
00441  * Set the spin box's value.
00442  */
00443 void TimeSpinBox::setValue(int minutes)
00444 {
00445         if (!enteredSetValue)
00446         {
00447                 enteredSetValue = true;
00448                 if (minutes > maxValue())
00449                         setValid(false);
00450                 else
00451                 {
00452                         if (invalid)
00453                         {
00454                                 invalid = false;
00455                                 setSpecialValueText(QString());
00456                                 setMinValue(minimumValue);
00457                         }
00458                         SpinBox2::setValue(minutes);
00459                         enteredSetValue = false;
00460                 }
00461         }
00462 }
00463 
00464 /******************************************************************************
00465  * Step the spin box value.
00466  * If it was invalid, set it valid and set the value to the minimum.
00467  */
00468 void TimeSpinBox::stepUp()
00469 {
00470         if (invalid)
00471                 setValid(true);
00472         else
00473                 SpinBox2::stepUp();
00474 }
00475 
00476 void TimeSpinBox::stepDown()
00477 {
00478         if (invalid)
00479                 setValid(true);
00480         else
00481                 SpinBox2::stepDown();
00482 }
00483 
00484 bool TimeSpinBox::valid() const
00485 {
00486         return value() >= minimumValue;
00487 }
00488 
00489 
00490 /******************************************************************************
00491  * Validate the time spin box input.
00492  * The entered time must contain a colon, but hours and/or minutes may be blank.
00493  */
00494 QValidator::State TimeSpinBox::TimeValidator::validate(QString& text, int& /*cursorPos*/) const
00495 {
00496         QValidator::State state = QValidator::Acceptable;
00497         QString hour;
00498         int hr;
00499         int mn = 0;
00500         int colon = text.find(':');
00501         if (colon >= 0)
00502         {
00503                 QString minute = text.mid(colon + 1).stripWhiteSpace();
00504                 if (minute.isEmpty())
00505                         state = QValidator::Intermediate;
00506                 else
00507                 {
00508                         bool ok;
00509                         if ((mn = minute.toUInt(&ok)) >= 60  ||  !ok)
00510                                 return QValidator::Invalid;
00511                 }
00512 
00513                 hour = text.left(colon).stripWhiteSpace();
00514         }
00515         else
00516         {
00517                 state = QValidator::Intermediate;
00518                 hour = text;
00519         }
00520 
00521         if (hour.isEmpty())
00522                 return QValidator::Intermediate;
00523         bool ok;
00524         if ((hr = hour.toUInt(&ok)) > maxMinute/60  ||  !ok)
00525                 return QValidator::Invalid;
00526         if (state == QValidator::Acceptable)
00527         {
00528                 int t = hr * 60 + mn;
00529                 if (t < minMinute  ||  t > maxMinute)
00530                         return QValidator::Invalid;
00531         }
00532         return state;
00533 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sat Oct 18 02:47:26 2003 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001