SVN_SILENT made messages (after extraction)
[kdepim.git] / kalarm / editdlg.cpp
blob3879f685c9cf0488965546f3ae791c933a98a644
1 /*
2 * editdlg.cpp - dialog to create or modify an alarm or alarm template
3 * Program: kalarm
4 * Copyright © 2001-2016 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 that types can work with it, but don't insert
221 // it into layout just yet
222 mButtonBox = new QDialogButtonBox(this);
223 if (mReadOnly)
225 mButtonBox->addButton(QDialogButtonBox::Cancel);
226 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
227 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
229 else if (mTemplate)
231 mButtonBox->addButton(QDialogButtonBox::Ok);
232 mButtonBox->addButton(QDialogButtonBox::Cancel);
233 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
234 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
236 else
238 mButtonBox->addButton(QDialogButtonBox::Ok);
239 mButtonBox->addButton(QDialogButtonBox::Cancel);
240 mTryButton = mButtonBox->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole);
241 mLoadTemplateButton = mButtonBox->addButton(i18nc("@action:button", "Load Template..."),
242 QDialogButtonBox::HelpRole);
243 mMoreLessButton = mButtonBox->addButton(QDialogButtonBox::RestoreDefaults);
245 connect(mButtonBox, &QDialogButtonBox::clicked,
246 this, &EditAlarmDlg::slotButtonClicked);
248 if (mButtonBox->button(QDialogButtonBox::Ok))
249 mButtonBox->button(QDialogButtonBox::Ok)->setWhatsThis(i18nc("@info:whatsthis", "Schedule the alarm at the specified time."));
251 QVBoxLayout* mainLayout = new QVBoxLayout(this);
252 if (mTemplate)
254 QFrame *frame = new QFrame;
255 QHBoxLayout* box = new QHBoxLayout();
256 frame->setLayout(box);
257 box->setMargin(0);
258 box->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
259 QLabel* label = new QLabel(i18nc("@label:textbox", "Template name:"));
260 label->setFixedSize(label->sizeHint());
261 box->addWidget(label);
262 mTemplateName = new QLineEdit();
263 mTemplateName->setReadOnly(mReadOnly);
264 connect(mTemplateName, &QLineEdit::textEdited, this, &EditAlarmDlg::contentsChanged);
265 label->setBuddy(mTemplateName);
266 box->addWidget(mTemplateName);
267 frame->setWhatsThis(i18nc("@info:whatsthis", "Enter the name of the alarm template"));
268 frame->setFixedHeight(box->sizeHint().height());
269 mainLayout->addWidget(frame);
271 mTabs = new QTabWidget(this);
272 mainLayout->addWidget(mTabs);
273 mTabScrollGroup = new StackedScrollGroup(this, mTabs);
275 StackedScrollWidget* mainScroll = new StackedScrollWidget(mTabScrollGroup);
276 mTabs->addTab(mainScroll, i18nc("@title:tab", "Alarm"));
277 mMainPageIndex = 0;
278 PageFrame* mainPage = new PageFrame(mainScroll);
279 mainScroll->setWidget(mainPage); // mainPage becomes the child of mainScroll
280 connect(mainPage, &PageFrame::shown, this, &EditAlarmDlg::slotShowMainPage);
281 QVBoxLayout* topLayout = new QVBoxLayout(mainPage);
282 topLayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
283 topLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
285 // Recurrence tab
286 StackedScrollWidget* recurScroll = new StackedScrollWidget(mTabScrollGroup);
287 mTabs->addTab(recurScroll, QString());
288 mRecurPageIndex = 1;
289 QFrame *recurTab = new QFrame;
290 QVBoxLayout* recurTabLayout = new QVBoxLayout();
291 recurTabLayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
292 recurTab->setLayout(recurTabLayout);
293 recurScroll->setWidget(recurTab); // recurTab becomes the child of recurScroll
294 mRecurrenceEdit = new RecurrenceEdit(mReadOnly);
295 recurTabLayout->addWidget(mRecurrenceEdit);
296 connect(mRecurrenceEdit, &RecurrenceEdit::shown, this, &EditAlarmDlg::slotShowRecurrenceEdit);
297 connect(mRecurrenceEdit, &RecurrenceEdit::typeChanged, this, &EditAlarmDlg::slotRecurTypeChange);
298 connect(mRecurrenceEdit, &RecurrenceEdit::frequencyChanged, this, &EditAlarmDlg::slotRecurFrequencyChange);
299 connect(mRecurrenceEdit, &RecurrenceEdit::repeatNeedsInitialisation, this, &EditAlarmDlg::slotSetSubRepetition);
300 connect(mRecurrenceEdit, &RecurrenceEdit::contentsChanged, this, &EditAlarmDlg::contentsChanged);
302 // Controls specific to the alarm type
303 QGroupBox* actionBox = new QGroupBox(i18nc("@title:group", "Action"), mainPage);
304 topLayout->addWidget(actionBox, 1);
305 QVBoxLayout* layout = new QVBoxLayout(actionBox);
306 layout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
307 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
309 type_init(actionBox, layout);
311 if (!mTemplate)
313 // Deferred date/time: visible only for a deferred recurring event.
314 mDeferGroup = new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage);
315 topLayout->addWidget(mDeferGroup);
316 QHBoxLayout* hlayout = new QHBoxLayout(mDeferGroup);
317 hlayout->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
318 hlayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
319 QLabel* label = new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup);
320 label->setFixedSize(label->sizeHint());
321 hlayout->addWidget(label);
322 mDeferTimeLabel = new QLabel(mDeferGroup);
323 hlayout->addWidget(mDeferTimeLabel);
325 mDeferChangeButton = new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup);
326 mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint());
327 connect(mDeferChangeButton, &QPushButton::clicked, this, &EditAlarmDlg::slotEditDeferral);
328 mDeferChangeButton->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral"));
329 hlayout->addWidget(mDeferChangeButton);
330 //?? mDeferGroup->addSpace(0);
333 QHBoxLayout* hlayout = new QHBoxLayout();
334 hlayout->setMargin(0);
335 topLayout->addLayout(hlayout);
337 // Date and time entry
338 if (mTemplate)
340 QGroupBox* templateTimeBox = new QGroupBox(i18nc("@title:group", "Time"), mainPage);
341 topLayout->addWidget(templateTimeBox);
342 QGridLayout* grid = new QGridLayout(templateTimeBox);
343 grid->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin));
344 grid->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
345 mTemplateTimeGroup = new ButtonGroup(templateTimeBox);
346 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::slotTemplateTimeType);
347 connect(mTemplateTimeGroup, &ButtonGroup::buttonSet, this, &EditAlarmDlg::contentsChanged);
349 mTemplateDefaultTime = new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox);
350 mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint());
351 mTemplateDefaultTime->setReadOnly(mReadOnly);
352 mTemplateDefaultTime->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. "
353 "The normal default start time will be used."));
354 mTemplateTimeGroup->addButton(mTemplateDefaultTime);
355 grid->addWidget(mTemplateDefaultTime, 0, 0, Qt::AlignLeft);
357 QWidget *box = new QWidget(templateTimeBox);
358 QHBoxLayout* layout = new QHBoxLayout(box);
359 layout->setMargin(0);
360 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
361 mTemplateUseTime = new RadioButton(i18nc("@option:radio", "Time:"), box);
362 mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint());
363 mTemplateUseTime->setReadOnly(mReadOnly);
364 mTemplateUseTime->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template."));
365 layout->addWidget(mTemplateUseTime);
366 mTemplateTimeGroup->addButton(mTemplateUseTime);
367 mTemplateTime = new TimeEdit();
368 mTemplateTime->setFixedSize(mTemplateTime->sizeHint());
369 mTemplateTime->setReadOnly(mReadOnly);
370 mTemplateTime->setWhatsThis(xi18nc("@info:whatsthis",
371 "<para>Enter the start time for alarms based on this template.</para><para>%1</para>",
372 TimeSpinBox::shiftWhatsThis()));
373 connect(mTemplateTime, &TimeEdit::valueChanged, this, &EditAlarmDlg::contentsChanged);
374 layout->addWidget(mTemplateTime);
375 layout->addStretch(1);
376 grid->addWidget(box, 0, 1, Qt::AlignLeft);
378 mTemplateAnyTime = new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox);
379 mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint());
380 mTemplateAnyTime->setReadOnly(mReadOnly);
381 mTemplateAnyTime->setWhatsThis(xi18nc("@info:whatsthis", "Set the <interface>Any time</interface> option for alarms based on this template."));
382 mTemplateTimeGroup->addButton(mTemplateAnyTime);
383 grid->addWidget(mTemplateAnyTime, 1, 0, Qt::AlignLeft);
385 box = new QWidget(templateTimeBox);
386 layout = new QHBoxLayout(box);
387 layout->setMargin(0);
388 layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
389 mTemplateUseTimeAfter = new RadioButton(i18nc("@option:radio", "Time from now:"), box);
390 mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint());
391 mTemplateUseTimeAfter->setReadOnly(mReadOnly);
392 mTemplateUseTimeAfter->setWhatsThis(i18nc("@info:whatsthis",
393 "Set alarms based on this template to start after the specified time "
394 "interval from when the alarm is created."));
395 layout->addWidget(mTemplateUseTimeAfter);
396 mTemplateTimeGroup->addButton(mTemplateUseTimeAfter);
397 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime);
398 mTemplateTimeAfter->setValue(1439);
399 mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint());
400 mTemplateTimeAfter->setReadOnly(mReadOnly);
401 connect(mTemplateTimeAfter, static_cast<void (TimeSpinBox::*)(int)>(&TimeSpinBox::valueChanged), this, &EditAlarmDlg::contentsChanged);
402 mTemplateTimeAfter->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para>",
403 AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis()));
404 layout->addWidget(mTemplateTimeAfter);
405 box->setFixedHeight(box->sizeHint().height());
406 grid->addWidget(box, 1, 1, Qt::AlignLeft);
408 hlayout->addStretch();
410 else
412 mTimeWidget = new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME, mainPage);
413 connect(mTimeWidget, &AlarmTimeWidget::dateOnlyToggled, this, &EditAlarmDlg::slotAnyTimeToggled);
414 connect(mTimeWidget, &AlarmTimeWidget::changed, this, &EditAlarmDlg::contentsChanged);
415 topLayout->addWidget(mTimeWidget);
418 // Optional controls depending on More/Less Options button
419 mMoreOptions = new QFrame(mainPage);
420 mMoreOptions->setFrameStyle(QFrame::NoFrame);
421 topLayout->addWidget(mMoreOptions);
422 QVBoxLayout* moreLayout = new QVBoxLayout(mMoreOptions);
423 moreLayout->setMargin(0);
424 moreLayout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
426 // Reminder
427 mReminder = createReminder(mMoreOptions);
428 if (mReminder)
430 mReminder->setFixedSize(mReminder->sizeHint());
431 connect(mReminder, &Reminder::changed, this, &EditAlarmDlg::contentsChanged);
432 moreLayout->addWidget(mReminder, 0, Qt::AlignLeft);
433 if (mTimeWidget)
434 connect(mTimeWidget, &AlarmTimeWidget::changed, mReminder, &Reminder::setDefaultUnits);
437 // Late cancel selector - default = allow late display
438 mLateCancel = new LateCancelSelector(true, mMoreOptions);
439 connect(mLateCancel, &LateCancelSelector::changed, this, &EditAlarmDlg::contentsChanged);
440 moreLayout->addWidget(mLateCancel, 0, Qt::AlignLeft);
442 PackedLayout* playout = new PackedLayout(Qt::AlignJustify);
443 playout->setSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing));
444 moreLayout->addLayout(playout);
446 // Acknowledgement confirmation required - default = no confirmation
447 CheckBox* confirmAck = type_createConfirmAckCheckbox(mMoreOptions);
448 if (confirmAck)
450 confirmAck->setFixedSize(confirmAck->sizeHint());
451 connect(confirmAck, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged);
452 playout->addWidget(confirmAck);
455 if (theApp()->korganizerEnabled())
457 // Show in KOrganizer checkbox
458 mShowInKorganizer = new CheckBox(i18n_chk_ShowInKOrganizer(), mMoreOptions);
459 mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint());
460 connect(mShowInKorganizer, &CheckBox::toggled, this, &EditAlarmDlg::contentsChanged);
461 mShowInKorganizer->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar"));
462 playout->addWidget(mShowInKorganizer);
465 mainLayout->addWidget(mButtonBox);
467 // Hide optional controls
468 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP);
469 showOptions(config.readEntry(EDIT_MORE_KEY, false));
471 // Initialise the state of all controls according to the specified event, if any
472 initValues(event);
473 if (mTemplateName)
474 mTemplateName->setFocus();
476 if (!mNewAlarm)
478 // Save the initial state of all controls so that we can later tell if they have changed
479 saveState((event && (mTemplate || !event->isTemplate())) ? event : Q_NULLPTR);
480 contentsChanged(); // enable/disable OK button
483 // Note the current desktop so that the dialog can be shown on it.
484 // If a main window is visible, the dialog will by KDE default always appear on its
485 // desktop. If the user invokes the dialog via the system tray on a different desktop,
486 // that can cause confusion.
487 mDesktop = KWindowSystem::currentDesktop();
489 if (theApp()->windowFocusBroken())
491 const QList<QWidget*> children = findChildren<QWidget*>();
492 foreach (QWidget* w, children)
493 w->installEventFilter(this);
497 EditAlarmDlg::~EditAlarmDlg()
499 delete mButtonBox;
500 mButtonBox = Q_NULLPTR; // prevent text edit contentsChanged() signal triggering a crash
501 delete mSavedEvent;
502 mWindowList.removeAll(this);
505 /******************************************************************************
506 * Return the number of instances.
508 int EditAlarmDlg::instanceCount()
510 return mWindowList.count();
513 /******************************************************************************
514 * Initialise the dialog controls from the specified event.
516 void EditAlarmDlg::initValues(const KAEvent* event)
518 setReadOnly(mDesiredReadOnly);
520 mChanged = false;
521 mOnlyDeferred = false;
522 mExpiredRecurrence = false;
523 mLateCancel->showAutoClose(false);
524 bool deferGroupVisible = false;
525 if (event)
527 // Set the values to those for the specified event
528 if (mTemplate)
529 mTemplateName->setText(event->templateName());
530 bool recurs = event->recurs();
531 if ((recurs || event->repetition()) && !mTemplate && event->deferred())
533 deferGroupVisible = true;
534 mDeferDateTime = event->deferDateTime();
535 mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
536 mDeferGroup->show();
538 if (mTemplate)
540 // Editing a template
541 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
542 bool noTime = !afterTime;
543 bool useTime = !event->mainDateTime().isDateOnly();
544 RadioButton* button = noTime ? mTemplateDefaultTime :
545 (afterTime > 0) ? mTemplateUseTimeAfter :
546 useTime ? mTemplateUseTime : mTemplateAnyTime;
547 button->setChecked(true);
548 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
549 if (!noTime && useTime)
550 mTemplateTime->setValue(event->mainDateTime().kDateTime().time());
551 else
552 mTemplateTime->setValue(0);
554 else
556 if (event->isTemplate())
558 // Initialising from an alarm template: use current date
559 KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
560 int afterTime = event->templateAfterTime();
561 if (afterTime >= 0)
563 mTimeWidget->setDateTime(now.addSecs(afterTime * 60));
564 mTimeWidget->selectTimeFromNow();
566 else
568 KDateTime dt = event->startDateTime().kDateTime();
569 dt.setTimeSpec(Preferences::timeZone());
570 QDate d = now.date();
571 if (!dt.isDateOnly() && now.time() >= dt.time())
572 d = d.addDays(1); // alarm time has already passed, so use tomorrow
573 dt.setDate(d);
574 mTimeWidget->setDateTime(dt);
577 else
579 mExpiredRecurrence = recurs && event->mainExpired();
580 mTimeWidget->setDateTime(recurs || event->category() == CalEvent::ARCHIVED ? event->startDateTime()
581 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
585 KAEvent::SubAction action = event->actionSubType();
586 AlarmText altext;
587 if (event->commandScript())
588 altext.setScript(event->cleanText());
589 else
590 altext.setText(event->cleanText());
591 setAction(action, altext);
593 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
594 TimePeriod::HoursMinutes);
595 if (mShowInKorganizer)
596 mShowInKorganizer->setChecked(event->copyToKOrganizer());
597 type_initValues(event);
598 mRecurrenceEdit->set(*event); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
599 mTabs->setTabText(mRecurPageIndex, recurText(*event));
601 else
603 // Set the values to their defaults
604 KDateTime defaultTime = KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
605 if (mTemplate)
607 mTemplateDefaultTime->setChecked(true);
608 mTemplateTime->setValue(0);
609 mTemplateTimeAfter->setValue(1);
611 else
612 mTimeWidget->setDateTime(defaultTime);
613 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes);
614 if (mShowInKorganizer)
615 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
616 type_initValues(Q_NULLPTR);
617 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
618 slotRecurFrequencyChange(); // update the Recurrence text
620 if (mReminder && mTimeWidget)
621 mReminder->setDefaultUnits(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
623 if (!deferGroupVisible && mDeferGroup)
624 mDeferGroup->hide();
626 bool empty = AlarmCalendar::resources()->events(CalEvent::TEMPLATE).isEmpty();
627 if (mLoadTemplateButton)
628 mLoadTemplateButton->setEnabled(!empty);
631 /******************************************************************************
632 * Initialise various values in the New Alarm dialogue.
634 void EditAlarmDlg::setTime(const DateTime& start)
636 mTimeWidget->setDateTime(start);
638 void EditAlarmDlg::setRecurrence(const KARecurrence& recur, const KCalCore::Duration& subRepeatInterval, int subRepeatCount)
640 KAEvent event;
641 event.setTime(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
642 event.setRecurrence(recur);
643 event.setRepetition(Repetition(subRepeatInterval, subRepeatCount - 1));
644 mRecurrenceEdit->set(event);
646 void EditAlarmDlg::setRepeatAtLogin()
648 mRecurrenceEdit->setRepeatAtLogin();
650 void EditAlarmDlg::setLateCancel(int minutes)
652 mLateCancel->setMinutes(minutes, mTimeWidget->getDateTime(Q_NULLPTR, false, false).isDateOnly(),
653 TimePeriod::HoursMinutes);
655 void EditAlarmDlg::setShowInKOrganizer(bool show)
657 mShowInKorganizer->setChecked(show);
660 /******************************************************************************
661 * Set the read-only status of all non-template controls.
663 void EditAlarmDlg::setReadOnly(bool readOnly)
665 mReadOnly = readOnly;
667 if (mTimeWidget)
668 mTimeWidget->setReadOnly(readOnly);
669 mLateCancel->setReadOnly(readOnly);
670 if (mDeferChangeButton)
672 if (readOnly)
673 mDeferChangeButton->hide();
674 else
675 mDeferChangeButton->show();
677 if (mShowInKorganizer)
678 mShowInKorganizer->setReadOnly(readOnly);
681 /******************************************************************************
682 * Save the state of all controls.
684 void EditAlarmDlg::saveState(const KAEvent* event)
686 delete mSavedEvent;
687 mSavedEvent = Q_NULLPTR;
688 if (event)
689 mSavedEvent = new KAEvent(*event);
690 if (mTemplate)
692 mSavedTemplateName = mTemplateName->text();
693 mSavedTemplateTimeType = mTemplateTimeGroup->checkedButton();
694 mSavedTemplateTime = mTemplateTime->time();
695 mSavedTemplateAfterTime = mTemplateTimeAfter->value();
697 checkText(mSavedTextFileCommandMessage, false);
698 if (mTimeWidget)
699 mSavedDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
700 mSavedLateCancel = mLateCancel->minutes();
701 if (mShowInKorganizer)
702 mSavedShowInKorganizer = mShowInKorganizer->isChecked();
703 mSavedRecurrenceType = mRecurrenceEdit->repeatType();
704 mSavedDeferTime = mDeferDateTime.kDateTime();
707 /******************************************************************************
708 * Check whether any of the controls has changed state since the dialog was
709 * first displayed.
710 * Reply = true if any non-deferral controls have changed, or if it's a new event.
711 * = false if no non-deferral controls have changed. In this case,
712 * mOnlyDeferred indicates whether deferral controls may have changed.
714 bool EditAlarmDlg::stateChanged() const
716 mChanged = true;
717 mOnlyDeferred = false;
718 if (!mSavedEvent)
719 return true;
720 QString textFileCommandMessage;
721 checkText(textFileCommandMessage, false);
722 if (mTemplate)
724 if (mSavedTemplateName != mTemplateName->text()
725 || mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton()
726 || (mTemplateUseTime->isChecked() && mSavedTemplateTime != mTemplateTime->time())
727 || (mTemplateUseTimeAfter->isChecked() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
728 return true;
730 else
732 KDateTime dt = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
733 if (mSavedDateTime.timeSpec() != dt.timeSpec() || mSavedDateTime != dt)
734 return true;
736 if (mSavedLateCancel != mLateCancel->minutes()
737 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
738 || textFileCommandMessage != mSavedTextFileCommandMessage
739 || mSavedRecurrenceType != mRecurrenceEdit->repeatType())
740 return true;
741 if (type_stateChanged())
742 return true;
743 if (mRecurrenceEdit->stateChanged())
744 return true;
745 if (mSavedEvent && mSavedEvent->deferred())
746 mOnlyDeferred = true;
747 mChanged = false;
748 return false;
751 /******************************************************************************
752 * Called whenever any of the controls changes state.
753 * Enable or disable the OK button depending on whether any controls have a
754 * different state from their initial state.
756 void EditAlarmDlg::contentsChanged()
758 // Don't do anything if it's a new alarm or we're still initialising
759 // (i.e. mSavedEvent null).
760 if (mSavedEvent && mButtonBox && mButtonBox->button(QDialogButtonBox::Ok))
761 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(stateChanged() || mDeferDateTime.kDateTime() != mSavedDeferTime);
764 /******************************************************************************
765 * Get the currently entered dialog data.
766 * The data is returned in the supplied KAEvent instance.
767 * Reply = false if the only change has been to an existing deferral.
769 bool EditAlarmDlg::getEvent(KAEvent& event, Akonadi::Collection& collection)
771 collection = mCollection;
772 if (mChanged)
774 // It's a new event, or the edit controls have changed
775 setEvent(event, mAlarmMessage, false);
776 return true;
779 // Only the deferral time may have changed
780 event = *mSavedEvent;
781 if (mOnlyDeferred)
783 // Just modify the original event, to avoid expired recurring events
784 // being returned as rubbish.
785 if (mDeferDateTime.isValid())
786 event.defer(mDeferDateTime, event.reminderDeferral(), false);
787 else
788 event.cancelDefer();
790 return false;
793 /******************************************************************************
794 * Extract the data in the dialog and set up a KAEvent from it.
795 * If 'trial' is true, the event is set up for a simple one-off test, ignoring
796 * recurrence, reminder, template etc. data.
798 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial)
800 KDateTime dt;
801 if (!trial)
803 if (!mTemplate)
804 dt = mAlarmDateTime.effectiveKDateTime();
805 else if (mTemplateUseTime->isChecked())
806 dt = KDateTime(QDate(2000,1,1), mTemplateTime->time());
809 int lateCancel = (trial || !mLateCancel->isEnabled()) ? 0 : mLateCancel->minutes();
810 type_setEvent(event, dt, text, lateCancel, trial);
812 if (!trial)
814 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
816 mRecurrenceEdit->updateEvent(event, !mTemplate);
817 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
818 bool dateOnly = mAlarmDateTime.isDateOnly();
819 if ((dateOnly && mAlarmDateTime.date() < now.date())
820 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
822 // A timed recurrence has an entered start date which has
823 // already expired, so we must adjust the next repetition.
824 event.setNextOccurrence(now);
826 mAlarmDateTime = event.startDateTime();
827 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime)
829 bool deferral = true;
830 bool deferReminder = false;
831 int reminder = mReminder ? mReminder->minutes() : 0;
832 if (reminder)
834 DateTime remindTime = mAlarmDateTime.addMins(-reminder);
835 if (mDeferDateTime >= remindTime)
837 if (remindTime > KDateTime::currentUtcDateTime())
838 deferral = false; // ignore deferral if it's after next reminder
839 else if (mDeferDateTime > remindTime)
840 deferReminder = true; // it's the reminder which is being deferred
843 if (deferral)
844 event.defer(mDeferDateTime, deferReminder, false);
847 if (mTemplate)
849 int afterTime = mTemplateDefaultTime->isChecked() ? 0
850 : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1;
851 event.setTemplate(mTemplateName->text(), afterTime);
856 /******************************************************************************
857 * Get the currently specified alarm flag bits.
859 KAEvent::Flags EditAlarmDlg::getAlarmFlags() const
861 KAEvent::Flags flags(0);
862 if (mShowInKorganizer && mShowInKorganizer->isEnabled() && mShowInKorganizer->isChecked())
863 flags |= KAEvent::COPY_KORGANIZER;
864 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
865 flags |= KAEvent::REPEAT_AT_LOGIN;
866 if (mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly())
867 flags |= KAEvent::ANY_TIME;
868 return flags;
871 /******************************************************************************
872 * Called when the dialog is displayed.
873 * The first time through, sets the size to the same as the last time it was
874 * displayed.
876 void EditAlarmDlg::showEvent(QShowEvent* se)
878 QDialog::showEvent(se);
879 if (!mDeferGroupHeight)
881 if (mDeferGroup)
882 mDeferGroupHeight = mDeferGroup->height() + style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
883 QSize s;
884 if (KAlarm::readConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s))
886 bool defer = mDeferGroup && !mDeferGroup->isHidden();
887 s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0));
888 if (!defer)
889 mTabScrollGroup->setSized();
890 resize(s);
893 slotResize();
894 KWindowSystem::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user
896 if (theApp()->needWindowFocusFix())
898 QApplication::setActiveWindow(this);
899 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
903 /******************************************************************************
904 * Called when the window is first shown, to ensure that it initially becomes
905 * the active window.
906 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
907 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
908 * windows.
910 void EditAlarmDlg::focusFixTimer()
912 if (theApp()->needWindowFocusFix()
913 && QApplication::focusWidget()->window() != this)
915 QApplication::setActiveWindow(this);
916 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
920 /******************************************************************************
921 * Called to detect when the mouse is pressed anywhere inside the window.
922 * Activates this window if a MessageWin window is also active.
923 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
924 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
925 * windows.
927 bool EditAlarmDlg::eventFilter(QObject*, QEvent* e)
929 if (theApp()->needWindowFocusFix())
931 if (e->type() == QEvent::MouseButtonPress)
932 QApplication::setActiveWindow(this);
934 return false;
937 /******************************************************************************
938 * Called when the dialog is closed.
940 void EditAlarmDlg::closeEvent(QCloseEvent* ce)
942 Q_EMIT rejected();
943 QDialog::closeEvent(ce);
946 /******************************************************************************
947 * Update the tab sizes (again) and if the resized dialog height is greater
948 * than the minimum, resize it again. This is necessary because (a) resizing
949 * tabs doesn't always work properly the first time, and (b) resizing to the
950 * minimum size hint doesn't always work either.
952 void EditAlarmDlg::slotResize()
954 QSize s = mTabScrollGroup->adjustSize(true);
955 s = minimumSizeHint();
956 if (height() > s.height())
958 // Resize to slightly greater than the minimum height.
959 // This is for some unknown reason necessary, since
960 // sometimes resizing to the minimum height fails.
961 resize(s.width(), s.height() + 2);
965 /******************************************************************************
966 * Called when the dialog's size has changed.
967 * Records the new size (adjusted to ignore the optional height of the deferred
968 * time edit widget) in the config file.
970 void EditAlarmDlg::resizeEvent(QResizeEvent* re)
972 if (isVisible() && mDeferGroupHeight)
974 QSize s = re->size();
975 s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
976 KAlarm::writeConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s);
978 QDialog::resizeEvent(re);
981 /******************************************************************************
982 * Called when any button is clicked.
984 void EditAlarmDlg::slotButtonClicked(QAbstractButton *button)
986 if (button == mTryButton)
987 slotTry();
988 else if (button == mLoadTemplateButton)
989 slotHelp();
990 else if (button == mMoreLessButton)
991 slotDefault();
992 else if (button == mButtonBox->button(QDialogButtonBox::Ok))
994 if (validate())
995 accept();
997 else
998 reject();
1001 /******************************************************************************
1002 * Called when the OK button is clicked.
1003 * Validate the input data.
1005 bool EditAlarmDlg::validate()
1007 if (!stateChanged())
1009 // No changes have been made except possibly to an existing deferral
1010 if (!mOnlyDeferred)
1011 reject();
1012 return mOnlyDeferred;
1014 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
1015 if (mTimeWidget
1016 && mTabs->currentIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN)
1017 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1018 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login?
1019 if (mTemplate)
1021 // Check that the template name is not blank and is unique
1022 QString errmsg;
1023 QString name = mTemplateName->text();
1024 if (name.isEmpty())
1025 errmsg = i18nc("@info", "You must enter a name for the alarm template");
1026 else if (name != mSavedTemplateName)
1028 if (AlarmCalendar::resources()->templateEvent(name))
1029 errmsg = i18nc("@info", "Template name is already in use");
1031 if (!errmsg.isEmpty())
1033 mTemplateName->setFocus();
1034 KAMessageBox::sorry(this, errmsg);
1035 return false;
1038 else if (mTimeWidget)
1040 QWidget* errWidget;
1041 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, !timedRecurrence, false, &errWidget);
1042 if (errWidget)
1044 // It's more than just an existing deferral being changed, so the time matters
1045 mTabs->setCurrentIndex(mMainPageIndex);
1046 errWidget->setFocus();
1047 mTimeWidget->getDateTime(); // display the error message now
1048 return false;
1051 if (!type_validate(false))
1052 return false;
1054 if (!mTemplate)
1056 if (mChanged && mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
1058 // Check whether the start date/time must be adjusted
1059 // to match the recurrence specification.
1060 DateTime dt = mAlarmDateTime; // setEvent() changes mAlarmDateTime
1061 KAEvent event;
1062 setEvent(event, mAlarmMessage, false);
1063 mAlarmDateTime = dt; // restore
1064 KDateTime pre = dt.effectiveKDateTime();
1065 bool dateOnly = dt.isDateOnly();
1066 if (dateOnly)
1067 pre = pre.addDays(-1);
1068 else
1069 pre = pre.addSecs(-1);
1070 DateTime next;
1071 event.nextOccurrence(pre, next, KAEvent::IGNORE_REPETITION);
1072 if (next != dt)
1074 QString prompt = dateOnly ? i18nc("@info The parameter is a date value",
1075 "The start date does not match the alarm's recurrence pattern, "
1076 "so it will be adjusted to the date of the next recurrence (%1).",
1077 KLocale::global()->formatDate(next.date(), KLocale::ShortDate))
1078 : i18nc("@info The parameter is a date/time value",
1079 "The start date/time does not match the alarm's recurrence pattern, "
1080 "so it will be adjusted to the date/time of the next recurrence (%1).",
1081 KLocale::global()->formatDateTime(next.kDateTime(), KLocale::ShortDate));
1082 if (KAMessageBox::warningContinueCancel(this, prompt) != KMessageBox::Continue)
1083 return false;
1087 if (timedRecurrence)
1089 KAEvent event;
1090 Akonadi::Collection c;
1091 getEvent(event, c); // this may adjust mAlarmDateTime
1092 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1093 bool dateOnly = mAlarmDateTime.isDateOnly();
1094 if ((dateOnly && mAlarmDateTime.date() < now.date())
1095 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
1097 // A timed recurrence has an entered start date which
1098 // has already expired, so we must adjust it.
1099 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1101 KAMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
1102 return false;
1104 if (event.workTimeOnly() && !event.nextTrigger(KAEvent::DISPLAY_TRIGGER).isValid())
1106 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
1107 != KMessageBox::Continue)
1108 return false;
1112 QString errmsg;
1113 QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg);
1114 if (errWidget)
1116 mTabs->setCurrentIndex(mRecurPageIndex);
1117 errWidget->setFocus();
1118 KAMessageBox::sorry(this, errmsg);
1119 return false;
1122 if (recurType != RecurrenceEdit::NO_RECUR)
1124 KAEvent recurEvent;
1125 int longestRecurMinutes = -1;
1126 int reminder = mReminder ? mReminder->minutes() : 0;
1127 if (reminder && !mReminder->isOnceOnly())
1129 mRecurrenceEdit->updateEvent(recurEvent, false);
1130 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1131 if (longestRecurMinutes && reminder >= longestRecurMinutes)
1133 mTabs->setCurrentIndex(mMainPageIndex);
1134 mReminder->setFocusOnCount();
1135 KAMessageBox::sorry(this, xi18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.",
1136 Reminder::i18n_chk_FirstRecurrenceOnly()));
1137 return false;
1140 if (mRecurrenceEdit->subRepetition())
1142 if (longestRecurMinutes < 0)
1144 mRecurrenceEdit->updateEvent(recurEvent, false);
1145 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1147 if (longestRecurMinutes > 0
1148 && recurEvent.repetition().intervalMinutes() * recurEvent.repetition().count() >= longestRecurMinutes - reminder)
1150 KAMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1151 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1152 return false;
1154 if (!recurEvent.repetition().isDaily()
1155 && ((mTemplate && mTemplateAnyTime->isChecked()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1157 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"));
1158 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1159 return false;
1163 if (!checkText(mAlarmMessage))
1164 return false;
1166 mCollection = Akonadi::Collection();
1167 // An item ID = -2 indicates that the caller already
1168 // knows which collection to use.
1169 if (mCollectionItemId >= -1)
1171 if (mCollectionItemId >= 0)
1173 mCollection = AlarmCalendar::resources()->collectionForEvent(mCollectionItemId);
1174 if (mCollection.isValid())
1176 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1177 if (!(AkonadiModel::instance()->types(mCollection) & type))
1178 mCollection = Akonadi::Collection(); // event may have expired while dialog was open
1181 bool cancelled = false;
1182 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1183 if (CollectionControlModel::isWritableEnabled(mCollection, type) <= 0)
1184 mCollection = CollectionControlModel::destination(type, this, false, &cancelled);
1185 if (!mCollection.isValid())
1187 if (!cancelled)
1188 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1189 return false;
1192 return true;
1195 /******************************************************************************
1196 * Called when the Try button is clicked.
1197 * Display/execute the alarm immediately for the user to check its configuration.
1199 void EditAlarmDlg::slotTry()
1201 QString text;
1202 if (checkText(text))
1204 if (!type_validate(true))
1205 return;
1206 KAEvent event;
1207 setEvent(event, text, true);
1208 if (!mNewAlarm && !stateChanged())
1210 // It's an existing alarm which hasn't been changed yet:
1211 // enable KALARM_UID environment variable to be set.
1212 event.setEventId(mEventId);
1214 type_aboutToTry();
1215 void* result = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1216 type_executedTry(text, result);
1220 /******************************************************************************
1221 * Called when the Load Template button is clicked.
1222 * Prompt to select a template and initialise the dialog with its contents.
1224 void EditAlarmDlg::slotHelp()
1226 KAEvent::Actions type;
1227 switch (mAlarmType)
1229 case KAEvent::FILE:
1230 case KAEvent::MESSAGE: type = KAEvent::ACT_DISPLAY; break;
1231 case KAEvent::COMMAND: type = KAEvent::ACT_COMMAND; break;
1232 case KAEvent::EMAIL: type = KAEvent::ACT_EMAIL; break;
1233 case KAEvent::AUDIO: type = KAEvent::ACT_AUDIO; break;
1234 default:
1235 return;
1237 // Use AutoQPointer to guard against crash on application exit while
1238 // the dialogue is still open. It prevents double deletion (both on
1239 // deletion of EditAlarmDlg, and on return from this function).
1240 AutoQPointer<TemplatePickDlg> dlg = new TemplatePickDlg(type, this);
1241 if (dlg->exec() == QDialog::Accepted)
1243 KAEvent event = dlg->selectedTemplate();
1244 initValues(&event);
1248 /******************************************************************************
1249 * Called when the More Options or Less Options buttons are clicked.
1250 * Show/hide the optional options and swap the More/Less buttons, and save the
1251 * new setting as the default from now on.
1253 void EditAlarmDlg::slotDefault()
1255 showOptions(!mShowingMore);
1256 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP);
1257 config.writeEntry(EDIT_MORE_KEY, mShowingMore);
1260 /******************************************************************************
1261 * Show/hide the optional options and swap the More/Less buttons.
1263 void EditAlarmDlg::showOptions(bool more)
1265 qCDebug(KALARM_LOG) << (more ? "More" : "Less");
1266 if (more)
1268 mMoreOptions->show();
1269 mMoreLessButton->setText(i18nc("@action:Button", "Less Options <<"));
1271 else
1273 mMoreOptions->hide();
1274 mMoreLessButton->setText(i18nc("@action:button", "More Options >>"));
1276 if (mTimeWidget)
1277 mTimeWidget->showMoreOptions(more);
1278 type_showOptions(more);
1279 mRecurrenceEdit->showMoreOptions(more);
1280 mShowingMore = more;
1281 QTimer::singleShot(0, this, &EditAlarmDlg::slotResize);
1284 /******************************************************************************
1285 * Called when the Change deferral button is clicked.
1287 void EditAlarmDlg::slotEditDeferral()
1289 if (!mTimeWidget)
1290 return;
1291 bool limit = true;
1292 Repetition repetition = mRecurrenceEdit->subRepetition();
1293 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1294 : mTimeWidget->getDateTime(Q_NULLPTR, !repetition, !mExpiredRecurrence);
1295 if (!start.isValid())
1297 if (!mExpiredRecurrence)
1298 return;
1299 limit = false;
1301 KDateTime now = KDateTime::currentUtcDateTime();
1302 if (limit)
1304 if (repetition && start < now)
1306 // Sub-repetition - find the time of the next one
1307 int repeatNum = repetition.isDaily()
1308 ? (start.daysTo(now) + repetition.intervalDays() - 1) / repetition.intervalDays()
1309 : (start.secsTo(now) + repetition.intervalSeconds() - 1) / repetition.intervalSeconds();
1310 if (repeatNum > repetition.count())
1312 mTimeWidget->getDateTime(); // output the appropriate error message
1313 return;
1315 start = repetition.duration(repeatNum).end(start.kDateTime());
1319 bool deferred = mDeferDateTime.isValid();
1320 // Use AutoQPointer to guard against crash on application exit while
1321 // the dialogue is still open. It prevents double deletion (both on
1322 // deletion of EditAlarmDlg, and on return from this function).
1323 AutoQPointer<DeferAlarmDlg> deferDlg = new DeferAlarmDlg((deferred ? mDeferDateTime : DateTime(now.addSecs(60).toTimeSpec(start.timeSpec()))),
1324 start.isDateOnly(), deferred, this);
1325 deferDlg->setObjectName(QStringLiteral("EditDeferDlg")); // used by LikeBack
1326 if (limit)
1328 // Don't allow deferral past the next recurrence
1329 int reminder = mReminder ? mReminder->minutes() : 0;
1330 if (reminder)
1332 DateTime remindTime = start.addMins(-reminder);
1333 if (KDateTime::currentUtcDateTime() < remindTime)
1334 start = remindTime;
1336 deferDlg->setLimit(start.addSecs(-60));
1338 if (deferDlg->exec() == QDialog::Accepted)
1340 mDeferDateTime = deferDlg->getDateTime();
1341 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString());
1342 contentsChanged();
1346 /******************************************************************************
1347 * Called when the main page is shown.
1348 * Sets the focus widget to the first edit field.
1350 void EditAlarmDlg::slotShowMainPage()
1352 if (!mMainPageShown)
1354 if (mTemplateName)
1355 mTemplateName->setFocus();
1356 mMainPageShown = true;
1358 else
1360 // Set scroll position to top, since it otherwise jumps randomly
1361 StackedScrollWidget* main = static_cast<StackedScrollWidget*>(mTabs->widget(0));
1362 main->verticalScrollBar()->setValue(0);
1364 if (mTimeWidget)
1366 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1367 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1368 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1369 mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1370 else
1371 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1375 /******************************************************************************
1376 * Called when the recurrence edit page is shown.
1377 * The recurrence defaults are set to correspond to the start date.
1378 * The first time, for a new alarm, the recurrence end date is set according to
1379 * the alarm start time.
1381 void EditAlarmDlg::slotShowRecurrenceEdit()
1383 mRecurPageIndex = mTabs->currentIndex();
1384 if (!mReadOnly && !mTemplate)
1386 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1387 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1388 bool expired = (mAlarmDateTime.effectiveKDateTime() < now);
1389 if (mRecurSetDefaultEndDate)
1391 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1392 mRecurSetDefaultEndDate = false;
1394 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1395 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1396 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime.kDateTime());
1398 mRecurPageShown = true;
1401 /******************************************************************************
1402 * Called when the recurrence type selection changes.
1403 * Enables/disables date-only alarms as appropriate.
1404 * Enables/disables controls depending on at-login setting.
1406 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1408 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1409 if (!mTemplate)
1411 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1412 if (mDeferGroup)
1413 mDeferGroup->setEnabled(recurs);
1414 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1415 if (atLogin)
1417 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1418 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.kDateTime());
1420 if (mReminder)
1421 mReminder->enableOnceOnly(recurs && !atLogin);
1423 if (mReminder)
1424 mReminder->setAfterOnly(atLogin);
1425 mLateCancel->setEnabled(!atLogin);
1426 if (mShowInKorganizer)
1427 mShowInKorganizer->setEnabled(!atLogin);
1428 slotRecurFrequencyChange();
1431 /******************************************************************************
1432 * Called when the recurrence frequency selection changes, or the sub-
1433 * repetition interval changes.
1434 * Updates the recurrence frequency text.
1436 void EditAlarmDlg::slotRecurFrequencyChange()
1438 slotSetSubRepetition();
1439 KAEvent event;
1440 mRecurrenceEdit->updateEvent(event, false);
1441 mTabs->setTabText(mRecurPageIndex, recurText(event));
1444 /******************************************************************************
1445 * Called when the Repetition within Recurrence button has been pressed to
1446 * display the sub-repetition dialog.
1447 * Alarm repetition has the following restrictions:
1448 * 1) Not allowed for a repeat-at-login alarm
1449 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1450 * 3) The overall repeat duration must be less than the recurrence interval.
1452 void EditAlarmDlg::slotSetSubRepetition()
1454 bool dateOnly = mTemplate ? mTemplateAnyTime->isChecked() : mTimeWidget->anyTime();
1455 mRecurrenceEdit->setSubRepetition((mReminder ? mReminder->minutes() : 0), dateOnly);
1458 /******************************************************************************
1459 * Called when one of the template time radio buttons is clicked,
1460 * to enable or disable the template time entry spin boxes.
1462 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton*)
1464 mTemplateTime->setEnabled(mTemplateUseTime->isChecked());
1465 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isChecked());
1468 /******************************************************************************
1469 * Called when the "Any time" checkbox is toggled in the date/time widget.
1470 * Sets the advance reminder and late cancel units to days if any time is checked.
1472 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1474 if (mReminder && mReminder->isReminder())
1475 mReminder->setDateOnly(anyTime);
1476 mLateCancel->setDateOnly(anyTime);
1479 bool EditAlarmDlg::dateOnly() const
1481 return mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isChecked();
1484 bool EditAlarmDlg::isTimedRecurrence() const
1486 return mRecurrenceEdit->isTimedRepeatType();
1489 void EditAlarmDlg::showMainPage()
1491 mTabs->setCurrentIndex(mMainPageIndex);
1494 #include "moc_editdlg.cpp"
1495 #include "moc_editdlg_p.cpp"
1497 // vim: et sw=4: