2 * recurrenceedit.cpp - widget to edit the event's recurrence definition
4 * Copyright © 2002-2010 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.
25 #include "recurrenceedit.moc"
26 #include "recurrenceeditprivate.moc"
28 #include "alarmtimewidget.h"
33 #include "kalarmapp.h"
35 #include "karecurrence.h"
36 #include "preferences.h"
37 #include "radiobutton.h"
38 #include "repetitionbutton.h"
41 #include "timespinbox.h"
42 #include "buttongroup.h"
44 #include <kcal/event.h>
48 #include <kcalendarsystem.h>
49 #include <kiconloader.h>
51 #include <kmessagebox.h>
54 #include <QPushButton>
56 #include <QStackedWidget>
57 #include <QListWidget>
59 #include <QGridLayout>
60 #include <QHBoxLayout>
61 #include <QVBoxLayout>
62 #include <QtAlgorithms>
67 class ListWidget
: public QListWidget
70 explicit ListWidget(QWidget
* parent
) : QListWidget(parent
) {}
71 virtual QSize
sizeHint() const { return minimumSizeHint(); }
74 // Collect these widget labels together to ensure consistent wording and
75 // translations across different modules.
76 QString
RecurrenceEdit::i18n_combo_NoRecur() { return i18nc("@item:inlistbox Recurrence type", "No Recurrence"); }
77 QString
RecurrenceEdit::i18n_combo_AtLogin() { return i18nc("@item:inlistbox Recurrence type", "At Login"); }
78 QString
RecurrenceEdit::i18n_combo_HourlyMinutely() { return i18nc("@item:inlistbox Recurrence type", "Hourly/Minutely"); }
79 QString
RecurrenceEdit::i18n_combo_Daily() { return i18nc("@item:inlistbox Recurrence type", "Daily"); }
80 QString
RecurrenceEdit::i18n_combo_Weekly() { return i18nc("@item:inlistbox Recurrence type", "Weekly"); }
81 QString
RecurrenceEdit::i18n_combo_Monthly() { return i18nc("@item:inlistbox Recurrence type", "Monthly"); }
82 QString
RecurrenceEdit::i18n_combo_Yearly() { return i18nc("@item:inlistbox Recurrence type", "Yearly"); }
85 RecurrenceEdit::RecurrenceEdit(bool readOnly
, QWidget
* parent
)
88 mRuleButtonType(INVALID_RECUR
),
93 mNoEmitTypeChanged(true),
97 QVBoxLayout
* topLayout
= new QVBoxLayout(this);
98 topLayout
->setMargin(0);
99 topLayout
->setSpacing(KDialog::spacingHint());
101 /* Create the recurrence rule Group box which holds the recurrence period
102 * selection buttons, and the weekly, monthly and yearly recurrence rule
103 * frames which specify options individual to each of these distinct
104 * sections of the recurrence rule. Each frame is made visible by the
105 * selection of its corresponding radio button.
108 QGroupBox
* recurGroup
= new QGroupBox(i18nc("@title:group", "Recurrence Rule"), this);
109 topLayout
->addWidget(recurGroup
);
110 QHBoxLayout
* hlayout
= new QHBoxLayout(recurGroup
);
111 hlayout
->setMargin(KDialog::marginHint());
112 hlayout
->setSpacing(KDialog::marginHint()); // use margin spacing due to vertical divider line
114 // Recurrence period radio buttons
115 QVBoxLayout
* vlayout
= new QVBoxLayout();
116 vlayout
->setSpacing(0);
117 vlayout
->setMargin(0);
118 hlayout
->addLayout(vlayout
);
119 mRuleButtonGroup
= new ButtonGroup(recurGroup
);
120 connect(mRuleButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SLOT(periodClicked(QAbstractButton
*)));
121 connect(mRuleButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SIGNAL(contentsChanged()));
123 mNoneButton
= new RadioButton(i18n_combo_NoRecur(), recurGroup
);
124 mNoneButton
->setFixedSize(mNoneButton
->sizeHint());
125 mNoneButton
->setReadOnly(mReadOnly
);
126 mNoneButton
->setWhatsThis(i18nc("@info:whatsthis", "Do not repeat the alarm"));
127 mRuleButtonGroup
->addButton(mNoneButton
);
128 vlayout
->addWidget(mNoneButton
);
130 mAtLoginButton
= new RadioButton(i18n_combo_AtLogin(), recurGroup
);
131 mAtLoginButton
->setFixedSize(mAtLoginButton
->sizeHint());
132 mAtLoginButton
->setReadOnly(mReadOnly
);
133 mAtLoginButton
->setWhatsThis(i18nc("@info:whatsthis",
134 "<para>Trigger the alarm at the specified date/time and at every login until then.</para>"
135 "<para>Note that it will also be triggered any time <application>KAlarm</application> is restarted.</para>"));
136 mRuleButtonGroup
->addButton(mAtLoginButton
);
137 vlayout
->addWidget(mAtLoginButton
);
139 mSubDailyButton
= new RadioButton(i18n_combo_HourlyMinutely(), recurGroup
);
140 mSubDailyButton
->setFixedSize(mSubDailyButton
->sizeHint());
141 mSubDailyButton
->setReadOnly(mReadOnly
);
142 mSubDailyButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at hourly/minutely intervals"));
143 mRuleButtonGroup
->addButton(mSubDailyButton
);
144 vlayout
->addWidget(mSubDailyButton
);
146 mDailyButton
= new RadioButton(i18n_combo_Daily(), recurGroup
);
147 mDailyButton
->setFixedSize(mDailyButton
->sizeHint());
148 mDailyButton
->setReadOnly(mReadOnly
);
149 mDailyButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at daily intervals"));
150 mRuleButtonGroup
->addButton(mDailyButton
);
151 vlayout
->addWidget(mDailyButton
);
153 mWeeklyButton
= new RadioButton(i18n_combo_Weekly(), recurGroup
);
154 mWeeklyButton
->setFixedSize(mWeeklyButton
->sizeHint());
155 mWeeklyButton
->setReadOnly(mReadOnly
);
156 mWeeklyButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at weekly intervals"));
157 mRuleButtonGroup
->addButton(mWeeklyButton
);
158 vlayout
->addWidget(mWeeklyButton
);
160 mMonthlyButton
= new RadioButton(i18n_combo_Monthly(), recurGroup
);
161 mMonthlyButton
->setFixedSize(mMonthlyButton
->sizeHint());
162 mMonthlyButton
->setReadOnly(mReadOnly
);
163 mMonthlyButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at monthly intervals"));
164 mRuleButtonGroup
->addButton(mMonthlyButton
);
165 vlayout
->addWidget(mMonthlyButton
);
167 mYearlyButton
= new RadioButton(i18n_combo_Yearly(), recurGroup
);
168 mYearlyButton
->setFixedSize(mYearlyButton
->sizeHint());
169 mYearlyButton
->setReadOnly(mReadOnly
);
170 mYearlyButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm at annual intervals"));
171 mRuleButtonGroup
->addButton(mYearlyButton
);
172 vlayout
->addWidget(mYearlyButton
);
173 vlayout
->addStretch(); // top-adjust the interval radio buttons
175 // Sub-repetition button
176 mSubRepetition
= new RepetitionButton(i18nc("@action:button", "Sub-Repetition"), true, recurGroup
);
177 mSubRepetition
->setFixedSize(mSubRepetition
->sizeHint());
178 mSubRepetition
->setReadOnly(mReadOnly
);
179 mSubRepetition
->setWhatsThis(i18nc("@info:whatsthis",
180 "Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due."));
181 connect(mSubRepetition
, SIGNAL(needsInitialisation()), SIGNAL(repeatNeedsInitialisation()));
182 connect(mSubRepetition
, SIGNAL(changed()), SIGNAL(frequencyChanged()));
183 connect(mSubRepetition
, SIGNAL(changed()), SIGNAL(contentsChanged()));
184 vlayout
->addSpacing(KDialog::spacingHint());
185 vlayout
->addWidget(mSubRepetition
);
187 // Vertical divider line
188 vlayout
= new QVBoxLayout();
189 vlayout
->setMargin(0);
190 hlayout
->addLayout(vlayout
);
191 QFrame
* divider
= new QFrame(recurGroup
);
192 divider
->setFrameStyle(QFrame::VLine
| QFrame::Sunken
);
193 vlayout
->addWidget(divider
, 1);
195 // Rule definition stack
196 mRuleStack
= new QStackedWidget(recurGroup
);
197 hlayout
->addWidget(mRuleStack
);
198 hlayout
->addStretch(1);
199 mNoRule
= new NoRule(mRuleStack
);
200 mSubDailyRule
= new SubDailyRule(mReadOnly
, mRuleStack
);
201 mDailyRule
= new DailyRule(mReadOnly
, mRuleStack
);
202 mWeeklyRule
= new WeeklyRule(mReadOnly
, mRuleStack
);
203 mMonthlyRule
= new MonthlyRule(mReadOnly
, mRuleStack
);
204 mYearlyRule
= new YearlyRule(mReadOnly
, mRuleStack
);
206 connect(mSubDailyRule
, SIGNAL(frequencyChanged()), SIGNAL(frequencyChanged()));
207 connect(mDailyRule
, SIGNAL(frequencyChanged()), SIGNAL(frequencyChanged()));
208 connect(mWeeklyRule
, SIGNAL(frequencyChanged()), SIGNAL(frequencyChanged()));
209 connect(mMonthlyRule
, SIGNAL(frequencyChanged()), SIGNAL(frequencyChanged()));
210 connect(mYearlyRule
, SIGNAL(frequencyChanged()), SIGNAL(frequencyChanged()));
211 connect(mSubDailyRule
, SIGNAL(changed()), SIGNAL(contentsChanged()));
212 connect(mDailyRule
, SIGNAL(changed()), SIGNAL(contentsChanged()));
213 connect(mWeeklyRule
, SIGNAL(changed()), SIGNAL(contentsChanged()));
214 connect(mMonthlyRule
, SIGNAL(changed()), SIGNAL(contentsChanged()));
215 connect(mYearlyRule
, SIGNAL(changed()), SIGNAL(contentsChanged()));
217 mRuleStack
->addWidget(mNoRule
);
218 mRuleStack
->addWidget(mSubDailyRule
);
219 mRuleStack
->addWidget(mDailyRule
);
220 mRuleStack
->addWidget(mWeeklyRule
);
221 mRuleStack
->addWidget(mMonthlyRule
);
222 mRuleStack
->addWidget(mYearlyRule
);
223 hlayout
->addSpacing(KDialog::marginHint());
225 // Create the recurrence range group which contains the controls
226 // which specify how long the recurrence is to last.
228 mRangeButtonBox
= new QGroupBox(i18nc("@title:group", "Recurrence End"), this);
229 topLayout
->addWidget(mRangeButtonBox
);
230 mRangeButtonGroup
= new ButtonGroup(mRangeButtonBox
);
231 connect(mRangeButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SLOT(rangeTypeClicked()));
232 connect(mRangeButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SIGNAL(contentsChanged()));
234 vlayout
= new QVBoxLayout(mRangeButtonBox
);
235 vlayout
->setMargin(KDialog::marginHint());
236 vlayout
->setSpacing(KDialog::spacingHint());
237 mNoEndDateButton
= new RadioButton(i18nc("@option:radio", "No end"), mRangeButtonBox
);
238 mNoEndDateButton
->setFixedSize(mNoEndDateButton
->sizeHint());
239 mNoEndDateButton
->setReadOnly(mReadOnly
);
240 mNoEndDateButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm indefinitely"));
241 mRangeButtonGroup
->addButton(mNoEndDateButton
);
242 vlayout
->addWidget(mNoEndDateButton
, 1, Qt::AlignLeft
);
243 QSize size
= mNoEndDateButton
->size();
245 hlayout
= new QHBoxLayout();
246 hlayout
->setMargin(0);
247 vlayout
->addLayout(hlayout
);
248 mRepeatCountButton
= new RadioButton(i18nc("@option:radio", "End after:"), mRangeButtonBox
);
249 mRepeatCountButton
->setReadOnly(mReadOnly
);
250 mRepeatCountButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm for the number of times specified"));
251 mRangeButtonGroup
->addButton(mRepeatCountButton
);
252 mRepeatCountEntry
= new SpinBox(1, 9999, mRangeButtonBox
);
253 mRepeatCountEntry
->setFixedSize(mRepeatCountEntry
->sizeHint());
254 mRepeatCountEntry
->setSingleShiftStep(10);
255 mRepeatCountEntry
->setSelectOnStep(false);
256 mRepeatCountEntry
->setReadOnly(mReadOnly
);
257 mRepeatCountEntry
->setWhatsThis(i18nc("@info:whatsthis", "Enter the total number of times to trigger the alarm"));
258 connect(mRepeatCountEntry
, SIGNAL(valueChanged(int)), SLOT(repeatCountChanged(int)));
259 connect(mRepeatCountEntry
, SIGNAL(valueChanged(int)), SIGNAL(contentsChanged()));
260 mRepeatCountButton
->setFocusWidget(mRepeatCountEntry
);
261 mRepeatCountLabel
= new QLabel(i18nc("@label", "occurrence(s)"), mRangeButtonBox
);
262 mRepeatCountLabel
->setFixedSize(mRepeatCountLabel
->sizeHint());
263 hlayout
->addWidget(mRepeatCountButton
);
264 hlayout
->addSpacing(KDialog::spacingHint());
265 hlayout
->addWidget(mRepeatCountEntry
);
266 hlayout
->addWidget(mRepeatCountLabel
);
267 hlayout
->addStretch();
268 size
= size
.expandedTo(mRepeatCountButton
->sizeHint());
270 hlayout
= new QHBoxLayout();
271 hlayout
->setMargin(0);
272 vlayout
->addLayout(hlayout
);
273 mEndDateButton
= new RadioButton(i18nc("@option:radio", "End by:"), mRangeButtonBox
);
274 mEndDateButton
->setReadOnly(mReadOnly
);
275 mEndDateButton
->setWhatsThis(
276 i18nc("@info:whatsthis", "<para>Repeat the alarm until the date/time specified.</para>"
277 "<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>"));
278 mRangeButtonGroup
->addButton(mEndDateButton
);
279 mEndDateEdit
= new DateEdit(mRangeButtonBox
);
280 mEndDateEdit
->setReadOnly(mReadOnly
);
281 static const QString tzText
= i18nc("@info/plain", "This uses the same time zone as the start time.");
282 mEndDateEdit
->setWhatsThis(i18nc("@info:whatsthis",
283 "<para>Enter the last date to repeat the alarm.</para><para>%1</para>", tzText
));
284 connect(mEndDateEdit
, SIGNAL(dateChanged(const QDate
&)), SIGNAL(contentsChanged()));
285 mEndDateButton
->setFocusWidget(mEndDateEdit
);
286 mEndTimeEdit
= new TimeEdit(mRangeButtonBox
);
287 mEndTimeEdit
->setFixedSize(mEndTimeEdit
->sizeHint());
288 mEndTimeEdit
->setReadOnly(mReadOnly
);
289 mEndTimeEdit
->setWhatsThis(i18nc("@info:whatsthis",
290 "<para>Enter the last time to repeat the alarm.</para><para>%1</para><para>%2</para>", tzText
, TimeSpinBox::shiftWhatsThis()));
291 connect(mEndTimeEdit
, SIGNAL(valueChanged(int)), SIGNAL(contentsChanged()));
292 mEndAnyTimeCheckBox
= new CheckBox(i18nc("@option:check", "Any time"), mRangeButtonBox
);
293 mEndAnyTimeCheckBox
->setFixedSize(mEndAnyTimeCheckBox
->sizeHint());
294 mEndAnyTimeCheckBox
->setReadOnly(mReadOnly
);
295 mEndAnyTimeCheckBox
->setWhatsThis(i18nc("@info:whatsthis", "Stop repeating the alarm after your first login on or after the specified end date"));
296 connect(mEndAnyTimeCheckBox
, SIGNAL(toggled(bool)), SLOT(slotAnyTimeToggled(bool)));
297 connect(mEndAnyTimeCheckBox
, SIGNAL(toggled(bool)), SIGNAL(contentsChanged()));
298 hlayout
->addWidget(mEndDateButton
);
299 hlayout
->addSpacing(KDialog::spacingHint());
300 hlayout
->addWidget(mEndDateEdit
);
301 hlayout
->addWidget(mEndTimeEdit
);
302 hlayout
->addWidget(mEndAnyTimeCheckBox
);
303 hlayout
->addStretch();
304 size
= size
.expandedTo(mEndDateButton
->sizeHint());
306 // Line up the widgets to the right of the radio buttons
307 mRepeatCountButton
->setFixedSize(size
);
308 mEndDateButton
->setFixedSize(size
);
310 // Create the exceptions group which specifies dates to be excluded
311 // from the recurrence.
313 mExceptionGroup
= new QGroupBox(i18nc("@title:group", "Exceptions"), this);
314 topLayout
->addWidget(mExceptionGroup
);
315 topLayout
->setStretchFactor(mExceptionGroup
, 2);
316 hlayout
= new QHBoxLayout(mExceptionGroup
);
317 hlayout
->setMargin(KDialog::marginHint());
318 hlayout
->setSpacing(KDialog::spacingHint());
319 vlayout
= new QVBoxLayout();
320 vlayout
->setMargin(0);
321 hlayout
->addLayout(vlayout
);
323 mExceptionDateList
= new ListWidget(mExceptionGroup
);
324 mExceptionDateList
->setWhatsThis(i18nc("@info:whatsthis", "The list of exceptions, i.e. dates/times excluded from the recurrence"));
325 connect(mExceptionDateList
, SIGNAL(currentRowChanged(int)), SLOT(enableExceptionButtons()));
326 vlayout
->addWidget(mExceptionDateList
);
330 mExceptionDateEdit
= 0;
331 mChangeExceptionButton
= 0;
332 mDeleteExceptionButton
= 0;
336 vlayout
= new QVBoxLayout();
337 vlayout
->setMargin(0);
338 hlayout
->addLayout(vlayout
);
339 mExceptionDateEdit
= new DateEdit(mExceptionGroup
);
340 mExceptionDateEdit
->setDate(KDateTime::currentLocalDate());
341 mExceptionDateEdit
->setWhatsThis(i18nc("@info:whatsthis",
342 "Enter a date to insert in the exceptions list. "
343 "Use in conjunction with the Add or Change button below."));
344 vlayout
->addWidget(mExceptionDateEdit
, 0, Qt::AlignLeft
);
346 hlayout
= new QHBoxLayout();
347 hlayout
->setMargin(0);
348 vlayout
->addLayout(hlayout
);
349 QPushButton
* button
= new QPushButton(i18nc("@action:button", "Add"), mExceptionGroup
);
350 button
->setWhatsThis(i18nc("@info:whatsthis", "Add the date entered above to the exceptions list"));
351 connect(button
, SIGNAL(clicked()), SLOT(addException()));
352 hlayout
->addWidget(button
);
354 mChangeExceptionButton
= new QPushButton(i18nc("@action:button", "Change"), mExceptionGroup
);
355 mChangeExceptionButton
->setWhatsThis(i18nc("@info:whatsthis",
356 "Replace the currently highlighted item in the exceptions list with the date entered above"));
357 connect(mChangeExceptionButton
, SIGNAL(clicked()), SLOT(changeException()));
358 hlayout
->addWidget(mChangeExceptionButton
);
360 mDeleteExceptionButton
= new QPushButton(i18nc("@action:button", "Delete"), mExceptionGroup
);
361 mDeleteExceptionButton
->setWhatsThis(i18nc("@info:whatsthis", "Remove the currently highlighted item from the exceptions list"));
362 connect(mDeleteExceptionButton
, SIGNAL(clicked()), SLOT(deleteException()));
363 hlayout
->addWidget(mDeleteExceptionButton
);
366 vlayout
->addStretch();
368 mExcludeHolidays
= new CheckBox(i18nc("@option:check", "Exclude holidays"), mExceptionGroup
);
369 mExcludeHolidays
->setReadOnly(mReadOnly
);
370 mExcludeHolidays
->setWhatsThis(i18nc("@info:whatsthis",
371 "<para>Do not trigger the alarm on holidays.</para>"
372 "<para>You can specify your holiday region in the Configuration dialog.</para>"));
373 connect(mExcludeHolidays
, SIGNAL(toggled(bool)), SIGNAL(contentsChanged()));
374 vlayout
->addWidget(mExcludeHolidays
);
376 mWorkTimeOnly
= new CheckBox(i18nc("@option:check", "Only during working time"), mExceptionGroup
);
377 mWorkTimeOnly
->setReadOnly(mReadOnly
);
378 mWorkTimeOnly
->setWhatsThis(i18nc("@info:whatsthis",
379 "<para>Only execute the alarm during working hours, on working days.</para>"
380 "<para>You can specify working days and hours in the Configuration dialog.</para>"));
381 connect(mWorkTimeOnly
, SIGNAL(toggled(bool)), SIGNAL(contentsChanged()));
382 vlayout
->addWidget(mWorkTimeOnly
);
384 topLayout
->addStretch();
385 mNoEmitTypeChanged
= false;
388 /******************************************************************************
389 * Show or hide the exception controls.
391 void RecurrenceEdit::showMoreOptions(bool more
)
394 mExceptionGroup
->show();
396 mExceptionGroup
->hide();
400 /******************************************************************************
401 * Verify the consistency of the entered data.
402 * Reply = widget to receive focus on error, or 0 if no error.
404 QWidget
* RecurrenceEdit::checkData(const KDateTime
& startDateTime
, QString
& errorMessage
) const
406 if (mAtLoginButton
->isChecked())
408 const_cast<RecurrenceEdit
*>(this)->mCurrStartDateTime
= startDateTime
;
409 if (mEndDateButton
->isChecked())
411 // N.B. End date/time takes the same time spec as start date/time
412 QWidget
* errWidget
= 0;
413 bool noTime
= !mEndTimeEdit
->isEnabled();
414 QDate endDate
= mEndDateEdit
->date();
415 if (endDate
< startDateTime
.date())
416 errWidget
= mEndDateEdit
;
417 else if (!noTime
&& QDateTime(endDate
, mEndTimeEdit
->time()) < startDateTime
.dateTime())
418 errWidget
= mEndTimeEdit
;
421 errorMessage
= noTime
422 ? i18nc("@info", "End date is earlier than start date")
423 : i18nc("@info", "End date/time is earlier than start date/time");
429 return mRule
->validate(errorMessage
);
432 /******************************************************************************
433 * Called when a recurrence period radio button is clicked.
435 void RecurrenceEdit::periodClicked(QAbstractButton
* button
)
437 RepeatType oldType
= mRuleButtonType
;
438 bool none
= (button
== mNoneButton
);
439 bool atLogin
= (button
== mAtLoginButton
);
440 bool subdaily
= (button
== mSubDailyButton
);
444 mRuleButtonType
= NO_RECUR
;
449 mRuleButtonType
= AT_LOGIN
;
450 mEndDateButton
->setChecked(true);
454 mRule
= mSubDailyRule
;
455 mRuleButtonType
= SUBDAILY
;
457 else if (button
== mDailyButton
)
460 mRuleButtonType
= DAILY
;
463 else if (button
== mWeeklyButton
)
466 mRuleButtonType
= WEEKLY
;
469 else if (button
== mMonthlyButton
)
471 mRule
= mMonthlyRule
;
472 mRuleButtonType
= MONTHLY
;
473 mMonthlyShown
= true;
475 else if (button
== mYearlyButton
)
478 mRuleButtonType
= ANNUAL
;
484 if (mRuleButtonType
!= oldType
)
486 mRuleStack
->setCurrentWidget(mRule
? mRule
: mNoRule
);
487 if (oldType
== NO_RECUR
|| none
)
488 mRangeButtonBox
->setEnabled(!none
);
489 mExceptionGroup
->setEnabled(!(none
|| atLogin
));
490 mEndAnyTimeCheckBox
->setEnabled(atLogin
);
493 mNoEndDateButton
->setEnabled(!atLogin
);
494 mRepeatCountButton
->setEnabled(!atLogin
);
497 mSubRepetition
->setEnabled(!(none
|| atLogin
));
498 if (!mNoEmitTypeChanged
)
499 emit
typeChanged(mRuleButtonType
);
503 void RecurrenceEdit::slotAnyTimeToggled(bool on
)
505 QAbstractButton
* button
= mRuleButtonGroup
->checkedButton();
506 mEndTimeEdit
->setEnabled((button
== mAtLoginButton
&& !on
)
507 || (button
== mSubDailyButton
&& mEndDateButton
->isChecked()));
510 /******************************************************************************
511 * Called when a recurrence range type radio button is clicked.
513 void RecurrenceEdit::rangeTypeClicked()
515 bool endDate
= mEndDateButton
->isChecked();
516 mEndDateEdit
->setEnabled(endDate
);
517 mEndTimeEdit
->setEnabled(endDate
518 && ((mAtLoginButton
->isChecked() && !mEndAnyTimeCheckBox
->isChecked())
519 || mSubDailyButton
->isChecked()));
520 bool repeatCount
= mRepeatCountButton
->isChecked();
521 mRepeatCountEntry
->setEnabled(repeatCount
);
522 mRepeatCountLabel
->setEnabled(repeatCount
);
525 void RecurrenceEdit::showEvent(QShowEvent
*)
528 mRule
->setFrequencyFocus();
530 mRuleButtonGroup
->checkedButton()->setFocus();
534 /******************************************************************************
535 * Return the sub-repetition interval and count within the recurrence, i.e. the
536 * number of repetitions after the main recurrence.
538 Repetition
RecurrenceEdit::subRepetition() const
540 return (mRuleButtonType
>= SUBDAILY
) ? mSubRepetition
->repetition() : Repetition();
543 /******************************************************************************
544 * Called when the Sub-Repetition button has been pressed to display the
545 * sub-repetition dialog.
546 * Alarm repetition has the following restrictions:
547 * 1) Not allowed for a repeat-at-login alarm
548 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
549 * 3) The overall repeat duration must be less than the recurrence interval.
551 void RecurrenceEdit::setSubRepetition(int reminderMinutes
, bool dateOnly
)
554 switch (mRuleButtonType
)
556 case RecurrenceEdit::NO_RECUR
:
557 case RecurrenceEdit::AT_LOGIN
: // alarm repeat not allowed
560 default: // repeat duration must be less than recurrence interval
563 updateEvent(event
, false);
564 maxDuration
= event
.longestRecurrenceInterval().asSeconds()/60 - reminderMinutes
- 1;
568 mSubRepetition
->initialise(mSubRepetition
->repetition(), dateOnly
, maxDuration
);
569 mSubRepetition
->setEnabled(mRuleButtonType
>= SUBDAILY
&& maxDuration
);
572 /******************************************************************************
573 * Activate the sub-repetition dialog.
575 void RecurrenceEdit::activateSubRepetition()
577 mSubRepetition
->activate();
580 /******************************************************************************
581 * Called when the value of the repeat count field changes, to reset the
582 * minimum value to 1 if the value was 0.
584 void RecurrenceEdit::repeatCountChanged(int value
)
586 if (value
> 0 && mRepeatCountEntry
->minimum() == 0)
587 mRepeatCountEntry
->setMinimum(1);
590 /******************************************************************************
591 * Add the date entered in the exception date edit control to the list of
594 void RecurrenceEdit::addException()
596 if (!mExceptionDateEdit
|| !mExceptionDateEdit
->isValid())
598 QDate date
= mExceptionDateEdit
->date();
599 DateList::Iterator it
;
602 for (it
= mExceptionDates
.begin(); it
!= mExceptionDates
.end(); ++index
, ++it
)
606 insert
= (date
!= *it
);
612 mExceptionDates
.insert(it
, date
);
613 mExceptionDateList
->insertItem(index
, new QListWidgetItem(KGlobal::locale()->formatDate(date
)));
614 emit
contentsChanged();
616 mExceptionDateList
->setCurrentItem(mExceptionDateList
->item(index
));
617 enableExceptionButtons();
620 /******************************************************************************
621 * Change the currently highlighted exception date to that entered in the
622 * exception date edit control.
624 void RecurrenceEdit::changeException()
626 if (!mExceptionDateEdit
|| !mExceptionDateEdit
->isValid())
628 QListWidgetItem
* item
= mExceptionDateList
->currentItem();
629 if (item
&& mExceptionDateList
->isItemSelected(item
))
631 int index
= mExceptionDateList
->row(item
);
632 QDate olddate
= mExceptionDates
[index
];
633 QDate newdate
= mExceptionDateEdit
->date();
634 if (newdate
!= olddate
)
636 mExceptionDates
.removeAt(index
);
637 mExceptionDateList
->takeItem(index
);
638 emit
contentsChanged();
644 /******************************************************************************
645 * Delete the currently highlighted exception date.
647 void RecurrenceEdit::deleteException()
649 QListWidgetItem
* item
= mExceptionDateList
->currentItem();
650 if (item
&& mExceptionDateList
->isItemSelected(item
))
652 int index
= mExceptionDateList
->row(item
);
653 mExceptionDates
.removeAt(index
);
654 mExceptionDateList
->takeItem(index
);
655 emit
contentsChanged();
656 enableExceptionButtons();
660 /******************************************************************************
661 * Enable/disable the exception group buttons according to whether any item is
662 * selected in the exceptions listbox.
664 void RecurrenceEdit::enableExceptionButtons()
666 QListWidgetItem
* item
= mExceptionDateList
->currentItem();
668 if (mDeleteExceptionButton
)
669 mDeleteExceptionButton
->setEnabled(enable
);
670 if (mChangeExceptionButton
)
671 mChangeExceptionButton
->setEnabled(enable
);
673 // Prevent the exceptions list box receiving keyboard focus is it's empty
674 mExceptionDateList
->setFocusPolicy(mExceptionDateList
->count() ? Qt::WheelFocus
: Qt::NoFocus
);
677 /******************************************************************************
678 * Notify this instance of a change in the alarm start date.
680 void RecurrenceEdit::setStartDate(const QDate
& start
, const QDate
& today
)
684 setRuleDefaults(start
);
687 mEndDateEdit
->setMinDate(today
);
688 if (mExceptionDateEdit
)
689 mExceptionDateEdit
->setMinDate(today
);
693 const QString startString
= i18nc("@info Date cannot be earlier than start date", "start date");
694 mEndDateEdit
->setMinDate(start
, startString
);
695 if (mExceptionDateEdit
)
696 mExceptionDateEdit
->setMinDate(start
, startString
);
701 /******************************************************************************
702 * Specify the default recurrence end date.
704 void RecurrenceEdit::setDefaultEndDate(const QDate
& end
)
706 if (!mEndDateButton
->isChecked())
707 mEndDateEdit
->setDate(end
);
710 void RecurrenceEdit::setEndDateTime(const KDateTime
& end
)
712 KDateTime edt
= end
.toTimeSpec(mCurrStartDateTime
.timeSpec());
713 mEndDateEdit
->setDate(edt
.date());
714 mEndTimeEdit
->setValue(edt
.time());
715 mEndTimeEdit
->setEnabled(!end
.isDateOnly());
716 mEndAnyTimeCheckBox
->setChecked(end
.isDateOnly());
719 KDateTime
RecurrenceEdit::endDateTime() const
721 if (mRuleButtonGroup
->checkedButton() == mAtLoginButton
&& mEndAnyTimeCheckBox
->isChecked())
722 return KDateTime(mEndDateEdit
->date(), mCurrStartDateTime
.timeSpec());
723 return KDateTime(mEndDateEdit
->date(), mEndTimeEdit
->time(), mCurrStartDateTime
.timeSpec());
726 /******************************************************************************
727 * Set all controls to their default values.
729 void RecurrenceEdit::setDefaults(const KDateTime
& from
)
731 mCurrStartDateTime
= from
;
732 QDate fromDate
= from
.date();
733 mNoEndDateButton
->setChecked(true);
735 mSubDailyRule
->setFrequency(1);
736 mDailyRule
->setFrequency(1);
737 mWeeklyRule
->setFrequency(1);
738 mMonthlyRule
->setFrequency(1);
739 mYearlyRule
->setFrequency(1);
741 setRuleDefaults(fromDate
);
742 mMonthlyRule
->setType(MonthYearRule::DATE
); // date in month
743 mYearlyRule
->setType(MonthYearRule::DATE
); // date in year
745 mEndDateEdit
->setDate(fromDate
);
747 mNoEmitTypeChanged
= true;
749 switch (Preferences::defaultRecurPeriod())
751 case AT_LOGIN
: button
= mAtLoginButton
; break;
752 case ANNUAL
: button
= mYearlyButton
; break;
753 case MONTHLY
: button
= mMonthlyButton
; break;
754 case WEEKLY
: button
= mWeeklyButton
; break;
755 case DAILY
: button
= mDailyButton
; break;
756 case SUBDAILY
: button
= mSubDailyButton
; break;
758 default: button
= mNoneButton
; break;
760 button
->setChecked(true);
761 mNoEmitTypeChanged
= false;
763 enableExceptionButtons();
768 /******************************************************************************
769 * Set the controls for weekly, monthly and yearly rules which have not so far
770 * been shown, to their default values, depending on the recurrence start date.
772 void RecurrenceEdit::setRuleDefaults(const QDate
& fromDate
)
774 int day
= fromDate
.day();
775 int dayOfWeek
= fromDate
.dayOfWeek();
776 int month
= fromDate
.month();
778 mDailyRule
->setDays(true);
780 mWeeklyRule
->setDay(dayOfWeek
);
782 mMonthlyRule
->setDefaultValues(day
, dayOfWeek
);
784 mYearlyRule
->setDefaultValues(day
, dayOfWeek
, month
);
787 /******************************************************************************
788 * Initialise the recurrence to select repeat-at-login.
789 * This function and set() are mutually exclusive: call one or the other, not both.
791 void RecurrenceEdit::setRepeatAtLogin()
793 mAtLoginButton
->setChecked(true);
794 mEndDateButton
->setChecked(true);
797 /******************************************************************************
798 * Set the state of all controls to reflect the data in the specified event.
800 void RecurrenceEdit::set(const KAEvent
& event
)
802 setDefaults(event
.mainDateTime().kDateTime());
803 if (event
.repeatAtLogin())
805 mAtLoginButton
->setChecked(true);
806 mEndDateButton
->setChecked(true);
809 mNoneButton
->setChecked(true);
810 KARecurrence
* recurrence
= event
.recurrence();
813 KARecurrence::Type rtype
= recurrence
->type();
816 case KARecurrence::MINUTELY
:
817 mSubDailyButton
->setChecked(true);
820 case KARecurrence::DAILY
:
822 mDailyButton
->setChecked(true);
823 QBitArray rDays
= recurrence
->days();
825 for (int i
= 0; i
< 7 && !set
; ++i
)
826 set
= rDays
.testBit(i
);
828 mDailyRule
->setDays(rDays
);
830 mDailyRule
->setDays(true);
833 case KARecurrence::WEEKLY
:
835 mWeeklyButton
->setChecked(true);
836 QBitArray rDays
= recurrence
->days();
837 mWeeklyRule
->setDays(rDays
);
840 case KARecurrence::MONTHLY_POS
: // on nth (Tuesday) of the month
842 QList
<RecurrenceRule::WDayPos
> posns
= recurrence
->monthPositions();
843 int i
= posns
.first().pos();
846 // It's every (Tuesday) of the month. Convert to a weekly recurrence
847 // (but ignoring any non-every xxxDay positions).
848 mWeeklyButton
->setChecked(true);
849 mWeeklyRule
->setFrequency(recurrence
->frequency());
851 for (int i
= 0, end
= posns
.count(); i
< end
; ++i
)
854 rDays
.setBit(posns
[i
].day() - 1, 1);
856 mWeeklyRule
->setDays(rDays
);
859 mMonthlyButton
->setChecked(true);
860 mMonthlyRule
->setPosition(i
, posns
.first().day());
863 case KARecurrence::MONTHLY_DAY
: // on nth day of the month
865 mMonthlyButton
->setChecked(true);
866 QList
<int> rmd
= recurrence
->monthDays();
867 int day
= (rmd
.isEmpty()) ? event
.mainDate().day() : rmd
.first();
868 mMonthlyRule
->setDate(day
);
871 case KARecurrence::ANNUAL_DATE
: // on the nth day of (months...) in the year
872 case KARecurrence::ANNUAL_POS
: // on the nth (Tuesday) of (months...) in the year
874 if (rtype
== KARecurrence::ANNUAL_DATE
)
876 mYearlyButton
->setChecked(true);
877 const QList
<int> rmd
= recurrence
->monthDays();
878 int day
= (rmd
.isEmpty()) ? event
.mainDate().day() : rmd
.first();
879 mYearlyRule
->setDate(day
);
880 mYearlyRule
->setFeb29Type(recurrence
->feb29Type());
882 else if (rtype
== KARecurrence::ANNUAL_POS
)
884 mYearlyButton
->setChecked(true);
885 QList
<RecurrenceRule::WDayPos
> posns
= recurrence
->yearPositions();
886 mYearlyRule
->setPosition(posns
.first().pos(), posns
.first().day());
888 mYearlyRule
->setMonths(recurrence
->yearMonths());
895 mRule
->setFrequency(recurrence
->frequency());
897 // Get range information
898 KDateTime endtime
= mCurrStartDateTime
;
899 int duration
= recurrence
->duration();
901 mNoEndDateButton
->setChecked(true);
904 mRepeatCountButton
->setChecked(true);
905 mRepeatCountEntry
->setValue(duration
);
909 mEndDateButton
->setChecked(true);
910 endtime
= recurrence
->endDateTime();
911 mEndTimeEdit
->setValue(endtime
.time());
913 mEndDateEdit
->setDate(endtime
.date());
915 // Get exception information
916 mExceptionDates
= event
.recurrence()->exDates();
917 qSort(mExceptionDates
);
918 mExceptionDateList
->clear();
919 for (int i
= 0, iend
= mExceptionDates
.count(); i
< iend
; ++i
)
920 new QListWidgetItem(KGlobal::locale()->formatDate(mExceptionDates
[i
]), mExceptionDateList
);
921 enableExceptionButtons();
922 mExcludeHolidays
->setChecked(event
.holidaysExcluded());
923 mWorkTimeOnly
->setChecked(event
.workTimeOnly());
925 // Get repetition within recurrence
926 mSubRepetition
->set(event
.repetition());
933 /******************************************************************************
934 * Update the specified KAEvent with the entered recurrence data.
935 * If 'adjustStart' is true, the start date/time will be adjusted if necessary
936 * to be the first date/time which recurs on or after the original start.
938 void RecurrenceEdit::updateEvent(KAEvent
& event
, bool adjustStart
)
940 // Get end date and repeat count, common to all types of recurring events
944 if (mNoEndDateButton
->isChecked())
946 else if (mRepeatCountButton
->isChecked())
947 repeatCount
= mRepeatCountEntry
->value();
951 endDate
= mEndDateEdit
->date();
952 endTime
= mEndTimeEdit
->time();
955 // Set up the recurrence according to the type selected
956 event
.startChanges();
957 QAbstractButton
* button
= mRuleButtonGroup
->checkedButton();
958 event
.setRepeatAtLogin(button
== mAtLoginButton
);
959 int frequency
= mRule
? mRule
->frequency() : 0;
960 if (button
== mSubDailyButton
)
962 KDateTime
endDateTime(endDate
, endTime
, mCurrStartDateTime
.timeSpec());
963 event
.setRecurMinutely(frequency
, repeatCount
, endDateTime
);
965 else if (button
== mDailyButton
)
967 event
.setRecurDaily(frequency
, mDailyRule
->days(), repeatCount
, endDate
);
969 else if (button
== mWeeklyButton
)
971 event
.setRecurWeekly(frequency
, mWeeklyRule
->days(), repeatCount
, endDate
);
973 else if (button
== mMonthlyButton
)
975 if (mMonthlyRule
->type() == MonthlyRule::POS
)
978 KAEvent::MonthPos pos
;
979 pos
.days
.fill(false);
980 pos
.days
.setBit(mMonthlyRule
->dayOfWeek() - 1);
981 pos
.weeknum
= mMonthlyRule
->week();
982 QList
<KAEvent::MonthPos
> poses
;
984 event
.setRecurMonthlyByPos(frequency
, poses
, repeatCount
, endDate
);
989 int daynum
= mMonthlyRule
->date();
991 daynums
.append(daynum
);
992 event
.setRecurMonthlyByDate(frequency
, daynums
, repeatCount
, endDate
);
995 else if (button
== mYearlyButton
)
997 QList
<int> months
= mYearlyRule
->months();
998 if (mYearlyRule
->type() == YearlyRule::POS
)
1001 KAEvent::MonthPos pos
;
1002 pos
.days
.fill(false);
1003 pos
.days
.setBit(mYearlyRule
->dayOfWeek() - 1);
1004 pos
.weeknum
= mYearlyRule
->week();
1005 QList
<KAEvent::MonthPos
> poses
;
1007 event
.setRecurAnnualByPos(frequency
, poses
, months
, repeatCount
, endDate
);
1011 // It's by date in month
1012 event
.setRecurAnnualByDate(frequency
, months
, mYearlyRule
->date(),
1013 mYearlyRule
->feb29Type(), repeatCount
, endDate
);
1022 if (!event
.recurs())
1025 return; // an error occurred setting up the recurrence
1028 event
.setFirstRecurrence();
1030 // Set up repetition within the recurrence
1031 // N.B. This requires the main recurrence to be set up first.
1032 event
.setRepetition((mRuleButtonType
< SUBDAILY
) ? Repetition() : mSubRepetition
->repetition());
1034 // Set up exceptions
1035 event
.recurrence()->setExDates(mExceptionDates
);
1036 event
.setWorkTimeOnly(mWorkTimeOnly
->isChecked());
1037 event
.setExcludeHolidays(mExcludeHolidays
->isChecked());
1043 /******************************************************************************
1044 * Save the state of all controls.
1046 void RecurrenceEdit::saveState()
1048 mSavedRuleButton
= mRuleButtonGroup
->checkedButton();
1051 mSavedRangeButton
= mRangeButtonGroup
->checkedButton();
1052 if (mSavedRangeButton
== mRepeatCountButton
)
1053 mSavedRecurCount
= mRepeatCountEntry
->value();
1054 else if (mSavedRangeButton
== mEndDateButton
)
1056 mSavedEndDateTime
= KDateTime(QDateTime(mEndDateEdit
->date(), mEndTimeEdit
->time()), mCurrStartDateTime
.timeSpec());
1057 mSavedEndDateTime
.setDateOnly(mEndAnyTimeCheckBox
->isChecked());
1059 mSavedExceptionDates
= mExceptionDates
;
1060 mSavedWorkTimeOnly
= mWorkTimeOnly
->isChecked();
1061 mSavedExclHolidays
= mExcludeHolidays
->isChecked();
1062 mSavedRepetition
= mSubRepetition
->repetition();
1065 /******************************************************************************
1066 * Check whether any of the controls have changed state since initialisation.
1068 bool RecurrenceEdit::stateChanged() const
1070 if (mSavedRuleButton
!= mRuleButtonGroup
->checkedButton()
1071 || mSavedRangeButton
!= mRangeButtonGroup
->checkedButton()
1072 || (mRule
&& mRule
->stateChanged()))
1074 if (mSavedRangeButton
== mRepeatCountButton
1075 && mSavedRecurCount
!= mRepeatCountEntry
->value())
1077 if (mSavedRangeButton
== mEndDateButton
)
1079 KDateTime
edt(QDateTime(mEndDateEdit
->date(), mEndTimeEdit
->time()), mCurrStartDateTime
.timeSpec());
1080 edt
.setDateOnly(mEndAnyTimeCheckBox
->isChecked());
1081 if (mSavedEndDateTime
!= edt
)
1084 if (mSavedExceptionDates
!= mExceptionDates
1085 || mSavedWorkTimeOnly
!= mWorkTimeOnly
->isChecked()
1086 || mSavedExclHolidays
!= mExcludeHolidays
->isChecked()
1087 || mSavedRepetition
!= mSubRepetition
->repetition())
1094 /*=============================================================================
1096 = Base class for rule widgets, including recurrence frequency.
1097 =============================================================================*/
1099 Rule::Rule(const QString
& freqText
, const QString
& freqWhatsThis
, bool time
, bool readOnly
, QWidget
* parent
)
1102 mLayout
= new QVBoxLayout(this);
1103 mLayout
->setMargin(0);
1104 mLayout
->setSpacing(KDialog::spacingHint());
1105 KHBox
* freqBox
= new KHBox(this);
1106 freqBox
->setMargin(0);
1107 mLayout
->addWidget(freqBox
, 0, Qt::AlignLeft
);
1108 KHBox
* box
= new KHBox(freqBox
); // this is to control the QWhatsThis text display area
1110 box
->setSpacing(KDialog::spacingHint());
1112 QLabel
* label
= new QLabel(i18nc("@label:spinbox", "Recur e&very"), box
);
1113 label
->setFixedSize(label
->sizeHint());
1117 mSpinBox
= mTimeSpinBox
= new TimeSpinBox(1, 5999, box
);
1118 mTimeSpinBox
->setFixedSize(mTimeSpinBox
->sizeHint());
1119 mTimeSpinBox
->setReadOnly(readOnly
);
1124 mSpinBox
= mIntSpinBox
= new SpinBox(1, 999, box
);
1125 mIntSpinBox
->setFixedSize(mIntSpinBox
->sizeHint());
1126 mIntSpinBox
->setReadOnly(readOnly
);
1128 connect(mSpinBox
, SIGNAL(valueChanged(int)), SIGNAL(frequencyChanged()));
1129 connect(mSpinBox
, SIGNAL(valueChanged(int)), SIGNAL(changed()));
1130 label
->setBuddy(mSpinBox
);
1131 label
= new QLabel(freqText
, box
);
1132 label
->setFixedSize(label
->sizeHint());
1133 box
->setFixedSize(sizeHint());
1134 box
->setWhatsThis(freqWhatsThis
);
1136 new QWidget(freqBox
); // left adjust the visible widgets
1137 freqBox
->setFixedHeight(freqBox
->sizeHint().height());
1138 freqBox
->setFocusProxy(mSpinBox
);
1141 int Rule::frequency() const
1144 return mIntSpinBox
->value();
1146 return mTimeSpinBox
->value();
1150 void Rule::setFrequency(int n
)
1153 mIntSpinBox
->setValue(n
);
1155 mTimeSpinBox
->setValue(n
);
1158 /******************************************************************************
1159 * Save the state of all controls.
1161 void Rule::saveState()
1163 mSavedFrequency
= frequency();
1166 /******************************************************************************
1167 * Check whether any of the controls have changed state since initialisation.
1169 bool Rule::stateChanged() const
1171 return (mSavedFrequency
!= frequency());
1175 /*=============================================================================
1176 = Class SubDailyRule
1177 = Sub-daily rule widget.
1178 =============================================================================*/
1180 SubDailyRule::SubDailyRule(bool readOnly
, QWidget
* parent
)
1181 : Rule(i18nc("@label Time units for user-entered numbers", "hours:minutes"),
1182 i18nc("@info:whatsthis", "Enter the number of hours and minutes between repetitions of the alarm"),
1183 true, readOnly
, parent
)
1187 /*=============================================================================
1189 = Daily/weekly rule widget base class.
1190 =============================================================================*/
1192 DayWeekRule::DayWeekRule(const QString
& freqText
, const QString
& freqWhatsThis
, const QString
& daysWhatsThis
,
1193 bool readOnly
, QWidget
* parent
)
1194 : Rule(freqText
, freqWhatsThis
, false, readOnly
, parent
),
1197 QGridLayout
* grid
= new QGridLayout();
1199 grid
->setRowStretch(0, 1);
1200 layout()->addLayout(grid
);
1202 QLabel
* label
= new QLabel(i18nc("@label On: Tuesday", "O&n:"), this);
1203 label
->setFixedSize(label
->sizeHint());
1204 grid
->addWidget(label
, 0, 0, Qt::AlignRight
| Qt::AlignTop
);
1205 grid
->setColumnMinimumWidth(1, KDialog::spacingHint());
1207 // List the days of the week starting at the user's start day of the week.
1208 // Save the first day of the week, just in case it changes while the dialog is open.
1209 QWidget
* box
= new QWidget(this); // this is to control the QWhatsThis text display area
1210 QGridLayout
* dgrid
= new QGridLayout(box
);
1211 dgrid
->setMargin(0);
1212 dgrid
->setSpacing(KDialog::spacingHint());
1213 const KCalendarSystem
* calendar
= KGlobal::locale()->calendar();
1214 for (int i
= 0; i
< 7; ++i
)
1216 int day
= KAlarm::localeDayInWeek_to_weekDay(i
);
1217 mDayBox
[i
] = new CheckBox(calendar
->weekDayName(day
), box
);
1218 mDayBox
[i
]->setFixedSize(mDayBox
[i
]->sizeHint());
1219 mDayBox
[i
]->setReadOnly(readOnly
);
1220 connect(mDayBox
[i
], SIGNAL(toggled(bool)), SIGNAL(changed()));
1221 dgrid
->addWidget(mDayBox
[i
], i
%4, i
/4, Qt::AlignLeft
);
1223 box
->setFixedSize(box
->sizeHint());
1224 box
->setWhatsThis(daysWhatsThis
);
1225 grid
->addWidget(box
, 0, 2, Qt::AlignLeft
);
1226 label
->setBuddy(mDayBox
[0]);
1227 grid
->setColumnStretch(3, 1);
1230 /******************************************************************************
1231 * Fetch which days of the week have been ticked.
1233 QBitArray
DayWeekRule::days() const
1237 for (int i
= 0; i
< 7; ++i
)
1238 if (mDayBox
[i
]->isChecked())
1239 ds
.setBit(KAlarm::localeDayInWeek_to_weekDay(i
) - 1, 1);
1243 /******************************************************************************
1244 * Tick/untick every day of the week.
1246 void DayWeekRule::setDays(bool tick
)
1248 for (int i
= 0; i
< 7; ++i
)
1249 mDayBox
[i
]->setChecked(tick
);
1252 /******************************************************************************
1253 * Tick/untick each day of the week according to the specified bits.
1255 void DayWeekRule::setDays(const QBitArray
& days
)
1257 for (int i
= 0; i
< 7; ++i
)
1259 bool x
= days
.testBit(KAlarm::localeDayInWeek_to_weekDay(i
) - 1);
1260 mDayBox
[i
]->setChecked(x
);
1264 /******************************************************************************
1265 * Tick the specified day of the week, and untick all other days.
1267 void DayWeekRule::setDay(int dayOfWeek
)
1269 for (int i
= 0; i
< 7; ++i
)
1270 mDayBox
[i
]->setChecked(false);
1271 if (dayOfWeek
> 0 && dayOfWeek
<= 7)
1272 mDayBox
[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek
)]->setChecked(true);
1275 /******************************************************************************
1276 * Validate: check that at least one day is selected.
1278 QWidget
* DayWeekRule::validate(QString
& errorMessage
)
1280 for (int i
= 0; i
< 7; ++i
)
1281 if (mDayBox
[i
]->isChecked())
1283 errorMessage
= i18nc("@info", "No day selected");
1287 /******************************************************************************
1288 * Save the state of all controls.
1290 void DayWeekRule::saveState()
1293 mSavedDays
= days();
1296 /******************************************************************************
1297 * Check whether any of the controls have changed state since initialisation.
1299 bool DayWeekRule::stateChanged() const
1301 return (Rule::stateChanged()
1302 || mSavedDays
!= days());
1306 /*=============================================================================
1308 = Daily rule widget.
1309 =============================================================================*/
1311 DailyRule::DailyRule(bool readOnly
, QWidget
* parent
)
1312 : DayWeekRule(i18nc("@label Time unit for user-entered number", "day(s)"),
1313 i18nc("@info:whatsthis", "Enter the number of days between repetitions of the alarm"),
1314 i18nc("@info:whatsthis", "Select the days of the week on which the alarm is allowed to occur"),
1319 /*=============================================================================
1321 = Weekly rule widget.
1322 =============================================================================*/
1324 WeeklyRule::WeeklyRule(bool readOnly
, QWidget
* parent
)
1325 : DayWeekRule(i18nc("@label Time unit for user-entered number", "week(s)"),
1326 i18nc("@info:whatsthis", "Enter the number of weeks between repetitions of the alarm"),
1327 i18nc("@info:whatsthis", "Select the days of the week on which to repeat the alarm"),
1332 /*=============================================================================
1333 = Class MonthYearRule
1334 = Monthly/yearly rule widget base class.
1335 =============================================================================*/
1337 MonthYearRule::MonthYearRule(const QString
& freqText
, const QString
& freqWhatsThis
, bool allowEveryWeek
,
1338 bool readOnly
, QWidget
* parent
)
1339 : Rule(freqText
, freqWhatsThis
, false, readOnly
, parent
),
1340 mEveryWeek(allowEveryWeek
)
1342 mButtonGroup
= new ButtonGroup(this);
1344 // Month day selector
1345 KHBox
* box
= new KHBox(this);
1347 box
->setSpacing(KDialog::spacingHint());
1348 layout()->addWidget(box
);
1350 mDayButton
= new RadioButton(i18nc("@option:radio On day number in the month", "O&n day"), box
);
1351 mDayButton
->setFixedSize(mDayButton
->sizeHint());
1352 mDayButton
->setReadOnly(readOnly
);
1353 mButtonGroup
->addButton(mDayButton
);
1354 mDayButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on the selected day of the month"));
1356 mDayCombo
= new ComboBox(box
);
1357 mDayCombo
->setEditable(false);
1358 mDayCombo
->setMaxVisibleItems(11);
1359 for (int i
= 0; i
< 31; ++i
)
1360 mDayCombo
->addItem(QString::number(i
+ 1));
1361 mDayCombo
->addItem(i18nc("@item:inlistbox Last day of month", "Last"));
1362 mDayCombo
->setFixedSize(mDayCombo
->sizeHint());
1363 mDayCombo
->setReadOnly(readOnly
);
1364 mDayCombo
->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the month on which to repeat the alarm"));
1365 mDayButton
->setFocusWidget(mDayCombo
);
1366 connect(mDayCombo
, SIGNAL(activated(int)), SLOT(slotDaySelected(int)));
1367 connect(mDayCombo
, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()));
1369 box
->setStretchFactor(new QWidget(box
), 1); // left adjust the controls
1370 box
->setFixedHeight(box
->sizeHint().height());
1372 // Month position selector
1373 box
= new KHBox(this);
1375 box
->setSpacing(KDialog::spacingHint());
1376 layout()->addWidget(box
);
1378 mPosButton
= new RadioButton(i18nc("@option:radio On the 1st Tuesday", "On t&he"), box
);
1379 mPosButton
->setFixedSize(mPosButton
->sizeHint());
1380 mPosButton
->setReadOnly(readOnly
);
1381 mButtonGroup
->addButton(mPosButton
);
1382 mPosButton
->setWhatsThis(i18nc("@info:whatsthis", "Repeat the alarm on one day of the week, in the selected week of the month"));
1384 mWeekCombo
= new ComboBox(box
);
1385 mWeekCombo
->setEditable(false);
1386 mWeekCombo
->addItem(i18nc("@item:inlistbox", "1st"));
1387 mWeekCombo
->addItem(i18nc("@item:inlistbox", "2nd"));
1388 mWeekCombo
->addItem(i18nc("@item:inlistbox", "3rd"));
1389 mWeekCombo
->addItem(i18nc("@item:inlistbox", "4th"));
1390 mWeekCombo
->addItem(i18nc("@item:inlistbox", "5th"));
1391 mWeekCombo
->addItem(i18nc("@item:inlistbox Last Monday in March", "Last"));
1392 mWeekCombo
->addItem(i18nc("@item:inlistbox", "2nd Last"));
1393 mWeekCombo
->addItem(i18nc("@item:inlistbox", "3rd Last"));
1394 mWeekCombo
->addItem(i18nc("@item:inlistbox", "4th Last"));
1395 mWeekCombo
->addItem(i18nc("@item:inlistbox", "5th Last"));
1398 mWeekCombo
->addItem(i18nc("@item:inlistbox Every (Monday...) in month", "Every"));
1399 mWeekCombo
->setMaxVisibleItems(11);
1401 mWeekCombo
->setWhatsThis(i18nc("@info:whatsthis", "Select the week of the month in which to repeat the alarm"));
1402 mWeekCombo
->setFixedSize(mWeekCombo
->sizeHint());
1403 mWeekCombo
->setReadOnly(readOnly
);
1404 mPosButton
->setFocusWidget(mWeekCombo
);
1405 connect(mWeekCombo
, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()));
1407 mDayOfWeekCombo
= new ComboBox(box
);
1408 mDayOfWeekCombo
->setEditable(false);
1409 const KCalendarSystem
* calendar
= KGlobal::locale()->calendar();
1410 for (int i
= 0; i
< 7; ++i
)
1412 int day
= KAlarm::localeDayInWeek_to_weekDay(i
);
1413 mDayOfWeekCombo
->addItem(calendar
->weekDayName(day
));
1415 mDayOfWeekCombo
->setReadOnly(readOnly
);
1416 mDayOfWeekCombo
->setWhatsThis(i18nc("@info:whatsthis", "Select the day of the week on which to repeat the alarm"));
1417 connect(mDayOfWeekCombo
, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()));
1419 box
->setStretchFactor(new QWidget(box
), 1); // left adjust the controls
1420 box
->setFixedHeight(box
->sizeHint().height());
1421 connect(mButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SLOT(clicked(QAbstractButton
*)));
1422 connect(mButtonGroup
, SIGNAL(buttonSet(QAbstractButton
*)), SIGNAL(changed()));
1425 MonthYearRule::DayPosType
MonthYearRule::type() const
1427 return (mButtonGroup
->checkedButton() == mDayButton
) ? DATE
: POS
;
1430 void MonthYearRule::setType(MonthYearRule::DayPosType type
)
1433 mDayButton
->setChecked(true);
1435 mPosButton
->setChecked(true);
1438 void MonthYearRule::setDefaultValues(int dayOfMonth
, int dayOfWeek
)
1441 mDayCombo
->setCurrentIndex(dayOfMonth
);
1442 mWeekCombo
->setCurrentIndex(dayOfMonth
/ 7);
1443 mDayOfWeekCombo
->setCurrentIndex(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek
));
1446 int MonthYearRule::date() const
1448 int daynum
= mDayCombo
->currentIndex() + 1;
1449 return (daynum
<= 31) ? daynum
: 31 - daynum
;
1452 int MonthYearRule::week() const
1454 int weeknum
= mWeekCombo
->currentIndex() + 1;
1455 return (weeknum
<= 5) ? weeknum
: (weeknum
== 11) ? 0 : 5 - weeknum
;
1458 int MonthYearRule::dayOfWeek() const
1460 return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo
->currentIndex());
1463 void MonthYearRule::setDate(int dayOfMonth
)
1465 mDayButton
->setChecked(true);;
1466 mDayCombo
->setCurrentIndex(dayOfMonth
> 0 ? dayOfMonth
- 1 : dayOfMonth
< 0 ? 30 - dayOfMonth
: 0); // day 0 shouldn't ever occur
1469 void MonthYearRule::setPosition(int week
, int dayOfWeek
)
1471 mPosButton
->setChecked(true);
1472 mWeekCombo
->setCurrentIndex((week
> 0) ? week
- 1 : (week
< 0) ? 4 - week
: mEveryWeek
? 10 : 0);
1473 mDayOfWeekCombo
->setCurrentIndex(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek
));
1476 void MonthYearRule::enableSelection(DayPosType type
)
1478 bool date
= (type
== DATE
);
1479 mDayCombo
->setEnabled(date
);
1480 mWeekCombo
->setEnabled(!date
);
1481 mDayOfWeekCombo
->setEnabled(!date
);
1484 void MonthYearRule::clicked(QAbstractButton
* button
)
1486 enableSelection(button
== mDayButton
? DATE
: POS
);
1489 void MonthYearRule::slotDaySelected(int index
)
1491 daySelected(index
<= 30 ? index
+ 1 : 30 - index
);
1494 /******************************************************************************
1495 * Save the state of all controls.
1497 void MonthYearRule::saveState()
1500 mSavedType
= type();
1501 if (mSavedType
== DATE
)
1505 mSavedWeek
= week();
1506 mSavedWeekDay
= dayOfWeek();
1510 /******************************************************************************
1511 * Check whether any of the controls have changed state since initialisation.
1513 bool MonthYearRule::stateChanged() const
1515 if (Rule::stateChanged()
1516 || mSavedType
!= type())
1518 if (mSavedType
== DATE
)
1520 if (mSavedDay
!= date())
1525 if (mSavedWeek
!= week()
1526 || mSavedWeekDay
!= dayOfWeek())
1533 /*=============================================================================
1535 = Monthly rule widget.
1536 =============================================================================*/
1538 MonthlyRule::MonthlyRule(bool readOnly
, QWidget
* parent
)
1539 : MonthYearRule(i18nc("@label Time unit for user-entered number", "month(s)"),
1540 i18nc("@info:whatsthis", "Enter the number of months between repetitions of the alarm"),
1541 false, readOnly
, parent
)
1545 /*=============================================================================
1547 = Yearly rule widget.
1548 =============================================================================*/
1550 YearlyRule::YearlyRule(bool readOnly
, QWidget
* parent
)
1551 : MonthYearRule(i18nc("@label Time unit for user-entered number", "year(s)"),
1552 i18nc("@info:whatsthis", "Enter the number of years between repetitions of the alarm"),
1553 true, readOnly
, parent
)
1555 // Set up the month selection widgets
1556 QHBoxLayout
* hlayout
= new QHBoxLayout();
1557 hlayout
->setMargin(0);
1558 layout()->addLayout(hlayout
);
1559 QLabel
* label
= new QLabel(i18nc("@label List of months to select", "Months:"), this);
1560 label
->setFixedSize(label
->sizeHint());
1561 hlayout
->addWidget(label
, 0, Qt::AlignLeft
| Qt::AlignTop
);
1563 // List the months of the year.
1564 QWidget
* w
= new QWidget(this); // this is to control the QWhatsThis text display area
1565 hlayout
->addWidget(w
, 1, Qt::AlignLeft
);
1566 QGridLayout
* grid
= new QGridLayout(w
);
1568 grid
->setSpacing(KDialog::spacingHint());
1569 const KCalendarSystem
* calendar
= KGlobal::locale()->calendar();
1570 int year
= KDateTime::currentLocalDate().year();
1571 for (int i
= 0; i
< 12; ++i
)
1573 mMonthBox
[i
] = new CheckBox(calendar
->monthName(i
+ 1, year
, KCalendarSystem::ShortName
), w
);
1574 mMonthBox
[i
]->setFixedSize(mMonthBox
[i
]->sizeHint());
1575 mMonthBox
[i
]->setReadOnly(readOnly
);
1576 connect(mMonthBox
[i
], SIGNAL(toggled(bool)), SIGNAL(changed()));
1577 grid
->addWidget(mMonthBox
[i
], i
%3, i
/3, Qt::AlignLeft
);
1579 connect(mMonthBox
[1], SIGNAL(toggled(bool)), SLOT(enableFeb29()));
1580 w
->setFixedHeight(w
->sizeHint().height());
1581 w
->setWhatsThis(i18nc("@info:whatsthis", "Select the months of the year in which to repeat the alarm"));
1583 // February 29th handling option
1584 KHBox
* f29box
= new KHBox(this);
1585 f29box
->setMargin(0);
1586 layout()->addWidget(f29box
);
1587 KHBox
* box
= new KHBox(f29box
); // this is to control the QWhatsThis text display area
1589 box
->setSpacing(KDialog::spacingHint());
1590 mFeb29Label
= new QLabel(i18nc("@label:listbox", "February 2&9th alarm in non-leap years:"), box
);
1591 mFeb29Label
->setFixedSize(mFeb29Label
->sizeHint());
1592 mFeb29Combo
= new ComboBox(box
);
1593 mFeb29Combo
->setEditable(false);
1594 mFeb29Combo
->addItem(i18nc("@item:inlistbox No date", "None"));
1595 mFeb29Combo
->addItem(i18nc("@item:inlistbox 1st March (short form)", "1 Mar"));
1596 mFeb29Combo
->addItem(i18nc("@item:inlistbox 28th February (short form)", "28 Feb"));
1597 mFeb29Combo
->setFixedSize(mFeb29Combo
->sizeHint());
1598 mFeb29Combo
->setReadOnly(readOnly
);
1599 connect(mFeb29Combo
, SIGNAL(currentIndexChanged(int)), SIGNAL(changed()));
1600 mFeb29Label
->setBuddy(mFeb29Combo
);
1601 box
->setFixedSize(box
->sizeHint());
1602 box
->setWhatsThis(i18nc("@info:whatsthis", "Select which date, if any, the February 29th alarm should trigger in non-leap years"));
1603 new QWidget(f29box
); // left adjust the visible widgets
1604 f29box
->setFixedHeight(f29box
->sizeHint().height());
1607 void YearlyRule::setDefaultValues(int dayOfMonth
, int dayOfWeek
, int month
)
1609 MonthYearRule::setDefaultValues(dayOfMonth
, dayOfWeek
);
1611 for (int i
= 0; i
< 12; ++i
)
1612 mMonthBox
[i
]->setChecked(i
== month
);
1613 setFeb29Type(KARecurrence::defaultFeb29Type());
1614 daySelected(dayOfMonth
); // enable/disable month checkboxes as appropriate
1617 /******************************************************************************
1618 * Fetch which months have been checked (1 - 12).
1619 * Reply = true if February has been checked.
1621 QList
<int> YearlyRule::months() const
1624 for (int i
= 0; i
< 12; ++i
)
1625 if (mMonthBox
[i
]->isChecked() && mMonthBox
[i
]->isEnabled())
1626 mnths
.append(i
+ 1);
1630 /******************************************************************************
1631 * Check/uncheck each month of the year according to the specified list.
1633 void YearlyRule::setMonths(const QList
<int>& mnths
)
1636 for (int i
= 0; i
< 12; ++i
)
1638 for (int i
= 0, end
= mnths
.count(); i
< end
; ++i
)
1639 checked
[mnths
[i
] - 1] = true;
1640 for (int i
= 0; i
< 12; ++i
)
1641 mMonthBox
[i
]->setChecked(checked
[i
]);
1645 /******************************************************************************
1646 * Return the date for February 29th alarms in non-leap years.
1648 KARecurrence::Feb29Type
YearlyRule::feb29Type() const
1650 if (mFeb29Combo
->isEnabled())
1652 switch (mFeb29Combo
->currentIndex())
1654 case 1: return KARecurrence::Feb29_Mar1
;
1655 case 2: return KARecurrence::Feb29_Feb28
;
1659 return KARecurrence::Feb29_None
;
1662 /******************************************************************************
1663 * Set the date for February 29th alarms to trigger in non-leap years.
1665 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type
)
1671 case KARecurrence::Feb29_None
: index
= 0; break;
1672 case KARecurrence::Feb29_Mar1
: index
= 1; break;
1673 case KARecurrence::Feb29_Feb28
: index
= 2; break;
1675 mFeb29Combo
->setCurrentIndex(index
);
1678 /******************************************************************************
1679 * Validate: check that at least one month is selected.
1681 QWidget
* YearlyRule::validate(QString
& errorMessage
)
1683 for (int i
= 0; i
< 12; ++i
)
1684 if (mMonthBox
[i
]->isChecked() && mMonthBox
[i
]->isEnabled())
1686 errorMessage
= i18nc("@info", "No month selected");
1687 return mMonthBox
[0];
1690 /******************************************************************************
1691 * Called when a yearly recurrence type radio button is clicked,
1692 * to enable/disable month checkboxes as appropriate for the date selected.
1694 void YearlyRule::clicked(QAbstractButton
* button
)
1696 MonthYearRule::clicked(button
);
1697 daySelected(buttonType(button
) == DATE
? date() : 1);
1700 /******************************************************************************
1701 * Called when a day of the month is selected in a yearly recurrence, to
1702 * disable months for which the day is out of range.
1704 void YearlyRule::daySelected(int day
)
1706 mMonthBox
[1]->setEnabled(day
<= 29); // February
1707 bool enable
= (day
!= 31);
1708 mMonthBox
[3]->setEnabled(enable
); // April
1709 mMonthBox
[5]->setEnabled(enable
); // June
1710 mMonthBox
[8]->setEnabled(enable
); // September
1711 mMonthBox
[10]->setEnabled(enable
); // November
1715 /******************************************************************************
1716 * Enable/disable the February 29th combo box depending on whether February
1719 void YearlyRule::enableFeb29()
1721 bool enable
= (type() == DATE
&& date() == 29 && mMonthBox
[1]->isChecked() && mMonthBox
[1]->isEnabled());
1722 mFeb29Label
->setEnabled(enable
);
1723 mFeb29Combo
->setEnabled(enable
);
1726 /******************************************************************************
1727 * Save the state of all controls.
1729 void YearlyRule::saveState()
1731 MonthYearRule::saveState();
1732 mSavedMonths
= months();
1733 mSavedFeb29Type
= feb29Type();
1736 /******************************************************************************
1737 * Check whether any of the controls have changed state since initialisation.
1739 bool YearlyRule::stateChanged() const
1741 return (MonthYearRule::stateChanged()
1742 || mSavedMonths
!= months()
1743 || mSavedFeb29Type
!= feb29Type());