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: