kalarm

recurrenceedit.cpp

00001 /*
00002  *  recurrenceedit.cpp  -  widget to edit the event's recurrence definition
00003  *  Program:  kalarm
00004  *  Copyright © 2002-2006 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       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     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     mRuleStack = new QWidgetStack(ruleFrame);
00196     layout->addWidget(mRuleStack);
00197     layout->addStretch(1);
00198     mRuleStack->addWidget(mNoRule, 0);
00199     mRuleStack->addWidget(mSubDailyRule, 1);
00200     mRuleStack->addWidget(mDailyRule, 2);
00201     mRuleStack->addWidget(mWeeklyRule, 3);
00202     mRuleStack->addWidget(mMonthlyRule, 4);
00203     mRuleStack->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     mNoEmitTypeChanged = 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         mRuleStack->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 (!mNoEmitTypeChanged)
00437             emit typeChanged(mRuleButtonType);
00438     }
00439 }
00440 
00441 void RecurrenceEdit::slotAnyTimeToggled(bool on)
00442 {
00443     QButton* button = mRuleButtonGroup->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         mRuleButtonGroup->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 (mRuleButtonGroup->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     mNoEmitTypeChanged = 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     mRuleButtonGroup->setButton(button);
00647     mNoEmitTypeChanged = 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         mRuleButtonGroup->setButton(mAtLoginButtonId);
00682         mEndDateButton->setChecked(true);
00683         return;
00684     }
00685     mRuleButtonGroup->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             mRuleButtonGroup->setButton(mSubDailyButtonId);
00695             break;
00696 
00697         case KARecurrence::DAILY:
00698         {
00699             mRuleButtonGroup->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             mRuleButtonGroup->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                 mRuleButtonGroup->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             mRuleButtonGroup->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             mRuleButtonGroup->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                 mRuleButtonGroup->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                 mRuleButtonGroup->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 = mRuleButtonGroup->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 (!event.recurs())
00898         return;    // an error occurred setting up the recurrence
00899     if (adjustStart)
00900         event.setFirstRecurrence();
00901 
00902     // Set up exceptions
00903     event.recurrence()->setExDates(mExceptionDates);
00904     event.setUpdated();
00905 }
00906 
00907 /******************************************************************************
00908  * Save the state of all controls.
00909  */
00910 void RecurrenceEdit::saveState()
00911 {
00912     mSavedRuleButton = mRuleButtonGroup->selected();
00913     if (mRule)
00914         mRule->saveState();
00915     mSavedRangeButton = mRangeButtonGroup->selected();
00916     if (mSavedRangeButton == mRepeatCountButton)
00917         mSavedRepeatCount = mRepeatCountEntry->value();
00918     else if (mSavedRangeButton == mEndDateButton)
00919         mSavedEndDateTime.set(QDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked());
00920     mSavedExceptionDates = mExceptionDates;
00921 }
00922 
00923 /******************************************************************************
00924  * Check whether any of the controls have changed state since initialisation.
00925  */
00926 bool RecurrenceEdit::stateChanged() const
00927 {
00928     if (mSavedRuleButton  != mRuleButtonGroup->selected()
00929     ||  mSavedRangeButton != mRangeButtonGroup->selected()
00930     ||  mRule  &&  mRule->stateChanged())
00931         return true;
00932     if (mSavedRangeButton == mRepeatCountButton
00933     &&  mSavedRepeatCount != mRepeatCountEntry->value())
00934         return true;
00935     if (mSavedRangeButton == mEndDateButton
00936     &&  mSavedEndDateTime != DateTime(QDateTime(mEndDateEdit->date(), mEndTimeEdit->time()), mEndAnyTimeCheckBox->isChecked()))
00937         return true;
00938     if (mSavedExceptionDates != mExceptionDates)
00939         return true;
00940     return false;
00941 }
00942 
00943 
00944 
00945 /*=============================================================================
00946 = Class Rule
00947 = Base class for rule widgets, including recurrence frequency.
00948 =============================================================================*/
00949 
00950 Rule::Rule(const QString& freqText, const QString& freqWhatsThis, bool time, bool readOnly, QWidget* parent, const char* name)
00951     : NoRule(parent, name)
00952 {
00953     mLayout = new QVBoxLayout(this, 0, KDialog::spacingHint());
00954     QHBox* freqBox = new QHBox(this);
00955     mLayout->addWidget(freqBox);
00956     QHBox* box = new QHBox(freqBox);    // this is to control the QWhatsThis text display area
00957     box->setSpacing(KDialog::spacingHint());
00958 
00959     QLabel* label = new QLabel(i18n("Recur e&very"), box);
00960     label->setFixedSize(label->sizeHint());
00961     if (time)
00962     {
00963         mIntSpinBox = 0;
00964         mSpinBox = mTimeSpinBox = new TimeSpinBox(1, 5999, box);
00965         mTimeSpinBox->setFixedSize(mTimeSpinBox->sizeHint());
00966         mTimeSpinBox->setReadOnly(readOnly);
00967     }
00968     else
00969     {
00970         mTimeSpinBox = 0;
00971         mSpinBox = mIntSpinBox = new SpinBox(1, 999, 1, box);
00972         mIntSpinBox->setFixedSize(mIntSpinBox->sizeHint());
00973         mIntSpinBox->setReadOnly(readOnly);
00974     }
00975     connect(mSpinBox, SIGNAL(valueChanged(int)), SIGNAL(frequencyChanged()));
00976     label->setBuddy(mSpinBox);
00977     label = new QLabel(freqText, box);
00978     label->setFixedSize(label->sizeHint());
00979     box->setFixedSize(sizeHint());
00980     QWhatsThis::add(box, freqWhatsThis);
00981 
00982     new QWidget(freqBox);     // left adjust the visible widgets
00983     freqBox->setFixedHeight(freqBox->sizeHint().height());
00984     freqBox->setFocusProxy(mSpinBox);
00985 }
00986 
00987 int Rule::frequency() const
00988 {
00989     if (mIntSpinBox)
00990         return mIntSpinBox->value();
00991     if (mTimeSpinBox)
00992         return mTimeSpinBox->value();
00993     return 0;
00994 }
00995 
00996 void Rule::setFrequency(int n)
00997 {
00998     if (mIntSpinBox)
00999         mIntSpinBox->setValue(n);
01000     if (mTimeSpinBox)
01001         mTimeSpinBox->setValue(n);
01002 }
01003 
01004 /******************************************************************************
01005  * Save the state of all controls.
01006  */
01007 void Rule::saveState()
01008 {
01009     mSavedFrequency = frequency();
01010 }
01011 
01012 /******************************************************************************
01013  * Check whether any of the controls have changed state since initialisation.
01014  */
01015 bool Rule::stateChanged() const
01016 {
01017     return (mSavedFrequency != frequency());
01018 }
01019 
01020 
01021 /*=============================================================================
01022 = Class SubDailyRule
01023 = Sub-daily rule widget.
01024 =============================================================================*/
01025 
01026 SubDailyRule::SubDailyRule(bool readOnly, QWidget* parent, const char* name)
01027     : Rule(i18n("hours:minutes"),
01028            i18n("Enter the number of hours and minutes between repetitions of the alarm"),
01029            true, readOnly, parent, name)
01030 { }
01031 
01032 
01033 /*=============================================================================
01034 = Class DayWeekRule
01035 = Daily/weekly rule widget base class.
01036 =============================================================================*/
01037 
01038 DayWeekRule::DayWeekRule(const QString& freqText, const QString& freqWhatsThis, const QString& daysWhatsThis,
01039                          bool readOnly, QWidget* parent, const char* name)
01040     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01041       mSavedDays(7)
01042 {
01043     QGridLayout* grid = new QGridLayout(layout(), 1, 4, KDialog::spacingHint());
01044     grid->setRowStretch(0, 1);
01045 
01046     QLabel* label = new QLabel(i18n("On: Tuesday", "O&n:"), this);
01047     label->setFixedSize(label->sizeHint());
01048     grid->addWidget(label, 0, 0, Qt::AlignRight | Qt::AlignTop);
01049     grid->addColSpacing(1, KDialog::spacingHint());
01050 
01051     // List the days of the week starting at the user's start day of the week.
01052     // Save the first day of the week, just in case it changes while the dialog is open.
01053     QWidget* box = new QWidget(this);   // this is to control the QWhatsThis text display area
01054     QGridLayout* dgrid = new QGridLayout(box, 4, 2, 0, KDialog::spacingHint());
01055     const KLocale* locale = KGlobal::locale();
01056     for (int i = 0;  i < 7;  ++i)
01057     {
01058         int day = KAlarm::localeDayInWeek_to_weekDay(i);
01059         mDayBox[i] = new CheckBox(weekDayName(day, locale), box);
01060         mDayBox[i]->setFixedSize(mDayBox[i]->sizeHint());
01061         mDayBox[i]->setReadOnly(readOnly);
01062         dgrid->addWidget(mDayBox[i], i%4, i/4, Qt::AlignAuto);
01063     }
01064     box->setFixedSize(box->sizeHint());
01065     QWhatsThis::add(box, daysWhatsThis);
01066     grid->addWidget(box, 0, 2, Qt::AlignAuto);
01067     label->setBuddy(mDayBox[0]);
01068     grid->setColStretch(3, 1);
01069 }
01070 
01071 /******************************************************************************
01072  * Fetch which days of the week have been ticked.
01073  */
01074 QBitArray DayWeekRule::days() const
01075 {
01076     QBitArray ds(7);
01077     ds.fill(false);
01078     for (int i = 0;  i < 7;  ++i)
01079         if (mDayBox[i]->isChecked())
01080             ds.setBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1, 1);
01081     return ds;
01082 }
01083 
01084 /******************************************************************************
01085  * Tick/untick every day of the week.
01086  */
01087 void DayWeekRule::setDays(bool tick)
01088 {
01089     for (int i = 0;  i < 7;  ++i)
01090         mDayBox[i]->setChecked(tick);
01091 }
01092 
01093 /******************************************************************************
01094  * Tick/untick each day of the week according to the specified bits.
01095  */
01096 void DayWeekRule::setDays(QBitArray& days)
01097 {
01098     for (int i = 0;  i < 7;  ++i)
01099     {
01100         bool x = days.testBit(KAlarm::localeDayInWeek_to_weekDay(i) - 1);
01101         mDayBox[i]->setChecked(x);
01102     }
01103 }
01104 
01105 /******************************************************************************
01106  * Tick the specified day of the week, and untick all other days.
01107  */
01108 void DayWeekRule::setDay(int dayOfWeek)
01109 {
01110     for (int i = 0;  i < 7;  ++i)
01111         mDayBox[i]->setChecked(false);
01112     if (dayOfWeek > 0  &&  dayOfWeek <= 7)
01113         mDayBox[KAlarm::weekDay_to_localeDayInWeek(dayOfWeek)]->setChecked(true);
01114 }
01115 
01116 /******************************************************************************
01117  * Validate: check that at least one day is selected.
01118  */
01119 QWidget* DayWeekRule::validate(QString& errorMessage)
01120 {
01121     for (int i = 0;  i < 7;  ++i)
01122         if (mDayBox[i]->isChecked())
01123             return 0;
01124     errorMessage = i18n("No day selected");
01125     return mDayBox[0];
01126 }
01127 
01128 /******************************************************************************
01129  * Save the state of all controls.
01130  */
01131 void DayWeekRule::saveState()
01132 {
01133     Rule::saveState();
01134     mSavedDays = days();
01135 }
01136 
01137 /******************************************************************************
01138  * Check whether any of the controls have changed state since initialisation.
01139  */
01140 bool DayWeekRule::stateChanged() const
01141 {
01142     return (Rule::stateChanged()
01143         ||  mSavedDays != days());
01144 }
01145 
01146 
01147 /*=============================================================================
01148 = Class DailyRule
01149 = Daily rule widget.
01150 =============================================================================*/
01151 
01152 DailyRule::DailyRule(bool readOnly, QWidget* parent, const char* name)
01153     : DayWeekRule(i18n("day(s)"),
01154                   i18n("Enter the number of days between repetitions of the alarm"),
01155 #if KDE_IS_VERSION(3,9,0)
01156                   i18n("Select the days of the week on which the alarm is allowed to occur"),
01157 #else
01158                   i18n("Select the days of the week on which to repeat the alarm"),
01159 #endif
01160                   readOnly, parent, name)
01161 { }
01162 
01163 
01164 /*=============================================================================
01165 = Class WeeklyRule
01166 = Weekly rule widget.
01167 =============================================================================*/
01168 
01169 WeeklyRule::WeeklyRule(bool readOnly, QWidget* parent, const char* name)
01170     : DayWeekRule(i18n("week(s)"),
01171                   i18n("Enter the number of weeks between repetitions of the alarm"),
01172                   i18n("Select the days of the week on which to repeat the alarm"),
01173                   readOnly, parent, name)
01174 { }
01175 
01176 
01177 /*=============================================================================
01178 = Class MonthYearRule
01179 = Monthly/yearly rule widget base class.
01180 =============================================================================*/
01181 
01182 MonthYearRule::MonthYearRule(const QString& freqText, const QString& freqWhatsThis, bool allowEveryWeek,
01183                              bool readOnly, QWidget* parent, const char* name)
01184     : Rule(freqText, freqWhatsThis, false, readOnly, parent, name),
01185       mEveryWeek(allowEveryWeek)
01186 {
01187     mButtonGroup = new ButtonGroup(this);
01188     mButtonGroup->hide();
01189 
01190     // Month day selector
01191     QHBox* box = new QHBox(this);
01192     box->setSpacing(KDialog::spacingHint());
01193     layout()->addWidget(box);
01194 
01195     mDayButton = new RadioButton(i18n("On day number in the month", "O&n day"), box);
01196     mDayButton->setFixedSize(mDayButton->sizeHint());
01197     mDayButton->setReadOnly(readOnly);
01198     mDayButtonId = mButtonGroup->insert(mDayButton);
01199     QWhatsThis::add(mDayButton, i18n("Repeat the alarm on the selected day of the month"));
01200 
01201     mDayCombo = new ComboBox(false, box);
01202     mDayCombo->setSizeLimit(11);
01203     for (int i = 0;  i < 31;  ++i)
01204         mDayCombo->insertItem(QString::number(i + 1));
01205     mDayCombo->insertItem(i18n("Last day of month", "Last"));
01206     mDayCombo->setFixedSize(mDayCombo->sizeHint());
01207     mDayCombo->setReadOnly(readOnly);
01208     QWhatsThis::add(mDayCombo, i18n("Select the day of the month on which to repeat the alarm"));
01209     mDayButton->setFocusWidget(mDayCombo);
01210     connect(mDayCombo, SIGNAL(activated(int)), SLOT(slotDaySelected(int)));
01211 
01212     box->setStretchFactor(new QWidget(box), 1);    // left adjust the controls
01213     box->setFixedHeight(box->sizeHint().height());
01214 
01215     // Month position selector
01216     box = new QHBox(this);
01217     box->setSpacing(KDialog::spacingHint());
01218     layout()->addWidget(box);
01219 
01220     mPosButton = new RadioButton(i18n("On the 1st Tuesday", "On t&he"), box);
01221     mPosButton->setFixedSize(mPosButton->sizeHint());
01222     mPosButton->setReadOnly(readOnly);
01223     mPosButtonId = mButtonGroup->insert(mPosButton);
01224     QWhatsThis::add(mPosButton,
01225           i18n("Repeat the alarm on one day of the week, in the selected week of the month"));
01226 
01227     mWeekCombo = new ComboBox(false, box);
01228     mWeekCombo->insertItem(i18n("1st"));
01229     mWeekCombo->insertItem(i18n("2nd"));
01230     mWeekCombo->insertItem(i18n("3rd"));
01231     mWeekCombo->insertItem(i18n("4th"));
01232     mWeekCombo->insertItem(i18n("5th"));
01233     mWeekCombo->insertItem(i18n("Last Monday in March", "Last"));
01234     mWeekCombo->insertItem(i18n("2nd Last"));
01235     mWeekCombo->insertItem(i18n("3rd Last"));
01236     mWeekCombo->insertItem(i18n("4th Last"));
01237     mWeekCombo->insertItem(i18n("5th Last"));
01238     if (mEveryWeek)
01239     {
01240         mWeekCombo->insertItem(i18n("Every (Monday...) in month", "Every"));
01241         mWeekCombo->setSizeLimit(11);
01242     }
01243     QWhatsThis::add(mWeekCombo, i18n("Select the week of the month in which to repeat the alarm"));
01244     mWeekCombo->setFixedSize(mWeekCombo->sizeHint());
01245     mWeekCombo->setReadOnly(readOnly);
01246     mPosButton->setFocusWidget(mWeekCombo);
01247 
01248     mDayOfWeekCombo = new ComboBox(false, box);
01249     const KLocale* locale = KGlobal::locale();
01250     for (int i = 0;  i < 7;  ++i)
01251     {
01252         int day = KAlarm::localeDayInWeek_to_weekDay(i);
01253         mDayOfWeekCombo->insertItem(weekDayName(day, locale));
01254     }
01255     mDayOfWeekCombo->setReadOnly(readOnly);
01256     QWhatsThis::add(mDayOfWeekCombo, i18n("Select the day of the week on which to repeat the alarm"));
01257 
01258     box->setStretchFactor(new QWidget(box), 1);    // left adjust the controls
01259     box->setFixedHeight(box->sizeHint().height());
01260     connect(mButtonGroup, SIGNAL(buttonSet(int)), SLOT(clicked(int)));
01261 }
01262 
01263 MonthYearRule::DayPosType MonthYearRule::type() const
01264 {
01265     return (mButtonGroup->selectedId() == mDayButtonId) ? DATE : POS;
01266 }
01267 
01268 void MonthYearRule::setType(MonthYearRule::DayPosType type)
01269 {
01270     mButtonGroup->setButton(type == DATE ? mDayButtonId : mPosButtonId);
01271 }
01272 
01273 void MonthYearRule::setDefaultValues(int dayOfMonth, int dayOfWeek)
01274 {
01275     --dayOfMonth;
01276     mDayCombo->setCurrentItem(dayOfMonth);
01277     mWeekCombo->setCurrentItem(dayOfMonth / 7);
01278     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01279 }
01280 
01281 int MonthYearRule::date() const
01282 {
01283     int daynum  = mDayCombo->currentItem() + 1;
01284     return (daynum <= 31) ? daynum : 31 - daynum;
01285 }
01286 
01287 int MonthYearRule::week() const
01288 {
01289     int weeknum = mWeekCombo->currentItem() + 1;
01290     return (weeknum <= 5) ? weeknum : (weeknum == 11) ? 0 : 5 - weeknum;
01291 }
01292 
01293 int MonthYearRule::dayOfWeek() const
01294 {
01295     return KAlarm::localeDayInWeek_to_weekDay(mDayOfWeekCombo->currentItem());
01296 }
01297 
01298 void MonthYearRule::setDate(int dayOfMonth)
01299 {
01300     mButtonGroup->setButton(mDayButtonId);
01301     mDayCombo->setCurrentItem(dayOfMonth > 0 ? dayOfMonth - 1 : dayOfMonth < 0 ? 30 - dayOfMonth : 0);   // day 0 shouldn't ever occur
01302 }
01303 
01304 void MonthYearRule::setPosition(int week, int dayOfWeek)
01305 {
01306     mButtonGroup->setButton(mPosButtonId);
01307     mWeekCombo->setCurrentItem((week > 0) ? week - 1 : (week < 0) ? 4 - week : mEveryWeek ? 10 : 0);
01308     mDayOfWeekCombo->setCurrentItem(KAlarm::weekDay_to_localeDayInWeek(dayOfWeek));
01309 }
01310 
01311 void MonthYearRule::enableSelection(DayPosType type)
01312 {
01313     bool date = (type == DATE);
01314     mDayCombo->setEnabled(date);
01315     mWeekCombo->setEnabled(!date);
01316     mDayOfWeekCombo->setEnabled(!date);
01317 }
01318 
01319 void MonthYearRule::clicked(int id)
01320 {
01321     enableSelection(id == mDayButtonId ? DATE : POS);
01322 }
01323 
01324 void MonthYearRule::slotDaySelected(int index)
01325 {
01326     daySelected(index <= 30 ? index + 1 : 30 - index);
01327 }
01328 
01329 /******************************************************************************
01330  * Save the state of all controls.
01331  */
01332 void MonthYearRule::saveState()
01333 {
01334     Rule::saveState();
01335     mSavedType = type();
01336     if (mSavedType == DATE)
01337         mSavedDay = date();
01338     else
01339     {
01340         mSavedWeek    = week();
01341         mSavedWeekDay = dayOfWeek();
01342     }
01343 }
01344 
01345 /******************************************************************************
01346  * Check whether any of the controls have changed state since initialisation.
01347  */
01348 bool MonthYearRule::stateChanged() const
01349 {
01350     if (Rule::stateChanged()
01351     ||  mSavedType != type())
01352         return true;
01353     if (mSavedType == DATE)
01354     {
01355         if (mSavedDay != date())
01356             return true;
01357     }
01358     else
01359     {
01360         if (mSavedWeek    != week()
01361         ||  mSavedWeekDay != dayOfWeek())
01362             return true;
01363     }
01364     return false;
01365 }
01366 
01367 
01368 /*=============================================================================
01369 = Class MonthlyRule
01370 = Monthly rule widget.
01371 =============================================================================*/
01372 
01373 MonthlyRule::MonthlyRule(bool readOnly, QWidget* parent, const char* name)
01374     : MonthYearRule(i18n("month(s)"),
01375            i18n("Enter the number of months between repetitions of the alarm"),
01376            false, readOnly, parent, name)
01377 { }
01378 
01379 
01380 /*=============================================================================
01381 = Class YearlyRule
01382 = Yearly rule widget.
01383 =============================================================================*/
01384 
01385 YearlyRule::YearlyRule(bool readOnly, QWidget* parent, const char* name)
01386     : MonthYearRule(i18n("year(s)"),
01387            i18n("Enter the number of years between repetitions of the alarm"),
01388            true, readOnly, parent, name)
01389 {
01390     // Set up the month selection widgets
01391     QBoxLayout* hlayout = new QHBoxLayout(layout(), KDialog::spacingHint());
01392     QLabel* label = new QLabel(i18n("first week of January", "of:"), this);
01393     label->setFixedSize(label->sizeHint());
01394     hlayout->addWidget(label, 0, Qt::AlignAuto | Qt::AlignTop);
01395 
01396     // List the months of the year.
01397     QWidget* w = new QWidget(this);   // this is to control the QWhatsThis text display area
01398     hlayout->addWidget(w, 1, Qt::AlignAuto);
01399     QGridLayout* grid = new QGridLayout(w, 4, 3, 0, KDialog::spacingHint());
01400     const KLocale* locale = KGlobal::locale();
01401     mMonthBox[0] = new CheckBox(locale->translate("January"), w);
01402     mMonthBox[1] = new CheckBox(locale->translate("February"), w);
01403     mMonthBox[2] = new CheckBox(locale->translate("March"), w);
01404     mMonthBox[3] = new CheckBox(locale->translate("April"), w);
01405     mMonthBox[4] = new CheckBox(locale->translate("May"), w);
01406     mMonthBox[5] = new CheckBox(locale->translate("June"), w);
01407     mMonthBox[6] = new CheckBox(locale->translate("July"), w);
01408     mMonthBox[7] = new CheckBox(locale->translate("August"), w);
01409     mMonthBox[8] = new CheckBox(locale->translate("September"), w);
01410     mMonthBox[9] = new CheckBox(locale->translate("October"), w);
01411     mMonthBox[10] = new CheckBox(locale->translate("November"), w);
01412     mMonthBox[11] = new CheckBox(locale->translate("December"), w);
01413     for (int i = 0;  i < 12;  ++i)
01414     {
01415         mMonthBox[i]->setFixedSize(mMonthBox[i]->sizeHint());
01416         mMonthBox[i]->setReadOnly(readOnly);
01417         grid->addWidget(mMonthBox[i], i%4, i/4, Qt::AlignAuto);
01418     }
01419     connect(mMonthBox[1], SIGNAL(toggled(bool)), SLOT(enableFeb29()));
01420     w->setFixedHeight(w->sizeHint().height());
01421     QWhatsThis::add(w, i18n("Select the months of the year in which to repeat the alarm"));
01422 
01423     // February 29th handling option
01424     QHBox* f29box = new QHBox(this);
01425     layout()->addWidget(f29box);
01426     QHBox* box = new QHBox(f29box);    // this is to control the QWhatsThis text display area
01427     box->setSpacing(KDialog::spacingHint());
01428     mFeb29Label = new QLabel(i18n("February 2&9th alarm in non-leap years:"), box);
01429     mFeb29Label->setFixedSize(mFeb29Label->sizeHint());
01430     mFeb29Combo = new ComboBox(false, box);
01431     mFeb29Combo->insertItem(i18n("No date", "None"));
01432     mFeb29Combo->insertItem(i18n("1st March (short form)", "1 Mar"));
01433     mFeb29Combo->insertItem(i18n("28th February (short form)", "28 Feb"));
01434     mFeb29Combo->setFixedSize(mFeb29Combo->sizeHint());
01435     mFeb29Combo->setReadOnly(readOnly);
01436     mFeb29Label->setBuddy(mFeb29Combo);
01437     box->setFixedSize(box->sizeHint());
01438     QWhatsThis::add(box,
01439           i18n("Select which date, if any, the February 29th alarm should trigger in non-leap years"));
01440     new QWidget(f29box);     // left adjust the visible widgets
01441     f29box->setFixedHeight(f29box->sizeHint().height());
01442 }
01443 
01444 void YearlyRule::setDefaultValues(int dayOfMonth, int dayOfWeek, int month)
01445 {
01446     MonthYearRule::setDefaultValues(dayOfMonth, dayOfWeek);
01447     --month;
01448     for (int i = 0;  i < 12;  ++i)
01449         mMonthBox[i]->setChecked(i == month);
01450     setFeb29Type(Preferences::defaultFeb29Type());
01451     daySelected(dayOfMonth);     // enable/disable month checkboxes as appropriate
01452 }
01453 
01454 /******************************************************************************
01455  * Fetch which months have been checked (1 - 12).
01456  * Reply = true if February has been checked.
01457  */
01458 QValueList<int> YearlyRule::months() const
01459 {
01460     QValueList<int> mnths;
01461     for (int i = 0;  i < 12;  ++i)
01462         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01463             mnths.append(i + 1);
01464     return mnths;
01465 }
01466 
01467 /******************************************************************************
01468  * Check/uncheck each month of the year according to the specified list.
01469  */
01470 void YearlyRule::setMonths(const QValueList<int>& mnths)
01471 {
01472     bool checked[12];
01473     for (int i = 0;  i < 12;  ++i)
01474         checked[i] = false;
01475     for (QValueListConstIterator<int> it = mnths.begin();  it != mnths.end();  ++it)
01476         checked[(*it) - 1] = true;
01477     for (int i = 0;  i < 12;  ++i)
01478         mMonthBox[i]->setChecked(checked[i]);
01479     enableFeb29();
01480 }
01481 
01482 /******************************************************************************
01483  * Return the date for February 29th alarms in non-leap years.
01484  */
01485 KARecurrence::Feb29Type YearlyRule::feb29Type() const
01486 {
01487     if (mFeb29Combo->isEnabled())
01488     {
01489         switch (mFeb29Combo->currentItem())
01490         {
01491             case 1:   return KARecurrence::FEB29_MAR1;
01492             case 2:   return KARecurrence::FEB29_FEB28;
01493             default:  break;
01494         }
01495     }
01496     return KARecurrence::FEB29_FEB29;
01497 }
01498 
01499 /******************************************************************************
01500  * Set the date for February 29th alarms to trigger in non-leap years.
01501  */
01502 void YearlyRule::setFeb29Type(KARecurrence::Feb29Type type)
01503 {
01504     int index;
01505     switch (type)
01506     {
01507         default:
01508         case KARecurrence::FEB29_FEB29:  index = 0;  break;
01509         case KARecurrence::FEB29_MAR1:   index = 1;  break;
01510         case KARecurrence::FEB29_FEB28:  index = 2;  break;
01511     }
01512     mFeb29Combo->setCurrentItem(index);
01513 }
01514 
01515 /******************************************************************************
01516  * Validate: check that at least one month is selected.
01517  */
01518 QWidget* YearlyRule::validate(QString& errorMessage)
01519 {
01520     for (int i = 0;  i < 12;  ++i)
01521         if (mMonthBox[i]->isChecked()  &&  mMonthBox[i]->isEnabled())
01522             return 0;
01523     errorMessage = i18n("No month selected");
01524     return mMonthBox[0];
01525 }
01526 
01527 /******************************************************************************
01528  * Called when a yearly recurrence type radio button is clicked,
01529  * to enable/disable month checkboxes as appropriate for the date selected.
01530  */
01531 void YearlyRule::clicked(int id)
01532 {
01533     MonthYearRule::clicked(id);
01534     daySelected(buttonType(id) == DATE ? date() : 1);
01535 }
01536 
01537 /******************************************************************************
01538  * Called when a day of the month is selected in a yearly recurrence, to
01539  * disable months for which the day is out of range.
01540  */
01541 void YearlyRule::daySelected(int day)
01542 {
01543     mMonthBox[1]->setEnabled(day <= 29);  // February
01544     bool enable = (day != 31);
01545     mMonthBox[3]->setEnabled(enable);     // April
01546     mMonthBox[5]->setEnabled(enable);     // June
01547     mMonthBox[8]->setEnabled(enable);     // September
01548     mMonthBox[10]->setEnabled(enable);    // November
01549     enableFeb29();
01550 }
01551 
01552 /******************************************************************************
01553  * Enable/disable the February 29th combo box depending on whether February
01554  * 29th is selected.
01555  */
01556 void YearlyRule::enableFeb29()
01557 {
01558     bool enable = (type() == DATE  &&  date() == 29  &&  mMonthBox[1]->isChecked()  &&  mMonthBox[1]->isEnabled());
01559     mFeb29Label->setEnabled(enable);
01560     mFeb29Combo->setEnabled(enable);
01561 }
01562 
01563 /******************************************************************************
01564  * Save the state of all controls.
01565  */
01566 void YearlyRule::saveState()
01567 {
01568     MonthYearRule::saveState();
01569     mSavedMonths    = months();
01570     mSavedFeb29Type = feb29Type();
01571 }
01572 
01573 /******************************************************************************
01574  * Check whether any of the controls have changed state since initialisation.
01575  */
01576 bool YearlyRule::stateChanged() const
01577 {
01578     return (MonthYearRule::stateChanged()
01579         ||  mSavedMonths    != months()
01580         ||  mSavedFeb29Type != feb29Type());
01581 }
01582 
01583 QString weekDayName(int day, const KLocale* locale)
01584 {
01585     switch (day)
01586     {
01587         case 1: return locale->translate("Monday");
01588         case 2: return locale->translate("Tuesday");
01589         case 3: return locale->translate("Wednesday");
01590         case 4: return locale->translate("Thursday");
01591         case 5: return locale->translate("Friday");
01592         case 6: return locale->translate("Saturday");
01593         case 7: return locale->translate("Sunday");
01594     }
01595     return QString();
01596 }
KDE Home | KDE Accessibility Home | Description of Access Keys