Warning, file /pim/kalarm/src/editdlg.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * editdlg.cpp - dialog to create or modify an alarm or alarm template 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 "editdlg.h" 0010 #include "editdlg_p.h" 0011 #include "editdlgtypes.h" 0012 0013 #include "alarmtimewidget.h" 0014 #include "deferdlg.h" 0015 #include "kalarmapp.h" 0016 #include "latecancel.h" 0017 #include "preferences.h" 0018 #include "recurrenceedit.h" 0019 #include "reminder.h" 0020 #include "resourcescalendar.h" 0021 #include "resources/resources.h" 0022 #include "lib/autoqpointer.h" 0023 #include "lib/buttongroup.h" 0024 #include "lib/checkbox.h" 0025 #include "lib/config.h" 0026 #include "lib/lineedit.h" 0027 #include "lib/messagebox.h" 0028 #include "lib/packedlayout.h" 0029 #include "lib/radiobutton.h" 0030 #include "lib/stackedwidgets.h" 0031 #include "templatepickdlg.h" 0032 #include "lib/timeedit.h" 0033 #include "lib/timespinbox.h" 0034 #include "config-kalarm.h" 0035 #include "kalarm_debug.h" 0036 0037 #include <KLocalizedString> 0038 #include <KConfig> 0039 #include <KSharedConfig> 0040 #if ENABLE_X11 0041 #include <KX11Extras> 0042 #endif 0043 0044 #include <QLabel> 0045 #include <QGroupBox> 0046 #include <QPushButton> 0047 #include <QGridLayout> 0048 #include <QHBoxLayout> 0049 #include <QVBoxLayout> 0050 #include <QResizeEvent> 0051 #include <QShowEvent> 0052 #include <QScrollBar> 0053 #include <QTimer> 0054 #include <QDialogButtonBox> 0055 #include <QLocale> 0056 #include <QStyle> 0057 0058 using namespace KCal; 0059 using namespace KAlarmCal; 0060 0061 namespace 0062 { 0063 const char EDIT_DIALOG_NAME[] = "EditDialog"; 0064 const char TEMPLATE_DIALOG_NAME[] = "EditTemplateDialog"; 0065 const char EDIT_MORE_KEY[] = "EditMore"; 0066 const QLatin1String EDIT_MORE_GROUP("ShowOpts"); 0067 const int maxDelayTime = 99*60 + 59; // < 100 hours 0068 } 0069 0070 inline QString recurText(const KAEvent& event) 0071 { 0072 QString r; 0073 if (event.repetition()) 0074 r = QStringLiteral("%1 / %2").arg(event.recurrenceText(), event.repetitionText()); 0075 else 0076 r = event.recurrenceText(); 0077 return i18nc("@title:tab", "Recurrence - [%1]", r); 0078 } 0079 0080 QList<EditAlarmDlg*> EditAlarmDlg::mWindowList; 0081 0082 // Collect these widget labels together to ensure consistent wording and 0083 // translations across different modules. 0084 QString EditAlarmDlg::i18n_chk_ShowInKOrganizer() { return i18nc("@option:check", "Show in KOrganizer"); } 0085 0086 0087 EditAlarmDlg* EditAlarmDlg::create(bool Template, Type type, QWidget* parent, GetResourceType getResource) 0088 { 0089 qCDebug(KALARM_LOG) << "EditAlarmDlg::create"; 0090 switch (type) 0091 { 0092 case DISPLAY: return new EditDisplayAlarmDlg(Template, parent, getResource); 0093 case COMMAND: return new EditCommandAlarmDlg(Template, parent, getResource); 0094 case EMAIL: return new EditEmailAlarmDlg(Template, parent, getResource); 0095 case AUDIO: return new EditAudioAlarmDlg(Template, parent, getResource); 0096 default: break; 0097 } 0098 return nullptr; 0099 } 0100 0101 EditAlarmDlg* EditAlarmDlg::create(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 0102 GetResourceType getResource, bool readOnly) 0103 { 0104 switch (event.actionTypes()) 0105 { 0106 case KAEvent::Action::Command: return new EditCommandAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly); 0107 case KAEvent::Action::DisplayCommand: 0108 case KAEvent::Action::Display: return new EditDisplayAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly); 0109 case KAEvent::Action::Email: return new EditEmailAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly); 0110 case KAEvent::Action::Audio: return new EditAudioAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly); 0111 default: 0112 break; 0113 } 0114 return nullptr; 0115 } 0116 0117 0118 /****************************************************************************** 0119 * Constructor. 0120 * Parameters: 0121 * Template = true to edit/create an alarm template 0122 * = false to edit/create an alarm. 0123 * event to initialise the dialog to show the specified event's data. 0124 */ 0125 EditAlarmDlg::EditAlarmDlg(bool Template, KAEvent::SubAction action, QWidget* parent, GetResourceType getResource) 0126 : QDialog(parent) 0127 , mAlarmType(action) 0128 , mTemplate(Template) 0129 , mNewAlarm(true) 0130 , mDesiredReadOnly(false) 0131 , mReadOnly(false) 0132 { 0133 init(KAEvent(), getResource); 0134 mWindowList.append(this); 0135 } 0136 0137 EditAlarmDlg::EditAlarmDlg(bool Template, const KAEvent& event, bool newAlarm, QWidget* parent, 0138 GetResourceType getResource, bool readOnly) 0139 : QDialog(parent) 0140 , mAlarmType(event.actionSubType()) 0141 , mEventId(newAlarm ? QString() : event.id()) 0142 , mTemplate(Template) 0143 , mNewAlarm(newAlarm) 0144 , mDesiredReadOnly(readOnly) 0145 , mReadOnly(readOnly) 0146 { 0147 init(event, getResource); 0148 mWindowList.append(this); 0149 } 0150 0151 void EditAlarmDlg::init(const KAEvent& event, GetResourceType getResource) 0152 { 0153 switch (getResource) 0154 { 0155 case RES_USE_EVENT_ID: 0156 if (event.isValid()) 0157 { 0158 mResourceEventId = event.id(); 0159 mUseResourceEventId = true; 0160 break; 0161 } 0162 [[fallthrough]]; // fall through to RES_PROMPT 0163 case RES_PROMPT: 0164 mResourceEventId.clear(); 0165 mUseResourceEventId = true; 0166 break; 0167 case RES_IGNORE: 0168 default: 0169 mResourceEventId.clear(); 0170 mUseResourceEventId = false; 0171 break; 0172 } 0173 } 0174 0175 void EditAlarmDlg::init(const KAEvent& event) 0176 { 0177 setObjectName(mTemplate ? QStringLiteral("TemplEditDlg") : QStringLiteral("EditDlg")); // used by LikeBack 0178 QString caption; 0179 if (mReadOnly) 0180 caption = mTemplate ? i18nc("@title:window", "Alarm Template [read-only]") 0181 : event.expired() ? i18nc("@title:window", "Archived Alarm [read-only]") 0182 : i18nc("@title:window", "Alarm [read-only]"); 0183 else 0184 caption = type_caption(); 0185 setWindowTitle(caption); 0186 0187 // Create button box now so that types can work with it, but don't insert 0188 // it into layout just yet 0189 mButtonBox = new QDialogButtonBox(this); 0190 if (!mReadOnly) 0191 { 0192 QPushButton* b = mButtonBox->addButton(QDialogButtonBox::Ok); 0193 b->setWhatsThis(i18nc("@info:whatsthis", "Schedule the alarm at the specified time.")); 0194 if (!mTemplate) 0195 { 0196 mLoadTemplateButton = mButtonBox->addButton(i18nc("@action:button", "Load Template..."), 0197 QDialogButtonBox::HelpRole); 0198 mLoadTemplateButton->setToolTip(i18nc("@info:tooltip", "Select an alarm template to preset the alarm")); 0199 } 0200 } 0201 mButtonBox->addButton(QDialogButtonBox::Cancel); 0202 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole); 0203 mMoreLessButton = mButtonBox->addButton(QString(), QDialogButtonBox::ResetRole); 0204 connect(mButtonBox, &QDialogButtonBox::clicked, this, &EditAlarmDlg::slotButtonClicked); 0205 0206 auto mainLayout = new QVBoxLayout(this); 0207 mTabs = new QTabWidget(this); 0208 mainLayout->addWidget(mTabs); 0209 mTabScrollGroup = new StackedScrollGroup(this, mTabs); 0210 0211 auto mainScroll = new StackedScrollWidget(mTabScrollGroup); 0212 mTabs->addTab(mainScroll, i18nc("@title:tab", "Alarm")); 0213 mMainPageIndex = 0; 0214 auto mainPage = new PageFrame(mainScroll); 0215 mainScroll->setWidget(mainPage); // mainPage becomes the child of mainScroll 0216 connect(mainPage, &PageFrame::shown, this, &EditAlarmDlg::slotShowMainPage); 0217 auto topLayout = new QVBoxLayout(mainPage); 0218 0219 // Recurrence tab 0220 auto recurScroll = new StackedScrollWidget(mTabScrollGroup); 0221 mTabs->addTab(recurScroll, QString()); 0222 mRecurPageIndex = 1; 0223 QFrame* recurTab = new QFrame; 0224 auto recurTabLayout = new QVBoxLayout(); 0225 recurTab->setLayout(recurTabLayout); 0226 recurScroll->setWidget(recurTab); // recurTab becomes the child of recurScroll 0227 mRecurrenceEdit = new RecurrenceEdit(mReadOnly); 0228 recurTabLayout->addWidget(mRecurrenceEdit); 0229 connect(mRecurrenceEdit, &RecurrenceEdit::shown, this, &EditAlarmDlg::slotShowRecurrenceEdit); 0230 connect(mRecurrenceEdit, &RecurrenceEdit::typeChanged, this, &EditAlarmDlg::slotRecurTypeChange); 0231 connect(mRecurrenceEdit, &RecurrenceEdit::frequencyChanged, this, &EditAlarmDlg::slotRecurFrequencyChange); 0232 connect(mRecurrenceEdit, &RecurrenceEdit::repeatNeedsInitialisation, this, &EditAlarmDlg::slotSetSubRepetition); 0233 connect(mRecurrenceEdit, &RecurrenceEdit::contentsChanged, this, &EditAlarmDlg::contentsChanged); 0234 0235 if (mTemplate || Preferences::useAlarmName()) 0236 { 0237 // Alarm/template name 0238 QFrame* frame = new QFrame; 0239 auto box = new QHBoxLayout(); 0240 frame->setLayout(box); 0241 box->setContentsMargins(0, 0, 0, 0); 0242 QLabel* label = new QLabel(mTemplate ? i18nc("@label:textbox", "Template name:") : i18nc("@label:textbox", "Alarm name:")); 0243 box->addWidget(label); 0244 mName = new QLineEdit(); 0245 mName->setReadOnly(mReadOnly); 0246 connect(mName, &QLineEdit::textEdited, this, &EditAlarmDlg::contentsChanged); 0247 label->setBuddy(mName); 0248 box->addWidget(mName); 0249 frame->setWhatsThis(mTemplate ? i18nc("@info:whatsthis", "Enter the name of the alarm template") 0250 : i18nc("@info:whatsthis", "Enter a name to help you identify this alarm. This is optional and need not be unique.")); 0251 topLayout->addWidget(frame); 0252 } 0253 0254 // Controls specific to the alarm type 0255 QGroupBox* actionBox = new QGroupBox(i18nc("@title:group", "Action"), mainPage); 0256 topLayout->addWidget(actionBox, 1); 0257 auto vlayout = new QVBoxLayout(actionBox); 0258 0259 type_init(actionBox, vlayout); 0260 0261 if (!mTemplate) 0262 { 0263 // Deferred date/time: visible only for a deferred recurring event. 0264 mDeferGroup = new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage); 0265 topLayout->addWidget(mDeferGroup); 0266 auto hlayout = new QHBoxLayout(mDeferGroup); 0267 QLabel* label = new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup); 0268 hlayout->addWidget(label); 0269 mDeferTimeLabel = new QLabel(mDeferGroup); 0270 hlayout->addWidget(mDeferTimeLabel); 0271 0272 mDeferChangeButton = new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup); 0273 connect(mDeferChangeButton, &QPushButton::clicked, this, &EditAlarmDlg::slotEditDeferral); 0274 mDeferChangeButton->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral")); 0275 hlayout->addWidget(mDeferChangeButton); 0276 } 0277 0278 auto hlayout = new QHBoxLayout(); 0279 hlayout->setContentsMargins(0, 0, 0, 0); 0280 topLayout->addLayout(hlayout); 0281 0282 // Date and time entry 0283 if (mTemplate) 0284 { 0285 QGroupBox* templateTimeBox = new QGroupBox(i18nc("@title:group", "Time"), mainPage); 0286 topLayout->addWidget(templateTimeBox); 0287 auto grid = new QGridLayout(templateTimeBox); 0288 mTemplateTimeGroup = new ButtonGroup(templateTimeBox); 0289 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::slotTemplateTimeType); 0290 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::contentsChanged); 0291 0292 mTemplateDefaultTime = new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox); 0293 mTemplateDefaultTime->setReadOnly(mReadOnly); 0294 mTemplateDefaultTime->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. " 0295 "The normal default start time will be used.")); 0296 mTemplateTimeGroup->addButton(mTemplateDefaultTime); 0297 grid->addWidget(mTemplateDefaultTime, 0, 0, Qt::AlignLeft); 0298 0299 QWidget* box = new QWidget(templateTimeBox); 0300 auto layout = new QHBoxLayout(box); 0301 layout->setContentsMargins(0, 0, 0, 0); 0302 mTemplateUseTime = new RadioButton(i18nc("@option:radio", "Time:"), box); 0303 mTemplateUseTime->setReadOnly(mReadOnly); 0304 mTemplateUseTime->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template.")); 0305 layout->addWidget(mTemplateUseTime); 0306 mTemplateTimeGroup->addButton(mTemplateUseTime); 0307 mTemplateTime = new TimeEdit(); 0308 mTemplateTime->setReadOnly(mReadOnly); 0309 mTemplateTime->setWhatsThis(xi18nc("@info:whatsthis", 0310 "<para>Enter the start time for alarms based on this template.</para><para>%1</para>", 0311 TimeSpinBox::shiftWhatsThis())); 0312 connect(mTemplateTime, &TimeEdit::valueChanged, this, &EditAlarmDlg::contentsChanged); 0313 layout->addWidget(mTemplateTime); 0314 layout->addStretch(1); 0315 grid->addWidget(box, 0, 1, Qt::AlignLeft); 0316 0317 mTemplateAnyTime = new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox); 0318 mTemplateAnyTime->setReadOnly(mReadOnly); 0319 mTemplateAnyTime->setWhatsThis(xi18nc("@info:whatsthis", "Set the <interface>Any time</interface> option for alarms based on this template.")); 0320 mTemplateTimeGroup->addButton(mTemplateAnyTime); 0321 grid->addWidget(mTemplateAnyTime, 1, 0, Qt::AlignLeft); 0322 0323 box = new QWidget(templateTimeBox); 0324 layout = new QHBoxLayout(box); 0325 layout->setContentsMargins(0, 0, 0, 0); 0326 mTemplateUseTimeAfter = new RadioButton(i18nc("@option:radio", "Time from now:"), box); 0327 mTemplateUseTimeAfter->setReadOnly(mReadOnly); 0328 mTemplateUseTimeAfter->setWhatsThis(i18nc("@info:whatsthis", 0329 "Set alarms based on this template to start after the specified time " 0330 "interval from when the alarm is created.")); 0331 layout->addWidget(mTemplateUseTimeAfter); 0332 mTemplateTimeGroup->addButton(mTemplateUseTimeAfter); 0333 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime); 0334 mTemplateTimeAfter->setValue(1439); 0335 mTemplateTimeAfter->setReadOnly(mReadOnly); 0336 connect(mTemplateTimeAfter, &TimeSpinBox::valueChanged, this, &EditAlarmDlg::contentsChanged); 0337 mTemplateTimeAfter->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para>", 0338 AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis())); 0339 layout->addWidget(mTemplateTimeAfter); 0340 grid->addWidget(box, 1, 1, Qt::AlignLeft); 0341 0342 hlayout->addStretch(); 0343 } 0344 else 0345 { 0346 mTimeWidget = new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME, mainPage); 0347 connect(mTimeWidget, &AlarmTimeWidget::dateOnlyToggled, this, &EditAlarmDlg::slotAnyTimeToggled); 0348 connect(mTimeWidget, &AlarmTimeWidget::changed, this, &EditAlarmDlg::contentsChanged); 0349 topLayout->addWidget(mTimeWidget); 0350 } 0351 0352 // Optional controls depending on More/Fewer Options button 0353 mMoreOptions = new QFrame(mainPage); 0354 mMoreOptions->setFrameStyle(QFrame::NoFrame); 0355 topLayout->addWidget(mMoreOptions); 0356 auto moreLayout = new QVBoxLayout(mMoreOptions); 0357 moreLayout->setContentsMargins(0, 0, 0, 0); 0358 0359 // Reminder 0360 mReminder = createReminder(mMoreOptions); 0361 if (mReminder) 0362 { 0363 connect(mReminder, &Reminder::changed, this, &EditAlarmDlg::contentsChanged); 0364 moreLayout->addWidget(mReminder, 0, Qt::AlignLeft); 0365 if (mTimeWidget) 0366 connect(mTimeWidget, &AlarmTimeWidget::changed, mReminder, &Reminder::setDefaultUnits); 0367 } 0368 0369 // Late cancel selector - default = allow late display 0370 mLateCancel = new LateCancelSelector(true, mMoreOptions); 0371 connect(mLateCancel, &LateCancelSelector::changed, this, &EditAlarmDlg::contentsChanged); 0372 auto layout = new QHBoxLayout; 0373 layout->setContentsMargins(0, 0, 0, 0); 0374 layout->addWidget(mLateCancel, 1); 0375 moreLayout->addLayout(layout); 0376 0377 PackedLayout* playout = new PackedLayout(Qt::AlignJustify); 0378 playout->setHorizontalSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); 0379 playout->setVerticalSpacing(2 * style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0380 moreLayout->addLayout(playout); 0381 0382 if (KernelWakeAlarm::isAvailable()) 0383 { 0384 // Wake from suspend checkbox 0385 mWakeFromSuspend = new CheckBox(i18nc("@option:check", "Wake from suspend"), mMoreOptions); 0386 connect(mWakeFromSuspend, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged); 0387 mWakeFromSuspend->setWhatsThis(i18nc("@info:whatsthis", "Check to wake the system if the alarm triggers while the system is suspended")); 0388 playout->addWidget(mWakeFromSuspend); 0389 } 0390 0391 // Acknowledgement confirmation required - default = no confirmation 0392 CheckBox* confirmAck = type_createConfirmAckCheckbox(mMoreOptions); 0393 if (confirmAck) 0394 { 0395 connect(confirmAck, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged); 0396 playout->addWidget(confirmAck); 0397 } 0398 0399 if (theApp()->korganizerEnabled()) 0400 { 0401 // Show in KOrganizer checkbox 0402 mShowInKorganizer = new CheckBox(i18n_chk_ShowInKOrganizer(), mMoreOptions); 0403 connect(mShowInKorganizer, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged); 0404 mShowInKorganizer->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar")); 0405 if (!confirmAck || !mWakeFromSuspend) 0406 playout->addWidget(mShowInKorganizer); 0407 else 0408 mLateCancel->addWidget(mShowInKorganizer); 0409 } 0410 0411 mainLayout->addWidget(mButtonBox); 0412 0413 // Hide optional controls 0414 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP); 0415 showOptions(config.readEntry(EDIT_MORE_KEY, false)); 0416 0417 // Initialise the state of all controls according to the specified event, if any 0418 initValues(event); 0419 if (mName) 0420 mName->setFocus(); 0421 0422 if (!mNewAlarm) 0423 { 0424 // Save the initial state of all controls so that we can later tell if they have changed 0425 saveState((event.isValid() && (mTemplate || !event.isTemplate())) ? &event : nullptr); 0426 contentsChanged(); // enable/disable OK button 0427 } 0428 0429 // Note the current virtual desktop so that the dialog can be shown on it. 0430 // If a main window is visible, the dialog will by KDE default always appear on its 0431 // desktop. If the user invokes the dialog via the system tray on a different desktop, 0432 // that can cause confusion. 0433 #if ENABLE_X11 0434 mDesktop = KX11Extras::currentDesktop(); 0435 #endif 0436 0437 if (theApp()->windowFocusBroken()) 0438 { 0439 const QList<QWidget*> children = findChildren<QWidget*>(); 0440 for (QWidget* w : children) 0441 w->installEventFilter(this); 0442 } 0443 } 0444 0445 EditAlarmDlg::~EditAlarmDlg() 0446 { 0447 delete mButtonBox; 0448 mButtonBox = nullptr; // prevent text edit contentsChanged() signal triggering a crash 0449 delete mSavedEvent; 0450 mWindowList.removeAll(this); 0451 } 0452 0453 /****************************************************************************** 0454 * Return the number of instances. 0455 */ 0456 int EditAlarmDlg::instanceCount() 0457 { 0458 return mWindowList.count(); 0459 } 0460 0461 /****************************************************************************** 0462 * Initialise the dialog controls from the specified event. 0463 */ 0464 void EditAlarmDlg::initValues(const KAEvent& event) 0465 { 0466 setReadOnly(mDesiredReadOnly); 0467 0468 mChanged = false; 0469 mOnlyDeferred = false; 0470 mExpiredRecurrence = false; 0471 mLateCancel->showAutoClose(false); 0472 bool deferGroupVisible = false; 0473 if (event.isValid()) 0474 { 0475 // Set the values to those for the specified event 0476 if (mName) 0477 mName->setText(event.name()); 0478 bool recurs = event.recurs(); 0479 if ((recurs || event.repetition()) && !mTemplate && event.deferred()) 0480 { 0481 deferGroupVisible = true; 0482 mDeferDateTime = event.deferDateTime(); 0483 mDeferTimeLabel->setText(mDeferDateTime.formatLocale()); 0484 mDeferGroup->show(); 0485 } 0486 if (mTemplate) 0487 { 0488 // Editing a template 0489 int afterTime = event.isTemplate() ? event.templateAfterTime() : -1; 0490 bool noTime = !afterTime; 0491 bool useTime = !event.mainDateTime().isDateOnly(); 0492 RadioButton* button = noTime ? mTemplateDefaultTime : 0493 (afterTime > 0) ? mTemplateUseTimeAfter : 0494 useTime ? mTemplateUseTime : mTemplateAnyTime; 0495 button->setChecked(true); 0496 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1); 0497 if (!noTime && useTime) 0498 mTemplateTime->setValue(event.mainDateTime().kDateTime().time()); 0499 else 0500 mTemplateTime->setValue(0); 0501 } 0502 else 0503 { 0504 if (event.isTemplate()) 0505 { 0506 // Initialising from an alarm template: use current date 0507 const KADateTime now = KADateTime::currentDateTime(Preferences::timeSpec()); 0508 int afterTime = event.templateAfterTime(); 0509 if (afterTime >= 0) 0510 { 0511 mTimeWidget->setDateTime(now.addSecs(afterTime * 60)); 0512 mTimeWidget->selectTimeFromNow(); 0513 } 0514 else 0515 { 0516 KADateTime dt = event.startDateTime().kDateTime(); 0517 dt.setTimeSpec(Preferences::timeSpec()); 0518 QDate d = now.date(); 0519 if (!dt.isDateOnly() && now.time() >= dt.time()) 0520 d = d.addDays(1); // alarm time has already passed, so use tomorrow 0521 dt.setDate(d); 0522 mTimeWidget->setDateTime(dt); 0523 } 0524 } 0525 else 0526 { 0527 mExpiredRecurrence = recurs && event.mainExpired(); 0528 mTimeWidget->setDateTime(recurs || event.category() == CalEvent::ARCHIVED ? event.startDateTime() 0529 : event.mainExpired() ? event.deferDateTime() : event.mainDateTime()); 0530 } 0531 } 0532 0533 KAEvent::SubAction action = event.actionSubType(); 0534 AlarmText altext; 0535 if (event.commandScript()) 0536 altext.setScript(event.cleanText()); 0537 else 0538 altext.setText(event.cleanText()); 0539 setAction(action, altext); 0540 0541 mLateCancel->setMinutes(event.lateCancel(), event.startDateTime().isDateOnly(), 0542 TimePeriod::HoursMinutes); 0543 if (mWakeFromSuspend) 0544 mWakeFromSuspend->setChecked(event.wakeFromSuspend()); 0545 if (mShowInKorganizer) 0546 mShowInKorganizer->setChecked(event.copyToKOrganizer()); 0547 type_initValues(event); 0548 mRecurrenceEdit->set(event); // must be called after mTimeWidget is set up, to ensure correct date-only enabling 0549 mTabs->setTabText(mRecurPageIndex, recurText(event)); 0550 } 0551 else 0552 { 0553 // Set the values to their defaults 0554 const KADateTime defaultTime = KADateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeSpec()); 0555 if (mTemplate) 0556 { 0557 mTemplateDefaultTime->setChecked(true); 0558 mTemplateTime->setValue(0); 0559 mTemplateTimeAfter->setValue(1); 0560 } 0561 else 0562 mTimeWidget->setDateTime(defaultTime); 0563 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes); 0564 if (mWakeFromSuspend) 0565 mWakeFromSuspend->setChecked(false); 0566 if (mShowInKorganizer) 0567 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer()); 0568 type_initValues(KAEvent()); 0569 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling 0570 slotRecurFrequencyChange(); // update the Recurrence text 0571 } 0572 if (mReminder && mTimeWidget) 0573 mReminder->setDefaultUnits(mTimeWidget->getDateTime(false, false)); 0574 0575 if (!deferGroupVisible && mDeferGroup) 0576 mDeferGroup->hide(); 0577 0578 bool empty = ResourcesCalendar::events(CalEvent::TEMPLATE).isEmpty(); 0579 if (mLoadTemplateButton) 0580 mLoadTemplateButton->setEnabled(!empty); 0581 } 0582 0583 /****************************************************************************** 0584 * Initialise various values in the New Alarm dialogue. 0585 */ 0586 void EditAlarmDlg::setName(const QString& name) 0587 { 0588 if (mName) 0589 mName->setText(name); 0590 } 0591 void EditAlarmDlg::setTime(const DateTime& start) 0592 { 0593 mTimeWidget->setDateTime(start); 0594 mTimeWidget->selectTimeFromNow(-1); // select 'At date/time' option 0595 } 0596 KADateTime EditAlarmDlg::time() const 0597 { 0598 return mTimeWidget->getDateTime(false, false); 0599 } 0600 void EditAlarmDlg::setRecurrence(const KARecurrence& recur, const KCalendarCore::Duration& subRepeatInterval, int subRepeatCount) 0601 { 0602 KAEvent event; 0603 event.setTime(mTimeWidget->getDateTime(false, false)); 0604 event.setRecurrence(recur); 0605 event.setRepetition(Repetition(subRepeatInterval, subRepeatCount - 1)); 0606 mRecurrenceEdit->set(event); 0607 } 0608 void EditAlarmDlg::setRepeatAtLogin() 0609 { 0610 mRecurrenceEdit->setRepeatAtLogin(); 0611 } 0612 void EditAlarmDlg::setLateCancel(int minutes) 0613 { 0614 mLateCancel->setMinutes(minutes, mTimeWidget->getDateTime(false, false).isDateOnly(), 0615 TimePeriod::HoursMinutes); 0616 } 0617 void EditAlarmDlg::setWakeFromSuspend(bool wake) 0618 { 0619 if (mWakeFromSuspend) 0620 mWakeFromSuspend->setChecked(wake); 0621 } 0622 void EditAlarmDlg::setShowInKOrganizer(bool show) 0623 { 0624 if (mShowInKorganizer) 0625 mShowInKorganizer->setChecked(show); 0626 } 0627 0628 /****************************************************************************** 0629 * Set the read-only status of all non-template controls. 0630 */ 0631 void EditAlarmDlg::setReadOnly(bool readOnly) 0632 { 0633 mReadOnly = readOnly; 0634 0635 if (mTimeWidget) 0636 mTimeWidget->setReadOnly(readOnly); 0637 mLateCancel->setReadOnly(readOnly); 0638 if (mDeferChangeButton) 0639 { 0640 if (readOnly) 0641 mDeferChangeButton->hide(); 0642 else 0643 mDeferChangeButton->show(); 0644 } 0645 if (mWakeFromSuspend) 0646 mWakeFromSuspend->setReadOnly(readOnly); 0647 if (mShowInKorganizer) 0648 mShowInKorganizer->setReadOnly(readOnly); 0649 } 0650 0651 /****************************************************************************** 0652 * Save the state of all controls. 0653 */ 0654 void EditAlarmDlg::saveState(const KAEvent* event) 0655 { 0656 delete mSavedEvent; 0657 mSavedEvent = nullptr; 0658 if (event) 0659 mSavedEvent = new KAEvent(*event); 0660 if (mName) 0661 mSavedName = mName->text(); 0662 if (mTemplate) 0663 { 0664 mSavedTemplateTimeType = mTemplateTimeGroup->checkedButton(); 0665 mSavedTemplateTime = mTemplateTime->time(); 0666 mSavedTemplateAfterTime = mTemplateTimeAfter->value(); 0667 } 0668 checkText(mSavedTextFileCommandMessage, false); 0669 if (mTimeWidget) 0670 mSavedDateTime = mTimeWidget->getDateTime(false, false); 0671 mSavedLateCancel = mLateCancel->minutes(); 0672 if (mWakeFromSuspend) 0673 mSavedWakeFromSuspend = mWakeFromSuspend->isChecked(); 0674 if (mShowInKorganizer) 0675 mSavedShowInKorganizer = mShowInKorganizer->isChecked(); 0676 mSavedRecurrenceType = mRecurrenceEdit->repeatType(); 0677 mSavedDeferTime = mDeferDateTime.kDateTime(); 0678 } 0679 0680 /****************************************************************************** 0681 * Check whether any of the controls has changed state since the dialog was 0682 * first displayed. 0683 * Reply = true if any non-deferral controls have changed, or if it's a new event. 0684 * = false if no non-deferral controls have changed. In this case, 0685 * mOnlyDeferred indicates whether deferral controls may have changed. 0686 */ 0687 bool EditAlarmDlg::stateChanged() const 0688 { 0689 mChanged = true; 0690 mOnlyDeferred = false; 0691 if (!mSavedEvent) 0692 return true; 0693 if (mName && mSavedName != mName->text()) 0694 return true; 0695 QString textFileCommandMessage; 0696 checkText(textFileCommandMessage, false); 0697 if (mTemplate) 0698 { 0699 if (mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton() 0700 || (mTemplateUseTime->isChecked() && mSavedTemplateTime != mTemplateTime->time()) 0701 || (mTemplateUseTimeAfter->isChecked() && mSavedTemplateAfterTime != mTemplateTimeAfter->value())) 0702 return true; 0703 } 0704 else 0705 { 0706 const KADateTime dt = mTimeWidget->getDateTime(false, false); 0707 if (mSavedDateTime.timeSpec() != dt.timeSpec() || mSavedDateTime != dt) 0708 return true; 0709 } 0710 if (mSavedLateCancel != mLateCancel->minutes() 0711 || (mWakeFromSuspend && mSavedWakeFromSuspend != mWakeFromSuspend->isChecked()) 0712 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked()) 0713 || textFileCommandMessage != mSavedTextFileCommandMessage 0714 || mSavedRecurrenceType != mRecurrenceEdit->repeatType()) 0715 return true; 0716 if (type_stateChanged()) 0717 return true; 0718 if (mRecurrenceEdit->stateChanged()) 0719 return true; 0720 if (mSavedEvent && mSavedEvent->deferred()) 0721 mOnlyDeferred = true; 0722 mChanged = false; 0723 return false; 0724 } 0725 0726 /****************************************************************************** 0727 * Called whenever any of the controls changes state. 0728 * Enable or disable the OK button depending on whether any controls have a 0729 * different state from their initial state. 0730 */ 0731 void EditAlarmDlg::contentsChanged() 0732 { 0733 // Don't do anything if it's a new alarm or we're still initialising 0734 // (i.e. mSavedEvent null). 0735 if (mSavedEvent && mButtonBox && mButtonBox->button(QDialogButtonBox::Ok)) 0736 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(stateChanged() || mDeferDateTime.kDateTime() != mSavedDeferTime); 0737 } 0738 0739 /****************************************************************************** 0740 * Get the currently entered dialog data. 0741 * The data is returned in the supplied KAEvent instance. 0742 * Reply = false if the only change has been to an existing deferral. 0743 */ 0744 bool EditAlarmDlg::getEvent(KAEvent& event, Resource& resource) 0745 { 0746 resource = mResource; 0747 if (mChanged) 0748 { 0749 // It's a new event, or the edit controls have changed 0750 setEvent(event, mAlarmMessage, false); 0751 return true; 0752 } 0753 0754 // Only the deferral time may have changed 0755 event = *mSavedEvent; 0756 if (mOnlyDeferred) 0757 { 0758 // Just modify the original event, to avoid expired recurring events 0759 // being returned as rubbish. 0760 if (mDeferDateTime.isValid()) 0761 event.defer(mDeferDateTime, event.reminderDeferral(), false); 0762 else 0763 event.cancelDefer(); 0764 } 0765 return false; 0766 } 0767 0768 /****************************************************************************** 0769 * Extract the data in the dialog and set up a KAEvent from it. 0770 * If 'trial' is true, the event is set up for a simple one-off test, ignoring 0771 * recurrence, reminder, template etc. data. 0772 */ 0773 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial) 0774 { 0775 KADateTime dt; 0776 if (!trial) 0777 { 0778 if (!mTemplate) 0779 dt = mAlarmDateTime.effectiveKDateTime(); 0780 else if (mTemplateUseTime->isChecked()) 0781 dt = KADateTime(QDate(2000,1,1), mTemplateTime->time()); 0782 } 0783 0784 const int lateCancel = (trial || !mLateCancel->isEnabled()) ? 0 : mLateCancel->minutes(); 0785 type_setEvent(event, dt, (mName ? mName->text() : QString()), text, lateCancel, trial); 0786 0787 if (!trial) 0788 { 0789 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR) 0790 { 0791 mRecurrenceEdit->updateEvent(event, !mTemplate); 0792 const KADateTime now = KADateTime::currentDateTime(mAlarmDateTime.timeSpec()); 0793 const bool dateOnly = mAlarmDateTime.isDateOnly(); 0794 if ((dateOnly && mAlarmDateTime.date() < now.date()) 0795 || (!dateOnly && mAlarmDateTime.kDateTime() < now)) 0796 { 0797 // A timed recurrence has an entered start date which has 0798 // already expired, so we must adjust the next repetition. 0799 event.setNextOccurrence(now); 0800 } 0801 mAlarmDateTime = event.startDateTime(); 0802 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime) 0803 { 0804 bool deferral = true; 0805 bool deferReminder = false; 0806 const int reminder = mReminder ? mReminder->minutes() : 0; 0807 if (reminder) 0808 { 0809 DateTime remindTime = mAlarmDateTime.addMins(-reminder); 0810 if (mDeferDateTime >= remindTime) 0811 { 0812 if (remindTime > KADateTime::currentUtcDateTime()) 0813 deferral = false; // ignore deferral if it's after next reminder 0814 else if (mDeferDateTime > remindTime) 0815 deferReminder = true; // it's the reminder which is being deferred 0816 } 0817 } 0818 if (deferral) 0819 event.defer(mDeferDateTime, deferReminder, false); 0820 } 0821 } 0822 if (mTemplate) 0823 { 0824 int afterTime = mTemplateDefaultTime->isChecked() ? 0 0825 : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1; 0826 event.setTemplate(mName->text(), afterTime); 0827 } 0828 } 0829 } 0830 0831 /****************************************************************************** 0832 * Get the currently specified alarm flag bits. 0833 */ 0834 KAEvent::Flags EditAlarmDlg::getAlarmFlags() const 0835 { 0836 KAEvent::Flags flags{}; 0837 if (mWakeFromSuspend && mWakeFromSuspend->isEnabled() && mWakeFromSuspend->isChecked()) 0838 flags |= KAEvent::WAKE_SUSPEND; 0839 if (mShowInKorganizer && mShowInKorganizer->isEnabled() && mShowInKorganizer->isChecked()) 0840 flags |= KAEvent::COPY_KORGANIZER; 0841 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN) 0842 flags |= KAEvent::REPEAT_AT_LOGIN; 0843 if (mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly()) 0844 flags |= KAEvent::ANY_TIME; 0845 return flags; 0846 } 0847 0848 /****************************************************************************** 0849 * Called when the dialog is displayed. 0850 * The first time through, sets the size to the same as the last time it was 0851 * displayed. 0852 */ 0853 void EditAlarmDlg::showEvent(QShowEvent* se) 0854 { 0855 QDialog::showEvent(se); 0856 if (!mDeferGroupHeight) 0857 { 0858 if (mDeferGroup) 0859 mDeferGroupHeight = mDeferGroup->height() + style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing); 0860 QSize s; 0861 if (Config::readWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s)) 0862 { 0863 const bool defer = mDeferGroup && !mDeferGroup->isHidden(); 0864 s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0)); 0865 if (!defer) 0866 mTabScrollGroup->setSized(); 0867 resize(s); 0868 } 0869 } 0870 slotResize(); 0871 #if ENABLE_X11 0872 KX11Extras::setOnDesktop(winId(), mDesktop); // ensure it displays on the virtual desktop expected by the user 0873 #endif 0874 0875 if (theApp()->needWindowFocusFix()) 0876 { 0877 QApplication::setActiveWindow(this); 0878 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer); //NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) 0879 } 0880 } 0881 0882 /****************************************************************************** 0883 * Called when the window is first shown, to ensure that it initially becomes 0884 * the active window. 0885 * This is only required on Ubuntu's Unity desktop, which doesn't transfer 0886 * keyboard focus properly between Edit Alarm Dialog windows and MessageWindow 0887 * windows. 0888 */ 0889 void EditAlarmDlg::focusFixTimer() 0890 { 0891 if (theApp()->needWindowFocusFix() 0892 && QApplication::focusWidget()->window() != this) 0893 { 0894 QApplication::setActiveWindow(this); 0895 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer); //NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) 0896 } 0897 } 0898 0899 /****************************************************************************** 0900 * Called to detect when the mouse is pressed anywhere inside the window. 0901 * Activates this window if a MessageWindow window is also active. 0902 * This is only required on Ubuntu's Unity desktop, which doesn't transfer 0903 * keyboard focus properly between Edit Alarm Dialog windows and MessageWindow 0904 * windows. 0905 */ 0906 bool EditAlarmDlg::eventFilter(QObject*, QEvent* e) 0907 { 0908 if (theApp()->needWindowFocusFix()) 0909 { 0910 if (e->type() == QEvent::MouseButtonPress) 0911 QApplication::setActiveWindow(this); 0912 } 0913 return false; 0914 } 0915 0916 /****************************************************************************** 0917 * Called when the dialog is closed. 0918 */ 0919 void EditAlarmDlg::closeEvent(QCloseEvent* ce) 0920 { 0921 Q_EMIT rejected(); 0922 QDialog::closeEvent(ce); 0923 } 0924 0925 /****************************************************************************** 0926 * Update the tab sizes (again) and if the resized dialog height is greater 0927 * than the minimum, resize it again. This is necessary because (a) resizing 0928 * tabs doesn't always work properly the first time, and (b) resizing to the 0929 * minimum size hint doesn't always work either. 0930 */ 0931 void EditAlarmDlg::slotResize() 0932 { 0933 mTabScrollGroup->adjustSize(true); 0934 const QSize s = minimumSizeHint(); 0935 if (height() > s.height()) 0936 { 0937 // Resize to slightly greater than the minimum height. 0938 // This is for some unknown reason necessary, since 0939 // sometimes resizing to the minimum height fails. 0940 resize(s.width(), s.height() + 2); 0941 } 0942 } 0943 0944 /****************************************************************************** 0945 * Called when the dialog's size has changed. 0946 * Records the new size (adjusted to ignore the optional height of the deferred 0947 * time edit widget) in the config file. 0948 */ 0949 void EditAlarmDlg::resizeEvent(QResizeEvent* re) 0950 { 0951 if (isVisible() && mDeferGroupHeight) 0952 { 0953 QSize s = re->size(); 0954 s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight)); 0955 Config::writeWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s); 0956 } 0957 QDialog::resizeEvent(re); 0958 } 0959 0960 /****************************************************************************** 0961 * Called when any button is clicked. 0962 */ 0963 void EditAlarmDlg::slotButtonClicked(QAbstractButton* button) 0964 { 0965 if (button == mTryButton) 0966 slotTry(); 0967 else if (button == mLoadTemplateButton) 0968 slotHelp(); 0969 else if (button == mMoreLessButton) 0970 slotDefault(); 0971 else if (button == mButtonBox->button(QDialogButtonBox::Ok)) 0972 { 0973 if (validate()) 0974 accept(); 0975 } 0976 else 0977 reject(); 0978 } 0979 0980 /****************************************************************************** 0981 * Called when the OK button is clicked. 0982 * Validate the input data. 0983 */ 0984 bool EditAlarmDlg::validate() 0985 { 0986 if (!stateChanged()) 0987 { 0988 // No changes have been made except possibly to an existing deferral 0989 if (!mOnlyDeferred) 0990 reject(); 0991 return mOnlyDeferred; 0992 } 0993 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType(); 0994 if (mTimeWidget 0995 && mTabs->currentIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN) 0996 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime()); 0997 const bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login? 0998 if (mTemplate) 0999 { 1000 // Check that the template name is not blank and is unique 1001 QString errmsg; 1002 const QString name = mName->text(); 1003 if (name.isEmpty()) 1004 errmsg = i18nc("@info", "You must enter a name for the alarm template"); 1005 else if (name != mSavedName) 1006 { 1007 if (ResourcesCalendar::templateEvent(name).isValid()) 1008 errmsg = i18nc("@info", "Template name is already in use"); 1009 } 1010 if (!errmsg.isEmpty()) 1011 { 1012 mName->setFocus(); 1013 KAMessageBox::error(this, errmsg); 1014 return false; 1015 } 1016 } 1017 else if (mTimeWidget) 1018 { 1019 QWidget* errWidget; 1020 mAlarmDateTime = mTimeWidget->getDateTime(!timedRecurrence, false, &errWidget); 1021 if (errWidget) 1022 { 1023 // It's more than just an existing deferral being changed, so the time matters 1024 mTabs->setCurrentIndex(mMainPageIndex); 1025 errWidget->setFocus(); 1026 mTimeWidget->getDateTime(); // display the error message now 1027 return false; 1028 } 1029 if (!mAlarmDateTime.isDateOnly() && KADateTime::currentUtcDateTime().secsTo(mAlarmDateTime.kDateTime()) / 60 < 10) 1030 { 1031 if (KAMessageBox::warningContinueCancel(this, xi18nc("@info", "<para>KAlarm does not provide high accuracy alarms.</para><para> The alarm will trigger at the minute boundary before the specified time from now.</para>"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("HighAccuracy")) 1032 != KMessageBox::Continue) 1033 { 1034 mTimeWidget->setFocus(); 1035 return false; 1036 } 1037 } 1038 } 1039 if (!type_validate(false)) 1040 return false; 1041 1042 if (!mTemplate) 1043 { 1044 if (mChanged && mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR) 1045 { 1046 // Check whether the start date/time must be adjusted 1047 // to match the recurrence specification. 1048 DateTime dt = mAlarmDateTime; // setEvent() changes mAlarmDateTime 1049 KAEvent event; 1050 setEvent(event, mAlarmMessage, false); 1051 mAlarmDateTime = dt; // restore 1052 KADateTime pre = dt.effectiveKDateTime(); 1053 bool dateOnly = dt.isDateOnly(); 1054 if (dateOnly) 1055 pre = pre.addDays(-1); 1056 else 1057 pre = pre.addSecs(-1); 1058 DateTime next; 1059 event.nextOccurrence(pre, next, KAEvent::Repeats::Ignore); 1060 if (next != dt) 1061 { 1062 QString prompt = dateOnly ? i18nc("@info The parameter is a date value", 1063 "The start date does not match the alarm's recurrence pattern, " 1064 "so it will be adjusted to the date of the next recurrence (%1).", 1065 QLocale().toString(next.date(), QLocale::ShortFormat)) 1066 : i18nc("@info The parameter is a date/time value", 1067 "The start date/time does not match the alarm's recurrence pattern, " 1068 "so it will be adjusted to the date/time of the next recurrence (%1).", 1069 QLocale().toString(next.effectiveDateTime(), QLocale::ShortFormat)); 1070 if (KAMessageBox::warningContinueCancel(this, prompt) != KMessageBox::Continue) 1071 return false; 1072 } 1073 } 1074 1075 if (timedRecurrence) 1076 { 1077 KAEvent event; 1078 Resource res; 1079 getEvent(event, res); // this may adjust mAlarmDateTime 1080 const KADateTime now = KADateTime::currentDateTime(mAlarmDateTime.timeSpec()); 1081 bool dateOnly = mAlarmDateTime.isDateOnly(); 1082 if ((dateOnly && mAlarmDateTime.date() < now.date()) 1083 || (!dateOnly && mAlarmDateTime.kDateTime() < now)) 1084 { 1085 // A timed recurrence has an entered start date which 1086 // has already expired, so we must adjust it. 1087 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::Repeats::RecurBefore) == KAEvent::OccurType::None) 1088 { 1089 KAMessageBox::error(this, i18nc("@info", "Recurrence has already expired")); 1090 return false; 1091 } 1092 if (event.workTimeOnly() && !event.nextTrigger(KAEvent::Trigger::Display).isValid()) 1093 { 1094 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours")) 1095 != KMessageBox::Continue) 1096 return false; 1097 } 1098 } 1099 } 1100 QString errmsg; 1101 QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg); 1102 if (errWidget) 1103 { 1104 mTabs->setCurrentIndex(mRecurPageIndex); 1105 errWidget->setFocus(); 1106 KAMessageBox::error(this, errmsg); 1107 return false; 1108 } 1109 } 1110 if (recurType != RecurrenceEdit::NO_RECUR) 1111 { 1112 KAEvent recurEvent; 1113 int longestRecurMinutes = -1; 1114 int reminder = mReminder ? mReminder->minutes() : 0; 1115 if (reminder && !mReminder->isOnceOnly()) 1116 { 1117 mRecurrenceEdit->updateEvent(recurEvent, false); 1118 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60; 1119 if (longestRecurMinutes && reminder >= longestRecurMinutes) 1120 { 1121 mTabs->setCurrentIndex(mMainPageIndex); 1122 mReminder->setFocusOnCount(); 1123 KAMessageBox::error(this, xi18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.", 1124 Reminder::i18n_chk_FirstRecurrenceOnly())); 1125 return false; 1126 } 1127 } 1128 if (mRecurrenceEdit->subRepetition()) 1129 { 1130 if (longestRecurMinutes < 0) 1131 { 1132 mRecurrenceEdit->updateEvent(recurEvent, false); 1133 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60; 1134 } 1135 if (longestRecurMinutes > 0 1136 && recurEvent.repetition().intervalMinutes() * recurEvent.repetition().count() >= longestRecurMinutes - reminder) 1137 { 1138 KAMessageBox::error(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period")); 1139 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again 1140 return false; 1141 } 1142 if (!recurEvent.repetition().isDaily() 1143 && ((mTemplate && mTemplateAnyTime->isChecked()) || (!mTemplate && mAlarmDateTime.isDateOnly()))) 1144 { 1145 KAMessageBox::error(this, i18nc("@info", "For a repetition within the recurrence, its period must be in units of days or weeks for a date-only alarm")); 1146 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again 1147 return false; 1148 } 1149 } 1150 } 1151 if (!checkText(mAlarmMessage)) 1152 return false; 1153 1154 mResource = Resource(); 1155 // mUseResourceEventId = false indicates that the caller already knows 1156 // which resource to use. 1157 if (mUseResourceEventId) 1158 { 1159 if (!mResourceEventId.isEmpty()) 1160 { 1161 mResource = Resources::resourceForEvent(mResourceEventId); 1162 if (mResource.isValid()) 1163 { 1164 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE; 1165 if (!(mResource.alarmTypes() & type)) 1166 mResource = Resource(); // event may have expired while dialog was open 1167 } 1168 } 1169 bool cancelled = false; 1170 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE; 1171 if (!mResource.isWritable(type)) 1172 mResource = Resources::destination(type, this, Resources::NoDestOption, &cancelled); 1173 if (!mResource.isValid()) 1174 { 1175 if (!cancelled) 1176 KAMessageBox::error(this, i18nc("@info", "You must select a calendar to save the alarm in")); 1177 return false; 1178 } 1179 } 1180 return true; 1181 } 1182 1183 /****************************************************************************** 1184 * Called when the Try button is clicked. 1185 * Display/execute the alarm immediately for the user to check its configuration. 1186 */ 1187 void EditAlarmDlg::slotTry() 1188 { 1189 QString text; 1190 if (checkText(text)) 1191 { 1192 if (!type_validate(true)) 1193 return; 1194 KAEvent event; 1195 setEvent(event, text, true); 1196 if (!mNewAlarm && !stateChanged()) 1197 { 1198 // It's an existing alarm which hasn't been changed yet: 1199 // enable KALARM_UID environment variable to be set. 1200 event.setEventId(mEventId); 1201 } 1202 type_aboutToTry(); 1203 void* result = theApp()->execAlarm(event, event.firstAlarm(), KAlarmApp::NoRecordCmdError | KAlarmApp::NoNotifyInhibit); 1204 type_executedTry(text, result); 1205 } 1206 } 1207 1208 /****************************************************************************** 1209 * Called when the Load Template button is clicked. 1210 * Prompt to select a template and initialise the dialog with its contents. 1211 */ 1212 void EditAlarmDlg::slotHelp() 1213 { 1214 KAEvent::Action type; 1215 switch (mAlarmType) 1216 { 1217 case KAEvent::SubAction::File: 1218 case KAEvent::SubAction::Message: type = KAEvent::Action::Display; break; 1219 case KAEvent::SubAction::Command: type = KAEvent::Action::Command; break; 1220 case KAEvent::SubAction::Email: type = KAEvent::Action::Email; break; 1221 case KAEvent::SubAction::Audio: type = KAEvent::Action::Audio; break; 1222 default: 1223 return; 1224 } 1225 // Use AutoQPointer to guard against crash on application exit while 1226 // the dialogue is still open. It prevents double deletion (both on 1227 // deletion of EditAlarmDlg, and on return from this function). 1228 AutoQPointer<TemplatePickDlg> dlg = new TemplatePickDlg(type, this); 1229 if (dlg->exec() == QDialog::Accepted) 1230 { 1231 KAEvent event = dlg->selectedTemplate(); 1232 initValues(event); 1233 } 1234 } 1235 1236 /****************************************************************************** 1237 * Called when the More Options or Fewer Options buttons are clicked. 1238 * Show/hide the optional options and swap the More/Less buttons, and save the 1239 * new setting as the default from now on. 1240 */ 1241 void EditAlarmDlg::slotDefault() 1242 { 1243 showOptions(!mShowingMore); 1244 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP); 1245 config.writeEntry(EDIT_MORE_KEY, mShowingMore); 1246 } 1247 1248 /****************************************************************************** 1249 * Show/hide the optional options and swap the More/Less buttons. 1250 */ 1251 void EditAlarmDlg::showOptions(bool more) 1252 { 1253 qCDebug(KALARM_LOG) << "EditAlarmDlg::showOptions:" << (more ? "More" : "Less"); 1254 if (more) 1255 { 1256 mMoreOptions->show(); 1257 mMoreLessButton->setText(i18nc("@action:Button", "Fewer Options <<")); 1258 mMoreLessButton->setToolTip(i18nc("@info:tooltip", "Show fewer options")); 1259 } 1260 else 1261 { 1262 mMoreOptions->hide(); 1263 mMoreLessButton->setText(i18nc("@action:button", "More Options >>")); 1264 mMoreLessButton->setToolTip(i18nc("@info:tooltip", "Show more options")); 1265 } 1266 if (mTimeWidget) 1267 mTimeWidget->showMoreOptions(more); 1268 type_showOptions(more); 1269 mRecurrenceEdit->showMoreOptions(more); 1270 mShowingMore = more; 1271 QTimer::singleShot(0, this, &EditAlarmDlg::slotResize); //NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) 1272 } 1273 1274 /****************************************************************************** 1275 * Called when the Change deferral button is clicked. 1276 */ 1277 void EditAlarmDlg::slotEditDeferral() 1278 { 1279 if (!mTimeWidget) 1280 return; 1281 bool limit = true; 1282 const Repetition repetition = mRecurrenceEdit->subRepetition(); 1283 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime()) 1284 : mTimeWidget->getDateTime(!repetition, !mExpiredRecurrence); 1285 if (!start.isValid()) 1286 { 1287 if (!mExpiredRecurrence) 1288 return; 1289 limit = false; 1290 } 1291 const KADateTime now = KADateTime::currentUtcDateTime(); 1292 if (limit) 1293 { 1294 if (repetition && start < now) 1295 { 1296 // Sub-repetition - find the time of the next one 1297 int repeatNum = repetition.isDaily() 1298 ? (start.daysTo(now) + repetition.intervalDays() - 1) / repetition.intervalDays() 1299 : (start.secsTo(now) + repetition.intervalSeconds() - 1) / repetition.intervalSeconds(); 1300 if (repeatNum > repetition.count()) 1301 { 1302 mTimeWidget->getDateTime(); // output the appropriate error message 1303 return; 1304 } 1305 start = KADateTime(repetition.duration(repeatNum).end(start.qDateTime())); 1306 } 1307 } 1308 1309 bool deferred = mDeferDateTime.isValid(); 1310 // Use AutoQPointer to guard against crash on application exit while 1311 // the dialogue is still open. It prevents double deletion (both on 1312 // deletion of EditAlarmDlg, and on return from this function). 1313 AutoQPointer<DeferAlarmDlg> deferDlg = new DeferAlarmDlg((deferred ? mDeferDateTime : DateTime(now.addSecs(60).toTimeSpec(start.timeSpec()))), 1314 start.isDateOnly(), deferred, this); 1315 deferDlg->setObjectName(QLatin1StringView("EditDeferDlg")); // used by LikeBack 1316 if (limit) 1317 { 1318 // Don't allow deferral past the next recurrence 1319 int reminder = mReminder ? mReminder->minutes() : 0; 1320 if (reminder) 1321 { 1322 DateTime remindTime = start.addMins(-reminder); 1323 if (KADateTime::currentUtcDateTime() < remindTime) 1324 start = remindTime; 1325 } 1326 deferDlg->setLimit(start.addSecs(-60)); 1327 } 1328 if (deferDlg->exec() == QDialog::Accepted) 1329 { 1330 mDeferDateTime = deferDlg->getDateTime(); 1331 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString()); 1332 contentsChanged(); 1333 } 1334 } 1335 1336 /****************************************************************************** 1337 * Called when the main page is shown. 1338 * Sets the focus widget to the first edit field. 1339 */ 1340 void EditAlarmDlg::slotShowMainPage() 1341 { 1342 if (!mMainPageShown) 1343 { 1344 if (mName) 1345 mName->setFocus(); 1346 mMainPageShown = true; 1347 } 1348 else 1349 { 1350 // Set scroll position to top, since it otherwise jumps randomly 1351 auto main = static_cast<StackedScrollWidget*>(mTabs->widget(0)); 1352 main->verticalScrollBar()->setValue(0); 1353 } 1354 if (mTimeWidget) 1355 { 1356 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN) 1357 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime()); 1358 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType()) 1359 mTimeWidget->setMinDateTime(); // don't set a minimum date/time 1360 else 1361 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock 1362 } 1363 } 1364 1365 /****************************************************************************** 1366 * Called when the recurrence edit page is shown. 1367 * The recurrence defaults are set to correspond to the start date. 1368 * The first time, for a new alarm, the recurrence end date is set according to 1369 * the alarm start time. 1370 */ 1371 void EditAlarmDlg::slotShowRecurrenceEdit() 1372 { 1373 mRecurPageIndex = mTabs->currentIndex(); 1374 if (!mReadOnly && !mTemplate) 1375 { 1376 mAlarmDateTime = mTimeWidget->getDateTime(false, false); 1377 const KADateTime now = KADateTime::currentDateTime(mAlarmDateTime.timeSpec()); 1378 bool expired = (mAlarmDateTime.effectiveKDateTime() < now); 1379 if (mRecurSetDefaultEndDate) 1380 { 1381 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date()); 1382 mRecurSetDefaultEndDate = false; 1383 } 1384 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date()); 1385 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN) 1386 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime.kDateTime()); 1387 } 1388 mRecurPageShown = true; 1389 } 1390 1391 /****************************************************************************** 1392 * Called when the recurrence type selection changes. 1393 * Enables/disables date-only alarms as appropriate. 1394 * Enables/disables controls depending on at-login setting. 1395 */ 1396 void EditAlarmDlg::slotRecurTypeChange(int repeatType) 1397 { 1398 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN); 1399 if (!mTemplate) 1400 { 1401 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR); 1402 if (mDeferGroup) 1403 mDeferGroup->setEnabled(recurs); 1404 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY); 1405 if (atLogin) 1406 { 1407 mAlarmDateTime = mTimeWidget->getDateTime(false, false); 1408 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.kDateTime()); 1409 } 1410 if (mReminder) 1411 mReminder->enableOnceOnly(recurs && !atLogin); 1412 } 1413 if (mReminder) 1414 mReminder->setAfterOnly(atLogin); 1415 mLateCancel->setEnabled(!atLogin); 1416 setWakeFromSuspendEnabledStatus(); 1417 if (mShowInKorganizer) 1418 mShowInKorganizer->setEnabled(!atLogin); 1419 slotRecurFrequencyChange(); 1420 } 1421 1422 /****************************************************************************** 1423 * Called when the recurrence frequency selection changes, or the sub- 1424 * repetition interval changes. 1425 * Updates the recurrence frequency text. 1426 */ 1427 void EditAlarmDlg::slotRecurFrequencyChange() 1428 { 1429 slotSetSubRepetition(); 1430 KAEvent event; 1431 mRecurrenceEdit->updateEvent(event, false); 1432 mTabs->setTabText(mRecurPageIndex, recurText(event)); 1433 } 1434 1435 /****************************************************************************** 1436 * Called when the Repetition within Recurrence button has been pressed to 1437 * display the sub-repetition dialog. 1438 * Alarm repetition has the following restrictions: 1439 * 1) Not allowed for a repeat-at-login alarm 1440 * 2) For a date-only alarm, the repeat interval must be a whole number of days. 1441 * 3) The overall repeat duration must be less than the recurrence interval. 1442 */ 1443 void EditAlarmDlg::slotSetSubRepetition() 1444 { 1445 bool dateOnly = mTemplate ? mTemplateAnyTime->isChecked() : mTimeWidget->anyTime(); 1446 mRecurrenceEdit->setSubRepetition((mReminder ? mReminder->minutes() : 0), dateOnly); 1447 } 1448 1449 /****************************************************************************** 1450 * Called when one of the template time radio buttons is clicked, 1451 * to enable or disable the template time entry spin boxes. 1452 */ 1453 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton*) 1454 { 1455 mTemplateTime->setEnabled(mTemplateUseTime->isChecked()); 1456 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isChecked()); 1457 slotAnyTimeToggled(mTemplateAnyTime && mTemplateAnyTime->isChecked()); 1458 } 1459 1460 /****************************************************************************** 1461 * Called when the "Any time" checkbox is toggled in the date/time widget. 1462 * Sets the advance reminder and late cancel units to days if any time is checked. 1463 */ 1464 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime) 1465 { 1466 if (mReminder) 1467 mReminder->setDateOnly(anyTime); 1468 mLateCancel->setDateOnly(anyTime); 1469 setWakeFromSuspendEnabledStatus(); 1470 } 1471 1472 /****************************************************************************** 1473 * Enable or disable the wake-from-suspend checkbox as appropriate. 1474 */ 1475 void EditAlarmDlg::setWakeFromSuspendEnabledStatus() 1476 { 1477 if (mWakeFromSuspend) 1478 { 1479 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN); 1480 bool anyTime = (mTimeWidget && mTimeWidget->anyTimeSelected()) 1481 || (mTemplateAnyTime && mTemplateAnyTime->isChecked()); 1482 mWakeFromSuspend->setEnabled(!atLogin && !anyTime); 1483 } 1484 } 1485 1486 bool EditAlarmDlg::dateOnly() const 1487 { 1488 return mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isChecked(); 1489 } 1490 1491 bool EditAlarmDlg::isTimedRecurrence() const 1492 { 1493 return mRecurrenceEdit->isTimedRepeatType(); 1494 } 1495 1496 void EditAlarmDlg::showMainPage() 1497 { 1498 mTabs->setCurrentIndex(mMainPageIndex); 1499 } 1500 1501 #include "moc_editdlg.cpp" 1502 #include "moc_editdlg_p.cpp" 1503 1504 // vim: et sw=4: