You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/kalarm/lib/timespinbox.cpp

365 lines
9.6 KiB

/*
* timespinbox.cpp - time spinbox widget
* Program: kalarm
* Copyright © 2001-2004,2007,2008 by David Jarvie <djarvie@kde.org>
*
* 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 <tqvalidator.h>
#include <tqlineedit.h>
#include <tdelocale.h>
#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;
}