File indexing completed on 2024-05-12 05:14:56
0001 /* 0002 * recurrenceedit.cpp - widget to edit the event's recurrence definition 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2002-2022 David Jarvie <djarvie@kde.org> 0005 * 0006 * Based originally on KOrganizer module koeditorrecurrence.cpp, 0007 * SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org> 0008 * 0009 * SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "recurrenceedit.h" 0013 #include "recurrenceedit_p.h" 0014 0015 #include "preferences.h" 0016 #include "repetitionbutton.h" 0017 #include "lib/buttongroup.h" 0018 #include "lib/checkbox.h" 0019 #include "lib/combobox.h" 0020 #include "lib/locale.h" 0021 #include "lib/radiobutton.h" 0022 #include "lib/spinbox.h" 0023 #include "lib/timeedit.h" 0024 #include "lib/timespinbox.h" 0025 #include "kalarmcalendar/kaevent.h" 0026 #include "kalarmcalendar/karecurrence.h" 0027 #include "kalarm_debug.h" 0028 0029 #include <KCalendarCore/Event> 0030 0031 #include <KLocalizedString> 0032 #include <KDateComboBox> 0033 0034 #include <QHBoxLayout> 0035 #include <QPushButton> 0036 #include <QLabel> 0037 #include <QStackedWidget> 0038 #include <QListWidget> 0039 #include <QGroupBox> 0040 #include <QGridLayout> 0041 #include <QVBoxLayout> 0042 #include <QtAlgorithms> 0043 #include <QLocale> 0044 0045 using namespace KCalendarCore; 0046 0047 class ListWidget : public QListWidget 0048 { 0049 public: 0050 explicit ListWidget(QWidget* parent) : QListWidget(parent) {} 0051 QSize sizeHint() const override { return minimumSizeHint(); } 0052 }; 0053 0054 // Collect these widget labels together to ensure consistent wording and 0055 // translations across different modules. 0056 QString RecurrenceEdit::i18n_combo_NoRecur() { return i18nc("@item:inlistbox Recurrence type", "No Recurrence"); } 0057 QString RecurrenceEdit::i18n_combo_AtLogin() { return i18nc("@item:inlistbox Recurrence type", "At Login"); } 0058 QString RecurrenceEdit::i18n_combo_HourlyMinutely() { return i18nc("@item:inlistbox Recurrence type", "Hourly/Minutely"); } 0059 QString RecurrenceEdit::i18n_combo_Daily() { return i18nc("@item:inlistbox Recurrence type", "Daily"); } 0060 QString RecurrenceEdit::i18n_combo_Weekly() { return i18nc("@item:inlistbox Recurrence type", "Weekly"); } 0061 QString RecurrenceEdit::i18n_combo_Monthly() { return i18nc("@item:inlistbox Recurrence type", "Monthly"); } 0062 QString RecurrenceEdit::i18n_combo_Yearly() { return i18nc("@item:inlistbox Recurrence type", "Yearly"); } 0063 0064 0065 RecurrenceEdit::RecurrenceEdit(bool readOnly, QWidget* parent) 0066 : QFrame(parent) 0067 , mReadOnly(readOnly) 0068 { 0069 qCDebug(KALARM_LOG) << "RecurrenceEdit:"; 0070 auto topLayout = new QVBoxLayout(this); 0071 topLayout->setContentsMargins(0, 0, 0, 0); 0072 0073 /* Create the recurrence rule Group box which holds the recurrence period 0074 * selection buttons, and the weekly, monthly and yearly recurrence rule 0075 * frames which specify options individual to each of these distinct 0076 * sections of the recurrence rule. Each frame is made visible by the 0077 * selection of its corresponding radio button. 0078 */ 0079 0080 QGroupBox* recurGroup = new QGroupBox(i18nc("@title:group", "Recurrence Rule"), this); 0081 topLayout->addWidget(recurGroup); 0082 auto hlayout = new QHBoxLayout(recurGroup); 0083 const int marLeft = style()->pixelMetric(QStyle::PM_LayoutLeftMargin); 0084 const int marRight = style()->pixelMetric(QStyle::PM_LayoutRightMargin); 0085 hlayout->setSpacing((marLeft + marRight)/2); // use margin spacing due to vertical divider line 0086 0087 // Recurrence period radio buttons 0088 auto vlayout = new QVBoxLayout(); 0089 vlayout->setSpacing(0); 0090 vlayout->setContentsMargins(0, 0, 0, 0); 0091 hlayout->addLayout(vlayout); 0092 mRuleButtonGroup = new ButtonGroup(recurGroup); 0093 connect(mRuleButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::periodClicked); 0094 connect(mRuleButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::contentsChanged); 0095 0096 mNoneButton = new RadioButton(i18n_combo_NoRecur(), recurGroup); 0097 mNoneButton->setReadOnly(mReadOnly); 0098 mNoneButton->setWhatsThis(i18nc("@info:whatsthis", "Do not repeat the alarm")); 0099 mRuleButtonGroup->addButton(mNoneButton); 0100 vlayout->addWidget(mNoneButton); 0101 0102 mAtLoginButton = new RadioButton(i18n_combo_AtLogin(), recurGroup); 0103 mAtLoginButton->setReadOnly(mReadOnly); 0104 mAtLoginButton->setWhatsThis(xi18nc("@info:whatsthis", 0105 "<para>Trigger the alarm at the specified date/time and at every login until then.</para>" 0106 "<para>Note that it will also be triggered any time <application>KAlarm</application> is restarted.</para>")); 0107 mRuleButtonGroup->addButton(mAtLoginButton); 0108 vlayout->addWidget(mAtLoginButton); 0109 0110 mSubDailyButton = new RadioButton(i18n_combo_HourlyMinutely(), recurGroup); 0111 mSubDailyButton->setReadOnly(mReadOnly); 0112 mSubDailyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at hourly/minutely intervals")); 0113 mRuleButtonGroup->addButton(mSubDailyButton); 0114 vlayout->addWidget(mSubDailyButton); 0115 0116 mDailyButton = new RadioButton(i18n_combo_Daily(), recurGroup); 0117 mDailyButton->setReadOnly(mReadOnly); 0118 mDailyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at daily intervals")); 0119 mRuleButtonGroup->addButton(mDailyButton); 0120 vlayout->addWidget(mDailyButton); 0121 0122 mWeeklyButton = new RadioButton(i18n_combo_Weekly(), recurGroup); 0123 mWeeklyButton->setReadOnly(mReadOnly); 0124 mWeeklyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at weekly intervals")); 0125 mRuleButtonGroup->addButton(mWeeklyButton); 0126 vlayout->addWidget(mWeeklyButton); 0127 0128 mMonthlyButton = new RadioButton(i18n_combo_Monthly(), recurGroup); 0129 mMonthlyButton->setReadOnly(mReadOnly); 0130 mMonthlyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at monthly intervals")); 0131 mRuleButtonGroup->addButton(mMonthlyButton); 0132 vlayout->addWidget(mMonthlyButton); 0133 0134 mYearlyButton = new RadioButton(i18n_combo_Yearly(), recurGroup); 0135 mYearlyButton->setReadOnly(mReadOnly); 0136 mYearlyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at annual intervals")); 0137 mRuleButtonGroup->addButton(mYearlyButton); 0138 vlayout->addWidget(mYearlyButton); 0139 vlayout->addStretch(); // top-adjust the interval radio buttons 0140 0141 // Sub-repetition button 0142 mSubRepetition = new RepetitionButton(i18nc("@action:button", "Sub-Repetition"), true, recurGroup); 0143 mSubRepetition->setReadOnly(mReadOnly); 0144 mSubRepetition->setWhatsThis(i18nc("@info:whatsthis", 0145 "Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due.")); 0146 connect(mSubRepetition, &RepetitionButton::needsInitialisation, this, &RecurrenceEdit::repeatNeedsInitialisation); 0147 connect(mSubRepetition, &RepetitionButton::changed, this, &RecurrenceEdit::frequencyChanged); 0148 connect(mSubRepetition, &RepetitionButton::changed, this, &RecurrenceEdit::contentsChanged); 0149 vlayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0150 vlayout->addWidget(mSubRepetition); 0151 0152 // Vertical divider line 0153 vlayout = new QVBoxLayout(); 0154 vlayout->setContentsMargins(0, 0, 0, 0); 0155 hlayout->addLayout(vlayout); 0156 QFrame* divider = new QFrame(recurGroup); 0157 divider->setFrameStyle(QFrame::VLine | QFrame::Sunken); 0158 vlayout->addWidget(divider, 1); 0159 0160 // Rule definition stack 0161 mRuleStack = new QStackedWidget(recurGroup); 0162 hlayout->addWidget(mRuleStack); 0163 hlayout->addStretch(1); 0164 mNoRule = new NoRule(mRuleStack); 0165 mSubDailyRule = new SubDailyRule(mReadOnly, mRuleStack); 0166 mDailyRule = new DailyRule(mReadOnly, mRuleStack); 0167 mWeeklyRule = new WeeklyRule(mReadOnly, mRuleStack); 0168 mMonthlyRule = new MonthlyRule(mReadOnly, mRuleStack); 0169 mYearlyRule = new YearlyRule(mReadOnly, mRuleStack); 0170 0171 connect(mSubDailyRule, &SubDailyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged); 0172 connect(mDailyRule, &DailyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged); 0173 connect(mWeeklyRule, &WeeklyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged); 0174 connect(mMonthlyRule, &MonthlyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged); 0175 connect(mYearlyRule, &YearlyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged); 0176 connect(mSubDailyRule, &SubDailyRule::changed, this, &RecurrenceEdit::contentsChanged); 0177 connect(mDailyRule, &DailyRule::changed, this, &RecurrenceEdit::contentsChanged); 0178 connect(mWeeklyRule, &WeeklyRule::changed, this, &RecurrenceEdit::contentsChanged); 0179 connect(mMonthlyRule, &MonthlyRule::changed, this, &RecurrenceEdit::contentsChanged); 0180 connect(mYearlyRule, &YearlyRule::changed, this, &RecurrenceEdit::contentsChanged); 0181 0182 mRuleStack->addWidget(mNoRule); 0183 mRuleStack->addWidget(mSubDailyRule); 0184 mRuleStack->addWidget(mDailyRule); 0185 mRuleStack->addWidget(mWeeklyRule); 0186 mRuleStack->addWidget(mMonthlyRule); 0187 mRuleStack->addWidget(mYearlyRule); 0188 hlayout->addSpacing((marLeft + marRight)/2); 0189 0190 // Create the recurrence range group which contains the controls 0191 // which specify how long the recurrence is to last. 0192 0193 mRangeButtonBox = new QGroupBox(i18nc("@title:group", "Recurrence End"), this); 0194 topLayout->addWidget(mRangeButtonBox); 0195 mRangeButtonGroup = new ButtonGroup(mRangeButtonBox); 0196 connect(mRangeButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::rangeTypeClicked); 0197 connect(mRangeButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::contentsChanged); 0198 0199 vlayout = new QVBoxLayout(mRangeButtonBox); 0200 mNoEndDateButton = new RadioButton(i18nc("@option:radio", "No end"), mRangeButtonBox); 0201 mNoEndDateButton->setReadOnly(mReadOnly); 0202 mNoEndDateButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm indefinitely")); 0203 mRangeButtonGroup->addButton(mNoEndDateButton); 0204 vlayout->addWidget(mNoEndDateButton, 1, Qt::AlignLeft); 0205 QSize size = mNoEndDateButton->size(); 0206 0207 hlayout = new QHBoxLayout(); 0208 hlayout->setContentsMargins(0, 0, 0, 0); 0209 vlayout->addLayout(hlayout); 0210 mRepeatCountButton = new RadioButton(i18nc("@option:radio", "End after:"), mRangeButtonBox); 0211 mRepeatCountButton->setReadOnly(mReadOnly); 0212 mRepeatCountButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm for the number of times specified")); 0213 mRangeButtonGroup->addButton(mRepeatCountButton); 0214 mRepeatCountEntry = new SpinBox(1, 9999, mRangeButtonBox); 0215 mRepeatCountEntry->setSingleShiftStep(10); 0216 mRepeatCountEntry->setSelectOnStep(false); 0217 mRepeatCountEntry->setReadOnly(mReadOnly); 0218 mRepeatCountEntry->setWhatsThis(i18nc("@info:whatsthis", "Enter the total number of times to trigger the alarm")); 0219 connect(mRepeatCountEntry, &SpinBox::valueChanged, this, &RecurrenceEdit::repeatCountChanged); 0220 connect(mRepeatCountEntry, &SpinBox::valueChanged, this, &RecurrenceEdit::contentsChanged); 0221 mRepeatCountButton->setFocusWidget(mRepeatCountEntry); 0222 mRepeatCountLabel = new QLabel(i18nc("@label", "occurrence(s)"), mRangeButtonBox); 0223 hlayout->addWidget(mRepeatCountButton); 0224 hlayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0225 hlayout->addWidget(mRepeatCountEntry); 0226 hlayout->addWidget(mRepeatCountLabel); 0227 hlayout->addStretch(); 0228 size = size.expandedTo(mRepeatCountButton->sizeHint()); 0229 0230 hlayout = new QHBoxLayout(); 0231 hlayout->setContentsMargins(0, 0, 0, 0); 0232 vlayout->addLayout(hlayout); 0233 mEndDateButton = new RadioButton(i18nc("@option:radio", "End by:"), mRangeButtonBox); 0234 mEndDateButton->setReadOnly(mReadOnly); 0235 mEndDateButton->setWhatsThis( 0236 xi18nc("@info:whatsthis", "<para>Repeat the alarm until the date/time specified.</para>" 0237 "<para><note>This applies to the main recurrence only. It does not limit any sub-repetition which will occur regardless after the last main recurrence.</note></para>")); 0238 mRangeButtonGroup->addButton(mEndDateButton); 0239 mEndDateEdit = new KDateComboBox(mRangeButtonBox); 0240 mEndDateEdit->setOptions(mReadOnly ? KDateComboBox::Options{} : KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker); 0241 static const QString tzText = i18nc("@info", "This uses the same time zone as the start time."); 0242 mEndDateEdit->setWhatsThis(xi18nc("@info:whatsthis", 0243 "<para>Enter the last date to repeat the alarm.</para><para>%1</para>", tzText)); 0244 connect(mEndDateEdit, &KDateComboBox::dateEdited, this, &RecurrenceEdit::contentsChanged); 0245 mEndDateButton->setFocusWidget(mEndDateEdit); 0246 mEndTimeEdit = new TimeEdit(mRangeButtonBox); 0247 mEndTimeEdit->setReadOnly(mReadOnly); 0248 mEndTimeEdit->setWhatsThis(xi18nc("@info:whatsthis", 0249 "<para>Enter the last time to repeat the alarm.</para><para>%1</para><para>%2</para>", tzText, TimeSpinBox::shiftWhatsThis())); 0250 connect(mEndTimeEdit, &TimeEdit::valueChanged, this, &RecurrenceEdit::contentsChanged); 0251 mEndAnyTimeCheckBox = new CheckBox(i18nc("@option:check", "Any time"), mRangeButtonBox); 0252 mEndAnyTimeCheckBox->setReadOnly(mReadOnly); 0253 mEndAnyTimeCheckBox->setWhatsThis(i18nc("@info:whatsthis", "Stop repeating the alarm after your first login on or after the specified end date")); 0254 connect(mEndAnyTimeCheckBox, &CheckBox::toggled, this, &RecurrenceEdit::slotAnyTimeToggled); 0255 connect(mEndAnyTimeCheckBox, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged); 0256 hlayout->addWidget(mEndDateButton); 0257 hlayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0258 hlayout->addWidget(mEndDateEdit); 0259 hlayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0260 hlayout->addWidget(mEndTimeEdit); 0261 hlayout->addWidget(mEndAnyTimeCheckBox); 0262 hlayout->addStretch(); 0263 size = size.expandedTo(mEndDateButton->sizeHint()); 0264 0265 // Line up the widgets to the right of the radio buttons 0266 mRepeatCountButton->setFixedSize(size); 0267 mEndDateButton->setFixedSize(size); 0268 0269 // Create the exceptions group which specifies dates to be excluded 0270 // from the recurrence. 0271 0272 mExceptionGroup = new QGroupBox(i18nc("@title:group", "Exceptions"), this); 0273 topLayout->addWidget(mExceptionGroup); 0274 topLayout->setStretchFactor(mExceptionGroup, 2); 0275 hlayout = new QHBoxLayout(mExceptionGroup); 0276 vlayout = new QVBoxLayout(); 0277 vlayout->setContentsMargins(0, 0, 0, 0); 0278 hlayout->addLayout(vlayout); 0279 0280 mExceptionDateList = new ListWidget(mExceptionGroup); 0281 mExceptionDateList->setWhatsThis(i18nc("@info:whatsthis", "The list of exceptions, i.e. dates/times excluded from the recurrence")); 0282 connect(mExceptionDateList, &QListWidget::currentRowChanged, this, &RecurrenceEdit::enableExceptionButtons); 0283 vlayout->addWidget(mExceptionDateList); 0284 0285 if (mReadOnly) 0286 { 0287 mExceptionDateEdit = nullptr; 0288 mChangeExceptionButton = nullptr; 0289 mDeleteExceptionButton = nullptr; 0290 } 0291 else 0292 { 0293 vlayout = new QVBoxLayout(); 0294 vlayout->setContentsMargins(0, 0, 0, 0); 0295 hlayout->addLayout(vlayout); 0296 mExceptionDateEdit = new KDateComboBox(mExceptionGroup); 0297 mExceptionDateEdit->setOptions(mReadOnly ? KDateComboBox::Options{} : KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker); 0298 mExceptionDateEdit->setDate(KADateTime::currentLocalDate()); 0299 mExceptionDateEdit->setWhatsThis(i18nc("@info:whatsthis", 0300 "Enter a date to insert in the exceptions list. " 0301 "Use in conjunction with the Add or Change button below.")); 0302 vlayout->addWidget(mExceptionDateEdit, 0, Qt::AlignLeft); 0303 0304 hlayout = new QHBoxLayout(); 0305 hlayout->setContentsMargins(0, 0, 0, 0); 0306 vlayout->addLayout(hlayout); 0307 QPushButton* button = new QPushButton(i18nc("@action:button", "Add"), mExceptionGroup); 0308 button->setWhatsThis(i18nc("@info:whatsthis", "Add the date entered above to the exceptions list")); 0309 connect(button, &QPushButton::clicked, this, &RecurrenceEdit::addException); 0310 hlayout->addWidget(button); 0311 0312 mChangeExceptionButton = new QPushButton(i18nc("@action:button", "Change"), mExceptionGroup); 0313 mChangeExceptionButton->setWhatsThis(i18nc("@info:whatsthis", 0314 "Replace the currently highlighted item in the exceptions list with the date entered above")); 0315 connect(mChangeExceptionButton, &QPushButton::clicked, this, &RecurrenceEdit::changeException); 0316 hlayout->addWidget(mChangeExceptionButton); 0317 0318 mDeleteExceptionButton = new QPushButton(i18nc("@action:button", "Delete"), mExceptionGroup); 0319 mDeleteExceptionButton->setWhatsThis(i18nc("@info:whatsthis", "Remove the currently highlighted item from the exceptions list")); 0320 connect(mDeleteExceptionButton, &QPushButton::clicked, this, &RecurrenceEdit::deleteException); 0321 hlayout->addWidget(mDeleteExceptionButton); 0322 } 0323 0324 vlayout->addStretch(); 0325 0326 mExcludeHolidays = new CheckBox(i18nc("@option:check", "Exclude holidays"), mExceptionGroup); 0327 mExcludeHolidays->setReadOnly(mReadOnly); 0328 mExcludeHolidays->setWhatsThis(xi18nc("@info:whatsthis", 0329 "<para>Do not trigger the alarm on holidays.</para>" 0330 "<para>You can specify your holiday region in the Configuration dialog.</para>")); 0331 connect(mExcludeHolidays, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged); 0332 vlayout->addWidget(mExcludeHolidays); 0333 0334 mWorkTimeOnly = new CheckBox(i18nc("@option:check", "Only during working time"), mExceptionGroup); 0335 mWorkTimeOnly->setReadOnly(mReadOnly); 0336 mWorkTimeOnly->setWhatsThis(xi18nc("@info:whatsthis", 0337 "<para>Only execute the alarm during working hours, on working days.</para>" 0338 "<para>You can specify working days and hours in the Configuration dialog.</para>")); 0339 connect(mWorkTimeOnly, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged); 0340 vlayout->addWidget(mWorkTimeOnly); 0341 0342 topLayout->addStretch(); 0343 mNoEmitTypeChanged = false; 0344 } 0345 0346 /****************************************************************************** 0347 * Show or hide the exception controls. 0348 */ 0349 void RecurrenceEdit::showMoreOptions(bool more) 0350 { 0351 if (more) 0352 mExceptionGroup->show(); 0353 else 0354 mExceptionGroup->hide(); 0355 updateGeometry(); 0356 } 0357 0358 /****************************************************************************** 0359 * Verify the consistency of the entered data. 0360 * Reply = widget to receive focus on error, or 0 if no error. 0361 */ 0362 QWidget* RecurrenceEdit::checkData(const KADateTime& startDateTime, QString& errorMessage) const 0363 { 0364 if (mAtLoginButton->isChecked()) 0365 return nullptr; 0366 const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime; 0367 if (mEndDateButton->isChecked()) 0368 { 0369 // N.B. End date/time takes the same time spec as start date/time 0370 QWidget* errWidget = nullptr; 0371 bool noTime = !mEndTimeEdit->isEnabled(); 0372 QDate endDate = mEndDateEdit->date(); 0373 if (endDate < startDateTime.date()) 0374 errWidget = mEndDateEdit; 0375 else if (!noTime && KADateTime(endDate, mEndTimeEdit->time(), startDateTime.timeSpec()) < startDateTime) 0376 errWidget = mEndTimeEdit; 0377 if (errWidget) 0378 { 0379 errorMessage = noTime 0380 ? i18nc("@info", "End date is earlier than start date") 0381 : i18nc("@info", "End date/time is earlier than start date/time"); 0382 return errWidget; 0383 } 0384 } 0385 if (!mRule) 0386 return nullptr; 0387 return mRule->validate(errorMessage); 0388 } 0389 0390 /****************************************************************************** 0391 * Called when a recurrence period radio button is clicked. 0392 */ 0393 void RecurrenceEdit::periodClicked(QAbstractButton* button) 0394 { 0395 const RepeatType oldType = mRuleButtonType; 0396 const bool none = (button == mNoneButton); 0397 const bool atLogin = (button == mAtLoginButton); 0398 const bool subdaily = (button == mSubDailyButton); 0399 if (none) 0400 { 0401 mRule = nullptr; 0402 mRuleButtonType = NO_RECUR; 0403 } 0404 else if (atLogin) 0405 { 0406 mRule = nullptr; 0407 mRuleButtonType = AT_LOGIN; 0408 mEndDateButton->setChecked(true); 0409 } 0410 else if (subdaily) 0411 { 0412 mRule = mSubDailyRule; 0413 mRuleButtonType = SUBDAILY; 0414 } 0415 else if (button == mDailyButton) 0416 { 0417 mRule = mDailyRule; 0418 mRuleButtonType = DAILY; 0419 mDailyShown = true; 0420 } 0421 else if (button == mWeeklyButton) 0422 { 0423 mRule = mWeeklyRule; 0424 mRuleButtonType = WEEKLY; 0425 mWeeklyShown = true; 0426 } 0427 else if (button == mMonthlyButton) 0428 { 0429 mRule = mMonthlyRule; 0430 mRuleButtonType = MONTHLY; 0431 mMonthlyShown = true; 0432 } 0433 else if (button == mYearlyButton) 0434 { 0435 mRule = mYearlyRule; 0436 mRuleButtonType = ANNUAL; 0437 mYearlyShown = true; 0438 } 0439 else 0440 return; 0441 0442 if (mRuleButtonType != oldType) 0443 { 0444 mRuleStack->setCurrentWidget(mRule ? mRule : mNoRule); 0445 if (oldType == NO_RECUR || none) 0446 mRangeButtonBox->setEnabled(!none); 0447 mExceptionGroup->setEnabled(!(none || atLogin)); 0448 mEndAnyTimeCheckBox->setEnabled(atLogin); 0449 if (!none) 0450 { 0451 mNoEndDateButton->setEnabled(!atLogin); 0452 mRepeatCountButton->setEnabled(!atLogin); 0453 } 0454 rangeTypeClicked(); 0455 mSubRepetition->setEnabled(!(none || atLogin)); 0456 if (!mNoEmitTypeChanged) 0457 Q_EMIT typeChanged(mRuleButtonType); 0458 } 0459 } 0460 0461 void RecurrenceEdit::slotAnyTimeToggled(bool on) 0462 { 0463 QAbstractButton* button = mRuleButtonGroup->checkedButton(); 0464 mEndTimeEdit->setEnabled((button == mAtLoginButton && !on) 0465 || (button == mSubDailyButton && mEndDateButton->isChecked())); 0466 } 0467 0468 /****************************************************************************** 0469 * Called when a recurrence range type radio button is clicked. 0470 */ 0471 void RecurrenceEdit::rangeTypeClicked() 0472 { 0473 const bool endDate = mEndDateButton->isChecked(); 0474 mEndDateEdit->setEnabled(endDate); 0475 mEndTimeEdit->setEnabled(endDate 0476 && ((mAtLoginButton->isChecked() && !mEndAnyTimeCheckBox->isChecked()) 0477 || mSubDailyButton->isChecked())); 0478 const bool repeatCount = mRepeatCountButton->isChecked(); 0479 mRepeatCountEntry->setEnabled(repeatCount); 0480 mRepeatCountLabel->setEnabled(repeatCount); 0481 } 0482 0483 void RecurrenceEdit::showEvent(QShowEvent*) 0484 { 0485 if (mRule) 0486 mRule->setFrequencyFocus(); 0487 else 0488 mRuleButtonGroup->checkedButton()->setFocus(); 0489 Q_EMIT shown(); 0490 } 0491 0492 /****************************************************************************** 0493 * Return the sub-repetition interval and count within the recurrence, i.e. the 0494 * number of repetitions after the main recurrence. 0495 */ 0496 Repetition RecurrenceEdit::subRepetition() const 0497 { 0498 return (mRuleButtonType >= SUBDAILY) ? mSubRepetition->repetition() : Repetition(); 0499 } 0500 0501 /****************************************************************************** 0502 * Called when the Sub-Repetition button has been pressed to display the 0503 * sub-repetition dialog. 0504 * Alarm repetition has the following restrictions: 0505 * 1) Not allowed for a repeat-at-login alarm 0506 * 2) For a date-only alarm, the repeat interval must be a whole number of days. 0507 * 3) The overall repeat duration must be less than the recurrence interval. 0508 */ 0509 void RecurrenceEdit::setSubRepetition(int reminderMinutes, bool dateOnly) 0510 { 0511 int maxDuration; 0512 switch (mRuleButtonType) 0513 { 0514 case RecurrenceEdit::NO_RECUR: 0515 case RecurrenceEdit::AT_LOGIN: // alarm repeat not allowed 0516 maxDuration = 0; 0517 break; 0518 default: // repeat duration must be less than recurrence interval 0519 { 0520 KAEvent event; 0521 updateEvent(event, false); 0522 maxDuration = event.longestRecurrenceInterval().asSeconds()/60 - reminderMinutes - 1; 0523 break; 0524 } 0525 } 0526 mSubRepetition->initialise(mSubRepetition->repetition(), dateOnly, maxDuration); 0527 mSubRepetition->setEnabled(mRuleButtonType >= SUBDAILY && maxDuration); 0528 } 0529 0530 /****************************************************************************** 0531 * Activate the sub-repetition dialog. 0532 */ 0533 void RecurrenceEdit::activateSubRepetition() 0534 { 0535 mSubRepetition->activate(); 0536 } 0537 0538 /****************************************************************************** 0539 * Called when the value of the repeat count field changes, to reset the 0540 * minimum value to 1 if the value was 0. 0541 */ 0542 void RecurrenceEdit::repeatCountChanged(int value) 0543 { 0544 if (value > 0 && mRepeatCountEntry->minimum() == 0) 0545 mRepeatCountEntry->setMinimum(1); 0546 } 0547 0548 /****************************************************************************** 0549 * Add the date entered in the exception date edit control to the list of 0550 * exception dates. 0551 */ 0552 void RecurrenceEdit::addException() 0553 { 0554 if (!mExceptionDateEdit || !mExceptionDateEdit->date().isValid()) 0555 return; 0556 const QDate date = mExceptionDateEdit->date(); 0557 DateList::Iterator it; 0558 int index = 0; 0559 bool insert = true; 0560 for (it = mExceptionDates.begin(); it != mExceptionDates.end(); ++index, ++it) 0561 { 0562 if (date <= *it) 0563 { 0564 insert = (date != *it); 0565 break; 0566 } 0567 } 0568 if (insert) 0569 { 0570 mExceptionDates.insert(it, date); 0571 mExceptionDateList->insertItem(index, new QListWidgetItem(QLocale().toString(date, QLocale::LongFormat))); 0572 Q_EMIT contentsChanged(); 0573 } 0574 mExceptionDateList->setCurrentItem(mExceptionDateList->item(index)); 0575 enableExceptionButtons(); 0576 } 0577 0578 /****************************************************************************** 0579 * Change the currently highlighted exception date to that entered in the 0580 * exception date edit control. 0581 */ 0582 void RecurrenceEdit::changeException() 0583 { 0584 if (!mExceptionDateEdit || !mExceptionDateEdit->date().isValid()) 0585 return; 0586 QListWidgetItem* item = mExceptionDateList->currentItem(); 0587 if (item && item->isSelected()) 0588 { 0589 const int index = mExceptionDateList->row(item); 0590 const QDate olddate = mExceptionDates.at(index); 0591 const QDate newdate = mExceptionDateEdit->date(); 0592 if (newdate != olddate) 0593 { 0594 mExceptionDates.removeAt(index); 0595 mExceptionDateList->takeItem(index); 0596 Q_EMIT contentsChanged(); 0597 addException(); 0598 } 0599 } 0600 } 0601 0602 /****************************************************************************** 0603 * Delete the currently highlighted exception date. 0604 */ 0605 void RecurrenceEdit::deleteException() 0606 { 0607 QListWidgetItem* item = mExceptionDateList->currentItem(); 0608 if (item && item->isSelected()) 0609 { 0610 const int index = mExceptionDateList->row(item); 0611 mExceptionDates.removeAt(index); 0612 mExceptionDateList->takeItem(index); 0613 Q_EMIT contentsChanged(); 0614 enableExceptionButtons(); 0615 } 0616 } 0617 0618 /****************************************************************************** 0619 * Enable/disable the exception group buttons according to whether any item is 0620 * selected in the exceptions listbox. 0621 */ 0622 void RecurrenceEdit::enableExceptionButtons() 0623 { 0624 QListWidgetItem* item = mExceptionDateList->currentItem(); 0625 const bool enable = item; 0626 if (mDeleteExceptionButton) 0627 mDeleteExceptionButton->setEnabled(enable); 0628 if (mChangeExceptionButton) 0629 mChangeExceptionButton->setEnabled(enable); 0630 0631 // Prevent the exceptions list box receiving keyboard focus is it's empty 0632 mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? Qt::WheelFocus : Qt::NoFocus); 0633 } 0634 0635 /****************************************************************************** 0636 * Notify this instance of a change in the alarm start date. 0637 */ 0638 void RecurrenceEdit::setStartDate(const QDate& start, const QDate& today) 0639 { 0640 if (!mReadOnly) 0641 { 0642 setRuleDefaults(start); 0643 if (start < today) 0644 { 0645 mEndDateEdit->setMinimumDate(today); 0646 if (mExceptionDateEdit) 0647 mExceptionDateEdit->setMinimumDate(today); 0648 } 0649 else 0650 { 0651 const QString startString = i18nc("@info", "Date cannot be earlier than start date"); 0652 mEndDateEdit->setMinimumDate(start, startString); 0653 if (mExceptionDateEdit) 0654 mExceptionDateEdit->setMinimumDate(start, startString); 0655 } 0656 } 0657 } 0658 0659 /****************************************************************************** 0660 * Specify the default recurrence end date. 0661 */ 0662 void RecurrenceEdit::setDefaultEndDate(const QDate& end) 0663 { 0664 if (!mEndDateButton->isChecked()) 0665 mEndDateEdit->setDate(end); 0666 } 0667 0668 void RecurrenceEdit::setEndDateTime(const KADateTime& end) 0669 { 0670 const KADateTime edt = end.toTimeSpec(mCurrStartDateTime.timeSpec()); 0671 mEndDateEdit->setDate(edt.date()); 0672 mEndTimeEdit->setValue(edt.time()); 0673 mEndTimeEdit->setEnabled(!end.isDateOnly()); 0674 mEndAnyTimeCheckBox->setChecked(end.isDateOnly()); 0675 } 0676 0677 KADateTime RecurrenceEdit::endDateTime() const 0678 { 0679 if (mRuleButtonGroup->checkedButton() == mAtLoginButton && mEndAnyTimeCheckBox->isChecked()) 0680 return KADateTime(mEndDateEdit->date(), mCurrStartDateTime.timeSpec()); 0681 return KADateTime(mEndDateEdit->date(), mEndTimeEdit->time(), mCurrStartDateTime.timeSpec()); 0682 } 0683 0684 /****************************************************************************** 0685 * Set all controls to their default values. 0686 */ 0687 void RecurrenceEdit::setDefaults(const KADateTime& from) 0688 { 0689 mCurrStartDateTime = from; 0690 const QDate fromDate = from.date(); 0691 mNoEndDateButton->setChecked(true); 0692 0693 mSubDailyRule->setFrequency(1); 0694 mDailyRule->setFrequency(1); 0695 mWeeklyRule->setFrequency(1); 0696 mMonthlyRule->setFrequency(1); 0697 mYearlyRule->setFrequency(1); 0698 0699 setRuleDefaults(fromDate); 0700 mMonthlyRule->setType(MonthYearRule::DATE); // date in month 0701 mYearlyRule->setType(MonthYearRule::DATE); // date in year 0702 0703 mEndDateEdit->setDate(fromDate); 0704 0705 mNoEmitTypeChanged = true; 0706 RadioButton* button; 0707 switch (Preferences::defaultRecurPeriod()) 0708 { 0709 case Preferences::Recur_Login: button = mAtLoginButton; break; 0710 case Preferences::Recur_Yearly: button = mYearlyButton; break; 0711 case Preferences::Recur_Monthly: button = mMonthlyButton; break; 0712 case Preferences::Recur_Weekly: button = mWeeklyButton; break; 0713 case Preferences::Recur_Daily: button = mDailyButton; break; 0714 case Preferences::Recur_SubDaily: button = mSubDailyButton; break; 0715 case Preferences::Recur_None: 0716 default: button = mNoneButton; break; 0717 } 0718 button->setChecked(true); 0719 mNoEmitTypeChanged = false; 0720 rangeTypeClicked(); 0721 enableExceptionButtons(); 0722 0723 saveState(); 0724 } 0725 0726 /****************************************************************************** 0727 * Set the controls for weekly, monthly and yearly rules which have not so far 0728 * been shown, to their default values, depending on the recurrence start date. 0729 */ 0730 void RecurrenceEdit::setRuleDefaults(const QDate& fromDate) 0731 { 0732 const int day = fromDate.day(); 0733 const int dayOfWeek = fromDate.dayOfWeek(); 0734 const int month = fromDate.month(); 0735 if (!mDailyShown) 0736 mDailyRule->setDays(true); 0737 if (!mWeeklyShown) 0738 mWeeklyRule->setDay(dayOfWeek); 0739 if (!mMonthlyShown) 0740 mMonthlyRule->setDefaultValues(day, dayOfWeek); 0741 if (!mYearlyShown) 0742 mYearlyRule->setDefaultValues(day, dayOfWeek, month); 0743 } 0744 0745 /****************************************************************************** 0746 * Initialise the recurrence to select repeat-at-login. 0747 * This function and set() are mutually exclusive: call one or the other, not both. 0748 */ 0749 void RecurrenceEdit::setRepeatAtLogin() 0750 { 0751 mAtLoginButton->setChecked(true); 0752 mEndDateButton->setChecked(true); 0753 } 0754 0755 /****************************************************************************** 0756 * Set the state of all controls to reflect the data in the specified event. 0757 */ 0758 void RecurrenceEdit::set(const KAEvent& event) 0759 { 0760 setDefaults(event.mainDateTime().kDateTime()); 0761 if (event.repeatAtLogin()) 0762 { 0763 mAtLoginButton->setChecked(true); 0764 mEndDateButton->setChecked(true); 0765 return; 0766 } 0767 mNoneButton->setChecked(true); 0768 KARecurrence* recurrence = event.recurrence(); 0769 if (!recurrence) 0770 return; 0771 const KARecurrence::Type rtype = recurrence->type(); 0772 switch (rtype) 0773 { 0774 case KARecurrence::MINUTELY: 0775 mSubDailyButton->setChecked(true); 0776 break; 0777 0778 case KARecurrence::DAILY: 0779 { 0780 mDailyButton->setChecked(true); 0781 const QBitArray rDays = recurrence->days(); 0782 if (rDays.count(true)) 0783 mDailyRule->setDays(rDays); 0784 else 0785 mDailyRule->setDays(true); 0786 break; 0787 } 0788 case KARecurrence::WEEKLY: 0789 { 0790 mWeeklyButton->setChecked(true); 0791 const QBitArray rDays = recurrence->days(); 0792 mWeeklyRule->setDays(rDays); 0793 break; 0794 } 0795 case KARecurrence::MONTHLY_POS: // on nth (Tuesday) of the month 0796 { 0797 const QList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions(); 0798 int i = posns.first().pos(); 0799 if (!i) 0800 { 0801 // It's every (Tuesday) of the month. Convert to a weekly recurrence 0802 // (but ignoring any non-every xxxDay positions). 0803 mWeeklyButton->setChecked(true); 0804 mWeeklyRule->setFrequency(recurrence->frequency()); 0805 QBitArray rDays(7); 0806 for (const RecurrenceRule::WDayPos& posn : posns) 0807 { 0808 if (!posn.pos()) 0809 rDays.setBit(posn.day() - 1, true); 0810 } 0811 mWeeklyRule->setDays(rDays); 0812 break; 0813 } 0814 mMonthlyButton->setChecked(true); 0815 mMonthlyRule->setPosition(i, posns.first().day()); 0816 break; 0817 } 0818 case KARecurrence::MONTHLY_DAY: // on nth day of the month 0819 { 0820 mMonthlyButton->setChecked(true); 0821 const QList<int> rmd = recurrence->monthDays(); 0822 const int day = (rmd.isEmpty()) ? event.mainDateTime().date().day() : rmd.first(); 0823 mMonthlyRule->setDate(day); 0824 break; 0825 } 0826 case KARecurrence::ANNUAL_DATE: // on the nth day of (months...) in the year 0827 case KARecurrence::ANNUAL_POS: // on the nth (Tuesday) of (months...) in the year 0828 { 0829 if (rtype == KARecurrence::ANNUAL_DATE) 0830 { 0831 mYearlyButton->setChecked(true); 0832 const QList<int> rmd = recurrence->monthDays(); 0833 const int day = (rmd.isEmpty()) ? event.mainDateTime().date().day() : rmd.first(); 0834 mYearlyRule->setDate(day); 0835 mYearlyRule->setFeb29Type(recurrence->feb29Type()); 0836 } 0837 else if (rtype == KARecurrence::ANNUAL_POS) 0838 { 0839 mYearlyButton->setChecked(true); 0840 const QList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions(); 0841 mYearlyRule->setPosition(posns.first().pos(), posns.first().day()); 0842 } 0843 mYearlyRule->setMonths(recurrence->yearMonths()); 0844 break; 0845 } 0846 default: 0847 return; 0848 } 0849 0850 mRule->setFrequency(recurrence->frequency()); 0851 0852 // Get range information 0853 KADateTime endtime = mCurrStartDateTime; 0854 const int duration = recurrence->duration(); 0855 if (duration == -1) 0856 mNoEndDateButton->setChecked(true); 0857 else if (duration) 0858 { 0859 mRepeatCountButton->setChecked(true); 0860 mRepeatCountEntry->setValue(duration); 0861 } 0862 else 0863 { 0864 mEndDateButton->setChecked(true); 0865 endtime = recurrence->endDateTime(); 0866 mEndTimeEdit->setValue(endtime.time()); 0867 } 0868 mEndDateEdit->setDate(endtime.date()); 0869 0870 // Get exception information 0871 mExceptionDates = event.recurrence()->exDates(); 0872 std::sort(mExceptionDates.begin(), mExceptionDates.end()); 0873 mExceptionDateList->clear(); 0874 for (const QDate& exceptionDate : std::as_const(mExceptionDates)) 0875 new QListWidgetItem(QLocale().toString(exceptionDate, QLocale::LongFormat), mExceptionDateList); 0876 enableExceptionButtons(); 0877 mExcludeHolidays->setChecked(event.holidaysExcluded()); 0878 mWorkTimeOnly->setChecked(event.workTimeOnly()); 0879 0880 // Get repetition within recurrence 0881 mSubRepetition->set(event.repetition()); 0882 0883 rangeTypeClicked(); 0884 0885 saveState(); 0886 } 0887 0888 /****************************************************************************** 0889 * Update the specified KAEvent with the entered recurrence data. 0890 * If 'adjustStart' is true, the start date/time will be adjusted if necessary 0891 * to be the first date/time which recurs on or after the original start. 0892 */ 0893 void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart) 0894 { 0895 // Get end date and repeat count, common to all types of recurring events 0896 QDate endDate; 0897 QTime endTime; 0898 int repeatCount; 0899 if (mNoEndDateButton->isChecked()) 0900 repeatCount = -1; 0901 else if (mRepeatCountButton->isChecked()) 0902 repeatCount = mRepeatCountEntry->value(); 0903 else 0904 { 0905 repeatCount = 0; 0906 endDate = mEndDateEdit->date(); 0907 endTime = mEndTimeEdit->time(); 0908 } 0909 0910 // Set up the recurrence according to the type selected 0911 event.startChanges(); 0912 QAbstractButton* button = mRuleButtonGroup->checkedButton(); 0913 event.setRepeatAtLogin(button == mAtLoginButton); 0914 const int frequency = mRule ? mRule->frequency() : 0; 0915 if (button == mSubDailyButton) 0916 { 0917 const KADateTime endDateTime(endDate, endTime, mCurrStartDateTime.timeSpec()); 0918 event.setRecurMinutely(frequency, repeatCount, endDateTime); 0919 } 0920 else if (button == mDailyButton) 0921 { 0922 event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate); 0923 } 0924 else if (button == mWeeklyButton) 0925 { 0926 event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate); 0927 } 0928 else if (button == mMonthlyButton) 0929 { 0930 if (mMonthlyRule->type() == MonthlyRule::POS) 0931 { 0932 // It's by position 0933 KAEvent::MonthPos pos; 0934 pos.days.fill(false); 0935 pos.days.setBit(mMonthlyRule->dayOfWeek() - 1); 0936 pos.weeknum = mMonthlyRule->week(); 0937 QList<KAEvent::MonthPos> poses(1, pos); 0938 event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate); 0939 } 0940 else 0941 { 0942 // It's by day 0943 const int daynum = mMonthlyRule->date(); 0944 QList<int> daynums(1, daynum); 0945 event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate); 0946 } 0947 } 0948 else if (button == mYearlyButton) 0949 { 0950 const QList<int> months = mYearlyRule->months(); 0951 if (mYearlyRule->type() == YearlyRule::POS) 0952 { 0953 // It's by position 0954 KAEvent::MonthPos pos; 0955 pos.days.fill(false); 0956 pos.days.setBit(mYearlyRule->dayOfWeek() - 1); 0957 pos.weeknum = mYearlyRule->week(); 0958 QList<KAEvent::MonthPos> poses(1, pos); 0959 event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate); 0960 } 0961 else 0962 { 0963 // It's by date in month 0964 event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(), 0965 mYearlyRule->feb29Type(), repeatCount, endDate); 0966 } 0967 } 0968 else 0969 { 0970 event.setNoRecur(); 0971 event.endChanges(); 0972 return; 0973 } 0974 if (!event.recurs()) 0975 { 0976 event.endChanges(); 0977 return; // an error occurred setting up the recurrence 0978 } 0979 if (adjustStart) 0980 event.setFirstRecurrence(); 0981 0982 // Set up repetition within the recurrence 0983 // N.B. This requires the main recurrence to be set up first. 0984 event.setRepetition((mRuleButtonType < SUBDAILY) ? Repetition() : mSubRepetition->repetition()); 0985 0986 // Set up exceptions 0987 event.recurrence()->setExDates(mExceptionDates); 0988 event.setWorkTimeOnly(mWorkTimeOnly->isChecked()); 0989 event.setExcludeHolidays(mExcludeHolidays->isChecked()); 0990 0991 event.endChanges(); 0992 } 0993 0994 /****************************************************************************** 0995 * Save the state of all controls. 0996 */ 0997 void RecurrenceEdit::saveState() 0998 { 0999 mSavedRuleButton = mRuleButtonGroup->checkedButton(); 1000 if (mRule) 1001 mRule->saveState(); 1002 mSavedRangeButton = mRangeButtonGroup->checkedButton(); 1003 if (mSavedRangeButton == mRepeatCountButton) 1004 mSavedRecurCount = mRepeatCountEntry->value(); 1005 else if (mSavedRangeButton == mEndDateButton) 1006 { 1007 mSavedEndDateTime = KADateTime(mEndDateEdit->date(), mEndTimeEdit->time(), mCurrStartDateTime.timeSpec()); 1008 mSavedEndDateTime.setDateOnly(mEndAnyTimeCheckBox->isChecked()); 1009 } 1010 mSavedExceptionDates = mExceptionDates; 1011 mSavedWorkTimeOnly = mWorkTimeOnly->isChecked(); 1012 mSavedExclHolidays = mExcludeHolidays->isChecked(); 1013 mSavedRepetition = mSubRepetition->repetition(); 1014 } 1015 1016 /****************************************************************************** 1017 * Check whether any of the controls have changed state since initialisation. 1018 */ 1019 bool RecurrenceEdit::stateChanged() const 1020 { 1021 if (mSavedRuleButton != mRuleButtonGroup->checkedButton() 1022 || mSavedRangeButton != mRangeButtonGroup->checkedButton() 1023 || (mRule && mRule->stateChanged())) 1024 return true; 1025 if (mSavedRangeButton == mRepeatCountButton 1026 && mSavedRecurCount != mRepeatCountEntry->value()) 1027 return true; 1028 if (mSavedRangeButton == mEndDateButton) 1029 { 1030 KADateTime edt(mEndDateEdit->date(), mEndTimeEdit->time(), mCurrStartDateTime.timeSpec()); 1031 edt.setDateOnly(mEndAnyTimeCheckBox->isChecked()); 1032 if (mSavedEndDateTime != edt) 1033 return true; 1034 } 1035 if (mSavedExceptionDates != mExceptionDates 1036 || mSavedWorkTimeOnly != mWorkTimeOnly->isChecked() 1037 || mSavedExclHolidays != mExcludeHolidays->isChecked() 1038 || mSavedRepetition != mSubRepetition->repetition()) 1039 return true; 1040 return false; 1041 } 1042 1043 1044 1045 /*============================================================================= 1046 = Class Rule 1047 = Base class for rule widgets, including recurrence frequency. 1048 =============================================================================*/ 1049 1050 Rule::Rule(const QString& freqText, const QString& freqWhatsThis, bool time, bool readOnly, QWidget* parent) 1051 : NoRule(parent) 1052 { 1053 mLayout = new QVBoxLayout(this); 1054 mLayout->setContentsMargins(0, 0, 0, 0); 1055 1056 auto freqLayout = new QHBoxLayout(); 1057 freqLayout->setContentsMargins(0, 0, 0, 0); 1058 mLayout->addLayout(freqLayout); 1059 QWidget* box = new QWidget(this); // this is to control the QWhatsThis text display area 1060 freqLayout->addWidget(box, 0, Qt::AlignLeft); 1061 auto boxLayout = new QHBoxLayout(box); 1062 boxLayout->setContentsMargins(0, 0, 0, 0); 1063 1064 QLabel* label = new QLabel(i18nc("@label:spinbox", "Recur every"), box); 1065 boxLayout->addWidget(label, 0, Qt::AlignLeft); 1066 if (time) 1067 { 1068 mIntSpinBox = nullptr; 1069 mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box); 1070 mTimeSpinBox->setReadOnly(readOnly); 1071 boxLayout->addWidget(mSpinBox, 0, Qt::AlignLeft); 1072 connect(mTimeSpinBox, &TimeSpinBox::valueChanged, this, &Rule::frequencyChanged); 1073 connect(mTimeSpinBox, &TimeSpinBox::valueChanged, this, &Rule::changed); 1074 } 1075 else 1076 { 1077 mTimeSpinBox = nullptr; 1078 mSpinBox = mIntSpinBox = new SpinBox(1, 999, box); 1079 mIntSpinBox->setReadOnly(readOnly); 1080 boxLayout->addWidget(mSpinBox, 0, Qt::AlignLeft); 1081 connect(mIntSpinBox, &QSpinBox::valueChanged, this, &Rule::frequencyChanged); 1082 connect(mIntSpinBox, &QSpinBox::valueChanged, this, &Rule::changed); 1083 } 1084 label->setBuddy(mSpinBox); 1085 label = new QLabel(freqText, box); 1086 boxLayout->addWidget(label, 0, Qt::AlignLeft); 1087 box->setFixedSize(sizeHint()); 1088 box->setWhatsThis(freqWhatsThis); 1089 } 1090 1091 int Rule::frequency() const 1092 { 1093 if (mIntSpinBox) 1094 return mIntSpinBox->value(); 1095 if (mTimeSpinBox) 1096 return mTimeSpinBox->value(); 1097 return 0; 1098 } 1099 1100 void Rule::setFrequency(int n) 1101 { 1102 if (mIntSpinBox) 1103 mIntSpinBox->setValue(n); 1104 if (mTimeSpinBox) 1105 mTimeSpinBox->setValue(n); 1106 } 1107 1108 /****************************************************************************** 1109 * Save the state of all controls. 1110 */ 1111 void Rule::saveState() 1112 { 1113 mSavedFrequency = frequency(); 1114 } 1115 1116 /****************************************************************************** 1117 * Check whether any of the controls have changed state since initialisation. 1118 */ 1119 bool Rule::stateChanged() const 1120 { 1121 return (mSavedFrequency != frequency()); 1122 } 1123 1124 1125 /*============================================================================= 1126 = Class SubDailyRule 1127 = Sub-daily rule widget. 1128 =============================================================================*/ 1129 1130 SubDailyRule::SubDailyRule(bool readOnly, QWidget* parent) 1131 : Rule(i18nc("@label Time units for user-entered numbers", "hours:minutes"), 1132 i18nc("@info:whatsthis", "Enter the number of hours and minutes between repetitions of the alarm"), 1133 true, readOnly, parent) 1134 { } 1135 1136 1137 /*============================================================================= 1138 = Class DayWeekRule 1139 = Daily/weekly rule widget base class. 1140 =============================================================================*/ 1141 1142 DayWeekRule::DayWeekRule(const QString& freqText, const QString& freqWhatsThis, const QString& daysWhatsThis, 1143 bool readOnly, QWidget* parent) 1144 : Rule(freqText, freqWhatsThis, false, readOnly, parent) 1145 , mSavedDays(7) 1146 { 1147 auto grid = new QGridLayout(); 1148 grid->setContentsMargins(0, 0, 0, 0); 1149 grid->setRowStretch(0, 1); 1150 layout()->addLayout(grid); 1151 1152 QLabel* label = new QLabel(i18nc("@label On: Tuesday", "On:"), this); 1153 grid->addWidget(label, 0, 0, Qt::AlignRight | Qt::AlignTop); 1154 grid->setColumnMinimumWidth(1, style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 1155 1156 // List the days of the week starting at the user's start day of the week. 1157 // Save the first day of the week, just in case it changes while the dialog is open. 1158 QWidget* box = new QWidget(this); // this is to control the QWhatsThis text display area 1159 auto dgrid = new QGridLayout(box); 1160 dgrid->setContentsMargins(0, 0, 0, 0); 1161 QLocale locale; 1162 for (int i = 0; i < 7; ++i) 1163 { 1164 const int day = Locale::localeDayInWeek_to_weekDay(i); 1165 mDayBox[i] = new CheckBox(locale.dayName(day), box); 1166 mDayBox[i]->setReadOnly(readOnly); 1167 connect(mDayBox[i], &QAbstractButton::toggled, this, &Rule::changed); 1168 dgrid->addWidget(mDayBox[i], i%4, i/4, Qt::AlignLeft); 1169 } 1170 box->setFixedSize(box->sizeHint()); 1171 box->setWhatsThis(daysWhatsThis); 1172 grid->addWidget(box, 0, 2, Qt::AlignLeft); 1173 label->setBuddy(mDayBox[0]); 1174 grid->setColumnStretch(3, 1); 1175 } 1176 1177 /****************************************************************************** 1178 * Fetch which days of the week have been ticked. 1179 */ 1180 QBitArray DayWeekRule::days() const 1181 { 1182 QBitArray ds(7); 1183 ds.fill(false); 1184 for (int i = 0; i < 7; ++i) 1185 if (mDayBox[i]->isChecked()) 1186 ds.setBit(Locale::localeDayInWeek_to_weekDay(i) - 1, true); 1187 return ds; 1188 } 1189 1190 /****************************************************************************** 1191 * Tick/untick every day of the week. 1192 */ 1193 void DayWeekRule::setDays(bool tick) 1194 { 1195 for (int i = 0; i < 7; ++i) 1196 mDayBox[i]->setChecked(tick); 1197 } 1198 1199 /****************************************************************************** 1200 * Tick/untick each day of the week according to the specified bits. 1201 */ 1202 void DayWeekRule::setDays(const QBitArray& days) 1203 { 1204 if (days.size() != 7) 1205 { 1206 qCWarning(KALARM_LOG) << "DayWeekRule::setDays: Error! 'days' parameter must have 7 elements: actual size" << days.size(); 1207 return; 1208 } 1209 for (int i = 0; i < 7; ++i) 1210 { 1211 bool x = days.testBit(Locale::localeDayInWeek_to_weekDay(i) - 1); 1212 mDayBox[i]->setChecked(x); 1213 } 1214 } 1215 1216 /****************************************************************************** 1217 * Tick the specified day of the week, and untick all other days. 1218 */ 1219 void DayWeekRule::setDay(int dayOfWeek) 1220 { 1221 for (int i = 0; i < 7; ++i) 1222 mDayBox[i]->setChecked(false); 1223 if (dayOfWeek > 0 && dayOfWeek <= 7) 1224 mDayBox[Locale::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true); 1225 } 1226 1227 /****************************************************************************** 1228 * Validate: check that at least one day is selected. 1229 */ 1230 QWidget* DayWeekRule::validate(QString& errorMessage) 1231 { 1232 for (int i = 0; i < 7; ++i) 1233 if (mDayBox[i]->isChecked()) 1234 return nullptr; 1235 errorMessage = i18nc("@info", "No day selected"); 1236 return mDayBox[0]; 1237 } 1238 1239 /****************************************************************************** 1240 * Save the state of all controls. 1241 */ 1242 void DayWeekRule::saveState() 1243 { 1244 Rule::saveState(); 1245 mSavedDays = days(); 1246 } 1247 1248 /****************************************************************************** 1249 * Check whether any of the controls have changed state since initialisation. 1250 */ 1251 bool DayWeekRule::stateChanged() const 1252 { 1253 return (Rule::stateChanged() 1254 || mSavedDays != days()); 1255 } 1256 1257 1258 /*============================================================================= 1259 = Class DailyRule 1260 = Daily rule widget. 1261 =============================================================================*/ 1262 1263 DailyRule::DailyRule(bool readOnly, QWidget* parent) 1264 : DayWeekRule(i18nc("@label Time unit for user-entered number", "day(s)"), 1265 i18nc("@info:whatsthis", "Enter the number of days between repetitions of the alarm"), 1266 i18nc("@info:whatsthis", "Select the days of the week on which the alarm is allowed to occur"), 1267 readOnly, parent) 1268 { } 1269 1270 1271 /*============================================================================= 1272 = Class WeeklyRule 1273 = Weekly rule widget. 1274 =============================================================================*/ 1275 1276 WeeklyRule::WeeklyRule(bool readOnly, QWidget* parent) 1277 : DayWeekRule(i18nc("@label Time unit for user-entered number", "week(s)"), 1278 i18nc("@info:whatsthis", "Enter the number of weeks between repetitions of the alarm"), 1279 i18nc("@info:whatsthis", "Select the days of the week on which to repeat the alarm"), 1280 readOnly, parent) 1281 { } 1282 1283 1284 /*============================================================================= 1285 = Class MonthYearRule 1286 = Monthly/yearly rule widget base class. 1287 =============================================================================*/ 1288 1289 MonthYearRule::MonthYearRule(const QString& freqText, const QString& freqWhatsThis, bool allowEveryWeek, 1290 bool readOnly, QWidget* parent) 1291 : Rule(freqText, freqWhatsThis, false, readOnly, parent) 1292 , mEveryWeek(allowEveryWeek) 1293 { 1294 mButtonGroup = new ButtonGroup(this); 1295 1296 // Month day selector 1297 auto boxLayout = new QGridLayout(); 1298 boxLayout->setContentsMargins(0, 0, 0, 0); 1299 layout()->addLayout(boxLayout); 1300 1301 mDayButton = new RadioButton(i18nc("@option:radio On day number in the month", "On day"), this); 1302 mDayButton->setFixedSize(mDayButton->sizeHint()); 1303 mDayButton->setReadOnly(readOnly); 1304 mButtonGroup->addButton(mDayButton); 1305 mDayButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on the selected day of the month")); 1306 boxLayout->addWidget(mDayButton, 0, 0); 1307 1308 QLocale locale; 1309 mDayCombo = new ComboBox(this); 1310 mDayCombo->setEditable(false); 1311 mDayCombo->setMaxVisibleItems(11); 1312 for (int i = 0; i < 31; ++i) 1313 mDayCombo->addItem(locale.toString(i + 1)); 1314 mDayCombo->addItem(i18nc("@item:inlistbox Last day of month", "Last")); 1315 mDayCombo->setReadOnly(readOnly); 1316 mDayCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the month on which to repeat the alarm")); 1317 mDayButton->setFocusWidget(mDayCombo); 1318 connect(mDayCombo, &ComboBox::activated, this, &MonthYearRule::slotDaySelected); 1319 connect(mDayCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed); 1320 boxLayout->addWidget(mDayCombo, 0, 1, 1, 2, Qt::AlignLeft); 1321 1322 // Month position selector 1323 mPosButton = new RadioButton(i18nc("@option:radio On the 1st Tuesday", "On the"), this); 1324 mPosButton->setFixedSize(mPosButton->sizeHint()); 1325 mPosButton->setReadOnly(readOnly); 1326 mButtonGroup->addButton(mPosButton); 1327 mPosButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on one day of the week, in the selected week of the month")); 1328 boxLayout->addWidget(mPosButton, 1, 0); 1329 1330 mWeekCombo = new ComboBox(this); 1331 mWeekCombo->setEditable(false); 1332 mWeekCombo->addItem(i18nc("@item:inlistbox", "1st")); 1333 mWeekCombo->addItem(i18nc("@item:inlistbox", "2nd")); 1334 mWeekCombo->addItem(i18nc("@item:inlistbox", "3rd")); 1335 mWeekCombo->addItem(i18nc("@item:inlistbox", "4th")); 1336 mWeekCombo->addItem(i18nc("@item:inlistbox", "5th")); 1337 mWeekCombo->addItem(i18nc("@item:inlistbox Last Monday in March", "Last")); 1338 mWeekCombo->addItem(i18nc("@item:inlistbox", "2nd Last")); 1339 mWeekCombo->addItem(i18nc("@item:inlistbox", "3rd Last")); 1340 mWeekCombo->addItem(i18nc("@item:inlistbox", "4th Last")); 1341 mWeekCombo->addItem(i18nc("@item:inlistbox", "5th Last")); 1342 if (mEveryWeek) 1343 { 1344 mWeekCombo->addItem(i18nc("@item:inlistbox Every (Monday...) in month", "Every")); 1345 mWeekCombo->setMaxVisibleItems(11); 1346 } 1347 mWeekCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the week of the month in which to repeat the alarm")); 1348 mWeekCombo->setReadOnly(readOnly); 1349 mPosButton->setFocusWidget(mWeekCombo); 1350 connect(mWeekCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed); 1351 boxLayout->addWidget(mWeekCombo, 1, 1); 1352 1353 mDayOfWeekCombo = new ComboBox(this); 1354 mDayOfWeekCombo->setEditable(false); 1355 for (int i = 0; i < 7; ++i) 1356 { 1357 int day = Locale::localeDayInWeek_to_weekDay(i); 1358 mDayOfWeekCombo->addItem(locale.dayName(day)); 1359 } 1360 mDayOfWeekCombo->setReadOnly(readOnly); 1361 mDayOfWeekCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the week on which to repeat the alarm")); 1362 connect(mDayOfWeekCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed); 1363 boxLayout->addWidget(mDayOfWeekCombo, 1, 2, Qt::AlignLeft); 1364 1365 connect(mButtonGroup, &ButtonGroup::buttonSet, this, &MonthYearRule::clicked); 1366 connect(mButtonGroup, &ButtonGroup::buttonSet, this, &MonthYearRule::changed); 1367 } 1368 1369 MonthYearRule::DayPosType MonthYearRule::type() const 1370 { 1371 return (mButtonGroup->checkedButton() == mDayButton) ? DATE : POS; 1372 } 1373 1374 void MonthYearRule::setType(MonthYearRule::DayPosType type) 1375 { 1376 if (type == DATE) 1377 mDayButton->setChecked(true); 1378 else 1379 mPosButton->setChecked(true); 1380 } 1381 1382 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek) 1383 { 1384 --dayOfMonth; 1385 mDayCombo->setCurrentIndex(dayOfMonth); 1386 mWeekCombo->setCurrentIndex(dayOfMonth / 7); 1387 mDayOfWeekCombo->setCurrentIndex(Locale::weekDay_to_localeDayInWeek(dayOfWeek)); 1388 } 1389 1390 int MonthYearRule::date() const 1391 { 1392 const int daynum = mDayCombo->currentIndex() + 1; 1393 return (daynum <= 31) ? daynum : 31 - daynum; 1394 } 1395 1396 int MonthYearRule::week() const 1397 { 1398 int weeknum = mWeekCombo->currentIndex() + 1; 1399 return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum; 1400 } 1401 1402 int MonthYearRule::dayOfWeek() const 1403 { 1404 return Locale::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentIndex()); 1405 } 1406 1407 void MonthYearRule::setDate(int dayOfMonth) 1408 { 1409 mDayButton->setChecked(true);; 1410 mDayCombo->setCurrentIndex(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0); // day 0 shouldn't ever occur 1411 } 1412 1413 void MonthYearRule::setPosition(int week, int dayOfWeek) 1414 { 1415 mPosButton->setChecked(true); 1416 mWeekCombo->setCurrentIndex((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0); 1417 mDayOfWeekCombo->setCurrentIndex(Locale::weekDay_to_localeDayInWeek(dayOfWeek)); 1418 } 1419 1420 void MonthYearRule::enableSelection(DayPosType type) 1421 { 1422 const bool date = (type == DATE); 1423 mDayCombo->setEnabled(date); 1424 mWeekCombo->setEnabled(!date); 1425 mDayOfWeekCombo->setEnabled(!date); 1426 } 1427 1428 void MonthYearRule::clicked(QAbstractButton* button) 1429 { 1430 enableSelection(button == mDayButton ? DATE : POS); 1431 } 1432 1433 void MonthYearRule::slotDaySelected(int index) 1434 { 1435 daySelected(index <= 30 ? index + 1 : 30 - index); 1436 } 1437 1438 /****************************************************************************** 1439 * Save the state of all controls. 1440 */ 1441 void MonthYearRule::saveState() 1442 { 1443 Rule::saveState(); 1444 mSavedType = type(); 1445 if (mSavedType == DATE) 1446 mSavedDay = date(); 1447 else 1448 { 1449 mSavedWeek = week(); 1450 mSavedWeekDay = dayOfWeek(); 1451 } 1452 } 1453 1454 /****************************************************************************** 1455 * Check whether any of the controls have changed state since initialisation. 1456 */ 1457 bool MonthYearRule::stateChanged() const 1458 { 1459 if (Rule::stateChanged() 1460 || mSavedType != type()) 1461 return true; 1462 if (mSavedType == DATE) 1463 { 1464 if (mSavedDay != date()) 1465 return true; 1466 } 1467 else 1468 { 1469 if (mSavedWeek != week() 1470 || mSavedWeekDay != dayOfWeek()) 1471 return true; 1472 } 1473 return false; 1474 } 1475 1476 1477 /*============================================================================= 1478 = Class MonthlyRule 1479 = Monthly rule widget. 1480 =============================================================================*/ 1481 1482 MonthlyRule::MonthlyRule(bool readOnly, QWidget* parent) 1483 : MonthYearRule(i18nc("@label Time unit for user-entered number", "month(s)"), 1484 i18nc("@info:whatsthis", "Enter the number of months between repetitions of the alarm"), 1485 false, readOnly, parent) 1486 { } 1487 1488 1489 /*============================================================================= 1490 = Class YearlyRule 1491 = Yearly rule widget. 1492 =============================================================================*/ 1493 1494 YearlyRule::YearlyRule(bool readOnly, QWidget* parent) 1495 : MonthYearRule(i18nc("@label Time unit for user-entered number", "year(s)"), 1496 i18nc("@info:whatsthis", "Enter the number of years between repetitions of the alarm"), 1497 true, readOnly, parent) 1498 { 1499 // Set up the month selection widgets 1500 auto hlayout = new QHBoxLayout(); 1501 hlayout->setContentsMargins(0, 0, 0, 0); 1502 layout()->addLayout(hlayout); 1503 QLabel* label = new QLabel(i18nc("@label List of months to select", "Months:"), this); 1504 hlayout->addWidget(label, 0, Qt::AlignLeft | Qt::AlignTop); 1505 1506 // List the months of the year. 1507 QWidget* w = new QWidget(this); // this is to control the QWhatsThis text display area 1508 hlayout->addWidget(w, 1, Qt::AlignLeft); 1509 auto grid = new QGridLayout(w); 1510 grid->setContentsMargins(0, 0, 0, 0); 1511 QLocale locale; 1512 for (int i = 0; i < 12; ++i) 1513 { 1514 mMonthBox[i] = new CheckBox(locale.monthName(i + 1, QLocale::ShortFormat), w); 1515 mMonthBox[i]->setReadOnly(readOnly); 1516 connect(mMonthBox[i], &QAbstractButton::toggled, this, &Rule::changed); 1517 grid->addWidget(mMonthBox[i], i%3, i/3, Qt::AlignLeft); 1518 } 1519 connect(mMonthBox[1], &QAbstractButton::toggled, this, &YearlyRule::enableFeb29); 1520 w->setFixedHeight(w->sizeHint().height()); 1521 w->setWhatsThis(i18nc("@info:whatsthis", "Select the months of the year in which to repeat the alarm")); 1522 1523 // February 29th handling option 1524 auto f29box = new QHBoxLayout; 1525 layout()->addLayout(f29box); 1526 w = new QWidget(this); // this is to control the QWhatsThis text display area 1527 f29box->addWidget(w, 0, Qt::AlignLeft); 1528 auto boxLayout = new QHBoxLayout(w); 1529 boxLayout->setContentsMargins(0, 0, 0, 0); 1530 mFeb29Label = new QLabel(i18nc("@label:listbox", "February 29th alarm in non-leap years:")); 1531 boxLayout->addWidget(mFeb29Label); 1532 mFeb29Combo = new ComboBox(); 1533 mFeb29Combo->setEditable(false); 1534 mFeb29Combo->addItem(i18nc("@item:inlistbox No date", "None")); 1535 mFeb29Combo->addItem(i18nc("@item:inlistbox 1st March (short form)", "1 Mar")); 1536 mFeb29Combo->addItem(i18nc("@item:inlistbox 28th February (short form)", "28 Feb")); 1537 mFeb29Combo->setReadOnly(readOnly); 1538 connect(mFeb29Combo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &YearlyRule::changed); 1539 mFeb29Label->setBuddy(mFeb29Combo); 1540 boxLayout->addWidget(mFeb29Combo); 1541 w->setFixedSize(w->sizeHint()); 1542 w->setWhatsThis(i18nc("@info:whatsthis", "Select which date, if any, the February 29th alarm should trigger in non-leap years")); 1543 } 1544 1545 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month) 1546 { 1547 MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek); 1548 --month; 1549 for (int i = 0; i < 12; ++i) 1550 mMonthBox[i]->setChecked(i == month); 1551 setFeb29Type(KARecurrence::defaultFeb29Type()); 1552 daySelected(dayOfMonth); // enable/disable month checkboxes as appropriate 1553 } 1554 1555 /****************************************************************************** 1556 * Fetch which months have been checked (1 - 12). 1557 * Reply = true if February has been checked. 1558 */ 1559 QList<int> YearlyRule::months() const 1560 { 1561 QList<int> mnths; 1562 for (int i = 0; i < 12; ++i) 1563 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled()) 1564 mnths.append(i + 1); 1565 return mnths; 1566 } 1567 1568 /****************************************************************************** 1569 * Check/uncheck each month of the year according to the specified list. 1570 */ 1571 void YearlyRule::setMonths(const QList<int>& mnths) 1572 { 1573 bool checked[12]; 1574 for (int i = 0; i < 12; ++i) 1575 checked[i] = false; 1576 for (int i = 0, end = mnths.count(); i < end; ++i) 1577 checked[mnths[i] - 1] = true; 1578 for (int i = 0; i < 12; ++i) 1579 mMonthBox[i]->setChecked(checked[i]); 1580 enableFeb29(); 1581 } 1582 1583 /****************************************************************************** 1584 * Return the date for February 29th alarms in non-leap years. 1585 */ 1586 KARecurrence::Feb29Type YearlyRule::feb29Type() const 1587 { 1588 if (mFeb29Combo->isEnabled()) 1589 { 1590 switch (mFeb29Combo->currentIndex()) 1591 { 1592 case 1: return KARecurrence::Feb29_Mar1; 1593 case 2: return KARecurrence::Feb29_Feb28; 1594 default: break; 1595 } 1596 } 1597 return KARecurrence::Feb29_None; 1598 } 1599 1600 /****************************************************************************** 1601 * Set the date for February 29th alarms to trigger in non-leap years. 1602 */ 1603 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type) 1604 { 1605 int index; 1606 switch (type) 1607 { 1608 default: 1609 case KARecurrence::Feb29_None: index = 0; break; 1610 case KARecurrence::Feb29_Mar1: index = 1; break; 1611 case KARecurrence::Feb29_Feb28: index = 2; break; 1612 } 1613 mFeb29Combo->setCurrentIndex(index); 1614 } 1615 1616 /****************************************************************************** 1617 * Validate: check that at least one month is selected. 1618 */ 1619 QWidget* YearlyRule::validate(QString& errorMessage) 1620 { 1621 for (int i = 0; i < 12; ++i) 1622 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled()) 1623 return nullptr; 1624 errorMessage = i18nc("@info", "No month selected"); 1625 return mMonthBox[0]; 1626 } 1627 1628 /****************************************************************************** 1629 * Called when a yearly recurrence type radio button is clicked, 1630 * to enable/disable month checkboxes as appropriate for the date selected. 1631 */ 1632 void YearlyRule::clicked(QAbstractButton* button) 1633 { 1634 MonthYearRule::clicked(button); 1635 daySelected(buttonType(button) == DATE ? date() : 1); 1636 } 1637 1638 /****************************************************************************** 1639 * Called when a day of the month is selected in a yearly recurrence, to 1640 * disable months for which the day is out of range. 1641 */ 1642 void YearlyRule::daySelected(int day) 1643 { 1644 mMonthBox[1]->setEnabled(day <= 29); // February 1645 const bool enable = (day != 31); 1646 mMonthBox[3]->setEnabled(enable); // April 1647 mMonthBox[5]->setEnabled(enable); // June 1648 mMonthBox[8]->setEnabled(enable); // September 1649 mMonthBox[10]->setEnabled(enable); // November 1650 enableFeb29(); 1651 } 1652 1653 /****************************************************************************** 1654 * Enable/disable the February 29th combo box depending on whether February 1655 * 29th is selected. 1656 */ 1657 void YearlyRule::enableFeb29() 1658 { 1659 const bool enable = (type() == DATE && date() == 29 && mMonthBox[1]->isChecked() && mMonthBox[1]->isEnabled()); 1660 mFeb29Label->setEnabled(enable); 1661 mFeb29Combo->setEnabled(enable); 1662 } 1663 1664 /****************************************************************************** 1665 * Save the state of all controls. 1666 */ 1667 void YearlyRule::saveState() 1668 { 1669 MonthYearRule::saveState(); 1670 mSavedMonths = months(); 1671 mSavedFeb29Type = feb29Type(); 1672 } 1673 1674 /****************************************************************************** 1675 * Check whether any of the controls have changed state since initialisation. 1676 */ 1677 bool YearlyRule::stateChanged() const 1678 { 1679 return (MonthYearRule::stateChanged() 1680 || mSavedMonths != months() 1681 || mSavedFeb29Type != feb29Type()); 1682 } 1683 1684 #include "moc_recurrenceedit_p.cpp" 1685 #include "moc_recurrenceedit.cpp" 1686 1687 // vim: et sw=4: