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"
62 using 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.
69 TQString RecurrenceEdit::i18n_Norecur() { return i18n("No recurrence"); }
70 TQString RecurrenceEdit::i18n_NoRecur() { return i18n("No Recurrence"); }
71 TQString RecurrenceEdit::i18n_AtLogin() { return i18n("At Login"); }
72 TQString RecurrenceEdit::i18n_l_Atlogin() { return i18n("At &login"); }
73 TQString RecurrenceEdit::i18n_HourlyMinutely() { return i18n("Hourly/Minutely"); }
74 TQString RecurrenceEdit::i18n_u_HourlyMinutely() { return i18n("Ho&urly/Minutely"); }
75 TQString RecurrenceEdit::i18n_Daily() { return i18n("Daily"); }
76 TQString RecurrenceEdit::i18n_d_Daily() { return i18n("&Daily"); }
77 TQString RecurrenceEdit::i18n_Weekly() { return i18n("Weekly"); }
78 TQString RecurrenceEdit::i18n_w_Weekly() { return i18n("&Weekly"); }
79 TQString RecurrenceEdit::i18n_Monthly() { return i18n("Monthly"); }
80 TQString RecurrenceEdit::i18n_m_Monthly() { return i18n("&Monthly"); }
81 TQString RecurrenceEdit::i18n_Yearly() { return i18n("Yearly"); }
82 TQString RecurrenceEdit::i18n_y_Yearly() { return i18n("&Yearly"); }
83 
84 
85 RecurrenceEdit::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, Qt::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, Qt::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, TQT_SIGNAL(buttonSet(int)), TQT_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, TQT_SIGNAL(needsInitialisation()), TQT_SIGNAL(repeatNeedsInitialisation()));
176  connect(mSubRepetition, TQT_SIGNAL(changed()), TQT_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, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
200  connect(mDailyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
201  connect(mWeeklyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
202  connect(mMonthlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
203  connect(mYearlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_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, TQT_SIGNAL(buttonSet(int)), TQT_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, TQT_SIGNAL(valueChanged(int)), TQT_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, TQT_SIGNAL(toggled(bool)), TQT_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, TQT_SIGNAL(selectionChanged()), TQT_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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(clicked()), TQT_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  */
355 TQWidget* 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  */
385 void 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 
453 void 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  */
463 void 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 
475 void 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 */
488 int 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 */
504 void 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 */
528 void RecurrenceEdit::activateSubRepetition()
529 {
530  mSubRepetition->activate();
531 }
532 
533 /******************************************************************************
534  * Called when the value of the repeat count field changes, to reset the
535  * minimum value to 1 if the value was 0.
536  */
537 void RecurrenceEdit::repeatCountChanged(int value)
538 {
539  if (value > 0 && mRepeatCountEntry->minValue() == 0)
540  mRepeatCountEntry->setMinValue(1);
541 }
542 
543 /******************************************************************************
544  * Add the date entered in the exception date edit control to the list of
545  * exception dates.
546  */
547 void RecurrenceEdit::addException()
548 {
549  if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
550  return;
551  TQDate date = mExceptionDateEdit->date();
552  TQValueList<TQDate>::Iterator it;
553  int index = 0;
554  bool insert = true;
555  for (it = mExceptionDates.begin(); it != mExceptionDates.end(); ++index, ++it)
556  {
557  if (date <= *it)
558  {
559  insert = (date != *it);
560  break;
561  }
562  }
563  if (insert)
564  {
565  mExceptionDates.insert(it, date);
566  mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(date), index);
567  }
568  mExceptionDateList->setCurrentItem(index);
569  enableExceptionButtons();
570 }
571 
572 /******************************************************************************
573  * Change the currently highlighted exception date to that entered in the
574  * exception date edit control.
575  */
576 void RecurrenceEdit::changeException()
577 {
578  if (!mExceptionDateEdit || !mExceptionDateEdit->isValid())
579  return;
580  int index = mExceptionDateList->currentItem();
581  if (index >= 0 && mExceptionDateList->isSelected(index))
582  {
583  TQDate olddate = mExceptionDates[index];
584  TQDate newdate = mExceptionDateEdit->date();
585  if (newdate != olddate)
586  {
587  mExceptionDates.remove(mExceptionDates.at(index));
588  mExceptionDateList->removeItem(index);
589  addException();
590  }
591  }
592 }
593 
594 /******************************************************************************
595  * Delete the currently highlighted exception date.
596  */
597 void RecurrenceEdit::deleteException()
598 {
599  int index = mExceptionDateList->currentItem();
600  if (index >= 0 && mExceptionDateList->isSelected(index))
601  {
602  mExceptionDates.remove(mExceptionDates.at(index));
603  mExceptionDateList->removeItem(index);
604  enableExceptionButtons();
605  }
606 }
607 
608 /******************************************************************************
609  * Enable/disable the exception group buttons according to whether any item is
610  * selected in the exceptions listbox.
611  */
612 void RecurrenceEdit::enableExceptionButtons()
613 {
614  int index = mExceptionDateList->currentItem();
615  bool enable = (index >= 0 && mExceptionDateList->isSelected(index));
616  if (mDeleteExceptionButton)
617  mDeleteExceptionButton->setEnabled(enable);
618  if (mChangeExceptionButton)
619  mChangeExceptionButton->setEnabled(enable);
620 
621  // Prevent the exceptions list box receiving keyboard focus is it's empty
622  mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? TQ_WheelFocus : TQ_NoFocus);
623 }
624 
625 /******************************************************************************
626  * Notify this instance of a change in the alarm start date.
627  */
628 void RecurrenceEdit::setStartDate(const TQDate& start, const TQDate& today)
629 {
630  if (!mReadOnly)
631  {
632  setRuleDefaults(start);
633  if (start < today)
634  {
635  mEndDateEdit->setMinDate(today);
636  if (mExceptionDateEdit)
637  mExceptionDateEdit->setMinDate(today);
638  }
639  else
640  {
641  const TQString startString = i18n("Date cannot be earlier than start date", "start date");
642  mEndDateEdit->setMinDate(start, startString);
643  if (mExceptionDateEdit)
644  mExceptionDateEdit->setMinDate(start, startString);
645  }
646  }
647 }
648 
649 /******************************************************************************
650  * Specify the default recurrence end date.
651  */
652 void RecurrenceEdit::setDefaultEndDate(const TQDate& end)
653 {
654  if (!mEndDateButton->isOn())
655  mEndDateEdit->setDate(end);
656 }
657 
658 void RecurrenceEdit::setEndDateTime(const DateTime& end)
659 {
660  mEndDateEdit->setDate(end.date());
661  mEndTimeEdit->setValue(end.time());
662  mEndTimeEdit->setEnabled(!end.isDateOnly());
663  mEndAnyTimeCheckBox->setChecked(end.isDateOnly());
664 }
665 
666 DateTime RecurrenceEdit::endDateTime() const
667 {
668  if (mRuleButtonGroup->selected() == mAtLoginButton && mEndAnyTimeCheckBox->isChecked())
669  return DateTime(mEndDateEdit->date());
670  return DateTime(mEndDateEdit->date(), mEndTimeEdit->time());
671 }
672 
673 /******************************************************************************
674  * Set all controls to their default values.
675  */
676 void RecurrenceEdit::setDefaults(const TQDateTime& from)
677 {
678  mCurrStartDateTime = from;
679  TQDate fromDate = from.date();
680  mNoEndDateButton->setChecked(true);
681 
682  mSubDailyRule->setFrequency(1);
683  mDailyRule->setFrequency(1);
684  mWeeklyRule->setFrequency(1);
685  mMonthlyRule->setFrequency(1);
686  mYearlyRule->setFrequency(1);
687 
688  setRuleDefaults(fromDate);
689  mMonthlyRule->setType(MonthYearRule::DATE); // date in month
690  mYearlyRule->setType(MonthYearRule::DATE); // date in year
691 
692  mEndDateEdit->setDate(fromDate);
693 
694  mNoEmitTypeChanged = true;
695  int button;
696  switch (Preferences::defaultRecurPeriod())
697  {
698  case AT_LOGIN: button = mAtLoginButtonId; break;
699  case ANNUAL: button = mYearlyButtonId; break;
700  case MONTHLY: button = mMonthlyButtonId; break;
701  case WEEKLY: button = mWeeklyButtonId; break;
702  case DAILY: button = mDailyButtonId; break;
703  case SUBDAILY: button = mSubDailyButtonId; break;
704  case NO_RECUR:
705  default: button = mNoneButtonId; break;
706  }
707  mRuleButtonGroup->setButton(button);
708  mNoEmitTypeChanged = false;
709  rangeTypeClicked();
710  enableExceptionButtons();
711 
712  saveState();
713 }
714 
715 /******************************************************************************
716  * Set the controls for weekly, monthly and yearly rules which have not so far
717  * been shown, to their default values, depending on the recurrence start date.
718  */
719 void RecurrenceEdit::setRuleDefaults(const TQDate& fromDate)
720 {
721  int day = fromDate.day();
722  int dayOfWeek = fromDate.dayOfWeek();
723  int month = fromDate.month();
724  if (!mDailyShown)
725  mDailyRule->setDays(true);
726  if (!mWeeklyShown)
727  mWeeklyRule->setDay(dayOfWeek);
728  if (!mMonthlyShown)
729  mMonthlyRule->setDefaultValues(day, dayOfWeek);
730  if (!mYearlyShown)
731  mYearlyRule->setDefaultValues(day, dayOfWeek, month);
732 }
733 
734 /******************************************************************************
735 * Set the state of all controls to reflect the data in the specified event.
736 * Set 'keepDuration' true to prevent the recurrence count being adjusted to the
737 * remaining number of recurrences.
738 */
739 void RecurrenceEdit::set(const KAEvent& event, bool keepDuration)
740 {
741  setDefaults(event.mainDateTime().dateTime());
742  if (event.repeatAtLogin())
743  {
744  mRuleButtonGroup->setButton(mAtLoginButtonId);
745  mEndDateButton->setChecked(true);
746  return;
747  }
748  mRuleButtonGroup->setButton(mNoneButtonId);
749  KARecurrence* recurrence = event.recurrence();
750  if (!recurrence)
751  return;
752  KARecurrence::Type rtype = recurrence->type();
753  switch (rtype)
754  {
755  case KARecurrence::MINUTELY:
756  mRuleButtonGroup->setButton(mSubDailyButtonId);
757  break;
758 
759  case KARecurrence::DAILY:
760  {
761  mRuleButtonGroup->setButton(mDailyButtonId);
762  TQBitArray rDays = recurrence->days();
763  bool set = false;
764  for (int i = 0; i < 7 && !set; ++i)
765  set = rDays.testBit(i);
766  if (set)
767  mDailyRule->setDays(rDays);
768  else
769  mDailyRule->setDays(true);
770  break;
771  }
772  case KARecurrence::WEEKLY:
773  {
774  mRuleButtonGroup->setButton(mWeeklyButtonId);
775  TQBitArray rDays = recurrence->days();
776  mWeeklyRule->setDays(rDays);
777  break;
778  }
779  case KARecurrence::MONTHLY_POS: // on nth (Tuesday) of the month
780  {
781  TQValueList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions();
782  int i = posns.first().pos();
783  if (!i)
784  {
785  // It's every (Tuesday) of the month. Convert to a weekly recurrence
786  // (but ignoring any non-every xxxDay positions).
787  mRuleButtonGroup->setButton(mWeeklyButtonId);
788  mWeeklyRule->setFrequency(recurrence->frequency());
789  TQBitArray rDays(7);
790  for (TQValueList<RecurrenceRule::WDayPos>::ConstIterator it = posns.begin(); it != posns.end(); ++it)
791  {
792  if (!(*it).pos())
793  rDays.setBit((*it).day() - 1, 1);
794  }
795  mWeeklyRule->setDays(rDays);
796  break;
797  }
798  mRuleButtonGroup->setButton(mMonthlyButtonId);
799  mMonthlyRule->setPosition(i, posns.first().day());
800  break;
801  }
802  case KARecurrence::MONTHLY_DAY: // on nth day of the month
803  {
804  mRuleButtonGroup->setButton(mMonthlyButtonId);
805  TQValueList<int> rmd = recurrence->monthDays();
806  int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
807  mMonthlyRule->setDate(day);
808  break;
809  }
810  case KARecurrence::ANNUAL_DATE: // on the nth day of (months...) in the year
811  case KARecurrence::ANNUAL_POS: // on the nth (Tuesday) of (months...) in the year
812  {
813  if (rtype == KARecurrence::ANNUAL_DATE)
814  {
815  mRuleButtonGroup->setButton(mYearlyButtonId);
816  const TQValueList<int> rmd = recurrence->monthDays();
817  int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
818  mYearlyRule->setDate(day);
819  mYearlyRule->setFeb29Type(recurrence->feb29Type());
820  }
821  else if (rtype == KARecurrence::ANNUAL_POS)
822  {
823  mRuleButtonGroup->setButton(mYearlyButtonId);
824  TQValueList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions();
825  mYearlyRule->setPosition(posns.first().pos(), posns.first().day());
826  }
827  mYearlyRule->setMonths(recurrence->yearMonths());
828  break;
829  }
830  default:
831  return;
832  }
833 
834  mRule->setFrequency(recurrence->frequency());
835 
836  // Get range information
837  TQDateTime endtime = mCurrStartDateTime;
838  int duration = recurrence->duration();
839  if (duration == -1)
840  mNoEndDateButton->setChecked(true);
841  else if (duration)
842  {
843  mRepeatCountButton->setChecked(true);
844  mRepeatCountEntry->setValue(duration);
845  }
846  else
847  {
848  mEndDateButton->setChecked(true);
849  endtime = recurrence->endDateTime();
850  mEndTimeEdit->setValue(endtime.time());
851  }
852  mEndDateEdit->setDate(endtime.date());
853 
854  // Get exception information
855  mExceptionDates = event.recurrence()->exDates();
856  qHeapSort(mExceptionDates);
857  mExceptionDateList->clear();
858  for (DateList::ConstIterator it = mExceptionDates.begin(); it != mExceptionDates.end(); ++it)
859  mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(*it));
860  enableExceptionButtons();
861 
862  // Get repetition within recurrence
863  mSubRepetition->set(event.repeatInterval(), event.repeatCount());
864 
865  rangeTypeClicked();
866 
867  saveState();
868 }
869 
870 /******************************************************************************
871  * Update the specified KAEvent with the entered recurrence data.
872  * If 'adjustStart' is true, the start date/time will be adjusted if necessary
873  * to be the first date/time which recurs on or after the original start.
874  */
875 void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart)
876 {
877  // Get end date and repeat count, common to all types of recurring events
878  TQDate endDate;
879  TQTime endTime;
880  int repeatCount;
881  if (mNoEndDateButton->isChecked())
882  repeatCount = -1;
883  else if (mRepeatCountButton->isChecked())
884  repeatCount = mRepeatCountEntry->value();
885  else
886  {
887  repeatCount = 0;
888  endDate = mEndDateEdit->date();
889  endTime = mEndTimeEdit->time();
890  }
891 
892  // Set up the recurrence according to the type selected
893  TQButton* button = mRuleButtonGroup->selected();
894  event.setRepeatAtLogin(button == mAtLoginButton);
895  int frequency = mRule ? mRule->frequency() : 0;
896  if (button == mSubDailyButton)
897  {
898  TQDateTime endDateTime(endDate, endTime);
899  event.setRecurMinutely(frequency, repeatCount, endDateTime);
900  }
901  else if (button == mDailyButton)
902  {
903  event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate);
904  }
905  else if (button == mWeeklyButton)
906  {
907  event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate);
908  }
909  else if (button == mMonthlyButton)
910  {
911  if (mMonthlyRule->type() == MonthlyRule::POS)
912  {
913  // It's by position
914  KAEvent::MonthPos pos;
915  pos.days.fill(false);
916  pos.days.setBit(mMonthlyRule->dayOfWeek() - 1);
917  pos.weeknum = mMonthlyRule->week();
918  TQValueList<KAEvent::MonthPos> poses;
919  poses.append(pos);
920  event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate);
921  }
922  else
923  {
924  // It's by day
925  int daynum = mMonthlyRule->date();
926  TQValueList<int> daynums;
927  daynums.append(daynum);
928  event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
929  }
930  }
931  else if (button == mYearlyButton)
932  {
933  TQValueList<int> months = mYearlyRule->months();
934  if (mYearlyRule->type() == YearlyRule::POS)
935  {
936  // It's by position
937  KAEvent::MonthPos pos;
938  pos.days.fill(false);
939  pos.days.setBit(mYearlyRule->dayOfWeek() - 1);
940  pos.weeknum = mYearlyRule->week();
941  TQValueList<KAEvent::MonthPos> poses;
942  poses.append(pos);
943  event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate);
944  }
945  else
946  {
947  // It's by date in month
948  event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(),
949  mYearlyRule->feb29Type(), repeatCount, endDate);
950  }
951  }
952  else
953  {
954  event.setNoRecur();
955  return;
956  }
957  if (!event.recurs())
958  return; // an error occurred setting up the recurrence
959  if (adjustStart)
960  event.setFirstRecurrence();
961 
962  // Set up repetition within the recurrence.
963  // N.B. This requires the main recurrence to be set up first.
964  int count = mSubRepetition->count();
965  if (mRuleButtonType < SUBDAILY)
966  count = 0;
967  event.setRepetition(mSubRepetition->interval(), count);
968 
969  // Set up exceptions
970  event.recurrence()->setExDates(mExceptionDates);
971 
972  event.setUpdated();
973 }
974 
975 /******************************************************************************
976  * Save the state of all controls.
977  */
978 void RecurrenceEdit::saveState()
979 {
980  mSavedRuleButton = mRuleButtonGroup->selected();
981  if (mRule)
982  mRule->saveState();
983  mSavedRangeButton = mRangeButtonGroup->selected();
984  if (mSavedRangeButton == mRepeatCountButton)
985  mSavedRecurCount = mRepeatCountEntry->value();
986  else if (mSavedRangeButton == mEndDateButton)
987  mSavedEndDateTime.set(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked());
988  mSavedExceptionDates = mExceptionDates;
989  mSavedRepeatInterval = mSubRepetition->interval();
990  mSavedRepeatCount = mSubRepetition->count();
991 }
992 
993 /******************************************************************************
994  * Check whether any of the controls have changed state since initialisation.
995  */
996 bool RecurrenceEdit::stateChanged() const
997 {
998  if (mSavedRuleButton != mRuleButtonGroup->selected()
999  || mSavedRangeButton != mRangeButtonGroup->selected()
1000  || (mRule && mRule->stateChanged()))
1001  return true;
1002  if (mSavedRangeButton == mRepeatCountButton
1003  && mSavedRecurCount != mRepeatCountEntry->value())
1004  return true;
1005  if (mSavedRangeButton == mEndDateButton
1006  && mSavedEndDateTime != DateTime(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked()))
1007  return true;
1008  if (mSavedExceptionDates != mExceptionDates
1009  || mSavedRepeatInterval != mSubRepetition->interval()
1010  || mSavedRepeatCount != mSubRepetition->count())
1011  return true;
1012  return false;
1013 }
1014 
1015 
1016 
1017 /*=============================================================================
1018 = Class Rule
1019 = Base class for rule widgets, including recurrence frequency.
1020 =============================================================================*/
1021 
1022 Rule::Rule(const TQString& freqText, const TQString& freqWhatsThis, bool time, bool readOnly, TQWidget* parent, const char* name)
1023  : NoRule(parent, name)
1024 {
1025  mLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
1026  TQHBox* freqBox = new TQHBox(this);
1027  mLayout->addWidget(freqBox);
1028  TQHBox* box = new TQHBox(freqBox); // this is to control the TQWhatsThis text display area
1029  box->setSpacing(KDialog::spacingHint());
1030 
1031  TQLabel* label = new TQLabel(i18n("Recur e&very"), box);
1032  label->setFixedSize(label->sizeHint());
1033  if (time)
1034  {
1035  mIntSpinBox = 0;
1036  mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
1037  mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
1038  mTimeSpinBox->setReadOnly(readOnly);
1039  }
1040  else
1041  {
1042  mTimeSpinBox = 0;
1043  mSpinBox = mIntSpinBox = new SpinBox(1, 999, 1, box);
1044  mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
1045  mIntSpinBox->setReadOnly(readOnly);
1046  }
1047  connect(mSpinBox, TQT_SIGNAL(valueChanged(int)), TQT_SIGNAL(frequencyChanged()));
1048  label->setBuddy(mSpinBox);
1049  label = new TQLabel(freqText, box);
1050  label->setFixedSize(label->sizeHint());
1051  box->setFixedSize(sizeHint());
1052  TQWhatsThis::add(box, freqWhatsThis);
1053 
1054  new TQWidget(freqBox); // left adjust the visible widgets
1055  freqBox->setFixedHeight(freqBox->sizeHint().height());
1056  freqBox->setFocusProxy(mSpinBox);
1057 }
1058 
1059 int Rule::frequency() const
1060 {
1061  if (mIntSpinBox)
1062  return mIntSpinBox->value();
1063  if (mTimeSpinBox)
1064  return mTimeSpinBox->value();
1065  return 0;
1066 }
1067 
1068 void Rule::setFrequency(int n)
1069 {
1070  if (mIntSpinBox)
1071  mIntSpinBox->setValue(n);
1072  if (mTimeSpinBox)
1073  mTimeSpinBox->setValue(n);
1074 }
1075 
1076 /******************************************************************************
1077  * Save the state of all controls.
1078  */
1079 void Rule::saveState()
1080 {
1081  mSavedFrequency = frequency();
1082 }
1083 
1084 /******************************************************************************
1085  * Check whether any of the controls have changed state since initialisation.
1086  */
1087 bool Rule::stateChanged() const
1088 {
1089  return (mSavedFrequency != frequency());
1090 }
1091 
1092 
1093 /*=============================================================================
1094 = Class SubDailyRule
1095 = Sub-daily rule widget.
1096 =============================================================================*/
1097 
1098 SubDailyRule::SubDailyRule(bool readOnly, TQWidget* parent, const char* name)
1099  : Rule(i18n("hours:minutes"),
1100  i18n("Enter the number of hours and minutes between repetitions of the alarm"),
1101  true, readOnly, parent, name)
1102 { }
1103 
1104 
1105 /*=============================================================================
1106 = Class DayWeekRule
1107 = Daily/weekly rule widget base class.
1108 =============================================================================*/
1109 
1110 DayWeekRule::DayWeekRule(const TQString& freqText, const TQString& freqWhatsThis, const TQString& daysWhatsThis,
1111  bool readOnly, TQWidget* parent, const char* name)
1112  : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
1113  mSavedDays(7)
1114 {
1115  TQGridLayout* grid = new TQGridLayout(layout(), 1, 4, KDialog::spacingHint());
1116  grid->setRowStretch(0, 1);
1117 
1118  TQLabel* label = new TQLabel(i18n("On: Tuesday", "O&n:"), this);
1119  label->setFixedSize(label->sizeHint());
1120  grid->addWidget(label, 0, 0, TQt::AlignRight | TQt::AlignTop);
1121  grid->addColSpacing(1, KDialog::spacingHint());
1122 
1123  // List the days of the week starting at the user's start day of the week.
1124  // Save the first day of the week, just in case it changes while the dialog is open.
1125  TQWidget* box = new TQWidget(this); // this is to control the TQWhatsThis text display area
1126  TQGridLayout* dgrid = new TQGridLayout(box, 4, 2, 0, KDialog::spacingHint());
1127  const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1128  for (int i = 0; i < 7; ++i)
1129  {
1130  int day = KAlarm::localeDayInWeek_to_weekDay(i);
1131  mDayBox[i] = new CheckBox(calendar->weekDayName(day), box);
1132  mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
1133  mDayBox[i]->setReadOnly(readOnly);
1134  dgrid->addWidget(mDayBox[i], i%4, i/4, TQt::AlignAuto);
1135  }
1136  box->setFixedSize(box->sizeHint());
1137  TQWhatsThis::add(box, daysWhatsThis);
1138  grid->addWidget(box, 0, 2, TQt::AlignAuto);
1139  label->setBuddy(mDayBox[0]);
1140  grid->setColStretch(3, 1);
1141 }
1142 
1143 /******************************************************************************
1144  * Fetch which days of the week have been ticked.
1145  */
1146 TQBitArray DayWeekRule::days() const
1147 {
1148  TQBitArray ds(7);
1149  ds.fill(false);
1150  for (int i = 0; i < 7; ++i)
1151  if (mDayBox[i]->isChecked())
1152  ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
1153  return ds;
1154 }
1155 
1156 /******************************************************************************
1157  * Tick/untick every day of the week.
1158  */
1159 void DayWeekRule::setDays(bool tick)
1160 {
1161  for (int i = 0; i < 7; ++i)
1162  mDayBox[i]->setChecked(tick);
1163 }
1164 
1165 /******************************************************************************
1166  * Tick/untick each day of the week according to the specified bits.
1167  */
1168 void DayWeekRule::setDays(const TQBitArray& days)
1169 {
1170  for (int i = 0; i < 7; ++i)
1171  {
1172  bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
1173  mDayBox[i]->setChecked(x);
1174  }
1175 }
1176 
1177 /******************************************************************************
1178  * Tick the specified day of the week, and untick all other days.
1179  */
1180 void DayWeekRule::setDay(int dayOfWeek)
1181 {
1182  for (int i = 0; i < 7; ++i)
1183  mDayBox[i]->setChecked(false);
1184  if (dayOfWeek > 0 && dayOfWeek <= 7)
1185  mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
1186 }
1187 
1188 /******************************************************************************
1189  * Validate: check that at least one day is selected.
1190  */
1191 TQWidget* DayWeekRule::validate(TQString& errorMessage)
1192 {
1193  for (int i = 0; i < 7; ++i)
1194  if (mDayBox[i]->isChecked())
1195  return 0;
1196  errorMessage = i18n("No day selected");
1197  return mDayBox[0];
1198 }
1199 
1200 /******************************************************************************
1201  * Save the state of all controls.
1202  */
1203 void DayWeekRule::saveState()
1204 {
1205  Rule::saveState();
1206  mSavedDays = days();
1207 }
1208 
1209 /******************************************************************************
1210  * Check whether any of the controls have changed state since initialisation.
1211  */
1212 bool DayWeekRule::stateChanged() const
1213 {
1214  return (Rule::stateChanged()
1215  || mSavedDays != days());
1216 }
1217 
1218 
1219 /*=============================================================================
1220 = Class DailyRule
1221 = Daily rule widget.
1222 =============================================================================*/
1223 
1224 DailyRule::DailyRule(bool readOnly, TQWidget* parent, const char* name)
1225  : DayWeekRule(i18n("day(s)"),
1226  i18n("Enter the number of days between repetitions of the alarm"),
1227  i18n("Select the days of the week on which the alarm is allowed to occur"),
1228  readOnly, parent, name)
1229 { }
1230 
1231 
1232 /*=============================================================================
1233 = Class WeeklyRule
1234 = Weekly rule widget.
1235 =============================================================================*/
1236 
1237 WeeklyRule::WeeklyRule(bool readOnly, TQWidget* parent, const char* name)
1238  : DayWeekRule(i18n("week(s)"),
1239  i18n("Enter the number of weeks between repetitions of the alarm"),
1240  i18n("Select the days of the week on which to repeat the alarm"),
1241  readOnly, parent, name)
1242 { }
1243 
1244 
1245 /*=============================================================================
1246 = Class MonthYearRule
1247 = Monthly/yearly rule widget base class.
1248 =============================================================================*/
1249 
1250 MonthYearRule::MonthYearRule(const TQString& freqText, const TQString& freqWhatsThis, bool allowEveryWeek,
1251  bool readOnly, TQWidget* parent, const char* name)
1252  : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
1253  mEveryWeek(allowEveryWeek)
1254 {
1255  mButtonGroup = new ButtonGroup(this);
1256  mButtonGroup->hide();
1257 
1258  // Month day selector
1259  TQHBox* box = new TQHBox(this);
1260  box->setSpacing(KDialog::spacingHint());
1261  layout()->addWidget(box);
1262 
1263  mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
1264  mDayButton->setFixedSize(mDayButton->sizeHint());
1265  mDayButton->setReadOnly(readOnly);
1266  mDayButtonId = mButtonGroup->insert(mDayButton);
1267  TQWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
1268 
1269  mDayCombo = new ComboBox(false, box);
1270  mDayCombo->setSizeLimit(11);
1271  for (int i = 0; i < 31; ++i)
1272  mDayCombo->insertItem(TQString::number(i + 1));
1273  mDayCombo->insertItem(i18n("Last day of month", "Last"));
1274  mDayCombo->setFixedSize(mDayCombo->sizeHint());
1275  mDayCombo->setReadOnly(readOnly);
1276  TQWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
1277  mDayButton->setFocusWidget(mDayCombo);
1278  connect(mDayCombo, TQT_SIGNAL(activated(int)), TQT_SLOT(slotDaySelected(int)));
1279 
1280  box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
1281  box->setFixedHeight(box->sizeHint().height());
1282 
1283  // Month position selector
1284  box = new TQHBox(this);
1285  box->setSpacing(KDialog::spacingHint());
1286  layout()->addWidget(box);
1287 
1288  mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
1289  mPosButton->setFixedSize(mPosButton->sizeHint());
1290  mPosButton->setReadOnly(readOnly);
1291  mPosButtonId = mButtonGroup->insert(mPosButton);
1292  TQWhatsThis::add(mPosButton,
1293  i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
1294 
1295  mWeekCombo = new ComboBox(false, box);
1296  mWeekCombo->insertItem(i18n("1st"));
1297  mWeekCombo->insertItem(i18n("2nd"));
1298  mWeekCombo->insertItem(i18n("3rd"));
1299  mWeekCombo->insertItem(i18n("4th"));
1300  mWeekCombo->insertItem(i18n("5th"));
1301  mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
1302  mWeekCombo->insertItem(i18n("2nd Last"));
1303  mWeekCombo->insertItem(i18n("3rd Last"));
1304  mWeekCombo->insertItem(i18n("4th Last"));
1305  mWeekCombo->insertItem(i18n("5th Last"));
1306  if (mEveryWeek)
1307  {
1308  mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
1309  mWeekCombo->setSizeLimit(11);
1310  }
1311  TQWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
1312  mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
1313  mWeekCombo->setReadOnly(readOnly);
1314  mPosButton->setFocusWidget(mWeekCombo);
1315 
1316  mDayOfWeekCombo = new ComboBox(false, box);
1317  const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1318  for (int i = 0; i < 7; ++i)
1319  {
1320  int day = KAlarm::localeDayInWeek_to_weekDay(i);
1321  mDayOfWeekCombo->insertItem(calendar->weekDayName(day));
1322  }
1323  mDayOfWeekCombo->setReadOnly(readOnly);
1324  TQWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
1325 
1326  box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
1327  box->setFixedHeight(box->sizeHint().height());
1328  connect(mButtonGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(clicked(int)));
1329 }
1330 
1331 MonthYearRule::DayPosType MonthYearRule::type() const
1332 {
1333  return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
1334 }
1335 
1336 void MonthYearRule::setType(MonthYearRule::DayPosType type)
1337 {
1338  mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
1339 }
1340 
1341 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
1342 {
1343  --dayOfMonth;
1344  mDayCombo->setCurrentItem(dayOfMonth);
1345  mWeekCombo->setCurrentItem(dayOfMonth / 7);
1346  mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1347 }
1348 
1349 int MonthYearRule::date() const
1350 {
1351  int daynum = mDayCombo->currentItem() + 1;
1352  return (daynum <= 31) ? daynum : 31 - daynum;
1353 }
1354 
1355 int MonthYearRule::week() const
1356 {
1357  int weeknum = mWeekCombo->currentItem() + 1;
1358  return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
1359 }
1360 
1361 int MonthYearRule::dayOfWeek() const
1362 {
1363  return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
1364 }
1365 
1366 void MonthYearRule::setDate(int dayOfMonth)
1367 {
1368  mButtonGroup->setButton(mDayButtonId);
1369  mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0); // day 0 shouldn't ever occur
1370 }
1371 
1372 void MonthYearRule::setPosition(int week, int dayOfWeek)
1373 {
1374  mButtonGroup->setButton(mPosButtonId);
1375  mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
1376  mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
1377 }
1378 
1379 void MonthYearRule::enableSelection(DayPosType type)
1380 {
1381  bool date = (type == DATE);
1382  mDayCombo->setEnabled(date);
1383  mWeekCombo->setEnabled(!date);
1384  mDayOfWeekCombo->setEnabled(!date);
1385 }
1386 
1387 void MonthYearRule::clicked(int id)
1388 {
1389  enableSelection(id == mDayButtonId ? DATE : POS);
1390 }
1391 
1392 void MonthYearRule::slotDaySelected(int index)
1393 {
1394  daySelected(index <= 30 ? index + 1 : 30 - index);
1395 }
1396 
1397 /******************************************************************************
1398  * Save the state of all controls.
1399  */
1400 void MonthYearRule::saveState()
1401 {
1402  Rule::saveState();
1403  mSavedType = type();
1404  if (mSavedType == DATE)
1405  mSavedDay = date();
1406  else
1407  {
1408  mSavedWeek = week();
1409  mSavedWeekDay = dayOfWeek();
1410  }
1411 }
1412 
1413 /******************************************************************************
1414  * Check whether any of the controls have changed state since initialisation.
1415  */
1416 bool MonthYearRule::stateChanged() const
1417 {
1418  if (Rule::stateChanged()
1419  || mSavedType != type())
1420  return true;
1421  if (mSavedType == DATE)
1422  {
1423  if (mSavedDay != date())
1424  return true;
1425  }
1426  else
1427  {
1428  if (mSavedWeek != week()
1429  || mSavedWeekDay != dayOfWeek())
1430  return true;
1431  }
1432  return false;
1433 }
1434 
1435 
1436 /*=============================================================================
1437 = Class MonthlyRule
1438 = Monthly rule widget.
1439 =============================================================================*/
1440 
1441 MonthlyRule::MonthlyRule(bool readOnly, TQWidget* parent, const char* name)
1442  : MonthYearRule(i18n("month(s)"),
1443  i18n("Enter the number of months between repetitions of the alarm"),
1444  false, readOnly, parent, name)
1445 { }
1446 
1447 
1448 /*=============================================================================
1449 = Class YearlyRule
1450 = Yearly rule widget.
1451 =============================================================================*/
1452 
1453 YearlyRule::YearlyRule(bool readOnly, TQWidget* parent, const char* name)
1454  : MonthYearRule(i18n("year(s)"),
1455  i18n("Enter the number of years between repetitions of the alarm"),
1456  true, readOnly, parent, name)
1457 {
1458  // Set up the month selection widgets
1459  TQBoxLayout* hlayout = new TQHBoxLayout(layout(), KDialog::spacingHint());
1460  TQLabel* label = new TQLabel(i18n("List of months to select", "Months:"), this);
1461  label->setFixedSize(label->sizeHint());
1462  hlayout->addWidget(label, 0, TQt::AlignAuto | TQt::AlignTop);
1463 
1464  // List the months of the year.
1465  TQWidget* w = new TQWidget(this); // this is to control the TQWhatsThis text display area
1466  hlayout->addWidget(w, 1, TQt::AlignAuto);
1467  TQGridLayout* grid = new TQGridLayout(w, 4, 3, 0, KDialog::spacingHint());
1468  const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
1469  int year = TQDate::currentDate().year();
1470  for (int i = 0; i < 12; ++i)
1471  {
1472  mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, true), w);
1473  mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
1474  mMonthBox[i]->setReadOnly(readOnly);
1475  grid->addWidget(mMonthBox[i], i%3, i/3, TQt::AlignAuto);
1476  }
1477  connect(mMonthBox[1], TQT_SIGNAL(toggled(bool)), TQT_SLOT(enableFeb29()));
1478  w->setFixedHeight(w->sizeHint().height());
1479  TQWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
1480 
1481  // February 29th handling option
1482  TQHBox* f29box = new TQHBox(this);
1483  layout()->addWidget(f29box);
1484  TQHBox* box = new TQHBox(f29box); // this is to control the TQWhatsThis text display area
1485  box->setSpacing(KDialog::spacingHint());
1486  mFeb29Label = new TQLabel(i18n("February 2&9th alarm in non-leap years:"), box);
1487  mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
1488  mFeb29Combo = new ComboBox(false, box);
1489  mFeb29Combo->insertItem(i18n("No date", "None"));
1490  mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
1491  mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
1492  mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
1493  mFeb29Combo->setReadOnly(readOnly);
1494  mFeb29Label->setBuddy(mFeb29Combo);
1495  box->setFixedSize(box->sizeHint());
1496  TQWhatsThis::add(box,
1497  i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
1498  new TQWidget(f29box); // left adjust the visible widgets
1499  f29box->setFixedHeight(f29box->sizeHint().height());
1500 }
1501 
1502 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
1503 {
1504  MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
1505  --month;
1506  for (int i = 0; i < 12; ++i)
1507  mMonthBox[i]->setChecked(i == month);
1508  setFeb29Type(Preferences::defaultFeb29Type());
1509  daySelected(dayOfMonth); // enable/disable month checkboxes as appropriate
1510 }
1511 
1512 /******************************************************************************
1513  * Fetch which months have been checked (1 - 12).
1514  * Reply = true if February has been checked.
1515  */
1516 TQValueList<int> YearlyRule::months() const
1517 {
1518  TQValueList<int> mnths;
1519  for (int i = 0; i < 12; ++i)
1520  if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1521  mnths.append(i + 1);
1522  return mnths;
1523 }
1524 
1525 /******************************************************************************
1526  * Check/uncheck each month of the year according to the specified list.
1527  */
1528 void YearlyRule::setMonths(const TQValueList<int>& mnths)
1529 {
1530  bool checked[12];
1531  for (int i = 0; i < 12; ++i)
1532  checked[i] = false;
1533  for (TQValueListConstIterator<int> it = mnths.begin(); it != mnths.end(); ++it)
1534  checked[(*it) - 1] = true;
1535  for (int i = 0; i < 12; ++i)
1536  mMonthBox[i]->setChecked(checked[i]);
1537  enableFeb29();
1538 }
1539 
1540 /******************************************************************************
1541  * Return the date for February 29th alarms in non-leap years.
1542  */
1543 KARecurrence::Feb29Type YearlyRule::feb29Type() const
1544 {
1545  if (mFeb29Combo->isEnabled())
1546  {
1547  switch (mFeb29Combo->currentItem())
1548  {
1549  case 1: return KARecurrence::FEB29_MAR1;
1550  case 2: return KARecurrence::FEB29_FEB28;
1551  default: break;
1552  }
1553  }
1554  return KARecurrence::FEB29_FEB29;
1555 }
1556 
1557 /******************************************************************************
1558  * Set the date for February 29th alarms to trigger in non-leap years.
1559  */
1560 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
1561 {
1562  int index;
1563  switch (type)
1564  {
1565  default:
1566  case KARecurrence::FEB29_FEB29: index = 0; break;
1567  case KARecurrence::FEB29_MAR1: index = 1; break;
1568  case KARecurrence::FEB29_FEB28: index = 2; break;
1569  }
1570  mFeb29Combo->setCurrentItem(index);
1571 }
1572 
1573 /******************************************************************************
1574  * Validate: check that at least one month is selected.
1575  */
1576 TQWidget* YearlyRule::validate(TQString& errorMessage)
1577 {
1578  for (int i = 0; i < 12; ++i)
1579  if (mMonthBox[i]->isChecked() && mMonthBox[i]->isEnabled())
1580  return 0;
1581  errorMessage = i18n("No month selected");
1582  return mMonthBox[0];
1583 }
1584 
1585 /******************************************************************************
1586  * Called when a yearly recurrence type radio button is clicked,
1587  * to enable/disable month checkboxes as appropriate for the date selected.
1588  */
1589 void YearlyRule::clicked(int id)
1590 {
1591  MonthYearRule::clicked(id);
1592  daySelected(buttonType(id) == DATE ? date() : 1);
1593 }
1594 
1595 /******************************************************************************
1596  * Called when a day of the month is selected in a yearly recurrence, to
1597  * disable months for which the day is out of range.
1598  */
1599 void YearlyRule::daySelected(int day)
1600 {
1601  mMonthBox[1]->setEnabled(day <= 29); // February
1602  bool enable = (day != 31);
1603  mMonthBox[3]->setEnabled(enable); // April
1604  mMonthBox[5]->setEnabled(enable); // June
1605  mMonthBox[8]->setEnabled(enable); // September
1606  mMonthBox[10]->setEnabled(enable); // November
1607  enableFeb29();
1608 }
1609 
1610 /******************************************************************************
1611  * Enable/disable the February 29th combo box depending on whether February
1612  * 29th is selected.
1613  */
1614 void YearlyRule::enableFeb29()
1615 {
1616  bool enable = (type() == DATE && date() == 29 && mMonthBox[1]->isChecked() && mMonthBox[1]->isEnabled());
1617  mFeb29Label->setEnabled(enable);
1618  mFeb29Combo->setEnabled(enable);
1619 }
1620 
1621 /******************************************************************************
1622  * Save the state of all controls.
1623  */
1624 void YearlyRule::saveState()
1625 {
1626  MonthYearRule::saveState();
1627  mSavedMonths = months();
1628  mSavedFeb29Type = feb29Type();
1629 }
1630 
1631 /******************************************************************************
1632  * Check whether any of the controls have changed state since initialisation.
1633  */
1634 bool YearlyRule::stateChanged() const
1635 {
1636  return (MonthYearRule::stateChanged()
1637  || mSavedMonths != months()
1638  || mSavedFeb29Type != feb29Type());
1639 }