File indexing completed on 2024-05-12 05:14:56

0001 /*
0002  *  repetitionbutton.cpp  -  pushbutton and dialog to specify alarm repetition
0003  *  Program:  kalarm
0004  *  SPDX-FileCopyrightText: 2004-2023 David Jarvie <djarvie@kde.org>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "repetitionbutton.h"
0010 
0011 #include "timeselector.h"
0012 #include "lib/buttongroup.h"
0013 #include "lib/radiobutton.h"
0014 #include "lib/spinbox.h"
0015 #include "lib/timeperiod.h"
0016 
0017 #include <KLocalizedString>
0018 
0019 #include <QGroupBox>
0020 #include <QVBoxLayout>
0021 #include <QHBoxLayout>
0022 #include <QDialogButtonBox>
0023 
0024 using namespace KCalendarCore;
0025 
0026 
0027 /*=============================================================================
0028 = Class RepetitionButton
0029 = Button to display the Simple Alarm Repetition dialog.
0030 =============================================================================*/
0031 
0032 RepetitionButton::RepetitionButton(const QString& caption, bool waitForInitialisation, QWidget* parent)
0033     : QPushButton(caption, parent)
0034     , mWaitForInit(waitForInitialisation)
0035 {
0036     setCheckable(true);
0037     setChecked(false);
0038     connect(this, &RepetitionButton::clicked, this, &RepetitionButton::slotPressed);
0039 }
0040 
0041 void RepetitionButton::set(const Repetition& repetition)
0042 {
0043     mRepetition = repetition;
0044     setChecked(mRepetition);
0045 }
0046 
0047 /******************************************************************************
0048 * Set the data for the dialog.
0049 */
0050 void RepetitionButton::set(const Repetition& repetition, bool dateOnly, int maxDuration)
0051 {
0052     mRepetition  = repetition;
0053     mMaxDuration = maxDuration;
0054     mDateOnly    = dateOnly;
0055     setChecked(mRepetition);
0056 }
0057 
0058 /******************************************************************************
0059 * Create the alarm repetition dialog.
0060 * If 'waitForInitialisation' is true, the dialog won't be displayed until set()
0061 * is called to initialise its data.
0062 */
0063 void RepetitionButton::activate(bool waitForInitialisation)
0064 {
0065     if (!mDialog)
0066         mDialog = new RepetitionDlg(i18nc("@title:window", "Alarm Sub-Repetition"), mReadOnly, this);
0067     mDialog->set(mRepetition, mDateOnly, mMaxDuration);
0068     if (waitForInitialisation)
0069         Q_EMIT needsInitialisation();     // request dialog initialisation
0070     else
0071         displayDialog();    // display the dialog now
0072 }
0073 
0074 /******************************************************************************
0075 * Set the data for the dialog and display it.
0076 * To be called only after needsInitialisation() has been emitted.
0077 */
0078 void RepetitionButton::initialise(const Repetition& repetition, bool dateOnly, int maxDuration)
0079 {
0080     mRepetition  = (maxDuration > 0  &&  repetition.intervalMinutes() > maxDuration)
0081                  ? Repetition() : repetition;
0082     mMaxDuration = maxDuration;
0083     mDateOnly    = dateOnly;
0084     if (mDialog)
0085     {
0086         mDialog->set(mRepetition, dateOnly, maxDuration);
0087         displayDialog();    // display the dialog now
0088     }
0089     else
0090         setChecked(mRepetition);
0091 }
0092 
0093 /******************************************************************************
0094 * Display the alarm sub-repetition dialog.
0095 * Alarm repetition has the following restrictions:
0096 * 1) Not allowed for a repeat-at-login alarm
0097 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
0098 * 3) The overall repeat duration must be less than the recurrence interval.
0099 */
0100 void RepetitionButton::displayDialog()
0101 {
0102     bool change = false;
0103     if (mReadOnly)
0104     {
0105         mDialog->setReadOnly(true);
0106         mDialog->exec();
0107     }
0108     else if (mDialog->exec() == QDialog::Accepted)
0109     {
0110         mRepetition = mDialog->repetition();
0111         change = true;
0112     }
0113     setChecked(mRepetition);
0114     delete mDialog;
0115     mDialog = nullptr;
0116     if (change)
0117         Q_EMIT changed();   // delete dialog first, or initialise() will redisplay dialog
0118 }
0119 
0120 
0121 /*=============================================================================
0122 = Class RepetitionDlg
0123 = Simple alarm repetition dialog.
0124 =============================================================================*/
0125 
0126 static const int MAX_COUNT = 9999;    // maximum range for count spinbox
0127 
0128 
0129 RepetitionDlg::RepetitionDlg(const QString& caption, bool readOnly, QWidget* parent)
0130     : QDialog(parent),
0131       mMaxDuration(-1),
0132       mDateOnly(false),
0133       mReadOnly(readOnly)
0134 {
0135     setWindowTitle(caption);
0136 
0137     auto topLayout = new QVBoxLayout(this);
0138 
0139     mTimeSelector = new TimeSelector(i18nc("@option:check Repeat every 10 minutes", "Repeat every"),
0140                       i18nc("@info:whatsthis", "Instead of the alarm triggering just once at each recurrence, "
0141                             "checking this option makes the alarm trigger multiple times at each recurrence."),
0142                       i18nc("@info:whatsthis", "Enter the time between repetitions of the alarm"),
0143                       true, this);
0144     connect(mTimeSelector, &TimeSelector::valueChanged, this, &RepetitionDlg::intervalChanged);
0145     connect(mTimeSelector, &TimeSelector::toggled, this, &RepetitionDlg::repetitionToggled);
0146     topLayout->addWidget(mTimeSelector, 0, Qt::AlignLeft);
0147 
0148     mButtonBox = new QGroupBox(this);
0149     topLayout->addWidget(mButtonBox);
0150     mButtonGroup = new ButtonGroup(mButtonBox);
0151     connect(mButtonGroup, &ButtonGroup::buttonSet, this, &RepetitionDlg::typeClicked);
0152 
0153     auto vlayout = new QVBoxLayout(mButtonBox);
0154     auto layout = new QHBoxLayout();
0155     layout->setContentsMargins(0, 0, 0, 0);
0156     vlayout->addLayout(layout);
0157     mCountButton = new RadioButton(i18nc("@option:radio", "Number of repetitions:"), mButtonBox);
0158     mCountButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify the number of times the alarm should repeat after each recurrence"));
0159     mButtonGroup->addButton(mCountButton);
0160     layout->addWidget(mCountButton);
0161     mCount = new SpinBox(1, MAX_COUNT, mButtonBox);
0162     mCount->setSingleShiftStep(10);
0163     mCount->setSelectOnStep(false);
0164     connect(mCount, &SpinBox::valueChanged, this, &RepetitionDlg::countChanged);
0165     mCount->setWhatsThis(i18nc("@info:whatsthis", "Enter the number of times to trigger the alarm after its initial occurrence"));
0166     layout->addWidget(mCount);
0167     mCountButton->setFocusWidget(mCount);
0168     layout->addStretch();
0169 
0170     layout = new QHBoxLayout();
0171     layout->setContentsMargins(0, 0, 0, 0);
0172     vlayout->addLayout(layout);
0173     mDurationButton = new RadioButton(i18nc("@option:radio", "Duration:"), mButtonBox);
0174     mDurationButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify how long the alarm is to be repeated"));
0175     mButtonGroup->addButton(mDurationButton);
0176     layout->addWidget(mDurationButton);
0177     mDuration = new TimePeriod(TimePeriod::ShowMinutes, mButtonBox);
0178     connect(mDuration, &TimePeriod::valueChanged, this, &RepetitionDlg::durationChanged);
0179     mDuration->setWhatsThis(i18nc("@info:whatsthis", "Enter the length of time to repeat the alarm"));
0180     layout->addWidget(mDuration);
0181     mDurationButton->setFocusWidget(mDuration);
0182     layout->addStretch();
0183 
0184     auto buttonBox = new QDialogButtonBox(this);
0185     buttonBox->addButton(QDialogButtonBox::Ok);
0186     buttonBox->addButton(QDialogButtonBox::Cancel);
0187     connect(buttonBox, &QDialogButtonBox::accepted,
0188             this, &QDialog::accept);
0189     connect(buttonBox, &QDialogButtonBox::rejected,
0190             this, &QDialog::reject);
0191     topLayout->addWidget(buttonBox);
0192 
0193     mCountButton->setChecked(true);
0194     repetitionToggled(false);
0195     setReadOnly(mReadOnly);
0196 }
0197 
0198 /******************************************************************************
0199 * Set the state of all controls to reflect the data in the specified alarm.
0200 */
0201 void RepetitionDlg::set(const Repetition& repetition, bool dateOnly, int maxDuration)
0202 {
0203     if (dateOnly != mDateOnly)
0204     {
0205         mDateOnly = dateOnly;
0206         mTimeSelector->setDateOnly(mDateOnly);
0207         mDuration->setDateOnly(mDateOnly);
0208     }
0209     mMaxDuration = maxDuration;
0210     if (mMaxDuration)
0211     {
0212         int maxhm = (mMaxDuration > 0) ? mMaxDuration : 9999;
0213         int maxdw = (mMaxDuration > 0) ? mMaxDuration / 1440 : 9999;
0214         mTimeSelector->setMaximum(maxhm, maxdw);
0215         mDuration->setMaximum(maxhm, maxdw);
0216     }
0217     // Set the units - needed later if the control is unchecked initially.
0218     TimePeriod::Units units = mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes;
0219     mTimeSelector->setPeriod(repetition.interval(), mDateOnly, units);
0220     if (!mMaxDuration  ||  !repetition)
0221         mTimeSelector->setChecked(false);
0222     else
0223     {
0224         bool on = mTimeSelector->isChecked();
0225         repetitionToggled(on);    // enable/disable controls
0226         if (on)
0227             intervalChanged(repetition.interval());    // ensure mCount range is set
0228         mCount->setValue(repetition.count());
0229         mDuration->setPeriod(repetition.duration(), mDateOnly, units);
0230         mCountButton->setChecked(true);
0231     }
0232     mTimeSelector->setEnabled(mMaxDuration);
0233 }
0234 
0235 /******************************************************************************
0236 * Set the read-only status.
0237 */
0238 void RepetitionDlg::setReadOnly(bool ro)
0239 {
0240     ro = ro || mReadOnly;
0241     mTimeSelector->setReadOnly(ro);
0242     mCountButton->setReadOnly(ro);
0243     mCount->setReadOnly(ro);
0244     mDurationButton->setReadOnly(ro);
0245     mDuration->setReadOnly(ro);
0246 }
0247 
0248 /******************************************************************************
0249 * Get the entered interval and repeat count.
0250 */
0251 Repetition RepetitionDlg::repetition() const
0252 {
0253     int count = 0;
0254     Duration interval = mTimeSelector->period();
0255     if (!interval.isNull())
0256     {
0257         if (mCountButton->isChecked())
0258             count = mCount->value();
0259         else if (mDurationButton->isChecked())
0260             count = mDuration->period().asSeconds() / interval.asSeconds();
0261     }
0262     return Repetition(interval, count);
0263 }
0264 
0265 /******************************************************************************
0266 * Called when the time interval widget has changed value.
0267 * Adjust the maximum repetition count accordingly.
0268 */
0269 void RepetitionDlg::intervalChanged(const Duration& interval)
0270 {
0271     if (mTimeSelector->isChecked()  &&  interval.asSeconds() > 0)
0272     {
0273         mCount->setRange(1, (mMaxDuration >= 0 ? mMaxDuration / (interval.asSeconds()/60) : MAX_COUNT));
0274         if (mCountButton->isChecked())
0275             countChanged(mCount->value());
0276         else
0277             durationChanged(mDuration->period());
0278     }
0279 }
0280 
0281 /******************************************************************************
0282 * Called when the count spinbox has changed value.
0283 * Adjust the duration accordingly.
0284 */
0285 void RepetitionDlg::countChanged(int count)
0286 {
0287     Duration interval = mTimeSelector->period();
0288     if (!interval.isNull())
0289     {
0290         bool blocked = mDuration->signalsBlocked();
0291         mDuration->blockSignals(true);
0292         mDuration->setPeriod(interval * count, mDateOnly,
0293                               (mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes));
0294         mDuration->blockSignals(blocked);
0295     }
0296 }
0297 
0298 /******************************************************************************
0299 * Called when the duration widget has changed value.
0300 * Adjust the count accordingly.
0301 */
0302 void RepetitionDlg::durationChanged(const Duration& duration)
0303 {
0304     Duration interval = mTimeSelector->period();
0305     if (!interval.isNull())
0306     {
0307         bool blocked = mCount->signalsBlocked();
0308         mCount->blockSignals(true);
0309         mCount->setValue(duration.asSeconds() / interval.asSeconds());
0310         mCount->blockSignals(blocked);
0311     }
0312 }
0313 
0314 /******************************************************************************
0315 * Called when the time period widget is toggled on or off.
0316 */
0317 void RepetitionDlg::repetitionToggled(bool on)
0318 {
0319     if (mMaxDuration == 0)
0320         on = false;
0321     mButtonBox->setEnabled(on);
0322     mCount->setEnabled(on  &&  mCountButton->isChecked());
0323     mDuration->setEnabled(on  &&  mDurationButton->isChecked());
0324 }
0325 
0326 /******************************************************************************
0327 * Called when one of the count or duration radio buttons is toggled.
0328 */
0329 void RepetitionDlg::typeClicked()
0330 {
0331     if (mTimeSelector->isChecked())
0332     {
0333         mCount->setEnabled(mCountButton->isChecked());
0334         mDuration->setEnabled(mDurationButton->isChecked());
0335     }
0336 }
0337 
0338 #include "moc_repetitionbutton.cpp"
0339 
0340 // vim: et sw=4: