/* * timespinbox.cpp - time spinbox widget * Program: kalarm * Copyright © 2001-2004,2007,2008 by David Jarvie * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kalarm.h" #include #include #include #include "timespinbox.moc" class TimeSpinBox::TimeValidator : public TQValidator { public: TimeValidator(int minMin, int maxMin, TQWidget* parent, const char* name = 0) : TQValidator(TQT_TQOBJECT(parent), name), minMinute(minMin), maxMinute(maxMin), m12Hour(false), mPm(false) { } virtual State validate(TQString&, int&) const; int minMinute, maxMinute; bool m12Hour; bool mPm; }; /*============================================================================= = Class TimeSpinBox = This is a spin box displaying a time in the format hh:mm, with a pair of = spin buttons for each of the hour and minute values. = It can operate in 3 modes: = 1) a time of day using the 24-hour clock. = 2) a time of day using the 12-hour clock. The value is held as 0:00 - 23:59, = but is displayed as 12:00 - 11:59. This is for use in a TimeEdit widget. = 3) a length of time, not restricted to the length of a day. =============================================================================*/ /****************************************************************************** * Construct a wrapping 00:00 - 23:59, or 12:00 - 11:59 time spin box. */ TimeSpinBox::TimeSpinBox(bool use24hour, TQWidget* parent, const char* name) : SpinBox2(0, 1439, 1, 60, parent, name), mMinimumValue(0), m12Hour(!use24hour), mPm(false), mInvalid(false), mEnteredSetValue(false) { mValidator = new TimeValidator(0, 1439, this, "TimeSpinBox validator"); mValidator->m12Hour = m12Hour; setValidator(mValidator); setWrapping(true); setReverseWithLayout(false); // keep buttons the same way round even if right-to-left language setShiftSteps(5, 360); // shift-left button increments 5 min / 6 hours setSelectOnStep(false); setAlignment(TQt::AlignHCenter); connect(this, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotValueChanged(int))); } /****************************************************************************** * Construct a non-wrapping time spin box. */ TimeSpinBox::TimeSpinBox(int minMinute, int maxMinute, TQWidget* parent, const char* name) : SpinBox2(minMinute, maxMinute, 1, 60, parent, name), mMinimumValue(minMinute), m12Hour(false), mInvalid(false), mEnteredSetValue(false) { mValidator = new TimeValidator(minMinute, maxMinute, this, "TimeSpinBox validator"); setValidator(mValidator); setReverseWithLayout(false); // keep buttons the same way round even if right-to-left language setShiftSteps(5, 300); // shift-left button increments 5 min / 5 hours setSelectOnStep(false); setAlignment(TQApplication::reverseLayout() ? TQt::AlignLeft : TQt::AlignRight); } TQString TimeSpinBox::shiftWhatsThis() { return i18n("Press the Shift key while clicking the spin buttons to adjust the time by a larger step (6 hours / 5 minutes)."); } TQTime TimeSpinBox::time() const { return TQTime(value() / 60, value() % 60); } TQString TimeSpinBox::mapValueToText(int v) { if (m12Hour) { if (v < 60) v += 720; // convert 0:nn to 12:nn else if (v >= 780) v -= 720; // convert 13 - 23 hours to 1 - 11 } TQString s; s.sprintf((wrapping() ? "%02d:%02d" : "%d:%02d"), v/60, v%60); return s; } /****************************************************************************** * Convert the user-entered text to a value in minutes. * The allowed formats are: * [hour]:[minute], where minute must be non-blank, or * hhmm, 4 digits, where hour < 24. */ int TimeSpinBox::mapTextToValue(bool* ok) { TQString text = cleanText(); int colon = text.find(':'); if (colon >= 0) { // [h]:m format for any time value TQString hour = text.left(colon).stripWhiteSpace(); TQString minute = text.mid(colon + 1).stripWhiteSpace(); if (!minute.isEmpty()) { bool okmin; bool okhour = true; int m = minute.toUInt(&okmin); int h = 0; if (!hour.isEmpty()) h = hour.toUInt(&okhour); if (okhour && okmin && m < 60) { if (m12Hour) { if (h == 0 || h > 12) h = 100; // error else if (h == 12) h = 0; // convert 12:nn to 0:nn if (mPm) h += 12; // convert to PM } int t = h * 60 + m; if (t >= mMinimumValue && t <= maxValue()) { if (ok) *ok = true; return t; } } } } else if (text.length() == 4) { // hhmm format for time of day bool okn; int mins = text.toUInt(&okn); if (okn) { int m = mins % 100; int h = mins / 100; if (m12Hour) { if (h == 0 || h > 12) h = 100; // error else if (h == 12) h = 0; // convert 12:nn to 0:nn if (mPm) h += 12; // convert to PM } int t = h * 60 + m; if (h < 24 && m < 60 && t >= mMinimumValue && t <= maxValue()) { if (ok) *ok = true; return t; } } } if (ok) *ok = false; return 0; } /****************************************************************************** * Set the spin box as valid or invalid. * If newly invalid, the value is displayed as asterisks. * If newly valid, the value is set to the minimum value. */ void TimeSpinBox::setValid(bool valid) { if (valid && mInvalid) { mInvalid = false; if (value() < mMinimumValue) SpinBox2::setValue(mMinimumValue); setSpecialValueText(TQString()); SpinBox2::setMinValue(mMinimumValue); } else if (!valid && !mInvalid) { mInvalid = true; SpinBox2::setMinValue(mMinimumValue - 1); setSpecialValueText(TQString::fromLatin1("**:**")); SpinBox2::setValue(mMinimumValue - 1); } } /****************************************************************************** * Set the spin box's minimum value. */ void TimeSpinBox::setMinValue(int minutes) { mMinimumValue = minutes; SpinBox2::setMinValue(mMinimumValue - (mInvalid ? 1 : 0)); } /****************************************************************************** * Set the spin box's value. */ void TimeSpinBox::setValue(int minutes) { if (!mEnteredSetValue) { mEnteredSetValue = true; mPm = (minutes >= 720); if (minutes > maxValue()) setValid(false); else { if (mInvalid) { mInvalid = false; setSpecialValueText(TQString()); SpinBox2::setMinValue(mMinimumValue); } SpinBox2::setValue(minutes); mEnteredSetValue = false; } } } /****************************************************************************** * Step the spin box value. * If it was invalid, set it valid and set the value to the minimum. */ void TimeSpinBox::stepUp() { if (mInvalid) setValid(true); else SpinBox2::stepUp(); } void TimeSpinBox::stepDown() { if (mInvalid) setValid(true); else SpinBox2::stepDown(); } bool TimeSpinBox::isValid() const { return value() >= mMinimumValue; } void TimeSpinBox::slotValueChanged(int value) { mPm = mValidator->mPm = (value >= 720); } TQSize TimeSpinBox::sizeHint() const { TQSize sz = SpinBox2::sizeHint(); TQFontMetrics fm(font()); return TQSize(sz.width() + fm.width(":"), sz.height()); } TQSize TimeSpinBox::minimumSizeHint() const { TQSize sz = SpinBox2::minimumSizeHint(); TQFontMetrics fm(font()); return TQSize(sz.width() + fm.width(":"), sz.height()); } /****************************************************************************** * Validate the time spin box input. * The entered time must either be 4 digits, or it must contain a colon, but * hours may be blank. */ TQValidator::State TimeSpinBox::TimeValidator::validate(TQString& text, int& /*cursorPos*/) const { TQString cleanText = text.stripWhiteSpace(); if (cleanText.isEmpty()) return TQValidator::Intermediate; TQValidator::State state = TQValidator::Acceptable; TQString hour; bool ok; int hr = 0; int mn = 0; int colon = cleanText.find(':'); if (colon >= 0) { TQString minute = cleanText.mid(colon + 1); if (minute.isEmpty()) state = TQValidator::Intermediate; else if ((mn = minute.toUInt(&ok)) >= 60 || !ok) return TQValidator::Invalid; hour = cleanText.left(colon); } else if (maxMinute >= 1440) { // The hhmm form of entry is only allowed for time-of-day, i.e. <= 2359 hour = cleanText; state = TQValidator::Intermediate; } else { if (cleanText.length() > 4) return TQValidator::Invalid; if (cleanText.length() < 4) state = TQValidator::Intermediate; hour = cleanText.left(2); TQString minute = cleanText.mid(2); if (!minute.isEmpty() && ((mn = minute.toUInt(&ok)) >= 60 || !ok)) return TQValidator::Invalid; } if (!hour.isEmpty()) { hr = hour.toUInt(&ok); if (m12Hour) { if (hr == 0 || hr > 12) hr = 100; // error; else if (hr == 12) hr = 0; // convert 12:nn to 0:nn if (mPm) hr += 12; // convert to PM } if (!ok || hr > maxMinute/60) return TQValidator::Invalid; } if (state == TQValidator::Acceptable) { int t = hr * 60 + mn; if (t < minMinute || t > maxMinute) return TQValidator::Invalid; } return state; }