kalarm

recurrenceedit.cpp
00001 /*
00002  *  recurrenceedit.cpp  -  widget to edit the event's recurrence definition
00003  *  Program:  kalarm
00004  *  Copyright © 2002-2008 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  Based originally on KOrganizer module koeditorrecurrence.cpp,
00007  *  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
00008  *
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License along
00020  *  with this program; if not, write to the Free Software Foundation, Inc.,
00021  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022  */
00023 
00024 #include "kalarm.h"
00025 
00026 #include <tqtooltip.h>
00027 #include <tqlayout.h>
00028 #include <tqvbox.h>
00029 #include <tqwidgetstack.h>
00030 #include <tqlistbox.h>
00031 #include <tqframe.h>
00032 #include <tqlabel.h>
00033 #include <tqpushbutton.h>
00034 #include <tqlineedit.h>
00035 #include <tqwhatsthis.h>
00036 
00037 #include <tdeglobal.h>
00038 #include <tdelocale.h>
00039 #include <kcalendarsystem.h>
00040 #include <kiconloader.h>
00041 #include <kdialog.h>
00042 #include <tdemessagebox.h>
00043 #include <kdebug.h>
00044 
00045 #include <libkcal/event.h>
00046 
00047 #include "alarmevent.h"
00048 #include "alarmtimewidget.h"
00049 #include "checkbox.h"
00050 #include "combobox.h"
00051 #include "dateedit.h"
00052 #include "functions.h"
00053 #include "kalarmapp.h"
00054 #include "karecurrence.h"
00055 #include "preferences.h"
00056 #include "radiobutton.h"
00057 #include "repetition.h"
00058 #include "spinbox.h"
00059 #include "timeedit.h"
00060 #include "timespinbox.h"
00061 #include "buttongroup.h"
00062 using namespace KCal;
00063 
00064 #include "recurrenceedit.moc"
00065 #include "recurrenceeditprivate.moc"
00066 
00067 // Collect these widget labels together to ensure consistent wording and
00068 // translations across different modules.
00069 TQString RecurrenceEdit::i18n_Norecur()           { return i18n("No recurrence"); }
00070 TQString RecurrenceEdit::i18n_NoRecur()           { return i18n("No Recurrence"); }
00071 TQString RecurrenceEdit::i18n_AtLogin()           { return i18n("At Login"); }
00072 TQString RecurrenceEdit::i18n_l_Atlogin()         { return i18n("At &login"); }
00073 TQString RecurrenceEdit::i18n_HourlyMinutely()    { return i18n("Hourly/Minutely"); }
00074 TQString RecurrenceEdit::i18n_u_HourlyMinutely()  { return i18n("Ho&urly/Minutely"); }
00075 TQString RecurrenceEdit::i18n_Daily()             { return i18n("Daily"); }
00076 TQString RecurrenceEdit::i18n_d_Daily()           { return i18n("&Daily"); }
00077 TQString RecurrenceEdit::i18n_Weekly()            { return i18n("Weekly"); }
00078 TQString RecurrenceEdit::i18n_w_Weekly()          { return i18n("&Weekly"); }
00079 TQString RecurrenceEdit::i18n_Monthly()           { return i18n("Monthly"); }
00080 TQString RecurrenceEdit::i18n_m_Monthly()         { return i18n("&Monthly"); }
00081 TQString RecurrenceEdit::i18n_Yearly()            { return i18n("Yearly"); }
00082 TQString RecurrenceEdit::i18n_y_Yearly()          { return i18n("&Yearly"); }
00083 
00084 
00085 RecurrenceEdit::RecurrenceEdit(bool readOnly, TQWidget* parent, const char* name)
00086     : TQFrame(parent, name),
00087       mRule(0),
00088       mRuleButtonType(INVALID_RECUR),
00089       mDailyShown(false),
00090       mWeeklyShown(false),
00091       mMonthlyShown(false),
00092       mYearlyShown(false),
00093       mNoEmitTypeChanged(true),
00094       mReadOnly(readOnly)
00095 {
00096     TQBoxLayout* layout;
00097     TQVBoxLayout* topLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
00098 
00099     /* Create the recurrence rule Group box which holds the recurrence period
00100      * selection buttons, and the weekly, monthly and yearly recurrence rule
00101      * frames which specify options individual to each of these distinct
00102      * sections of the recurrence rule. Each frame is made visible by the
00103      * selection of its corresponding radio button.
00104      */
00105 
00106     TQGroupBox* recurGroup = new TQGroupBox(1, Qt::Vertical, i18n("Recurrence Rule"), this, "recurGroup");
00107     topLayout->addWidget(recurGroup);
00108     TQFrame* ruleFrame = new TQFrame(recurGroup, "ruleFrame");
00109     layout = new TQVBoxLayout(ruleFrame, 0);
00110     layout->addSpacing(KDialog::spacingHint()/2);
00111 
00112     layout = new TQHBoxLayout(layout, 0);
00113     TQBoxLayout* lay = new TQVBoxLayout(layout, 0);
00114     mRuleButtonGroup = new ButtonGroup(1, Qt::Horizontal, ruleFrame);
00115     mRuleButtonGroup->setInsideMargin(0);
00116     mRuleButtonGroup->setFrameStyle(TQFrame::NoFrame);
00117     lay->addWidget(mRuleButtonGroup);
00118     lay->addStretch();    // top-adjust the interval radio buttons
00119     connect(mRuleButtonGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(periodClicked(int)));
00120 
00121     mNoneButton = new RadioButton(i18n_Norecur(), mRuleButtonGroup);
00122     mNoneButton->setFixedSize(mNoneButton->sizeHint());
00123     mNoneButton->setReadOnly(mReadOnly);
00124     TQWhatsThis::add(mNoneButton, i18n("Do not repeat the alarm"));
00125 
00126     mAtLoginButton = new RadioButton(i18n_l_Atlogin(), mRuleButtonGroup);
00127     mAtLoginButton->setFixedSize(mAtLoginButton->sizeHint());
00128     mAtLoginButton->setReadOnly(mReadOnly);
00129     TQWhatsThis::add(mAtLoginButton,
00130           i18n("Trigger the alarm at the specified date/time and at every login until then.\n"
00131                "Note that it will also be triggered any time the alarm daemon is restarted."));
00132 
00133     mSubDailyButton = new RadioButton(i18n_u_HourlyMinutely(), mRuleButtonGroup);
00134     mSubDailyButton->setFixedSize(mSubDailyButton->sizeHint());
00135     mSubDailyButton->setReadOnly(mReadOnly);
00136     TQWhatsThis::add(mSubDailyButton,
00137           i18n("Repeat the alarm at hourly/minutely intervals"));
00138 
00139     mDailyButton = new RadioButton(i18n_d_Daily(), mRuleButtonGroup);
00140     mDailyButton->setFixedSize(mDailyButton->sizeHint());
00141     mDailyButton->setReadOnly(mReadOnly);
00142     TQWhatsThis::add(mDailyButton,
00143           i18n("Repeat the alarm at daily intervals"));
00144 
00145     mWeeklyButton = new RadioButton(i18n_w_Weekly(), mRuleButtonGroup);
00146     mWeeklyButton->setFixedSize(mWeeklyButton->sizeHint());
00147     mWeeklyButton->setReadOnly(mReadOnly);
00148     TQWhatsThis::add(mWeeklyButton,
00149           i18n("Repeat the alarm at weekly intervals"));
00150 
00151     mMonthlyButton = new RadioButton(i18n_m_Monthly(), mRuleButtonGroup);
00152     mMonthlyButton->setFixedSize(mMonthlyButton->sizeHint());
00153     mMonthlyButton->setReadOnly(mReadOnly);
00154     TQWhatsThis::add(mMonthlyButton,
00155           i18n("Repeat the alarm at monthly intervals"));
00156 
00157     mYearlyButton = new RadioButton(i18n_y_Yearly(), mRuleButtonGroup);
00158     mYearlyButton->setFixedSize(mYearlyButton->sizeHint());
00159     mYearlyButton->setReadOnly(mReadOnly);
00160     TQWhatsThis::add(mYearlyButton,
00161           i18n("Repeat the alarm at annual intervals"));
00162 
00163     mNoneButtonId     = mRuleButtonGroup->id(mNoneButton);
00164     mAtLoginButtonId  = mRuleButtonGroup->id(mAtLoginButton);
00165     mSubDailyButtonId = mRuleButtonGroup->id(mSubDailyButton);
00166     mDailyButtonId    = mRuleButtonGroup->id(mDailyButton);
00167     mWeeklyButtonId   = mRuleButtonGroup->id(mWeeklyButton);
00168     mMonthlyButtonId  = mRuleButtonGroup->id(mMonthlyButton);
00169     mYearlyButtonId   = mRuleButtonGroup->id(mYearlyButton);
00170 
00171     // Sub-repetition button
00172     mSubRepetition = new RepetitionButton(i18n("Sub-Repetition"), true, ruleFrame);
00173     mSubRepetition->setFixedSize(mSubRepetition->sizeHint());
00174     mSubRepetition->setReadOnly(mReadOnly);
00175     connect(mSubRepetition, TQT_SIGNAL(needsInitialisation()), TQT_SIGNAL(repeatNeedsInitialisation()));
00176     connect(mSubRepetition, TQT_SIGNAL(changed()), TQT_SIGNAL(frequencyChanged()));
00177     TQWhatsThis::add(mSubRepetition, i18n("Set up a repetition within the recurrence, to trigger the alarm multiple times each time the recurrence is due."));
00178     lay->addSpacing(KDialog::spacingHint());
00179     lay->addWidget(mSubRepetition);
00180 
00181     lay = new TQVBoxLayout(layout);
00182 
00183     lay->addStretch();
00184     layout = new TQHBoxLayout(lay);
00185 
00186     layout->addSpacing(KDialog::marginHint());
00187     TQFrame* divider = new TQFrame(ruleFrame);
00188     divider->setFrameStyle(TQFrame::VLine | TQFrame::Sunken);
00189     layout->addWidget(divider);
00190     layout->addSpacing(KDialog::marginHint());
00191 
00192     mNoRule       = new NoRule(ruleFrame, "noFrame");
00193     mSubDailyRule = new SubDailyRule(mReadOnly, ruleFrame, "subdayFrame");
00194     mDailyRule    = new DailyRule(mReadOnly, ruleFrame, "dayFrame");
00195     mWeeklyRule   = new WeeklyRule(mReadOnly, ruleFrame, "weekFrame");
00196     mMonthlyRule  = new MonthlyRule(mReadOnly, ruleFrame, "monthFrame");
00197     mYearlyRule   = new YearlyRule(mReadOnly, ruleFrame, "yearFrame");
00198 
00199     connect(mSubDailyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
00200     connect(mDailyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
00201     connect(mWeeklyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
00202     connect(mMonthlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
00203     connect(mYearlyRule, TQT_SIGNAL(frequencyChanged()), this, TQT_SIGNAL(frequencyChanged()));
00204 
00205     mRuleStack = new TQWidgetStack(ruleFrame);
00206     layout->addWidget(mRuleStack);
00207     layout->addStretch(1);
00208     mRuleStack->addWidget(mNoRule, 0);
00209     mRuleStack->addWidget(mSubDailyRule, 1);
00210     mRuleStack->addWidget(mDailyRule, 2);
00211     mRuleStack->addWidget(mWeeklyRule, 3);
00212     mRuleStack->addWidget(mMonthlyRule, 4);
00213     mRuleStack->addWidget(mYearlyRule, 5);
00214     layout->addSpacing(KDialog::marginHint());
00215 
00216     // Create the recurrence range group which contains the controls
00217     // which specify how long the recurrence is to last.
00218 
00219     mRangeButtonGroup = new ButtonGroup(i18n("Recurrence End"), this, "mRangeButtonGroup");
00220     connect(mRangeButtonGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(rangeTypeClicked()));
00221     topLayout->addWidget(mRangeButtonGroup);
00222 
00223     TQVBoxLayout* vlayout = new TQVBoxLayout(mRangeButtonGroup, KDialog::marginHint(), KDialog::spacingHint());
00224     vlayout->addSpacing(fontMetrics().lineSpacing()/2);
00225     mNoEndDateButton = new RadioButton(i18n("No &end"), mRangeButtonGroup);
00226     mNoEndDateButton->setFixedSize(mNoEndDateButton->sizeHint());
00227     mNoEndDateButton->setReadOnly(mReadOnly);
00228     TQWhatsThis::add(mNoEndDateButton, i18n("Repeat the alarm indefinitely"));
00229     vlayout->addWidget(mNoEndDateButton, 1, TQt::AlignAuto);
00230     TQSize size = mNoEndDateButton->size();
00231 
00232     layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
00233     mRepeatCountButton = new RadioButton(i18n("End a&fter:"), mRangeButtonGroup);
00234     mRepeatCountButton->setReadOnly(mReadOnly);
00235     TQWhatsThis::add(mRepeatCountButton,
00236           i18n("Repeat the alarm for the number of times specified"));
00237     mRepeatCountEntry = new SpinBox(1, 9999, 1, mRangeButtonGroup);
00238     mRepeatCountEntry->setFixedSize(mRepeatCountEntry->sizeHint());
00239     mRepeatCountEntry->setLineShiftStep(10);
00240     mRepeatCountEntry->setSelectOnStep(false);
00241     mRepeatCountEntry->setReadOnly(mReadOnly);
00242     connect(mRepeatCountEntry, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(repeatCountChanged(int)));
00243     TQWhatsThis::add(mRepeatCountEntry,
00244           i18n("Enter the total number of times to trigger the alarm"));
00245     mRepeatCountButton->setFocusWidget(mRepeatCountEntry);
00246     mRepeatCountLabel = new TQLabel(i18n("occurrence(s)"), mRangeButtonGroup);
00247     mRepeatCountLabel->setFixedSize(mRepeatCountLabel->sizeHint());
00248     layout->addWidget(mRepeatCountButton);
00249     layout->addSpacing(KDialog::spacingHint());
00250     layout->addWidget(mRepeatCountEntry);
00251     layout->addWidget(mRepeatCountLabel);
00252     layout->addStretch();
00253     size = size.expandedTo(mRepeatCountButton->sizeHint());
00254 
00255     layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
00256     mEndDateButton = new RadioButton(i18n("End &by:"), mRangeButtonGroup);
00257     mEndDateButton->setReadOnly(mReadOnly);
00258     TQWhatsThis::add(mEndDateButton,
00259           i18n("Repeat the alarm until the date/time specified.\n\n"
00260                "Note: This applies to the main recurrence only. It does not limit any sub-repetition which will occur regardless after the last main recurrence."));
00261     mEndDateEdit = new DateEdit(mRangeButtonGroup);
00262     mEndDateEdit->setFixedSize(mEndDateEdit->sizeHint());
00263     mEndDateEdit->setReadOnly(mReadOnly);
00264     TQWhatsThis::add(mEndDateEdit,
00265           i18n("Enter the last date to repeat the alarm"));
00266     mEndDateButton->setFocusWidget(mEndDateEdit);
00267     mEndTimeEdit = new TimeEdit(mRangeButtonGroup);
00268     mEndTimeEdit->setFixedSize(mEndTimeEdit->sizeHint());
00269     mEndTimeEdit->setReadOnly(mReadOnly);
00270     static const TQString lastTimeText = i18n("Enter the last time to repeat the alarm.");
00271     TQWhatsThis::add(mEndTimeEdit, TQString("%1\n\n%2").arg(lastTimeText).arg(TimeSpinBox::shiftWhatsThis()));
00272     mEndAnyTimeCheckBox = new CheckBox(i18n("Any time"), mRangeButtonGroup);
00273     mEndAnyTimeCheckBox->setFixedSize(mEndAnyTimeCheckBox->sizeHint());
00274     mEndAnyTimeCheckBox->setReadOnly(mReadOnly);
00275     connect(mEndAnyTimeCheckBox, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotAnyTimeToggled(bool)));
00276     TQWhatsThis::add(mEndAnyTimeCheckBox,
00277           i18n("Stop repeating the alarm after your first login on or after the specified end date"));
00278     layout->addWidget(mEndDateButton);
00279     layout->addSpacing(KDialog::spacingHint());
00280     layout->addWidget(mEndDateEdit);
00281     layout->addWidget(mEndTimeEdit);
00282     layout->addWidget(mEndAnyTimeCheckBox);
00283     layout->addStretch();
00284     size = size.expandedTo(mEndDateButton->sizeHint());
00285 
00286     // Line up the widgets to the right of the radio buttons
00287     mRepeatCountButton->setFixedSize(size);
00288     mEndDateButton->setFixedSize(size);
00289 
00290     // Create the exceptions group which specifies dates to be excluded
00291     // from the recurrence.
00292 
00293     mExceptionGroup = new TQGroupBox(i18n("E&xceptions"), this, "mExceptionGroup");
00294     topLayout->addWidget(mExceptionGroup);
00295     topLayout->setStretchFactor(mExceptionGroup, 2);
00296     vlayout = new TQVBoxLayout(mExceptionGroup, KDialog::marginHint(), KDialog::spacingHint());
00297     vlayout->addSpacing(fontMetrics().lineSpacing()/2);
00298     layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
00299     vlayout = new TQVBoxLayout(layout);
00300 
00301     mExceptionDateList = new TQListBox(mExceptionGroup);
00302     mExceptionDateList->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding));
00303     connect(mExceptionDateList, TQT_SIGNAL(selectionChanged()), TQT_SLOT(enableExceptionButtons()));
00304     TQWhatsThis::add(mExceptionDateList,
00305           i18n("The list of exceptions, i.e. dates/times excluded from the recurrence"));
00306     vlayout->addWidget(mExceptionDateList);
00307 
00308     if (mReadOnly)
00309     {
00310         mExceptionDateEdit     = 0;
00311         mChangeExceptionButton = 0;
00312         mDeleteExceptionButton = 0;
00313     }
00314     else
00315     {
00316         vlayout = new TQVBoxLayout(layout);
00317         mExceptionDateEdit = new DateEdit(mExceptionGroup);
00318         mExceptionDateEdit->setFixedSize(mExceptionDateEdit->sizeHint());
00319         mExceptionDateEdit->setDate(TQDate::currentDate());
00320         TQWhatsThis::add(mExceptionDateEdit,
00321               i18n("Enter a date to insert in the exceptions list. "
00322                    "Use in conjunction with the Add or Change button below."));
00323         vlayout->addWidget(mExceptionDateEdit);
00324 
00325         layout = new TQHBoxLayout(vlayout, KDialog::spacingHint());
00326         TQPushButton* button = new TQPushButton(i18n("Add"), mExceptionGroup);
00327         button->setFixedSize(button->sizeHint());
00328         connect(button, TQT_SIGNAL(clicked()), TQT_SLOT(addException()));
00329         TQWhatsThis::add(button,
00330               i18n("Add the date entered above to the exceptions list"));
00331         layout->addWidget(button);
00332 
00333         mChangeExceptionButton = new TQPushButton(i18n("Change"), mExceptionGroup);
00334         mChangeExceptionButton->setFixedSize(mChangeExceptionButton->sizeHint());
00335         connect(mChangeExceptionButton, TQT_SIGNAL(clicked()), TQT_SLOT(changeException()));
00336         TQWhatsThis::add(mChangeExceptionButton,
00337               i18n("Replace the currently highlighted item in the exceptions list with the date entered above"));
00338         layout->addWidget(mChangeExceptionButton);
00339 
00340         mDeleteExceptionButton = new TQPushButton(i18n("Delete"), mExceptionGroup);
00341         mDeleteExceptionButton->setFixedSize(mDeleteExceptionButton->sizeHint());
00342         connect(mDeleteExceptionButton, TQT_SIGNAL(clicked()), TQT_SLOT(deleteException()));
00343         TQWhatsThis::add(mDeleteExceptionButton,
00344               i18n("Remove the currently highlighted item from the exceptions list"));
00345         layout->addWidget(mDeleteExceptionButton);
00346     }
00347 
00348     mNoEmitTypeChanged = false;
00349 }
00350 
00351 /******************************************************************************
00352  * Verify the consistency of the entered data.
00353  * Reply = widget to receive focus on error, or 0 if no error.
00354  */
00355 TQWidget* RecurrenceEdit::checkData(const TQDateTime& startDateTime, TQString& errorMessage) const
00356 {
00357     if (mAtLoginButton->isOn())
00358         return 0;
00359     const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime;
00360     if (mEndDateButton->isChecked())
00361     {
00362         TQWidget* errWidget = 0;
00363         bool noTime = !mEndTimeEdit->isEnabled();
00364         TQDate endDate = mEndDateEdit->date();
00365         if (endDate < startDateTime.date())
00366             errWidget = mEndDateEdit;
00367         else if (!noTime  &&  TQDateTime(endDate, mEndTimeEdit->time()) < startDateTime)
00368             errWidget = mEndTimeEdit;
00369         if (errWidget)
00370         {
00371             errorMessage = noTime
00372                          ? i18n("End date is earlier than start date")
00373                          : i18n("End date/time is earlier than start date/time");
00374             return errWidget;
00375         }
00376     }
00377     if (!mRule)
00378         return 0;
00379     return mRule->validate(errorMessage);
00380 }
00381 
00382 /******************************************************************************
00383  * Called when a recurrence period radio button is clicked.
00384  */
00385 void RecurrenceEdit::periodClicked(int id)
00386 {
00387     RepeatType oldType = mRuleButtonType;
00388     bool none     = (id == mNoneButtonId);
00389     bool atLogin  = (id == mAtLoginButtonId);
00390     bool subdaily = (id == mSubDailyButtonId);
00391     if (none)
00392     {
00393         mRule = 0;
00394         mRuleButtonType = NO_RECUR;
00395     }
00396     else if (atLogin)
00397     {
00398         mRule = 0;
00399         mRuleButtonType = AT_LOGIN;
00400         mRangeButtonGroup->setButton(mRangeButtonGroup->id(mEndDateButton));
00401     }
00402     else if (subdaily)
00403     {
00404         mRule = mSubDailyRule;
00405         mRuleButtonType = SUBDAILY;
00406     }
00407     else if (id == mDailyButtonId)
00408     {
00409         mRule = mDailyRule;
00410         mRuleButtonType = DAILY;
00411         mDailyShown = true;
00412     }
00413     else if (id == mWeeklyButtonId)
00414     {
00415         mRule = mWeeklyRule;
00416         mRuleButtonType = WEEKLY;
00417         mWeeklyShown = true;
00418     }
00419     else if (id == mMonthlyButtonId)
00420     {
00421         mRule = mMonthlyRule;
00422         mRuleButtonType = MONTHLY;
00423         mMonthlyShown = true;
00424     }
00425     else if (id == mYearlyButtonId)
00426     {
00427         mRule = mYearlyRule;
00428         mRuleButtonType = ANNUAL;
00429         mYearlyShown = true;
00430     }
00431     else
00432         return;
00433 
00434     if (mRuleButtonType != oldType)
00435     {
00436         mRuleStack->raiseWidget(mRule ? mRule : mNoRule);
00437         if (oldType == NO_RECUR  ||  none)
00438             mRangeButtonGroup->setEnabled(!none);
00439         mExceptionGroup->setEnabled(!(none || atLogin));
00440         mEndAnyTimeCheckBox->setEnabled(atLogin);
00441         if (!none)
00442         {
00443             mNoEndDateButton->setEnabled(!atLogin);
00444             mRepeatCountButton->setEnabled(!atLogin);
00445         }
00446         rangeTypeClicked();
00447         mSubRepetition->setEnabled(!(none || atLogin));
00448         if (!mNoEmitTypeChanged)
00449             emit typeChanged(mRuleButtonType);
00450     }
00451 }
00452 
00453 void RecurrenceEdit::slotAnyTimeToggled(bool on)
00454 {
00455     TQButton* button = mRuleButtonGroup->selected();
00456     mEndTimeEdit->setEnabled((button == mAtLoginButton && !on)
00457                          ||  (button == mSubDailyButton && mEndDateButton->isChecked()));
00458 }
00459 
00460 /******************************************************************************
00461  * Called when a recurrence range type radio button is clicked.
00462  */
00463 void RecurrenceEdit::rangeTypeClicked()
00464 {
00465     bool endDate = mEndDateButton->isOn();
00466     mEndDateEdit->setEnabled(endDate);
00467     mEndTimeEdit->setEnabled(endDate
00468                              &&  ((mAtLoginButton->isOn() && !mEndAnyTimeCheckBox->isChecked())
00469                                   ||  mSubDailyButton->isOn()));
00470     bool repeatCount = mRepeatCountButton->isOn();
00471     mRepeatCountEntry->setEnabled(repeatCount);
00472     mRepeatCountLabel->setEnabled(repeatCount);
00473 }
00474 
00475 void RecurrenceEdit::showEvent(TQShowEvent*)
00476 {
00477     if (mRule)
00478         mRule->setFrequencyFocus();
00479     else
00480         mRuleButtonGroup->selected()->setFocus();
00481     emit shown();
00482 }
00483 
00484  /******************************************************************************
00485 * Return the sub-repetition count within the recurrence, i.e. the number of
00486 * repetitions after the main recurrence.
00487 */
00488 int RecurrenceEdit::subRepeatCount(int* subRepeatInterval) const
00489 {
00490     int count = (mRuleButtonType >= SUBDAILY) ? mSubRepetition->count() : 0;
00491     if (subRepeatInterval)
00492         *subRepeatInterval = count ? mSubRepetition->interval() : 0;
00493     return count;
00494 }
00495 
00496 /******************************************************************************
00497 *  Called when the Sub-Repetition button has been pressed to display the
00498 *  sub-repetition dialog.
00499 *  Alarm repetition has the following restrictions:
00500 *  1) Not allowed for a repeat-at-login alarm
00501 *  2) For a date-only alarm, the repeat interval must be a whole number of days.
00502 *  3) The overall repeat duration must be less than the recurrence interval.
00503 */
00504 void RecurrenceEdit::setSubRepetition(int reminderMinutes, bool dateOnly)
00505 {
00506     int maxDuration;
00507     switch (mRuleButtonType)
00508     {
00509         case RecurrenceEdit::NO_RECUR:
00510         case RecurrenceEdit::AT_LOGIN:   // alarm repeat not allowed
00511             maxDuration = 0;
00512             break;
00513         default:          // repeat duration must be less than recurrence interval
00514         {
00515             KAEvent event;
00516             updateEvent(event, false);
00517             maxDuration = event.longestRecurrenceInterval() - reminderMinutes - 1;
00518             break;
00519         }
00520     }
00521     mSubRepetition->initialise(mSubRepetition->interval(), mSubRepetition->count(), dateOnly, maxDuration);
00522     mSubRepetition->setEnabled(mRuleButtonType >= SUBDAILY && maxDuration);
00523 }
00524 
00525 /******************************************************************************
00526 * Activate the sub-repetition dialog.
00527 */
00528 void RecurrenceEdit::activateSubRepetition()
00529 {
00530     mSubRepetition->activate();
00531 }
00532 
00533 /******************************************************************************
00534  * Called when the value of the repeat count field changes, to reset the
00535  * minimum value to 1 if the value was 0.
00536  */
00537 void RecurrenceEdit::repeatCountChanged(int value)
00538 {
00539     if (value > 0  &&  mRepeatCountEntry->minValue() == 0)
00540         mRepeatCountEntry->setMinValue(1);
00541 }
00542 
00543 /******************************************************************************
00544  * Add the date entered in the exception date edit control to the list of
00545  * exception dates.
00546  */
00547 void RecurrenceEdit::addException()
00548 {
00549     if (!mExceptionDateEdit  ||  !mExceptionDateEdit->isValid())
00550         return;
00551     TQDate date = mExceptionDateEdit->date();
00552     TQValueList<TQDate>::Iterator it;
00553     int index = 0;
00554     bool insert = true;
00555     for (it = mExceptionDates.begin();  it != mExceptionDates.end();  ++index, ++it)
00556     {
00557         if (date <= *it)
00558         {
00559             insert = (date != *it);
00560             break;
00561         }
00562     }
00563     if (insert)
00564     {
00565         mExceptionDates.insert(it, date);
00566         mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(date), index);
00567     }
00568     mExceptionDateList->setCurrentItem(index);
00569     enableExceptionButtons();
00570 }
00571 
00572 /******************************************************************************
00573  * Change the currently highlighted exception date to that entered in the
00574  * exception date edit control.
00575  */
00576 void RecurrenceEdit::changeException()
00577 {
00578     if (!mExceptionDateEdit  ||  !mExceptionDateEdit->isValid())
00579         return;
00580     int index = mExceptionDateList->currentItem();
00581     if (index >= 0  &&  mExceptionDateList->isSelected(index))
00582     {
00583         TQDate olddate = mExceptionDates[index];
00584         TQDate newdate = mExceptionDateEdit->date();
00585         if (newdate != olddate)
00586         {
00587             mExceptionDates.remove(mExceptionDates.at(index));
00588             mExceptionDateList->removeItem(index);
00589             addException();
00590         }
00591     }
00592 }
00593 
00594 /******************************************************************************
00595  * Delete the currently highlighted exception date.
00596  */
00597 void RecurrenceEdit::deleteException()
00598 {
00599     int index = mExceptionDateList->currentItem();
00600     if (index >= 0  &&  mExceptionDateList->isSelected(index))
00601     {
00602         mExceptionDates.remove(mExceptionDates.at(index));
00603         mExceptionDateList->removeItem(index);
00604         enableExceptionButtons();
00605     }
00606 }
00607 
00608 /******************************************************************************
00609  * Enable/disable the exception group buttons according to whether any item is
00610  * selected in the exceptions listbox.
00611  */
00612 void RecurrenceEdit::enableExceptionButtons()
00613 {
00614     int index = mExceptionDateList->currentItem();
00615     bool enable = (index >= 0  &&  mExceptionDateList->isSelected(index));
00616     if (mDeleteExceptionButton)
00617         mDeleteExceptionButton->setEnabled(enable);
00618     if (mChangeExceptionButton)
00619         mChangeExceptionButton->setEnabled(enable);
00620 
00621     // Prevent the exceptions list box receiving keyboard focus is it's empty
00622     mExceptionDateList->setFocusPolicy(mExceptionDateList->count() ? TQ_WheelFocus : TQ_NoFocus);
00623 }
00624 
00625 /******************************************************************************
00626  * Notify this instance of a change in the alarm start date.
00627  */
00628 void RecurrenceEdit::setStartDate(const TQDate& start, const TQDate& today)
00629 {
00630     if (!mReadOnly)
00631     {
00632         setRuleDefaults(start);
00633         if (start < today)
00634         {
00635             mEndDateEdit->setMinDate(today);
00636             if (mExceptionDateEdit)
00637                 mExceptionDateEdit->setMinDate(today);
00638         }
00639         else
00640         {
00641             const TQString startString = i18n("Date cannot be earlier than start date", "start date");
00642             mEndDateEdit->setMinDate(start, startString);
00643             if (mExceptionDateEdit)
00644                 mExceptionDateEdit->setMinDate(start, startString);
00645         }
00646     }
00647 }
00648 
00649 /******************************************************************************
00650  * Specify the default recurrence end date.
00651  */
00652 void RecurrenceEdit::setDefaultEndDate(const TQDate& end)
00653 {
00654     if (!mEndDateButton->isOn())
00655         mEndDateEdit->setDate(end);
00656 }
00657 
00658 void RecurrenceEdit::setEndDateTime(const DateTime& end)
00659 {
00660     mEndDateEdit->setDate(end.date());
00661     mEndTimeEdit->setValue(end.time());
00662     mEndTimeEdit->setEnabled(!end.isDateOnly());
00663     mEndAnyTimeCheckBox->setChecked(end.isDateOnly());
00664 }
00665 
00666 DateTime RecurrenceEdit::endDateTime() const
00667 {
00668     if (mRuleButtonGroup->selected() == mAtLoginButton  &&  mEndAnyTimeCheckBox->isChecked())
00669         return DateTime(mEndDateEdit->date());
00670     return DateTime(mEndDateEdit->date(), mEndTimeEdit->time());
00671 }
00672 
00673 /******************************************************************************
00674  * Set all controls to their default values.
00675  */
00676 void RecurrenceEdit::setDefaults(const TQDateTime& from)
00677 {
00678     mCurrStartDateTime = from;
00679     TQDate fromDate = from.date();
00680     mNoEndDateButton->setChecked(true);
00681 
00682     mSubDailyRule->setFrequency(1);
00683     mDailyRule->setFrequency(1);
00684     mWeeklyRule->setFrequency(1);
00685     mMonthlyRule->setFrequency(1);
00686     mYearlyRule->setFrequency(1);
00687 
00688     setRuleDefaults(fromDate);
00689     mMonthlyRule->setType(MonthYearRule::DATE);   // date in month
00690     mYearlyRule->setType(MonthYearRule::DATE);    // date in year
00691 
00692     mEndDateEdit->setDate(fromDate);
00693 
00694     mNoEmitTypeChanged = true;
00695     int button;
00696     switch (Preferences::defaultRecurPeriod())
00697     {
00698         case AT_LOGIN: button = mAtLoginButtonId;  break;
00699         case ANNUAL:   button = mYearlyButtonId;   break;
00700         case MONTHLY:  button = mMonthlyButtonId;  break;
00701         case WEEKLY:   button = mWeeklyButtonId;   break;
00702         case DAILY:    button = mDailyButtonId;    break;
00703         case SUBDAILY: button = mSubDailyButtonId; break;
00704         case NO_RECUR:
00705         default:       button = mNoneButtonId;     break;
00706     }
00707     mRuleButtonGroup->setButton(button);
00708     mNoEmitTypeChanged = false;
00709     rangeTypeClicked();
00710     enableExceptionButtons();
00711 
00712     saveState();
00713 }
00714 
00715 /******************************************************************************
00716  * Set the controls for weekly, monthly and yearly rules which have not so far
00717  * been shown, to their default values, depending on the recurrence start date.
00718  */
00719 void RecurrenceEdit::setRuleDefaults(const TQDate& fromDate)
00720 {
00721     int day       = fromDate.day();
00722     int dayOfWeek = fromDate.dayOfWeek();
00723     int month     = fromDate.month();
00724     if (!mDailyShown)
00725         mDailyRule->setDays(true);
00726     if (!mWeeklyShown)
00727         mWeeklyRule->setDay(dayOfWeek);
00728     if (!mMonthlyShown)
00729         mMonthlyRule->setDefaultValues(day, dayOfWeek);
00730     if (!mYearlyShown)
00731         mYearlyRule->setDefaultValues(day, dayOfWeek, month);
00732 }
00733 
00734 /******************************************************************************
00735 * Set the state of all controls to reflect the data in the specified event.
00736 * Set 'keepDuration' true to prevent the recurrence count being adjusted to the
00737 * remaining number of recurrences.
00738 */
00739 void RecurrenceEdit::set(const KAEvent& event, bool keepDuration)
00740 {
00741     setDefaults(event.mainDateTime().dateTime());
00742     if (event.repeatAtLogin())
00743     {
00744         mRuleButtonGroup->setButton(mAtLoginButtonId);
00745         mEndDateButton->setChecked(true);
00746         return;
00747     }
00748     mRuleButtonGroup->setButton(mNoneButtonId);
00749     KARecurrence* recurrence = event.recurrence();
00750     if (!recurrence)
00751         return;
00752     KARecurrence::Type rtype = recurrence->type();
00753     switch (rtype)
00754     {
00755         case KARecurrence::MINUTELY:
00756             mRuleButtonGroup->setButton(mSubDailyButtonId);
00757             break;
00758 
00759         case KARecurrence::DAILY:
00760         {
00761             mRuleButtonGroup->setButton(mDailyButtonId);
00762             TQBitArray rDays = recurrence->days();
00763             bool set = false;
00764             for (int i = 0;  i < 7 && !set;  ++i)
00765                 set = rDays.testBit(i);
00766             if (set)
00767                 mDailyRule->setDays(rDays);
00768             else
00769                 mDailyRule->setDays(true);
00770             break;
00771         }
00772         case KARecurrence::WEEKLY:
00773         {
00774             mRuleButtonGroup->setButton(mWeeklyButtonId);
00775             TQBitArray rDays = recurrence->days();
00776             mWeeklyRule->setDays(rDays);
00777             break;
00778         }
00779         case KARecurrence::MONTHLY_POS:    // on nth (Tuesday) of the month
00780         {
00781             TQValueList<RecurrenceRule::WDayPos> posns = recurrence->monthPositions();
00782             int i = posns.first().pos();
00783             if (!i)
00784             {
00785                 // It's every (Tuesday) of the month. Convert to a weekly recurrence
00786                 // (but ignoring any non-every xxxDay positions).
00787                 mRuleButtonGroup->setButton(mWeeklyButtonId);
00788                 mWeeklyRule->setFrequency(recurrence->frequency());
00789                 TQBitArray rDays(7);
00790                 for (TQValueList<RecurrenceRule::WDayPos>::ConstIterator it = posns.begin();  it != posns.end();  ++it)
00791                 {
00792                     if (!(*it).pos())
00793                         rDays.setBit((*it).day() - 1, 1);
00794                 }
00795                 mWeeklyRule->setDays(rDays);
00796                 break;
00797             }
00798             mRuleButtonGroup->setButton(mMonthlyButtonId);
00799             mMonthlyRule->setPosition(i, posns.first().day());
00800             break;
00801         }
00802         case KARecurrence::MONTHLY_DAY:     // on nth day of the month
00803         {
00804             mRuleButtonGroup->setButton(mMonthlyButtonId);
00805             TQValueList<int> rmd = recurrence->monthDays();
00806             int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
00807             mMonthlyRule->setDate(day);
00808             break;
00809         }
00810         case KARecurrence::ANNUAL_DATE:   // on the nth day of (months...) in the year
00811         case KARecurrence::ANNUAL_POS:     // on the nth (Tuesday) of (months...) in the year
00812         {
00813             if (rtype == KARecurrence::ANNUAL_DATE)
00814             {
00815                 mRuleButtonGroup->setButton(mYearlyButtonId);
00816                 const TQValueList<int> rmd = recurrence->monthDays();
00817                 int day = (rmd.isEmpty()) ? event.mainDate().day() : rmd.first();
00818                 mYearlyRule->setDate(day);
00819                 mYearlyRule->setFeb29Type(recurrence->feb29Type());
00820             }
00821             else if (rtype == KARecurrence::ANNUAL_POS)
00822             {
00823                 mRuleButtonGroup->setButton(mYearlyButtonId);
00824                 TQValueList<RecurrenceRule::WDayPos> posns = recurrence->yearPositions();
00825                 mYearlyRule->setPosition(posns.first().pos(), posns.first().day());
00826             }
00827             mYearlyRule->setMonths(recurrence->yearMonths());
00828             break;
00829         }
00830         default:
00831             return;
00832     }
00833 
00834     mRule->setFrequency(recurrence->frequency());
00835 
00836     // Get range information
00837     TQDateTime endtime = mCurrStartDateTime;
00838     int duration = recurrence->duration();
00839     if (duration == -1)
00840         mNoEndDateButton->setChecked(true);
00841     else if (duration)
00842     {
00843         mRepeatCountButton->setChecked(true);
00844         mRepeatCountEntry->setValue(duration);
00845     }
00846     else
00847     {
00848         mEndDateButton->setChecked(true);
00849         endtime = recurrence->endDateTime();
00850         mEndTimeEdit->setValue(endtime.time());
00851     }
00852     mEndDateEdit->setDate(endtime.date());
00853 
00854     // Get exception information
00855     mExceptionDates = event.recurrence()->exDates();
00856     qHeapSort(mExceptionDates);
00857     mExceptionDateList->clear();
00858     for (DateList::ConstIterator it = mExceptionDates.begin();  it != mExceptionDates.end();  ++it)
00859         mExceptionDateList->insertItem(TDEGlobal::locale()->formatDate(*it));
00860     enableExceptionButtons();
00861 
00862     // Get repetition within recurrence
00863     mSubRepetition->set(event.repeatInterval(), event.repeatCount());
00864 
00865     rangeTypeClicked();
00866 
00867     saveState();
00868 }
00869 
00870 /******************************************************************************
00871  * Update the specified KAEvent with the entered recurrence data.
00872  * If 'adjustStart' is true, the start date/time will be adjusted if necessary
00873  * to be the first date/time which recurs on or after the original start.
00874  */
00875 void RecurrenceEdit::updateEvent(KAEvent& event, bool adjustStart)
00876 {
00877     // Get end date and repeat count, common to all types of recurring events
00878     TQDate  endDate;
00879     TQTime  endTime;
00880     int    repeatCount;
00881     if (mNoEndDateButton->isChecked())
00882         repeatCount = -1;
00883     else if (mRepeatCountButton->isChecked())
00884         repeatCount = mRepeatCountEntry->value();
00885     else
00886     {
00887         repeatCount = 0;
00888         endDate = mEndDateEdit->date();
00889         endTime = mEndTimeEdit->time();
00890     }
00891 
00892     // Set up the recurrence according to the type selected
00893     TQButton* button = mRuleButtonGroup->selected();
00894     event.setRepeatAtLogin(button == mAtLoginButton);
00895     int frequency = mRule ? mRule->frequency() : 0;
00896     if (button == mSubDailyButton)
00897     {
00898         TQDateTime endDateTime(endDate, endTime);
00899         event.setRecurMinutely(frequency, repeatCount, endDateTime);
00900     }
00901     else if (button == mDailyButton)
00902     {
00903         event.setRecurDaily(frequency, mDailyRule->days(), repeatCount, endDate);
00904     }
00905     else if (button == mWeeklyButton)
00906     {
00907         event.setRecurWeekly(frequency, mWeeklyRule->days(), repeatCount, endDate);
00908     }
00909     else if (button == mMonthlyButton)
00910     {
00911         if (mMonthlyRule->type() == MonthlyRule::POS)
00912         {
00913             // It's by position
00914             KAEvent::MonthPos pos;
00915             pos.days.fill(false);
00916             pos.days.setBit(mMonthlyRule->dayOfWeek() - 1);
00917             pos.weeknum = mMonthlyRule->week();
00918             TQValueList<KAEvent::MonthPos> poses;
00919             poses.append(pos);
00920             event.setRecurMonthlyByPos(frequency, poses, repeatCount, endDate);
00921         }
00922         else
00923         {
00924             // It's by day
00925             int daynum = mMonthlyRule->date();
00926             TQValueList<int> daynums;
00927             daynums.append(daynum);
00928             event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
00929         }
00930     }
00931     else if (button == mYearlyButton)
00932     {
00933         TQValueList<int> months = mYearlyRule->months();
00934         if (mYearlyRule->type() == YearlyRule::POS)
00935         {
00936             // It's by position
00937             KAEvent::MonthPos pos;
00938             pos.days.fill(false);
00939             pos.days.setBit(mYearlyRule->dayOfWeek() - 1);
00940             pos.weeknum = mYearlyRule->week();
00941             TQValueList<KAEvent::MonthPos> poses;
00942             poses.append(pos);
00943             event.setRecurAnnualByPos(frequency, poses, months, repeatCount, endDate);
00944         }
00945         else
00946         {
00947             // It's by date in month
00948             event.setRecurAnnualByDate(frequency, months, mYearlyRule->date(),
00949                                        mYearlyRule->feb29Type(), repeatCount, endDate);
00950         }
00951     }
00952     else
00953     {
00954         event.setNoRecur();
00955         return;
00956     }
00957     if (!event.recurs())
00958         return;    // an error occurred setting up the recurrence
00959     if (adjustStart)
00960         event.setFirstRecurrence();
00961 
00962     // Set up repetition within the recurrence.
00963     // N.B. This requires the main recurrence to be set up first.
00964     int count = mSubRepetition->count();
00965     if (mRuleButtonType < SUBDAILY)
00966         count = 0;
00967     event.setRepetition(mSubRepetition->interval(), count);
00968 
00969     // Set up exceptions
00970     event.recurrence()->setExDates(mExceptionDates);
00971 
00972     event.setUpdated();
00973 }
00974 
00975 /******************************************************************************
00976  * Save the state of all controls.
00977  */
00978 void RecurrenceEdit::saveState()
00979 {
00980     mSavedRuleButton = mRuleButtonGroup->selected();
00981     if (mRule)
00982         mRule->saveState();
00983     mSavedRangeButton = mRangeButtonGroup->selected();
00984     if (mSavedRangeButton == mRepeatCountButton)
00985         mSavedRecurCount = mRepeatCountEntry->value();
00986     else if (mSavedRangeButton == mEndDateButton)
00987         mSavedEndDateTime.set(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked());
00988     mSavedExceptionDates = mExceptionDates;
00989     mSavedRepeatInterval = mSubRepetition->interval();
00990     mSavedRepeatCount    = mSubRepetition->count();
00991 }
00992 
00993 /******************************************************************************
00994  * Check whether any of the controls have changed state since initialisation.
00995  */
00996 bool RecurrenceEdit::stateChanged() const
00997 {
00998     if (mSavedRuleButton  != mRuleButtonGroup->selected()
00999     ||  mSavedRangeButton != mRangeButtonGroup->selected()
01000     ||  (mRule  &&  mRule->stateChanged()))
01001         return true;
01002     if (mSavedRangeButton == mRepeatCountButton
01003     &&  mSavedRecurCount  != mRepeatCountEntry->value())
01004         return true;
01005     if (mSavedRangeButton == mEndDateButton
01006     &&  mSavedEndDateTime != DateTime(TQDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked()))
01007         return true;
01008     if (mSavedExceptionDates != mExceptionDates
01009     ||  mSavedRepeatInterval != mSubRepetition->interval()
01010     ||  mSavedRepeatCount    != mSubRepetition->count())
01011         return true;
01012     return false;
01013 }
01014 
01015 
01016 
01017 /*=============================================================================
01018 = Class Rule
01019 = Base class for rule widgets, including recurrence frequency.
01020 =============================================================================*/
01021 
01022 Rule::Rule(const TQString& freqText, const TQString& freqWhatsThis, bool time, bool readOnly, TQWidget* parent, const char* name)
01023     : NoRule(parent, name)
01024 {
01025     mLayout = new TQVBoxLayout(this, 0, KDialog::spacingHint());
01026     TQHBox* freqBox = new TQHBox(this);
01027     mLayout->addWidget(freqBox);
01028     TQHBox* box = new TQHBox(freqBox);    // this is to control the TQWhatsThis text display area
01029     box->setSpacing(KDialog::spacingHint());
01030 
01031     TQLabel* label = new TQLabel(i18n("Recur e&very"), box);
01032     label->setFixedSize(label->sizeHint());
01033     if (time)
01034     {
01035         mIntSpinBox = 0;
01036         mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
01037         mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
01038         mTimeSpinBox->setReadOnly(readOnly);
01039     }
01040     else
01041     {
01042         mTimeSpinBox = 0;
01043         mSpinBox = mIntSpinBox = new SpinBox(1, 999, 1, box);
01044         mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
01045         mIntSpinBox->setReadOnly(readOnly);
01046     }
01047     connect(mSpinBox, TQT_SIGNAL(valueChanged(int)), TQT_SIGNAL(frequencyChanged()));
01048     label->setBuddy(mSpinBox);
01049     label = new TQLabel(freqText, box);
01050     label->setFixedSize(label->sizeHint());
01051     box->setFixedSize(sizeHint());
01052     TQWhatsThis::add(box, freqWhatsThis);
01053 
01054     new TQWidget(freqBox);     // left adjust the visible widgets
01055     freqBox->setFixedHeight(freqBox->sizeHint().height());
01056     freqBox->setFocusProxy(mSpinBox);
01057 }
01058 
01059 int Rule::frequency() const
01060 {
01061     if (mIntSpinBox)
01062         return mIntSpinBox->value();
01063     if (mTimeSpinBox)
01064         return mTimeSpinBox->value();
01065     return 0;
01066 }
01067 
01068 void Rule::setFrequency(int n)
01069 {
01070     if (mIntSpinBox)
01071         mIntSpinBox->setValue(n);
01072     if (mTimeSpinBox)
01073         mTimeSpinBox->setValue(n);
01074 }
01075 
01076 /******************************************************************************
01077  * Save the state of all controls.
01078  */
01079 void Rule::saveState()
01080 {
01081     mSavedFrequency = frequency();
01082 }
01083 
01084 /******************************************************************************
01085  * Check whether any of the controls have changed state since initialisation.
01086  */
01087 bool Rule::stateChanged() const
01088 {
01089     return (mSavedFrequency != frequency());
01090 }
01091 
01092 
01093 /*=============================================================================
01094 = Class SubDailyRule
01095 = Sub-daily rule widget.
01096 =============================================================================*/
01097 
01098 SubDailyRule::SubDailyRule(bool readOnly, TQWidget* parent, const char* name)
01099     : Rule(i18n("hours:minutes"),
01100            i18n("Enter the number of hours and minutes between repetitions of the alarm"),
01101            true, readOnly, parent, name)
01102 { }
01103 
01104 
01105 /*=============================================================================
01106 = Class DayWeekRule
01107 = Daily/weekly rule widget base class.
01108 =============================================================================*/
01109 
01110 DayWeekRule::DayWeekRule(const TQString& freqText, const TQString& freqWhatsThis, const TQString& daysWhatsThis,
01111                          bool readOnly, TQWidget* parent, const char* name)
01112     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01113       mSavedDays(7)
01114 {
01115     TQGridLayout* grid = new TQGridLayout(layout(), 1, 4, KDialog::spacingHint());
01116     grid->setRowStretch(0, 1);
01117 
01118     TQLabel* label = new TQLabel(i18n("On: Tuesday", "O&n:"), this);
01119     label->setFixedSize(label->sizeHint());
01120     grid->addWidget(label, 0, 0, TQt::AlignRight | TQt::AlignTop);
01121     grid->addColSpacing(1, KDialog::spacingHint());
01122 
01123     // List the days of the week starting at the user's start day of the week.
01124     // Save the first day of the week, just in case it changes while the dialog is open.
01125     TQWidget* box = new TQWidget(this);   // this is to control the TQWhatsThis text display area
01126     TQGridLayout* dgrid = new TQGridLayout(box, 4, 2, 0, KDialog::spacingHint());
01127     const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
01128     for (int i = 0;  i < 7;  ++i)
01129     {
01130         int day = KAlarm::localeDayInWeek_to_weekDay(i);
01131         mDayBox[i] = new CheckBox(calendar->weekDayName(day), box);
01132         mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
01133         mDayBox[i]->setReadOnly(readOnly);
01134         dgrid->addWidget(mDayBox[i], i%4, i/4, TQt::AlignAuto);
01135     }
01136     box->setFixedSize(box->sizeHint());
01137     TQWhatsThis::add(box, daysWhatsThis);
01138     grid->addWidget(box, 0, 2, TQt::AlignAuto);
01139     label->setBuddy(mDayBox[0]);
01140     grid->setColStretch(3, 1);
01141 }
01142 
01143 /******************************************************************************
01144  * Fetch which days of the week have been ticked.
01145  */
01146 TQBitArray DayWeekRule::days() const
01147 {
01148     TQBitArray ds(7);
01149     ds.fill(false);
01150     for (int i = 0;  i < 7;  ++i)
01151         if (mDayBox[i]->isChecked())
01152             ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
01153     return ds;
01154 }
01155 
01156 /******************************************************************************
01157  * Tick/untick every day of the week.
01158  */
01159 void DayWeekRule::setDays(bool tick)
01160 {
01161     for (int i = 0;  i < 7;  ++i)
01162         mDayBox[i]->setChecked(tick);
01163 }
01164 
01165 /******************************************************************************
01166  * Tick/untick each day of the week according to the specified bits.
01167  */
01168 void DayWeekRule::setDays(const TQBitArray& days)
01169 {
01170     for (int i = 0;  i < 7;  ++i)
01171     {
01172         bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
01173         mDayBox[i]->setChecked(x);
01174     }
01175 }
01176 
01177 /******************************************************************************
01178  * Tick the specified day of the week, and untick all other days.
01179  */
01180 void DayWeekRule::setDay(int dayOfWeek)
01181 {
01182     for (int i = 0;  i < 7;  ++i)
01183         mDayBox[i]->setChecked(false);
01184     if (dayOfWeek > 0  &&  dayOfWeek <= 7)
01185         mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
01186 }
01187 
01188 /******************************************************************************
01189  * Validate: check that at least one day is selected.
01190  */
01191 TQWidget* DayWeekRule::validate(TQString& errorMessage)
01192 {
01193     for (int i = 0;  i < 7;  ++i)
01194         if (mDayBox[i]->isChecked())
01195             return 0;
01196     errorMessage = i18n("No day selected");
01197     return mDayBox[0];
01198 }
01199 
01200 /******************************************************************************
01201  * Save the state of all controls.
01202  */
01203 void DayWeekRule::saveState()
01204 {
01205     Rule::saveState();
01206     mSavedDays = days();
01207 }
01208 
01209 /******************************************************************************
01210  * Check whether any of the controls have changed state since initialisation.
01211  */
01212 bool DayWeekRule::stateChanged() const
01213 {
01214     return (Rule::stateChanged()
01215         ||  mSavedDays != days());
01216 }
01217 
01218 
01219 /*=============================================================================
01220 = Class DailyRule
01221 = Daily rule widget.
01222 =============================================================================*/
01223 
01224 DailyRule::DailyRule(bool readOnly, TQWidget* parent, const char* name)
01225     : DayWeekRule(i18n("day(s)"),
01226                   i18n("Enter the number of days between repetitions of the alarm"),
01227                   i18n("Select the days of the week on which the alarm is allowed to occur"),
01228                   readOnly, parent, name)
01229 { }
01230 
01231 
01232 /*=============================================================================
01233 = Class WeeklyRule
01234 = Weekly rule widget.
01235 =============================================================================*/
01236 
01237 WeeklyRule::WeeklyRule(bool readOnly, TQWidget* parent, const char* name)
01238     : DayWeekRule(i18n("week(s)"),
01239                   i18n("Enter the number of weeks between repetitions of the alarm"),
01240                   i18n("Select the days of the week on which to repeat the alarm"),
01241                   readOnly, parent, name)
01242 { }
01243 
01244 
01245 /*=============================================================================
01246 = Class MonthYearRule
01247 = Monthly/yearly rule widget base class.
01248 =============================================================================*/
01249 
01250 MonthYearRule::MonthYearRule(const TQString& freqText, const TQString& freqWhatsThis, bool allowEveryWeek,
01251                              bool readOnly, TQWidget* parent, const char* name)
01252     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01253       mEveryWeek(allowEveryWeek)
01254 {
01255     mButtonGroup = new ButtonGroup(this);
01256     mButtonGroup->hide();
01257 
01258     // Month day selector
01259     TQHBox* box = new TQHBox(this);
01260     box->setSpacing(KDialog::spacingHint());
01261     layout()->addWidget(box);
01262 
01263     mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
01264     mDayButton->setFixedSize(mDayButton->sizeHint());
01265     mDayButton->setReadOnly(readOnly);
01266     mDayButtonId = mButtonGroup->insert(mDayButton);
01267     TQWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
01268 
01269     mDayCombo = new ComboBox(false, box);
01270     mDayCombo->setSizeLimit(11);
01271     for (int i = 0;  i < 31;  ++i)
01272         mDayCombo->insertItem(TQString::number(i + 1));
01273     mDayCombo->insertItem(i18n("Last day of month", "Last"));
01274     mDayCombo->setFixedSize(mDayCombo->sizeHint());
01275     mDayCombo->setReadOnly(readOnly);
01276     TQWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
01277     mDayButton->setFocusWidget(mDayCombo);
01278     connect(mDayCombo, TQT_SIGNAL(activated(int)), TQT_SLOT(slotDaySelected(int)));
01279 
01280     box->setStretchFactor(new TQWidget(box), 1);    // left adjust the controls
01281     box->setFixedHeight(box->sizeHint().height());
01282 
01283     // Month position selector
01284     box = new TQHBox(this);
01285     box->setSpacing(KDialog::spacingHint());
01286     layout()->addWidget(box);
01287 
01288     mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
01289     mPosButton->setFixedSize(mPosButton->sizeHint());
01290     mPosButton->setReadOnly(readOnly);
01291     mPosButtonId = mButtonGroup->insert(mPosButton);
01292     TQWhatsThis::add(mPosButton,
01293           i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
01294 
01295     mWeekCombo = new ComboBox(false, box);
01296     mWeekCombo->insertItem(i18n("1st"));
01297     mWeekCombo->insertItem(i18n("2nd"));
01298     mWeekCombo->insertItem(i18n("3rd"));
01299     mWeekCombo->insertItem(i18n("4th"));
01300     mWeekCombo->insertItem(i18n("5th"));
01301     mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
01302     mWeekCombo->insertItem(i18n("2nd Last"));
01303     mWeekCombo->insertItem(i18n("3rd Last"));
01304     mWeekCombo->insertItem(i18n("4th Last"));
01305     mWeekCombo->insertItem(i18n("5th Last"));
01306     if (mEveryWeek)
01307     {
01308         mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
01309         mWeekCombo->setSizeLimit(11);
01310     }
01311     TQWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
01312     mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
01313     mWeekCombo->setReadOnly(readOnly);
01314     mPosButton->setFocusWidget(mWeekCombo);
01315 
01316     mDayOfWeekCombo = new ComboBox(false, box);
01317     const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
01318     for (int i = 0;  i < 7;  ++i)
01319     {
01320         int day = KAlarm::localeDayInWeek_to_weekDay(i);
01321         mDayOfWeekCombo->insertItem(calendar->weekDayName(day));
01322     }
01323     mDayOfWeekCombo->setReadOnly(readOnly);
01324     TQWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
01325 
01326     box->setStretchFactor(new TQWidget(box), 1);    // left adjust the controls
01327     box->setFixedHeight(box->sizeHint().height());
01328     connect(mButtonGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(clicked(int)));
01329 }
01330 
01331 MonthYearRule::DayPosType MonthYearRule::type() const
01332 {
01333     return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
01334 }
01335 
01336 void MonthYearRule::setType(MonthYearRule::DayPosType type)
01337 {
01338     mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
01339 }
01340 
01341 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
01342 {
01343     --dayOfMonth;
01344     mDayCombo->setCurrentItem(dayOfMonth);
01345     mWeekCombo->setCurrentItem(dayOfMonth / 7);
01346     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01347 }
01348 
01349 int MonthYearRule::date() const
01350 {
01351     int daynum  = mDayCombo->currentItem() + 1;
01352     return (daynum <= 31) ? daynum : 31 - daynum;
01353 }
01354 
01355 int MonthYearRule::week() const
01356 {
01357     int weeknum = mWeekCombo->currentItem() + 1;
01358     return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
01359 }
01360 
01361 int MonthYearRule::dayOfWeek() const
01362 {
01363     return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
01364 }
01365 
01366 void MonthYearRule::setDate(int dayOfMonth)
01367 {
01368     mButtonGroup->setButton(mDayButtonId);
01369     mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0);   // day 0 shouldn't ever occur
01370 }
01371 
01372 void MonthYearRule::setPosition(int week, int dayOfWeek)
01373 {
01374     mButtonGroup->setButton(mPosButtonId);
01375     mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
01376     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01377 }
01378 
01379 void MonthYearRule::enableSelection(DayPosType type)
01380 {
01381     bool date = (type == DATE);
01382     mDayCombo->setEnabled(date);
01383     mWeekCombo->setEnabled(!date);
01384     mDayOfWeekCombo->setEnabled(!date);
01385 }
01386 
01387 void MonthYearRule::clicked(int id)
01388 {
01389     enableSelection(id == mDayButtonId ? DATE : POS);
01390 }
01391 
01392 void MonthYearRule::slotDaySelected(int index)
01393 {
01394     daySelected(index <= 30 ? index + 1 : 30 - index);
01395 }
01396 
01397 /******************************************************************************
01398  * Save the state of all controls.
01399  */
01400 void MonthYearRule::saveState()
01401 {
01402     Rule::saveState();
01403     mSavedType = type();
01404     if (mSavedType == DATE)
01405         mSavedDay = date();
01406     else
01407     {
01408         mSavedWeek    = week();
01409         mSavedWeekDay = dayOfWeek();
01410     }
01411 }
01412 
01413 /******************************************************************************
01414  * Check whether any of the controls have changed state since initialisation.
01415  */
01416 bool MonthYearRule::stateChanged() const
01417 {
01418     if (Rule::stateChanged()
01419     ||  mSavedType != type())
01420         return true;
01421     if (mSavedType == DATE)
01422     {
01423         if (mSavedDay != date())
01424             return true;
01425     }
01426     else
01427     {
01428         if (mSavedWeek    != week()
01429         ||  mSavedWeekDay != dayOfWeek())
01430             return true;
01431     }
01432     return false;
01433 }
01434 
01435 
01436 /*=============================================================================
01437 = Class MonthlyRule
01438 = Monthly rule widget.
01439 =============================================================================*/
01440 
01441 MonthlyRule::MonthlyRule(bool readOnly, TQWidget* parent, const char* name)
01442     : MonthYearRule(i18n("month(s)"),
01443            i18n("Enter the number of months between repetitions of the alarm"),
01444            false, readOnly, parent, name)
01445 { }
01446 
01447 
01448 /*=============================================================================
01449 = Class YearlyRule
01450 = Yearly rule widget.
01451 =============================================================================*/
01452 
01453 YearlyRule::YearlyRule(bool readOnly, TQWidget* parent, const char* name)
01454     : MonthYearRule(i18n("year(s)"),
01455            i18n("Enter the number of years between repetitions of the alarm"),
01456            true, readOnly, parent, name)
01457 {
01458     // Set up the month selection widgets
01459     TQBoxLayout* hlayout = new TQHBoxLayout(layout(), KDialog::spacingHint());
01460     TQLabel* label = new TQLabel(i18n("List of months to select", "Months:"), this);
01461     label->setFixedSize(label->sizeHint());
01462     hlayout->addWidget(label, 0, TQt::AlignAuto | TQt::AlignTop);
01463 
01464     // List the months of the year.
01465     TQWidget* w = new TQWidget(this);   // this is to control the TQWhatsThis text display area
01466     hlayout->addWidget(w, 1, TQt::AlignAuto);
01467     TQGridLayout* grid = new TQGridLayout(w, 4, 3, 0, KDialog::spacingHint());
01468     const KCalendarSystem* calendar = TDEGlobal::locale()->calendar();
01469     int year = TQDate::currentDate().year();
01470     for (int i = 0;  i < 12;  ++i)
01471     {
01472         mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, true), w);
01473         mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
01474         mMonthBox[i]->setReadOnly(readOnly);
01475         grid->addWidget(mMonthBox[i], i%3, i/3, TQt::AlignAuto);
01476     }
01477     connect(mMonthBox[1], TQT_SIGNAL(toggled(bool)), TQT_SLOT(enableFeb29()));
01478     w->setFixedHeight(w->sizeHint().height());
01479     TQWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
01480 
01481     // February 29th handling option
01482     TQHBox* f29box = new TQHBox(this);
01483     layout()->addWidget(f29box);
01484     TQHBox* box = new TQHBox(f29box);    // this is to control the TQWhatsThis text display area
01485     box->setSpacing(KDialog::spacingHint());
01486     mFeb29Label = new TQLabel(i18n("February 2&9th alarm in non-leap years:"), box);
01487     mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
01488     mFeb29Combo = new ComboBox(false, box);
01489     mFeb29Combo->insertItem(i18n("No date", "None"));
01490     mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
01491     mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
01492     mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
01493     mFeb29Combo->setReadOnly(readOnly);
01494     mFeb29Label->setBuddy(mFeb29Combo);
01495     box->setFixedSize(box->sizeHint());
01496     TQWhatsThis::add(box,
01497           i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
01498     new TQWidget(f29box);     // left adjust the visible widgets
01499     f29box->setFixedHeight(f29box->sizeHint().height());
01500 }
01501 
01502 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
01503 {
01504     MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
01505     --month;
01506     for (int i = 0;  i < 12;  ++i)
01507         mMonthBox[i]->setChecked(i == month);
01508     setFeb29Type(Preferences::defaultFeb29Type());
01509     daySelected(dayOfMonth);     // enable/disable month checkboxes as appropriate
01510 }
01511 
01512 /******************************************************************************
01513  * Fetch which months have been checked (1 - 12).
01514  * Reply = true if February has been checked.
01515  */
01516 TQValueList<int> YearlyRule::months() const
01517 {
01518     TQValueList<int> mnths;
01519     for (int i = 0;  i < 12;  ++i)
01520         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01521             mnths.append(i + 1);
01522     return mnths;
01523 }
01524 
01525 /******************************************************************************
01526  * Check/uncheck each month of the year according to the specified list.
01527  */
01528 void YearlyRule::setMonths(const TQValueList<int>& mnths)
01529 {
01530     bool checked[12];
01531     for (int i = 0;  i < 12;  ++i)
01532         checked[i] = false;
01533     for (TQValueListConstIterator<int> it = mnths.begin();  it != mnths.end();  ++it)
01534         checked[(*it) - 1] = true;
01535     for (int i = 0;  i < 12;  ++i)
01536         mMonthBox[i]->setChecked(checked[i]);
01537     enableFeb29();
01538 }
01539 
01540 /******************************************************************************
01541  * Return the date for February 29th alarms in non-leap years.
01542  */
01543 KARecurrence::Feb29Type YearlyRule::feb29Type() const
01544 {
01545     if (mFeb29Combo->isEnabled())
01546     {
01547         switch (mFeb29Combo->currentItem())
01548         {
01549             case 1:   return KARecurrence::FEB29_MAR1;
01550             case 2:   return KARecurrence::FEB29_FEB28;
01551             default:  break;
01552         }
01553     }
01554     return KARecurrence::FEB29_FEB29;
01555 }
01556 
01557 /******************************************************************************
01558  * Set the date for February 29th alarms to trigger in non-leap years.
01559  */
01560 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
01561 {
01562     int index;
01563     switch (type)
01564     {
01565         default:
01566         case KARecurrence::FEB29_FEB29:  index = 0;  break;
01567         case KARecurrence::FEB29_MAR1:   index = 1;  break;
01568         case KARecurrence::FEB29_FEB28:  index = 2;  break;
01569     }
01570     mFeb29Combo->setCurrentItem(index);
01571 }
01572 
01573 /******************************************************************************
01574  * Validate: check that at least one month is selected.
01575  */
01576 TQWidget* YearlyRule::validate(TQString& errorMessage)
01577 {
01578     for (int i = 0;  i < 12;  ++i)
01579         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01580             return 0;
01581     errorMessage = i18n("No month selected");
01582     return mMonthBox[0];
01583 }
01584 
01585 /******************************************************************************
01586  * Called when a yearly recurrence type radio button is clicked,
01587  * to enable/disable month checkboxes as appropriate for the date selected.
01588  */
01589 void YearlyRule::clicked(int id)
01590 {
01591     MonthYearRule::clicked(id);
01592     daySelected(buttonType(id) == DATE ? date() : 1);
01593 }
01594 
01595 /******************************************************************************
01596  * Called when a day of the month is selected in a yearly recurrence, to
01597  * disable months for which the day is out of range.
01598  */
01599 void YearlyRule::daySelected(int day)
01600 {
01601     mMonthBox[1]->setEnabled(day <= 29);  // February
01602     bool enable = (day != 31);
01603     mMonthBox[3]->setEnabled(enable);     // April
01604     mMonthBox[5]->setEnabled(enable);     // June
01605     mMonthBox[8]->setEnabled(enable);     // September
01606     mMonthBox[10]->setEnabled(enable);    // November
01607     enableFeb29();
01608 }
01609 
01610 /******************************************************************************
01611  * Enable/disable the February 29th combo box depending on whether February
01612  * 29th is selected.
01613  */
01614 void YearlyRule::enableFeb29()
01615 {
01616     bool enable = (type() == DATE  &&  date() == 29  &&  mMonthBox[1]->isChecked()  &&  mMonthBox[1]->isEnabled());
01617     mFeb29Label->setEnabled(enable);
01618     mFeb29Combo->setEnabled(enable);
01619 }
01620 
01621 /******************************************************************************
01622  * Save the state of all controls.
01623  */
01624 void YearlyRule::saveState()
01625 {
01626     MonthYearRule::saveState();
01627     mSavedMonths    = months();
01628     mSavedFeb29Type = feb29Type();
01629 }
01630 
01631 /******************************************************************************
01632  * Check whether any of the controls have changed state since initialisation.
01633  */
01634 bool YearlyRule::stateChanged() const
01635 {
01636     return (MonthYearRule::stateChanged()
01637         ||  mSavedMonths    != months()
01638         ||  mSavedFeb29Type != feb29Type());
01639 }