SVN_SILENT made messages (after extraction)
[kdepim.git] / kalarm / recurrenceedit.cpp
blob0eb0c325779a3ab146a76d66cfc8400b41c24daa
1 /*
2 * recurrenceedit.cpp - widget to edit the event's recurrence definition
3 * Program: kalarm
4 * Copyright © 2002-2016 by David Jarvie <djarvie@kde.org>
6 * Based originally on KOrganizer module koeditorrecurrence.cpp,
7 * Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "kalarm.h"
25 #include "recurrenceedit.h"
26 #include "recurrenceedit_p.h"
28 #include "alarmtimewidget.h"
29 #include "checkbox.h"
30 #include "combobox.h"
31 #include "kalarmapp.h"
32 #include "kalocale.h"
33 #include "preferences.h"
34 #include "radiobutton.h"
35 #include "repetitionbutton.h"
36 #include "spinbox.h"
37 #include "timeedit.h"
38 #include "timespinbox.h"
39 #include "buttongroup.h"
41 #include <kalarmcal/kaevent.h>
42 #include <kalarmcal/karecurrence.h>
44 #include <KCalCore/Event>
45 using namespace KCalCore;
47 #include <KLocalizedString>
48 #include <kcalendarsystem.h>
49 #include <kmessagebox.h>
50 #include <kdatecombobox.h>
52 #include <QHBoxLayout>
53 #include <QPushButton>
54 #include <QLabel>
55 #include <QStackedWidget>
56 #include <QListWidget>
57 #include <QGroupBox>
58 #include <QGridLayout>
59 #include <QHBoxLayout>
60 #include <QVBoxLayout>
61 #include <QtAlgorithms>
62 #include "kalarm_debug.h"
65 class ListWidget : public QListWidget
67 public:
68 explicit ListWidget(QWidget* parent) : QListWidget(parent) {}
69 QSize sizeHint() const Q_DECL_OVERRIDE { return minimumSizeHint(); }
72 // Collect these widget labels together to ensure consistent wording and
73 // translations across different modules.
74 QString RecurrenceEdit::i18n_combo_NoRecur() { return i18nc("@item:inlistbox Recurrence type", "No Recurrence"); }
75 QString RecurrenceEdit::i18n_combo_AtLogin() { return i18nc("@item:inlistbox Recurrence type", "At Login"); }
76 QString RecurrenceEdit::i18n_combo_HourlyMinutely() { return i18nc("@item:inlistbox Recurrence type", "Hourly/Minutely"); }
77 QString RecurrenceEdit::i18n_combo_Daily() { return i18nc("@item:inlistbox Recurrence type", "Daily"); }
78 QString RecurrenceEdit::i18n_combo_Weekly() { return i18nc("@item:inlistbox Recurrence type", "Weekly"); }
79 QString RecurrenceEdit::i18n_combo_Monthly() { return i18nc("@item:inlistbox Recurrence type", "Monthly"); }
80 QString RecurrenceEdit::i18n_combo_Yearly() { return i18nc("@item:inlistbox Recurrence type", "Yearly"); }
83 RecurrenceEdit::RecurrenceEdit(bool readOnly, QWidget* parent)
84 : QFrame(parent),
85 mRule(Q_NULLPTR),
86 mRuleButtonType(INVALID_RECUR),
87 mDailyShown(false),
88 mWeeklyShown(false),
89 mMonthlyShown(false),
90 mYearlyShown(false),
91 mNoEmitTypeChanged(true),
92 mReadOnly(readOnly)
94 qCDebug(KALARM_LOG);
95 QVBoxLayout* topLayout = new QVBoxLayout(this);
96 topLayout->setMargin(0);
97 topLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
99 /* Create the recurrence rule Group box which holds the recurrence period
100 * selection buttons, and the weekly, monthly and yearly recurrence rule
101 * frames which specify options individual to each of these distinct
102 * sections of the recurrence rule. Each frame is made visible by the
103 * selection of its corresponding radio button.
106 QGroupBox* recurGroup = new QGroupBox(i18nc("@title:group", "Recurrence Rule"), this);
107 topLayout->addWidget(recurGroup);
108 QHBoxLayout* hlayout = new QHBoxLayout(recurGroup);
109 hlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
110 hlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultChildMargin)); // use margin spacing due to vertical divider line
112 // Recurrence period radio buttons
113 QVBoxLayout* vlayout = new QVBoxLayout();
114 vlayout->setSpacing(0);
115 vlayout->setMargin(0);
116 hlayout->addLayout(vlayout);
117 mRuleButtonGroup = new ButtonGroup(recurGroup);
118 connect(mRuleButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::periodClicked);
119 connect(mRuleButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::contentsChanged);
121 mNoneButton = new RadioButton(i18n_combo_NoRecur(), recurGroup);
122 mNoneButton->setFixedSize(mNoneButton->sizeHint());
123 mNoneButton->setReadOnly(mReadOnly);
124 mNoneButton->setWhatsThis(i18nc("@info:whatsthis", "Do not repeat the alarm"));
125 mRuleButtonGroup->addButton(mNoneButton);
126 vlayout->addWidget(mNoneButton);
128 mAtLoginButton = new RadioButton(i18n_combo_AtLogin(), recurGroup);
129 mAtLoginButton->setFixedSize(mAtLoginButton->sizeHint());
130 mAtLoginButton->setReadOnly(mReadOnly);
131 mAtLoginButton->setWhatsThis(xi18nc("@info:whatsthis",
132 "<para>Trigger the alarm at the specified date/time and at every login until then.</para>"
133 "<para>Note that it will also be triggered any time <application>KAlarm</application> is restarted.</para>"));
134 mRuleButtonGroup->addButton(mAtLoginButton);
135 vlayout->addWidget(mAtLoginButton);
137 mSubDailyButton = new RadioButton(i18n_combo_HourlyMinutely(), recurGroup);
138 mSubDailyButton->setFixedSize(mSubDailyButton->sizeHint());
139 mSubDailyButton->setReadOnly(mReadOnly);
140 mSubDailyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at hourly/minutely intervals"));
141 mRuleButtonGroup->addButton(mSubDailyButton);
142 vlayout->addWidget(mSubDailyButton);
144 mDailyButton = new RadioButton(i18n_combo_Daily(), recurGroup);
145 mDailyButton->setFixedSize(mDailyButton->sizeHint());
146 mDailyButton->setReadOnly(mReadOnly);
147 mDailyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at daily intervals"));
148 mRuleButtonGroup->addButton(mDailyButton);
149 vlayout->addWidget(mDailyButton);
151 mWeeklyButton = new RadioButton(i18n_combo_Weekly(), recurGroup);
152 mWeeklyButton->setFixedSize(mWeeklyButton->sizeHint());
153 mWeeklyButton->setReadOnly(mReadOnly);
154 mWeeklyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at weekly intervals"));
155 mRuleButtonGroup->addButton(mWeeklyButton);
156 vlayout->addWidget(mWeeklyButton);
158 mMonthlyButton = new RadioButton(i18n_combo_Monthly(), recurGroup);
159 mMonthlyButton->setFixedSize(mMonthlyButton->sizeHint());
160 mMonthlyButton->setReadOnly(mReadOnly);
161 mMonthlyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at monthly intervals"));
162 mRuleButtonGroup->addButton(mMonthlyButton);
163 vlayout->addWidget(mMonthlyButton);
165 mYearlyButton = new RadioButton(i18n_combo_Yearly(), recurGroup);
166 mYearlyButton->setFixedSize(mYearlyButton->sizeHint());
167 mYearlyButton->setReadOnly(mReadOnly);
168 mYearlyButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at annual intervals"));
169 mRuleButtonGroup->addButton(mYearlyButton);
170 vlayout->addWidget(mYearlyButton);
171 vlayout->addStretch(); // top-adjust the interval radio buttons
173 // Sub-repetition button
174 mSubRepetition = new RepetitionButton(i18nc("@action:button", "Sub-Repetition"), true, recurGroup);
175 mSubRepetition->setFixedSize(mSubRepetition->sizeHint());
176 mSubRepetition->setReadOnly(mReadOnly);
177 mSubRepetition->setWhatsThis(i18nc("@info:whatsthis",
178 "Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due."));
179 connect(mSubRepetition, &RepetitionButton::needsInitialisation, this, &RecurrenceEdit::repeatNeedsInitialisation);
180 connect(mSubRepetition, &RepetitionButton::changed, this, &RecurrenceEdit::frequencyChanged);
181 connect(mSubRepetition, &RepetitionButton::changed, this, &RecurrenceEdit::contentsChanged);
182 vlayout->addSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
183 vlayout->addWidget(mSubRepetition);
185 // Vertical divider line
186 vlayout = new QVBoxLayout();
187 vlayout->setMargin(0);
188 hlayout->addLayout(vlayout);
189 QFrame* divider = new QFrame(recurGroup);
190 divider->setFrameStyle(QFrame::VLine | QFrame::Sunken);
191 vlayout->addWidget(divider, 1);
193 // Rule definition stack
194 mRuleStack = new QStackedWidget(recurGroup);
195 hlayout->addWidget(mRuleStack);
196 hlayout->addStretch(1);
197 mNoRule = new NoRule(mRuleStack);
198 mSubDailyRule = new SubDailyRule(mReadOnly, mRuleStack);
199 mDailyRule = new DailyRule(mReadOnly, mRuleStack);
200 mWeeklyRule = new WeeklyRule(mReadOnly, mRuleStack);
201 mMonthlyRule = new MonthlyRule(mReadOnly, mRuleStack);
202 mYearlyRule = new YearlyRule(mReadOnly, mRuleStack);
204 connect(mSubDailyRule, &SubDailyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged);
205 connect(mDailyRule, &DailyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged);
206 connect(mWeeklyRule, &WeeklyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged);
207 connect(mMonthlyRule, &MonthlyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged);
208 connect(mYearlyRule, &YearlyRule::frequencyChanged, this, &RecurrenceEdit::frequencyChanged);
209 connect(mSubDailyRule, &SubDailyRule::changed, this, &RecurrenceEdit::contentsChanged);
210 connect(mDailyRule, &DailyRule::changed, this, &RecurrenceEdit::contentsChanged);
211 connect(mWeeklyRule, &WeeklyRule::changed, this, &RecurrenceEdit::contentsChanged);
212 connect(mMonthlyRule, &MonthlyRule::changed, this, &RecurrenceEdit::contentsChanged);
213 connect(mYearlyRule, &YearlyRule::changed, this, &RecurrenceEdit::contentsChanged);
215 mRuleStack->addWidget(mNoRule);
216 mRuleStack->addWidget(mSubDailyRule);
217 mRuleStack->addWidget(mDailyRule);
218 mRuleStack->addWidget(mWeeklyRule);
219 mRuleStack->addWidget(mMonthlyRule);
220 mRuleStack->addWidget(mYearlyRule);
221 hlayout->addSpacing(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
223 // Create the recurrence range group which contains the controls
224 // which specify how long the recurrence is to last.
226 mRangeButtonBox = new QGroupBox(i18nc("@title:group", "Recurrence End"), this);
227 topLayout->addWidget(mRangeButtonBox);
228 mRangeButtonGroup = new ButtonGroup(mRangeButtonBox);
229 connect(mRangeButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::rangeTypeClicked);
230 connect(mRangeButtonGroup, &ButtonGroup::buttonSet, this, &RecurrenceEdit::contentsChanged);
232 vlayout = new QVBoxLayout(mRangeButtonBox);
233 vlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
234 vlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
235 mNoEndDateButton = new RadioButton(i18nc("@option:radio", "No end"), mRangeButtonBox);
236 mNoEndDateButton->setFixedSize(mNoEndDateButton->sizeHint());
237 mNoEndDateButton->setReadOnly(mReadOnly);
238 mNoEndDateButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm indefinitely"));
239 mRangeButtonGroup->addButton(mNoEndDateButton);
240 vlayout->addWidget(mNoEndDateButton, 1, Qt::AlignLeft);
241 QSize size = mNoEndDateButton->size();
243 hlayout = new QHBoxLayout();
244 hlayout->setMargin(0);
245 vlayout->addLayout(hlayout);
246 mRepeatCountButton = new RadioButton(i18nc("@option:radio", "End after:"), mRangeButtonBox);
247 mRepeatCountButton->setReadOnly(mReadOnly);
248 mRepeatCountButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm for the number of times specified"));
249 mRangeButtonGroup->addButton(mRepeatCountButton);
250 mRepeatCountEntry = new SpinBox(1, 9999, mRangeButtonBox);
251 mRepeatCountEntry->setFixedSize(mRepeatCountEntry->sizeHint());
252 mRepeatCountEntry->setSingleShiftStep(10);
253 mRepeatCountEntry->setSelectOnStep(false);
254 mRepeatCountEntry->setReadOnly(mReadOnly);
255 mRepeatCountEntry->setWhatsThis(i18nc("@info:whatsthis", "Enter the total number of times to trigger the alarm"));
256 connect(mRepeatCountEntry, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged), this, &RecurrenceEdit::repeatCountChanged);
257 connect(mRepeatCountEntry, static_cast<void (SpinBox::*)(int)>(&SpinBox::valueChanged), this, &RecurrenceEdit::contentsChanged);
258 mRepeatCountButton->setFocusWidget(mRepeatCountEntry);
259 mRepeatCountLabel = new QLabel(i18nc("@label", "occurrence(s)"), mRangeButtonBox);
260 mRepeatCountLabel->setFixedSize(mRepeatCountLabel->sizeHint());
261 hlayout->addWidget(mRepeatCountButton);
262 hlayout->addSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
263 hlayout->addWidget(mRepeatCountEntry);
264 hlayout->addWidget(mRepeatCountLabel);
265 hlayout->addStretch();
266 size = size.expandedTo(mRepeatCountButton->sizeHint());
268 hlayout = new QHBoxLayout();
269 hlayout->setMargin(0);
270 vlayout->addLayout(hlayout);
271 mEndDateButton = new RadioButton(i18nc("@option:radio", "End by:"), mRangeButtonBox);
272 mEndDateButton->setReadOnly(mReadOnly);
273 mEndDateButton->setWhatsThis(
274 xi18nc("@info:whatsthis", "<para>Repeat the alarm until the date/time specified.</para>"
275 "<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>"));
276 mRangeButtonGroup->addButton(mEndDateButton);
277 mEndDateEdit = new KDateComboBox(mRangeButtonBox);
278 mEndDateEdit->setOptions(mReadOnly ? KDateComboBox::Options(0) : KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker);
279 static const QString tzText = i18nc("@info", "This uses the same time zone as the start time.");
280 mEndDateEdit->setWhatsThis(xi18nc("@info:whatsthis",
281 "<para>Enter the last date to repeat the alarm.</para><para>%1</para>", tzText));
282 connect(mEndDateEdit, &KDateComboBox::dateEdited, this, &RecurrenceEdit::contentsChanged);
283 mEndDateButton->setFocusWidget(mEndDateEdit);
284 mEndTimeEdit = new TimeEdit(mRangeButtonBox);
285 mEndTimeEdit->setFixedSize(mEndTimeEdit->sizeHint());
286 mEndTimeEdit->setReadOnly(mReadOnly);
287 mEndTimeEdit->setWhatsThis(xi18nc("@info:whatsthis",
288 "<para>Enter the last time to repeat the alarm.</para><para>%1</para><para>%2</para>", tzText, TimeSpinBox::shiftWhatsThis()));
289 connect(mEndTimeEdit, &TimeEdit::valueChanged, this, &RecurrenceEdit::contentsChanged);
290 mEndAnyTimeCheckBox = new CheckBox(i18nc("@option:check", "Any time"), mRangeButtonBox);
291 mEndAnyTimeCheckBox->setFixedSize(mEndAnyTimeCheckBox->sizeHint());
292 mEndAnyTimeCheckBox->setReadOnly(mReadOnly);
293 mEndAnyTimeCheckBox->setWhatsThis(i18nc("@info:whatsthis", "Stop repeating the alarm after your first login on or after the specified end date"));
294 connect(mEndAnyTimeCheckBox, &CheckBox::toggled, this, &RecurrenceEdit::slotAnyTimeToggled);
295 connect(mEndAnyTimeCheckBox, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged);
296 hlayout->addWidget(mEndDateButton);
297 hlayout->addSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
298 hlayout->addWidget(mEndDateEdit);
299 hlayout->addWidget(mEndTimeEdit);
300 hlayout->addWidget(mEndAnyTimeCheckBox);
301 hlayout->addStretch();
302 size = size.expandedTo(mEndDateButton->sizeHint());
304 // Line up the widgets to the right of the radio buttons
305 mRepeatCountButton->setFixedSize(size);
306 mEndDateButton->setFixedSize(size);
308 // Create the exceptions group which specifies dates to be excluded
309 // from the recurrence.
311 mExceptionGroup = new QGroupBox(i18nc("@title:group", "Exceptions"), this);
312 topLayout->addWidget(mExceptionGroup);
313 topLayout->setStretchFactor(mExceptionGroup, 2);
314 hlayout = new QHBoxLayout(mExceptionGroup);
315 hlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
316 hlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
317 vlayout = new QVBoxLayout();
318 vlayout->setMargin(0);
319 hlayout->addLayout(vlayout);
321 mExceptionDateList = new ListWidget(mExceptionGroup);
322 mExceptionDateList->setWhatsThis(i18nc("@info:whatsthis", "The list of exceptions, i.e. dates/times excluded from the recurrence"));
323 connect(mExceptionDateList, &QListWidget::currentRowChanged, this, &RecurrenceEdit::enableExceptionButtons);
324 vlayout->addWidget(mExceptionDateList);
326 if (mReadOnly)
328 mExceptionDateEdit = Q_NULLPTR;
329 mChangeExceptionButton = Q_NULLPTR;
330 mDeleteExceptionButton = Q_NULLPTR;
332 else
334 vlayout = new QVBoxLayout();
335 vlayout->setMargin(0);
336 hlayout->addLayout(vlayout);
337 mExceptionDateEdit = new KDateComboBox(mExceptionGroup);
338 mExceptionDateEdit->setOptions(mReadOnly ? KDateComboBox::Options(0) : KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker);
339 mExceptionDateEdit->setDate(KDateTime::currentLocalDate());
340 mExceptionDateEdit->setWhatsThis(i18nc("@info:whatsthis",
341 "Enter a date to insert in the exceptions list. "
342 "Use in conjunction with the Add or Change button below."));
343 vlayout->addWidget(mExceptionDateEdit, 0, Qt::AlignLeft);
345 hlayout = new QHBoxLayout();
346 hlayout->setMargin(0);
347 vlayout->addLayout(hlayout);
348 QPushButton* button = new QPushButton(i18nc("@action:button", "Add"), mExceptionGroup);
349 button->setWhatsThis(i18nc("@info:whatsthis", "Add the date entered above to the exceptions list"));
350 connect(button, &QPushButton::clicked, this, &RecurrenceEdit::addException);
351 hlayout->addWidget(button);
353 mChangeExceptionButton = new QPushButton(i18nc("@action:button", "Change"), mExceptionGroup);
354 mChangeExceptionButton->setWhatsThis(i18nc("@info:whatsthis",
355 "Replace the currently highlighted item in the exceptions list with the date entered above"));
356 connect(mChangeExceptionButton, &QPushButton::clicked, this, &RecurrenceEdit::changeException);
357 hlayout->addWidget(mChangeExceptionButton);
359 mDeleteExceptionButton = new QPushButton(i18nc("@action:button", "Delete"), mExceptionGroup);
360 mDeleteExceptionButton->setWhatsThis(i18nc("@info:whatsthis", "Remove the currently highlighted item from the exceptions list"));
361 connect(mDeleteExceptionButton, &QPushButton::clicked, this, &RecurrenceEdit::deleteException);
362 hlayout->addWidget(mDeleteExceptionButton);
365 vlayout->addStretch();
367 mExcludeHolidays = new CheckBox(i18nc("@option:check", "Exclude holidays"), mExceptionGroup);
368 mExcludeHolidays->setReadOnly(mReadOnly);
369 mExcludeHolidays->setWhatsThis(xi18nc("@info:whatsthis",
370 "<para>Do not trigger the alarm on holidays.</para>"
371 "<para>You can specify your holiday region in the Configuration dialog.</para>"));
372 connect(mExcludeHolidays, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged);
373 vlayout->addWidget(mExcludeHolidays);
375 mWorkTimeOnly = new CheckBox(i18nc("@option:check", "Only during working time"), mExceptionGroup);
376 mWorkTimeOnly->setReadOnly(mReadOnly);
377 mWorkTimeOnly->setWhatsThis(xi18nc("@info:whatsthis",
378 "<para>Only execute the alarm during working hours, on working days.</para>"
379 "<para>You can specify working days and hours in the Configuration dialog.</para>"));
380 connect(mWorkTimeOnly, &CheckBox::toggled, this, &RecurrenceEdit::contentsChanged);
381 vlayout->addWidget(mWorkTimeOnly);
383 topLayout->addStretch();
384 mNoEmitTypeChanged = false;
387 /******************************************************************************
388 * Show or hide the exception controls.
390 void RecurrenceEdit::showMoreOptions(bool more)
392 if (more)
393 mExceptionGroup->show();
394 else
395 mExceptionGroup->hide();
396 updateGeometry();
399 /******************************************************************************
400 * Verify the consistency of the entered data.
401 * Reply = widget to receive focus on error, or 0 if no error.
403 QWidget* RecurrenceEdit::checkData(const KDateTime& startDateTime, QString& errorMessage) const
405 if (mAtLoginButton->isChecked())
406 return Q_NULLPTR;
407 const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime;
408 if (mEndDateButton->isChecked())
410 // N.B. End date/time takes the same time spec as start date/time
411 QWidget* errWidget = Q_NULLPTR;
412 bool noTime = !mEndTimeEdit->isEnabled();
413 QDate endDate = mEndDateEdit->date();
414 if (endDate < startDateTime.date())
415 errWidget = mEndDateEdit;
416 else if (!noTime && QDateTime(endDate, mEndTimeEdit->time()) < startDateTime.dateTime())
417 errWidget = mEndTimeEdit;
418 if (errWidget)
420 errorMessage = noTime
421 ? i18nc("@info", "End date is earlier than start date")
422 : i18nc("@info", "End date/time is earlier than start date/time");
423 return errWidget;
426 if (!mRule)
427 return Q_NULLPTR;
428 return mRule->validate(errorMessage);
431 /******************************************************************************
432 * Called when a recurrence period radio button is clicked.
434 void RecurrenceEdit::periodClicked(QAbstractButton* button)
436 RepeatType oldType = mRuleButtonType;
437 bool none = (button == mNoneButton);
438 bool atLogin = (button == mAtLoginButton);
439 bool subdaily = (button == mSubDailyButton);
440 if (none)
442 mRule = Q_NULLPTR;
443 mRuleButtonType = NO_RECUR;
445 else if (atLogin)
447 mRule = Q_NULLPTR;
448 mRuleButtonType = AT_LOGIN;
449 mEndDateButton->setChecked(true);
451 else if (subdaily)
453 mRule = mSubDailyRule;
454 mRuleButtonType = SUBDAILY;
456 else if (button == mDailyButton)
458 mRule = mDailyRule;
459 mRuleButtonType = DAILY;
460 mDailyShown = true;
462 else if (button == mWeeklyButton)
464 mRule = mWeeklyRule;
465 mRuleButtonType = WEEKLY;
466 mWeeklyShown = true;
468 else if (button == mMonthlyButton)
470 mRule = mMonthlyRule;
471 mRuleButtonType = MONTHLY;
472 mMonthlyShown = true;
474 else if (button == mYearlyButton)
476 mRule = mYearlyRule;
477 mRuleButtonType = ANNUAL;
478 mYearlyShown = true;
480 else
481 return;
483 if (mRuleButtonType != oldType)
485 mRuleStack->setCurrentWidget(mRule ? mRule : mNoRule);
486 if (oldType == NO_RECUR || none)
487 mRangeButtonBox->setEnabled(!none);
488 mExceptionGroup->setEnabled(!(none || atLogin));
489 mEndAnyTimeCheckBox->setEnabled(atLogin);
490 if (!none)
492 mNoEndDateButton->setEnabled(!atLogin);
493 mRepeatCountButton->setEnabled(!atLogin);
495 rangeTypeClicked();
496 mSubRepetition->setEnabled(!(none || atLogin));
497 if (!mNoEmitTypeChanged)
498 Q_EMIT typeChanged(mRuleButtonType);
502 void RecurrenceEdit::slotAnyTimeToggled(bool on)
504 QAbstractButton* button = mRuleButtonGroup->checkedButton();
505 mEndTimeEdit->setEnabled((button == mAtLoginButton && !on)
506 || (button == mSubDailyButton && mEndDateButton->isChecked()));
509 /******************************************************************************
510 * Called when a recurrence range type radio button is clicked.
512 void RecurrenceEdit::rangeTypeClicked()
514 bool endDate = mEndDateButton->isChecked();
515 mEndDateEdit->setEnabled(endDate);
516 mEndTimeEdit->setEnabled(endDate
517 && ((mAtLoginButton->isChecked() && !mEndAnyTimeCheckBox->isChecked())
518 || mSubDailyButton->isChecked()));
519 bool repeatCount = mRepeatCountButton->isChecked();
520 mRepeatCountEntry->setEnabled(repeatCount);
521 mRepeatCountLabel->setEnabled(repeatCount);
524 void RecurrenceEdit::showEvent(QShowEvent*)
526 if (mRule)
527 mRule->setFrequencyFocus();
528 else
529 mRuleButtonGroup->checkedButton()->setFocus();
530 Q_EMIT shown();
533 /******************************************************************************
534 * Return the sub-repetition interval and count within the recurrence, i.e. the
535 * number of repetitions after the main recurrence.
537 Repetition RecurrenceEdit::subRepetition() const
539 return (mRuleButtonType >= SUBDAILY) ? mSubRepetition->repetition() : Repetition();
542 /******************************************************************************
543 * Called when the Sub-Repetition button has been pressed to display the
544 * sub-repetition dialog.
545 * Alarm repetition has the following restrictions:
546 * 1) Not allowed for a repeat-at-login alarm
547 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
548 * 3) The overall repeat duration must be less than the recurrence interval.
550 void RecurrenceEdit::setSubRepetition(int reminderMinutes, bool dateOnly)
552 int maxDuration;
553 switch (mRuleButtonType)
555 case RecurrenceEdit::NO_RECUR:
556 case RecurrenceEdit::AT_LOGIN: // alarm repeat not allowed
557 maxDuration = 0;
558 break;
559 default: // repeat duration must be less than recurrence interval
561 KAEvent event;
562 updateEvent(event, false);
563 maxDuration = event.longestRecurrenceInterval().asSeconds()/60 - reminderMinutes - 1;
564 break;
567 mSubRepetition->initialise(mSubRepetition->repetition(), dateOnly, maxDuration);
568 mSubRepetition->setEnabled(mRuleButtonType >= SUBDAILY && maxDuration);
571 /******************************************************************************
572 * Activate the sub-repetition dialog.
574 void RecurrenceEdit::activateSubRepetition()
576 mSubRepetition->activate();
579 /******************************************************************************
580 * Called when the value of the repeat count field changes, to reset the
581 * minimum value to 1 if the value was 0.
583 void RecurrenceEdit::repeatCountChanged(int value)
585 if (value > 0 && mRepeatCountEntry->minimum() == 0)
586 mRepeatCountEntry->setMinimum(1);
589 /******************************************************************************
590 * Add the date entered in the exception date edit control to the list of
591 * exception dates.
593 void RecurrenceEdit::addException()
595 if (!mExceptionDateEdit || !mExceptionDateEdit->date().isValid())
596 return;
597 QDate date = mExceptionDateEdit->date();
598 DateList::Iterator it;
599 int index = 0;
600 bool insert = true;
601 for (it = mExceptionDates.begin(); it != mExceptionDates.end(); ++index, ++it)
603 if (date <= *it)
605 insert = (date != *it);
606 break;
609 if (insert)
611 mExceptionDates.insert(it, date);
612 mExceptionDateList->insertItem(index, new QListWidgetItem(KLocale::global()->formatDate(date)));
613 Q_EMIT contentsChanged();
615 mExceptionDateList->setCurrentItem(mExceptionDateList->item(index));
616 enableExceptionButtons();
619 /******************************************************************************
620 * Change the currently highlighted exception date to that entered in the
621 * exception date edit control.
623 void RecurrenceEdit::changeException()
625 if (!mExceptionDateEdit || !mExceptionDateEdit->date().isValid())
626 return;
627 QListWidgetItem* item = mExceptionDateList->currentItem();
628 if (item && mExceptionDateList->isItemSelected(item))
630 int index = mExceptionDateList->row(item);
631 QDate olddate = mExceptionDates[index];
632 QDate newdate = mExceptionDateEdit->date();
633 if (newdate != olddate)
635 mExceptionDates.removeAt(index);
636 mExceptionDateList->takeItem(index);
637 Q_EMIT contentsChanged();
638 addException();
643 /******************************************************************************
644 * Delete the currently highlighted exception date.
646 void RecurrenceEdit::deleteException()
648 QListWidgetItem* item = mExceptionDateList->currentItem();
649 if (item && mExceptionDateList->isItemSelected(item))
651 int index = mExceptionDateList->row(item);
652 mExceptionDates.removeAt(index);
653 mExceptionDateList->takeItem(index);
654 Q_EMIT contentsChanged();
655 enableExceptionButtons();
659 /******************************************************************************
660 * Enable/disable the exception group buttons according to whether any item is
661 * selected in the exceptions listbox.
663 void RecurrenceEdit::enableExceptionButtons()
665 QListWidgetItem* item = mExceptionDateList->currentItem();
666 bool enable = item;
667 if (mDeleteExceptionButton)
668 mDeleteExceptionButton->setEnabled(enable);
669 if (mChangeExceptionButton)
670 mChangeExceptionButton->setEnabled(enable);
672 // Prevent the exceptions list box receiving keyboard focus is it's empty
673 mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? Qt::WheelFocus : Qt::NoFocus);
676 /******************************************************************************
677 * Notify this instance of a change in the alarm start date.
679 void RecurrenceEdit::setStartDate(const QDate& start, const QDate& today)
681 if (!mReadOnly)
683 setRuleDefaults(start);
684 if (start < today)
686 mEndDateEdit->setMinimumDate(today);
687 if (mExceptionDateEdit)
688 mExceptionDateEdit->setMinimumDate(today);
690 else
692 const QString startString = i18nc("@info", "Date cannot be earlier than start date");
693 mEndDateEdit->setMinimumDate(start, startString);
694 if (mExceptionDateEdit)
695 mExceptionDateEdit->setMinimumDate(start, startString);
700 /******************************************************************************
701 * Specify the default recurrence end date.
703 void RecurrenceEdit::setDefaultEndDate(const QDate& end)
705 if (!mEndDateButton->isChecked())
706 mEndDateEdit->setDate(end);
709 void RecurrenceEdit::setEndDateTime(const KDateTime& end)
711 KDateTime edt = end.toTimeSpec(mCurrStartDateTime.timeSpec());
712 mEndDateEdit->setDate(edt.date());
713 mEndTimeEdit->setValue(edt.time());
714 mEndTimeEdit->setEnabled(!end.isDateOnly());
715 mEndAnyTimeCheckBox->setChecked(end.isDateOnly());
718 KDateTime RecurrenceEdit::endDateTime() const
720 if (mRuleButtonGroup->checkedButton() == mAtLoginButton && mEndAnyTimeCheckBox->isChecked())
721 return KDateTime(mEndDateEdit->date(), mCurrStartDateTime.timeSpec());
722 return KDateTime(mEndDateEdit->date(), mEndTimeEdit->time(), mCurrStartDateTime.timeSpec());
725 /******************************************************************************
726 * Set all controls to their default values.
728 void RecurrenceEdit::setDefaults(const KDateTime& from)
730 mCurrStartDateTime = from;
731 QDate fromDate = from.date();
732 mNoEndDateButton->setChecked(true);
734 mSubDailyRule->setFrequency(1);
735 mDailyRule->setFrequency(1);
736 mWeeklyRule->setFrequency(1);
737 mMonthlyRule->setFrequency(1);
738 mYearlyRule->setFrequency(1);
740 setRuleDefaults(fromDate);
741 mMonthlyRule->setType(MonthYearRule::DATE); // date in month
742 mYearlyRule->setType(MonthYearRule::DATE); // date in year
744 mEndDateEdit->setDate(fromDate);
746 mNoEmitTypeChanged = true;
747 RadioButton* button;
748 switch (Preferences::defaultRecurPeriod())
750 case AT_LOGIN: button = mAtLoginButton; break;
751 case ANNUAL: button = mYearlyButton; break;
752 case MONTHLY: button = mMonthlyButton; break;
753 case WEEKLY: button = mWeeklyButton; break;
754 case DAILY: button = mDailyButton; break;
755 case SUBDAILY: button = mSubDailyButton; break;
756 case NO_RECUR:
757 default: button = mNoneButton; break;
759 button->setChecked(true);
760 mNoEmitTypeChanged = false;
761 rangeTypeClicked();
762 enableExceptionButtons();
764 saveState();
767 /******************************************************************************
768 * Set the controls for weekly, monthly and yearly rules which have not so far
769 * been shown, to their default values, depending on the recurrence start date.
771 void RecurrenceEdit::setRuleDefaults(const QDate& fromDate)
773 int day = fromDate.day();
774 int dayOfWeek = fromDate.dayOfWeek();
775 int month = fromDate.month();
776 if (!mDailyShown)
777 mDailyRule->setDays(true);
778 if (!mWeeklyShown)
779 mWeeklyRule->setDay(dayOfWeek);
780 if (!mMonthlyShown)
781 mMonthlyRule->setDefaultValues(day, dayOfWeek);
782 if (!mYearlyShown)
783 mYearlyRule->setDefaultValues(day, dayOfWeek, month);
786 /******************************************************************************
787 * Initialise the recurrence to select repeat-at-login.
788 * This function and set() are mutually exclusive: call one or the other, not both.
790 void RecurrenceEdit::setRepeatAtLogin()
792 mAtLoginButton->setChecked(true);
793 mEndDateButton->setChecked(true);
796 /******************************************************************************
797 * Set the state of all controls to reflect the data in the specified event.
799 void RecurrenceEdit::set(const KAEvent& event)
801 setDefaults(event.mainDateTime().kDateTime());
802 if (event.repeatAtLogin())
804 mAtLoginButton->setChecked(true);
805 mEndDateButton->setChecked(true);
806 return;
808 mNoneButton->setChecked(true);
809 KARecurrence* recurrence = event.recurrence();
810 if (!recurrence)
811 return;
812 KARecurrence::Type rtype = recurrence->type();
813 switch (rtype)
815 case KARecurrence::MINUTELY:
816 mSubDailyButton->setChecked(true);
817 break;
819 case KARecurrence::DAILY:
821 mDailyButton->setChecked(true);
822 QBitArray rDays = recurrence->days();
823 bool set = false;
824 for (int i = 0; i < 7 && !set; ++i)
825 set = rDays.testBit(i);
826 if (set)
827 mDailyRule->setDays(rDays);
828 else
829 mDailyRule->setDays(true);
830 break;
832 case KARecurrence::WEEKLY:
834 mWeeklyButton->setChecked(true);
835 QBitArray rDays = recurrence->days();
836 mWeeklyRule->setDays(rDays);
837 break;
839 case KARecurrence::MONTHLY_POS: // on nth (Tuesday) of the month
841 QList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions();
842 int i = posns.first().pos();
843 if (!i)
845 // It's every (Tuesday) of the month. Convert to a weekly recurrence
846 // (but ignoring any non-every xxxDay positions).
847 mWeeklyButton->setChecked(true);
848 mWeeklyRule->setFrequency(recurrence->frequency());
849 QBitArray rDays(7);
850 for (int i = 0, end = posns.count(); i < end; ++i)
852 if (!posns[i].pos())
853 rDays.setBit(posns[i].day() - 1, 1);
855 mWeeklyRule->setDays(rDays);
856 break;
858 mMonthlyButton->setChecked(true);
859 mMonthlyRule->setPosition(i, posns.first().day());
860 break;
862 case KARecurrence::MONTHLY_DAY: // on nth day of the month
864 mMonthlyButton->setChecked(true);
865 QList<int> rmd = recurrence->monthDays();
866 int day = (rmd.isEmpty()) ? event.mainDateTime().date().day() : rmd.first();
867 mMonthlyRule->setDate(day);
868 break;
870 case KARecurrence::ANNUAL_DATE: // on the nth day of (months...) in the year
871 case KARecurrence::ANNUAL_POS: // on the nth (Tuesday) of (months...) in the year
873 if (rtype == KARecurrence::ANNUAL_DATE)
875 mYearlyButton->setChecked(true);
876 const QList<int> rmd = recurrence->monthDays();
877 int day = (rmd.isEmpty()) ? event.mainDateTime().date().day() : rmd.first();
878 mYearlyRule->setDate(day);
879 mYearlyRule->setFeb29Type(recurrence->feb29Type());
881 else if (rtype == KARecurrence::ANNUAL_POS)
883 mYearlyButton->setChecked(true);
884 QList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions();
885 mYearlyRule->setPosition(posns.first().pos(), posns.first().day());
887 mYearlyRule->setMonths(recurrence->yearMonths());
888 break;
890 default:
891 return;
894 mRule->setFrequency(recurrence->frequency());
896 // Get range information
897 KDateTime endtime = mCurrStartDateTime;
898 int duration = recurrence->duration();
899 if (duration == -1)
900 mNoEndDateButton->setChecked(true);
901 else if (duration)
903 mRepeatCountButton->setChecked(true);
904 mRepeatCountEntry->setValue(duration);
906 else
908 mEndDateButton->setChecked(true);
909 endtime = recurrence->endDateTime();
910 mEndTimeEdit->setValue(endtime.time());
912 mEndDateEdit->setDate(endtime.date());
914 // Get exception information
915 mExceptionDates = event.recurrence()->exDates();
916 qSort(mExceptionDates);
917 mExceptionDateList->clear();
918 for (int i = 0, iend = mExceptionDates.count(); i < iend; ++i)
919 new QListWidgetItem(KLocale::global()->formatDate(mExceptionDates[i]), mExceptionDateList);
920 enableExceptionButtons();
921 mExcludeHolidays->setChecked(event.holidaysExcluded());
922 mWorkTimeOnly->setChecked(event.workTimeOnly());
924 // Get repetition within recurrence
925 mSubRepetition->set(event.repetition());
927 rangeTypeClicked();
929 saveState();
932 /******************************************************************************
933 * Update the specified KAEvent with the entered recurrence data.
934 * If 'adjustStart' is true, the start date/time will be adjusted if necessary
935 * to be the first date/time which recurs on or after the original start.
937 void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart)
939 // Get end date and repeat count, common to all types of recurring events
940 QDate endDate;
941 QTime endTime;
942 int repeatCount;
943 if (mNoEndDateButton->isChecked())
944 repeatCount = -1;
945 else if (mRepeatCountButton->isChecked())
946 repeatCount = mRepeatCountEntry->value();
947 else
949 repeatCount = 0;
950 endDate = mEndDateEdit->date();
951 endTime = mEndTimeEdit->time();
954 // Set up the recurrence according to the type selected
955 event.startChanges();
956 QAbstractButton* button = mRuleButtonGroup->checkedButton();
957 event.setRepeatAtLogin(button == mAtLoginButton);
958 int frequency = mRule ? mRule->frequency() : 0;
959 if (button == mSubDailyButton)
961 KDateTime endDateTime(endDate, endTime, mCurrStartDateTime.timeSpec());
962 event.setRecurMinutely(frequency, repeatCount, endDateTime);
964 else if (button == mDailyButton)
966 event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate);
968 else if (button == mWeeklyButton)
970 event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate);
972 else if (button == mMonthlyButton)
974 if (mMonthlyRule->type() == MonthlyRule::POS)
976 // It's by position
977 KAEvent::MonthPos pos;
978 pos.days.fill(false);
979 pos.days.setBit(mMonthlyRule->dayOfWeek() - 1);
980 pos.weeknum = mMonthlyRule->week();
981 QVector<KAEvent::MonthPos> poses(1, pos);
982 event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate);
984 else
986 // It's by day
987 int daynum = mMonthlyRule->date();
988 QVector<int> daynums(1, daynum);
989 event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
992 else if (button == mYearlyButton)
994 QVector<int> months = mYearlyRule->months();
995 if (mYearlyRule->type() == YearlyRule::POS)
997 // It's by position
998 KAEvent::MonthPos pos;
999 pos.days.fill(false);
1000 pos.days.setBit(mYearlyRule->dayOfWeek() - 1);
1001 pos.weeknum = mYearlyRule->week();
1002 QVector<KAEvent::MonthPos> poses(1, pos);
1003 event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate);
1005 else
1007 // It's by date in month
1008 event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(),
1009 mYearlyRule->feb29Type(), repeatCount, endDate);
1012 else
1014 event.setNoRecur();
1015 event.endChanges();
1016 return;
1018 if (!event.recurs())
1020 event.endChanges();
1021 return; // an error occurred setting up the recurrence
1023 if (adjustStart)
1024 event.setFirstRecurrence();
1026 // Set up repetition within the recurrence
1027 // N.B. This requires the main recurrence to be set up first.
1028 event.setRepetition((mRuleButtonType < SUBDAILY) ? Repetition() : mSubRepetition->repetition());
1030 // Set up exceptions
1031 event.recurrence()->setExDates(mExceptionDates);
1032 event.setWorkTimeOnly(mWorkTimeOnly->isChecked());
1033 event.setExcludeHolidays(mExcludeHolidays->isChecked());
1035 event.endChanges();
1038 /******************************************************************************
1039 * Save the state of all controls.
1041 void RecurrenceEdit::saveState()
1043 mSavedRuleButton = mRuleButtonGroup->checkedButton();
1044 if (mRule)
1045 mRule->saveState();
1046 mSavedRangeButton = mRangeButtonGroup->checkedButton();
1047 if (mSavedRangeButton == mRepeatCountButton)
1048 mSavedRecurCount = mRepeatCountEntry->value();
1049 else if (mSavedRangeButton == mEndDateButton)
1051 mSavedEndDateTime = KDateTime(QDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mCurrStartDateTime.timeSpec());
1052 mSavedEndDateTime.setDateOnly(mEndAnyTimeCheckBox->isChecked());
1054 mSavedExceptionDates = mExceptionDates;
1055 mSavedWorkTimeOnly = mWorkTimeOnly->isChecked();
1056 mSavedExclHolidays = mExcludeHolidays->isChecked();
1057 mSavedRepetition = mSubRepetition->repetition();
1060 /******************************************************************************
1061 * Check whether any of the controls have changed state since initialisation.
1063 bool RecurrenceEdit::stateChanged() const
1065 if (mSavedRuleButton != mRuleButtonGroup->checkedButton()
1066 || mSavedRangeButton != mRangeButtonGroup->checkedButton()
1067 || (mRule && mRule->stateChanged()))
1068 return true;
1069 if (mSavedRangeButton == mRepeatCountButton
1070 && mSavedRecurCount != mRepeatCountEntry->value())
1071 return true;
1072 if (mSavedRangeButton == mEndDateButton)
1074 KDateTime edt(QDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mCurrStartDateTime.timeSpec());
1075 edt.setDateOnly(mEndAnyTimeCheckBox->isChecked());
1076 if (mSavedEndDateTime != edt)
1077 return true;
1079 if (mSavedExceptionDates != mExceptionDates
1080 || mSavedWorkTimeOnly != mWorkTimeOnly->isChecked()
1081 || mSavedExclHolidays != mExcludeHolidays->isChecked()
1082 || mSavedRepetition != mSubRepetition->repetition())
1083 return true;
1084 return false;
1089 /*=============================================================================
1090 = Class Rule
1091 = Base class for rule widgets, including recurrence frequency.
1092 =============================================================================*/
1094 Rule::Rule(const QString& freqText, const QString& freqWhatsThis, bool time, bool readOnly, QWidget* parent)
1095 : NoRule(parent)
1097 mLayout = new QVBoxLayout(this);
1098 mLayout->setMargin(0);
1099 mLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1101 QHBoxLayout* freqLayout = new QHBoxLayout();
1102 freqLayout->setMargin(0);
1103 freqLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1104 mLayout->addLayout(freqLayout);
1105 QWidget* box = new QWidget(this); // this is to control the QWhatsThis text display area
1106 freqLayout->addWidget(box, 0, Qt::AlignLeft);
1107 QHBoxLayout* boxLayout = new QHBoxLayout(box);
1108 boxLayout->setMargin(0);
1110 QLabel* label = new QLabel(i18nc("@label:spinbox", "Recur e&very"), box);
1111 label->setFixedSize(label->sizeHint());
1112 boxLayout->addWidget(label, 0, Qt::AlignLeft);
1113 if (time)
1115 mIntSpinBox = Q_NULLPTR;
1116 mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
1117 mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
1118 mTimeSpinBox->setReadOnly(readOnly);
1119 boxLayout->addWidget(mSpinBox, 0, Qt::AlignLeft);
1121 else
1123 mTimeSpinBox = Q_NULLPTR;
1124 mSpinBox = mIntSpinBox = new SpinBox(1, 999, box);
1125 mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
1126 mIntSpinBox->setReadOnly(readOnly);
1127 boxLayout->addWidget(mSpinBox, 0, Qt::AlignLeft);
1129 connect(mSpinBox, SIGNAL(valueChanged(int)), SIGNAL(frequencyChanged()));
1130 connect(mSpinBox, SIGNAL(valueChanged(int)), SIGNAL(changed()));
1131 label->setBuddy(mSpinBox);
1132 label = new QLabel(freqText, box);
1133 label->setFixedSize(label->sizeHint());
1134 boxLayout->addWidget(label, 0, Qt::AlignLeft);
1135 box->setFixedSize(sizeHint());
1136 box->setWhatsThis(freqWhatsThis);
1139 int Rule::frequency() const
1141 if (mIntSpinBox)
1142 return mIntSpinBox->value();
1143 if (mTimeSpinBox)
1144 return mTimeSpinBox->value();
1145 return 0;
1148 void Rule::setFrequency(int n)
1150 if (mIntSpinBox)
1151 mIntSpinBox->setValue(n);
1152 if (mTimeSpinBox)
1153 mTimeSpinBox->setValue(n);
1156 /******************************************************************************
1157 * Save the state of all controls.
1159 void Rule::saveState()
1161 mSavedFrequency = frequency();
1164 /******************************************************************************
1165 * Check whether any of the controls have changed state since initialisation.
1167 bool Rule::stateChanged() const
1169 return (mSavedFrequency != frequency());
1173 /*=============================================================================
1174 = Class SubDailyRule
1175 = Sub-daily rule widget.
1176 =============================================================================*/
1178 SubDailyRule::SubDailyRule(bool readOnly, QWidget* parent)
1179 : Rule(i18nc("@label Time units for user-entered numbers", "hours:minutes"),
1180 i18nc("@info:whatsthis", "Enter the number of hours and minutes between repetitions of the alarm"),
1181 true, readOnly, parent)
1185 /*=============================================================================
1186 = Class DayWeekRule
1187 = Daily/weekly rule widget base class.
1188 =============================================================================*/
1190 DayWeekRule::DayWeekRule(const QString& freqText, const QString& freqWhatsThis, const QString& daysWhatsThis,
1191 bool readOnly, QWidget* parent)
1192 : Rule(freqText, freqWhatsThis, false, readOnly, parent),
1193 mSavedDays(7)
1195 QGridLayout* grid = new QGridLayout();
1196 grid->setMargin(0);
1197 grid->setRowStretch(0, 1);
1198 layout()->addLayout(grid);
1200 QLabel* label = new QLabel(i18nc("@label On: Tuesday", "O&n:"), this);
1201 label->setFixedSize(label->sizeHint());
1202 grid->addWidget(label, 0, 0, Qt::AlignRight | Qt::AlignTop);
1203 grid->setColumnMinimumWidth(1, style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1205 // List the days of the week starting at the user's start day of the week.
1206 // Save the first day of the week, just in case it changes while the dialog is open.
1207 QWidget* box = new QWidget(this); // this is to control the QWhatsThis text display area
1208 QGridLayout* dgrid = new QGridLayout(box);
1209 dgrid->setMargin(0);
1210 dgrid->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1211 const KCalendarSystem* calendar = KLocale::global()->calendar();
1212 for (int i = 0; i < 7; ++i)
1214 int day = KAlarm::localeDayInWeek_to_weekDay(i);
1215 mDayBox[i] = new CheckBox(calendar->weekDayName(day), box);
1216 mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
1217 mDayBox[i]->setReadOnly(readOnly);
1218 connect(mDayBox[i], &QAbstractButton::toggled, this, &Rule::changed);
1219 dgrid->addWidget(mDayBox[i], i%4, i/4, Qt::AlignLeft);
1221 box->setFixedSize(box->sizeHint());
1222 box->setWhatsThis(daysWhatsThis);
1223 grid->addWidget(box, 0, 2, Qt::AlignLeft);
1224 label->setBuddy(mDayBox[0]);
1225 grid->setColumnStretch(3, 1);
1228 /******************************************************************************
1229 * Fetch which days of the week have been ticked.
1231 QBitArray DayWeekRule::days() const
1233 QBitArray ds(7);
1234 ds.fill(false);
1235 for (int i = 0; i < 7; ++i)
1236 if (mDayBox[i]->isChecked())
1237 ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
1238 return ds;
1241 /******************************************************************************
1242 * Tick/untick every day of the week.
1244 void DayWeekRule::setDays(bool tick)
1246 for (int i = 0; i < 7; ++i)
1247 mDayBox[i]->setChecked(tick);
1250 /******************************************************************************
1251 * Tick/untick each day of the week according to the specified bits.
1253 void DayWeekRule::setDays(const QBitArray& days)
1255 for (int i = 0; i < 7; ++i)
1257 bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
1258 mDayBox[i]->setChecked(x);
1262 /******************************************************************************
1263 * Tick the specified day of the week, and untick all other days.
1265 void DayWeekRule::setDay(int dayOfWeek)
1267 for (int i = 0; i < 7; ++i)
1268 mDayBox[i]->setChecked(false);
1269 if (dayOfWeek > 0 && dayOfWeek <= 7)
1270 mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
1273 /******************************************************************************
1274 * Validate: check that at least one day is selected.
1276 QWidget* DayWeekRule::validate(QString& errorMessage)
1278 for (int i = 0; i < 7; ++i)
1279 if (mDayBox[i]->isChecked())
1280 return Q_NULLPTR;
1281 errorMessage = i18nc("@info", "No day selected");
1282 return mDayBox[0];
1285 /******************************************************************************
1286 * Save the state of all controls.
1288 void DayWeekRule::saveState()
1290 Rule::saveState();
1291 mSavedDays = days();
1294 /******************************************************************************
1295 * Check whether any of the controls have changed state since initialisation.
1297 bool DayWeekRule::stateChanged() const
1299 return (Rule::stateChanged()
1300 || mSavedDays != days());
1304 /*=============================================================================
1305 = Class DailyRule
1306 = Daily rule widget.
1307 =============================================================================*/
1309 DailyRule::DailyRule(bool readOnly, QWidget* parent)
1310 : DayWeekRule(i18nc("@label Time unit for user-entered number", "day(s)"),
1311 i18nc("@info:whatsthis", "Enter the number of days between repetitions of the alarm"),
1312 i18nc("@info:whatsthis", "Select the days of the week on which the alarm is allowed to occur"),
1313 readOnly, parent)
1317 /*=============================================================================
1318 = Class WeeklyRule
1319 = Weekly rule widget.
1320 =============================================================================*/
1322 WeeklyRule::WeeklyRule(bool readOnly, QWidget* parent)
1323 : DayWeekRule(i18nc("@label Time unit for user-entered number", "week(s)"),
1324 i18nc("@info:whatsthis", "Enter the number of weeks between repetitions of the alarm"),
1325 i18nc("@info:whatsthis", "Select the days of the week on which to repeat the alarm"),
1326 readOnly, parent)
1330 /*=============================================================================
1331 = Class MonthYearRule
1332 = Monthly/yearly rule widget base class.
1333 =============================================================================*/
1335 MonthYearRule::MonthYearRule(const QString& freqText, const QString& freqWhatsThis, bool allowEveryWeek,
1336 bool readOnly, QWidget* parent)
1337 : Rule(freqText, freqWhatsThis, false, readOnly, parent),
1338 mEveryWeek(allowEveryWeek)
1340 mButtonGroup = new ButtonGroup(this);
1342 // Month day selector
1343 QGridLayout* boxLayout = new QGridLayout();
1344 boxLayout->setMargin(0);
1345 boxLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1346 layout()->addLayout(boxLayout);
1348 mDayButton = new RadioButton(i18nc("@option:radio On day number in the month", "O&n day"), this);
1349 mDayButton->setFixedSize(mDayButton->sizeHint());
1350 mDayButton->setReadOnly(readOnly);
1351 mButtonGroup->addButton(mDayButton);
1352 mDayButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on the selected day of the month"));
1353 boxLayout->addWidget(mDayButton, 0, 0);
1355 mDayCombo = new ComboBox(this);
1356 mDayCombo->setEditable(false);
1357 mDayCombo->setMaxVisibleItems(11);
1358 for (int i = 0; i < 31; ++i)
1359 mDayCombo->addItem(QString::number(i + 1));
1360 mDayCombo->addItem(i18nc("@item:inlistbox Last day of month", "Last"));
1361 mDayCombo->setFixedSize(mDayCombo->sizeHint());
1362 mDayCombo->setReadOnly(readOnly);
1363 mDayCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the month on which to repeat the alarm"));
1364 mDayButton->setFocusWidget(mDayCombo);
1365 connect(mDayCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::activated), this, &MonthYearRule::slotDaySelected);
1366 connect(mDayCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed);
1367 boxLayout->addWidget(mDayCombo, 0, 1, 1, 2, Qt::AlignLeft);
1369 // Month position selector
1370 mPosButton = new RadioButton(i18nc("@option:radio On the 1st Tuesday", "On t&he"), this);
1371 mPosButton->setFixedSize(mPosButton->sizeHint());
1372 mPosButton->setReadOnly(readOnly);
1373 mButtonGroup->addButton(mPosButton);
1374 mPosButton->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on one day of the week, in the selected week of the month"));
1375 boxLayout->addWidget(mPosButton, 1, 0);
1377 mWeekCombo = new ComboBox(this);
1378 mWeekCombo->setEditable(false);
1379 mWeekCombo->addItem(i18nc("@item:inlistbox", "1st"));
1380 mWeekCombo->addItem(i18nc("@item:inlistbox", "2nd"));
1381 mWeekCombo->addItem(i18nc("@item:inlistbox", "3rd"));
1382 mWeekCombo->addItem(i18nc("@item:inlistbox", "4th"));
1383 mWeekCombo->addItem(i18nc("@item:inlistbox", "5th"));
1384 mWeekCombo->addItem(i18nc("@item:inlistbox Last Monday in March", "Last"));
1385 mWeekCombo->addItem(i18nc("@item:inlistbox", "2nd Last"));
1386 mWeekCombo->addItem(i18nc("@item:inlistbox", "3rd Last"));
1387 mWeekCombo->addItem(i18nc("@item:inlistbox", "4th Last"));
1388 mWeekCombo->addItem(i18nc("@item:inlistbox", "5th Last"));
1389 if (mEveryWeek)
1391 mWeekCombo->addItem(i18nc("@item:inlistbox Every (Monday...) in month", "Every"));
1392 mWeekCombo->setMaxVisibleItems(11);
1394 mWeekCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the week of the month in which to repeat the alarm"));
1395 mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
1396 mWeekCombo->setReadOnly(readOnly);
1397 mPosButton->setFocusWidget(mWeekCombo);
1398 connect(mWeekCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed);
1399 boxLayout->addWidget(mWeekCombo, 1, 1);
1401 mDayOfWeekCombo = new ComboBox(this);
1402 mDayOfWeekCombo->setEditable(false);
1403 const KCalendarSystem* calendar = KLocale::global()->calendar();
1404 for (int i = 0; i < 7; ++i)
1406 int day = KAlarm::localeDayInWeek_to_weekDay(i);
1407 mDayOfWeekCombo->addItem(calendar->weekDayName(day));
1409 mDayOfWeekCombo->setReadOnly(readOnly);
1410 mDayOfWeekCombo->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the week on which to repeat the alarm"));
1411 connect(mDayOfWeekCombo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &MonthYearRule::changed);
1412 boxLayout->addWidget(mDayOfWeekCombo, 1, 2, Qt::AlignLeft);
1414 connect(mButtonGroup, &ButtonGroup::buttonSet, this, &MonthYearRule::clicked);
1415 connect(mButtonGroup, &ButtonGroup::buttonSet, this, &MonthYearRule::changed);
1418 MonthYearRule::DayPosType MonthYearRule::type() const
1420 return (mButtonGroup->checkedButton() == mDayButton) ? DATE : POS;
1423 void MonthYearRule::setType(MonthYearRule::DayPosType type)
1425 if (type == DATE)
1426 mDayButton->setChecked(true);
1427 else
1428 mPosButton->setChecked(true);
1431 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
1433 --dayOfMonth;
1434 mDayCombo->setCurrentIndex(dayOfMonth);
1435 mWeekCombo->setCurrentIndex(dayOfMonth / 7);
1436 mDayOfWeekCombo->setCurrentIndex(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1439 int MonthYearRule::date() const
1441 int daynum = mDayCombo->currentIndex() + 1;
1442 return (daynum <= 31) ? daynum : 31 - daynum;
1445 int MonthYearRule::week() const
1447 int weeknum = mWeekCombo->currentIndex() + 1;
1448 return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
1451 int MonthYearRule::dayOfWeek() const
1453 return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentIndex());
1456 void MonthYearRule::setDate(int dayOfMonth)
1458 mDayButton->setChecked(true);;
1459 mDayCombo->setCurrentIndex(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0); // day 0 shouldn't ever occur
1462 void MonthYearRule::setPosition(int week, int dayOfWeek)
1464 mPosButton->setChecked(true);
1465 mWeekCombo->setCurrentIndex((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
1466 mDayOfWeekCombo->setCurrentIndex(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1469 void MonthYearRule::enableSelection(DayPosType type)
1471 bool date = (type == DATE);
1472 mDayCombo->setEnabled(date);
1473 mWeekCombo->setEnabled(!date);
1474 mDayOfWeekCombo->setEnabled(!date);
1477 void MonthYearRule::clicked(QAbstractButton* button)
1479 enableSelection(button == mDayButton ? DATE : POS);
1482 void MonthYearRule::slotDaySelected(int index)
1484 daySelected(index <= 30 ? index + 1 : 30 - index);
1487 /******************************************************************************
1488 * Save the state of all controls.
1490 void MonthYearRule::saveState()
1492 Rule::saveState();
1493 mSavedType = type();
1494 if (mSavedType == DATE)
1495 mSavedDay = date();
1496 else
1498 mSavedWeek = week();
1499 mSavedWeekDay = dayOfWeek();
1503 /******************************************************************************
1504 * Check whether any of the controls have changed state since initialisation.
1506 bool MonthYearRule::stateChanged() const
1508 if (Rule::stateChanged()
1509 || mSavedType != type())
1510 return true;
1511 if (mSavedType == DATE)
1513 if (mSavedDay != date())
1514 return true;
1516 else
1518 if (mSavedWeek != week()
1519 || mSavedWeekDay != dayOfWeek())
1520 return true;
1522 return false;
1526 /*=============================================================================
1527 = Class MonthlyRule
1528 = Monthly rule widget.
1529 =============================================================================*/
1531 MonthlyRule::MonthlyRule(bool readOnly, QWidget* parent)
1532 : MonthYearRule(i18nc("@label Time unit for user-entered number", "month(s)"),
1533 i18nc("@info:whatsthis", "Enter the number of months between repetitions of the alarm"),
1534 false, readOnly, parent)
1538 /*=============================================================================
1539 = Class YearlyRule
1540 = Yearly rule widget.
1541 =============================================================================*/
1543 YearlyRule::YearlyRule(bool readOnly, QWidget* parent)
1544 : MonthYearRule(i18nc("@label Time unit for user-entered number", "year(s)"),
1545 i18nc("@info:whatsthis", "Enter the number of years between repetitions of the alarm"),
1546 true, readOnly, parent)
1548 // Set up the month selection widgets
1549 QHBoxLayout* hlayout = new QHBoxLayout();
1550 hlayout->setMargin(0);
1551 layout()->addLayout(hlayout);
1552 QLabel* label = new QLabel(i18nc("@label List of months to select", "Months:"), this);
1553 label->setFixedSize(label->sizeHint());
1554 hlayout->addWidget(label, 0, Qt::AlignLeft | Qt::AlignTop);
1556 // List the months of the year.
1557 QWidget* w = new QWidget(this); // this is to control the QWhatsThis text display area
1558 hlayout->addWidget(w, 1, Qt::AlignLeft);
1559 QGridLayout* grid = new QGridLayout(w);
1560 grid->setMargin(0);
1561 grid->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1562 const KCalendarSystem* calendar = KLocale::global()->calendar();
1563 int year = KDateTime::currentLocalDate().year();
1564 for (int i = 0; i < 12; ++i)
1566 mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, KCalendarSystem::ShortName), w);
1567 mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
1568 mMonthBox[i]->setReadOnly(readOnly);
1569 connect(mMonthBox[i], &QAbstractButton::toggled, this, &Rule::changed);
1570 grid->addWidget(mMonthBox[i], i%3, i/3, Qt::AlignLeft);
1572 connect(mMonthBox[1], &QAbstractButton::toggled, this, &YearlyRule::enableFeb29);
1573 w->setFixedHeight(w->sizeHint().height());
1574 w->setWhatsThis(i18nc("@info:whatsthis", "Select the months of the year in which to repeat the alarm"));
1576 // February 29th handling option
1577 QHBoxLayout* f29box = new QHBoxLayout(this);
1578 layout()->addLayout(f29box);
1579 w = new QWidget(this); // this is to control the QWhatsThis text display area
1580 f29box->addWidget(w, 0, Qt::AlignLeft);
1581 QHBoxLayout* boxLayout = new QHBoxLayout(w);
1582 boxLayout->setMargin(0);
1583 boxLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
1584 mFeb29Label = new QLabel(i18nc("@label:listbox", "February 2&9th alarm in non-leap years:"));
1585 mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
1586 boxLayout->addWidget(mFeb29Label);
1587 mFeb29Combo = new ComboBox();
1588 mFeb29Combo->setEditable(false);
1589 mFeb29Combo->addItem(i18nc("@item:inlistbox No date", "None"));
1590 mFeb29Combo->addItem(i18nc("@item:inlistbox 1st March (short form)", "1 Mar"));
1591 mFeb29Combo->addItem(i18nc("@item:inlistbox 28th February (short form)", "28 Feb"));
1592 mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
1593 mFeb29Combo->setReadOnly(readOnly);
1594 connect(mFeb29Combo, static_cast<void (ComboBox::*)(int)>(&ComboBox::currentIndexChanged), this, &YearlyRule::changed);
1595 mFeb29Label->setBuddy(mFeb29Combo);
1596 boxLayout->addWidget(mFeb29Combo);
1597 w->setFixedSize(w->sizeHint());
1598 w->setWhatsThis(i18nc("@info:whatsthis", "Select which date, if any, the February 29th alarm should trigger in non-leap years"));
1601 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
1603 MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
1604 --month;
1605 for (int i = 0; i < 12; ++i)
1606 mMonthBox[i]->setChecked(i == month);
1607 setFeb29Type(KARecurrence::defaultFeb29Type());
1608 daySelected(dayOfMonth); // enable/disable month checkboxes as appropriate
1611 /******************************************************************************
1612 * Fetch which months have been checked (1 - 12).
1613 * Reply = true if February has been checked.
1615 QVector<int> YearlyRule::months() const
1617 QVector<int> mnths;
1618 for (int i = 0; i < 12; ++i)
1619 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1620 mnths.append(i + 1);
1621 return mnths;
1624 /******************************************************************************
1625 * Check/uncheck each month of the year according to the specified list.
1627 void YearlyRule::setMonths(const QList<int>& mnths)
1629 bool checked[12];
1630 for (int i = 0; i < 12; ++i)
1631 checked[i] = false;
1632 for (int i = 0, end = mnths.count(); i < end; ++i)
1633 checked[mnths[i] - 1] = true;
1634 for (int i = 0; i < 12; ++i)
1635 mMonthBox[i]->setChecked(checked[i]);
1636 enableFeb29();
1639 /******************************************************************************
1640 * Return the date for February 29th alarms in non-leap years.
1642 KARecurrence::Feb29Type YearlyRule::feb29Type() const
1644 if (mFeb29Combo->isEnabled())
1646 switch (mFeb29Combo->currentIndex())
1648 case 1: return KARecurrence::Feb29_Mar1;
1649 case 2: return KARecurrence::Feb29_Feb28;
1650 default: break;
1653 return KARecurrence::Feb29_None;
1656 /******************************************************************************
1657 * Set the date for February 29th alarms to trigger in non-leap years.
1659 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
1661 int index;
1662 switch (type)
1664 default:
1665 case KARecurrence::Feb29_None: index = 0; break;
1666 case KARecurrence::Feb29_Mar1: index = 1; break;
1667 case KARecurrence::Feb29_Feb28: index = 2; break;
1669 mFeb29Combo->setCurrentIndex(index);
1672 /******************************************************************************
1673 * Validate: check that at least one month is selected.
1675 QWidget* YearlyRule::validate(QString& errorMessage)
1677 for (int i = 0; i < 12; ++i)
1678 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1679 return Q_NULLPTR;
1680 errorMessage = i18nc("@info", "No month selected");
1681 return mMonthBox[0];
1684 /******************************************************************************
1685 * Called when a yearly recurrence type radio button is clicked,
1686 * to enable/disable month checkboxes as appropriate for the date selected.
1688 void YearlyRule::clicked(QAbstractButton* button)
1690 MonthYearRule::clicked(button);
1691 daySelected(buttonType(button) == DATE ? date() : 1);
1694 /******************************************************************************
1695 * Called when a day of the month is selected in a yearly recurrence, to
1696 * disable months for which the day is out of range.
1698 void YearlyRule::daySelected(int day)
1700 mMonthBox[1]->setEnabled(day <= 29); // February
1701 bool enable = (day != 31);
1702 mMonthBox[3]->setEnabled(enable); // April
1703 mMonthBox[5]->setEnabled(enable); // June
1704 mMonthBox[8]->setEnabled(enable); // September
1705 mMonthBox[10]->setEnabled(enable); // November
1706 enableFeb29();
1709 /******************************************************************************
1710 * Enable/disable the February 29th combo box depending on whether February
1711 * 29th is selected.
1713 void YearlyRule::enableFeb29()
1715 bool enable = (type() == DATE && date() == 29 && mMonthBox[1]->isChecked() && mMonthBox[1]->isEnabled());
1716 mFeb29Label->setEnabled(enable);
1717 mFeb29Combo->setEnabled(enable);
1720 /******************************************************************************
1721 * Save the state of all controls.
1723 void YearlyRule::saveState()
1725 MonthYearRule::saveState();
1726 mSavedMonths = months();
1727 mSavedFeb29Type = feb29Type();
1730 /******************************************************************************
1731 * Check whether any of the controls have changed state since initialisation.
1733 bool YearlyRule::stateChanged() const
1735 return (MonthYearRule::stateChanged()
1736 || mSavedMonths != months()
1737 || mSavedFeb29Type != feb29Type());
1742 // vim: et sw=4: