Continue to port to QRegularExpression
[kdepim.git] / kalarm / editdlg.cpp
blob30e1875b8f3219c183f771bcec80f092dd488dfc
1 /*
2 * editdlg.cpp - dialog to create or modify an alarm or alarm template
3 * Program: kalarm
4 * Copyright © 2001-2015 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h"
22 #include "editdlg.h"
23 #include "editdlg_p.h"
24 #include "editdlgtypes.h"
26 #include "alarmcalendar.h"
27 #include "collectionmodel.h"
28 #include "alarmtimewidget.h"
29 #include "autoqpointer.h"
30 #include "buttongroup.h"
31 #include "checkbox.h"
32 #include "deferdlg.h"
33 #include "functions.h"
34 #include "kalarmapp.h"
35 #include "latecancel.h"
36 #include "lineedit.h"
37 #include "mainwindow.h"
38 #include "messagebox.h"
39 #include "packedlayout.h"
40 #include "preferences.h"
41 #include "radiobutton.h"
42 #include "recurrenceedit.h"
43 #include "reminder.h"
44 #include "shellprocess.h"
45 #include "spinbox.h"
46 #include "stackedwidgets.h"
47 #include "templatepickdlg.h"
48 #include "timeedit.h"
49 #include "timespinbox.h"
51 #include <Libkdepim/MaillistDrag>
53 #include <kglobal.h>
54 #include <KLocalizedString>
55 #include <kconfig.h>
56 #include <KSharedConfig>
57 #include <kwindowsystem.h>
58 #include <KTimeZone>
60 #include <QLabel>
61 #include <QGroupBox>
62 #include <QPushButton>
63 #include <QGridLayout>
64 #include <QHBoxLayout>
65 #include <QVBoxLayout>
66 #include <QResizeEvent>
67 #include <QShowEvent>
68 #include <QScrollBar>
69 #include <QTimer>
70 #include <QDialogButtonBox>
71 #include "kalarm_debug.h"
73 using namespace KCal;
74 using namespace KAlarmCal;
76 static const char EDIT_DIALOG_NAME[] = "EditDialog";
77 static const char TEMPLATE_DIALOG_NAME[] = "EditTemplateDialog";
78 static const char EDIT_MORE_GROUP[] = "ShowOpts";
79 static const char EDIT_MORE_KEY[] = "EditMore";
80 static const int maxDelayTime = 99*60 + 59; // < 100 hours
82 inline QString recurText(const KAEvent& event)
84 QString r;
85 if (event.repetition())
86 r = QStringLiteral("%1 / %2").arg(event.recurrenceText()).arg(event.repetitionText());
87 else
88 r = event.recurrenceText();
89 return i18nc("@title:tab", "Recurrence - [%1]", r);
92 QList<EditAlarmDlg*> EditAlarmDlg::mWindowList;
94 // Collect these widget labels together to ensure consistent wording and
95 // translations across different modules.
96 QString EditAlarmDlg::i18n_chk_ShowInKOrganizer() { return i18nc("@option:check", "Show in KOrganizer"); }
99 EditAlarmDlg* EditAlarmDlg::create(bool Template, Type type, QWidget* parent, GetResourceType getResource)
101 qCDebug(KALARM_LOG);
102 switch (type)
104 case DISPLAY: return new EditDisplayAlarmDlg(Template, parent, getResource);
105 case COMMAND: return new EditCommandAlarmDlg(Template, parent, getResource);
106 case EMAIL: return new EditEmailAlarmDlg(Template, parent, getResource);
107 case AUDIO: return new EditAudioAlarmDlg(Template, parent, getResource);
108 default: break;
110 return Q_NULLPTR;
113 EditAlarmDlg* EditAlarmDlg::create(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
114 GetResourceType getResource, bool readOnly)
116 switch (event->actionTypes())
118 case KAEvent::ACT_COMMAND: return new EditCommandAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
119 case KAEvent::ACT_DISPLAY_COMMAND:
120 case KAEvent::ACT_DISPLAY: return new EditDisplayAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
121 case KAEvent::ACT_EMAIL: return new EditEmailAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
122 case KAEvent::ACT_AUDIO: return new EditAudioAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
123 default:
124 break;
126 return Q_NULLPTR;
130 /******************************************************************************
131 * Constructor.
132 * Parameters:
133 * Template = true to edit/create an alarm template
134 * = false to edit/create an alarm.
135 * event != to initialise the dialog to show the specified event's data.
137 EditAlarmDlg::EditAlarmDlg(bool Template, KAEvent::SubAction action, QWidget* parent, GetResourceType getResource)
138 : QDialog(parent),
139 mAlarmType(action),
140 mLoadTemplateButton(Q_NULLPTR),
141 mMainPageShown(false),
142 mRecurPageShown(false),
143 mRecurSetDefaultEndDate(true),
144 mTemplateName(Q_NULLPTR),
145 mDeferGroup(Q_NULLPTR),
146 mDeferChangeButton(Q_NULLPTR),
147 mTimeWidget(Q_NULLPTR),
148 mShowInKorganizer(Q_NULLPTR),
149 mDeferGroupHeight(0),
150 mTemplate(Template),
151 mNewAlarm(true),
152 mDesiredReadOnly(false),
153 mReadOnly(false),
154 mShowingMore(true),
155 mSavedEvent(Q_NULLPTR)
157 init(Q_NULLPTR, getResource);
158 mWindowList.append(this);
161 EditAlarmDlg::EditAlarmDlg(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
162 GetResourceType getResource, bool readOnly)
163 : QDialog(parent),
164 mAlarmType(event->actionSubType()),
165 mLoadTemplateButton(Q_NULLPTR),
166 mMainPageShown(false),
167 mRecurPageShown(false),
168 mRecurSetDefaultEndDate(true),
169 mTemplateName(Q_NULLPTR),
170 mDeferGroup(Q_NULLPTR),
171 mDeferChangeButton(Q_NULLPTR),
172 mTimeWidget(Q_NULLPTR),
173 mShowInKorganizer(Q_NULLPTR),
174 mDeferGroupHeight(0),
175 mEventId(newAlarm ? QString() : event->id()),
176 mTemplate(Template),
177 mNewAlarm(newAlarm),
178 mDesiredReadOnly(readOnly),
179 mReadOnly(readOnly),
180 mShowingMore(true),
181 mSavedEvent(Q_NULLPTR)
183 init(event, getResource);
184 mWindowList.append(this);
187 void EditAlarmDlg::init(const KAEvent* event, GetResourceType getResource)
189 switch (getResource)
191 case RES_USE_EVENT_ID:
192 if (event)
194 mCollectionItemId = event->itemId();
195 break;
197 // fall through to RES_PROMPT
198 case RES_PROMPT:
199 mCollectionItemId = -1;
200 break;
201 case RES_IGNORE:
202 default:
203 mCollectionItemId = -2;
204 break;
208 void EditAlarmDlg::init(const KAEvent* event)
210 setObjectName(mTemplate ? QStringLiteral("TemplEditDlg") : QStringLiteral("EditDlg")); // used by LikeBack
211 QString caption;
212 if (mReadOnly)
213 caption = mTemplate ? i18nc("@title:window", "Alarm Template [read-only]")
214 : event->expired() ? i18nc("@title:window", "Archived Alarm [read-only]")
215 : i18nc("@title:window", "Alarm [read-only]");
216 else
217 caption = type_caption();
218 setWindowTitle(caption);
220 // Create button box now so taht types can work with it, but don't insert
221 // it into layout just yet
222 mButtonBox = new QDialogButtonBox(this);
223 if (mReadOnly) {
224 mButtonBox->addButton(QDialogButtonBox::Cancel);
225 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
226 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
227 } else if (mTemplate) {
228 mButtonBox->addButton(QDialogButtonBox::Ok);
229 mButtonBox->addButton(QDialogButtonBox::Cancel);
230 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
231 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
232 } else {
233 mButtonBox->addButton(QDialogButtonBox::Ok);
234 mButtonBox->addButton(QDialogButtonBox::Cancel);
235 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
236 mLoadTemplateButton = mButtonBox->addButton(i18nc("@action:button", "Load Template..."),
237 QDialogButtonBox::HelpRole);
238 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
240 connect(mButtonBox, &QDialogButtonBox::clicked,
241 this, &EditAlarmDlg::slotButtonClicked);
243 if (mButtonBox->button(QDialogButtonBox::Ok)) {
244 mButtonBox->button(QDialogButtonBox::Ok)->setWhatsThis(i18nc("@info:whatsthis", "Schedule the alarm at the specified time."));
247 QVBoxLayout* mainLayout = new QVBoxLayout(this);
248 if (mTemplate)
250 QFrame *frame = new QFrame;
251 QHBoxLayout* box = new QHBoxLayout();
252 frame->setLayout(box);
253 box->setMargin(0);
254 box->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
255 QLabel* label = new QLabel(i18nc("@label:textbox", "Template name:"));
256 label->setFixedSize(label->sizeHint());
257 box->addWidget(label);
258 mTemplateName = new QLineEdit();
259 mTemplateName->setReadOnly(mReadOnly);
260 connect(mTemplateName, &QLineEdit::textEdited, this, &EditAlarmDlg::contentsChanged);
261 label->setBuddy(mTemplateName);
262 box->addWidget(mTemplateName);
263 frame->setWhatsThis(i18nc("@info:whatsthis", "Enter the name of the alarm template"));
264 frame->setFixedHeight(box->sizeHint().height());
265 mainLayout->addWidget(frame);
267 mTabs = new QTabWidget(this);
268 mainLayout->addWidget(mTabs);
269 mTabScrollGroup = new StackedScrollGroup(this, mTabs);
271 StackedScrollWidget* mainScroll = new StackedScrollWidget(mTabScrollGroup);
272 mTabs->addTab(mainScroll, i18nc("@title:tab", "Alarm"));
273 mMainPageIndex = 0;
274 PageFrame* mainPage = new PageFrame(mainScroll);
275 mainScroll->setWidget(mainPage); // mainPage becomes the child of mainScroll
276 connect(mainPage, &PageFrame::shown, this, &EditAlarmDlg::slotShowMainPage);
277 QVBoxLayout* topLayout = new QVBoxLayout(mainPage);
278 topLayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
279 topLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
281 // Recurrence tab
282 StackedScrollWidget* recurScroll = new StackedScrollWidget(mTabScrollGroup);
283 mTabs->addTab(recurScroll, QString());
284 mRecurPageIndex = 1;
285 QFrame *recurTab = new QFrame;
286 QVBoxLayout* recurTabLayout = new QVBoxLayout();
287 recurTabLayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
288 recurTab->setLayout(recurTabLayout);
289 recurScroll->setWidget(recurTab); // recurTab becomes the child of recurScroll
290 mRecurrenceEdit = new RecurrenceEdit(mReadOnly);
291 recurTabLayout->addWidget(mRecurrenceEdit);
292 connect(mRecurrenceEdit, &RecurrenceEdit::shown, this, &EditAlarmDlg::slotShowRecurrenceEdit);
293 connect(mRecurrenceEdit, &RecurrenceEdit::typeChanged, this, &EditAlarmDlg::slotRecurTypeChange);
294 connect(mRecurrenceEdit, &RecurrenceEdit::frequencyChanged, this, &EditAlarmDlg::slotRecurFrequencyChange);
295 connect(mRecurrenceEdit, &RecurrenceEdit::repeatNeedsInitialisation, this, &EditAlarmDlg::slotSetSubRepetition);
296 connect(mRecurrenceEdit, &RecurrenceEdit::contentsChanged, this, &EditAlarmDlg::contentsChanged);
298 // Controls specific to the alarm type
299 QGroupBox* actionBox = new QGroupBox(i18nc("@title:group", "Action"), mainPage);
300 topLayout->addWidget(actionBox, 1);
301 QVBoxLayout* layout = new QVBoxLayout(actionBox);
302 layout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
303 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
305 type_init(actionBox, layout);
307 if (!mTemplate)
309 // Deferred date/time: visible only for a deferred recurring event.
310 mDeferGroup = new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage);
311 topLayout->addWidget(mDeferGroup);
312 QHBoxLayout* hlayout = new QHBoxLayout(mDeferGroup);
313 hlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
314 hlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
315 QLabel* label = new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup);
316 label->setFixedSize(label->sizeHint());
317 hlayout->addWidget(label);
318 mDeferTimeLabel = new QLabel(mDeferGroup);
319 hlayout->addWidget(mDeferTimeLabel);
321 mDeferChangeButton = new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup);
322 mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint());
323 connect(mDeferChangeButton, &QPushButton::clicked, this, &EditAlarmDlg::slotEditDeferral);
324 mDeferChangeButton->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral"));
325 hlayout->addWidget(mDeferChangeButton);
326 //?? mDeferGroup->addSpace(0);
329 QHBoxLayout* hlayout = new QHBoxLayout();
330 hlayout->setMargin(0);
331 topLayout->addLayout(hlayout);
333 // Date and time entry
334 if (mTemplate)
336 QGroupBox* templateTimeBox = new QGroupBox(i18nc("@title:group", "Time"), mainPage);
337 topLayout->addWidget(templateTimeBox);
338 QGridLayout* grid = new QGridLayout(templateTimeBox);
339 grid->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
340 grid->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
341 mTemplateTimeGroup = new ButtonGroup(templateTimeBox);
342 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::slotTemplateTimeType);
343 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::contentsChanged);
345 mTemplateDefaultTime = new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox);
346 mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint());
347 mTemplateDefaultTime->setReadOnly(mReadOnly);
348 mTemplateDefaultTime->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. "
349 "The normal default start time will be used."));
350 mTemplateTimeGroup->addButton(mTemplateDefaultTime);
351 grid->addWidget(mTemplateDefaultTime, 0, 0, Qt::AlignLeft);
353 QWidget *box = new QWidget(templateTimeBox);
354 QHBoxLayout* layout = new QHBoxLayout(box);
355 layout->setMargin(0);
356 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
357 mTemplateUseTime = new RadioButton(i18nc("@option:radio", "Time:"), box);
358 mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint());
359 mTemplateUseTime->setReadOnly(mReadOnly);
360 mTemplateUseTime->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template."));
361 layout->addWidget(mTemplateUseTime);
362 mTemplateTimeGroup->addButton(mTemplateUseTime);
363 mTemplateTime = new TimeEdit();
364 mTemplateTime->setFixedSize(mTemplateTime->sizeHint());
365 mTemplateTime->setReadOnly(mReadOnly);
366 mTemplateTime->setWhatsThis(xi18nc("@info:whatsthis",
367 "<para>Enter the start time for alarms based on this template.</para><para>%1</para>",
368 TimeSpinBox::shiftWhatsThis()));
369 connect(mTemplateTime, &TimeEdit::valueChanged, this, &EditAlarmDlg::contentsChanged);
370 layout->addWidget(mTemplateTime);
371 layout->addStretch(1);
372 grid->addWidget(box, 0, 1, Qt::AlignLeft);
374 mTemplateAnyTime = new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox);
375 mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint());
376 mTemplateAnyTime->setReadOnly(mReadOnly);
377 mTemplateAnyTime->setWhatsThis(xi18nc("@info:whatsthis", "Set the <interface>Any time</interface> option for alarms based on this template."));
378 mTemplateTimeGroup->addButton(mTemplateAnyTime);
379 grid->addWidget(mTemplateAnyTime, 1, 0, Qt::AlignLeft);
381 box = new QWidget(templateTimeBox);
382 layout = new QHBoxLayout(box);
383 layout->setMargin(0);
384 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
385 mTemplateUseTimeAfter = new RadioButton(i18nc("@option:radio", "Time from now:"), box);
386 mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint());
387 mTemplateUseTimeAfter->setReadOnly(mReadOnly);
388 mTemplateUseTimeAfter->setWhatsThis(i18nc("@info:whatsthis",
389 "Set alarms based on this template to start after the specified time "
390 "interval from when the alarm is created."));
391 layout->addWidget(mTemplateUseTimeAfter);
392 mTemplateTimeGroup->addButton(mTemplateUseTimeAfter);
393 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime);
394 mTemplateTimeAfter->setValue(1439);
395 mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint());
396 mTemplateTimeAfter->setReadOnly(mReadOnly);
397 connect(mTemplateTimeAfter, static_cast<void (TimeSpinBox::*)(int)>(&TimeSpinBox::valueChanged), this, &EditAlarmDlg::contentsChanged);
398 mTemplateTimeAfter->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para>",
399 AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis()));
400 layout->addWidget(mTemplateTimeAfter);
401 box->setFixedHeight(box->sizeHint().height());
402 grid->addWidget(box, 1, 1, Qt::AlignLeft);
404 hlayout->addStretch();
406 else
408 mTimeWidget = new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME, mainPage);
409 connect(mTimeWidget, &AlarmTimeWidget::dateOnlyToggled, this, &EditAlarmDlg::slotAnyTimeToggled);
410 connect(mTimeWidget, &AlarmTimeWidget::changed, this, &EditAlarmDlg::contentsChanged);
411 topLayout->addWidget(mTimeWidget);
414 // Optional controls depending on More/Less Options button
415 mMoreOptions = new QFrame(mainPage);
416 mMoreOptions->setFrameStyle(QFrame::NoFrame);
417 topLayout->addWidget(mMoreOptions);
418 QVBoxLayout* moreLayout = new QVBoxLayout(mMoreOptions);
419 moreLayout->setMargin(0);
420 moreLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
422 // Reminder
423 mReminder = createReminder(mMoreOptions);
424 if (mReminder)
426 mReminder->setFixedSize(mReminder->sizeHint());
427 connect(mReminder, &Reminder::changed, this, &EditAlarmDlg::contentsChanged);
428 moreLayout->addWidget(mReminder, 0, Qt::AlignLeft);
429 if (mTimeWidget)
430 connect(mTimeWidget, &AlarmTimeWidget::changed, mReminder, &Reminder::setDefaultUnits);
433 // Late cancel selector - default = allow late display
434 mLateCancel = new LateCancelSelector(true, mMoreOptions);
435 connect(mLateCancel, &LateCancelSelector::changed, this, &EditAlarmDlg::contentsChanged);
436 moreLayout->addWidget(mLateCancel, 0, Qt::AlignLeft);
438 PackedLayout* playout = new PackedLayout(Qt::AlignJustify);
439 playout->setSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
440 moreLayout->addLayout(playout);
442 // Acknowledgement confirmation required - default = no confirmation
443 CheckBox* confirmAck = type_createConfirmAckCheckbox(mMoreOptions);
444 if (confirmAck)
446 confirmAck->setFixedSize(confirmAck->sizeHint());
447 connect(confirmAck, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged);
448 playout->addWidget(confirmAck);
451 if (theApp()->korganizerEnabled())
453 // Show in KOrganizer checkbox
454 mShowInKorganizer = new CheckBox(i18n_chk_ShowInKOrganizer(), mMoreOptions);
455 mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint());
456 connect(mShowInKorganizer, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged);
457 mShowInKorganizer->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar"));
458 playout->addWidget(mShowInKorganizer);
461 mainLayout->addWidget(mButtonBox);
463 // Hide optional controls
464 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP);
465 showOptions(config.readEntry(EDIT_MORE_KEY, false));
467 // Initialise the state of all controls according to the specified event, if any
468 initValues(event);
469 if (mTemplateName)
470 mTemplateName->setFocus();
472 if (!mNewAlarm)
474 // Save the initial state of all controls so that we can later tell if they have changed
475 saveState((event && (mTemplate || !event->isTemplate())) ? event : Q_NULLPTR);
476 contentsChanged(); // enable/disable OK button
479 // Note the current desktop so that the dialog can be shown on it.
480 // If a main window is visible, the dialog will by KDE default always appear on its
481 // desktop. If the user invokes the dialog via the system tray on a different desktop,
482 // that can cause confusion.
483 mDesktop = KWindowSystem::currentDesktop();
485 if (theApp()->windowFocusBroken())
487 const QList<QWidget*> children = findChildren<QWidget*>();
488 foreach (QWidget* w, children)
489 w->installEventFilter(this);
493 EditAlarmDlg::~EditAlarmDlg()
495 delete mSavedEvent;
496 mWindowList.removeAll(this);
499 /******************************************************************************
500 * Return the number of instances.
502 int EditAlarmDlg::instanceCount()
504 return mWindowList.count();
507 /******************************************************************************
508 * Initialise the dialog controls from the specified event.
510 void EditAlarmDlg::initValues(const KAEvent* event)
512 setReadOnly(mDesiredReadOnly);
514 mChanged = false;
515 mOnlyDeferred = false;
516 mExpiredRecurrence = false;
517 mLateCancel->showAutoClose(false);
518 bool deferGroupVisible = false;
519 if (event)
521 // Set the values to those for the specified event
522 if (mTemplate)
523 mTemplateName->setText(event->templateName());
524 bool recurs = event->recurs();
525 if ((recurs || event->repetition()) && !mTemplate && event->deferred())
527 deferGroupVisible = true;
528 mDeferDateTime = event->deferDateTime();
529 mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
530 mDeferGroup->show();
532 if (mTemplate)
534 // Editing a template
535 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
536 bool noTime = !afterTime;
537 bool useTime = !event->mainDateTime().isDateOnly();
538 RadioButton* button = noTime ? mTemplateDefaultTime :
539 (afterTime > 0) ? mTemplateUseTimeAfter :
540 useTime ? mTemplateUseTime : mTemplateAnyTime;
541 button->setChecked(true);
542 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
543 if (!noTime && useTime)
544 mTemplateTime->setValue(event->mainDateTime().kDateTime().time());
545 else
546 mTemplateTime->setValue(0);
548 else
550 if (event->isTemplate())
552 // Initialising from an alarm template: use current date
553 KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
554 int afterTime = event->templateAfterTime();
555 if (afterTime >= 0)
557 mTimeWidget->setDateTime(now.addSecs(afterTime * 60));
558 mTimeWidget->selectTimeFromNow();
560 else
562 KDateTime dt = event->startDateTime().kDateTime();
563 dt.setTimeSpec(Preferences::timeZone());
564 QDate d = now.date();
565 if (!dt.isDateOnly() && now.time() >= dt.time())
566 d = d.addDays(1); // alarm time has already passed, so use tomorrow
567 dt.setDate(d);
568 mTimeWidget->setDateTime(dt);
571 else
573 mExpiredRecurrence = recurs && event->mainExpired();
574 mTimeWidget->setDateTime(recurs || event->category() == CalEvent::ARCHIVED ? event->startDateTime()
575 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
579 KAEvent::SubAction action = event->actionSubType();
580 AlarmText altext;
581 if (event->commandScript())
582 altext.setScript(event->cleanText());
583 else
584 altext.setText(event->cleanText());
585 setAction(action, altext);
587 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
588 TimePeriod::HoursMinutes);
589 if (mShowInKorganizer)
590 mShowInKorganizer->setChecked(event->copyToKOrganizer());
591 type_initValues(event);
592 mRecurrenceEdit->set(*event); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
593 mTabs->setTabText(mRecurPageIndex, recurText(*event));
595 else
597 // Set the values to their defaults
598 KDateTime defaultTime = KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
599 if (mTemplate)
601 mTemplateDefaultTime->setChecked(true);
602 mTemplateTime->setValue(0);
603 mTemplateTimeAfter->setValue(1);
605 else
606 mTimeWidget->setDateTime(defaultTime);
607 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes);
608 if (mShowInKorganizer)
609 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
610 type_initValues(Q_NULLPTR);
611 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
612 slotRecurFrequencyChange(); // update the Recurrence text
614 if (mReminder && mTimeWidget)
615 mReminder->setDefaultUnits(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
617 if (!deferGroupVisible && mDeferGroup)
618 mDeferGroup->hide();
620 bool empty = AlarmCalendar::resources()->events(CalEvent::TEMPLATE).isEmpty();
621 if (mLoadTemplateButton) {
622 mLoadTemplateButton->setEnabled(!empty);
626 /******************************************************************************
627 * Initialise various values in the New Alarm dialogue.
629 void EditAlarmDlg::setTime(const DateTime& start)
631 mTimeWidget->setDateTime(start);
633 void EditAlarmDlg::setRecurrence(const KARecurrence& recur, const KCalCore::Duration& subRepeatInterval, int subRepeatCount)
635 KAEvent event;
636 event.setTime(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
637 event.setRecurrence(recur);
638 event.setRepetition(Repetition(subRepeatInterval, subRepeatCount - 1));
639 mRecurrenceEdit->set(event);
641 void EditAlarmDlg::setRepeatAtLogin()
643 mRecurrenceEdit->setRepeatAtLogin();
645 void EditAlarmDlg::setLateCancel(int minutes)
647 mLateCancel->setMinutes(minutes, mTimeWidget->getDateTime(Q_NULLPTR, false, false).isDateOnly(),
648 TimePeriod::HoursMinutes);
650 void EditAlarmDlg::setShowInKOrganizer(bool show)
652 mShowInKorganizer->setChecked(show);
655 /******************************************************************************
656 * Set the read-only status of all non-template controls.
658 void EditAlarmDlg::setReadOnly(bool readOnly)
660 mReadOnly = readOnly;
662 if (mTimeWidget)
663 mTimeWidget->setReadOnly(readOnly);
664 mLateCancel->setReadOnly(readOnly);
665 if (mDeferChangeButton)
667 if (readOnly)
668 mDeferChangeButton->hide();
669 else
670 mDeferChangeButton->show();
672 if (mShowInKorganizer)
673 mShowInKorganizer->setReadOnly(readOnly);
676 /******************************************************************************
677 * Save the state of all controls.
679 void EditAlarmDlg::saveState(const KAEvent* event)
681 delete mSavedEvent;
682 mSavedEvent = Q_NULLPTR;
683 if (event)
684 mSavedEvent = new KAEvent(*event);
685 if (mTemplate)
687 mSavedTemplateName = mTemplateName->text();
688 mSavedTemplateTimeType = mTemplateTimeGroup->checkedButton();
689 mSavedTemplateTime = mTemplateTime->time();
690 mSavedTemplateAfterTime = mTemplateTimeAfter->value();
692 checkText(mSavedTextFileCommandMessage, false);
693 if (mTimeWidget)
694 mSavedDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
695 mSavedLateCancel = mLateCancel->minutes();
696 if (mShowInKorganizer)
697 mSavedShowInKorganizer = mShowInKorganizer->isChecked();
698 mSavedRecurrenceType = mRecurrenceEdit->repeatType();
699 mSavedDeferTime = mDeferDateTime.kDateTime();
702 /******************************************************************************
703 * Check whether any of the controls has changed state since the dialog was
704 * first displayed.
705 * Reply = true if any non-deferral controls have changed, or if it's a new event.
706 * = false if no non-deferral controls have changed. In this case,
707 * mOnlyDeferred indicates whether deferral controls may have changed.
709 bool EditAlarmDlg::stateChanged() const
711 mChanged = true;
712 mOnlyDeferred = false;
713 if (!mSavedEvent)
714 return true;
715 QString textFileCommandMessage;
716 checkText(textFileCommandMessage, false);
717 if (mTemplate)
719 if (mSavedTemplateName != mTemplateName->text()
720 || mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton()
721 || (mTemplateUseTime->isChecked() && mSavedTemplateTime != mTemplateTime->time())
722 || (mTemplateUseTimeAfter->isChecked() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
723 return true;
725 else
727 KDateTime dt = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
728 if (mSavedDateTime.timeSpec() != dt.timeSpec() || mSavedDateTime != dt)
729 return true;
731 if (mSavedLateCancel != mLateCancel->minutes()
732 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
733 || textFileCommandMessage != mSavedTextFileCommandMessage
734 || mSavedRecurrenceType != mRecurrenceEdit->repeatType())
735 return true;
736 if (type_stateChanged())
737 return true;
738 if (mRecurrenceEdit->stateChanged())
739 return true;
740 if (mSavedEvent && mSavedEvent->deferred())
741 mOnlyDeferred = true;
742 mChanged = false;
743 return false;
746 /******************************************************************************
747 * Called whenever any of the controls changes state.
748 * Enable or disable the OK button depending on whether any controls have a
749 * different state from their initial state.
751 void EditAlarmDlg::contentsChanged()
753 // Don't do anything if it's a new alarm or we're still initialising
754 // (i.e. mSavedEvent null).
755 if (mSavedEvent && mButtonBox->button(QDialogButtonBox::Ok))
756 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(stateChanged() || mDeferDateTime.kDateTime() != mSavedDeferTime);
759 /******************************************************************************
760 * Get the currently entered dialog data.
761 * The data is returned in the supplied KAEvent instance.
762 * Reply = false if the only change has been to an existing deferral.
764 bool EditAlarmDlg::getEvent(KAEvent& event, Akonadi::Collection& collection)
766 collection = mCollection;
767 if (mChanged)
769 // It's a new event, or the edit controls have changed
770 setEvent(event, mAlarmMessage, false);
771 return true;
774 // Only the deferral time may have changed
775 event = *mSavedEvent;
776 if (mOnlyDeferred)
778 // Just modify the original event, to avoid expired recurring events
779 // being returned as rubbish.
780 if (mDeferDateTime.isValid())
781 event.defer(mDeferDateTime, event.reminderDeferral(), false);
782 else
783 event.cancelDefer();
785 return false;
788 /******************************************************************************
789 * Extract the data in the dialog and set up a KAEvent from it.
790 * If 'trial' is true, the event is set up for a simple one-off test, ignoring
791 * recurrence, reminder, template etc. data.
793 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial)
795 KDateTime dt;
796 if (!trial)
798 if (!mTemplate)
799 dt = mAlarmDateTime.effectiveKDateTime();
800 else if (mTemplateUseTime->isChecked())
801 dt = KDateTime(QDate(2000,1,1), mTemplateTime->time());
804 int lateCancel = (trial || !mLateCancel->isEnabled()) ? 0 : mLateCancel->minutes();
805 type_setEvent(event, dt, text, lateCancel, trial);
807 if (!trial)
809 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
811 mRecurrenceEdit->updateEvent(event, !mTemplate);
812 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
813 bool dateOnly = mAlarmDateTime.isDateOnly();
814 if ((dateOnly && mAlarmDateTime.date() < now.date())
815 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
817 // A timed recurrence has an entered start date which has
818 // already expired, so we must adjust the next repetition.
819 event.setNextOccurrence(now);
821 mAlarmDateTime = event.startDateTime();
822 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime)
824 bool deferral = true;
825 bool deferReminder = false;
826 int reminder = mReminder ? mReminder->minutes() : 0;
827 if (reminder)
829 DateTime remindTime = mAlarmDateTime.addMins(-reminder);
830 if (mDeferDateTime >= remindTime)
832 if (remindTime > KDateTime::currentUtcDateTime())
833 deferral = false; // ignore deferral if it's after next reminder
834 else if (mDeferDateTime > remindTime)
835 deferReminder = true; // it's the reminder which is being deferred
838 if (deferral)
839 event.defer(mDeferDateTime, deferReminder, false);
842 if (mTemplate)
844 int afterTime = mTemplateDefaultTime->isChecked() ? 0
845 : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1;
846 event.setTemplate(mTemplateName->text(), afterTime);
851 /******************************************************************************
852 * Get the currently specified alarm flag bits.
854 KAEvent::Flags EditAlarmDlg::getAlarmFlags() const
856 KAEvent::Flags flags(0);
857 if (mShowInKorganizer && mShowInKorganizer->isEnabled() && mShowInKorganizer->isChecked())
858 flags |= KAEvent::COPY_KORGANIZER;
859 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
860 flags |= KAEvent::REPEAT_AT_LOGIN;
861 if (mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly())
862 flags |= KAEvent::ANY_TIME;
863 return flags;
866 /******************************************************************************
867 * Called when the dialog is displayed.
868 * The first time through, sets the size to the same as the last time it was
869 * displayed.
871 void EditAlarmDlg::showEvent(QShowEvent* se)
873 QDialog::showEvent(se);
874 if (!mDeferGroupHeight)
876 if (mDeferGroup)
877 mDeferGroupHeight = mDeferGroup->height() + style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
878 QSize s;
879 if (KAlarm::readConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s))
881 bool defer = mDeferGroup && !mDeferGroup->isHidden();
882 s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0));
883 if (!defer)
884 mTabScrollGroup->setSized();
885 resize(s);
888 slotResize();
889 KWindowSystem::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user
891 if (theApp()->needWindowFocusFix())
893 QApplication::setActiveWindow(this);
894 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
898 /******************************************************************************
899 * Called when the window is first shown, to ensure that it initially becomes
900 * the active window.
901 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
902 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
903 * windows.
905 void EditAlarmDlg::focusFixTimer()
907 if (theApp()->needWindowFocusFix()
908 && QApplication::focusWidget()->window() != this)
910 QApplication::setActiveWindow(this);
911 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
915 /******************************************************************************
916 * Called to detect when the mouse is pressed anywhere inside the window.
917 * Activates this window if a MessageWin window is also active.
918 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
919 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
920 * windows.
922 bool EditAlarmDlg::eventFilter(QObject*, QEvent* e)
924 if (theApp()->needWindowFocusFix())
926 if (e->type() == QEvent::MouseButtonPress)
927 QApplication::setActiveWindow(this);
929 return false;
932 /******************************************************************************
933 * Called when the dialog is closed.
935 void EditAlarmDlg::closeEvent(QCloseEvent* ce)
937 Q_EMIT rejected();
938 QDialog::closeEvent(ce);
941 /******************************************************************************
942 * Update the tab sizes (again) and if the resized dialog height is greater
943 * than the minimum, resize it again. This is necessary because (a) resizing
944 * tabs doesn't always work properly the first time, and (b) resizing to the
945 * minimum size hint doesn't always work either.
947 void EditAlarmDlg::slotResize()
949 QSize s = mTabScrollGroup->adjustSize(true);
950 s = minimumSizeHint();
951 if (height() > s.height())
953 // Resize to slightly greater than the minimum height.
954 // This is for some unknown reason necessary, since
955 // sometimes resizing to the minimum height fails.
956 resize(s.width(), s.height() + 2);
960 /******************************************************************************
961 * Called when the dialog's size has changed.
962 * Records the new size (adjusted to ignore the optional height of the deferred
963 * time edit widget) in the config file.
965 void EditAlarmDlg::resizeEvent(QResizeEvent* re)
967 if (isVisible() && mDeferGroupHeight)
969 QSize s = re->size();
970 s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
971 KAlarm::writeConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s);
973 QDialog::resizeEvent(re);
976 /******************************************************************************
977 * Called when any button is clicked.
979 void EditAlarmDlg::slotButtonClicked(QAbstractButton *button)
981 if (button == mTryButton) {
982 slotTry();
983 } else if (button == mLoadTemplateButton) {
984 slotHelp();
985 } else if (button == mMoreLessButton) {
986 slotDefault();
987 } else if (button == mButtonBox->button(QDialogButtonBox::Ok)) {
988 if (validate()) {
989 accept();
991 } else {
992 reject();
996 /******************************************************************************
997 * Called when the OK button is clicked.
998 * Validate the input data.
1000 bool EditAlarmDlg::validate()
1002 if (!stateChanged())
1004 // No changes have been made except possibly to an existing deferral
1005 if (!mOnlyDeferred)
1006 reject();
1007 return mOnlyDeferred;
1009 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
1010 if (mTimeWidget
1011 && mTabs->currentIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN)
1012 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1013 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login?
1014 if (mTemplate)
1016 // Check that the template name is not blank and is unique
1017 QString errmsg;
1018 QString name = mTemplateName->text();
1019 if (name.isEmpty())
1020 errmsg = i18nc("@info", "You must enter a name for the alarm template");
1021 else if (name != mSavedTemplateName)
1023 if (AlarmCalendar::resources()->templateEvent(name))
1024 errmsg = i18nc("@info", "Template name is already in use");
1026 if (!errmsg.isEmpty())
1028 mTemplateName->setFocus();
1029 KAMessageBox::sorry(this, errmsg);
1030 return false;
1033 else if (mTimeWidget)
1035 QWidget* errWidget;
1036 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, !timedRecurrence, false, &errWidget);
1037 if (errWidget)
1039 // It's more than just an existing deferral being changed, so the time matters
1040 mTabs->setCurrentIndex(mMainPageIndex);
1041 errWidget->setFocus();
1042 mTimeWidget->getDateTime(); // display the error message now
1043 return false;
1046 if (!type_validate(false))
1047 return false;
1049 if (!mTemplate)
1051 if (mChanged && mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
1053 // Check whether the start date/time must be adjusted
1054 // to match the recurrence specification.
1055 DateTime dt = mAlarmDateTime; // setEvent() changes mAlarmDateTime
1056 KAEvent event;
1057 setEvent(event, mAlarmMessage, false);
1058 mAlarmDateTime = dt; // restore
1059 KDateTime pre = dt.effectiveKDateTime();
1060 bool dateOnly = dt.isDateOnly();
1061 if (dateOnly)
1062 pre = pre.addDays(-1);
1063 else
1064 pre = pre.addSecs(-1);
1065 DateTime next;
1066 event.nextOccurrence(pre, next, KAEvent::IGNORE_REPETITION);
1067 if (next != dt)
1069 QString prompt = dateOnly ? i18nc("@info The parameter is a date value",
1070 "The start date does not match the alarm's recurrence pattern, "
1071 "so it will be adjusted to the date of the next recurrence (%1).",
1072 KLocale::global()->formatDate(next.date(), KLocale::ShortDate))
1073 : i18nc("@info The parameter is a date/time value",
1074 "The start date/time does not match the alarm's recurrence pattern, "
1075 "so it will be adjusted to the date/time of the next recurrence (%1).",
1076 KLocale::global()->formatDateTime(next.kDateTime(), KLocale::ShortDate));
1077 if (KAMessageBox::warningContinueCancel(this, prompt) != KMessageBox::Continue)
1078 return false;
1082 if (timedRecurrence)
1084 KAEvent event;
1085 Akonadi::Collection c;
1086 getEvent(event, c); // this may adjust mAlarmDateTime
1087 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1088 bool dateOnly = mAlarmDateTime.isDateOnly();
1089 if ((dateOnly && mAlarmDateTime.date() < now.date())
1090 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
1092 // A timed recurrence has an entered start date which
1093 // has already expired, so we must adjust it.
1094 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1096 KAMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
1097 return false;
1099 if (event.workTimeOnly() && !event.nextTrigger(KAEvent::DISPLAY_TRIGGER).isValid())
1101 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
1102 != KMessageBox::Continue)
1103 return false;
1107 QString errmsg;
1108 QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg);
1109 if (errWidget)
1111 mTabs->setCurrentIndex(mRecurPageIndex);
1112 errWidget->setFocus();
1113 KAMessageBox::sorry(this, errmsg);
1114 return false;
1117 if (recurType != RecurrenceEdit::NO_RECUR)
1119 KAEvent recurEvent;
1120 int longestRecurMinutes = -1;
1121 int reminder = mReminder ? mReminder->minutes() : 0;
1122 if (reminder && !mReminder->isOnceOnly())
1124 mRecurrenceEdit->updateEvent(recurEvent, false);
1125 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1126 if (longestRecurMinutes && reminder >= longestRecurMinutes)
1128 mTabs->setCurrentIndex(mMainPageIndex);
1129 mReminder->setFocusOnCount();
1130 KAMessageBox::sorry(this, xi18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.",
1131 Reminder::i18n_chk_FirstRecurrenceOnly()));
1132 return false;
1135 if (mRecurrenceEdit->subRepetition())
1137 if (longestRecurMinutes < 0)
1139 mRecurrenceEdit->updateEvent(recurEvent, false);
1140 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1142 if (longestRecurMinutes > 0
1143 && recurEvent.repetition().intervalMinutes() * recurEvent.repetition().count() >= longestRecurMinutes - reminder)
1145 KAMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1146 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1147 return false;
1149 if (!recurEvent.repetition().isDaily()
1150 && ((mTemplate && mTemplateAnyTime->isChecked()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1152 KAMessageBox::sorry(this, i18nc("@info", "For a repetition within the recurrence, its period must be in units of days or weeks for a date-only alarm"));
1153 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1154 return false;
1158 if (!checkText(mAlarmMessage))
1159 return false;
1161 mCollection = Akonadi::Collection();
1162 // An item ID = -2 indicates that the caller already
1163 // knows which collection to use.
1164 if (mCollectionItemId >= -1)
1166 if (mCollectionItemId >= 0)
1168 mCollection = AlarmCalendar::resources()->collectionForEvent(mCollectionItemId);
1169 if (mCollection.isValid())
1171 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1172 if (!(AkonadiModel::instance()->types(mCollection) & type))
1173 mCollection = Akonadi::Collection(); // event may have expired while dialog was open
1176 bool cancelled = false;
1177 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1178 if (CollectionControlModel::isWritableEnabled(mCollection, type) <= 0)
1179 mCollection = CollectionControlModel::destination(type, this, false, &cancelled);
1180 if (!mCollection.isValid())
1182 if (!cancelled)
1183 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1184 return false;
1187 return true;
1190 /******************************************************************************
1191 * Called when the Try button is clicked.
1192 * Display/execute the alarm immediately for the user to check its configuration.
1194 void EditAlarmDlg::slotTry()
1196 QString text;
1197 if (checkText(text))
1199 if (!type_validate(true))
1200 return;
1201 KAEvent event;
1202 setEvent(event, text, true);
1203 if (!mNewAlarm && !stateChanged())
1205 // It's an existing alarm which hasn't been changed yet:
1206 // enable KALARM_UID environment variable to be set.
1207 event.setEventId(mEventId);
1209 type_aboutToTry();
1210 void* result = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1211 type_executedTry(text, result);
1215 /******************************************************************************
1216 * Called when the Load Template button is clicked.
1217 * Prompt to select a template and initialise the dialog with its contents.
1219 void EditAlarmDlg::slotHelp()
1221 KAEvent::Actions type;
1222 switch (mAlarmType)
1224 case KAEvent::FILE:
1225 case KAEvent::MESSAGE: type = KAEvent::ACT_DISPLAY; break;
1226 case KAEvent::COMMAND: type = KAEvent::ACT_COMMAND; break;
1227 case KAEvent::EMAIL: type = KAEvent::ACT_EMAIL; break;
1228 case KAEvent::AUDIO: type = KAEvent::ACT_AUDIO; break;
1229 default:
1230 return;
1232 // Use AutoQPointer to guard against crash on application exit while
1233 // the dialogue is still open. It prevents double deletion (both on
1234 // deletion of EditAlarmDlg, and on return from this function).
1235 AutoQPointer<TemplatePickDlg> dlg = new TemplatePickDlg(type, this);
1236 if (dlg->exec() == QDialog::Accepted)
1238 KAEvent event = dlg->selectedTemplate();
1239 initValues(&event);
1243 /******************************************************************************
1244 * Called when the More Options or Less Options buttons are clicked.
1245 * Show/hide the optional options and swap the More/Less buttons, and save the
1246 * new setting as the default from now on.
1248 void EditAlarmDlg::slotDefault()
1250 showOptions(!mShowingMore);
1251 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP);
1252 config.writeEntry(EDIT_MORE_KEY, mShowingMore);
1255 /******************************************************************************
1256 * Show/hide the optional options and swap the More/Less buttons.
1258 void EditAlarmDlg::showOptions(bool more)
1260 qCDebug(KALARM_LOG) << (more ? "More" : "Less");
1261 if (more)
1263 mMoreOptions->show();
1264 mMoreLessButton->setText(i18nc("@action:Button", "Less Options <<"));
1266 else
1268 mMoreOptions->hide();
1269 mMoreLessButton->setText(i18nc("@action:button", "More Options >>"));
1271 if (mTimeWidget)
1272 mTimeWidget->showMoreOptions(more);
1273 type_showOptions(more);
1274 mRecurrenceEdit->showMoreOptions(more);
1275 mShowingMore = more;
1276 QTimer::singleShot(0, this, &EditAlarmDlg::slotResize);
1279 /******************************************************************************
1280 * Called when the Change deferral button is clicked.
1282 void EditAlarmDlg::slotEditDeferral()
1284 if (!mTimeWidget)
1285 return;
1286 bool limit = true;
1287 Repetition repetition = mRecurrenceEdit->subRepetition();
1288 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1289 : mTimeWidget->getDateTime(Q_NULLPTR, !repetition, !mExpiredRecurrence);
1290 if (!start.isValid())
1292 if (!mExpiredRecurrence)
1293 return;
1294 limit = false;
1296 KDateTime now = KDateTime::currentUtcDateTime();
1297 if (limit)
1299 if (repetition && start < now)
1301 // Sub-repetition - find the time of the next one
1302 int repeatNum = repetition.isDaily()
1303 ? (start.daysTo(now) + repetition.intervalDays() - 1) / repetition.intervalDays()
1304 : (start.secsTo(now) + repetition.intervalSeconds() - 1) / repetition.intervalSeconds();
1305 if (repeatNum > repetition.count())
1307 mTimeWidget->getDateTime(); // output the appropriate error message
1308 return;
1310 start = repetition.duration(repeatNum).end(start.kDateTime());
1314 bool deferred = mDeferDateTime.isValid();
1315 // Use AutoQPointer to guard against crash on application exit while
1316 // the dialogue is still open. It prevents double deletion (both on
1317 // deletion of EditAlarmDlg, and on return from this function).
1318 AutoQPointer<DeferAlarmDlg> deferDlg = new DeferAlarmDlg((deferred ? mDeferDateTime : DateTime(now.addSecs(60).toTimeSpec(start.timeSpec()))),
1319 start.isDateOnly(), deferred, this);
1320 deferDlg->setObjectName(QStringLiteral("EditDeferDlg")); // used by LikeBack
1321 if (limit)
1323 // Don't allow deferral past the next recurrence
1324 int reminder = mReminder ? mReminder->minutes() : 0;
1325 if (reminder)
1327 DateTime remindTime = start.addMins(-reminder);
1328 if (KDateTime::currentUtcDateTime() < remindTime)
1329 start = remindTime;
1331 deferDlg->setLimit(start.addSecs(-60));
1333 if (deferDlg->exec() == QDialog::Accepted)
1335 mDeferDateTime = deferDlg->getDateTime();
1336 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString());
1337 contentsChanged();
1341 /******************************************************************************
1342 * Called when the main page is shown.
1343 * Sets the focus widget to the first edit field.
1345 void EditAlarmDlg::slotShowMainPage()
1347 if (!mMainPageShown)
1349 if (mTemplateName)
1350 mTemplateName->setFocus();
1351 mMainPageShown = true;
1353 else
1355 // Set scroll position to top, since it otherwise jumps randomly
1356 StackedScrollWidget* main = static_cast<StackedScrollWidget*>(mTabs->widget(0));
1357 main->verticalScrollBar()->setValue(0);
1359 if (mTimeWidget)
1361 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1362 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1363 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1364 mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1365 else
1366 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1370 /******************************************************************************
1371 * Called when the recurrence edit page is shown.
1372 * The recurrence defaults are set to correspond to the start date.
1373 * The first time, for a new alarm, the recurrence end date is set according to
1374 * the alarm start time.
1376 void EditAlarmDlg::slotShowRecurrenceEdit()
1378 mRecurPageIndex = mTabs->currentIndex();
1379 if (!mReadOnly && !mTemplate)
1381 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1382 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1383 bool expired = (mAlarmDateTime.effectiveKDateTime() < now);
1384 if (mRecurSetDefaultEndDate)
1386 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1387 mRecurSetDefaultEndDate = false;
1389 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1390 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1391 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime.kDateTime());
1393 mRecurPageShown = true;
1396 /******************************************************************************
1397 * Called when the recurrence type selection changes.
1398 * Enables/disables date-only alarms as appropriate.
1399 * Enables/disables controls depending on at-login setting.
1401 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1403 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1404 if (!mTemplate)
1406 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1407 if (mDeferGroup)
1408 mDeferGroup->setEnabled(recurs);
1409 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1410 if (atLogin)
1412 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1413 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.kDateTime());
1415 if (mReminder)
1416 mReminder->enableOnceOnly(recurs && !atLogin);
1418 if (mReminder)
1419 mReminder->setAfterOnly(atLogin);
1420 mLateCancel->setEnabled(!atLogin);
1421 if (mShowInKorganizer)
1422 mShowInKorganizer->setEnabled(!atLogin);
1423 slotRecurFrequencyChange();
1426 /******************************************************************************
1427 * Called when the recurrence frequency selection changes, or the sub-
1428 * repetition interval changes.
1429 * Updates the recurrence frequency text.
1431 void EditAlarmDlg::slotRecurFrequencyChange()
1433 slotSetSubRepetition();
1434 KAEvent event;
1435 mRecurrenceEdit->updateEvent(event, false);
1436 mTabs->setTabText(mRecurPageIndex, recurText(event));
1439 /******************************************************************************
1440 * Called when the Repetition within Recurrence button has been pressed to
1441 * display the sub-repetition dialog.
1442 * Alarm repetition has the following restrictions:
1443 * 1) Not allowed for a repeat-at-login alarm
1444 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1445 * 3) The overall repeat duration must be less than the recurrence interval.
1447 void EditAlarmDlg::slotSetSubRepetition()
1449 bool dateOnly = mTemplate ? mTemplateAnyTime->isChecked() : mTimeWidget->anyTime();
1450 mRecurrenceEdit->setSubRepetition((mReminder ? mReminder->minutes() : 0), dateOnly);
1453 /******************************************************************************
1454 * Called when one of the template time radio buttons is clicked,
1455 * to enable or disable the template time entry spin boxes.
1457 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton*)
1459 mTemplateTime->setEnabled(mTemplateUseTime->isChecked());
1460 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isChecked());
1463 /******************************************************************************
1464 * Called when the "Any time" checkbox is toggled in the date/time widget.
1465 * Sets the advance reminder and late cancel units to days if any time is checked.
1467 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1469 if (mReminder && mReminder->isReminder())
1470 mReminder->setDateOnly(anyTime);
1471 mLateCancel->setDateOnly(anyTime);
1474 bool EditAlarmDlg::dateOnly() const
1476 return mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isChecked();
1479 bool EditAlarmDlg::isTimedRecurrence() const
1481 return mRecurrenceEdit->isTimedRepeatType();
1484 void EditAlarmDlg::showMainPage()
1486 mTabs->setCurrentIndex(mMainPageIndex);
1489 #include "moc_editdlg.cpp"
1490 #include "moc_editdlg_p.cpp"
1492 // vim: et sw=4: