kalarm/lib

timespinbox.cpp

00001 /*
00002  *  timespinbox.cpp  -  time spinbox widget
00003  *  Program:  kalarm
00004  *  Copyright (C) 2001 - 2004 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 along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 
00023 #include <qvalidator.h>
00024 #include <klocale.h>
00025 
00026 #include "timespinbox.moc"
00027 
00028 
00029 class TimeSpinBox::TimeValidator : public QValidator
00030 {
00031     public:
00032         TimeValidator(int minMin, int maxMin, QWidget* parent, const char* name = 0)
00033             : QValidator(parent, name),
00034                   minMinute(minMin), maxMinute(maxMin), m12Hour(false), mPm(false) { }
00035         virtual State validate(QString&, int&) const;
00036         int  minMinute, maxMinute;
00037         bool m12Hour;
00038         bool mPm;
00039 };
00040 
00041 
00042 /*=============================================================================
00043 = Class TimeSpinBox
00044 = This is a spin box displaying a time in the format hh:mm, with a pair of
00045 = spin buttons for each of the hour and minute values.
00046 = It can operate in 3 modes:
00047 =  1) a time of day using the 24-hour clock.
00048 =  2) a time of day using the 12-hour clock. The value is held as 0:00 - 23:59,
00049 =     but is displayed as 12:00 - 11:59. This is for use in a TimeEdit widget.
00050 =  3) a length of time, not restricted to the length of a day.
00051 =============================================================================*/
00052 
00053 /******************************************************************************
00054  * Construct a wrapping 00:00 - 23:59, or 12:00 - 11:59 time spin box.
00055  */
00056 TimeSpinBox::TimeSpinBox(bool use24hour, QWidget* parent, const char* name)
00057     : SpinBox2(0, 1439, 1, 60, parent, name),
00058       mMinimumValue(0),
00059       m12Hour(!use24hour),
00060       mPm(false),
00061       mInvalid(false),
00062       mEnteredSetValue(false)
00063 {
00064     mValidator = new TimeValidator(0, 1439, this, "TimeSpinBox validator");
00065     mValidator->m12Hour = m12Hour;
00066     setValidator(mValidator);
00067     setWrapping(true);
00068     setReverseWithLayout(false);   // keep buttons the same way round even if right-to-left language
00069     setShiftSteps(5, 360);    // shift-left button increments 5 min / 6 hours
00070     setSelectOnStep(false);
00071     connect(this, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
00072 }
00073 
00074 /******************************************************************************
00075  * Construct a non-wrapping time spin box.
00076  */
00077 TimeSpinBox::TimeSpinBox(int minMinute, int maxMinute, QWidget* parent, const char* name)
00078     : SpinBox2(minMinute, maxMinute, 1, 60, parent, name),
00079       mMinimumValue(minMinute),
00080       m12Hour(false),
00081       mInvalid(false),
00082       mEnteredSetValue(false)
00083 {
00084     mValidator = new TimeValidator(minMinute, maxMinute, this, "TimeSpinBox validator");
00085     setValidator(mValidator);
00086     setReverseWithLayout(false);   // keep buttons the same way round even if right-to-left language
00087     setShiftSteps(5, 360);    // shift-left button increments 5 min / 6 hours
00088     setSelectOnStep(false);
00089 }
00090 
00091 QString TimeSpinBox::shiftWhatsThis()
00092 {
00093     return i18n("Press the Shift key while clicking the spin buttons to adjust the time by a larger step (6 hours / 5 minutes).");
00094 }
00095 
00096 QTime TimeSpinBox::time() const
00097 {
00098     return QTime(value() / 60, value() % 60);
00099 }
00100 
00101 QString TimeSpinBox::mapValueToText(int v)
00102 {
00103     if (m12Hour)
00104     {
00105         if (v < 60)
00106             v += 720;      // convert 0:nn to 12:nn
00107         else if (v >= 780)
00108             v -= 720;      // convert 13 - 23 hours to 1 - 11
00109     }
00110     QString s;
00111     s.sprintf("%02d:%02d", v/60, v%60);
00112     return s;
00113 }
00114 
00115 /******************************************************************************
00116  * Convert the user-entered text to a value in minutes.
00117  * The allowed formats are:
00118  *    [hour]:[minute], where minute must be non-blank, or
00119  *    hhmm, 4 digits, where hour < 24.
00120  */
00121 int TimeSpinBox::mapTextToValue(bool* ok)
00122 {
00123     QString text = cleanText();
00124     int colon = text.find(':');
00125     if (colon >= 0)
00126     {
00127         // [h]:m format for any time value
00128         QString hour   = text.left(colon).stripWhiteSpace();
00129         QString minute = text.mid(colon + 1).stripWhiteSpace();
00130         if (!minute.isEmpty())
00131         {
00132             bool okmin;
00133             bool okhour = true;
00134             int m = minute.toUInt(&okmin);
00135             int h = 0;
00136             if (!hour.isEmpty())
00137                 h = hour.toUInt(&okhour);
00138             if (okhour  &&  okmin  &&  m < 60)
00139             {
00140                 if (m12Hour)
00141                 {
00142                     if (h == 0  ||  h > 12)
00143                         h = 100;     // error
00144                     else if (h == 12)
00145                         h = 0;       // convert 12:nn to 0:nn
00146                     if (mPm)
00147                         h += 12;     // convert to PM
00148                 }
00149                 int t = h * 60 + m;
00150                 if (t >= mMinimumValue  &&  t <= maxValue())
00151                 {
00152                     if (ok)
00153                         *ok = true;
00154                     return t;
00155                 }
00156             }
00157         }
00158     }
00159     else if (text.length() == 4)
00160     {
00161         // hhmm format for time of day
00162         bool okn;
00163         int mins = text.toUInt(&okn);
00164         if (okn)
00165         {
00166             int m = mins % 100;
00167             int h = mins / 100;
00168             if (m12Hour)
00169             {
00170                 if (h == 0  ||  h > 12)
00171                     h = 100;    // error
00172                 else if (h == 12)
00173                     h = 0;      // convert 12:nn to 0:nn
00174                 if (mPm)
00175                     h += 12;    // convert to PM
00176             }
00177             int t = h * 60 + m;
00178             if (h < 24  &&  m < 60  &&  t >= mMinimumValue  &&  t <= maxValue())
00179             {
00180                 if (ok)
00181                     *ok = true;
00182                 return t;
00183             }
00184         }
00185 
00186     }
00187     if (ok)
00188         *ok = false;
00189     return 0;
00190 }
00191 
00192 /******************************************************************************
00193  * Set the spin box as valid or invalid.
00194  * If newly invalid, the value is displayed as asterisks.
00195  * If newly valid, the value is set to the minimum value.
00196  */
00197 void TimeSpinBox::setValid(bool valid)
00198 {
00199     if (valid  &&  mInvalid)
00200     {
00201         mInvalid = false;
00202         if (value() < mMinimumValue)
00203             SpinBox2::setValue(mMinimumValue);
00204         setSpecialValueText(QString());
00205         setMinValue(mMinimumValue);
00206     }
00207     else if (!valid  &&  !mInvalid)
00208     {
00209         mInvalid = true;
00210         setMinValue(mMinimumValue - 1);
00211         setSpecialValueText(QString::fromLatin1("**:**"));
00212         SpinBox2::setValue(mMinimumValue - 1);
00213     }
00214 }
00215 
00216 /******************************************************************************
00217  * Set the spin box's value.
00218  */
00219 void TimeSpinBox::setValue(int minutes)
00220 {
00221     if (!mEnteredSetValue)
00222     {
00223         mEnteredSetValue = true;
00224         mPm = (minutes >= 720);
00225         if (minutes > maxValue())
00226             setValid(false);
00227         else
00228         {
00229             if (mInvalid)
00230             {
00231                 mInvalid = false;
00232                 setSpecialValueText(QString());
00233                 setMinValue(mMinimumValue);
00234             }
00235             SpinBox2::setValue(minutes);
00236             mEnteredSetValue = false;
00237         }
00238     }
00239 }
00240 
00241 /******************************************************************************
00242  * Step the spin box value.
00243  * If it was invalid, set it valid and set the value to the minimum.
00244  */
00245 void TimeSpinBox::stepUp()
00246 {
00247     if (mInvalid)
00248         setValid(true);
00249     else
00250         SpinBox2::stepUp();
00251 }
00252 
00253 void TimeSpinBox::stepDown()
00254 {
00255     if (mInvalid)
00256         setValid(true);
00257     else
00258         SpinBox2::stepDown();
00259 }
00260 
00261 bool TimeSpinBox::isValid() const
00262 {
00263     return value() >= mMinimumValue;
00264 }
00265 
00266 void TimeSpinBox::slotValueChanged(int value)
00267 {
00268     mPm = mValidator->mPm = (value >= 720);
00269 }
00270 
00271 /******************************************************************************
00272  * Validate the time spin box input.
00273  * The entered time must either be 4 digits, or it must contain a colon, but
00274  * hours may be blank.
00275  */
00276 QValidator::State TimeSpinBox::TimeValidator::validate(QString& text, int& /*cursorPos*/) const
00277 {
00278     QString cleanText = text.stripWhiteSpace();
00279     if (cleanText.isEmpty())
00280         return QValidator::Intermediate;
00281     QValidator::State state = QValidator::Acceptable;
00282     QString hour;
00283     bool ok;
00284     int hr = 0;
00285     int mn = 0;
00286     int colon = cleanText.find(':');
00287     if (colon >= 0)
00288     {
00289         QString minute = cleanText.mid(colon + 1);
00290         if (minute.isEmpty())
00291             state = QValidator::Intermediate;
00292         else if ((mn = minute.toUInt(&ok)) >= 60  ||  !ok)
00293             return QValidator::Invalid;
00294 
00295         hour = cleanText.left(colon);
00296     }
00297     else if (maxMinute >= 1440)
00298     {
00299         // The hhmm form of entry is only allowed for time-of-day, i.e. <= 2359
00300         hour = cleanText;
00301         state = QValidator::Intermediate;
00302     }
00303     else
00304     {
00305         if (cleanText.length() > 4)
00306             return QValidator::Invalid;
00307         if (cleanText.length() < 4)
00308             state = QValidator::Intermediate;
00309         hour = cleanText.left(2);
00310         QString minute = cleanText.mid(2);
00311         if (!minute.isEmpty()
00312         &&  ((mn = minute.toUInt(&ok)) >= 60  ||  !ok))
00313             return QValidator::Invalid;
00314     }
00315 
00316     if (!hour.isEmpty())
00317     {
00318         hr = hour.toUInt(&ok);
00319         if (m12Hour)
00320         {
00321             if (hr == 0  ||  hr > 12)
00322                 hr = 100;    // error;
00323             else if (hr == 12)
00324                 hr = 0;      // convert 12:nn to 0:nn
00325             if (mPm)
00326                 hr += 12;    // convert to PM
00327         }
00328         if (!ok  ||  hr > maxMinute/60)
00329             return QValidator::Invalid;
00330     }
00331     if (state == QValidator::Acceptable)
00332     {
00333         int t = hr * 60 + mn;
00334         if (t < minMinute  ||  t > maxMinute)
00335             return QValidator::Invalid;
00336     }
00337     return state;
00338 }
KDE Home | KDE Accessibility Home | Description of Access Keys