SVN_SILENT made messages (.desktop file) - always resolve ours
[kdepim.git] / kalarm / editdlg.cpp
blob81ab80930a728f7d115a8763985d295155219f82
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 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 mSavedEvent;
500 mWindowList.removeAll(this);
503 /******************************************************************************
504 * Return the number of instances.
506 int EditAlarmDlg::instanceCount()
508 return mWindowList.count();
511 /******************************************************************************
512 * Initialise the dialog controls from the specified event.
514 void EditAlarmDlg::initValues(const KAEvent* event)
516 setReadOnly(mDesiredReadOnly);
518 mChanged = false;
519 mOnlyDeferred = false;
520 mExpiredRecurrence = false;
521 mLateCancel->showAutoClose(false);
522 bool deferGroupVisible = false;
523 if (event)
525 // Set the values to those for the specified event
526 if (mTemplate)
527 mTemplateName->setText(event->templateName());
528 bool recurs = event->recurs();
529 if ((recurs || event->repetition()) && !mTemplate && event->deferred())
531 deferGroupVisible = true;
532 mDeferDateTime = event->deferDateTime();
533 mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
534 mDeferGroup->show();
536 if (mTemplate)
538 // Editing a template
539 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
540 bool noTime = !afterTime;
541 bool useTime = !event->mainDateTime().isDateOnly();
542 RadioButton* button = noTime ? mTemplateDefaultTime :
543 (afterTime > 0) ? mTemplateUseTimeAfter :
544 useTime ? mTemplateUseTime : mTemplateAnyTime;
545 button->setChecked(true);
546 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
547 if (!noTime && useTime)
548 mTemplateTime->setValue(event->mainDateTime().kDateTime().time());
549 else
550 mTemplateTime->setValue(0);
552 else
554 if (event->isTemplate())
556 // Initialising from an alarm template: use current date
557 KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
558 int afterTime = event->templateAfterTime();
559 if (afterTime >= 0)
561 mTimeWidget->setDateTime(now.addSecs(afterTime * 60));
562 mTimeWidget->selectTimeFromNow();
564 else
566 KDateTime dt = event->startDateTime().kDateTime();
567 dt.setTimeSpec(Preferences::timeZone());
568 QDate d = now.date();
569 if (!dt.isDateOnly() && now.time() >= dt.time())
570 d = d.addDays(1); // alarm time has already passed, so use tomorrow
571 dt.setDate(d);
572 mTimeWidget->setDateTime(dt);
575 else
577 mExpiredRecurrence = recurs && event->mainExpired();
578 mTimeWidget->setDateTime(recurs || event->category() == CalEvent::ARCHIVED ? event->startDateTime()
579 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
583 KAEvent::SubAction action = event->actionSubType();
584 AlarmText altext;
585 if (event->commandScript())
586 altext.setScript(event->cleanText());
587 else
588 altext.setText(event->cleanText());
589 setAction(action, altext);
591 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
592 TimePeriod::HoursMinutes);
593 if (mShowInKorganizer)
594 mShowInKorganizer->setChecked(event->copyToKOrganizer());
595 type_initValues(event);
596 mRecurrenceEdit->set(*event); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
597 mTabs->setTabText(mRecurPageIndex, recurText(*event));
599 else
601 // Set the values to their defaults
602 KDateTime defaultTime = KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
603 if (mTemplate)
605 mTemplateDefaultTime->setChecked(true);
606 mTemplateTime->setValue(0);
607 mTemplateTimeAfter->setValue(1);
609 else
610 mTimeWidget->setDateTime(defaultTime);
611 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes);
612 if (mShowInKorganizer)
613 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
614 type_initValues(Q_NULLPTR);
615 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
616 slotRecurFrequencyChange(); // update the Recurrence text
618 if (mReminder && mTimeWidget)
619 mReminder->setDefaultUnits(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
621 if (!deferGroupVisible && mDeferGroup)
622 mDeferGroup->hide();
624 bool empty = AlarmCalendar::resources()->events(CalEvent::TEMPLATE).isEmpty();
625 if (mLoadTemplateButton)
626 mLoadTemplateButton->setEnabled(!empty);
629 /******************************************************************************
630 * Initialise various values in the New Alarm dialogue.
632 void EditAlarmDlg::setTime(const DateTime& start)
634 mTimeWidget->setDateTime(start);
636 void EditAlarmDlg::setRecurrence(const KARecurrence& recur, const KCalCore::Duration& subRepeatInterval, int subRepeatCount)
638 KAEvent event;
639 event.setTime(mTimeWidget->getDateTime(Q_NULLPTR, false, false));
640 event.setRecurrence(recur);
641 event.setRepetition(Repetition(subRepeatInterval, subRepeatCount - 1));
642 mRecurrenceEdit->set(event);
644 void EditAlarmDlg::setRepeatAtLogin()
646 mRecurrenceEdit->setRepeatAtLogin();
648 void EditAlarmDlg::setLateCancel(int minutes)
650 mLateCancel->setMinutes(minutes, mTimeWidget->getDateTime(Q_NULLPTR, false, false).isDateOnly(),
651 TimePeriod::HoursMinutes);
653 void EditAlarmDlg::setShowInKOrganizer(bool show)
655 mShowInKorganizer->setChecked(show);
658 /******************************************************************************
659 * Set the read-only status of all non-template controls.
661 void EditAlarmDlg::setReadOnly(bool readOnly)
663 mReadOnly = readOnly;
665 if (mTimeWidget)
666 mTimeWidget->setReadOnly(readOnly);
667 mLateCancel->setReadOnly(readOnly);
668 if (mDeferChangeButton)
670 if (readOnly)
671 mDeferChangeButton->hide();
672 else
673 mDeferChangeButton->show();
675 if (mShowInKorganizer)
676 mShowInKorganizer->setReadOnly(readOnly);
679 /******************************************************************************
680 * Save the state of all controls.
682 void EditAlarmDlg::saveState(const KAEvent* event)
684 delete mSavedEvent;
685 mSavedEvent = Q_NULLPTR;
686 if (event)
687 mSavedEvent = new KAEvent(*event);
688 if (mTemplate)
690 mSavedTemplateName = mTemplateName->text();
691 mSavedTemplateTimeType = mTemplateTimeGroup->checkedButton();
692 mSavedTemplateTime = mTemplateTime->time();
693 mSavedTemplateAfterTime = mTemplateTimeAfter->value();
695 checkText(mSavedTextFileCommandMessage, false);
696 if (mTimeWidget)
697 mSavedDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
698 mSavedLateCancel = mLateCancel->minutes();
699 if (mShowInKorganizer)
700 mSavedShowInKorganizer = mShowInKorganizer->isChecked();
701 mSavedRecurrenceType = mRecurrenceEdit->repeatType();
702 mSavedDeferTime = mDeferDateTime.kDateTime();
705 /******************************************************************************
706 * Check whether any of the controls has changed state since the dialog was
707 * first displayed.
708 * Reply = true if any non-deferral controls have changed, or if it's a new event.
709 * = false if no non-deferral controls have changed. In this case,
710 * mOnlyDeferred indicates whether deferral controls may have changed.
712 bool EditAlarmDlg::stateChanged() const
714 mChanged = true;
715 mOnlyDeferred = false;
716 if (!mSavedEvent)
717 return true;
718 QString textFileCommandMessage;
719 checkText(textFileCommandMessage, false);
720 if (mTemplate)
722 if (mSavedTemplateName != mTemplateName->text()
723 || mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton()
724 || (mTemplateUseTime->isChecked() && mSavedTemplateTime != mTemplateTime->time())
725 || (mTemplateUseTimeAfter->isChecked() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
726 return true;
728 else
730 KDateTime dt = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
731 if (mSavedDateTime.timeSpec() != dt.timeSpec() || mSavedDateTime != dt)
732 return true;
734 if (mSavedLateCancel != mLateCancel->minutes()
735 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
736 || textFileCommandMessage != mSavedTextFileCommandMessage
737 || mSavedRecurrenceType != mRecurrenceEdit->repeatType())
738 return true;
739 if (type_stateChanged())
740 return true;
741 if (mRecurrenceEdit->stateChanged())
742 return true;
743 if (mSavedEvent && mSavedEvent->deferred())
744 mOnlyDeferred = true;
745 mChanged = false;
746 return false;
749 /******************************************************************************
750 * Called whenever any of the controls changes state.
751 * Enable or disable the OK button depending on whether any controls have a
752 * different state from their initial state.
754 void EditAlarmDlg::contentsChanged()
756 // Don't do anything if it's a new alarm or we're still initialising
757 // (i.e. mSavedEvent null).
758 if (mSavedEvent && mButtonBox->button(QDialogButtonBox::Ok))
759 mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(stateChanged() || mDeferDateTime.kDateTime() != mSavedDeferTime);
762 /******************************************************************************
763 * Get the currently entered dialog data.
764 * The data is returned in the supplied KAEvent instance.
765 * Reply = false if the only change has been to an existing deferral.
767 bool EditAlarmDlg::getEvent(KAEvent& event, Akonadi::Collection& collection)
769 collection = mCollection;
770 if (mChanged)
772 // It's a new event, or the edit controls have changed
773 setEvent(event, mAlarmMessage, false);
774 return true;
777 // Only the deferral time may have changed
778 event = *mSavedEvent;
779 if (mOnlyDeferred)
781 // Just modify the original event, to avoid expired recurring events
782 // being returned as rubbish.
783 if (mDeferDateTime.isValid())
784 event.defer(mDeferDateTime, event.reminderDeferral(), false);
785 else
786 event.cancelDefer();
788 return false;
791 /******************************************************************************
792 * Extract the data in the dialog and set up a KAEvent from it.
793 * If 'trial' is true, the event is set up for a simple one-off test, ignoring
794 * recurrence, reminder, template etc. data.
796 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial)
798 KDateTime dt;
799 if (!trial)
801 if (!mTemplate)
802 dt = mAlarmDateTime.effectiveKDateTime();
803 else if (mTemplateUseTime->isChecked())
804 dt = KDateTime(QDate(2000,1,1), mTemplateTime->time());
807 int lateCancel = (trial || !mLateCancel->isEnabled()) ? 0 : mLateCancel->minutes();
808 type_setEvent(event, dt, text, lateCancel, trial);
810 if (!trial)
812 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
814 mRecurrenceEdit->updateEvent(event, !mTemplate);
815 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
816 bool dateOnly = mAlarmDateTime.isDateOnly();
817 if ((dateOnly && mAlarmDateTime.date() < now.date())
818 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
820 // A timed recurrence has an entered start date which has
821 // already expired, so we must adjust the next repetition.
822 event.setNextOccurrence(now);
824 mAlarmDateTime = event.startDateTime();
825 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime)
827 bool deferral = true;
828 bool deferReminder = false;
829 int reminder = mReminder ? mReminder->minutes() : 0;
830 if (reminder)
832 DateTime remindTime = mAlarmDateTime.addMins(-reminder);
833 if (mDeferDateTime >= remindTime)
835 if (remindTime > KDateTime::currentUtcDateTime())
836 deferral = false; // ignore deferral if it's after next reminder
837 else if (mDeferDateTime > remindTime)
838 deferReminder = true; // it's the reminder which is being deferred
841 if (deferral)
842 event.defer(mDeferDateTime, deferReminder, false);
845 if (mTemplate)
847 int afterTime = mTemplateDefaultTime->isChecked() ? 0
848 : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1;
849 event.setTemplate(mTemplateName->text(), afterTime);
854 /******************************************************************************
855 * Get the currently specified alarm flag bits.
857 KAEvent::Flags EditAlarmDlg::getAlarmFlags() const
859 KAEvent::Flags flags(0);
860 if (mShowInKorganizer && mShowInKorganizer->isEnabled() && mShowInKorganizer->isChecked())
861 flags |= KAEvent::COPY_KORGANIZER;
862 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
863 flags |= KAEvent::REPEAT_AT_LOGIN;
864 if (mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly())
865 flags |= KAEvent::ANY_TIME;
866 return flags;
869 /******************************************************************************
870 * Called when the dialog is displayed.
871 * The first time through, sets the size to the same as the last time it was
872 * displayed.
874 void EditAlarmDlg::showEvent(QShowEvent* se)
876 QDialog::showEvent(se);
877 if (!mDeferGroupHeight)
879 if (mDeferGroup)
880 mDeferGroupHeight = mDeferGroup->height() + style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
881 QSize s;
882 if (KAlarm::readConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s))
884 bool defer = mDeferGroup && !mDeferGroup->isHidden();
885 s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0));
886 if (!defer)
887 mTabScrollGroup->setSized();
888 resize(s);
891 slotResize();
892 KWindowSystem::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user
894 if (theApp()->needWindowFocusFix())
896 QApplication::setActiveWindow(this);
897 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
901 /******************************************************************************
902 * Called when the window is first shown, to ensure that it initially becomes
903 * the active window.
904 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
905 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
906 * windows.
908 void EditAlarmDlg::focusFixTimer()
910 if (theApp()->needWindowFocusFix()
911 && QApplication::focusWidget()->window() != this)
913 QApplication::setActiveWindow(this);
914 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer);
918 /******************************************************************************
919 * Called to detect when the mouse is pressed anywhere inside the window.
920 * Activates this window if a MessageWin window is also active.
921 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
922 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
923 * windows.
925 bool EditAlarmDlg::eventFilter(QObject*, QEvent* e)
927 if (theApp()->needWindowFocusFix())
929 if (e->type() == QEvent::MouseButtonPress)
930 QApplication::setActiveWindow(this);
932 return false;
935 /******************************************************************************
936 * Called when the dialog is closed.
938 void EditAlarmDlg::closeEvent(QCloseEvent* ce)
940 Q_EMIT rejected();
941 QDialog::closeEvent(ce);
944 /******************************************************************************
945 * Update the tab sizes (again) and if the resized dialog height is greater
946 * than the minimum, resize it again. This is necessary because (a) resizing
947 * tabs doesn't always work properly the first time, and (b) resizing to the
948 * minimum size hint doesn't always work either.
950 void EditAlarmDlg::slotResize()
952 QSize s = mTabScrollGroup->adjustSize(true);
953 s = minimumSizeHint();
954 if (height() > s.height())
956 // Resize to slightly greater than the minimum height.
957 // This is for some unknown reason necessary, since
958 // sometimes resizing to the minimum height fails.
959 resize(s.width(), s.height() + 2);
963 /******************************************************************************
964 * Called when the dialog's size has changed.
965 * Records the new size (adjusted to ignore the optional height of the deferred
966 * time edit widget) in the config file.
968 void EditAlarmDlg::resizeEvent(QResizeEvent* re)
970 if (isVisible() && mDeferGroupHeight)
972 QSize s = re->size();
973 s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
974 KAlarm::writeConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s);
976 QDialog::resizeEvent(re);
979 /******************************************************************************
980 * Called when any button is clicked.
982 void EditAlarmDlg::slotButtonClicked(QAbstractButton *button)
984 if (button == mTryButton)
985 slotTry();
986 else if (button == mLoadTemplateButton)
987 slotHelp();
988 else if (button == mMoreLessButton)
989 slotDefault();
990 else if (button == mButtonBox->button(QDialogButtonBox::Ok))
992 if (validate())
993 accept();
995 else
996 reject();
999 /******************************************************************************
1000 * Called when the OK button is clicked.
1001 * Validate the input data.
1003 bool EditAlarmDlg::validate()
1005 if (!stateChanged())
1007 // No changes have been made except possibly to an existing deferral
1008 if (!mOnlyDeferred)
1009 reject();
1010 return mOnlyDeferred;
1012 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
1013 if (mTimeWidget
1014 && mTabs->currentIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN)
1015 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1016 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login?
1017 if (mTemplate)
1019 // Check that the template name is not blank and is unique
1020 QString errmsg;
1021 QString name = mTemplateName->text();
1022 if (name.isEmpty())
1023 errmsg = i18nc("@info", "You must enter a name for the alarm template");
1024 else if (name != mSavedTemplateName)
1026 if (AlarmCalendar::resources()->templateEvent(name))
1027 errmsg = i18nc("@info", "Template name is already in use");
1029 if (!errmsg.isEmpty())
1031 mTemplateName->setFocus();
1032 KAMessageBox::sorry(this, errmsg);
1033 return false;
1036 else if (mTimeWidget)
1038 QWidget* errWidget;
1039 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, !timedRecurrence, false, &errWidget);
1040 if (errWidget)
1042 // It's more than just an existing deferral being changed, so the time matters
1043 mTabs->setCurrentIndex(mMainPageIndex);
1044 errWidget->setFocus();
1045 mTimeWidget->getDateTime(); // display the error message now
1046 return false;
1049 if (!type_validate(false))
1050 return false;
1052 if (!mTemplate)
1054 if (mChanged && mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
1056 // Check whether the start date/time must be adjusted
1057 // to match the recurrence specification.
1058 DateTime dt = mAlarmDateTime; // setEvent() changes mAlarmDateTime
1059 KAEvent event;
1060 setEvent(event, mAlarmMessage, false);
1061 mAlarmDateTime = dt; // restore
1062 KDateTime pre = dt.effectiveKDateTime();
1063 bool dateOnly = dt.isDateOnly();
1064 if (dateOnly)
1065 pre = pre.addDays(-1);
1066 else
1067 pre = pre.addSecs(-1);
1068 DateTime next;
1069 event.nextOccurrence(pre, next, KAEvent::IGNORE_REPETITION);
1070 if (next != dt)
1072 QString prompt = dateOnly ? i18nc("@info The parameter is a date value",
1073 "The start date does not match the alarm's recurrence pattern, "
1074 "so it will be adjusted to the date of the next recurrence (%1).",
1075 KLocale::global()->formatDate(next.date(), KLocale::ShortDate))
1076 : i18nc("@info The parameter is a date/time value",
1077 "The start date/time does not match the alarm's recurrence pattern, "
1078 "so it will be adjusted to the date/time of the next recurrence (%1).",
1079 KLocale::global()->formatDateTime(next.kDateTime(), KLocale::ShortDate));
1080 if (KAMessageBox::warningContinueCancel(this, prompt) != KMessageBox::Continue)
1081 return false;
1085 if (timedRecurrence)
1087 KAEvent event;
1088 Akonadi::Collection c;
1089 getEvent(event, c); // this may adjust mAlarmDateTime
1090 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1091 bool dateOnly = mAlarmDateTime.isDateOnly();
1092 if ((dateOnly && mAlarmDateTime.date() < now.date())
1093 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
1095 // A timed recurrence has an entered start date which
1096 // has already expired, so we must adjust it.
1097 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1099 KAMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
1100 return false;
1102 if (event.workTimeOnly() && !event.nextTrigger(KAEvent::DISPLAY_TRIGGER).isValid())
1104 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
1105 != KMessageBox::Continue)
1106 return false;
1110 QString errmsg;
1111 QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg);
1112 if (errWidget)
1114 mTabs->setCurrentIndex(mRecurPageIndex);
1115 errWidget->setFocus();
1116 KAMessageBox::sorry(this, errmsg);
1117 return false;
1120 if (recurType != RecurrenceEdit::NO_RECUR)
1122 KAEvent recurEvent;
1123 int longestRecurMinutes = -1;
1124 int reminder = mReminder ? mReminder->minutes() : 0;
1125 if (reminder && !mReminder->isOnceOnly())
1127 mRecurrenceEdit->updateEvent(recurEvent, false);
1128 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1129 if (longestRecurMinutes && reminder >= longestRecurMinutes)
1131 mTabs->setCurrentIndex(mMainPageIndex);
1132 mReminder->setFocusOnCount();
1133 KAMessageBox::sorry(this, xi18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.",
1134 Reminder::i18n_chk_FirstRecurrenceOnly()));
1135 return false;
1138 if (mRecurrenceEdit->subRepetition())
1140 if (longestRecurMinutes < 0)
1142 mRecurrenceEdit->updateEvent(recurEvent, false);
1143 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1145 if (longestRecurMinutes > 0
1146 && recurEvent.repetition().intervalMinutes() * recurEvent.repetition().count() >= longestRecurMinutes - reminder)
1148 KAMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1149 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1150 return false;
1152 if (!recurEvent.repetition().isDaily()
1153 && ((mTemplate && mTemplateAnyTime->isChecked()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1155 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"));
1156 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1157 return false;
1161 if (!checkText(mAlarmMessage))
1162 return false;
1164 mCollection = Akonadi::Collection();
1165 // An item ID = -2 indicates that the caller already
1166 // knows which collection to use.
1167 if (mCollectionItemId >= -1)
1169 if (mCollectionItemId >= 0)
1171 mCollection = AlarmCalendar::resources()->collectionForEvent(mCollectionItemId);
1172 if (mCollection.isValid())
1174 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1175 if (!(AkonadiModel::instance()->types(mCollection) & type))
1176 mCollection = Akonadi::Collection(); // event may have expired while dialog was open
1179 bool cancelled = false;
1180 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1181 if (CollectionControlModel::isWritableEnabled(mCollection, type) <= 0)
1182 mCollection = CollectionControlModel::destination(type, this, false, &cancelled);
1183 if (!mCollection.isValid())
1185 if (!cancelled)
1186 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1187 return false;
1190 return true;
1193 /******************************************************************************
1194 * Called when the Try button is clicked.
1195 * Display/execute the alarm immediately for the user to check its configuration.
1197 void EditAlarmDlg::slotTry()
1199 QString text;
1200 if (checkText(text))
1202 if (!type_validate(true))
1203 return;
1204 KAEvent event;
1205 setEvent(event, text, true);
1206 if (!mNewAlarm && !stateChanged())
1208 // It's an existing alarm which hasn't been changed yet:
1209 // enable KALARM_UID environment variable to be set.
1210 event.setEventId(mEventId);
1212 type_aboutToTry();
1213 void* result = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1214 type_executedTry(text, result);
1218 /******************************************************************************
1219 * Called when the Load Template button is clicked.
1220 * Prompt to select a template and initialise the dialog with its contents.
1222 void EditAlarmDlg::slotHelp()
1224 KAEvent::Actions type;
1225 switch (mAlarmType)
1227 case KAEvent::FILE:
1228 case KAEvent::MESSAGE: type = KAEvent::ACT_DISPLAY; break;
1229 case KAEvent::COMMAND: type = KAEvent::ACT_COMMAND; break;
1230 case KAEvent::EMAIL: type = KAEvent::ACT_EMAIL; break;
1231 case KAEvent::AUDIO: type = KAEvent::ACT_AUDIO; break;
1232 default:
1233 return;
1235 // Use AutoQPointer to guard against crash on application exit while
1236 // the dialogue is still open. It prevents double deletion (both on
1237 // deletion of EditAlarmDlg, and on return from this function).
1238 AutoQPointer<TemplatePickDlg> dlg = new TemplatePickDlg(type, this);
1239 if (dlg->exec() == QDialog::Accepted)
1241 KAEvent event = dlg->selectedTemplate();
1242 initValues(&event);
1246 /******************************************************************************
1247 * Called when the More Options or Less Options buttons are clicked.
1248 * Show/hide the optional options and swap the More/Less buttons, and save the
1249 * new setting as the default from now on.
1251 void EditAlarmDlg::slotDefault()
1253 showOptions(!mShowingMore);
1254 KConfigGroup config(KSharedConfig::openConfig(), EDIT_MORE_GROUP);
1255 config.writeEntry(EDIT_MORE_KEY, mShowingMore);
1258 /******************************************************************************
1259 * Show/hide the optional options and swap the More/Less buttons.
1261 void EditAlarmDlg::showOptions(bool more)
1263 qCDebug(KALARM_LOG) << (more ? "More" : "Less");
1264 if (more)
1266 mMoreOptions->show();
1267 mMoreLessButton->setText(i18nc("@action:Button", "Less Options <<"));
1269 else
1271 mMoreOptions->hide();
1272 mMoreLessButton->setText(i18nc("@action:button", "More Options >>"));
1274 if (mTimeWidget)
1275 mTimeWidget->showMoreOptions(more);
1276 type_showOptions(more);
1277 mRecurrenceEdit->showMoreOptions(more);
1278 mShowingMore = more;
1279 QTimer::singleShot(0, this, &EditAlarmDlg::slotResize);
1282 /******************************************************************************
1283 * Called when the Change deferral button is clicked.
1285 void EditAlarmDlg::slotEditDeferral()
1287 if (!mTimeWidget)
1288 return;
1289 bool limit = true;
1290 Repetition repetition = mRecurrenceEdit->subRepetition();
1291 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1292 : mTimeWidget->getDateTime(Q_NULLPTR, !repetition, !mExpiredRecurrence);
1293 if (!start.isValid())
1295 if (!mExpiredRecurrence)
1296 return;
1297 limit = false;
1299 KDateTime now = KDateTime::currentUtcDateTime();
1300 if (limit)
1302 if (repetition && start < now)
1304 // Sub-repetition - find the time of the next one
1305 int repeatNum = repetition.isDaily()
1306 ? (start.daysTo(now) + repetition.intervalDays() - 1) / repetition.intervalDays()
1307 : (start.secsTo(now) + repetition.intervalSeconds() - 1) / repetition.intervalSeconds();
1308 if (repeatNum > repetition.count())
1310 mTimeWidget->getDateTime(); // output the appropriate error message
1311 return;
1313 start = repetition.duration(repeatNum).end(start.kDateTime());
1317 bool deferred = mDeferDateTime.isValid();
1318 // Use AutoQPointer to guard against crash on application exit while
1319 // the dialogue is still open. It prevents double deletion (both on
1320 // deletion of EditAlarmDlg, and on return from this function).
1321 AutoQPointer<DeferAlarmDlg> deferDlg = new DeferAlarmDlg((deferred ? mDeferDateTime : DateTime(now.addSecs(60).toTimeSpec(start.timeSpec()))),
1322 start.isDateOnly(), deferred, this);
1323 deferDlg->setObjectName(QStringLiteral("EditDeferDlg")); // used by LikeBack
1324 if (limit)
1326 // Don't allow deferral past the next recurrence
1327 int reminder = mReminder ? mReminder->minutes() : 0;
1328 if (reminder)
1330 DateTime remindTime = start.addMins(-reminder);
1331 if (KDateTime::currentUtcDateTime() < remindTime)
1332 start = remindTime;
1334 deferDlg->setLimit(start.addSecs(-60));
1336 if (deferDlg->exec() == QDialog::Accepted)
1338 mDeferDateTime = deferDlg->getDateTime();
1339 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString());
1340 contentsChanged();
1344 /******************************************************************************
1345 * Called when the main page is shown.
1346 * Sets the focus widget to the first edit field.
1348 void EditAlarmDlg::slotShowMainPage()
1350 if (!mMainPageShown)
1352 if (mTemplateName)
1353 mTemplateName->setFocus();
1354 mMainPageShown = true;
1356 else
1358 // Set scroll position to top, since it otherwise jumps randomly
1359 StackedScrollWidget* main = static_cast<StackedScrollWidget*>(mTabs->widget(0));
1360 main->verticalScrollBar()->setValue(0);
1362 if (mTimeWidget)
1364 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1365 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1366 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1367 mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1368 else
1369 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1373 /******************************************************************************
1374 * Called when the recurrence edit page is shown.
1375 * The recurrence defaults are set to correspond to the start date.
1376 * The first time, for a new alarm, the recurrence end date is set according to
1377 * the alarm start time.
1379 void EditAlarmDlg::slotShowRecurrenceEdit()
1381 mRecurPageIndex = mTabs->currentIndex();
1382 if (!mReadOnly && !mTemplate)
1384 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1385 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1386 bool expired = (mAlarmDateTime.effectiveKDateTime() < now);
1387 if (mRecurSetDefaultEndDate)
1389 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1390 mRecurSetDefaultEndDate = false;
1392 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1393 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1394 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime.kDateTime());
1396 mRecurPageShown = true;
1399 /******************************************************************************
1400 * Called when the recurrence type selection changes.
1401 * Enables/disables date-only alarms as appropriate.
1402 * Enables/disables controls depending on at-login setting.
1404 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1406 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1407 if (!mTemplate)
1409 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1410 if (mDeferGroup)
1411 mDeferGroup->setEnabled(recurs);
1412 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1413 if (atLogin)
1415 mAlarmDateTime = mTimeWidget->getDateTime(Q_NULLPTR, false, false);
1416 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.kDateTime());
1418 if (mReminder)
1419 mReminder->enableOnceOnly(recurs && !atLogin);
1421 if (mReminder)
1422 mReminder->setAfterOnly(atLogin);
1423 mLateCancel->setEnabled(!atLogin);
1424 if (mShowInKorganizer)
1425 mShowInKorganizer->setEnabled(!atLogin);
1426 slotRecurFrequencyChange();
1429 /******************************************************************************
1430 * Called when the recurrence frequency selection changes, or the sub-
1431 * repetition interval changes.
1432 * Updates the recurrence frequency text.
1434 void EditAlarmDlg::slotRecurFrequencyChange()
1436 slotSetSubRepetition();
1437 KAEvent event;
1438 mRecurrenceEdit->updateEvent(event, false);
1439 mTabs->setTabText(mRecurPageIndex, recurText(event));
1442 /******************************************************************************
1443 * Called when the Repetition within Recurrence button has been pressed to
1444 * display the sub-repetition dialog.
1445 * Alarm repetition has the following restrictions:
1446 * 1) Not allowed for a repeat-at-login alarm
1447 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1448 * 3) The overall repeat duration must be less than the recurrence interval.
1450 void EditAlarmDlg::slotSetSubRepetition()
1452 bool dateOnly = mTemplate ? mTemplateAnyTime->isChecked() : mTimeWidget->anyTime();
1453 mRecurrenceEdit->setSubRepetition((mReminder ? mReminder->minutes() : 0), dateOnly);
1456 /******************************************************************************
1457 * Called when one of the template time radio buttons is clicked,
1458 * to enable or disable the template time entry spin boxes.
1460 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton*)
1462 mTemplateTime->setEnabled(mTemplateUseTime->isChecked());
1463 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isChecked());
1466 /******************************************************************************
1467 * Called when the "Any time" checkbox is toggled in the date/time widget.
1468 * Sets the advance reminder and late cancel units to days if any time is checked.
1470 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1472 if (mReminder && mReminder->isReminder())
1473 mReminder->setDateOnly(anyTime);
1474 mLateCancel->setDateOnly(anyTime);
1477 bool EditAlarmDlg::dateOnly() const
1479 return mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isChecked();
1482 bool EditAlarmDlg::isTimedRecurrence() const
1484 return mRecurrenceEdit->isTimedRepeatType();
1487 void EditAlarmDlg::showMainPage()
1489 mTabs->setCurrentIndex(mMainPageIndex);
1492 #include "moc_editdlg.cpp"
1493 #include "moc_editdlg_p.cpp"
1495 // vim: et sw=4: