kalarm

recurrenceedit.cpp

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