2 * editdlg.cpp - dialog to create or modify an alarm or alarm template
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.
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"
33 #include "functions.h"
34 #include "kalarmapp.h"
35 #include "latecancel.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"
44 #include "shellprocess.h"
46 #include "stackedwidgets.h"
47 #include "templatepickdlg.h"
49 #include "timespinbox.h"
51 #include <Libkdepim/MaillistDrag>
54 #include <KLocalizedString>
56 #include <KSharedConfig>
57 #include <kwindowsystem.h>
62 #include <QPushButton>
63 #include <QGridLayout>
64 #include <QHBoxLayout>
65 #include <QVBoxLayout>
66 #include <QResizeEvent>
70 #include <QDialogButtonBox>
71 #include "kalarm_debug.h"
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
)
85 if (event
.repetition())
86 r
= QStringLiteral("%1 / %2").arg(event
.recurrenceText()).arg(event
.repetitionText());
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
)
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
);
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
);
130 /******************************************************************************
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
)
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),
152 mDesiredReadOnly(false),
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
)
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()),
178 mDesiredReadOnly(readOnly
),
181 mSavedEvent(Q_NULLPTR
)
183 init(event
, getResource
);
184 mWindowList
.append(this);
187 void EditAlarmDlg::init(const KAEvent
* event
, GetResourceType getResource
)
191 case RES_USE_EVENT_ID
:
194 mCollectionItemId
= event
->itemId();
197 // fall through to RES_PROMPT
199 mCollectionItemId
= -1;
203 mCollectionItemId
= -2;
208 void EditAlarmDlg::init(const KAEvent
* event
)
210 setObjectName(mTemplate
? QStringLiteral("TemplEditDlg") : QStringLiteral("EditDlg")); // used by LikeBack
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]");
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);
225 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
226 mTryButton
= mButtonBox
->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole
);
227 mMoreLessButton
= mButtonBox
->addButton(QDialogButtonBox::RestoreDefaults
);
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
);
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);
254 QFrame
*frame
= new QFrame
;
255 QHBoxLayout
* box
= new QHBoxLayout();
256 frame
->setLayout(box
);
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"));
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
));
286 StackedScrollWidget
* recurScroll
= new StackedScrollWidget(mTabScrollGroup
);
287 mTabs
->addTab(recurScroll
, QString());
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
);
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
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();
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
));
427 mReminder
= createReminder(mMoreOptions
);
430 mReminder
->setFixedSize(mReminder
->sizeHint());
431 connect(mReminder
, &Reminder::changed
, this, &EditAlarmDlg::contentsChanged
);
432 moreLayout
->addWidget(mReminder
, 0, Qt::AlignLeft
);
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
);
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
474 mTemplateName
->setFocus();
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()
500 mButtonBox
= Q_NULLPTR
; // prevent text edit contentsChanged() signal triggering a crash
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
);
521 mOnlyDeferred
= false;
522 mExpiredRecurrence
= false;
523 mLateCancel
->showAutoClose(false);
524 bool deferGroupVisible
= false;
527 // Set the values to those for the specified event
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());
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());
552 mTemplateTime
->setValue(0);
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();
563 mTimeWidget
->setDateTime(now
.addSecs(afterTime
* 60));
564 mTimeWidget
->selectTimeFromNow();
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
574 mTimeWidget
->setDateTime(dt
);
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();
587 if (event
->commandScript())
588 altext
.setScript(event
->cleanText());
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
));
603 // Set the values to their defaults
604 KDateTime defaultTime
= KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
607 mTemplateDefaultTime
->setChecked(true);
608 mTemplateTime
->setValue(0);
609 mTemplateTimeAfter
->setValue(1);
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
)
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
)
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
;
668 mTimeWidget
->setReadOnly(readOnly
);
669 mLateCancel
->setReadOnly(readOnly
);
670 if (mDeferChangeButton
)
673 mDeferChangeButton
->hide();
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
)
687 mSavedEvent
= Q_NULLPTR
;
689 mSavedEvent
= new KAEvent(*event
);
692 mSavedTemplateName
= mTemplateName
->text();
693 mSavedTemplateTimeType
= mTemplateTimeGroup
->checkedButton();
694 mSavedTemplateTime
= mTemplateTime
->time();
695 mSavedTemplateAfterTime
= mTemplateTimeAfter
->value();
697 checkText(mSavedTextFileCommandMessage
, false);
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
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
717 mOnlyDeferred
= false;
720 QString textFileCommandMessage
;
721 checkText(textFileCommandMessage
, false);
724 if (mSavedTemplateName
!= mTemplateName
->text()
725 || mSavedTemplateTimeType
!= mTemplateTimeGroup
->checkedButton()
726 || (mTemplateUseTime
->isChecked() && mSavedTemplateTime
!= mTemplateTime
->time())
727 || (mTemplateUseTimeAfter
->isChecked() && mSavedTemplateAfterTime
!= mTemplateTimeAfter
->value()))
732 KDateTime dt
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
733 if (mSavedDateTime
.timeSpec() != dt
.timeSpec() || mSavedDateTime
!= dt
)
736 if (mSavedLateCancel
!= mLateCancel
->minutes()
737 || (mShowInKorganizer
&& mSavedShowInKorganizer
!= mShowInKorganizer
->isChecked())
738 || textFileCommandMessage
!= mSavedTextFileCommandMessage
739 || mSavedRecurrenceType
!= mRecurrenceEdit
->repeatType())
741 if (type_stateChanged())
743 if (mRecurrenceEdit
->stateChanged())
745 if (mSavedEvent
&& mSavedEvent
->deferred())
746 mOnlyDeferred
= true;
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
;
774 // It's a new event, or the edit controls have changed
775 setEvent(event
, mAlarmMessage
, false);
779 // Only the deferral time may have changed
780 event
= *mSavedEvent
;
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);
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
)
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
);
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;
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
844 event
.defer(mDeferDateTime
, deferReminder
, false);
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
;
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
876 void EditAlarmDlg::showEvent(QShowEvent
* se
)
878 QDialog::showEvent(se
);
879 if (!mDeferGroupHeight
)
882 mDeferGroupHeight
= mDeferGroup
->height() + style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
);
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));
889 mTabScrollGroup
->setSized();
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
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
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
927 bool EditAlarmDlg::eventFilter(QObject
*, QEvent
* e
)
929 if (theApp()->needWindowFocusFix())
931 if (e
->type() == QEvent::MouseButtonPress
)
932 QApplication::setActiveWindow(this);
937 /******************************************************************************
938 * Called when the dialog is closed.
940 void EditAlarmDlg::closeEvent(QCloseEvent
* ce
)
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
)
988 else if (button
== mLoadTemplateButton
)
990 else if (button
== mMoreLessButton
)
992 else if (button
== mButtonBox
->button(QDialogButtonBox::Ok
))
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
1012 return mOnlyDeferred
;
1014 RecurrenceEdit::RepeatType recurType
= mRecurrenceEdit
->repeatType();
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?
1021 // Check that the template name is not blank and is unique
1023 QString name
= mTemplateName
->text();
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
);
1038 else if (mTimeWidget
)
1041 mAlarmDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, !timedRecurrence
, false, &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
1051 if (!type_validate(false))
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
1062 setEvent(event
, mAlarmMessage
, false);
1063 mAlarmDateTime
= dt
; // restore
1064 KDateTime pre
= dt
.effectiveKDateTime();
1065 bool dateOnly
= dt
.isDateOnly();
1067 pre
= pre
.addDays(-1);
1069 pre
= pre
.addSecs(-1);
1071 event
.nextOccurrence(pre
, next
, KAEvent::IGNORE_REPETITION
);
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
)
1087 if (timedRecurrence
)
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"));
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
)
1113 QWidget
* errWidget
= mRecurrenceEdit
->checkData(mAlarmDateTime
.effectiveKDateTime(), errmsg
);
1116 mTabs
->setCurrentIndex(mRecurPageIndex
);
1117 errWidget
->setFocus();
1118 KAMessageBox::sorry(this, errmsg
);
1122 if (recurType
!= RecurrenceEdit::NO_RECUR
)
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()));
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
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
1163 if (!checkText(mAlarmMessage
))
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())
1188 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
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()
1202 if (checkText(text
))
1204 if (!type_validate(true))
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
);
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
;
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;
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();
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");
1268 mMoreOptions
->show();
1269 mMoreLessButton
->setText(i18nc("@action:Button", "Less Options <<"));
1273 mMoreOptions
->hide();
1274 mMoreLessButton
->setText(i18nc("@action:button", "More Options >>"));
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()
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
)
1301 KDateTime now
= KDateTime::currentUtcDateTime();
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
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
1328 // Don't allow deferral past the next recurrence
1329 int reminder
= mReminder
? mReminder
->minutes() : 0;
1332 DateTime remindTime
= start
.addMins(-reminder
);
1333 if (KDateTime::currentUtcDateTime() < 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());
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
)
1355 mTemplateName
->setFocus();
1356 mMainPageShown
= true;
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);
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
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
);
1411 bool recurs
= (mRecurrenceEdit
->repeatType() != RecurrenceEdit::NO_RECUR
);
1413 mDeferGroup
->setEnabled(recurs
);
1414 mTimeWidget
->enableAnyTime(!recurs
|| repeatType
!= RecurrenceEdit::SUBDAILY
);
1417 mAlarmDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
1418 mRecurrenceEdit
->setEndDateTime(mAlarmDateTime
.kDateTime());
1421 mReminder
->enableOnceOnly(recurs
&& !atLogin
);
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();
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"