/* * recurrenceedit.cpp - widget to edit the event's recurrence definition * Program: kalarm * Copyright © 2002-2008 by David Jarvie * * Based originally on KOrganizer module koeditorrecurrence.cpp, * Copyright (c) 2000,2001 Cornelius Schumacher * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, Qt::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, Qt::Horizontal, ruleFrame); mRuleButtonGroup->setInsideMargin(0); mRuleButtonGroup->setFrameStyle(TQFrame::NoFrame); lay->addWidget(mRuleButtonGroup); lay->addStretch(); // top-adjust the interval radio buttons connect(mRuleButtonGroup, TQT_SIGNAL(buttonSet(int)), TQT_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, TQT_SIGNAL(needsInitialisation()), TQT_SIGNAL(repeatNeedsInitialisation())); connect(mSubRepetition, TQT_SIGNAL(changed()), TQT_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, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged())); connect(mDailyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged())); connect(mWeeklyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged())); connect(mMonthlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged())); connect(mYearlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_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, TQT_SIGNAL(buttonSet(int)), TQT_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, TQT_SIGNAL(valueChanged(int)), TQT_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, TQT_SIGNAL(toggled(bool)), TQT_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, TQT_SIGNAL(selectionChanged()), TQT_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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(clicked()), TQT_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(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 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::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() ? TQ_WheelFocus : TQ_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 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::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 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 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 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 poses; poses.append(pos); event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate); } else { // It's by day int daynum = mMonthlyRule->date(); TQValueList daynums; daynums.append(daynum); event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate); } } else if (button == mYearlyButton) { TQValueList 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 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, TQT_SIGNAL(valueChanged(int)), TQT_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, TQT_SIGNAL(activated(int)), TQT_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, TQT_SIGNAL(buttonSet(int)), TQT_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], TQT_SIGNAL(toggled(bool)), TQT_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 YearlyRule::months() const { TQValueList 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& mnths) { bool checked[12]; for (int i = 0; i < 12; ++i) checked[i] = false; for (TQValueListConstIterator 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()); }