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/recurrenceedit.cpp

1733 lines
58 KiB

/*
* recurrenceedit.cpp - widget to edit the event's recurrence definition
* Program: kalarm
* Copyright © 2002-2008 by David Jarvie <djarvie@kde.org>
*
* Based originally on KOrganizer module koeditorrecurrence.cpp,
* Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@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 <tqtooltip.h>
#include <tqlayout.h>
#include <tqvbox.h>
#include <tqwidgetstack.h>
#include <tqlistbox.h>
#include <tqframe.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqlineedit.h>
#include <tqwhatsthis.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kcalendarsystem.h>
#include <kiconloader.h>
#include <kdialog.h>
#include <tdemessagebox.h>
#include <kdebug.h>
#include <libkcal/event.h>
#include "alarmevent.h"
#include "alarmtimewidget.h"
#include "checkbox.h"
#include "combobox.h"
#include "dateedit.h"
#include "functions.h"
#include "kalarmapp.h"
#include "karecurrence.h"
#include "preferences.h"
#include "radiobutton.h"
#include "repetition.h"
#include "spinbox.h"
#include "timeedit.h"
#include "timespinbox.h"
#include "buttongroup.h"
using namespace KCal;
#include "recurrenceedit.moc"
#include "recurrenceeditprivate.moc"
// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
TQString RecurrenceEdit::i18n_Norecur() { return i18n("No recurrence"); }
TQString RecurrenceEdit::i18n_NoRecur() { return i18n("No Recurrence"); }
TQString RecurrenceEdit::i18n_AtLogin() { return i18n("At Login"); }
TQString RecurrenceEdit::i18n_l_Atlogin() { return i18n("At &login"); }
TQString RecurrenceEdit::i18n_HourlyMinutely() { return i18n("Hourly/Minutely"); }
TQString RecurrenceEdit::i18n_u_HourlyMinutely() { return i18n("Ho&urly/Minutely"); }
TQString RecurrenceEdit::i18n_Daily() { return i18n("Daily"); }
TQString RecurrenceEdit::i18n_d_Daily() { return i18n("&Daily"); }
TQString RecurrenceEdit::i18n_Weekly() { return i18n("Weekly"); }
TQString RecurrenceEdit::i18n_w_Weekly() { return i18n("&Weekly"); }
TQString RecurrenceEdit::i18n_Monthly() { return i18n("Monthly"); }
TQString RecurrenceEdit::i18n_m_Monthly() { return i18n("&Monthly"); }
TQString RecurrenceEdit::i18n_Yearly() { return i18n("Yearly"); }
TQString RecurrenceEdit::i18n_y_Yearly() { return i18n("&Yearly"); }
RecurrenceEdit::RecurrenceEdit(bool readOnly, TQWidget* parent, const char* name)
: TQFrame(parent, name),
mRule(0),
mRuleButtonType(INVALID_RECUR),
mDailyShown(false),
mWeeklyShown(false),
mMonthlyShown(false),
mYearlyShown(false),
mNoEmitTypeChanged(true),
mReadOnly(readOnly)
{
TQBoxLayout* layout;
TQVBoxLayout* topLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
/* Create the recurrence rule Group box which holds the recurrence period
* selection buttons, and the weekly, monthly and yearly recurrence rule
* frames which specify options individual to each of these distinct
* sections of the recurrence rule. Each frame is made visible by the
* selection of its corresponding radio button.
*/
TQGroupBox* recurGroup = new TQGroupBox(1, TQt::Vertical, i18n("Recurrence Rule"), this, "recurGroup");
topLayout->addWidget(recurGroup);
TQFrame* ruleFrame = new TQFrame(recurGroup, "ruleFrame");
layout = new TQVBoxLayout(ruleFrame, 0);
layout->addSpacing(KDialog::spacingHint()/2);
layout = new TQHBoxLayout(layout, 0);
TQBoxLayout* lay = new TQVBoxLayout(layout, 0);
mRuleButtonGroup = new ButtonGroup(1, TQt::Horizontal, ruleFrame);
mRuleButtonGroup->setInsideMargin(0);
mRuleButtonGroup->setFrameStyle(TQFrame::NoFrame);
lay->addWidget(mRuleButtonGroup);
lay->addStretch(); // top-adjust the interval radio buttons
connect(mRuleButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(periodClicked(int)));
mNoneButton = new RadioButton(i18n_Norecur(), mRuleButtonGroup);
mNoneButton->setFixedSize(mNoneButton->sizeHint());
mNoneButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mNoneButton, i18n("Do not repeat the alarm"));
mAtLoginButton = new RadioButton(i18n_l_Atlogin(), mRuleButtonGroup);
mAtLoginButton->setFixedSize(mAtLoginButton->sizeHint());
mAtLoginButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mAtLoginButton,
i18n("Trigger the alarm at the specified date/time and at every login until then.\n"
"Note that it will also be triggered any time the alarm daemon is restarted."));
mSubDailyButton = new RadioButton(i18n_u_HourlyMinutely(), mRuleButtonGroup);
mSubDailyButton->setFixedSize(mSubDailyButton->sizeHint());
mSubDailyButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mSubDailyButton,
i18n("Repeat the alarm at hourly/minutely intervals"));
mDailyButton = new RadioButton(i18n_d_Daily(), mRuleButtonGroup);
mDailyButton->setFixedSize(mDailyButton->sizeHint());
mDailyButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mDailyButton,
i18n("Repeat the alarm at daily intervals"));
mWeeklyButton = new RadioButton(i18n_w_Weekly(), mRuleButtonGroup);
mWeeklyButton->setFixedSize(mWeeklyButton->sizeHint());
mWeeklyButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mWeeklyButton,
i18n("Repeat the alarm at weekly intervals"));
mMonthlyButton = new RadioButton(i18n_m_Monthly(), mRuleButtonGroup);
mMonthlyButton->setFixedSize(mMonthlyButton->sizeHint());
mMonthlyButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mMonthlyButton,
i18n("Repeat the alarm at monthly intervals"));
mYearlyButton = new RadioButton(i18n_y_Yearly(), mRuleButtonGroup);
mYearlyButton->setFixedSize(mYearlyButton->sizeHint());
mYearlyButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mYearlyButton,
i18n("Repeat the alarm at annual intervals"));
mNoneButtonId = mRuleButtonGroup->id(mNoneButton);
mAtLoginButtonId = mRuleButtonGroup->id(mAtLoginButton);
mSubDailyButtonId = mRuleButtonGroup->id(mSubDailyButton);
mDailyButtonId = mRuleButtonGroup->id(mDailyButton);
mWeeklyButtonId = mRuleButtonGroup->id(mWeeklyButton);
mMonthlyButtonId = mRuleButtonGroup->id(mMonthlyButton);
mYearlyButtonId = mRuleButtonGroup->id(mYearlyButton);
// Sub-repetition button
mSubRepetition = new RepetitionButton(i18n("Sub-Repetition"), true, ruleFrame);
mSubRepetition->setFixedSize(mSubRepetition->sizeHint());
mSubRepetition->setReadOnly(mReadOnly);
connect(mSubRepetition, TQ_SIGNAL(needsInitialisation()), TQ_SIGNAL(repeatNeedsInitialisation()));
connect(mSubRepetition, TQ_SIGNAL(changed()), TQ_SIGNAL(frequencyChanged()));
TQWhatsThis::add(mSubRepetition, i18n("Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due."));
lay->addSpacing(KDialog::spacingHint());
lay->addWidget(mSubRepetition);
lay = new TQVBoxLayout(layout);
lay->addStretch();
layout = new TQHBoxLayout(lay);
layout->addSpacing(KDialog::marginHint());
TQFrame* divider = new TQFrame(ruleFrame);
divider->setFrameStyle(TQFrame::VLine | TQFrame::Sunken);
layout->addWidget(divider);
layout->addSpacing(KDialog::marginHint());
mNoRule = new NoRule(ruleFrame, "noFrame");
mSubDailyRule = new SubDailyRule(mReadOnly, ruleFrame, "subdayFrame");
mDailyRule = new DailyRule(mReadOnly, ruleFrame, "dayFrame");
mWeeklyRule = new WeeklyRule(mReadOnly, ruleFrame, "weekFrame");
mMonthlyRule = new MonthlyRule(mReadOnly, ruleFrame, "monthFrame");
mYearlyRule = new YearlyRule(mReadOnly, ruleFrame, "yearFrame");
connect(mSubDailyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
connect(mDailyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
connect(mWeeklyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
connect(mMonthlyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
connect(mYearlyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
mRuleStack = new TQWidgetStack(ruleFrame);
layout->addWidget(mRuleStack);
layout->addStretch(1);
mRuleStack->addWidget(mNoRule, 0);
mRuleStack->addWidget(mSubDailyRule, 1);
mRuleStack->addWidget(mDailyRule, 2);
mRuleStack->addWidget(mWeeklyRule, 3);
mRuleStack->addWidget(mMonthlyRule, 4);
mRuleStack->addWidget(mYearlyRule, 5);
layout->addSpacing(KDialog::marginHint());
// Create the recurrence range group which contains the controls
// which specify how long the recurrence is to last.
mRangeButtonGroup = new ButtonGroup(i18n("Recurrence End"), this, "mRangeButtonGroup");
connect(mRangeButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(rangeTypeClicked()));
topLayout->addWidget(mRangeButtonGroup);
TQVBoxLayout* vlayout = new TQVBoxLayout(mRangeButtonGroup, KDialog::marginHint(), KDialog::spacingHint());
vlayout->addSpacing(fontMetrics().lineSpacing()/2);
mNoEndDateButton = new RadioButton(i18n("No &end"), mRangeButtonGroup);
mNoEndDateButton->setFixedSize(mNoEndDateButton->sizeHint());
mNoEndDateButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mNoEndDateButton, i18n("Repeat the alarm indefinitely"));
vlayout->addWidget(mNoEndDateButton, 1, TQt::AlignAuto);
TQSize size = mNoEndDateButton->size();
layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
mRepeatCountButton = new RadioButton(i18n("End a&fter:"), mRangeButtonGroup);
mRepeatCountButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mRepeatCountButton,
i18n("Repeat the alarm for the number of times specified"));
mRepeatCountEntry = new SpinBox(1, 9999, 1, mRangeButtonGroup);
mRepeatCountEntry->setFixedSize(mRepeatCountEntry->sizeHint());
mRepeatCountEntry->setLineShiftStep(10);
mRepeatCountEntry->setSelectOnStep(false);
mRepeatCountEntry->setReadOnly(mReadOnly);
connect(mRepeatCountEntry, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(repeatCountChanged(int)));
TQWhatsThis::add(mRepeatCountEntry,
i18n("Enter the total number of times to trigger the alarm"));
mRepeatCountButton->setFocusWidget(mRepeatCountEntry);
mRepeatCountLabel = new TQLabel(i18n("occurrence(s)"), mRangeButtonGroup);
mRepeatCountLabel->setFixedSize(mRepeatCountLabel->sizeHint());
layout->addWidget(mRepeatCountButton);
layout->addSpacing(KDialog::spacingHint());
layout->addWidget(mRepeatCountEntry);
layout->addWidget(mRepeatCountLabel);
layout->addStretch();
size = size.expandedTo(mRepeatCountButton->sizeHint());
layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
mEndDateButton = new RadioButton(i18n("End &by:"), mRangeButtonGroup);
mEndDateButton->setReadOnly(mReadOnly);
TQWhatsThis::add(mEndDateButton,
i18n("Repeat the alarm until the date/time specified.\n\n"
"Note: This applies to the main recurrence only. It does not limit any sub-repetition which will occur regardless after the last main recurrence."));
mEndDateEdit = new DateEdit(mRangeButtonGroup);
mEndDateEdit->setFixedSize(mEndDateEdit->sizeHint());
mEndDateEdit->setReadOnly(mReadOnly);
TQWhatsThis::add(mEndDateEdit,
i18n("Enter the last date to repeat the alarm"));
mEndDateButton->setFocusWidget(mEndDateEdit);
mEndTimeEdit = new TimeEdit(mRangeButtonGroup);
mEndTimeEdit->setFixedSize(mEndTimeEdit->sizeHint());
mEndTimeEdit->setReadOnly(mReadOnly);
static const TQString lastTimeText = i18n("Enter the last time to repeat the alarm.");
TQWhatsThis::add(mEndTimeEdit, TQString("%1\n\n%2").arg(lastTimeText).arg(TimeSpinBox::shiftWhatsThis()));
mEndAnyTimeCheckBox = new CheckBox(i18n("Any time"), mRangeButtonGroup);
mEndAnyTimeCheckBox->setFixedSize(mEndAnyTimeCheckBox->sizeHint());
mEndAnyTimeCheckBox->setReadOnly(mReadOnly);
connect(mEndAnyTimeCheckBox, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotAnyTimeToggled(bool)));
TQWhatsThis::add(mEndAnyTimeCheckBox,
i18n("Stop repeating the alarm after your first login on or after the specified end date"));
layout->addWidget(mEndDateButton);
layout->addSpacing(KDialog::spacingHint());
layout->addWidget(mEndDateEdit);
layout->addWidget(mEndTimeEdit);
layout->addWidget(mEndAnyTimeCheckBox);
layout->addStretch();
size = size.expandedTo(mEndDateButton->sizeHint());
// Line up the widgets to the right of the radio buttons
mRepeatCountButton->setFixedSize(size);
mEndDateButton->setFixedSize(size);
// Create the exceptions group which specifies dates to be excluded
// from the recurrence.
mExceptionGroup = new TQGroupBox(i18n("E&xceptions"), this, "mExceptionGroup");
topLayout->addWidget(mExceptionGroup);
topLayout->setStretchFactor(mExceptionGroup, 2);
vlayout = new TQVBoxLayout(mExceptionGroup, KDialog::marginHint(), KDialog::spacingHint());
vlayout->addSpacing(fontMetrics().lineSpacing()/2);
layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
vlayout = new TQVBoxLayout(layout);
mExceptionDateList = new TQListBox(mExceptionGroup);
mExceptionDateList->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding));
connect(mExceptionDateList, TQ_SIGNAL(selectionChanged()), TQ_SLOT(enableExceptionButtons()));
TQWhatsThis::add(mExceptionDateList,
i18n("The list of exceptions, i.e. dates/times excluded from the recurrence"));
vlayout->addWidget(mExceptionDateList);
if (mReadOnly)
{
mExceptionDateEdit = 0;
mChangeExceptionButton = 0;
mDeleteExceptionButton = 0;
}
else
{
vlayout = new TQVBoxLayout(layout);
mExceptionDateEdit = new DateEdit(mExceptionGroup);
mExceptionDateEdit->setFixedSize(mExceptionDateEdit->sizeHint());
mExceptionDateEdit->setDate(TQDate::currentDate());
TQWhatsThis::add(mExceptionDateEdit,
i18n("Enter a date to insert in the exceptions list. "
"Use in conjunction with the Add or Change button below."));
vlayout->addWidget(mExceptionDateEdit);
layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
TQPushButton* button = new TQPushButton(i18n("Add"), mExceptionGroup);
button->setFixedSize(button->sizeHint());
connect(button, TQ_SIGNAL(clicked()), TQ_SLOT(addException()));
TQWhatsThis::add(button,
i18n("Add the date entered above to the exceptions list"));
layout->addWidget(button);
mChangeExceptionButton = new TQPushButton(i18n("Change"), mExceptionGroup);
mChangeExceptionButton->setFixedSize(mChangeExceptionButton->sizeHint());
connect(mChangeExceptionButton, TQ_SIGNAL(clicked()), TQ_SLOT(changeException()));
TQWhatsThis::add(mChangeExceptionButton,
i18n("Replace the currently highlighted item in the exceptions list with the date entered above"));
layout->addWidget(mChangeExceptionButton);
mDeleteExceptionButton = new TQPushButton(i18n("Delete"), mExceptionGroup);
mDeleteExceptionButton->setFixedSize(mDeleteExceptionButton->sizeHint());
connect(mDeleteExceptionButton, TQ_SIGNAL(clicked()), TQ_SLOT(deleteException()));
TQWhatsThis::add(mDeleteExceptionButton,
i18n("Remove the currently highlighted item from the exceptions list"));
layout->addWidget(mDeleteExceptionButton);
}
mNoEmitTypeChanged = false;
}
/******************************************************************************
* Verify the consistency of the entered data.
* Reply = widget to receive focus on error, or 0 if no error.
*/
TQWidget* RecurrenceEdit::checkData(const TQDateTime& startDateTime, TQString& errorMessage) const
{
if (mAtLoginButton->isOn())
return 0;
const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime;
if (mEndDateButton->isChecked())
{
TQWidget* errWidget = 0;
bool noTime = !mEndTimeEdit->isEnabled();
TQDate endDate = mEndDateEdit->date();
if (endDate < startDateTime.date())
errWidget = mEndDateEdit;
else if (!noTime && TQDateTime(endDate, mEndTimeEdit->time()) < startDateTime)
errWidget = mEndTimeEdit;
if (errWidget)
{
errorMessage = noTime
? i18n("End date is earlier than start date")
: i18n("End date/time is earlier than start date/time");
return errWidget;
}
}
if (!mRule)
return 0;
return mRule->validate(errorMessage);
}
/******************************************************************************
* Called when a recurrence period radio button is clicked.
*/
void RecurrenceEdit::periodClicked(int id)
{
RepeatType oldType = mRuleButtonType;
bool none = (id == mNoneButtonId);
bool atLogin = (id == mAtLoginButtonId);
bool subdaily = (id == mSubDailyButtonId);
if (none)
{
mRule = 0;
mRuleButtonType = NO_RECUR;
}
else if (atLogin)
{
mRule = 0;
mRuleButtonType = AT_LOGIN;
mRangeButtonGroup->setButton(mRangeButtonGroup->id(mEndDateButton));
}
else if (subdaily)
{
mRule = mSubDailyRule;
mRuleButtonType = SUBDAILY;
}
else if (id == mDailyButtonId)
{
mRule = mDailyRule;
mRuleButtonType = DAILY;
mDailyShown = true;
}
else if (id == mWeeklyButtonId)
{
mRule = mWeeklyRule;
mRuleButtonType = WEEKLY;
mWeeklyShown = true;
}
else if (id == mMonthlyButtonId)
{
mRule = mMonthlyRule;
mRuleButtonType = MONTHLY;
mMonthlyShown = true;
}
else if (id == mYearlyButtonId)
{
mRule = mYearlyRule;
mRuleButtonType = ANNUAL;
mYearlyShown = true;
}
else
return;
if (mRuleButtonType != oldType)
{
mRuleStack->raiseWidget(mRule ? mRule : mNoRule);
if (oldType == NO_RECUR || none)
mRangeButtonGroup->setEnabled(!none);
mExceptionGroup->setEnabled(!(none || atLogin));
mEndAnyTimeCheckBox->setEnabled(atLogin);
if (!none)
{
mNoEndDateButton->setEnabled(!atLogin);
mRepeatCountButton->setEnabled(!atLogin);
}
rangeTypeClicked();
mSubRepetition->setEnabled(!(none || atLogin));
if (!mNoEmitTypeChanged)
emit typeChanged(mRuleButtonType);
}
}
void RecurrenceEdit::slotAnyTimeToggled(bool on)
{
TQButton* button = mRuleButtonGroup->selected();
mEndTimeEdit->setEnabled((button == mAtLoginButton && !on)
|| (button == mSubDailyButton && mEndDateButton->isChecked()));
}
/******************************************************************************
* Called when a recurrence range type radio button is clicked.
*/
void RecurrenceEdit::rangeTypeClicked()
{
bool endDate = mEndDateButton->isOn();
mEndDateEdit->setEnabled(endDate);
mEndTimeEdit->setEnabled(endDate
&& ((mAtLoginButton->isOn() && !mEndAnyTimeCheckBox->isChecked())
|| mSubDailyButton->isOn()));
bool repeatCount = mRepeatCountButton->isOn();
mRepeatCountEntry->setEnabled(repeatCount);
mRepeatCountLabel->setEnabled(repeatCount);
}
void RecurrenceEdit::showEvent(TQShowEvent*)
{
if (mRule)
mRule->setFrequencyFocus();
else
mRuleButtonGroup->selected()->setFocus();
emit shown();
}
/******************************************************************************
* Return the sub-repetition count within the recurrence, i.e. the number of
* repetitions after the main recurrence.
*/
int RecurrenceEdit::subRepeatCount(int* subRepeatInterval) const
{
int count = (mRuleButtonType >= SUBDAILY) ? mSubRepetition->count() : 0;
if (subRepeatInterval)
*subRepeatInterval = count ? mSubRepetition->interval() : 0;
return count;
}
/******************************************************************************
* Called when the Sub-Repetition button has been pressed to display the
* sub-repetition dialog.
* Alarm repetition has the following restrictions:
* 1) Not allowed for a repeat-at-login alarm
* 2) For a date-only alarm, the repeat interval must be a whole number of days.
* 3) The overall repeat duration must be less than the recurrence interval.
*/
void RecurrenceEdit::setSubRepetition(int reminderMinutes, bool dateOnly)
{
int maxDuration;
switch (mRuleButtonType)
{
case RecurrenceEdit::NO_RECUR:
case RecurrenceEdit::AT_LOGIN: // alarm repeat not allowed
maxDuration = 0;
break;
default: // repeat duration must be less than recurrence interval
{
KAEvent event;
updateEvent(event, false);
maxDuration = event.longestRecurrenceInterval() - reminderMinutes - 1;
break;
}
}
mSubRepetition->initialise(mSubRepetition->interval(), mSubRepetition->count(), dateOnly, maxDuration);
mSubRepetition->setEnabled(mRuleButtonType >= SUBDAILY && maxDuration);
}
/******************************************************************************
* Activate the sub-repetition dialog.
*/
void RecurrenceEdit::activateSubRepetition()
{
mSubRepetition->activate();
}
/******************************************************************************
* For weekly, monthly and yearly recurrence, checks that the specified date
* matches the days allowed. For the other recurrence simply return true.
*/
bool RecurrenceEdit::validateDate(const DateTime &date) const
{
if (mRuleButtonType == RecurrenceEdit::DAILY)
{
TQBitArray selectedDays = mDailyRule->days();
if (!selectedDays[date.date().dayOfWeek()-1])
return false;
}
else if (mRuleButtonType == RecurrenceEdit::WEEKLY)
{
TQBitArray selectedDays = mWeeklyRule->days();
if (!selectedDays[date.date().dayOfWeek()-1])
return false;
}
else if (mRuleButtonType == RecurrenceEdit::MONTHLY)
{
if (mMonthlyRule->type() == MonthYearRule::DATE)
{
// on the nth day of the month
int comboDate = mMonthlyRule->date();
if ((comboDate > 0 && date.date().day() != comboDate) ||
(comboDate <=0 && date.date().day() != date.date().daysInMonth()))
return false;
}
else
{
// on the nth weekday (i.e. Monday) of the month
if (date.date().dayOfWeek() != mMonthlyRule->dayOfWeek())
return false;
int monthDay = date.date().day();
int weekNum = mMonthlyRule->week();
int minDay = 0, maxDay = 0;
if (weekNum > 0 )
{
minDay = (weekNum-1) * 7;
maxDay = weekNum * 7;
}
else if (weekNum < 0)
{
int dim = date.date().daysInMonth();
minDay = dim + weekNum * 7;
maxDay = dim + (weekNum+1) * 7;
}
if (monthDay <= minDay || monthDay > maxDay)
return false;
}
}
else if (mRuleButtonType == RecurrenceEdit::ANNUAL)
{
TQValueList<int> months = mYearlyRule->months();
if (!months.contains(date.date().month()))
return false;
if (mYearlyRule->type() == MonthYearRule::DATE)
{
// on the nth day of the month
int comboDate = mYearlyRule->date();
if ((comboDate > 0 && date.date().day() != comboDate) ||
(comboDate <=0 && date.date().day() != date.date().daysInMonth()))
return false;
}
else
{
// on the nth weekday (i.e. Monday) of the month
if (date.date().dayOfWeek() != mYearlyRule->dayOfWeek())
return false;
int monthDay = date.date().day();
int weekNum = mYearlyRule->week();
int minDay = 0, maxDay = 0;
if (weekNum > 0 )
{
minDay = (weekNum-1) * 7;
maxDay = weekNum * 7;
}
else if (weekNum < 0)
{
int dim = date.date().daysInMonth();
minDay = dim + weekNum * 7;
maxDay = dim + (weekNum+1) * 7;
}
if (monthDay <= minDay || monthDay > maxDay)
return false;
}
}
return true;
}
/******************************************************************************
* Called when the value of the repeat count field changes, to reset the
* minimum value to 1 if the value was 0.
*/
void RecurrenceEdit::repeatCountChanged(int value)
{
if (value > 0 && mRepeatCountEntry->minValue() == 0)
mRepeatCountEntry->setMinValue(1);
}
/******************************************************************************
* Add the date entered in the exception date edit control to the list of
* exception dates.
*/
void RecurrenceEdit::addException()
{
if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
return;
TQDate date = mExceptionDateEdit->date();
TQValueList<TQDate>::Iterator it;
int index = 0;
bool insert = true;
for (it = mExceptionDates.begin(); it != mExceptionDates.end(); ++index, ++it)
{
if (date <= *it)
{
insert = (date != *it);
break;
}
}
if (insert)
{
mExceptionDates.insert(it, date);
mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(date), index);
}
mExceptionDateList->setCurrentItem(index);
enableExceptionButtons();
}
/******************************************************************************
* Change the currently highlighted exception date to that entered in the
* exception date edit control.
*/
void RecurrenceEdit::changeException()
{
if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
return;
int index = mExceptionDateList->currentItem();
if (index >= 0 && mExceptionDateList->isSelected(index))
{
TQDate olddate = mExceptionDates[index];
TQDate newdate = mExceptionDateEdit->date();
if (newdate != olddate)
{
mExceptionDates.remove(mExceptionDates.at(index));
mExceptionDateList->removeItem(index);
addException();
}
}
}
/******************************************************************************
* Delete the currently highlighted exception date.
*/
void RecurrenceEdit::deleteException()
{
int index = mExceptionDateList->currentItem();
if (index >= 0 && mExceptionDateList->isSelected(index))
{
mExceptionDates.remove(mExceptionDates.at(index));
mExceptionDateList->removeItem(index);
enableExceptionButtons();
}
}
/******************************************************************************
* Enable/disable the exception group buttons according to whether any item is
* selected in the exceptions listbox.
*/
void RecurrenceEdit::enableExceptionButtons()
{
int index = mExceptionDateList->currentItem();
bool enable = (index >= 0 && mExceptionDateList->isSelected(index));
if (mDeleteExceptionButton)
mDeleteExceptionButton->setEnabled(enable);
if (mChangeExceptionButton)
mChangeExceptionButton->setEnabled(enable);
// Prevent the exceptions list box receiving keyboard focus is it's empty
mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? TQWidget::WheelFocus : TQWidget::NoFocus);
}
/******************************************************************************
* Notify this instance of a change in the alarm start date.
*/
void RecurrenceEdit::setStartDate(const TQDate& start, const TQDate& today)
{
if (!mReadOnly)
{
setRuleDefaults(start);
if (start < today)
{
mEndDateEdit->setMinDate(today);
if (mExceptionDateEdit)
mExceptionDateEdit->setMinDate(today);
}
else
{
const TQString startString = i18n("Date cannot be earlier than start date", "start date");
mEndDateEdit->setMinDate(start, startString);
if (mExceptionDateEdit)
mExceptionDateEdit->setMinDate(start, startString);
}
}
}
/******************************************************************************
* Specify the default recurrence end date.
*/
void RecurrenceEdit::setDefaultEndDate(const TQDate& end)
{
if (!mEndDateButton->isOn())
mEndDateEdit->setDate(end);
}
void RecurrenceEdit::setEndDateTime(const DateTime& end)
{
mEndDateEdit->setDate(end.date());
mEndTimeEdit->setValue(end.time());
mEndTimeEdit->setEnabled(!end.isDateOnly());
mEndAnyTimeCheckBox->setChecked(end.isDateOnly());
}
DateTime RecurrenceEdit::endDateTime() const
{
if (mRuleButtonGroup->selected() == mAtLoginButton && mEndAnyTimeCheckBox->isChecked())
return DateTime(mEndDateEdit->date());
return DateTime(mEndDateEdit->date(), mEndTimeEdit->time());
}
/******************************************************************************
* Set all controls to their default values.
*/
void RecurrenceEdit::setDefaults(const TQDateTime& from)
{
mCurrStartDateTime = from;
TQDate fromDate = from.date();
mNoEndDateButton->setChecked(true);
mSubDailyRule->setFrequency(1);
mDailyRule->setFrequency(1);
mWeeklyRule->setFrequency(1);
mMonthlyRule->setFrequency(1);
mYearlyRule->setFrequency(1);
setRuleDefaults(fromDate);
mMonthlyRule->setType(MonthYearRule::DATE); // date in month
mYearlyRule->setType(MonthYearRule::DATE); // date in year
mEndDateEdit->setDate(fromDate);
mNoEmitTypeChanged = true;
int button;
switch (Preferences::defaultRecurPeriod())
{
case AT_LOGIN: button = mAtLoginButtonId; break;
case ANNUAL: button = mYearlyButtonId; break;
case MONTHLY: button = mMonthlyButtonId; break;
case WEEKLY: button = mWeeklyButtonId; break;
case DAILY: button = mDailyButtonId; break;
case SUBDAILY: button = mSubDailyButtonId; break;
case NO_RECUR:
default: button = mNoneButtonId; break;
}
mRuleButtonGroup->setButton(button);
mNoEmitTypeChanged = false;
rangeTypeClicked();
enableExceptionButtons();
saveState();
}
/******************************************************************************
* Set the controls for weekly, monthly and yearly rules which have not so far
* been shown, to their default values, depending on the recurrence start date.
*/
void RecurrenceEdit::setRuleDefaults(const TQDate& fromDate)
{
int day = fromDate.day();
int dayOfWeek = fromDate.dayOfWeek();
int month = fromDate.month();
if (!mDailyShown)
mDailyRule->setDays(true);
if (!mWeeklyShown)
mWeeklyRule->setDay(dayOfWeek);
if (!mMonthlyShown)
mMonthlyRule->setDefaultValues(day, dayOfWeek);
if (!mYearlyShown)
mYearlyRule->setDefaultValues(day, dayOfWeek, month);
}
/******************************************************************************
* Set the state of all controls to reflect the data in the specified event.
* Set 'keepDuration' true to prevent the recurrence count being adjusted to the
* remaining number of recurrences.
*/
void RecurrenceEdit::set(const KAEvent& event, bool keepDuration)
{
setDefaults(event.mainDateTime().dateTime());
if (event.repeatAtLogin())
{
mRuleButtonGroup->setButton(mAtLoginButtonId);
mEndDateButton->setChecked(true);
return;
}
mRuleButtonGroup->setButton(mNoneButtonId);
KARecurrence* recurrence = event.recurrence();
if (!recurrence)
return;
KARecurrence::Type rtype = recurrence->type();
switch (rtype)
{
case KARecurrence::MINUTELY:
mRuleButtonGroup->setButton(mSubDailyButtonId);
break;
case KARecurrence::DAILY:
{
mRuleButtonGroup->setButton(mDailyButtonId);
TQBitArray rDays = recurrence->days();
bool set = false;
for (int i = 0; i < 7 && !set; ++i)
set = rDays.testBit(i);
if (set)
mDailyRule->setDays(rDays);
else
mDailyRule->setDays(true);
break;
}
case KARecurrence::WEEKLY:
{
mRuleButtonGroup->setButton(mWeeklyButtonId);
TQBitArray rDays = recurrence->days();
mWeeklyRule->setDays(rDays);
break;
}
case KARecurrence::MONTHLY_POS: // on nth (Tuesday) of the month
{
TQValueList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions();
int i = posns.first().pos();
if (!i)
{
// It's every (Tuesday) of the month. Convert to a weekly recurrence
// (but ignoring any non-every xxxDay positions).
mRuleButtonGroup->setButton(mWeeklyButtonId);
mWeeklyRule->setFrequency(recurrence->frequency());
TQBitArray rDays(7);
for (TQValueList<RecurrenceRule::WDayPos>::ConstIterator it = posns.begin(); it != posns.end(); ++it)
{
if (!(*it).pos())
rDays.setBit((*it).day() - 1, 1);
}
mWeeklyRule->setDays(rDays);
break;
}
mRuleButtonGroup->setButton(mMonthlyButtonId);
mMonthlyRule->setPosition(i, posns.first().day());
break;
}
case KARecurrence::MONTHLY_DAY: // on nth day of the month
{
mRuleButtonGroup->setButton(mMonthlyButtonId);
TQValueList<int> rmd = recurrence->monthDays();
int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
mMonthlyRule->setDate(day);
break;
}
case KARecurrence::ANNUAL_DATE: // on the nth day of (months...) in the year
case KARecurrence::ANNUAL_POS: // on the nth (Tuesday) of (months...) in the year
{
if (rtype == KARecurrence::ANNUAL_DATE)
{
mRuleButtonGroup->setButton(mYearlyButtonId);
const TQValueList<int> rmd = recurrence->monthDays();
int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
mYearlyRule->setDate(day);
mYearlyRule->setFeb29Type(recurrence->feb29Type());
}
else if (rtype == KARecurrence::ANNUAL_POS)
{
mRuleButtonGroup->setButton(mYearlyButtonId);
TQValueList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions();
mYearlyRule->setPosition(posns.first().pos(), posns.first().day());
}
mYearlyRule->setMonths(recurrence->yearMonths());
break;
}
default:
return;
}
mRule->setFrequency(recurrence->frequency());
// Get range information
TQDateTime endtime = mCurrStartDateTime;
int duration = recurrence->duration();
if (duration == -1)
mNoEndDateButton->setChecked(true);
else if (duration)
{
mRepeatCountButton->setChecked(true);
mRepeatCountEntry->setValue(duration);
}
else
{
mEndDateButton->setChecked(true);
endtime = recurrence->endDateTime();
mEndTimeEdit->setValue(endtime.time());
}
mEndDateEdit->setDate(endtime.date());
// Get exception information
mExceptionDates = event.recurrence()->exDates();
qHeapSort(mExceptionDates);
mExceptionDateList->clear();
for (DateList::ConstIterator it = mExceptionDates.begin(); it != mExceptionDates.end(); ++it)
mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(*it));
enableExceptionButtons();
// Get repetition within recurrence
mSubRepetition->set(event.repeatInterval(), event.repeatCount());
rangeTypeClicked();
saveState();
}
/******************************************************************************
* Update the specified KAEvent with the entered recurrence data.
* If 'adjustStart' is true, the start date/time will be adjusted if necessary
* to be the first date/time which recurs on or after the original start.
*/
void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart)
{
// Get end date and repeat count, common to all types of recurring events
TQDate endDate;
TQTime endTime;
int repeatCount;
if (mNoEndDateButton->isChecked())
repeatCount = -1;
else if (mRepeatCountButton->isChecked())
repeatCount = mRepeatCountEntry->value();
else
{
repeatCount = 0;
endDate = mEndDateEdit->date();
endTime = mEndTimeEdit->time();
}
// Set up the recurrence according to the type selected
TQButton* button = mRuleButtonGroup->selected();
event.setRepeatAtLogin(button == mAtLoginButton);
int frequency = mRule ? mRule->frequency() : 0;
if (button == mSubDailyButton)
{
TQDateTime endDateTime(endDate, endTime);
event.setRecurMinutely(frequency, repeatCount, endDateTime);
}
else if (button == mDailyButton)
{
event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate);
}
else if (button == mWeeklyButton)
{
event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate);
}
else if (button == mMonthlyButton)
{
if (mMonthlyRule->type() == MonthlyRule::POS)
{
// It's by position
KAEvent::MonthPos pos;
pos.days.fill(false);
pos.days.setBit(mMonthlyRule->dayOfWeek() - 1);
pos.weeknum = mMonthlyRule->week();
TQValueList<KAEvent::MonthPos> poses;
poses.append(pos);
event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate);
}
else
{
// It's by day
int daynum = mMonthlyRule->date();
TQValueList<int> daynums;
daynums.append(daynum);
event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
}
}
else if (button == mYearlyButton)
{
TQValueList<int> months = mYearlyRule->months();
if (mYearlyRule->type() == YearlyRule::POS)
{
// It's by position
KAEvent::MonthPos pos;
pos.days.fill(false);
pos.days.setBit(mYearlyRule->dayOfWeek() - 1);
pos.weeknum = mYearlyRule->week();
TQValueList<KAEvent::MonthPos> poses;
poses.append(pos);
event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate);
}
else
{
// It's by date in month
event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(),
mYearlyRule->feb29Type(), repeatCount, endDate);
}
}
else
{
event.setNoRecur();
return;
}
if (!event.recurs())
return; // an error occurred setting up the recurrence
if (adjustStart)
event.setFirstRecurrence();
// Set up repetition within the recurrence.
// N.B. This requires the main recurrence to be set up first.
int count = mSubRepetition->count();
if (mRuleButtonType < SUBDAILY)
count = 0;
event.setRepetition(mSubRepetition->interval(), count);
// Set up exceptions
event.recurrence()->setExDates(mExceptionDates);
event.setUpdated();
}
/******************************************************************************
* Save the state of all controls.
*/
void RecurrenceEdit::saveState()
{
mSavedRuleButton = mRuleButtonGroup->selected();
if (mRule)
mRule->saveState();
mSavedRangeButton = mRangeButtonGroup->selected();
if (mSavedRangeButton == mRepeatCountButton)
mSavedRecurCount = mRepeatCountEntry->value();
else if (mSavedRangeButton == mEndDateButton)
mSavedEndDateTime.set(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked());
mSavedExceptionDates = mExceptionDates;
mSavedRepeatInterval = mSubRepetition->interval();
mSavedRepeatCount = mSubRepetition->count();
}
/******************************************************************************
* Check whether any of the controls have changed state since initialisation.
*/
bool RecurrenceEdit::stateChanged() const
{
if (mSavedRuleButton != mRuleButtonGroup->selected()
|| mSavedRangeButton != mRangeButtonGroup->selected()
|| (mRule && mRule->stateChanged()))
return true;
if (mSavedRangeButton == mRepeatCountButton
&& mSavedRecurCount != mRepeatCountEntry->value())
return true;
if (mSavedRangeButton == mEndDateButton
&& mSavedEndDateTime != DateTime(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked()))
return true;
if (mSavedExceptionDates != mExceptionDates
|| mSavedRepeatInterval != mSubRepetition->interval()
|| mSavedRepeatCount != mSubRepetition->count())
return true;
return false;
}
/*=============================================================================
= Class Rule
= Base class for rule widgets, including recurrence frequency.
=============================================================================*/
Rule::Rule(const TQString& freqText, const TQString& freqWhatsThis, bool time, bool readOnly, TQWidget* parent, const char* name)
: NoRule(parent, name)
{
mLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
TQHBox* freqBox = new TQHBox(this);
mLayout->addWidget(freqBox);
TQHBox* box = new TQHBox(freqBox); // this is to control the TQWhatsThis text display area
box->setSpacing(KDialog::spacingHint());
TQLabel* label = new TQLabel(i18n("Recur e&very"), box);
label->setFixedSize(label->sizeHint());
if (time)
{
mIntSpinBox = 0;
mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
mTimeSpinBox->setReadOnly(readOnly);
}
else
{
mTimeSpinBox = 0;
mSpinBox = mIntSpinBox = new SpinBox(1, 999, 1, box);
mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
mIntSpinBox->setReadOnly(readOnly);
}
connect(mSpinBox, TQ_SIGNAL(valueChanged(int)), TQ_SIGNAL(frequencyChanged()));
label->setBuddy(mSpinBox);
label = new TQLabel(freqText, box);
label->setFixedSize(label->sizeHint());
box->setFixedSize(sizeHint());
TQWhatsThis::add(box, freqWhatsThis);
new TQWidget(freqBox); // left adjust the visible widgets
freqBox->setFixedHeight(freqBox->sizeHint().height());
freqBox->setFocusProxy(mSpinBox);
}
int Rule::frequency() const
{
if (mIntSpinBox)
return mIntSpinBox->value();
if (mTimeSpinBox)
return mTimeSpinBox->value();
return 0;
}
void Rule::setFrequency(int n)
{
if (mIntSpinBox)
mIntSpinBox->setValue(n);
if (mTimeSpinBox)
mTimeSpinBox->setValue(n);
}
/******************************************************************************
* Save the state of all controls.
*/
void Rule::saveState()
{
mSavedFrequency = frequency();
}
/******************************************************************************
* Check whether any of the controls have changed state since initialisation.
*/
bool Rule::stateChanged() const
{
return (mSavedFrequency != frequency());
}
/*=============================================================================
= Class SubDailyRule
= Sub-daily rule widget.
=============================================================================*/
SubDailyRule::SubDailyRule(bool readOnly, TQWidget* parent, const char* name)
: Rule(i18n("hours:minutes"),
i18n("Enter the number of hours and minutes between repetitions of the alarm"),
true, readOnly, parent, name)
{ }
/*=============================================================================
= Class DayWeekRule
= Daily/weekly rule widget base class.
=============================================================================*/
DayWeekRule::DayWeekRule(const TQString& freqText, const TQString& freqWhatsThis, const TQString& daysWhatsThis,
bool readOnly, TQWidget* parent, const char* name)
: Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
mSavedDays(7)
{
TQGridLayout* grid = new TQGridLayout(layout(), 1, 4, KDialog::spacingHint());
grid->setRowStretch(0, 1);
TQLabel* label = new TQLabel(i18n("On: Tuesday", "O&n:"), this);
label->setFixedSize(label->sizeHint());
grid->addWidget(label, 0, 0, TQt::AlignRight | TQt::AlignTop);
grid->addColSpacing(1, KDialog::spacingHint());
// List the days of the week starting at the user's start day of the week.
// Save the first day of the week, just in case it changes while the dialog is open.
TQWidget* box = new TQWidget(this); // this is to control the TQWhatsThis text display area
TQGridLayout* dgrid = new TQGridLayout(box, 4, 2, 0, KDialog::spacingHint());
const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
for (int i = 0; i < 7; ++i)
{
int day = KAlarm::localeDayInWeek_to_weekDay(i);
mDayBox[i] = new CheckBox(calendar->weekDayName(day), box);
mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
mDayBox[i]->setReadOnly(readOnly);
dgrid->addWidget(mDayBox[i], i%4, i/4, TQt::AlignAuto);
}
box->setFixedSize(box->sizeHint());
TQWhatsThis::add(box, daysWhatsThis);
grid->addWidget(box, 0, 2, TQt::AlignAuto);
label->setBuddy(mDayBox[0]);
grid->setColStretch(3, 1);
}
/******************************************************************************
* Fetch which days of the week have been ticked.
*/
TQBitArray DayWeekRule::days() const
{
TQBitArray ds(7);
ds.fill(false);
for (int i = 0; i < 7; ++i)
if (mDayBox[i]->isChecked())
ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
return ds;
}
/******************************************************************************
* Tick/untick every day of the week.
*/
void DayWeekRule::setDays(bool tick)
{
for (int i = 0; i < 7; ++i)
mDayBox[i]->setChecked(tick);
}
/******************************************************************************
* Tick/untick each day of the week according to the specified bits.
*/
void DayWeekRule::setDays(const TQBitArray& days)
{
for (int i = 0; i < 7; ++i)
{
bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
mDayBox[i]->setChecked(x);
}
}
/******************************************************************************
* Tick the specified day of the week, and untick all other days.
*/
void DayWeekRule::setDay(int dayOfWeek)
{
for (int i = 0; i < 7; ++i)
mDayBox[i]->setChecked(false);
if (dayOfWeek > 0 && dayOfWeek <= 7)
mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
}
/******************************************************************************
* Validate: check that at least one day is selected.
*/
TQWidget* DayWeekRule::validate(TQString& errorMessage)
{
for (int i = 0; i < 7; ++i)
if (mDayBox[i]->isChecked())
return 0;
errorMessage = i18n("No day selected");
return mDayBox[0];
}
/******************************************************************************
* Save the state of all controls.
*/
void DayWeekRule::saveState()
{
Rule::saveState();
mSavedDays = days();
}
/******************************************************************************
* Check whether any of the controls have changed state since initialisation.
*/
bool DayWeekRule::stateChanged() const
{
return (Rule::stateChanged()
|| mSavedDays != days());
}
/*=============================================================================
= Class DailyRule
= Daily rule widget.
=============================================================================*/
DailyRule::DailyRule(bool readOnly, TQWidget* parent, const char* name)
: DayWeekRule(i18n("day(s)"),
i18n("Enter the number of days between repetitions of the alarm"),
i18n("Select the days of the week on which the alarm is allowed to occur"),
readOnly, parent, name)
{ }
/*=============================================================================
= Class WeeklyRule
= Weekly rule widget.
=============================================================================*/
WeeklyRule::WeeklyRule(bool readOnly, TQWidget* parent, const char* name)
: DayWeekRule(i18n("week(s)"),
i18n("Enter the number of weeks between repetitions of the alarm"),
i18n("Select the days of the week on which to repeat the alarm"),
readOnly, parent, name)
{ }
/*=============================================================================
= Class MonthYearRule
= Monthly/yearly rule widget base class.
=============================================================================*/
MonthYearRule::MonthYearRule(const TQString& freqText, const TQString& freqWhatsThis, bool allowEveryWeek,
bool readOnly, TQWidget* parent, const char* name)
: Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
mEveryWeek(allowEveryWeek)
{
mButtonGroup = new ButtonGroup(this);
mButtonGroup->hide();
// Month day selector
TQHBox* box = new TQHBox(this);
box->setSpacing(KDialog::spacingHint());
layout()->addWidget(box);
mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
mDayButton->setFixedSize(mDayButton->sizeHint());
mDayButton->setReadOnly(readOnly);
mDayButtonId = mButtonGroup->insert(mDayButton);
TQWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
mDayCombo = new ComboBox(false, box);
mDayCombo->setSizeLimit(11);
for (int i = 0; i < 31; ++i)
mDayCombo->insertItem(TQString::number(i + 1));
mDayCombo->insertItem(i18n("Last day of month", "Last"));
mDayCombo->setFixedSize(mDayCombo->sizeHint());
mDayCombo->setReadOnly(readOnly);
TQWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
mDayButton->setFocusWidget(mDayCombo);
connect(mDayCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotDaySelected(int)));
box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
box->setFixedHeight(box->sizeHint().height());
// Month position selector
box = new TQHBox(this);
box->setSpacing(KDialog::spacingHint());
layout()->addWidget(box);
mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
mPosButton->setFixedSize(mPosButton->sizeHint());
mPosButton->setReadOnly(readOnly);
mPosButtonId = mButtonGroup->insert(mPosButton);
TQWhatsThis::add(mPosButton,
i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
mWeekCombo = new ComboBox(false, box);
mWeekCombo->insertItem(i18n("1st"));
mWeekCombo->insertItem(i18n("2nd"));
mWeekCombo->insertItem(i18n("3rd"));
mWeekCombo->insertItem(i18n("4th"));
mWeekCombo->insertItem(i18n("5th"));
mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
mWeekCombo->insertItem(i18n("2nd Last"));
mWeekCombo->insertItem(i18n("3rd Last"));
mWeekCombo->insertItem(i18n("4th Last"));
mWeekCombo->insertItem(i18n("5th Last"));
if (mEveryWeek)
{
mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
mWeekCombo->setSizeLimit(11);
}
TQWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
mWeekCombo->setReadOnly(readOnly);
mPosButton->setFocusWidget(mWeekCombo);
mDayOfWeekCombo = new ComboBox(false, box);
const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
for (int i = 0; i < 7; ++i)
{
int day = KAlarm::localeDayInWeek_to_weekDay(i);
mDayOfWeekCombo->insertItem(calendar->weekDayName(day));
}
mDayOfWeekCombo->setReadOnly(readOnly);
TQWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
box->setFixedHeight(box->sizeHint().height());
connect(mButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(clicked(int)));
}
MonthYearRule::DayPosType MonthYearRule::type() const
{
return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
}
void MonthYearRule::setType(MonthYearRule::DayPosType type)
{
mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
}
void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
{
--dayOfMonth;
mDayCombo->setCurrentItem(dayOfMonth);
mWeekCombo->setCurrentItem(dayOfMonth / 7);
mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
}
int MonthYearRule::date() const
{
int daynum = mDayCombo->currentItem() + 1;
return (daynum <= 31) ? daynum : 31 - daynum;
}
int MonthYearRule::week() const
{
int weeknum = mWeekCombo->currentItem() + 1;
return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
}
int MonthYearRule::dayOfWeek() const
{
return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
}
void MonthYearRule::setDate(int dayOfMonth)
{
mButtonGroup->setButton(mDayButtonId);
mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0); // day 0 shouldn't ever occur
}
void MonthYearRule::setPosition(int week, int dayOfWeek)
{
mButtonGroup->setButton(mPosButtonId);
mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
}
void MonthYearRule::enableSelection(DayPosType type)
{
bool date = (type == DATE);
mDayCombo->setEnabled(date);
mWeekCombo->setEnabled(!date);
mDayOfWeekCombo->setEnabled(!date);
}
void MonthYearRule::clicked(int id)
{
enableSelection(id == mDayButtonId ? DATE : POS);
}
void MonthYearRule::slotDaySelected(int index)
{
daySelected(index <= 30 ? index + 1 : 30 - index);
}
/******************************************************************************
* Save the state of all controls.
*/
void MonthYearRule::saveState()
{
Rule::saveState();
mSavedType = type();
if (mSavedType == DATE)
mSavedDay = date();
else
{
mSavedWeek = week();
mSavedWeekDay = dayOfWeek();
}
}
/******************************************************************************
* Check whether any of the controls have changed state since initialisation.
*/
bool MonthYearRule::stateChanged() const
{
if (Rule::stateChanged()
|| mSavedType != type())
return true;
if (mSavedType == DATE)
{
if (mSavedDay != date())
return true;
}
else
{
if (mSavedWeek != week()
|| mSavedWeekDay != dayOfWeek())
return true;
}
return false;
}
/*=============================================================================
= Class MonthlyRule
= Monthly rule widget.
=============================================================================*/
MonthlyRule::MonthlyRule(bool readOnly, TQWidget* parent, const char* name)
: MonthYearRule(i18n("month(s)"),
i18n("Enter the number of months between repetitions of the alarm"),
false, readOnly, parent, name)
{ }
/*=============================================================================
= Class YearlyRule
= Yearly rule widget.
=============================================================================*/
YearlyRule::YearlyRule(bool readOnly, TQWidget* parent, const char* name)
: MonthYearRule(i18n("year(s)"),
i18n("Enter the number of years between repetitions of the alarm"),
true, readOnly, parent, name)
{
// Set up the month selection widgets
TQBoxLayout* hlayout = new TQHBoxLayout(layout(), KDialog::spacingHint());
TQLabel* label = new TQLabel(i18n("List of months to select", "Months:"), this);
label->setFixedSize(label->sizeHint());
hlayout->addWidget(label, 0, TQt::AlignAuto | TQt::AlignTop);
// List the months of the year.
TQWidget* w = new TQWidget(this); // this is to control the TQWhatsThis text display area
hlayout->addWidget(w, 1, TQt::AlignAuto);
TQGridLayout* grid = new TQGridLayout(w, 4, 3, 0, KDialog::spacingHint());
const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
int year = TQDate::currentDate().year();
for (int i = 0; i < 12; ++i)
{
mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, true), w);
mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
mMonthBox[i]->setReadOnly(readOnly);
grid->addWidget(mMonthBox[i], i%3, i/3, TQt::AlignAuto);
}
connect(mMonthBox[1], TQ_SIGNAL(toggled(bool)), TQ_SLOT(enableFeb29()));
w->setFixedHeight(w->sizeHint().height());
TQWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
// February 29th handling option
TQHBox* f29box = new TQHBox(this);
layout()->addWidget(f29box);
TQHBox* box = new TQHBox(f29box); // this is to control the TQWhatsThis text display area
box->setSpacing(KDialog::spacingHint());
mFeb29Label = new TQLabel(i18n("February 2&9th alarm in non-leap years:"), box);
mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
mFeb29Combo = new ComboBox(false, box);
mFeb29Combo->insertItem(i18n("No date", "None"));
mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
mFeb29Combo->setReadOnly(readOnly);
mFeb29Label->setBuddy(mFeb29Combo);
box->setFixedSize(box->sizeHint());
TQWhatsThis::add(box,
i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
new TQWidget(f29box); // left adjust the visible widgets
f29box->setFixedHeight(f29box->sizeHint().height());
}
void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
{
MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
--month;
for (int i = 0; i < 12; ++i)
mMonthBox[i]->setChecked(i == month);
setFeb29Type(Preferences::defaultFeb29Type());
daySelected(dayOfMonth); // enable/disable month checkboxes as appropriate
}
/******************************************************************************
* Fetch which months have been checked (1 - 12).
* Reply = true if February has been checked.
*/
TQValueList<int> YearlyRule::months() const
{
TQValueList<int> mnths;
for (int i = 0; i < 12; ++i)
if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
mnths.append(i + 1);
return mnths;
}
/******************************************************************************
* Check/uncheck each month of the year according to the specified list.
*/
void YearlyRule::setMonths(const TQValueList<int>& mnths)
{
bool checked[12];
for (int i = 0; i < 12; ++i)
checked[i] = false;
for (TQValueListConstIterator<int> it = mnths.begin(); it != mnths.end(); ++it)
checked[(*it) - 1] = true;
for (int i = 0; i < 12; ++i)
mMonthBox[i]->setChecked(checked[i]);
enableFeb29();
}
/******************************************************************************
* Return the date for February 29th alarms in non-leap years.
*/
KARecurrence::Feb29Type YearlyRule::feb29Type() const
{
if (mFeb29Combo->isEnabled())
{
switch (mFeb29Combo->currentItem())
{
case 1: return KARecurrence::FEB29_MAR1;
case 2: return KARecurrence::FEB29_FEB28;
default: break;
}
}
return KARecurrence::FEB29_FEB29;
}
/******************************************************************************
* Set the date for February 29th alarms to trigger in non-leap years.
*/
void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
{
int index;
switch (type)
{
default:
case KARecurrence::FEB29_FEB29: index = 0; break;
case KARecurrence::FEB29_MAR1: index = 1; break;
case KARecurrence::FEB29_FEB28: index = 2; break;
}
mFeb29Combo->setCurrentItem(index);
}
/******************************************************************************
* Validate: check that at least one month is selected.
*/
TQWidget* YearlyRule::validate(TQString& errorMessage)
{
for (int i = 0; i < 12; ++i)
if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
return 0;
errorMessage = i18n("No month selected");
return mMonthBox[0];
}
/******************************************************************************
* Called when a yearly recurrence type radio button is clicked,
* to enable/disable month checkboxes as appropriate for the date selected.
*/
void YearlyRule::clicked(int id)
{
MonthYearRule::clicked(id);
daySelected(buttonType(id) == DATE ? date() : 1);
}
/******************************************************************************
* Called when a day of the month is selected in a yearly recurrence, to
* disable months for which the day is out of range.
*/
void YearlyRule::daySelected(int day)
{
mMonthBox[1]->setEnabled(day <= 29); // February
bool enable = (day != 31);
mMonthBox[3]->setEnabled(enable); // April
mMonthBox[5]->setEnabled(enable); // June
mMonthBox[8]->setEnabled(enable); // September
mMonthBox[10]->setEnabled(enable); // November
enableFeb29();
}
/******************************************************************************
* Enable/disable the February 29th combo box depending on whether February
* 29th is selected.
*/
void YearlyRule::enableFeb29()
{
bool enable = (type() == DATE && date() == 29 && mMonthBox[1]->isChecked() && mMonthBox[1]->isEnabled());
mFeb29Label->setEnabled(enable);
mFeb29Combo->setEnabled(enable);
}
/******************************************************************************
* Save the state of all controls.
*/
void YearlyRule::saveState()
{
MonthYearRule::saveState();
mSavedMonths = months();
mSavedFeb29Type = feb29Type();
}
/******************************************************************************
* Check whether any of the controls have changed state since initialisation.
*/
bool YearlyRule::stateChanged() const
{
return (MonthYearRule::stateChanged()
|| mSavedMonths != months()
|| mSavedFeb29Type != feb29Type());
}