/*
 *  alarmtimewidget.cpp  -  alarm date/time entry widget
 *  Program:  kalarm
 *  Copyright © 2001-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 <QGroupBox>
#include <QGridLayout>
#include <QLabel>

#include <kdialog.h>
#include <kmessagebox.h>
#include <klocale.h>

#include "buttongroup.h"
#include "checkbox.h"
#include "dateedit.h"
#include "datetime.h"
#include "preferences.h"
#include "pushbutton.h"
#include "radiobutton.h"
#include "synchtimer.h"
#include "timeedit.h"
#include "timespinbox.h"
#include "timezonecombo.h"
#include "alarmtimewidget.moc"

static const QTime time_23_59(23, 59);


const int AlarmTimeWidget::maxDelayTime = 999*60 + 59;    // < 1000 hours

QString AlarmTimeWidget::i18n_TimeAfterPeriod()
{
	return i18nc("@info/plain", "Enter the length of time (in hours and minutes) after "
	            "the current time to schedule the alarm.");
}


/******************************************************************************
* Construct a widget with a group box and title.
*/
AlarmTimeWidget::AlarmTimeWidget(const QString& groupBoxTitle, int mode, QWidget* parent)
	: QFrame(parent),
	  mMinDateTimeIsNow(false),
	  mPastMax(false),
	  mMinMaxTimeSet(false)
{
	init(mode, groupBoxTitle);
}

/******************************************************************************
* Construct a widget without a group box or title.
*/
AlarmTimeWidget::AlarmTimeWidget(int mode, QWidget* parent)
	: QFrame(parent),
	  mMinDateTimeIsNow(false),
	  mPastMax(false),
	  mMinMaxTimeSet(false)
{
	init(mode);
}

void AlarmTimeWidget::init(int mode, const QString& title)
{
	static const QString recurText = i18nc("@info/plain",
	                                       "If a recurrence is configured, the start date/time will be adjusted "
	                                       "to the first recurrence on or after the entered date/time."); 
	static const QString tzText = i18nc("@info/plain",
	                                    "This uses KAlarm's default time zone, set in the Configuration dialog.");

	QWidget* topWidget;
	if (title.isEmpty())
		topWidget = this;
	else
	{
		QBoxLayout* layout = new QVBoxLayout(this);
		layout->setMargin(0);
		layout->setSpacing(0);
		topWidget = new QGroupBox(title, this);
		layout->addWidget(topWidget);
	}
	mDeferring = mode & DEFER_TIME;
	mButtonGroup = new ButtonGroup(this);
	connect(mButtonGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(slotButtonSet(QAbstractButton*)));
	QVBoxLayout* topLayout = new QVBoxLayout(topWidget);
	topLayout->setSpacing(KDialog::spacingHint());
	topLayout->setMargin(title.isEmpty() ? 0 : KDialog::marginHint());

	// At time radio button/label
	mAtTimeRadio = new RadioButton((mDeferring ? i18nc("@option:radio", "Defer to date/time:") : i18nc("@option:radio", "At date/time:")), topWidget);
	mAtTimeRadio->setFixedSize(mAtTimeRadio->sizeHint());
	mAtTimeRadio->setWhatsThis(mDeferring ? i18nc("@info:whatsthis", "Reschedule the alarm to the specified date and time.")
	                                      : i18nc("@info:whatsthis", "Specify the date, or date and time, to schedule the alarm."));
	mButtonGroup->addButton(mAtTimeRadio);

	// Date edit box
	mDateEdit = new DateEdit(topWidget);
	connect(mDateEdit, SIGNAL(dateEntered(const QDate&)), SLOT(dateTimeChanged()));
	mDateEdit->setWhatsThis(i18nc("@info:whatsthis",
	      "<para>Enter the date to schedule the alarm.</para>"
	      "<para>%1</para>", (mDeferring ? tzText : recurText)));
	mAtTimeRadio->setFocusWidget(mDateEdit);

	// Time edit box and Any time checkbox
	KHBox* timeBox = new KHBox(topWidget);
	timeBox->setSpacing(2*KDialog::spacingHint());
	mTimeEdit = new TimeEdit(timeBox);
	mTimeEdit->setFixedSize(mTimeEdit->sizeHint());
	connect(mTimeEdit, SIGNAL(valueChanged(int)), SLOT(dateTimeChanged()));
	mTimeEdit->setWhatsThis(i18nc("@info:whatsthis",
	      "<para>Enter the time to schedule the alarm.</para>"
	      "<para>%1</para>"
	      "<para>%2</para>", (mDeferring ? tzText : recurText), TimeSpinBox::shiftWhatsThis()));

	mAnyTime = -1;    // current status is uninitialised
	if (mDeferring)
	{
		mAnyTimeAllowed = false;
		mAnyTimeCheckBox = 0;
	}
	else
	{
		mAnyTimeAllowed = true;
		mAnyTimeCheckBox = new CheckBox(i18nc("@option:check", "Any time"), timeBox);
		mAnyTimeCheckBox->setFixedSize(mAnyTimeCheckBox->sizeHint());
		connect(mAnyTimeCheckBox, SIGNAL(toggled(bool)), SLOT(slotAnyTimeToggled(bool)));
		mAnyTimeCheckBox->setWhatsThis(i18nc("@info:whatsthis",
		      "Check to specify only a date (without a time) for the alarm. The alarm will trigger at the first opportunity on the selected date."));
	}

	// 'Time from now' radio button/label
	mAfterTimeRadio = new RadioButton((mDeferring ? i18nc("@option:radio", "Defer for time interval:") : i18nc("@option:radio", "Time from now:")), topWidget);
	mAfterTimeRadio->setFixedSize(mAfterTimeRadio->sizeHint());
	mAfterTimeRadio->setWhatsThis(mDeferring ? i18nc("@info:whatsthis", "Reschedule the alarm for the specified time interval after now.")
	                                         : i18nc("@info:whatsthis", "Schedule the alarm after the specified time interval from now."));
	mButtonGroup->addButton(mAfterTimeRadio);

	// Delay time spin box
	mDelayTimeEdit = new TimeSpinBox(1, maxDelayTime, topWidget);
	mDelayTimeEdit->setValue(1439);
	mDelayTimeEdit->setFixedSize(mDelayTimeEdit->sizeHint());
	connect(mDelayTimeEdit, SIGNAL(valueChanged(int)), SLOT(delayTimeChanged(int)));
	mDelayTimeEdit->setWhatsThis(mDeferring ? i18nc("@info:whatsthis", "<para>%1</para><para>%2</para>", i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis())
	                                        : i18nc("@info:whatsthis", "<para>%1</para><para>%2</para><para>%3</para>", i18n_TimeAfterPeriod(), recurText, TimeSpinBox::shiftWhatsThis()));
	mAfterTimeRadio->setFocusWidget(mDelayTimeEdit);

	// Set up the layout, either narrow or wide
	QGridLayout* grid = new QGridLayout();
	grid->setMargin(0);
	topLayout->addLayout(grid);
	if (mDeferring)
	{
		grid->addWidget(mAtTimeRadio, 0, 0);
		grid->addWidget(mDateEdit, 0, 1, Qt::AlignLeft);
		grid->addWidget(timeBox, 1, 1, Qt::AlignLeft);
		grid->setColumnStretch(2, 1);
		topLayout->addStretch();
		QHBoxLayout* layout = new QHBoxLayout();
		topLayout->addLayout(layout);
		layout->addWidget(mAfterTimeRadio);
		layout->addWidget(mDelayTimeEdit);
		layout->addStretch();
	}
	else
	{
		grid->addWidget(mAtTimeRadio, 0, 0, Qt::AlignLeft);
		grid->addWidget(mDateEdit, 0, 1, Qt::AlignLeft);
		grid->addWidget(timeBox, 0, 2, Qt::AlignLeft);
		grid->setRowStretch(1, 1);
		grid->addWidget(mAfterTimeRadio, 2, 0, Qt::AlignLeft);
		grid->addWidget(mDelayTimeEdit, 2, 1, Qt::AlignLeft);

		// Time zone selection push button
		mTimeZoneButton = new PushButton(i18nc("@action:button", "Time Zone..."), topWidget);
		connect(mTimeZoneButton, SIGNAL(clicked()), SLOT(showTimeZoneSelector()));
		mTimeZoneButton->setWhatsThis(i18nc("@info:whatsthis",
		      "Choose a time zone for this alarm which is different from the default time zone set in KAlarm's configuration dialog."));
		grid->addWidget(mTimeZoneButton, 2, 2, 1, 2, Qt::AlignRight);

		grid->setColumnStretch(2, 1);
		topLayout->addStretch();

		QHBoxLayout* layout = new QHBoxLayout();
		topLayout->addLayout(layout);
		layout->setSpacing(2*KDialog::spacingHint());

		// Time zone selector
		mTimeZoneBox = new KHBox(topWidget);   // this is to control the QWhatsThis text display area
		mTimeZoneBox->setMargin(0);
		mTimeZoneBox->setSpacing(KDialog::spacingHint());
		QLabel* label = new QLabel(i18nc("@label:listbox", "Time zone:"), mTimeZoneBox);
		mTimeZone = new TimeZoneCombo(mTimeZoneBox);
		mTimeZone->setMaxVisibleItems(15);
		connect(mTimeZone, SIGNAL(activated(int)), SLOT(slotTimeZoneChanged()));
		mTimeZoneBox->setWhatsThis(i18nc("@info:whatsthis", "Select the time zone to use for this alarm."));
		label->setBuddy(mTimeZone);
		layout->addWidget(mTimeZoneBox);

		// Time zone checkbox
		mNoTimeZone = new CheckBox(i18nc("@option:check", "Ignore time zone"), topWidget);
		connect(mNoTimeZone, SIGNAL(toggled(bool)), SLOT(slotTimeZoneToggled(bool)));
		mNoTimeZone->setWhatsThis(i18nc("@info:whatsthis",
		                                "<para>Check to use the local computer time, ignoring time zones.</para>"
		                                "<para>You are recommended not to use this option if the alarm has a "
		                                "recurrence specified in hours/minutes. If you do, the alarm may "
		                                "occur at unexpected times after daylight saving time shifts.</para>"));
		layout->addWidget(mNoTimeZone);
		layout->addStretch();

		// Initially show only the time zone button, not time zone selector
		mTimeZoneBox->hide();
		mNoTimeZone->hide();
	}

	// Initialise the radio button statuses
	mAtTimeRadio->setChecked(true);
	slotButtonSet(mAtTimeRadio);

	// Timeout every minute to update alarm time fields.
	MinuteTimer::connect(this, SLOT(updateTimes()));
}

/******************************************************************************
* Set or clear read-only status for the controls
*/
void AlarmTimeWidget::setReadOnly(bool ro)
{
	mAtTimeRadio->setReadOnly(ro);
	mDateEdit->setReadOnly(ro);
	mTimeEdit->setReadOnly(ro);
	if (mAnyTimeCheckBox)
		mAnyTimeCheckBox->setReadOnly(ro);
	mAfterTimeRadio->setReadOnly(ro);
	if (!mDeferring)
	{
		mTimeZoneButton->setReadOnly(ro);
		mTimeZone->setReadOnly(ro);
		mNoTimeZone->setReadOnly(ro);
	}
	mDelayTimeEdit->setReadOnly(ro);
}

/******************************************************************************
* Select the "Time from now" radio button.
*/
void AlarmTimeWidget::selectTimeFromNow(int minutes)
{
	mAfterTimeRadio->setChecked(true);
	if (minutes > 0)
		mDelayTimeEdit->setValue(minutes);
}

/******************************************************************************
* Fetch the entered date/time.
* If 'checkExpired' is true and the entered value <= current time, an error occurs.
* If 'minsFromNow' is non-null, it is set to the number of minutes' delay selected,
* or to zero if a date/time was entered.
* In this case, if 'showErrorMessage' is true, output an error message.
* 'errorWidget' if non-null, is set to point to the widget containing the error.
* Reply = invalid date/time if error.
*/
KDateTime AlarmTimeWidget::getDateTime(int* minsFromNow, bool checkExpired, bool showErrorMessage, QWidget** errorWidget) const
{
	if (minsFromNow)
		*minsFromNow = 0;
	if (errorWidget)
		*errorWidget = 0;
	KDateTime now = KDateTime::currentUtcDateTime();
	now.setTime(QTime(now.time().hour(), now.time().minute(), 0));
	if (!mAtTimeRadio->isChecked())
	{
		if (!mDelayTimeEdit->isValid())
		{
			if (showErrorMessage)
				KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid time"));
			if (errorWidget)
				*errorWidget = mDelayTimeEdit;
			return KDateTime();
		}
		int delayMins = mDelayTimeEdit->value();
		if (minsFromNow)
			*minsFromNow = delayMins;
		return now.addSecs(delayMins * 60).toTimeSpec(mTimeSpec);
	}
	else
	{
		bool dateOnly = mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked();
		if (!mDateEdit->isValid()  ||  !mTimeEdit->isValid())
		{
			// The date and/or time is invalid
			if (!mDateEdit->isValid())
			{
				if (showErrorMessage)
					KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid date"));
				if (errorWidget)
					*errorWidget = mDateEdit;
			}
			else
			{
				if (showErrorMessage)
					KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid time"));
				if (errorWidget)
					*errorWidget = mTimeEdit;
			}
			return KDateTime();
		}

		KDateTime result;
		if (dateOnly)
		{
			result = KDateTime(mDateEdit->date(), mTimeSpec);
			if (checkExpired  &&  result.date() < now.date())
			{
				if (showErrorMessage)
					KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Alarm date has already expired"));
				if (errorWidget)
					*errorWidget = mDateEdit;
				return KDateTime();
			}
		}
		else
		{
			result = KDateTime(mDateEdit->date(), mTimeEdit->time(), mTimeSpec);
			if (checkExpired  &&  result <= now.addSecs(1))
			{
				if (showErrorMessage)
					KMessageBox::sorry(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Alarm time has already expired"));
				if (errorWidget)
					*errorWidget = mTimeEdit;
				return KDateTime();
			}
		}
		return result;
	}
}

/******************************************************************************
* Set the date/time.
*/
void AlarmTimeWidget::setDateTime(const DateTime& dt)
{
	// Set the time zone first so that the call to dateTimeChanged() works correctly.
	if (mDeferring)
		mTimeSpec = dt.timeSpec().isValid() ? dt.timeSpec() : KDateTime::LocalZone;
	else
	{
		KTimeZone tz = dt.timeZone();
		mNoTimeZone->setChecked(!tz.isValid());
		mTimeZone->setTimeZone(tz.isValid() ? tz : Preferences::timeZone());
		slotTimeZoneChanged();
	}

	if (dt.date().isValid())
	{
		mTimeEdit->setValue(dt.effectiveTime());
		mDateEdit->setDate(dt.date());
		dateTimeChanged();     // update the delay time edit box
	}
	else
	{
		mTimeEdit->setValid(false);
		mDateEdit->setInvalid();
		mDelayTimeEdit->setValid(false);
	}
	if (mAnyTimeCheckBox)
	{
		bool dateOnly = dt.isDateOnly();
		if (dateOnly)
			mAnyTimeAllowed = true;
		mAnyTimeCheckBox->setChecked(dateOnly);
		setAnyTime();
	}
}

/******************************************************************************
* Set the minimum date/time to track the current time.
*/
void AlarmTimeWidget::setMinDateTimeIsCurrent()
{
	mMinDateTimeIsNow = true;
	mMinDateTime = KDateTime();
	KDateTime now = KDateTime::currentDateTime(mTimeSpec);
	mDateEdit->setMinDate(now.date());
	setMaxMinTimeIf(now);
}

/******************************************************************************
* Set the minimum date/time, adjusting the entered date/time if necessary.
* If 'dt' is invalid, any current minimum date/time is cleared.
*/
void AlarmTimeWidget::setMinDateTime(const KDateTime& dt)
{
	mMinDateTimeIsNow = false;
	mMinDateTime = dt.toTimeSpec(mTimeSpec);
	mDateEdit->setMinDate(mMinDateTime.date());
	setMaxMinTimeIf(KDateTime::currentDateTime(mTimeSpec));
}

/******************************************************************************
* Set the maximum date/time, adjusting the entered date/time if necessary.
* If 'dt' is invalid, any current maximum date/time is cleared.
*/
void AlarmTimeWidget::setMaxDateTime(const DateTime& dt)
{
	mPastMax = false;
	if (dt.isValid()  &&  dt.isDateOnly())
		mMaxDateTime = dt.effectiveKDateTime().addSecs(24*3600 - 60).toTimeSpec(mTimeSpec);
	else
		mMaxDateTime = dt.kDateTime().toTimeSpec(mTimeSpec);
	mDateEdit->setMaxDate(mMaxDateTime.date());
	KDateTime now = KDateTime::currentDateTime(mTimeSpec);
	setMaxMinTimeIf(now);
	setMaxDelayTime(now);
}

/******************************************************************************
* If the minimum and maximum date/times fall on the same date, set the minimum
* and maximum times in the time edit box.
*/
void AlarmTimeWidget::setMaxMinTimeIf(const KDateTime& now)
{
	int   mint = 0;
	QTime maxt = time_23_59;
	mMinMaxTimeSet = false;
	if (mMaxDateTime.isValid())
	{
		bool set = true;
		KDateTime minDT;
		if (mMinDateTimeIsNow)
			minDT = now.addSecs(60);
		else if (mMinDateTime.isValid())
			minDT = mMinDateTime;
		else
			set = false;
		if (set  &&  mMaxDateTime.date() == minDT.date())
		{
			// The minimum and maximum times are on the same date, so
			// constrain the time value.
			mint = minDT.time().hour()*60 + minDT.time().minute();
			maxt = mMaxDateTime.time();
			mMinMaxTimeSet = true;
		}
	}
	mTimeEdit->setMinimum(mint);
	mTimeEdit->setMaximum(maxt);
	mTimeEdit->setWrapping(!mint  &&  maxt == time_23_59);
}

/******************************************************************************
* Set the maximum value for the delay time edit box, depending on the maximum
* value for the date/time.
*/
void AlarmTimeWidget::setMaxDelayTime(const KDateTime& now)
{
	int maxVal = maxDelayTime;
	if (mMaxDateTime.isValid())
	{
		if (now.date().daysTo(mMaxDateTime.date()) < 100)    // avoid possible 32-bit overflow on secsTo()
		{
			KDateTime dt(now);
			dt.setTime(QTime(now.time().hour(), now.time().minute(), 0));   // round down to nearest minute
			maxVal = dt.secsTo(mMaxDateTime) / 60;
			if (maxVal > maxDelayTime)
				maxVal = maxDelayTime;
		}
	}
	mDelayTimeEdit->setMaximum(maxVal);
}

/******************************************************************************
* Set the status for whether a time is specified, or just a date.
*/
void AlarmTimeWidget::setAnyTime()
{
	int old = mAnyTime;
	mAnyTime = (mAtTimeRadio->isChecked() && mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked()) ? 1 : 0;
	if (mAnyTime != old)
		emit dateOnlyToggled(mAnyTime);
}

/******************************************************************************
* Enable/disable the "date only" radio button.
*/
void AlarmTimeWidget::enableAnyTime(bool enable)
{
	if (mAnyTimeCheckBox)
	{
		mAnyTimeAllowed = enable;
		bool at = mAtTimeRadio->isChecked();
		mAnyTimeCheckBox->setEnabled(enable && at);
		if (at)
			mTimeEdit->setEnabled(!enable || !mAnyTimeCheckBox->isChecked());
		setAnyTime();
	}
}

/******************************************************************************
* Called every minute to update the alarm time data entry fields.
* If the maximum date/time has been reached, a 'pastMax()' signal is emitted.
*/
void AlarmTimeWidget::updateTimes()
{
	KDateTime now;
	if (mMinDateTimeIsNow)
	{
		// Make sure that the minimum date is updated when the day changes
		now = KDateTime::currentDateTime(mTimeSpec);
		mDateEdit->setMinDate(now.date());
	}
	if (mMaxDateTime.isValid())
	{
		if (!now.isValid())
			now = KDateTime::currentDateTime(mTimeSpec);
		if (!mPastMax)
		{
			// Check whether the maximum date/time has now been reached
			if (now.date() >= mMaxDateTime.date())
			{
				// The current date has reached or has passed the maximum date
				if (now.date() > mMaxDateTime.date()
				||  (!mAnyTime && now.time() > mTimeEdit->maxTime()))
				{
					mPastMax = true;
					emit pastMax();
				}
				else if (mMinDateTimeIsNow  &&  !mMinMaxTimeSet)
				{
					// The minimum date/time tracks the clock, so set the minimum
					// and maximum times
					setMaxMinTimeIf(now);
				}
			}
		}
		setMaxDelayTime(now);
	}

	if (mAtTimeRadio->isChecked())
		dateTimeChanged();
	else
		delayTimeChanged(mDelayTimeEdit->value());
}


/******************************************************************************
* Called when the radio button states have been changed.
* Updates the appropriate edit box.
*/
void AlarmTimeWidget::slotButtonSet(QAbstractButton*)
{
	bool at = mAtTimeRadio->isChecked();
	mDateEdit->setEnabled(at);
	mTimeEdit->setEnabled(at && (!mAnyTimeAllowed || !mAnyTimeCheckBox || !mAnyTimeCheckBox->isChecked()));
	if (mAnyTimeCheckBox)
		mAnyTimeCheckBox->setEnabled(at && mAnyTimeAllowed);
	// Ensure that the value of the delay edit box is > 0.
	KDateTime att(mDateEdit->date(), mTimeEdit->time(), mTimeSpec);
	int minutes = (KDateTime::currentUtcDateTime().secsTo(att) + 59) / 60;
	if (minutes <= 0)
		mDelayTimeEdit->setValid(true);
	mDelayTimeEdit->setEnabled(!at);
	setAnyTime();
}

/******************************************************************************
* Called after the mAnyTimeCheckBox checkbox has been toggled.
*/
void AlarmTimeWidget::slotAnyTimeToggled(bool on)
{
	on = (on && mAnyTimeAllowed);
	mTimeEdit->setEnabled(!on && mAtTimeRadio->isChecked());
	setAnyTime();
	if (on)
		emit changed(KDateTime(mDateEdit->date(), mTimeSpec));
	else
		emit changed(KDateTime(mDateEdit->date(), mTimeEdit->time(), mTimeSpec));
}

/******************************************************************************
* Called after a new selection has been made in the time zone combo box.
* Re-evaluates the time specification to use.
*/
void AlarmTimeWidget::slotTimeZoneChanged()
{
	if (mNoTimeZone->isChecked())
		mTimeSpec = KDateTime::ClockTime;
	else
	{
		KTimeZone tz = mTimeZone->timeZone();
		mTimeSpec = tz.isValid() ? KDateTime::Spec(tz) : KDateTime::LocalZone;
	}
	if (!mTimeZoneBox->isVisible()  &&  mTimeSpec != Preferences::timeZone())
	{
		// The current time zone is not the default one, so
		// show the time zone selection controls
		showTimeZoneSelector();
	}
	mMinDateTime = mMinDateTime.toTimeSpec(mTimeSpec);
	mMaxDateTime = mMaxDateTime.toTimeSpec(mTimeSpec);
	updateTimes();
}

/******************************************************************************
* Called after the mNoTimeZone checkbox has been toggled.
*/
void AlarmTimeWidget::slotTimeZoneToggled(bool on)
{
	mTimeZone->setEnabled(!on);
	slotTimeZoneChanged();
}

/******************************************************************************
* Called after the mTimeZoneButton button has been clicked.
* Show the time zone selection controls, and hide the button.
*/
void AlarmTimeWidget::showTimeZoneSelector()
{
	mTimeZoneButton->hide();
	mTimeZoneBox->show();
	mNoTimeZone->show();
}

/******************************************************************************
* Show or hide the time zone button.
*/
void AlarmTimeWidget::showMoreOptions(bool more)
{
	if (more)
	{
		if (!mTimeZoneBox->isVisible())
			mTimeZoneButton->show();
	}
	else
		mTimeZoneButton->hide();
}

/******************************************************************************
* Called when the date or time edit box values have changed.
* Updates the time delay edit box accordingly.
*/
void AlarmTimeWidget::dateTimeChanged()
{
	KDateTime dt(mDateEdit->date(), mTimeEdit->time(), mTimeSpec);
	int minutes = (KDateTime::currentUtcDateTime().secsTo(dt) + 59) / 60;
	bool blocked = mDelayTimeEdit->signalsBlocked();
	mDelayTimeEdit->blockSignals(true);     // prevent infinite recursion between here and delayTimeChanged()
	if (minutes <= 0  ||  minutes > mDelayTimeEdit->maximum())
		mDelayTimeEdit->setValid(false);
	else
		mDelayTimeEdit->setValue(minutes);
	mDelayTimeEdit->blockSignals(blocked);
	if (mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked())
		emit changed(KDateTime(dt.date(), mTimeSpec));
	else
		emit changed(dt);
}

/******************************************************************************
* Called when the delay time edit box value has changed.
* Updates the Date and Time edit boxes accordingly.
*/
void AlarmTimeWidget::delayTimeChanged(int minutes)
{
	if (mDelayTimeEdit->isValid())
	{
		QDateTime dt = KDateTime::currentUtcDateTime().addSecs(minutes * 60).toTimeSpec(mTimeSpec).dateTime();
		bool blockedT = mTimeEdit->signalsBlocked();
		bool blockedD = mDateEdit->signalsBlocked();
		mTimeEdit->blockSignals(true);     // prevent infinite recursion between here and dateTimeChanged()
		mDateEdit->blockSignals(true);
		mTimeEdit->setValue(dt.time());
		mDateEdit->setDate(dt.date());
		mTimeEdit->blockSignals(blockedT);
		mDateEdit->blockSignals(blockedD);
		emit changed(KDateTime(dt.date(), dt.time(), mTimeSpec));
	}
}
