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