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 <qtooltip.h>
00027 #include <qlayout.h>
00028 #include <qvbox.h>
00029 #include <qwidgetstack.h>
00030 #include <qlistbox.h>
00031 #include <qframe.h>
00032 #include <qlabel.h>
00033 #include <qpushbutton.h>
00034 #include <qlineedit.h>
00035 #include <qwhatsthis.h>
00036 
00037 #include <kglobal.h>
00038 #include <klocale.h>
00039 #include <kcalendarsystem.h>
00040 #include <kiconloader.h>
00041 #include <kdialog.h>
00042 #include <kmessagebox.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 QString RecurrenceEdit::i18n_Norecur()           { return i18n("No recurrence"); }
00070 QString RecurrenceEdit::i18n_NoRecur()           { return i18n("No Recurrence"); }
00071 QString RecurrenceEdit::i18n_AtLogin()           { return i18n("At Login"); }
00072 QString RecurrenceEdit::i18n_l_Atlogin()         { return i18n("At &login"); }
00073 QString RecurrenceEdit::i18n_HourlyMinutely()    { return i18n("Hourly/Minutely"); }
00074 QString RecurrenceEdit::i18n_u_HourlyMinutely()  { return i18n("Ho&urly/Minutely"); }
00075 QString RecurrenceEdit::i18n_Daily()             { return i18n("Daily"); }
00076 QString RecurrenceEdit::i18n_d_Daily()           { return i18n("&Daily"); }
00077 QString RecurrenceEdit::i18n_Weekly()            { return i18n("Weekly"); }
00078 QString RecurrenceEdit::i18n_w_Weekly()          { return i18n("&Weekly"); }
00079 QString RecurrenceEdit::i18n_Monthly()           { return i18n("Monthly"); }
00080 QString RecurrenceEdit::i18n_m_Monthly()         { return i18n("&Monthly"); }
00081 QString RecurrenceEdit::i18n_Yearly()            { return i18n("Yearly"); }
00082 QString RecurrenceEdit::i18n_y_Yearly()          { return i18n("&Yearly"); }
00083 
00084 
00085 RecurrenceEdit::RecurrenceEdit(bool readOnly, QWidget* parent, const char* name)
00086     : QFrame(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     QBoxLayout* layout;
00097     QVBoxLayout* topLayout = new QVBoxLayout(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     QGroupBox* recurGroup = new QGroupBox(1, Qt::Vertical, i18n("Recurrence Rule"), this, "recurGroup");
00107     topLayout->addWidget(recurGroup);
00108     QFrame* ruleFrame = new QFrame(recurGroup, "ruleFrame");
00109     layout = new QVBoxLayout(ruleFrame, 0);
00110     layout->addSpacing(KDialog::spacingHint()/2);
00111 
00112     layout = new QHBoxLayout(layout, 0);
00113     QBoxLayout* lay = new QVBoxLayout(layout, 0);
00114     mRuleButtonGroup = new ButtonGroup(1, Qt::Horizontal, ruleFrame);
00115     mRuleButtonGroup->setInsideMargin(0);
00116     mRuleButtonGroup->setFrameStyle(QFrame::NoFrame);
00117     lay->addWidget(mRuleButtonGroup);
00118     lay->addStretch();    // top-adjust the interval radio buttons
00119     connect(mRuleButtonGroup, SIGNAL(buttonSet(int)), SLOT(periodClicked(int)));
00120 
00121     mNoneButton = new RadioButton(i18n_Norecur(), mRuleButtonGroup);
00122     mNoneButton->setFixedSize(mNoneButton->sizeHint());
00123     mNoneButton->setReadOnly(mReadOnly);
00124     QWhatsThis::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     QWhatsThis::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     QWhatsThis::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     QWhatsThis::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     QWhatsThis::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     QWhatsThis::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     QWhatsThis::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, SIGNAL(needsInitialisation()), SIGNAL(repeatNeedsInitialisation()));
00176     connect(mSubRepetition, SIGNAL(changed()), SIGNAL(frequencyChanged()));
00177     QWhatsThis::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 QVBoxLayout(layout);
00182 
00183     lay->addStretch();
00184     layout = new QHBoxLayout(lay);
00185 
00186     layout->addSpacing(KDialog::marginHint());
00187     QFrame* divider = new QFrame(ruleFrame);
00188     divider->setFrameStyle(QFrame::VLine | QFrame::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, SIGNAL(frequencyChanged()), this, SIGNAL(frequencyChanged()));
00200     connect(mDailyRule, SIGNAL(frequencyChanged()), this, SIGNAL(frequencyChanged()));
00201     connect(mWeeklyRule, SIGNAL(frequencyChanged()), this, SIGNAL(frequencyChanged()));
00202     connect(mMonthlyRule, SIGNAL(frequencyChanged()), this, SIGNAL(frequencyChanged()));
00203     connect(mYearlyRule, SIGNAL(frequencyChanged()), this, SIGNAL(frequencyChanged()));
00204 
00205     mRuleStack = new QWidgetStack(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, SIGNAL(buttonSet(int)), SLOT(rangeTypeClicked()));
00221     topLayout->addWidget(mRangeButtonGroup);
00222 
00223     QVBoxLayout* vlayout = new QVBoxLayout(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     QWhatsThis::add(mNoEndDateButton, i18n("Repeat the alarm indefinitely"));
00229     vlayout->addWidget(mNoEndDateButton, 1, Qt::AlignAuto);
00230     QSize size = mNoEndDateButton->size();
00231 
00232     layout = new QHBoxLayout(vlayout, KDialog::spacingHint());
00233     mRepeatCountButton = new RadioButton(i18n("End a&fter:"), mRangeButtonGroup);
00234     mRepeatCountButton->setReadOnly(mReadOnly);
00235     QWhatsThis::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, SIGNAL(valueChanged(int)), SLOT(repeatCountChanged(int)));
00243     QWhatsThis::add(mRepeatCountEntry,
00244           i18n("Enter the total number of times to trigger the alarm"));
00245     mRepeatCountButton->setFocusWidget(mRepeatCountEntry);
00246     mRepeatCountLabel = new QLabel(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 QHBoxLayout(vlayout, KDialog::spacingHint());
00256     mEndDateButton = new RadioButton(i18n("End &by:"), mRangeButtonGroup);
00257     mEndDateButton->setReadOnly(mReadOnly);
00258     QWhatsThis::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     QWhatsThis::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 QString lastTimeText = i18n("Enter the last time to repeat the alarm.");
00271     QWhatsThis::add(mEndTimeEdit, QString("%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, SIGNAL(toggled(bool)), SLOT(slotAnyTimeToggled(bool)));
00276     QWhatsThis::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 QGroupBox(i18n("E&xceptions"), this, "mExceptionGroup");
00294     topLayout->addWidget(mExceptionGroup);
00295     topLayout->setStretchFactor(mExceptionGroup, 2);
00296     vlayout = new QVBoxLayout(mExceptionGroup, KDialog::marginHint(), KDialog::spacingHint());
00297     vlayout->addSpacing(fontMetrics().lineSpacing()/2);
00298     layout = new QHBoxLayout(vlayout, KDialog::spacingHint());
00299     vlayout = new QVBoxLayout(layout);
00300 
00301     mExceptionDateList = new QListBox(mExceptionGroup);
00302     mExceptionDateList->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
00303     connect(mExceptionDateList, SIGNAL(selectionChanged()), SLOT(enableExceptionButtons()));
00304     QWhatsThis::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 QVBoxLayout(layout);
00317         mExceptionDateEdit = new DateEdit(mExceptionGroup);
00318         mExceptionDateEdit->setFixedSize(mExceptionDateEdit->sizeHint());
00319         mExceptionDateEdit->setDate(QDate::currentDate());
00320         QWhatsThis::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 QHBoxLayout(vlayout, KDialog::spacingHint());
00326         QPushButton* button = new QPushButton(i18n("Add"), mExceptionGroup);
00327         button->setFixedSize(button->sizeHint());
00328         connect(button, SIGNAL(clicked()), SLOT(addException()));
00329         QWhatsThis::add(button,
00330               i18n("Add the date entered above to the exceptions list"));
00331         layout->addWidget(button);
00332 
00333         mChangeExceptionButton = new QPushButton(i18n("Change"), mExceptionGroup);
00334         mChangeExceptionButton->setFixedSize(mChangeExceptionButton->sizeHint());
00335         connect(mChangeExceptionButton, SIGNAL(clicked()), SLOT(changeException()));
00336         QWhatsThis::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 QPushButton(i18n("Delete"), mExceptionGroup);
00341         mDeleteExceptionButton->setFixedSize(mDeleteExceptionButton->sizeHint());
00342         connect(mDeleteExceptionButton, SIGNAL(clicked()), SLOT(deleteException()));
00343         QWhatsThis::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 QWidget* RecurrenceEdit::checkData(const QDateTime& startDateTime, QString& errorMessage) const
00356 {
00357     if (mAtLoginButton->isOn())
00358         return 0;
00359     const_cast<RecurrenceEdit*>(this)->mCurrStartDateTime = startDateTime;
00360     if (mEndDateButton->isChecked())
00361     {
00362         QWidget* errWidget = 0;
00363         bool noTime = !mEndTimeEdit->isEnabled();
00364         QDate endDate = mEndDateEdit->date();
00365         if (endDate < startDateTime.date())
00366             errWidget = mEndDateEdit;
00367         else if (!noTime  &&  QDateTime(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     QButton* 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(QShowEvent*)
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     QDate date = mExceptionDateEdit->date();
00552     QValueList<QDate>::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(KGlobal::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         QDate olddate = mExceptionDates[index];
00584         QDate 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() ? QWidget::WheelFocus : QWidget::NoFocus);
00623 }
00624 
00625 /******************************************************************************
00626  * Notify this instance of a change in the alarm start date.
00627  */
00628 void RecurrenceEdit::setStartDate(const QDate& start, const QDate& 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 QString 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 QDate& 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 QDateTime& from)
00677 {
00678     mCurrStartDateTime = from;
00679     QDate 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 QDate& 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             QBitArray 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             QBitArray rDays = recurrence->days();
00776             mWeeklyRule->setDays(rDays);
00777             break;
00778         }
00779         case KARecurrence::MONTHLY_POS:    // on nth (Tuesday) of the month
00780         {
00781             QValueList<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                 QBitArray rDays(7);
00790                 for (QValueList<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             QValueList<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 QValueList<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                 QValueList<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     QDateTime 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(KGlobal::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     QDate  endDate;
00879     QTime  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     QButton* button = mRuleButtonGroup->selected();
00894     event.setRepeatAtLogin(button == mAtLoginButton);
00895     int frequency = mRule ? mRule->frequency() : 0;
00896     if (button == mSubDailyButton)
00897     {
00898         QDateTime 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             QValueList<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             QValueList<int> daynums;
00927             daynums.append(daynum);
00928             event.setRecurMonthlyByDate(frequency, daynums, repeatCount, endDate);
00929         }
00930     }
00931     else if (button == mYearlyButton)
00932     {
00933         QValueList<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             QValueList<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(QDateTime(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(QDateTime(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 QString& freqText, const QString& freqWhatsThis, bool time, bool readOnly, QWidget* parent, const char* name)
01023     : NoRule(parent, name)
01024 {
01025     mLayout = new QVBoxLayout(this, 0, KDialog::spacingHint());
01026     QHBox* freqBox = new QHBox(this);
01027     mLayout->addWidget(freqBox);
01028     QHBox* box = new QHBox(freqBox);    // this is to control the QWhatsThis text display area
01029     box->setSpacing(KDialog::spacingHint());
01030 
01031     QLabel* label = new QLabel(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, SIGNAL(valueChanged(int)), SIGNAL(frequencyChanged()));
01048     label->setBuddy(mSpinBox);
01049     label = new QLabel(freqText, box);
01050     label->setFixedSize(label->sizeHint());
01051     box->setFixedSize(sizeHint());
01052     QWhatsThis::add(box, freqWhatsThis);
01053 
01054     new QWidget(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, QWidget* 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 QString& freqText, const QString& freqWhatsThis, const QString& daysWhatsThis,
01111                          bool readOnly, QWidget* parent, const char* name)
01112     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01113       mSavedDays(7)
01114 {
01115     QGridLayout* grid = new QGridLayout(layout(), 1, 4, KDialog::spacingHint());
01116     grid->setRowStretch(0, 1);
01117 
01118     QLabel* label = new QLabel(i18n("On: Tuesday", "O&n:"), this);
01119     label->setFixedSize(label->sizeHint());
01120     grid->addWidget(label, 0, 0, Qt::AlignRight | Qt::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     QWidget* box = new QWidget(this);   // this is to control the QWhatsThis text display area
01126     QGridLayout* dgrid = new QGridLayout(box, 4, 2, 0, KDialog::spacingHint());
01127     const KCalendarSystem* calendar = KGlobal::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, Qt::AlignAuto);
01135     }
01136     box->setFixedSize(box->sizeHint());
01137     QWhatsThis::add(box, daysWhatsThis);
01138     grid->addWidget(box, 0, 2, Qt::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 QBitArray DayWeekRule::days() const
01147 {
01148     QBitArray 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 QBitArray& 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 QWidget* DayWeekRule::validate(QString& 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, QWidget* parent, const char* name)
01225     : DayWeekRule(i18n("day(s)"),
01226                   i18n("Enter the number of days between repetitions of the alarm"),
01227 #if KDE_IS_VERSION(3,9,0)
01228                   i18n("Select the days of the week on which the alarm is allowed to occur"),
01229 #else
01230                   i18n("Select the days of the week on which to repeat the alarm"),
01231 #endif
01232                   readOnly, parent, name)
01233 { }
01234 
01235 
01236 /*=============================================================================
01237 = Class WeeklyRule
01238 = Weekly rule widget.
01239 =============================================================================*/
01240 
01241 WeeklyRule::WeeklyRule(bool readOnly, QWidget* parent, const char* name)
01242     : DayWeekRule(i18n("week(s)"),
01243                   i18n("Enter the number of weeks between repetitions of the alarm"),
01244                   i18n("Select the days of the week on which to repeat the alarm"),
01245                   readOnly, parent, name)
01246 { }
01247 
01248 
01249 /*=============================================================================
01250 = Class MonthYearRule
01251 = Monthly/yearly rule widget base class.
01252 =============================================================================*/
01253 
01254 MonthYearRule::MonthYearRule(const QString& freqText, const QString& freqWhatsThis, bool allowEveryWeek,
01255                              bool readOnly, QWidget* parent, const char* name)
01256     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01257       mEveryWeek(allowEveryWeek)
01258 {
01259     mButtonGroup = new ButtonGroup(this);
01260     mButtonGroup->hide();
01261 
01262     // Month day selector
01263     QHBox* box = new QHBox(this);
01264     box->setSpacing(KDialog::spacingHint());
01265     layout()->addWidget(box);
01266 
01267     mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
01268     mDayButton->setFixedSize(mDayButton->sizeHint());
01269     mDayButton->setReadOnly(readOnly);
01270     mDayButtonId = mButtonGroup->insert(mDayButton);
01271     QWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
01272 
01273     mDayCombo = new ComboBox(false, box);
01274     mDayCombo->setSizeLimit(11);
01275     for (int i = 0;  i < 31;  ++i)
01276         mDayCombo->insertItem(QString::number(i + 1));
01277     mDayCombo->insertItem(i18n("Last day of month", "Last"));
01278     mDayCombo->setFixedSize(mDayCombo->sizeHint());
01279     mDayCombo->setReadOnly(readOnly);
01280     QWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
01281     mDayButton->setFocusWidget(mDayCombo);
01282     connect(mDayCombo, SIGNAL(activated(int)), SLOT(slotDaySelected(int)));
01283 
01284     box->setStretchFactor(new QWidget(box), 1);    // left adjust the controls
01285     box->setFixedHeight(box->sizeHint().height());
01286 
01287     // Month position selector
01288     box = new QHBox(this);
01289     box->setSpacing(KDialog::spacingHint());
01290     layout()->addWidget(box);
01291 
01292     mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
01293     mPosButton->setFixedSize(mPosButton->sizeHint());
01294     mPosButton->setReadOnly(readOnly);
01295     mPosButtonId = mButtonGroup->insert(mPosButton);
01296     QWhatsThis::add(mPosButton,
01297           i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
01298 
01299     mWeekCombo = new ComboBox(false, box);
01300     mWeekCombo->insertItem(i18n("1st"));
01301     mWeekCombo->insertItem(i18n("2nd"));
01302     mWeekCombo->insertItem(i18n("3rd"));
01303     mWeekCombo->insertItem(i18n("4th"));
01304     mWeekCombo->insertItem(i18n("5th"));
01305     mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
01306     mWeekCombo->insertItem(i18n("2nd Last"));
01307     mWeekCombo->insertItem(i18n("3rd Last"));
01308     mWeekCombo->insertItem(i18n("4th Last"));
01309     mWeekCombo->insertItem(i18n("5th Last"));
01310     if (mEveryWeek)
01311     {
01312         mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
01313         mWeekCombo->setSizeLimit(11);
01314     }
01315     QWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
01316     mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
01317     mWeekCombo->setReadOnly(readOnly);
01318     mPosButton->setFocusWidget(mWeekCombo);
01319 
01320     mDayOfWeekCombo = new ComboBox(false, box);
01321     const KCalendarSystem* calendar = KGlobal::locale()->calendar();
01322     for (int i = 0;  i < 7;  ++i)
01323     {
01324         int day = KAlarm::localeDayInWeek_to_weekDay(i);
01325         mDayOfWeekCombo->insertItem(calendar->weekDayName(day));
01326     }
01327     mDayOfWeekCombo->setReadOnly(readOnly);
01328     QWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
01329 
01330     box->setStretchFactor(new QWidget(box), 1);    // left adjust the controls
01331     box->setFixedHeight(box->sizeHint().height());
01332     connect(mButtonGroup, SIGNAL(buttonSet(int)), SLOT(clicked(int)));
01333 }
01334 
01335 MonthYearRule::DayPosType MonthYearRule::type() const
01336 {
01337     return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
01338 }
01339 
01340 void MonthYearRule::setType(MonthYearRule::DayPosType type)
01341 {
01342     mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
01343 }
01344 
01345 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
01346 {
01347     --dayOfMonth;
01348     mDayCombo->setCurrentItem(dayOfMonth);
01349     mWeekCombo->setCurrentItem(dayOfMonth / 7);
01350     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01351 }
01352 
01353 int MonthYearRule::date() const
01354 {
01355     int daynum  = mDayCombo->currentItem() + 1;
01356     return (daynum <= 31) ? daynum : 31 - daynum;
01357 }
01358 
01359 int MonthYearRule::week() const
01360 {
01361     int weeknum = mWeekCombo->currentItem() + 1;
01362     return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
01363 }
01364 
01365 int MonthYearRule::dayOfWeek() const
01366 {
01367     return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
01368 }
01369 
01370 void MonthYearRule::setDate(int dayOfMonth)
01371 {
01372     mButtonGroup->setButton(mDayButtonId);
01373     mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0);   // day 0 shouldn't ever occur
01374 }
01375 
01376 void MonthYearRule::setPosition(int week, int dayOfWeek)
01377 {
01378     mButtonGroup->setButton(mPosButtonId);
01379     mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
01380     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01381 }
01382 
01383 void MonthYearRule::enableSelection(DayPosType type)
01384 {
01385     bool date = (type == DATE);
01386     mDayCombo->setEnabled(date);
01387     mWeekCombo->setEnabled(!date);
01388     mDayOfWeekCombo->setEnabled(!date);
01389 }
01390 
01391 void MonthYearRule::clicked(int id)
01392 {
01393     enableSelection(id == mDayButtonId ? DATE : POS);
01394 }
01395 
01396 void MonthYearRule::slotDaySelected(int index)
01397 {
01398     daySelected(index <= 30 ? index + 1 : 30 - index);
01399 }
01400 
01401 /******************************************************************************
01402  * Save the state of all controls.
01403  */
01404 void MonthYearRule::saveState()
01405 {
01406     Rule::saveState();
01407     mSavedType = type();
01408     if (mSavedType == DATE)
01409         mSavedDay = date();
01410     else
01411     {
01412         mSavedWeek    = week();
01413         mSavedWeekDay = dayOfWeek();
01414     }
01415 }
01416 
01417 /******************************************************************************
01418  * Check whether any of the controls have changed state since initialisation.
01419  */
01420 bool MonthYearRule::stateChanged() const
01421 {
01422     if (Rule::stateChanged()
01423     ||  mSavedType != type())
01424         return true;
01425     if (mSavedType == DATE)
01426     {
01427         if (mSavedDay != date())
01428             return true;
01429     }
01430     else
01431     {
01432         if (mSavedWeek    != week()
01433         ||  mSavedWeekDay != dayOfWeek())
01434             return true;
01435     }
01436     return false;
01437 }
01438 
01439 
01440 /*=============================================================================
01441 = Class MonthlyRule
01442 = Monthly rule widget.
01443 =============================================================================*/
01444 
01445 MonthlyRule::MonthlyRule(bool readOnly, QWidget* parent, const char* name)
01446     : MonthYearRule(i18n("month(s)"),
01447            i18n("Enter the number of months between repetitions of the alarm"),
01448            false, readOnly, parent, name)
01449 { }
01450 
01451 
01452 /*=============================================================================
01453 = Class YearlyRule
01454 = Yearly rule widget.
01455 =============================================================================*/
01456 
01457 YearlyRule::YearlyRule(bool readOnly, QWidget* parent, const char* name)
01458     : MonthYearRule(i18n("year(s)"),
01459            i18n("Enter the number of years between repetitions of the alarm"),
01460            true, readOnly, parent, name)
01461 {
01462     // Set up the month selection widgets
01463     QBoxLayout* hlayout = new QHBoxLayout(layout(), KDialog::spacingHint());
01464     QLabel* label = new QLabel(i18n("List of months to select", "Months:"), this);
01465     label->setFixedSize(label->sizeHint());
01466     hlayout->addWidget(label, 0, Qt::AlignAuto | Qt::AlignTop);
01467 
01468     // List the months of the year.
01469     QWidget* w = new QWidget(this);   // this is to control the QWhatsThis text display area
01470     hlayout->addWidget(w, 1, Qt::AlignAuto);
01471     QGridLayout* grid = new QGridLayout(w, 4, 3, 0, KDialog::spacingHint());
01472     const KCalendarSystem* calendar = KGlobal::locale()->calendar();
01473     int year = QDate::currentDate().year();
01474     for (int i = 0;  i < 12;  ++i)
01475     {
01476         mMonthBox[i] = new CheckBox(calendar->monthName(i + 1, year, true), w);
01477         mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
01478         mMonthBox[i]->setReadOnly(readOnly);
01479         grid->addWidget(mMonthBox[i], i%3, i/3, Qt::AlignAuto);
01480     }
01481     connect(mMonthBox[1], SIGNAL(toggled(bool)), SLOT(enableFeb29()));
01482     w->setFixedHeight(w->sizeHint().height());
01483     QWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
01484 
01485     // February 29th handling option
01486     QHBox* f29box = new QHBox(this);
01487     layout()->addWidget(f29box);
01488     QHBox* box = new QHBox(f29box);    // this is to control the QWhatsThis text display area
01489     box->setSpacing(KDialog::spacingHint());
01490     mFeb29Label = new QLabel(i18n("February 2&9th alarm in non-leap years:"), box);
01491     mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
01492     mFeb29Combo = new ComboBox(false, box);
01493     mFeb29Combo->insertItem(i18n("No date", "None"));
01494     mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
01495     mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
01496     mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
01497     mFeb29Combo->setReadOnly(readOnly);
01498     mFeb29Label->setBuddy(mFeb29Combo);
01499     box->setFixedSize(box->sizeHint());
01500     QWhatsThis::add(box,
01501           i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
01502     new QWidget(f29box);     // left adjust the visible widgets
01503     f29box->setFixedHeight(f29box->sizeHint().height());
01504 }
01505 
01506 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
01507 {
01508     MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
01509     --month;
01510     for (int i = 0;  i < 12;  ++i)
01511         mMonthBox[i]->setChecked(i == month);
01512     setFeb29Type(Preferences::defaultFeb29Type());
01513     daySelected(dayOfMonth);     // enable/disable month checkboxes as appropriate
01514 }
01515 
01516 /******************************************************************************
01517  * Fetch which months have been checked (1 - 12).
01518  * Reply = true if February has been checked.
01519  */
01520 QValueList<int> YearlyRule::months() const
01521 {
01522     QValueList<int> mnths;
01523     for (int i = 0;  i < 12;  ++i)
01524         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01525             mnths.append(i + 1);
01526     return mnths;
01527 }
01528 
01529 /******************************************************************************
01530  * Check/uncheck each month of the year according to the specified list.
01531  */
01532 void YearlyRule::setMonths(const QValueList<int>& mnths)
01533 {
01534     bool checked[12];
01535     for (int i = 0;  i < 12;  ++i)
01536         checked[i] = false;
01537     for (QValueListConstIterator<int> it = mnths.begin();  it != mnths.end();  ++it)
01538         checked[(*it) - 1] = true;
01539     for (int i = 0;  i < 12;  ++i)
01540         mMonthBox[i]->setChecked(checked[i]);
01541     enableFeb29();
01542 }
01543 
01544 /******************************************************************************
01545  * Return the date for February 29th alarms in non-leap years.
01546  */
01547 KARecurrence::Feb29Type YearlyRule::feb29Type() const
01548 {
01549     if (mFeb29Combo->isEnabled())
01550     {
01551         switch (mFeb29Combo->currentItem())
01552         {
01553             case 1:   return KARecurrence::FEB29_MAR1;
01554             case 2:   return KARecurrence::FEB29_FEB28;
01555             default:  break;
01556         }
01557     }
01558     return KARecurrence::FEB29_FEB29;
01559 }
01560 
01561 /******************************************************************************
01562  * Set the date for February 29th alarms to trigger in non-leap years.
01563  */
01564 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
01565 {
01566     int index;
01567     switch (type)
01568     {
01569         default:
01570         case KARecurrence::FEB29_FEB29:  index = 0;  break;
01571         case KARecurrence::FEB29_MAR1:   index = 1;  break;
01572         case KARecurrence::FEB29_FEB28:  index = 2;  break;
01573     }
01574     mFeb29Combo->setCurrentItem(index);
01575 }
01576 
01577 /******************************************************************************
01578  * Validate: check that at least one month is selected.
01579  */
01580 QWidget* YearlyRule::validate(QString& errorMessage)
01581 {
01582     for (int i = 0;  i < 12;  ++i)
01583         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01584             return 0;
01585     errorMessage = i18n("No month selected");
01586     return mMonthBox[0];
01587 }
01588 
01589 /******************************************************************************
01590  * Called when a yearly recurrence type radio button is clicked,
01591  * to enable/disable month checkboxes as appropriate for the date selected.
01592  */
01593 void YearlyRule::clicked(int id)
01594 {
01595     MonthYearRule::clicked(id);
01596     daySelected(buttonType(id) == DATE ? date() : 1);
01597 }
01598 
01599 /******************************************************************************
01600  * Called when a day of the month is selected in a yearly recurrence, to
01601  * disable months for which the day is out of range.
01602  */
01603 void YearlyRule::daySelected(int day)
01604 {
01605     mMonthBox[1]->setEnabled(day <= 29);  // February
01606     bool enable = (day != 31);
01607     mMonthBox[3]->setEnabled(enable);     // April
01608     mMonthBox[5]->setEnabled(enable);     // June
01609     mMonthBox[8]->setEnabled(enable);     // September
01610     mMonthBox[10]->setEnabled(enable);    // November
01611     enableFeb29();
01612 }
01613 
01614 /******************************************************************************
01615  * Enable/disable the February 29th combo box depending on whether February
01616  * 29th is selected.
01617  */
01618 void YearlyRule::enableFeb29()
01619 {
01620     bool enable = (type() == DATE  &&  date() == 29  &&  mMonthBox[1]->isChecked()  &&  mMonthBox[1]->isEnabled());
01621     mFeb29Label->setEnabled(enable);
01622     mFeb29Combo->setEnabled(enable);
01623 }
01624 
01625 /******************************************************************************
01626  * Save the state of all controls.
01627  */
01628 void YearlyRule::saveState()
01629 {
01630     MonthYearRule::saveState();
01631     mSavedMonths    = months();
01632     mSavedFeb29Type = feb29Type();
01633 }
01634 
01635 /******************************************************************************
01636  * Check whether any of the controls have changed state since initialisation.
01637  */
01638 bool YearlyRule::stateChanged() const
01639 {
01640     return (MonthYearRule::stateChanged()
01641         ||  mSavedMonths    != months()
01642         ||  mSavedFeb29Type != feb29Type());
01643 }
KDE Home | KDE Accessibility Home | Description of Access Keys