kalarm

recurrenceedit.cpp
1/*
2 * recurrenceedit.cpp - widget to edit the event's recurrence definition
3 * Program: kalarm
4 * Copyright © 2002-2008 by David Jarvie <djarvie@kde.org>
5 *
6 * Based originally on KOrganizer module koeditorrecurrence.cpp,
7 * Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
8 *
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.
13 *
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.
18 *
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.
22 */
23
24#include "kalarm.h"
25
26#include <tqtooltip.h>
27#include <tqlayout.h>
28#include <tqvbox.h>
29#include <tqwidgetstack.h>
30#include <tqlistbox.h>
31#include <tqframe.h>
32#include <tqlabel.h>
33#include <tqpushbutton.h>
34#include <tqlineedit.h>
35#include <tqwhatsthis.h>
36
37#include <tdeglobal.h>
38#include <tdelocale.h>
39#include <kcalendarsystem.h>
40#include <kiconloader.h>
41#include <kdialog.h>
42#include <tdemessagebox.h>
43#include <kdebug.h>
44
45#include <libkcal/event.h>
46
47#include "alarmevent.h"
48#include "alarmtimewidget.h"
49#include "checkbox.h"
50#include "combobox.h"
51#include "dateedit.h"
52#include "functions.h"
53#include "kalarmapp.h"
54#include "karecurrence.h"
55#include "preferences.h"
56#include "radiobutton.h"
57#include "repetition.h"
58#include "spinbox.h"
59#include "timeedit.h"
60#include "timespinbox.h"
61#include "buttongroup.h"
62using namespace KCal;
63
64#include "recurrenceedit.moc"
65#include "recurrenceeditprivate.moc"
66
67// Collect these widget labels together to ensure consistent wording and
68// translations across different modules.
69TQString RecurrenceEdit::i18n_Norecur() { return i18n("No recurrence"); }
70TQString RecurrenceEdit::i18n_NoRecur() { return i18n("No Recurrence"); }
71TQString RecurrenceEdit::i18n_AtLogin() { return i18n("At Login"); }
72TQString RecurrenceEdit::i18n_l_Atlogin() { return i18n("At &login"); }
73TQString RecurrenceEdit::i18n_HourlyMinutely() { return i18n("Hourly/Minutely"); }
74TQString RecurrenceEdit::i18n_u_HourlyMinutely() { return i18n("Ho&urly/Minutely"); }
75TQString RecurrenceEdit::i18n_Daily() { return i18n("Daily"); }
76TQString RecurrenceEdit::i18n_d_Daily() { return i18n("&Daily"); }
77TQString RecurrenceEdit::i18n_Weekly() { return i18n("Weekly"); }
78TQString RecurrenceEdit::i18n_w_Weekly() { return i18n("&Weekly"); }
79TQString RecurrenceEdit::i18n_Monthly() { return i18n("Monthly"); }
80TQString RecurrenceEdit::i18n_m_Monthly() { return i18n("&Monthly"); }
81TQString RecurrenceEdit::i18n_Yearly() { return i18n("Yearly"); }
82TQString RecurrenceEdit::i18n_y_Yearly() { return i18n("&Yearly"); }
83
84
85RecurrenceEdit::RecurrenceEdit(bool readOnly, TQWidget* parent, const char* name)
86 : TQFrame(parent, name),
87 mRule(0),
88 mRuleButtonType(INVALID_RECUR),
89 mDailyShown(false),
90 mWeeklyShown(false),
91 mMonthlyShown(false),
92 mYearlyShown(false),
93 mNoEmitTypeChanged(true),
94 mReadOnly(readOnly)
95{
96 TQBoxLayout* layout;
97 TQVBoxLayout* topLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
98
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.
104 */
105
106 TQGroupBox* recurGroup = new TQGroupBox(1, TQt::Vertical, i18n("Recurrence Rule"), this, "recurGroup");
107 topLayout->addWidget(recurGroup);
108 TQFrame* ruleFrame = new TQFrame(recurGroup, "ruleFrame");
109 layout = new TQVBoxLayout(ruleFrame, 0);
110 layout->addSpacing(KDialog::spacingHint()/2);
111
112 layout = new TQHBoxLayout(layout, 0);
113 TQBoxLayout* lay = new TQVBoxLayout(layout, 0);
114 mRuleButtonGroup = new ButtonGroup(1, TQt::Horizontal, ruleFrame);
115 mRuleButtonGroup->setInsideMargin(0);
116 mRuleButtonGroup->setFrameStyle(TQFrame::NoFrame);
117 lay->addWidget(mRuleButtonGroup);
118 lay->addStretch(); // top-adjust the interval radio buttons
119 connect(mRuleButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(periodClicked(int)));
120
121 mNoneButton = new RadioButton(i18n_Norecur(), mRuleButtonGroup);
122 mNoneButton->setFixedSize(mNoneButton->sizeHint());
123 mNoneButton->setReadOnly(mReadOnly);
124 TQWhatsThis::add(mNoneButton, i18n("Do not repeat the alarm"));
125
126 mAtLoginButton = new RadioButton(i18n_l_Atlogin(), mRuleButtonGroup);
127 mAtLoginButton->setFixedSize(mAtLoginButton->sizeHint());
128 mAtLoginButton->setReadOnly(mReadOnly);
129 TQWhatsThis::add(mAtLoginButton,
130 i18n("Trigger the alarm at the specified date/time and at every login until then.\n"
131 "Note that it will also be triggered any time the alarm daemon is restarted."));
132
133 mSubDailyButton = new RadioButton(i18n_u_HourlyMinutely(), mRuleButtonGroup);
134 mSubDailyButton->setFixedSize(mSubDailyButton->sizeHint());
135 mSubDailyButton->setReadOnly(mReadOnly);
136 TQWhatsThis::add(mSubDailyButton,
137 i18n("Repeat the alarm at hourly/minutely intervals"));
138
139 mDailyButton = new RadioButton(i18n_d_Daily(), mRuleButtonGroup);
140 mDailyButton->setFixedSize(mDailyButton->sizeHint());
141 mDailyButton->setReadOnly(mReadOnly);
142 TQWhatsThis::add(mDailyButton,
143 i18n("Repeat the alarm at daily intervals"));
144
145 mWeeklyButton = new RadioButton(i18n_w_Weekly(), mRuleButtonGroup);
146 mWeeklyButton->setFixedSize(mWeeklyButton->sizeHint());
147 mWeeklyButton->setReadOnly(mReadOnly);
148 TQWhatsThis::add(mWeeklyButton,
149 i18n("Repeat the alarm at weekly intervals"));
150
151 mMonthlyButton = new RadioButton(i18n_m_Monthly(), mRuleButtonGroup);
152 mMonthlyButton->setFixedSize(mMonthlyButton->sizeHint());
153 mMonthlyButton->setReadOnly(mReadOnly);
154 TQWhatsThis::add(mMonthlyButton,
155 i18n("Repeat the alarm at monthly intervals"));
156
157 mYearlyButton = new RadioButton(i18n_y_Yearly(), mRuleButtonGroup);
158 mYearlyButton->setFixedSize(mYearlyButton->sizeHint());
159 mYearlyButton->setReadOnly(mReadOnly);
160 TQWhatsThis::add(mYearlyButton,
161 i18n("Repeat the alarm at annual intervals"));
162
163 mNoneButtonId = mRuleButtonGroup->id(mNoneButton);
164 mAtLoginButtonId = mRuleButtonGroup->id(mAtLoginButton);
165 mSubDailyButtonId = mRuleButtonGroup->id(mSubDailyButton);
166 mDailyButtonId = mRuleButtonGroup->id(mDailyButton);
167 mWeeklyButtonId = mRuleButtonGroup->id(mWeeklyButton);
168 mMonthlyButtonId = mRuleButtonGroup->id(mMonthlyButton);
169 mYearlyButtonId = mRuleButtonGroup->id(mYearlyButton);
170
171 // Sub-repetition button
172 mSubRepetition = new RepetitionButton(i18n("Sub-Repetition"), true, ruleFrame);
173 mSubRepetition->setFixedSize(mSubRepetition->sizeHint());
174 mSubRepetition->setReadOnly(mReadOnly);
175 connect(mSubRepetition, TQ_SIGNAL(needsInitialisation()), TQ_SIGNAL(repeatNeedsInitialisation()));
176 connect(mSubRepetition, TQ_SIGNAL(changed()), TQ_SIGNAL(frequencyChanged()));
177 TQWhatsThis::add(mSubRepetition, i18n("Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due."));
178 lay->addSpacing(KDialog::spacingHint());
179 lay->addWidget(mSubRepetition);
180
181 lay = new TQVBoxLayout(layout);
182
183 lay->addStretch();
184 layout = new TQHBoxLayout(lay);
185
186 layout->addSpacing(KDialog::marginHint());
187 TQFrame* divider = new TQFrame(ruleFrame);
188 divider->setFrameStyle(TQFrame::VLine | TQFrame::Sunken);
189 layout->addWidget(divider);
190 layout->addSpacing(KDialog::marginHint());
191
192 mNoRule = new NoRule(ruleFrame, "noFrame");
193 mSubDailyRule = new SubDailyRule(mReadOnly, ruleFrame, "subdayFrame");
194 mDailyRule = new DailyRule(mReadOnly, ruleFrame, "dayFrame");
195 mWeeklyRule = new WeeklyRule(mReadOnly, ruleFrame, "weekFrame");
196 mMonthlyRule = new MonthlyRule(mReadOnly, ruleFrame, "monthFrame");
197 mYearlyRule = new YearlyRule(mReadOnly, ruleFrame, "yearFrame");
198
199 connect(mSubDailyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
200 connect(mDailyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
201 connect(mWeeklyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
202 connect(mMonthlyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
203 connect(mYearlyRule, TQ_SIGNAL(frequencyChanged()), this, TQ_SIGNAL(frequencyChanged()));
204
205 mRuleStack = new TQWidgetStack(ruleFrame);
206 layout->addWidget(mRuleStack);
207 layout->addStretch(1);
208 mRuleStack->addWidget(mNoRule, 0);
209 mRuleStack->addWidget(mSubDailyRule, 1);
210 mRuleStack->addWidget(mDailyRule, 2);
211 mRuleStack->addWidget(mWeeklyRule, 3);
212 mRuleStack->addWidget(mMonthlyRule, 4);
213 mRuleStack->addWidget(mYearlyRule, 5);
214 layout->addSpacing(KDialog::marginHint());
215
216 // Create the recurrence range group which contains the controls
217 // which specify how long the recurrence is to last.
218
219 mRangeButtonGroup = new ButtonGroup(i18n("Recurrence End"), this, "mRangeButtonGroup");
220 connect(mRangeButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(rangeTypeClicked()));
221 topLayout->addWidget(mRangeButtonGroup);
222
223 TQVBoxLayout* vlayout = new TQVBoxLayout(mRangeButtonGroup, KDialog::marginHint(), KDialog::spacingHint());
224 vlayout->addSpacing(fontMetrics().lineSpacing()/2);
225 mNoEndDateButton = new RadioButton(i18n("No &end"), mRangeButtonGroup);
226 mNoEndDateButton->setFixedSize(mNoEndDateButton->sizeHint());
227 mNoEndDateButton->setReadOnly(mReadOnly);
228 TQWhatsThis::add(mNoEndDateButton, i18n("Repeat the alarm indefinitely"));
229 vlayout->addWidget(mNoEndDateButton, 1, TQt::AlignAuto);
230 TQSize size = mNoEndDateButton->size();
231
232 layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
233 mRepeatCountButton = new RadioButton(i18n("End a&fter:"), mRangeButtonGroup);
234 mRepeatCountButton->setReadOnly(mReadOnly);
235 TQWhatsThis::add(mRepeatCountButton,
236 i18n("Repeat the alarm for the number of times specified"));
237 mRepeatCountEntry = new SpinBox(1, 9999, 1, mRangeButtonGroup);
238 mRepeatCountEntry->setFixedSize(mRepeatCountEntry->sizeHint());
239 mRepeatCountEntry->setLineShiftStep(10);
240 mRepeatCountEntry->setSelectOnStep(false);
241 mRepeatCountEntry->setReadOnly(mReadOnly);
242 connect(mRepeatCountEntry, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(repeatCountChanged(int)));
243 TQWhatsThis::add(mRepeatCountEntry,
244 i18n("Enter the total number of times to trigger the alarm"));
245 mRepeatCountButton->setFocusWidget(mRepeatCountEntry);
246 mRepeatCountLabel = new TQLabel(i18n("occurrence(s)"), mRangeButtonGroup);
247 mRepeatCountLabel->setFixedSize(mRepeatCountLabel->sizeHint());
248 layout->addWidget(mRepeatCountButton);
249 layout->addSpacing(KDialog::spacingHint());
250 layout->addWidget(mRepeatCountEntry);
251 layout->addWidget(mRepeatCountLabel);
252 layout->addStretch();
253 size = size.expandedTo(mRepeatCountButton->sizeHint());
254
255 layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
256 mEndDateButton = new RadioButton(i18n("End &by:"), mRangeButtonGroup);
257 mEndDateButton->setReadOnly(mReadOnly);
258 TQWhatsThis::add(mEndDateButton,
259 i18n("Repeat the alarm until the date/time specified.\n\n"
260 "Note: This applies to the main recurrence only. It does not limit any sub-repetition which will occur regardless after the last main recurrence."));
261 mEndDateEdit = new DateEdit(mRangeButtonGroup);
262 mEndDateEdit->setFixedSize(mEndDateEdit->sizeHint());
263 mEndDateEdit->setReadOnly(mReadOnly);
264 TQWhatsThis::add(mEndDateEdit,
265 i18n("Enter the last date to repeat the alarm"));
266 mEndDateButton->setFocusWidget(mEndDateEdit);
267 mEndTimeEdit = new TimeEdit(mRangeButtonGroup);
268 mEndTimeEdit->setFixedSize(mEndTimeEdit->sizeHint());
269 mEndTimeEdit->setReadOnly(mReadOnly);
270 static const TQString lastTimeText = i18n("Enter the last time to repeat the alarm.");
271 TQWhatsThis::add(mEndTimeEdit, TQString("%1\n\n%2").arg(lastTimeText).arg(TimeSpinBox::shiftWhatsThis()));
272 mEndAnyTimeCheckBox = new CheckBox(i18n("Any time"), mRangeButtonGroup);
273 mEndAnyTimeCheckBox->setFixedSize(mEndAnyTimeCheckBox->sizeHint());
274 mEndAnyTimeCheckBox->setReadOnly(mReadOnly);
275 connect(mEndAnyTimeCheckBox, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotAnyTimeToggled(bool)));
276 TQWhatsThis::add(mEndAnyTimeCheckBox,
277 i18n("Stop repeating the alarm after your first login on or after the specified end date"));
278 layout->addWidget(mEndDateButton);
279 layout->addSpacing(KDialog::spacingHint());
280 layout->addWidget(mEndDateEdit);
281 layout->addWidget(mEndTimeEdit);
282 layout->addWidget(mEndAnyTimeCheckBox);
283 layout->addStretch();
284 size = size.expandedTo(mEndDateButton->sizeHint());
285
286 // Line up the widgets to the right of the radio buttons
287 mRepeatCountButton->setFixedSize(size);
288 mEndDateButton->setFixedSize(size);
289
290 // Create the exceptions group which specifies dates to be excluded
291 // from the recurrence.
292
293 mExceptionGroup = new TQGroupBox(i18n("E&xceptions"), this, "mExceptionGroup");
294 topLayout->addWidget(mExceptionGroup);
295 topLayout->setStretchFactor(mExceptionGroup, 2);
296 vlayout = new TQVBoxLayout(mExceptionGroup, KDialog::marginHint(), KDialog::spacingHint());
297 vlayout->addSpacing(fontMetrics().lineSpacing()/2);
298 layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
299 vlayout = new TQVBoxLayout(layout);
300
301 mExceptionDateList = new TQListBox(mExceptionGroup);
302 mExceptionDateList->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding));
303 connect(mExceptionDateList, TQ_SIGNAL(selectionChanged()), TQ_SLOT(enableExceptionButtons()));
304 TQWhatsThis::add(mExceptionDateList,
305 i18n("The list of exceptions, i.e. dates/times excluded from the recurrence"));
306 vlayout->addWidget(mExceptionDateList);
307
308 if (mReadOnly)
309 {
310 mExceptionDateEdit = 0;
311 mChangeExceptionButton = 0;
312 mDeleteExceptionButton = 0;
313 }
314 else
315 {
316 vlayout = new TQVBoxLayout(layout);
317 mExceptionDateEdit = new DateEdit(mExceptionGroup);
318 mExceptionDateEdit->setFixedSize(mExceptionDateEdit->sizeHint());
319 mExceptionDateEdit->setDate(TQDate::currentDate());
320 TQWhatsThis::add(mExceptionDateEdit,
321 i18n("Enter a date to insert in the exceptions list. "
322 "Use in conjunction with the Add or Change button below."));
323 vlayout->addWidget(mExceptionDateEdit);
324
325 layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
326 TQPushButton* button = new TQPushButton(i18n("Add"), mExceptionGroup);
327 button->setFixedSize(button->sizeHint());
328 connect(button, TQ_SIGNAL(clicked()), TQ_SLOT(addException()));
329 TQWhatsThis::add(button,
330 i18n("Add the date entered above to the exceptions list"));
331 layout->addWidget(button);
332
333 mChangeExceptionButton = new TQPushButton(i18n("Change"), mExceptionGroup);
334 mChangeExceptionButton->setFixedSize(mChangeExceptionButton->sizeHint());
335 connect(mChangeExceptionButton, TQ_SIGNAL(clicked()), TQ_SLOT(changeException()));
336 TQWhatsThis::add(mChangeExceptionButton,
337 i18n("Replace the currently highlighted item in the exceptions list with the date entered above"));
338 layout->addWidget(mChangeExceptionButton);
339
340 mDeleteExceptionButton = new TQPushButton(i18n("Delete"), mExceptionGroup);
341 mDeleteExceptionButton->setFixedSize(mDeleteExceptionButton->sizeHint());
342 connect(mDeleteExceptionButton, TQ_SIGNAL(clicked()), TQ_SLOT(deleteException()));
343 TQWhatsThis::add(mDeleteExceptionButton,
344 i18n("Remove the currently highlighted item from the exceptions list"));
345 layout->addWidget(mDeleteExceptionButton);
346 }
347
348 mNoEmitTypeChanged = false;
349}
350
351/******************************************************************************
352 * Verify the consistency of the entered data.
353 * Reply = widget to receive focus on error, or 0 if no error.
354 */
355TQWidget* RecurrenceEdit::checkData(const TQDateTime& startDateTime, TQString& errorMessage) const
356{
357 if (mAtLoginButton->isOn())
358 return 0;
359 const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime;
360 if (mEndDateButton->isChecked())
361 {
362 TQWidget* errWidget = 0;
363 bool noTime = !mEndTimeEdit->isEnabled();
364 TQDate endDate = mEndDateEdit->date();
365 if (endDate < startDateTime.date())
366 errWidget = mEndDateEdit;
367 else if (!noTime && TQDateTime(endDate, mEndTimeEdit->time()) < startDateTime)
368 errWidget = mEndTimeEdit;
369 if (errWidget)
370 {
371 errorMessage = noTime
372 ? i18n("End date is earlier than start date")
373 : i18n("End date/time is earlier than start date/time");
374 return errWidget;
375 }
376 }
377 if (!mRule)
378 return 0;
379 return mRule->validate(errorMessage);
380}
381
382/******************************************************************************
383 * Called when a recurrence period radio button is clicked.
384 */
385void RecurrenceEdit::periodClicked(int id)
386{
387 RepeatType oldType = mRuleButtonType;
388 bool none = (id == mNoneButtonId);
389 bool atLogin = (id == mAtLoginButtonId);
390 bool subdaily = (id == mSubDailyButtonId);
391 if (none)
392 {
393 mRule = 0;
394 mRuleButtonType = NO_RECUR;
395 }
396 else if (atLogin)
397 {
398 mRule = 0;
399 mRuleButtonType = AT_LOGIN;
400 mRangeButtonGroup->setButton(mRangeButtonGroup->id(mEndDateButton));
401 }
402 else if (subdaily)
403 {
404 mRule = mSubDailyRule;
405 mRuleButtonType = SUBDAILY;
406 }
407 else if (id == mDailyButtonId)
408 {
409 mRule = mDailyRule;
410 mRuleButtonType = DAILY;
411 mDailyShown = true;
412 }
413 else if (id == mWeeklyButtonId)
414 {
415 mRule = mWeeklyRule;
416 mRuleButtonType = WEEKLY;
417 mWeeklyShown = true;
418 }
419 else if (id == mMonthlyButtonId)
420 {
421 mRule = mMonthlyRule;
422 mRuleButtonType = MONTHLY;
423 mMonthlyShown = true;
424 }
425 else if (id == mYearlyButtonId)
426 {
427 mRule = mYearlyRule;
428 mRuleButtonType = ANNUAL;
429 mYearlyShown = true;
430 }
431 else
432 return;
433
434 if (mRuleButtonType != oldType)
435 {
436 mRuleStack->raiseWidget(mRule ? mRule : mNoRule);
437 if (oldType == NO_RECUR || none)
438 mRangeButtonGroup->setEnabled(!none);
439 mExceptionGroup->setEnabled(!(none || atLogin));
440 mEndAnyTimeCheckBox->setEnabled(atLogin);
441 if (!none)
442 {
443 mNoEndDateButton->setEnabled(!atLogin);
444 mRepeatCountButton->setEnabled(!atLogin);
445 }
446 rangeTypeClicked();
447 mSubRepetition->setEnabled(!(none || atLogin));
448 if (!mNoEmitTypeChanged)
449 emit typeChanged(mRuleButtonType);
450 }
451}
452
453void RecurrenceEdit::slotAnyTimeToggled(bool on)
454{
455 TQButton* button = mRuleButtonGroup->selected();
456 mEndTimeEdit->setEnabled((button == mAtLoginButton && !on)
457 || (button == mSubDailyButton && mEndDateButton->isChecked()));
458}
459
460/******************************************************************************
461 * Called when a recurrence range type radio button is clicked.
462 */
463void RecurrenceEdit::rangeTypeClicked()
464{
465 bool endDate = mEndDateButton->isOn();
466 mEndDateEdit->setEnabled(endDate);
467 mEndTimeEdit->setEnabled(endDate
468 && ((mAtLoginButton->isOn() && !mEndAnyTimeCheckBox->isChecked())
469 || mSubDailyButton->isOn()));
470 bool repeatCount = mRepeatCountButton->isOn();
471 mRepeatCountEntry->setEnabled(repeatCount);
472 mRepeatCountLabel->setEnabled(repeatCount);
473}
474
475void RecurrenceEdit::showEvent(TQShowEvent*)
476{
477 if (mRule)
478 mRule->setFrequencyFocus();
479 else
480 mRuleButtonGroup->selected()->setFocus();
481 emit shown();
482}
483
484 /******************************************************************************
485* Return the sub-repetition count within the recurrence, i.e. the number of
486* repetitions after the main recurrence.
487*/
488int RecurrenceEdit::subRepeatCount(int* subRepeatInterval) const
489{
490 int count = (mRuleButtonType >= SUBDAILY) ? mSubRepetition->count() : 0;
491 if (subRepeatInterval)
492 *subRepeatInterval = count ? mSubRepetition->interval() : 0;
493 return count;
494}
495
496/******************************************************************************
497* Called when the Sub-Repetition button has been pressed to display the
498* sub-repetition dialog.
499* Alarm repetition has the following restrictions:
500* 1) Not allowed for a repeat-at-login alarm
501* 2) For a date-only alarm, the repeat interval must be a whole number of days.
502* 3) The overall repeat duration must be less than the recurrence interval.
503*/
504void RecurrenceEdit::setSubRepetition(int reminderMinutes, bool dateOnly)
505{
506 int maxDuration;
507 switch (mRuleButtonType)
508 {
509 case RecurrenceEdit::NO_RECUR:
510 case RecurrenceEdit::AT_LOGIN: // alarm repeat not allowed
511 maxDuration = 0;
512 break;
513 default: // repeat duration must be less than recurrence interval
514 {
515 KAEvent event;
516 updateEvent(event, false);
517 maxDuration = event.longestRecurrenceInterval() - reminderMinutes - 1;
518 break;
519 }
520 }
521 mSubRepetition->initialise(mSubRepetition->interval(), mSubRepetition->count(), dateOnly, maxDuration);
522 mSubRepetition->setEnabled(mRuleButtonType >= SUBDAILY && maxDuration);
523}
524
525/******************************************************************************
526* Activate the sub-repetition dialog.
527*/
528void RecurrenceEdit::activateSubRepetition()
529{
530 mSubRepetition->activate();
531}
532
533/******************************************************************************
534* For weekly, monthly and yearly recurrence, checks that the specified date
535* matches the days allowed. For the other recurrence simply return true.
536*/
537bool RecurrenceEdit::validateDate(const DateTime &date) const
538{
539 if (mRuleButtonType == RecurrenceEdit::DAILY)
540 {
541 TQBitArray selectedDays = mDailyRule->days();
542 if (!selectedDays[date.date().dayOfWeek()-1])
543 return false;
544 }
545 else if (mRuleButtonType == RecurrenceEdit::WEEKLY)
546 {
547 TQBitArray selectedDays = mWeeklyRule->days();
548 if (!selectedDays[date.date().dayOfWeek()-1])
549 return false;
550 }
551 else if (mRuleButtonType == RecurrenceEdit::MONTHLY)
552 {
553 if (mMonthlyRule->type() == MonthYearRule::DATE)
554 {
555 // on the nth day of the month
556 int comboDate = mMonthlyRule->date();
557 if ((comboDate > 0 && date.date().day() != comboDate) ||
558 (comboDate <=0 && date.date().day() != date.date().daysInMonth()))
559 return false;
560 }
561 else
562 {
563 // on the nth weekday (i.e. Monday) of the month
564 if (date.date().dayOfWeek() != mMonthlyRule->dayOfWeek())
565 return false;
566
567 int monthDay = date.date().day();
568 int weekNum = mMonthlyRule->week();
569 int minDay = 0, maxDay = 0;
570 if (weekNum > 0 )
571 {
572 minDay = (weekNum-1) * 7;
573 maxDay = weekNum * 7;
574 }
575 else if (weekNum < 0)
576 {
577 int dim = date.date().daysInMonth();
578 minDay = dim + weekNum * 7;
579 maxDay = dim + (weekNum+1) * 7;
580 }
581 if (monthDay <= minDay || monthDay > maxDay)
582 return false;
583 }
584 }
585 else if (mRuleButtonType == RecurrenceEdit::ANNUAL)
586 {
587 TQValueList<int> months = mYearlyRule->months();
588 if (!months.contains(date.date().month()))
589 return false;
590
591 if (mYearlyRule->type() == MonthYearRule::DATE)
592 {
593 // on the nth day of the month
594 int comboDate = mYearlyRule->date();
595 if ((comboDate > 0 && date.date().day() != comboDate) ||
596 (comboDate <=0 && date.date().day() != date.date().daysInMonth()))
597 return false;
598 }
599 else
600 {
601 // on the nth weekday (i.e. Monday) of the month
602 if (date.date().dayOfWeek() != mYearlyRule->dayOfWeek())
603 return false;
604
605 int monthDay = date.date().day();
606 int weekNum = mYearlyRule->week();
607 int minDay = 0, maxDay = 0;
608 if (weekNum > 0 )
609 {
610 minDay = (weekNum-1) * 7;
611 maxDay = weekNum * 7;
612 }
613 else if (weekNum < 0)
614 {
615 int dim = date.date().daysInMonth();
616 minDay = dim + weekNum * 7;
617 maxDay = dim + (weekNum+1) * 7;
618 }
619 if (monthDay <= minDay || monthDay > maxDay)
620 return false;
621 }
622 }
623 return true;
624}
625
626/******************************************************************************
627 * Called when the value of the repeat count field changes, to reset the
628 * minimum value to 1 if the value was 0.
629 */
630void RecurrenceEdit::repeatCountChanged(int value)
631{
632 if (value > 0 && mRepeatCountEntry->minValue() == 0)
633 mRepeatCountEntry->setMinValue(1);
634}
635
636/******************************************************************************
637 * Add the date entered in the exception date edit control to the list of
638 * exception dates.
639 */
640void RecurrenceEdit::addException()
641{
642 if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
643 return;
644 TQDate date = mExceptionDateEdit->date();
645 TQValueList<TQDate>::Iterator it;
646 int index = 0;
647 bool insert = true;
648 for (it = mExceptionDates.begin(); it != mExceptionDates.end(); ++index, ++it)
649 {
650 if (date <= *it)
651 {
652 insert = (date != *it);
653 break;
654 }
655 }
656 if (insert)
657 {
658 mExceptionDates.insert(it, date);
659 mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(date), index);
660 }
661 mExceptionDateList->setCurrentItem(index);
662 enableExceptionButtons();
663}
664
665/******************************************************************************
666 * Change the currently highlighted exception date to that entered in the
667 * exception date edit control.
668 */
669void RecurrenceEdit::changeException()
670{
671 if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
672 return;
673 int index = mExceptionDateList->currentItem();
674 if (index >= 0 && mExceptionDateList->isSelected(index))
675 {
676 TQDate olddate = mExceptionDates[index];
677 TQDate newdate = mExceptionDateEdit->date();
678 if (newdate != olddate)
679 {
680 mExceptionDates.remove(mExceptionDates.at(index));
681 mExceptionDateList->removeItem(index);
682 addException();
683 }
684 }
685}
686
687/******************************************************************************
688 * Delete the currently highlighted exception date.
689 */
690void RecurrenceEdit::deleteException()
691{
692 int index = mExceptionDateList->currentItem();
693 if (index >= 0 && mExceptionDateList->isSelected(index))
694 {
695 mExceptionDates.remove(mExceptionDates.at(index));
696 mExceptionDateList->removeItem(index);
697 enableExceptionButtons();
698 }
699}
700
701/******************************************************************************
702 * Enable/disable the exception group buttons according to whether any item is
703 * selected in the exceptions listbox.
704 */
705void RecurrenceEdit::enableExceptionButtons()
706{
707 int index = mExceptionDateList->currentItem();
708 bool enable = (index >= 0 && mExceptionDateList->isSelected(index));
709 if (mDeleteExceptionButton)
710 mDeleteExceptionButton->setEnabled(enable);
711 if (mChangeExceptionButton)
712 mChangeExceptionButton->setEnabled(enable);
713
714 // Prevent the exceptions list box receiving keyboard focus is it's empty
715 mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? TQWidget::WheelFocus : TQWidget::NoFocus);
716}
717
718/******************************************************************************
719 * Notify this instance of a change in the alarm start date.
720 */
721void RecurrenceEdit::setStartDate(const TQDate& start, const TQDate& today)
722{
723 if (!mReadOnly)
724 {
725 setRuleDefaults(start);
726 if (start < today)
727 {
728 mEndDateEdit->setMinDate(today);
729 if (mExceptionDateEdit)
730 mExceptionDateEdit->setMinDate(today);
731 }
732 else
733 {
734 const TQString startString = i18n("Date cannot be earlier than start date", "start date");
735 mEndDateEdit->setMinDate(start, startString);
736 if (mExceptionDateEdit)
737 mExceptionDateEdit->setMinDate(start, startString);
738 }
739 }
740}
741
742/******************************************************************************
743 * Specify the default recurrence end date.
744 */
745void RecurrenceEdit::setDefaultEndDate(const TQDate& end)
746{
747 if (!mEndDateButton->isOn())
748 mEndDateEdit->setDate(end);
749}
750
751void RecurrenceEdit::setEndDateTime(const DateTime& end)
752{
753 mEndDateEdit->setDate(end.date());
754 mEndTimeEdit->setValue(end.time());
755 mEndTimeEdit->setEnabled(!end.isDateOnly());
756 mEndAnyTimeCheckBox->setChecked(end.isDateOnly());
757}
758
759DateTime RecurrenceEdit::endDateTime() const
760{
761 if (mRuleButtonGroup->selected() == mAtLoginButton && mEndAnyTimeCheckBox->isChecked())
762 return DateTime(mEndDateEdit->date());
763 return DateTime(mEndDateEdit->date(), mEndTimeEdit->time());
764}
765
766/******************************************************************************
767 * Set all controls to their default values.
768 */
769void RecurrenceEdit::setDefaults(const TQDateTime& from)
770{
771 mCurrStartDateTime = from;
772 TQDate fromDate = from.date();
773 mNoEndDateButton->setChecked(true);
774
775 mSubDailyRule->setFrequency(1);
776 mDailyRule->setFrequency(1);
777 mWeeklyRule->setFrequency(1);
778 mMonthlyRule->setFrequency(1);
779 mYearlyRule->setFrequency(1);
780
781 setRuleDefaults(fromDate);
782 mMonthlyRule->setType(MonthYearRule::DATE); // date in month
783 mYearlyRule->setType(MonthYearRule::DATE); // date in year
784
785 mEndDateEdit->setDate(fromDate);
786
787 mNoEmitTypeChanged = true;
788 int button;
789 switch (Preferences::defaultRecurPeriod())
790 {
791 case AT_LOGIN: button = mAtLoginButtonId; break;
792 case ANNUAL: button = mYearlyButtonId; break;
793 case MONTHLY: button = mMonthlyButtonId; break;
794 case WEEKLY: button = mWeeklyButtonId; break;
795 case DAILY: button = mDailyButtonId; break;
796 case SUBDAILY: button = mSubDailyButtonId; break;
797 case NO_RECUR:
798 default: button = mNoneButtonId; break;
799 }
800 mRuleButtonGroup->setButton(button);
801 mNoEmitTypeChanged = false;
802 rangeTypeClicked();
803 enableExceptionButtons();
804
805 saveState();
806}
807
808/******************************************************************************
809 * Set the controls for weekly, monthly and yearly rules which have not so far
810 * been shown, to their default values, depending on the recurrence start date.
811 */
812void RecurrenceEdit::setRuleDefaults(const TQDate& fromDate)
813{
814 int day = fromDate.day();
815 int dayOfWeek = fromDate.dayOfWeek();
816 int month = fromDate.month();
817 if (!mDailyShown)
818 mDailyRule->setDays(true);
819 if (!mWeeklyShown)
820 mWeeklyRule->setDay(dayOfWeek);
821 if (!mMonthlyShown)
822 mMonthlyRule->setDefaultValues(day, dayOfWeek);
823 if (!mYearlyShown)
824 mYearlyRule->setDefaultValues(day, dayOfWeek, month);
825}
826
827/******************************************************************************
828* Set the state of all controls to reflect the data in the specified event.
829* Set 'keepDuration' true to prevent the recurrence count being adjusted to the
830* remaining number of recurrences.
831*/
832void RecurrenceEdit::set(const KAEvent& event, bool keepDuration)
833{
834 setDefaults(event.mainDateTime().dateTime());
835 if (event.repeatAtLogin())
836 {
837 mRuleButtonGroup->setButton(mAtLoginButtonId);
838 mEndDateButton->setChecked(true);
839 return;
840 }
841 mRuleButtonGroup->setButton(mNoneButtonId);
842 KARecurrence* recurrence = event.recurrence();
843 if (!recurrence)
844 return;
845 KARecurrence::Type rtype = recurrence->type();
846 switch (rtype)
847 {
848 case KARecurrence::MINUTELY:
849 mRuleButtonGroup->setButton(mSubDailyButtonId);
850 break;
851
852 case KARecurrence::DAILY:
853 {
854 mRuleButtonGroup->setButton(mDailyButtonId);
855 TQBitArray rDays = recurrence->days();
856 bool set = false;
857 for (int i = 0; i < 7 && !set; ++i)
858 set = rDays.testBit(i);
859 if (set)
860 mDailyRule->setDays(rDays);
861 else
862 mDailyRule->setDays(true);
863 break;
864 }
865 case KARecurrence::WEEKLY:
866 {
867 mRuleButtonGroup->setButton(mWeeklyButtonId);
868 TQBitArray rDays = recurrence->days();
869 mWeeklyRule->setDays(rDays);
870 break;
871 }
872 case KARecurrence::MONTHLY_POS: // on nth (Tuesday) of the month
873 {
874 TQValueList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions();
875 int i = posns.first().pos();
876 if (!i)
877 {
878 // It's every (Tuesday) of the month. Convert to a weekly recurrence
879 // (but ignoring any non-every xxxDay positions).
880 mRuleButtonGroup->setButton(mWeeklyButtonId);
881 mWeeklyRule->setFrequency(recurrence->frequency());
882 TQBitArray rDays(7);
883 for (TQValueList<RecurrenceRule::WDayPos>::ConstIterator it = posns.begin(); it != posns.end(); ++it)
884 {
885 if (!(*it).pos())
886 rDays.setBit((*it).day() - 1, 1);
887 }
888 mWeeklyRule->setDays(rDays);
889 break;
890 }
891 mRuleButtonGroup->setButton(mMonthlyButtonId);
892 mMonthlyRule->setPosition(i, posns.first().day());
893 break;
894 }
895 case KARecurrence::MONTHLY_DAY: // on nth day of the month
896 {
897 mRuleButtonGroup->setButton(mMonthlyButtonId);
898 TQValueList<int> rmd = recurrence->monthDays();
899 int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
900 mMonthlyRule->setDate(day);
901 break;
902 }
903 case KARecurrence::ANNUAL_DATE: // on the nth day of (months...) in the year
904 case KARecurrence::ANNUAL_POS: // on the nth (Tuesday) of (months...) in the year
905 {
906 if (rtype == KARecurrence::ANNUAL_DATE)
907 {
908 mRuleButtonGroup->setButton(mYearlyButtonId);
909 const TQValueList<int> rmd = recurrence->monthDays();
910 int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
911 mYearlyRule->setDate(day);
912 mYearlyRule->setFeb29Type(recurrence->feb29Type());
913 }
914 else if (rtype == KARecurrence::ANNUAL_POS)
915 {
916 mRuleButtonGroup->setButton(mYearlyButtonId);
917 TQValueList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions();
918 mYearlyRule->setPosition(posns.first().pos(), posns.first().day());
919 }
920 mYearlyRule->setMonths(recurrence->yearMonths());
921 break;
922 }
923 default:
924 return;
925 }
926
927 mRule->setFrequency(recurrence->frequency());
928
929 // Get range information
930 TQDateTime endtime = mCurrStartDateTime;
931 int duration = recurrence->duration();
932 if (duration == -1)
933 mNoEndDateButton->setChecked(true);
934 else if (duration)
935 {
936 mRepeatCountButton->setChecked(true);
937 mRepeatCountEntry->setValue(duration);
938 }
939 else
940 {
941 mEndDateButton->setChecked(true);
942 endtime = recurrence->endDateTime();
943 mEndTimeEdit->setValue(endtime.time());
944 }
945 mEndDateEdit->setDate(endtime.date());
946
947 // Get exception information
948 mExceptionDates = event.recurrence()->exDates();
949 qHeapSort(mExceptionDates);
950 mExceptionDateList->clear();
951 for (DateList::ConstIterator it = mExceptionDates.begin(); it != mExceptionDates.end(); ++it)
952 mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(*it));
953 enableExceptionButtons();
954
955 // Get repetition within recurrence
956 mSubRepetition->set(event.repeatInterval(), event.repeatCount());
957
958 rangeTypeClicked();
959
960 saveState();
961}
962
963/******************************************************************************
964 * Update the specified KAEvent with the entered recurrence data.
965 * If 'adjustStart' is true, the start date/time will be adjusted if necessary
966 * to be the first date/time which recurs on or after the original start.
967 */
968void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart)
969{
970 // Get end date and repeat count, common to all types of recurring events
971 TQDate endDate;
972 TQTime endTime;
973 int repeatCount;
974 if (mNoEndDateButton->isChecked())
975 repeatCount = -1;
976 else if (mRepeatCountButton->isChecked())
977 repeatCount = mRepeatCountEntry->value();
978 else
979 {
980 repeatCount = 0;
981 endDate = mEndDateEdit->date();
982 endTime = mEndTimeEdit->time();
983 }
984
985 // Set up the recurrence according to the type selected
986 TQButton* button = mRuleButtonGroup->selected();
987 event.setRepeatAtLogin(button == mAtLoginButton);
988 int frequency = mRule ? mRule->frequency() : 0;
989 if (button == mSubDailyButton)
990 {
991 TQDateTime endDateTime(endDate, endTime);
992 event.setRecurMinutely(frequency, repeatCount, endDateTime);
993 }
994 else if (button == mDailyButton)
995 {
996 event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate);
997 }
998 else if (button == mWeeklyButton)
999 {
1000 event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate);
1001 }
1002 else if (button == mMonthlyButton)
1003 {
1004 if (mMonthlyRule->type() == MonthlyRule::POS)
1005 {
1006 // It's by position
1007 KAEvent::MonthPos pos;
1008 pos.days.fill(false);
1009 pos.days.setBit(mMonthlyRule->dayOfWeek() - 1);
1010 pos.weeknum = mMonthlyRule->week();
1011 TQValueList<KAEvent::MonthPos> poses;
1012 poses.append(pos);
1013 event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate);
1014 }
1015 else
1016 {
1017 // It's by day
1018 int daynum = mMonthlyRule->date();
1019 TQValueList<int> daynums;
1020 daynums.append(daynum);
1021 event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
1022 }
1023 }
1024 else if (button == mYearlyButton)
1025 {
1026 TQValueList<int> months = mYearlyRule->months();
1027 if (mYearlyRule->type() == YearlyRule::POS)
1028 {
1029 // It's by position
1030 KAEvent::MonthPos pos;
1031 pos.days.fill(false);
1032 pos.days.setBit(mYearlyRule->dayOfWeek() - 1);
1033 pos.weeknum = mYearlyRule->week();
1034 TQValueList<KAEvent::MonthPos> poses;
1035 poses.append(pos);
1036 event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate);
1037 }
1038 else
1039 {
1040 // It's by date in month
1041 event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(),
1042 mYearlyRule->feb29Type(), repeatCount, endDate);
1043 }
1044 }
1045 else
1046 {
1047 event.setNoRecur();
1048 return;
1049 }
1050 if (!event.recurs())
1051 return; // an error occurred setting up the recurrence
1052 if (adjustStart)
1053 event.setFirstRecurrence();
1054
1055 // Set up repetition within the recurrence.
1056 // N.B. This requires the main recurrence to be set up first.
1057 int count = mSubRepetition->count();
1058 if (mRuleButtonType < SUBDAILY)
1059 count = 0;
1060 event.setRepetition(mSubRepetition->interval(), count);
1061
1062 // Set up exceptions
1063 event.recurrence()->setExDates(mExceptionDates);
1064
1065 event.setUpdated();
1066}
1067
1068/******************************************************************************
1069 * Save the state of all controls.
1070 */
1071void RecurrenceEdit::saveState()
1072{
1073 mSavedRuleButton = mRuleButtonGroup->selected();
1074 if (mRule)
1075 mRule->saveState();
1076 mSavedRangeButton = mRangeButtonGroup->selected();
1077 if (mSavedRangeButton == mRepeatCountButton)
1078 mSavedRecurCount = mRepeatCountEntry->value();
1079 else if (mSavedRangeButton == mEndDateButton)
1080 mSavedEndDateTime.set(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked());
1081 mSavedExceptionDates = mExceptionDates;
1082 mSavedRepeatInterval = mSubRepetition->interval();
1083 mSavedRepeatCount = mSubRepetition->count();
1084}
1085
1086/******************************************************************************
1087 * Check whether any of the controls have changed state since initialisation.
1088 */
1089bool RecurrenceEdit::stateChanged() const
1090{
1091 if (mSavedRuleButton != mRuleButtonGroup->selected()
1092 || mSavedRangeButton != mRangeButtonGroup->selected()
1093 || (mRule && mRule->stateChanged()))
1094 return true;
1095 if (mSavedRangeButton == mRepeatCountButton
1096 && mSavedRecurCount != mRepeatCountEntry->value())
1097 return true;
1098 if (mSavedRangeButton == mEndDateButton
1099 && mSavedEndDateTime != DateTime(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked()))
1100 return true;
1101 if (mSavedExceptionDates != mExceptionDates
1102 || mSavedRepeatInterval != mSubRepetition->interval()
1103 || mSavedRepeatCount != mSubRepetition->count())
1104 return true;
1105 return false;
1106}
1107
1108
1109
1110/*=============================================================================
1111= Class Rule
1112= Base class for rule widgets, including recurrence frequency.
1113=============================================================================*/
1114
1115Rule::Rule(const TQString& freqText, const TQString& freqWhatsThis, bool time, bool readOnly, TQWidget* parent, const char* name)
1116 : NoRule(parent, name)
1117{
1118 mLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
1119 TQHBox* freqBox = new TQHBox(this);
1120 mLayout->addWidget(freqBox);
1121 TQHBox* box = new TQHBox(freqBox); // this is to control the TQWhatsThis text display area
1122 box->setSpacing(KDialog::spacingHint());
1123
1124 TQLabel* label = new TQLabel(i18n("Recur e&very"), box);
1125 label->setFixedSize(label->sizeHint());
1126 if (time)
1127 {
1128 mIntSpinBox = 0;
1129 mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
1130 mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
1131 mTimeSpinBox->setReadOnly(readOnly);
1132 }
1133 else
1134 {
1135 mTimeSpinBox = 0;
1136 mSpinBox = mIntSpinBox = new SpinBox(1, 999, 1, box);
1137 mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
1138 mIntSpinBox->setReadOnly(readOnly);
1139 }
1140 connect(mSpinBox, TQ_SIGNAL(valueChanged(int)), TQ_SIGNAL(frequencyChanged()));
1141 label->setBuddy(mSpinBox);
1142 label = new TQLabel(freqText, box);
1143 label->setFixedSize(label->sizeHint());
1144 box->setFixedSize(sizeHint());
1145 TQWhatsThis::add(box, freqWhatsThis);
1146
1147 new TQWidget(freqBox); // left adjust the visible widgets
1148 freqBox->setFixedHeight(freqBox->sizeHint().height());
1149 freqBox->setFocusProxy(mSpinBox);
1150}
1151
1152int Rule::frequency() const
1153{
1154 if (mIntSpinBox)
1155 return mIntSpinBox->value();
1156 if (mTimeSpinBox)
1157 return mTimeSpinBox->value();
1158 return 0;
1159}
1160
1161void Rule::setFrequency(int n)
1162{
1163 if (mIntSpinBox)
1164 mIntSpinBox->setValue(n);
1165 if (mTimeSpinBox)
1166 mTimeSpinBox->setValue(n);
1167}
1168
1169/******************************************************************************
1170 * Save the state of all controls.
1171 */
1172void Rule::saveState()
1173{
1174 mSavedFrequency = frequency();
1175}
1176
1177/******************************************************************************
1178 * Check whether any of the controls have changed state since initialisation.
1179 */
1180bool Rule::stateChanged() const
1181{
1182 return (mSavedFrequency != frequency());
1183}
1184
1185
1186/*=============================================================================
1187= Class SubDailyRule
1188= Sub-daily rule widget.
1189=============================================================================*/
1190
1191SubDailyRule::SubDailyRule(bool readOnly, TQWidget* parent, const char* name)
1192 : Rule(i18n("hours:minutes"),
1193 i18n("Enter the number of hours and minutes between repetitions of the alarm"),
1194 true, readOnly, parent, name)
1195{ }
1196
1197
1198/*=============================================================================
1199= Class DayWeekRule
1200= Daily/weekly rule widget base class.
1201=============================================================================*/
1202
1203DayWeekRule::DayWeekRule(const TQString& freqText, const TQString& freqWhatsThis, const TQString& daysWhatsThis,
1204 bool readOnly, TQWidget* parent, const char* name)
1205 : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
1206 mSavedDays(7)
1207{
1208 TQGridLayout* grid = new TQGridLayout(layout(), 1, 4, KDialog::spacingHint());
1209 grid->setRowStretch(0, 1);
1210
1211 TQLabel* label = new TQLabel(i18n("On: Tuesday", "O&n:"), this);
1212 label->setFixedSize(label->sizeHint());
1213 grid->addWidget(label, 0, 0, TQt::AlignRight | TQt::AlignTop);
1214 grid->addColSpacing(1, KDialog::spacingHint());
1215
1216 // List the days of the week starting at the user's start day of the week.
1217 // Save the first day of the week, just in case it changes while the dialog is open.
1218 TQWidget* box = new TQWidget(this); // this is to control the TQWhatsThis text display area
1219 TQGridLayout* dgrid = new TQGridLayout(box, 4, 2, 0, KDialog::spacingHint());
1220 const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1221 for (int i = 0; i < 7; ++i)
1222 {
1223 int day = KAlarm::localeDayInWeek_to_weekDay(i);
1224 mDayBox[i] = new CheckBox(calendar->weekDayName(day), box);
1225 mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
1226 mDayBox[i]->setReadOnly(readOnly);
1227 dgrid->addWidget(mDayBox[i], i%4, i/4, TQt::AlignAuto);
1228 }
1229 box->setFixedSize(box->sizeHint());
1230 TQWhatsThis::add(box, daysWhatsThis);
1231 grid->addWidget(box, 0, 2, TQt::AlignAuto);
1232 label->setBuddy(mDayBox[0]);
1233 grid->setColStretch(3, 1);
1234}
1235
1236/******************************************************************************
1237 * Fetch which days of the week have been ticked.
1238 */
1239TQBitArray DayWeekRule::days() const
1240{
1241 TQBitArray ds(7);
1242 ds.fill(false);
1243 for (int i = 0; i < 7; ++i)
1244 if (mDayBox[i]->isChecked())
1245 ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
1246 return ds;
1247}
1248
1249/******************************************************************************
1250 * Tick/untick every day of the week.
1251 */
1252void DayWeekRule::setDays(bool tick)
1253{
1254 for (int i = 0; i < 7; ++i)
1255 mDayBox[i]->setChecked(tick);
1256}
1257
1258/******************************************************************************
1259 * Tick/untick each day of the week according to the specified bits.
1260 */
1261void DayWeekRule::setDays(const TQBitArray& days)
1262{
1263 for (int i = 0; i < 7; ++i)
1264 {
1265 bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
1266 mDayBox[i]->setChecked(x);
1267 }
1268}
1269
1270/******************************************************************************
1271 * Tick the specified day of the week, and untick all other days.
1272 */
1273void DayWeekRule::setDay(int dayOfWeek)
1274{
1275 for (int i = 0; i < 7; ++i)
1276 mDayBox[i]->setChecked(false);
1277 if (dayOfWeek > 0 && dayOfWeek <= 7)
1278 mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
1279}
1280
1281/******************************************************************************
1282 * Validate: check that at least one day is selected.
1283 */
1284TQWidget* DayWeekRule::validate(TQString& errorMessage)
1285{
1286 for (int i = 0; i < 7; ++i)
1287 if (mDayBox[i]->isChecked())
1288 return 0;
1289 errorMessage = i18n("No day selected");
1290 return mDayBox[0];
1291}
1292
1293/******************************************************************************
1294 * Save the state of all controls.
1295 */
1296void DayWeekRule::saveState()
1297{
1298 Rule::saveState();
1299 mSavedDays = days();
1300}
1301
1302/******************************************************************************
1303 * Check whether any of the controls have changed state since initialisation.
1304 */
1305bool DayWeekRule::stateChanged() const
1306{
1307 return (Rule::stateChanged()
1308 || mSavedDays != days());
1309}
1310
1311
1312/*=============================================================================
1313= Class DailyRule
1314= Daily rule widget.
1315=============================================================================*/
1316
1317DailyRule::DailyRule(bool readOnly, TQWidget* parent, const char* name)
1318 : DayWeekRule(i18n("day(s)"),
1319 i18n("Enter the number of days between repetitions of the alarm"),
1320 i18n("Select the days of the week on which the alarm is allowed to occur"),
1321 readOnly, parent, name)
1322{ }
1323
1324
1325/*=============================================================================
1326= Class WeeklyRule
1327= Weekly rule widget.
1328=============================================================================*/
1329
1330WeeklyRule::WeeklyRule(bool readOnly, TQWidget* parent, const char* name)
1331 : DayWeekRule(i18n("week(s)"),
1332 i18n("Enter the number of weeks between repetitions of the alarm"),
1333 i18n("Select the days of the week on which to repeat the alarm"),
1334 readOnly, parent, name)
1335{ }
1336
1337
1338/*=============================================================================
1339= Class MonthYearRule
1340= Monthly/yearly rule widget base class.
1341=============================================================================*/
1342
1343MonthYearRule::MonthYearRule(const TQString& freqText, const TQString& freqWhatsThis, bool allowEveryWeek,
1344 bool readOnly, TQWidget* parent, const char* name)
1345 : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
1346 mEveryWeek(allowEveryWeek)
1347{
1348 mButtonGroup = new ButtonGroup(this);
1349 mButtonGroup->hide();
1350
1351 // Month day selector
1352 TQHBox* box = new TQHBox(this);
1353 box->setSpacing(KDialog::spacingHint());
1354 layout()->addWidget(box);
1355
1356 mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
1357 mDayButton->setFixedSize(mDayButton->sizeHint());
1358 mDayButton->setReadOnly(readOnly);
1359 mDayButtonId = mButtonGroup->insert(mDayButton);
1360 TQWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
1361
1362 mDayCombo = new ComboBox(false, box);
1363 mDayCombo->setSizeLimit(11);
1364 for (int i = 0; i < 31; ++i)
1365 mDayCombo->insertItem(TQString::number(i + 1));
1366 mDayCombo->insertItem(i18n("Last day of month", "Last"));
1367 mDayCombo->setFixedSize(mDayCombo->sizeHint());
1368 mDayCombo->setReadOnly(readOnly);
1369 TQWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
1370 mDayButton->setFocusWidget(mDayCombo);
1371 connect(mDayCombo, TQ_SIGNAL(activated(int)), TQ_SLOT(slotDaySelected(int)));
1372
1373 box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
1374 box->setFixedHeight(box->sizeHint().height());
1375
1376 // Month position selector
1377 box = new TQHBox(this);
1378 box->setSpacing(KDialog::spacingHint());
1379 layout()->addWidget(box);
1380
1381 mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
1382 mPosButton->setFixedSize(mPosButton->sizeHint());
1383 mPosButton->setReadOnly(readOnly);
1384 mPosButtonId = mButtonGroup->insert(mPosButton);
1385 TQWhatsThis::add(mPosButton,
1386 i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
1387
1388 mWeekCombo = new ComboBox(false, box);
1389 mWeekCombo->insertItem(i18n("1st"));
1390 mWeekCombo->insertItem(i18n("2nd"));
1391 mWeekCombo->insertItem(i18n("3rd"));
1392 mWeekCombo->insertItem(i18n("4th"));
1393 mWeekCombo->insertItem(i18n("5th"));
1394 mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
1395 mWeekCombo->insertItem(i18n("2nd Last"));
1396 mWeekCombo->insertItem(i18n("3rd Last"));
1397 mWeekCombo->insertItem(i18n("4th Last"));
1398 mWeekCombo->insertItem(i18n("5th Last"));
1399 if (mEveryWeek)
1400 {
1401 mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
1402 mWeekCombo->setSizeLimit(11);
1403 }
1404 TQWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
1405 mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
1406 mWeekCombo->setReadOnly(readOnly);
1407 mPosButton->setFocusWidget(mWeekCombo);
1408
1409 mDayOfWeekCombo = new ComboBox(false, box);
1410 const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1411 for (int i = 0; i < 7; ++i)
1412 {
1413 int day = KAlarm::localeDayInWeek_to_weekDay(i);
1414 mDayOfWeekCombo->insertItem(calendar->weekDayName(day));
1415 }
1416 mDayOfWeekCombo->setReadOnly(readOnly);
1417 TQWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
1418
1419 box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
1420 box->setFixedHeight(box->sizeHint().height());
1421 connect(mButtonGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(clicked(int)));
1422}
1423
1424MonthYearRule::DayPosType MonthYearRule::type() const
1425{
1426 return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
1427}
1428
1429void MonthYearRule::setType(MonthYearRule::DayPosType type)
1430{
1431 mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
1432}
1433
1434void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
1435{
1436 --dayOfMonth;
1437 mDayCombo->setCurrentItem(dayOfMonth);
1438 mWeekCombo->setCurrentItem(dayOfMonth / 7);
1439 mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1440}
1441
1442int MonthYearRule::date() const
1443{
1444 int daynum = mDayCombo->currentItem() + 1;
1445 return (daynum <= 31) ? daynum : 31 - daynum;
1446}
1447
1448int MonthYearRule::week() const
1449{
1450 int weeknum = mWeekCombo->currentItem() + 1;
1451 return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
1452}
1453
1454int MonthYearRule::dayOfWeek() const
1455{
1456 return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
1457}
1458
1459void MonthYearRule::setDate(int dayOfMonth)
1460{
1461 mButtonGroup->setButton(mDayButtonId);
1462 mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0); // day 0 shouldn't ever occur
1463}
1464
1465void MonthYearRule::setPosition(int week, int dayOfWeek)
1466{
1467 mButtonGroup->setButton(mPosButtonId);
1468 mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
1469 mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1470}
1471
1472void MonthYearRule::enableSelection(DayPosType type)
1473{
1474 bool date = (type == DATE);
1475 mDayCombo->setEnabled(date);
1476 mWeekCombo->setEnabled(!date);
1477 mDayOfWeekCombo->setEnabled(!date);
1478}
1479
1480void MonthYearRule::clicked(int id)
1481{
1482 enableSelection(id == mDayButtonId ? DATE : POS);
1483}
1484
1485void MonthYearRule::slotDaySelected(int index)
1486{
1487 daySelected(index <= 30 ? index + 1 : 30 - index);
1488}
1489
1490/******************************************************************************
1491 * Save the state of all controls.
1492 */
1493void MonthYearRule::saveState()
1494{
1495 Rule::saveState();
1496 mSavedType = type();
1497 if (mSavedType == DATE)
1498 mSavedDay = date();
1499 else
1500 {
1501 mSavedWeek = week();
1502 mSavedWeekDay = dayOfWeek();
1503 }
1504}
1505
1506/******************************************************************************
1507 * Check whether any of the controls have changed state since initialisation.
1508 */
1509bool MonthYearRule::stateChanged() const
1510{
1511 if (Rule::stateChanged()
1512 || mSavedType != type())
1513 return true;
1514 if (mSavedType == DATE)
1515 {
1516 if (mSavedDay != date())
1517 return true;
1518 }
1519 else
1520 {
1521 if (mSavedWeek != week()
1522 || mSavedWeekDay != dayOfWeek())
1523 return true;
1524 }
1525 return false;
1526}
1527
1528
1529/*=============================================================================
1530= Class MonthlyRule
1531= Monthly rule widget.
1532=============================================================================*/
1533
1534MonthlyRule::MonthlyRule(bool readOnly, TQWidget* parent, const char* name)
1535 : MonthYearRule(i18n("month(s)"),
1536 i18n("Enter the number of months between repetitions of the alarm"),
1537 false, readOnly, parent, name)
1538{ }
1539
1540
1541/*=============================================================================
1542= Class YearlyRule
1543= Yearly rule widget.
1544=============================================================================*/
1545
1546YearlyRule::YearlyRule(bool readOnly, TQWidget* parent, const char* name)
1547 : MonthYearRule(i18n("year(s)"),
1548 i18n("Enter the number of years between repetitions of the alarm"),
1549 true, readOnly, parent, name)
1550{
1551 // Set up the month selection widgets
1552 TQBoxLayout* hlayout = new TQHBoxLayout(layout(), KDialog::spacingHint());
1553 TQLabel* label = new TQLabel(i18n("List of months to select", "Months:"), this);
1554 label->setFixedSize(label->sizeHint());
1555 hlayout->addWidget(label, 0, TQt::AlignAuto | TQt::AlignTop);
1556
1557 // List the months of the year.
1558 TQWidget* w = new TQWidget(this); // this is to control the TQWhatsThis text display area
1559 hlayout->addWidget(w, 1, TQt::AlignAuto);
1560 TQGridLayout* grid = new TQGridLayout(w, 4, 3, 0, KDialog::spacingHint());
1561 const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1562 int year = TQDate::currentDate().year();
1563 for (int i = 0; i < 12; ++i)
1564 {
1565 mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, true), w);
1566 mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
1567 mMonthBox[i]->setReadOnly(readOnly);
1568 grid->addWidget(mMonthBox[i], i%3, i/3, TQt::AlignAuto);
1569 }
1570 connect(mMonthBox[1], TQ_SIGNAL(toggled(bool)), TQ_SLOT(enableFeb29()));
1571 w->setFixedHeight(w->sizeHint().height());
1572 TQWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
1573
1574 // February 29th handling option
1575 TQHBox* f29box = new TQHBox(this);
1576 layout()->addWidget(f29box);
1577 TQHBox* box = new TQHBox(f29box); // this is to control the TQWhatsThis text display area
1578 box->setSpacing(KDialog::spacingHint());
1579 mFeb29Label = new TQLabel(i18n("February 2&9th alarm in non-leap years:"), box);
1580 mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
1581 mFeb29Combo = new ComboBox(false, box);
1582 mFeb29Combo->insertItem(i18n("No date", "None"));
1583 mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
1584 mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
1585 mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
1586 mFeb29Combo->setReadOnly(readOnly);
1587 mFeb29Label->setBuddy(mFeb29Combo);
1588 box->setFixedSize(box->sizeHint());
1589 TQWhatsThis::add(box,
1590 i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
1591 new TQWidget(f29box); // left adjust the visible widgets
1592 f29box->setFixedHeight(f29box->sizeHint().height());
1593}
1594
1595void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
1596{
1597 MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
1598 --month;
1599 for (int i = 0; i < 12; ++i)
1600 mMonthBox[i]->setChecked(i == month);
1601 setFeb29Type(Preferences::defaultFeb29Type());
1602 daySelected(dayOfMonth); // enable/disable month checkboxes as appropriate
1603}
1604
1605/******************************************************************************
1606 * Fetch which months have been checked (1 - 12).
1607 * Reply = true if February has been checked.
1608 */
1609TQValueList<int> YearlyRule::months() const
1610{
1611 TQValueList<int> mnths;
1612 for (int i = 0; i < 12; ++i)
1613 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1614 mnths.append(i + 1);
1615 return mnths;
1616}
1617
1618/******************************************************************************
1619 * Check/uncheck each month of the year according to the specified list.
1620 */
1621void YearlyRule::setMonths(const TQValueList<int>& mnths)
1622{
1623 bool checked[12];
1624 for (int i = 0; i < 12; ++i)
1625 checked[i] = false;
1626 for (TQValueListConstIterator<int> it = mnths.begin(); it != mnths.end(); ++it)
1627 checked[(*it) - 1] = true;
1628 for (int i = 0; i < 12; ++i)
1629 mMonthBox[i]->setChecked(checked[i]);
1630 enableFeb29();
1631}
1632
1633/******************************************************************************
1634 * Return the date for February 29th alarms in non-leap years.
1635 */
1636KARecurrence::Feb29Type YearlyRule::feb29Type() const
1637{
1638 if (mFeb29Combo->isEnabled())
1639 {
1640 switch (mFeb29Combo->currentItem())
1641 {
1642 case 1: return KARecurrence::FEB29_MAR1;
1643 case 2: return KARecurrence::FEB29_FEB28;
1644 default: break;
1645 }
1646 }
1647 return KARecurrence::FEB29_FEB29;
1648}
1649
1650/******************************************************************************
1651 * Set the date for February 29th alarms to trigger in non-leap years.
1652 */
1653void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
1654{
1655 int index;
1656 switch (type)
1657 {
1658 default:
1659 case KARecurrence::FEB29_FEB29: index = 0; break;
1660 case KARecurrence::FEB29_MAR1: index = 1; break;
1661 case KARecurrence::FEB29_FEB28: index = 2; break;
1662 }
1663 mFeb29Combo->setCurrentItem(index);
1664}
1665
1666/******************************************************************************
1667 * Validate: check that at least one month is selected.
1668 */
1669TQWidget* YearlyRule::validate(TQString& errorMessage)
1670{
1671 for (int i = 0; i < 12; ++i)
1672 if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1673 return 0;
1674 errorMessage = i18n("No month selected");
1675 return mMonthBox[0];
1676}
1677
1678/******************************************************************************
1679 * Called when a yearly recurrence type radio button is clicked,
1680 * to enable/disable month checkboxes as appropriate for the date selected.
1681 */
1682void YearlyRule::clicked(int id)
1683{
1684 MonthYearRule::clicked(id);
1685 daySelected(buttonType(id) == DATE ? date() : 1);
1686}
1687
1688/******************************************************************************
1689 * Called when a day of the month is selected in a yearly recurrence, to
1690 * disable months for which the day is out of range.
1691 */
1692void YearlyRule::daySelected(int day)
1693{
1694 mMonthBox[1]->setEnabled(day <= 29); // February
1695 bool enable = (day != 31);
1696 mMonthBox[3]->setEnabled(enable); // April
1697 mMonthBox[5]->setEnabled(enable); // June
1698 mMonthBox[8]->setEnabled(enable); // September
1699 mMonthBox[10]->setEnabled(enable); // November
1700 enableFeb29();
1701}
1702
1703/******************************************************************************
1704 * Enable/disable the February 29th combo box depending on whether February
1705 * 29th is selected.
1706 */
1707void YearlyRule::enableFeb29()
1708{
1709 bool enable = (type() == DATE && date() == 29 && mMonthBox[1]->isChecked() && mMonthBox[1]->isEnabled());
1710 mFeb29Label->setEnabled(enable);
1711 mFeb29Combo->setEnabled(enable);
1712}
1713
1714/******************************************************************************
1715 * Save the state of all controls.
1716 */
1717void YearlyRule::saveState()
1718{
1719 MonthYearRule::saveState();
1720 mSavedMonths = months();
1721 mSavedFeb29Type = feb29Type();
1722}
1723
1724/******************************************************************************
1725 * Check whether any of the controls have changed state since initialisation.
1726 */
1727bool YearlyRule::stateChanged() const
1728{
1729 return (MonthYearRule::stateChanged()
1730 || mSavedMonths != months()
1731 || mSavedFeb29Type != feb29Type());
1732}
represents calendar alarms and events
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232
miscellaneous functions
the KAlarm application object