File indexing completed on 2024-05-12 05:14:38
0001 /* 0002 * alarmtimewidget.cpp - alarm date/time entry widget 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2001-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "alarmtimewidget.h" 0010 0011 #include "preferences.h" 0012 #include "lib/buttongroup.h" 0013 #include "lib/checkbox.h" 0014 #include "lib/messagebox.h" 0015 #include "lib/pushbutton.h" 0016 #include "lib/radiobutton.h" 0017 #include "lib/synchtimer.h" 0018 #include "lib/timeedit.h" 0019 #include "lib/timeperiod.h" 0020 #include "lib/timespinbox.h" 0021 #include "lib/timezonecombo.h" 0022 0023 #include <KDateComboBox> 0024 #include <KLocalizedString> 0025 0026 #include <QTimeZone> 0027 #include <QGroupBox> 0028 #include <QGridLayout> 0029 #include <QLabel> 0030 #include <QHBoxLayout> 0031 #include <QStandardItemModel> 0032 0033 //clazy:excludeall=non-pod-global-static 0034 0035 static const QTime time_23_59(23, 59); 0036 0037 0038 const int AlarmTimeWidget::maxDelayTime = 999*60 + 59; // < 1000 hours 0039 0040 QString AlarmTimeWidget::i18n_TimeAfterPeriod() 0041 { 0042 return i18nc("@info", "Enter the length of time (in hours and minutes) after " 0043 "the current time to schedule the alarm."); 0044 } 0045 0046 0047 /****************************************************************************** 0048 * Construct a widget with a group box and title. 0049 */ 0050 AlarmTimeWidget::AlarmTimeWidget(const QString& groupBoxTitle, Mode mode, QWidget* parent) 0051 : QFrame(parent) 0052 { 0053 init(mode, groupBoxTitle); 0054 } 0055 0056 /****************************************************************************** 0057 * Construct a widget without a group box or title. 0058 */ 0059 AlarmTimeWidget::AlarmTimeWidget(Mode mode, QWidget* parent) 0060 : QFrame(parent) 0061 { 0062 init(mode); 0063 } 0064 0065 void AlarmTimeWidget::init(Mode mode, const QString& title) 0066 { 0067 static const QString recurText = i18nc("@info", 0068 "If a recurrence is configured, the start date/time will be adjusted " 0069 "to the first recurrence on or after the entered date/time."); 0070 static const QString tzText = i18nc("@info", 0071 "This uses KAlarm's default time zone, set in the Configuration dialog."); 0072 0073 QWidget* topWidget; 0074 if (title.isEmpty()) 0075 topWidget = this; 0076 else 0077 { 0078 QBoxLayout* layout = new QVBoxLayout(this); 0079 layout->setContentsMargins(0, 0, 0, 0); 0080 layout->setSpacing(0); 0081 topWidget = new QGroupBox(title, this); 0082 layout->addWidget(topWidget); 0083 } 0084 mDeferring = mode & DEFER_TIME; 0085 mButtonGroup = new ButtonGroup(this); 0086 connect(mButtonGroup, &ButtonGroup::buttonSet, this, &AlarmTimeWidget::slotButtonSet); 0087 auto topLayout = new QVBoxLayout(topWidget); 0088 if (title.isEmpty()) 0089 topLayout->setContentsMargins(0, 0, 0, 0); 0090 0091 // At time radio button/label 0092 mAtTimeRadio = new RadioButton((mDeferring ? i18nc("@option:radio", "Defer until:") : i18nc("@option:radio", "At date/time:")), topWidget); 0093 mAtTimeRadio->setWhatsThis(mDeferring ? i18nc("@info:whatsthis", "Reschedule the alarm to the specified date and time.") 0094 : i18nc("@info:whatsthis", "Specify the date, or date and time, to schedule the alarm.")); 0095 mButtonGroup->addButton(mAtTimeRadio); 0096 0097 // Date edit box 0098 mDateEdit = new KDateComboBox(topWidget); 0099 mDateEdit->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker); 0100 connect(mDateEdit, &KDateComboBox::dateChanged, this, &AlarmTimeWidget::dateTimeChanged); 0101 connect(mDateEdit, &KDateComboBox::dateEdited, this, &AlarmTimeWidget::dateTimeChanged); 0102 mDateEdit->setWhatsThis(xi18nc("@info:whatsthis", 0103 "<para>Enter the date to schedule the alarm.</para>" 0104 "<para>%1</para>", (mDeferring ? tzText : recurText))); 0105 mAtTimeRadio->setFocusWidget(mDateEdit); 0106 0107 // Time edit box and Any time checkbox 0108 QWidget* timeBox = new QWidget(topWidget); 0109 auto timeBoxHLayout = new QHBoxLayout(timeBox); 0110 timeBoxHLayout->setContentsMargins(0, 0, 0, 0); 0111 mTimeEdit = new TimeEdit(timeBox); 0112 timeBoxHLayout->addWidget(mTimeEdit); 0113 connect(mTimeEdit, &TimeEdit::valueChanged, this, &AlarmTimeWidget::dateTimeChanged); 0114 mTimeEdit->setWhatsThis(xi18nc("@info:whatsthis", 0115 "<para>Enter the time to schedule the alarm.</para>" 0116 "<para>%1</para>" 0117 "<para>%2</para>", (mDeferring ? tzText : recurText), TimeSpinBox::shiftWhatsThis())); 0118 0119 mAnyTime = -1; // current status is uninitialised 0120 if (mode == DEFER_TIME) 0121 { 0122 mAnyTimeAllowed = false; 0123 mAnyTimeCheckBox = nullptr; 0124 } 0125 else 0126 { 0127 mAnyTimeAllowed = true; 0128 mAnyTimeCheckBox = new CheckBox(i18nc("@option:check", "Any time"), timeBox); 0129 timeBoxHLayout->addWidget(mAnyTimeCheckBox); 0130 connect(mAnyTimeCheckBox, &CheckBox::toggled, this, &AlarmTimeWidget::slotAnyTimeToggled); 0131 mAnyTimeCheckBox->setToolTip(i18nc("@info:tooltip", "Set only a date (without a time) for the alarm")); 0132 mAnyTimeCheckBox->setWhatsThis(i18nc("@info:whatsthis", 0133 "Check to specify only a date (without a time) for the alarm. The alarm will trigger at the first opportunity on the selected date.")); 0134 } 0135 0136 // 'Time from now' radio button/label 0137 mAfterTimeRadio = new RadioButton((mDeferring ? i18nc("@option:radio Defer for time interval", "Defer for:") : i18nc("@option:radio", "Time from now:")), topWidget); 0138 mAfterTimeRadio->setWhatsThis(mDeferring ? i18nc("@info:whatsthis", "Reschedule the alarm for the specified time interval after now.") 0139 : i18nc("@info:whatsthis", "Schedule the alarm after the specified time interval from now.")); 0140 mButtonGroup->addButton(mAfterTimeRadio); 0141 0142 if (mDeferring) 0143 { 0144 // Delay time period 0145 mDelayTimePeriod = new TimePeriod(TimePeriod::ShowMinutes, topWidget); 0146 mDelayTimePeriod->setPeriod(0, false, TimePeriod::HoursMinutes); 0147 connect(mDelayTimePeriod, &TimePeriod::valueChanged, 0148 this, [this](const KCalendarCore::Duration& period) { delayTimeChanged(period.asSeconds()/60); }); 0149 mDelayTimePeriod->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para>", i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis())); 0150 mAfterTimeRadio->setFocusWidget(mDelayTimePeriod); 0151 0152 // Delay presets 0153 mPresetsCombo = new ComboBox(topWidget); 0154 mPresetsCombo->setEditable(false); 0155 mPresetsCombo->setPlaceholderText(i18nc("@item:inlistbox", "Preset")); 0156 const KLocalizedString minutesText = ki18ncp("@item:inlistbox", "1 minute", "%1 minutes"); 0157 const KLocalizedString hoursText = ki18ncp("@item:inlistbox", "1 hour", "%1 hours"); 0158 mPresetsCombo->addItem(minutesText.subs(5).toString(), 5); 0159 mPresetsCombo->addItem(minutesText.subs(10).toString(), 10); 0160 mPresetsCombo->addItem(minutesText.subs(15).toString(), 15); 0161 mPresetsCombo->addItem(minutesText.subs(30).toString(), 30); 0162 mPresetsCombo->addItem(minutesText.subs(45).toString(), 45); 0163 mPresetsCombo->addItem(hoursText.subs(1).toString(), 60); 0164 mPresetsCombo->addItem(hoursText.subs(3).toString(), 180); 0165 mPresetsCombo->addItem(i18nc("@item:inlistbox", "1 day"), 1440); 0166 mPresetsCombo->addItem(i18nc("@item:inlistbox", "1 week"), 7*1440); 0167 mPresetsCombo->setCurrentIndex(-1); 0168 connect(mPresetsCombo, &ComboBox::activated, this, &AlarmTimeWidget::slotPresetSelected); 0169 mPresetsCombo->setToolTip(i18nc("@info:tooltip", "Select time delay from a list of preset values")); 0170 } 0171 else 0172 { 0173 // Delay time spin box 0174 mDelayTimeEdit = new TimeSpinBox(1, maxDelayTime, topWidget); 0175 mDelayTimeEdit->setValue(1439); 0176 connect(mDelayTimeEdit, &TimeSpinBox::valueChanged, this, &AlarmTimeWidget::delayTimeChanged); 0177 mDelayTimeEdit->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para><para>%3</para>", i18n_TimeAfterPeriod(), recurText, TimeSpinBox::shiftWhatsThis())); 0178 mAfterTimeRadio->setFocusWidget(mDelayTimeEdit); 0179 } 0180 0181 // Set up the layout 0182 auto grid = new QGridLayout(); 0183 grid->setContentsMargins(0, 0, 0, 0); 0184 topLayout->addLayout(grid); 0185 const int atRow = mDeferring ? 2 : 0; 0186 const int afterRow = mDeferring ? 0 : 2; 0187 grid->addWidget(mAtTimeRadio, atRow, 0, Qt::AlignLeft); 0188 auto hLayout = new QHBoxLayout; 0189 hLayout->addWidget(mDateEdit); 0190 hLayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0191 hLayout->addWidget(timeBox); 0192 grid->addLayout(hLayout, atRow, 1, Qt::AlignLeft); 0193 grid->setRowStretch(1, 1); 0194 grid->addWidget(mAfterTimeRadio, afterRow, 0, Qt::AlignLeft); 0195 if (mDelayTimeEdit) 0196 grid->addWidget(mDelayTimeEdit, 2, 1, Qt::AlignLeft); 0197 else 0198 { 0199 hLayout = new QHBoxLayout; 0200 hLayout->addWidget(mDelayTimePeriod); 0201 hLayout->addSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0202 hLayout->addWidget(mPresetsCombo); 0203 grid->addLayout(hLayout, 0, 1, Qt::AlignLeft); 0204 } 0205 0206 if (!mDeferring) 0207 { 0208 // Time zone selection push button 0209 mTimeZoneButton = new PushButton(i18nc("@action:button", "Time Zone..."), topWidget); 0210 connect(mTimeZoneButton, &PushButton::clicked, this, &AlarmTimeWidget::showTimeZoneSelector); 0211 mTimeZoneButton->setToolTip(i18nc("@info:tooltip", "Choose a time zone for this alarm")); 0212 mTimeZoneButton->setWhatsThis(i18nc("@info:whatsthis", 0213 "Choose a time zone for this alarm which is different from the default time zone set in KAlarm's configuration dialog.")); 0214 grid->addWidget(mTimeZoneButton, 2, 2, 1, 2, Qt::AlignRight); 0215 0216 grid->setColumnStretch(2, 1); 0217 topLayout->addStretch(); 0218 0219 auto layout = new QHBoxLayout(); 0220 topLayout->addLayout(layout); 0221 layout->setSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0222 0223 // Time zone selector 0224 mTimeZoneBox = new QWidget(topWidget); // this is to control the QWhatsThis text display area 0225 auto hlayout = new QHBoxLayout(mTimeZoneBox); 0226 hlayout->setContentsMargins(0, 0, 0, 0); 0227 QLabel* label = new QLabel(i18nc("@label:listbox", "Time zone:"), mTimeZoneBox); 0228 hlayout->addWidget(label); 0229 mTimeZone = new TimeZoneCombo(mTimeZoneBox); 0230 hlayout->addWidget(mTimeZone); 0231 mTimeZone->setMaxVisibleItems(15); 0232 connect(mTimeZone, &TimeZoneCombo::activated, this, &AlarmTimeWidget::slotTimeZoneChanged); 0233 mTimeZoneBox->setWhatsThis(i18nc("@info:whatsthis", "Select the time zone to use for this alarm.")); 0234 label->setBuddy(mTimeZone); 0235 layout->addWidget(mTimeZoneBox); 0236 layout->addStretch(); 0237 0238 // Initially show only the time zone button, not time zone selector 0239 mTimeZoneBox->hide(); 0240 } 0241 0242 // Initialise the radio button statuses 0243 mAtTimeRadio->setChecked(true); 0244 slotButtonSet(mAtTimeRadio); 0245 0246 // Timeout every minute to update alarm time fields. 0247 MinuteTimer::connect(this, SLOT(updateTimes())); 0248 } 0249 0250 /****************************************************************************** 0251 * Set or clear read-only status for the controls 0252 */ 0253 void AlarmTimeWidget::setReadOnly(bool ro) 0254 { 0255 mAtTimeRadio->setReadOnly(ro); 0256 mDateEdit->setOptions(ro ? KDateComboBox::Options{} : KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker); 0257 mTimeEdit->setReadOnly(ro); 0258 if (mAnyTimeCheckBox) 0259 mAnyTimeCheckBox->setReadOnly(ro); 0260 mAfterTimeRadio->setReadOnly(ro); 0261 if (!mDeferring) 0262 mTimeZone->setReadOnly(ro); 0263 if (mDelayTimeEdit) 0264 mDelayTimeEdit->setReadOnly(ro); 0265 else 0266 mDelayTimePeriod->setReadOnly(ro); 0267 } 0268 0269 /****************************************************************************** 0270 * Select the "Time from now" radio button, or if minutes < 0, select 0271 * 'At date/time'. 0272 */ 0273 void AlarmTimeWidget::selectTimeFromNow(int minutes) 0274 { 0275 if (minutes >= 0) 0276 { 0277 mAfterTimeRadio->setChecked(true); 0278 if (minutes > 0) 0279 { 0280 if (mDelayTimeEdit) 0281 mDelayTimeEdit->setValue(minutes); 0282 else 0283 mDelayTimePeriod->setMinutes(minutes); 0284 } 0285 } 0286 else 0287 mAtTimeRadio->setChecked(true); 0288 } 0289 0290 /****************************************************************************** 0291 * Fetch the entered date/time. 0292 * If 'checkExpired' is true and the entered value <= current time, an error occurs. 0293 * If 'minsFromNow' is non-null, it is set to the number of minutes' delay selected, 0294 * or to zero if a date/time was entered. 0295 * In this case, if 'showErrorMessage' is true, output an error message. 0296 * 'errorWidget' if non-null, is set to point to the widget containing the error. 0297 * Reply = invalid date/time if error. 0298 */ 0299 KADateTime AlarmTimeWidget::getDateTime(int* minsFromNow, bool checkExpired, bool showErrorMessage, QWidget** errorWidget) const 0300 { 0301 if (minsFromNow) 0302 *minsFromNow = 0; 0303 if (errorWidget) 0304 *errorWidget = nullptr; 0305 KADateTime now = KADateTime::currentUtcDateTime(); 0306 now.setTime(QTime(now.time().hour(), now.time().minute(), 0)); 0307 if (!mAtTimeRadio->isChecked()) 0308 { 0309 // A relative time has been entered. 0310 if ((mDelayTimeEdit && !mDelayTimeEdit->isValid()) 0311 || (mDelayTimePeriod && !mDelayTimePeriod->isValid())) 0312 { 0313 if (showErrorMessage) 0314 KAMessageBox::error(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid time")); 0315 if (errorWidget) 0316 *errorWidget = mDelayTimeEdit ? (QWidget*)mDelayTimeEdit : (QWidget*)mDelayTimePeriod; 0317 return {}; 0318 } 0319 const int delayMins = mDelayTimeEdit ? mDelayTimeEdit->value() : mDelayTimePeriod->minutes(); 0320 if (minsFromNow) 0321 *minsFromNow = delayMins; 0322 return now.addSecs(delayMins * 60).toTimeSpec(mTimeSpec); 0323 } 0324 else 0325 { 0326 // An absolute time has been entered. 0327 const bool dateOnly = mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked(); 0328 if (!mDateEdit->date().isValid() || !mTimeEdit->isValid()) 0329 { 0330 // The date and/or time is invalid 0331 if (!mDateEdit->date().isValid()) 0332 { 0333 if (showErrorMessage) 0334 KAMessageBox::error(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid date")); 0335 if (errorWidget) 0336 *errorWidget = mDateEdit; 0337 } 0338 else 0339 { 0340 if (showErrorMessage) 0341 KAMessageBox::error(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Invalid time")); 0342 if (errorWidget) 0343 *errorWidget = mTimeEdit; 0344 } 0345 return {}; 0346 } 0347 0348 KADateTime result; 0349 if (dateOnly) 0350 { 0351 result = KADateTime(mDateEdit->date(), mTimeSpec); 0352 if (checkExpired && result.date() < now.date()) 0353 { 0354 if (showErrorMessage) 0355 KAMessageBox::error(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Alarm date has already expired")); 0356 if (errorWidget) 0357 *errorWidget = mDateEdit; 0358 return {}; 0359 } 0360 } 0361 else 0362 { 0363 result = KADateTime(mDateEdit->date(), mTimeEdit->time(), mTimeSpec); 0364 if (checkExpired && result <= now.addSecs(1)) 0365 { 0366 if (showErrorMessage) 0367 KAMessageBox::error(const_cast<AlarmTimeWidget*>(this), i18nc("@info", "Alarm time has already expired")); 0368 if (errorWidget) 0369 *errorWidget = mTimeEdit; 0370 return {}; 0371 } 0372 } 0373 return result; 0374 } 0375 } 0376 0377 /****************************************************************************** 0378 * Set the date/time. 0379 */ 0380 void AlarmTimeWidget::setDateTime(const DateTime& dt) 0381 { 0382 // Set the time zone first so that the call to dateTimeChanged() works correctly. 0383 if (mDeferring) 0384 mTimeSpec = dt.timeSpec().isValid() ? dt.timeSpec() : KADateTime::LocalZone; 0385 else 0386 { 0387 const QTimeZone tz = (dt.timeSpec() == KADateTime::LocalZone) ? QTimeZone() : dt.timeZone(); 0388 mTimeZone->setTimeZone(tz); 0389 slotTimeZoneChanged(); 0390 } 0391 0392 if (dt.date().isValid()) 0393 { 0394 mTimeEdit->setValue(dt.effectiveTime()); 0395 mDateEdit->setDate(dt.date()); 0396 dateTimeChanged(); // update the delay time edit box 0397 } 0398 else 0399 { 0400 mTimeEdit->setValid(false); 0401 mDateEdit->setDate(QDate()); 0402 if (mDelayTimeEdit) 0403 mDelayTimeEdit->setValid(false); 0404 else 0405 mDelayTimePeriod->setValid(false); 0406 } 0407 if (mAnyTimeCheckBox) 0408 { 0409 const bool dateOnly = dt.isDateOnly(); 0410 if (dateOnly) 0411 mAnyTimeAllowed = true; 0412 mAnyTimeCheckBox->setChecked(dateOnly); 0413 setAnyTime(); 0414 } 0415 } 0416 0417 /****************************************************************************** 0418 * Set the minimum date/time to track the current time. 0419 */ 0420 void AlarmTimeWidget::setMinDateTimeIsCurrent() 0421 { 0422 mMinDateTimeIsNow = true; 0423 mMinDateTime = KADateTime(); 0424 const KADateTime now = KADateTime::currentDateTime(mTimeSpec); 0425 mDateEdit->setMinimumDate(now.date()); 0426 setMaxMinTimeIf(now); 0427 } 0428 0429 /****************************************************************************** 0430 * Set the minimum date/time, adjusting the entered date/time if necessary. 0431 * If 'dt' is invalid, any current minimum date/time is cleared. 0432 */ 0433 void AlarmTimeWidget::setMinDateTime(const KADateTime& dt) 0434 { 0435 mMinDateTimeIsNow = false; 0436 mMinDateTime = dt.toTimeSpec(mTimeSpec); 0437 mDateEdit->setMinimumDate(mMinDateTime.date()); 0438 setMaxMinTimeIf(KADateTime::currentDateTime(mTimeSpec)); 0439 } 0440 0441 /****************************************************************************** 0442 * Set the maximum date/time, adjusting the entered date/time if necessary. 0443 * If 'dt' is invalid, any current maximum date/time is cleared. 0444 */ 0445 void AlarmTimeWidget::setMaxDateTime(const DateTime& dt) 0446 { 0447 mPastMax = false; 0448 if (dt.isValid() && dt.isDateOnly()) 0449 mMaxDateTime = dt.effectiveKDateTime().addSecs(24*3600 - 60).toTimeSpec(mTimeSpec); 0450 else 0451 mMaxDateTime = dt.kDateTime().toTimeSpec(mTimeSpec); 0452 mDateEdit->setMaximumDate(mMaxDateTime.date()); 0453 const KADateTime now = KADateTime::currentDateTime(mTimeSpec); 0454 setMaxMinTimeIf(now); 0455 setMaxDelayTime(now); 0456 } 0457 0458 /****************************************************************************** 0459 * If the minimum and maximum date/times fall on the same date, set the minimum 0460 * and maximum times in the time edit box. 0461 */ 0462 void AlarmTimeWidget::setMaxMinTimeIf(const KADateTime& now) 0463 { 0464 int mint = 0; 0465 QTime maxt = time_23_59; 0466 mMinMaxTimeSet = false; 0467 if (mMaxDateTime.isValid()) 0468 { 0469 bool set = true; 0470 KADateTime minDT; 0471 if (mMinDateTimeIsNow) 0472 minDT = now.addSecs(60); 0473 else if (mMinDateTime.isValid()) 0474 minDT = mMinDateTime; 0475 else 0476 set = false; 0477 if (set && mMaxDateTime.date() == minDT.date()) 0478 { 0479 // The minimum and maximum times are on the same date, so 0480 // constrain the time value. 0481 mint = minDT.time().hour()*60 + minDT.time().minute(); 0482 maxt = mMaxDateTime.time(); 0483 mMinMaxTimeSet = true; 0484 } 0485 } 0486 mTimeEdit->setMinimum(mint); 0487 mTimeEdit->setMaximum(maxt); 0488 mTimeEdit->setWrapping(!mint && maxt == time_23_59); 0489 } 0490 0491 /****************************************************************************** 0492 * Set the maximum value for the delay time edit box, depending on the maximum 0493 * value for the date/time. 0494 */ 0495 void AlarmTimeWidget::setMaxDelayTime(const KADateTime& now) 0496 { 0497 int maxVal = maxDelayTime; 0498 if (mMaxDateTime.isValid()) 0499 { 0500 if (now.date().daysTo(mMaxDateTime.date()) < 100) // avoid possible 32-bit overflow on secsTo() 0501 { 0502 KADateTime dt(now); 0503 dt.setTime(QTime(now.time().hour(), now.time().minute(), 0)); // round down to nearest minute 0504 maxVal = dt.secsTo(mMaxDateTime) / 60; 0505 if (maxVal > maxDelayTime) 0506 maxVal = maxDelayTime; 0507 } 0508 } 0509 if (mDelayTimeEdit) 0510 mDelayTimeEdit->setMaximum(maxVal); 0511 else 0512 { 0513 mDelayTimePeriod->setMaxMinutes(maxVal); 0514 // Disable all presets greater than the maximum delay minutes 0515 auto* model = qobject_cast<QStandardItemModel*>(mPresetsCombo->model()); 0516 if (model) 0517 { 0518 for (int i = 0, count = mPresetsCombo->count(); i < count; ++i) 0519 { 0520 const int minutes = mPresetsCombo->itemData(i).toInt(); 0521 model->item(i)->setEnabled(minutes <= maxVal); 0522 } 0523 } 0524 } 0525 } 0526 0527 /****************************************************************************** 0528 * Set the status for whether a time is specified, or just a date. 0529 */ 0530 void AlarmTimeWidget::setAnyTime() 0531 { 0532 const int old = mAnyTime; 0533 mAnyTime = (mAtTimeRadio->isChecked() && mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked()) ? 1 : 0; 0534 if (mAnyTime != old) 0535 Q_EMIT dateOnlyToggled(mAnyTime); 0536 } 0537 0538 /****************************************************************************** 0539 * Return whether the "date only" checkbox is selected. 0540 */ 0541 bool AlarmTimeWidget::anyTimeSelected() const 0542 { 0543 return mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked(); 0544 } 0545 0546 /****************************************************************************** 0547 * Enable/disable the "date only" checkbox. 0548 */ 0549 void AlarmTimeWidget::enableAnyTime(bool enable) 0550 { 0551 if (mAnyTimeCheckBox) 0552 { 0553 mAnyTimeAllowed = enable; 0554 const bool at = mAtTimeRadio->isChecked(); 0555 mAnyTimeCheckBox->setEnabled(enable && at); 0556 if (at) 0557 mTimeEdit->setEnabled(!enable || !mAnyTimeCheckBox->isChecked()); 0558 setAnyTime(); 0559 } 0560 } 0561 0562 /****************************************************************************** 0563 * Set the keyboard focus to the time from now field. 0564 */ 0565 void AlarmTimeWidget::focusTimeFromNow() 0566 { 0567 if (!mAtTimeRadio->isChecked()) 0568 { 0569 if (mDelayTimeEdit) 0570 mDelayTimeEdit->setFocus(); 0571 else 0572 mDelayTimePeriod->setFocus(); 0573 } 0574 } 0575 0576 /****************************************************************************** 0577 * Called every minute to update the alarm time data entry fields. 0578 * If the maximum date/time has been reached, a 'pastMax()' signal is emitted. 0579 */ 0580 void AlarmTimeWidget::updateTimes() 0581 { 0582 KADateTime now; 0583 if (mMinDateTimeIsNow) 0584 { 0585 // Make sure that the minimum date is updated when the day changes 0586 now = KADateTime::currentDateTime(mTimeSpec); 0587 mDateEdit->setMinimumDate(now.date()); 0588 } 0589 if (mMaxDateTime.isValid()) 0590 { 0591 if (!now.isValid()) 0592 now = KADateTime::currentDateTime(mTimeSpec); 0593 if (!mPastMax) 0594 { 0595 // Check whether the maximum date/time has now been reached 0596 if (now.date() >= mMaxDateTime.date()) 0597 { 0598 // The current date has reached or has passed the maximum date 0599 if (now.date() > mMaxDateTime.date() 0600 || (!mAnyTime && now.time() > mTimeEdit->maxTime())) 0601 { 0602 mPastMax = true; 0603 Q_EMIT pastMax(); 0604 } 0605 else if (mMinDateTimeIsNow && !mMinMaxTimeSet) 0606 { 0607 // The minimum date/time tracks the clock, so set the minimum 0608 // and maximum times 0609 setMaxMinTimeIf(now); 0610 } 0611 } 0612 } 0613 setMaxDelayTime(now); 0614 } 0615 0616 if (mAtTimeRadio->isChecked()) 0617 dateTimeChanged(); 0618 else 0619 { 0620 if (mDelayTimeEdit) 0621 delayTimeChanged(mDelayTimeEdit->value()); 0622 else 0623 delayTimeChanged(mDelayTimePeriod->minutes()); 0624 } 0625 } 0626 0627 0628 /****************************************************************************** 0629 * Called when the radio button states have been changed. 0630 * Updates the appropriate edit box. 0631 */ 0632 void AlarmTimeWidget::slotButtonSet(QAbstractButton*) 0633 { 0634 const bool at = mAtTimeRadio->isChecked(); 0635 mDateEdit->setEnabled(at); 0636 mTimeEdit->setEnabled(at && (!mAnyTimeAllowed || !mAnyTimeCheckBox || !mAnyTimeCheckBox->isChecked())); 0637 if (mAnyTimeCheckBox) 0638 mAnyTimeCheckBox->setEnabled(at && mAnyTimeAllowed); 0639 // Ensure that the value of the delay edit box is > 0. 0640 const KADateTime att(mDateEdit->date(), mTimeEdit->time(), mTimeSpec); 0641 const int minutes = (KADateTime::currentUtcDateTime().secsTo(att) + 59) / 60; 0642 if (mDelayTimeEdit) 0643 { 0644 if (minutes <= 0) 0645 mDelayTimeEdit->setValid(true); 0646 mDelayTimeEdit->setEnabled(!at); 0647 } 0648 else 0649 { 0650 if (minutes <= 0) 0651 mDelayTimePeriod->setValid(true); 0652 mDelayTimePeriod->setEnabled(!at); 0653 mPresetsCombo->setEnabled(!at); 0654 QPalette pal = mPresetsCombo->palette(); 0655 pal.setColor(QPalette::PlaceholderText, pal.color(at ? QPalette::Disabled : QPalette::Active, QPalette::Text)); 0656 mPresetsCombo->setPalette(pal); 0657 } 0658 setAnyTime(); 0659 } 0660 0661 /****************************************************************************** 0662 * Called after the mAnyTimeCheckBox checkbox has been toggled. 0663 */ 0664 void AlarmTimeWidget::slotAnyTimeToggled(bool on) 0665 { 0666 on = (on && mAnyTimeAllowed); 0667 mTimeEdit->setEnabled(!on && mAtTimeRadio->isChecked()); 0668 setAnyTime(); 0669 if (on) 0670 Q_EMIT changed(KADateTime(mDateEdit->date(), mTimeSpec)); 0671 else 0672 Q_EMIT changed(KADateTime(mDateEdit->date(), mTimeEdit->time(), mTimeSpec)); 0673 } 0674 0675 /****************************************************************************** 0676 * Called after a new selection has been made in the time zone combo box. 0677 * Re-evaluates the time specification to use. 0678 */ 0679 void AlarmTimeWidget::slotTimeZoneChanged() 0680 { 0681 const QTimeZone tz = mTimeZone->timeZone(); 0682 mTimeSpec = tz.isValid() ? KADateTime::Spec(tz) : KADateTime::LocalZone; 0683 if (!mTimeZoneBox->isVisible() && mTimeSpec != Preferences::timeSpec()) 0684 { 0685 // The current time zone is not the default one, so 0686 // show the time zone selection controls 0687 showTimeZoneSelector(); 0688 } 0689 mMinDateTime = mMinDateTime.toTimeSpec(mTimeSpec); 0690 mMaxDateTime = mMaxDateTime.toTimeSpec(mTimeSpec); 0691 updateTimes(); 0692 } 0693 0694 /****************************************************************************** 0695 * Called after the mTimeZoneButton button has been clicked. 0696 * Show the time zone selection controls, and hide the button. 0697 */ 0698 void AlarmTimeWidget::showTimeZoneSelector() 0699 { 0700 mTimeZoneButton->hide(); 0701 mTimeZoneBox->show(); 0702 } 0703 0704 /****************************************************************************** 0705 * Show or hide the time zone button. 0706 */ 0707 void AlarmTimeWidget::showMoreOptions(bool more) 0708 { 0709 if (more) 0710 { 0711 if (!mTimeZoneBox->isVisible()) 0712 mTimeZoneButton->show(); 0713 } 0714 else 0715 mTimeZoneButton->hide(); 0716 } 0717 0718 /****************************************************************************** 0719 * Called when the date or time edit box values have changed. 0720 * Updates the time delay edit box accordingly. 0721 */ 0722 void AlarmTimeWidget::dateTimeChanged() 0723 { 0724 const KADateTime dt(mDateEdit->date(), mTimeEdit->time(), mTimeSpec); 0725 const int minutes = (KADateTime::currentUtcDateTime().secsTo(dt) + 59) / 60; 0726 if (mDelayTimeEdit) 0727 { 0728 const bool blocked = mDelayTimeEdit->signalsBlocked(); 0729 mDelayTimeEdit->blockSignals(true); // prevent infinite recursion between here and delayTimeChanged() 0730 if (minutes <= 0 || minutes > mDelayTimeEdit->maximum()) 0731 mDelayTimeEdit->setValid(false); 0732 else 0733 mDelayTimeEdit->setValue(minutes); 0734 mDelayTimeEdit->blockSignals(blocked); 0735 } 0736 else 0737 { 0738 const bool blocked = mDelayTimePeriod->signalsBlocked(); 0739 mDelayTimePeriod->blockSignals(true); // prevent infinite recursion between here and delayTimeChanged() 0740 if (minutes <= 0 || minutes > mDelayTimePeriod->maxMinutes()) 0741 mDelayTimePeriod->setValid(false); 0742 else 0743 mDelayTimePeriod->setMinutes(minutes); 0744 mDelayTimePeriod->blockSignals(blocked); 0745 } 0746 if (mAnyTimeAllowed && mAnyTimeCheckBox && mAnyTimeCheckBox->isChecked()) 0747 Q_EMIT changed(KADateTime(dt.date(), mTimeSpec)); 0748 else 0749 Q_EMIT changed(dt); 0750 } 0751 0752 /****************************************************************************** 0753 * Called when the delay time edit box value has changed. 0754 * Updates the Date and Time edit boxes accordingly. 0755 */ 0756 void AlarmTimeWidget::delayTimeChanged(int minutes) 0757 { 0758 if ((mDelayTimeEdit && mDelayTimeEdit->isValid()) 0759 || (mDelayTimePeriod && mDelayTimePeriod->isValid())) 0760 { 0761 QDateTime dt = KADateTime::currentUtcDateTime().addSecs(minutes * 60).toTimeSpec(mTimeSpec).qDateTime(); 0762 const bool blockedT = mTimeEdit->signalsBlocked(); 0763 const bool blockedD = mDateEdit->signalsBlocked(); 0764 mTimeEdit->blockSignals(true); // prevent infinite recursion between here and dateTimeChanged() 0765 mDateEdit->blockSignals(true); 0766 mTimeEdit->setValue(dt.time()); 0767 mDateEdit->setDate(dt.date()); 0768 mTimeEdit->blockSignals(blockedT); 0769 mDateEdit->blockSignals(blockedD); 0770 Q_EMIT changed(KADateTime(dt.date(), dt.time(), mTimeSpec)); 0771 } 0772 } 0773 0774 /****************************************************************************** 0775 * Called when a new item is selected in the presets combo box. 0776 */ 0777 void AlarmTimeWidget::slotPresetSelected(int index) 0778 { 0779 const int minutes = mPresetsCombo->itemData(index).toInt(); 0780 if (minutes > 0) 0781 { 0782 switch (mDelayTimePeriod->units()) 0783 { 0784 case TimePeriod::Minutes: 0785 case TimePeriod::HoursMinutes: 0786 if (minutes < 1440) 0787 mDelayTimePeriod->setMinutes(minutes); 0788 else 0789 mDelayTimePeriod->setMinutes(minutes, TimePeriod::Days); 0790 break; 0791 case TimePeriod::Days: 0792 case TimePeriod::Weeks: 0793 if (minutes >= 1440) 0794 mDelayTimePeriod->setMinutes(minutes); 0795 else 0796 mDelayTimePeriod->setMinutes(minutes, TimePeriod::HoursMinutes); 0797 break; 0798 } 0799 } 0800 mPresetsCombo->blockSignals(true); 0801 mPresetsCombo->setCurrentIndex(-1); 0802 mPresetsCombo->blockSignals(false); 0803 } 0804 0805 #include "moc_alarmtimewidget.cpp" 0806 0807 // vim: et sw=4: