2 * editdlg.cpp - dialog to create or modify an alarm or alarm template
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.
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 taht types can work with it, but don't insert
221 // it into layout just yet
222 mButtonBox
= new QDialogButtonBox(this);
224 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
225 mTryButton
= mButtonBox
->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole
);
226 mMoreLessButton
= mButtonBox
->addButton(QDialogButtonBox::RestoreDefaults
);
227 } else if (mTemplate
) {
228 mButtonBox
->addButton(QDialogButtonBox::Ok
);
229 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
230 mTryButton
= mButtonBox
->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole
);
231 mMoreLessButton
= mButtonBox
->addButton(QDialogButtonBox::RestoreDefaults
);
233 mButtonBox
->addButton(QDialogButtonBox::Ok
);
234 mButtonBox
->addButton(QDialogButtonBox::Cancel
);
235 mTryButton
= mButtonBox
->addButton(i18nc("@action:button", "Try"), QDialogButtonBox::ActionRole
);
236 mLoadTemplateButton
= mButtonBox
->addButton(i18nc("@action:button", "Load Template..."),
237 QDialogButtonBox::HelpRole
);
238 mMoreLessButton
= mButtonBox
->addButton(QDialogButtonBox::RestoreDefaults
);
240 connect(mButtonBox
, &QDialogButtonBox::clicked
,
241 this, &EditAlarmDlg::slotButtonClicked
);
243 if (mButtonBox
->button(QDialogButtonBox::Ok
)) {
244 mButtonBox
->button(QDialogButtonBox::Ok
)->setWhatsThis(i18nc("@info:whatsthis", "Schedule the alarm at the specified time."));
247 QVBoxLayout
* mainLayout
= new QVBoxLayout(this);
250 QFrame
*frame
= new QFrame
;
251 QHBoxLayout
* box
= new QHBoxLayout();
252 frame
->setLayout(box
);
254 box
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
255 QLabel
* label
= new QLabel(i18nc("@label:textbox", "Template name:"));
256 label
->setFixedSize(label
->sizeHint());
257 box
->addWidget(label
);
258 mTemplateName
= new QLineEdit();
259 mTemplateName
->setReadOnly(mReadOnly
);
260 connect(mTemplateName
, &QLineEdit::textEdited
, this, &EditAlarmDlg::contentsChanged
);
261 label
->setBuddy(mTemplateName
);
262 box
->addWidget(mTemplateName
);
263 frame
->setWhatsThis(i18nc("@info:whatsthis", "Enter the name of the alarm template"));
264 frame
->setFixedHeight(box
->sizeHint().height());
265 mainLayout
->addWidget(frame
);
267 mTabs
= new QTabWidget(this);
268 mainLayout
->addWidget(mTabs
);
269 mTabScrollGroup
= new StackedScrollGroup(this, mTabs
);
271 StackedScrollWidget
* mainScroll
= new StackedScrollWidget(mTabScrollGroup
);
272 mTabs
->addTab(mainScroll
, i18nc("@title:tab", "Alarm"));
274 PageFrame
* mainPage
= new PageFrame(mainScroll
);
275 mainScroll
->setWidget(mainPage
); // mainPage becomes the child of mainScroll
276 connect(mainPage
, &PageFrame::shown
, this, &EditAlarmDlg::slotShowMainPage
);
277 QVBoxLayout
* topLayout
= new QVBoxLayout(mainPage
);
278 topLayout
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
279 topLayout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
282 StackedScrollWidget
* recurScroll
= new StackedScrollWidget(mTabScrollGroup
);
283 mTabs
->addTab(recurScroll
, QString());
285 QFrame
*recurTab
= new QFrame
;
286 QVBoxLayout
* recurTabLayout
= new QVBoxLayout();
287 recurTabLayout
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
288 recurTab
->setLayout(recurTabLayout
);
289 recurScroll
->setWidget(recurTab
); // recurTab becomes the child of recurScroll
290 mRecurrenceEdit
= new RecurrenceEdit(mReadOnly
);
291 recurTabLayout
->addWidget(mRecurrenceEdit
);
292 connect(mRecurrenceEdit
, &RecurrenceEdit::shown
, this, &EditAlarmDlg::slotShowRecurrenceEdit
);
293 connect(mRecurrenceEdit
, &RecurrenceEdit::typeChanged
, this, &EditAlarmDlg::slotRecurTypeChange
);
294 connect(mRecurrenceEdit
, &RecurrenceEdit::frequencyChanged
, this, &EditAlarmDlg::slotRecurFrequencyChange
);
295 connect(mRecurrenceEdit
, &RecurrenceEdit::repeatNeedsInitialisation
, this, &EditAlarmDlg::slotSetSubRepetition
);
296 connect(mRecurrenceEdit
, &RecurrenceEdit::contentsChanged
, this, &EditAlarmDlg::contentsChanged
);
298 // Controls specific to the alarm type
299 QGroupBox
* actionBox
= new QGroupBox(i18nc("@title:group", "Action"), mainPage
);
300 topLayout
->addWidget(actionBox
, 1);
301 QVBoxLayout
* layout
= new QVBoxLayout(actionBox
);
302 layout
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
303 layout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
305 type_init(actionBox
, layout
);
309 // Deferred date/time: visible only for a deferred recurring event.
310 mDeferGroup
= new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage
);
311 topLayout
->addWidget(mDeferGroup
);
312 QHBoxLayout
* hlayout
= new QHBoxLayout(mDeferGroup
);
313 hlayout
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
314 hlayout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
315 QLabel
* label
= new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup
);
316 label
->setFixedSize(label
->sizeHint());
317 hlayout
->addWidget(label
);
318 mDeferTimeLabel
= new QLabel(mDeferGroup
);
319 hlayout
->addWidget(mDeferTimeLabel
);
321 mDeferChangeButton
= new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup
);
322 mDeferChangeButton
->setFixedSize(mDeferChangeButton
->sizeHint());
323 connect(mDeferChangeButton
, &QPushButton::clicked
, this, &EditAlarmDlg::slotEditDeferral
);
324 mDeferChangeButton
->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral"));
325 hlayout
->addWidget(mDeferChangeButton
);
326 //?? mDeferGroup->addSpace(0);
329 QHBoxLayout
* hlayout
= new QHBoxLayout();
330 hlayout
->setMargin(0);
331 topLayout
->addLayout(hlayout
);
333 // Date and time entry
336 QGroupBox
* templateTimeBox
= new QGroupBox(i18nc("@title:group", "Time"), mainPage
);
337 topLayout
->addWidget(templateTimeBox
);
338 QGridLayout
* grid
= new QGridLayout(templateTimeBox
);
339 grid
->setMargin(style()->pixelMetric(QStyle::PM_DefaultChildMargin
));
340 grid
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
341 mTemplateTimeGroup
= new ButtonGroup(templateTimeBox
);
342 connect(mTemplateTimeGroup
, &ButtonGroup::buttonSet
, this, &EditAlarmDlg::slotTemplateTimeType
);
343 connect(mTemplateTimeGroup
, &ButtonGroup::buttonSet
, this, &EditAlarmDlg::contentsChanged
);
345 mTemplateDefaultTime
= new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox
);
346 mTemplateDefaultTime
->setFixedSize(mTemplateDefaultTime
->sizeHint());
347 mTemplateDefaultTime
->setReadOnly(mReadOnly
);
348 mTemplateDefaultTime
->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. "
349 "The normal default start time will be used."));
350 mTemplateTimeGroup
->addButton(mTemplateDefaultTime
);
351 grid
->addWidget(mTemplateDefaultTime
, 0, 0, Qt::AlignLeft
);
353 QWidget
*box
= new QWidget(templateTimeBox
);
354 QHBoxLayout
* layout
= new QHBoxLayout(box
);
355 layout
->setMargin(0);
356 layout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
357 mTemplateUseTime
= new RadioButton(i18nc("@option:radio", "Time:"), box
);
358 mTemplateUseTime
->setFixedSize(mTemplateUseTime
->sizeHint());
359 mTemplateUseTime
->setReadOnly(mReadOnly
);
360 mTemplateUseTime
->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template."));
361 layout
->addWidget(mTemplateUseTime
);
362 mTemplateTimeGroup
->addButton(mTemplateUseTime
);
363 mTemplateTime
= new TimeEdit();
364 mTemplateTime
->setFixedSize(mTemplateTime
->sizeHint());
365 mTemplateTime
->setReadOnly(mReadOnly
);
366 mTemplateTime
->setWhatsThis(xi18nc("@info:whatsthis",
367 "<para>Enter the start time for alarms based on this template.</para><para>%1</para>",
368 TimeSpinBox::shiftWhatsThis()));
369 connect(mTemplateTime
, &TimeEdit::valueChanged
, this, &EditAlarmDlg::contentsChanged
);
370 layout
->addWidget(mTemplateTime
);
371 layout
->addStretch(1);
372 grid
->addWidget(box
, 0, 1, Qt::AlignLeft
);
374 mTemplateAnyTime
= new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox
);
375 mTemplateAnyTime
->setFixedSize(mTemplateAnyTime
->sizeHint());
376 mTemplateAnyTime
->setReadOnly(mReadOnly
);
377 mTemplateAnyTime
->setWhatsThis(xi18nc("@info:whatsthis", "Set the <interface>Any time</interface> option for alarms based on this template."));
378 mTemplateTimeGroup
->addButton(mTemplateAnyTime
);
379 grid
->addWidget(mTemplateAnyTime
, 1, 0, Qt::AlignLeft
);
381 box
= new QWidget(templateTimeBox
);
382 layout
= new QHBoxLayout(box
);
383 layout
->setMargin(0);
384 layout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
385 mTemplateUseTimeAfter
= new RadioButton(i18nc("@option:radio", "Time from now:"), box
);
386 mTemplateUseTimeAfter
->setFixedSize(mTemplateUseTimeAfter
->sizeHint());
387 mTemplateUseTimeAfter
->setReadOnly(mReadOnly
);
388 mTemplateUseTimeAfter
->setWhatsThis(i18nc("@info:whatsthis",
389 "Set alarms based on this template to start after the specified time "
390 "interval from when the alarm is created."));
391 layout
->addWidget(mTemplateUseTimeAfter
);
392 mTemplateTimeGroup
->addButton(mTemplateUseTimeAfter
);
393 mTemplateTimeAfter
= new TimeSpinBox(1, maxDelayTime
);
394 mTemplateTimeAfter
->setValue(1439);
395 mTemplateTimeAfter
->setFixedSize(mTemplateTimeAfter
->sizeHint());
396 mTemplateTimeAfter
->setReadOnly(mReadOnly
);
397 connect(mTemplateTimeAfter
, static_cast<void (TimeSpinBox::*)(int)>(&TimeSpinBox::valueChanged
), this, &EditAlarmDlg::contentsChanged
);
398 mTemplateTimeAfter
->setWhatsThis(xi18nc("@info:whatsthis", "<para>%1</para><para>%2</para>",
399 AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis()));
400 layout
->addWidget(mTemplateTimeAfter
);
401 box
->setFixedHeight(box
->sizeHint().height());
402 grid
->addWidget(box
, 1, 1, Qt::AlignLeft
);
404 hlayout
->addStretch();
408 mTimeWidget
= new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME
, mainPage
);
409 connect(mTimeWidget
, &AlarmTimeWidget::dateOnlyToggled
, this, &EditAlarmDlg::slotAnyTimeToggled
);
410 connect(mTimeWidget
, &AlarmTimeWidget::changed
, this, &EditAlarmDlg::contentsChanged
);
411 topLayout
->addWidget(mTimeWidget
);
414 // Optional controls depending on More/Less Options button
415 mMoreOptions
= new QFrame(mainPage
);
416 mMoreOptions
->setFrameStyle(QFrame::NoFrame
);
417 topLayout
->addWidget(mMoreOptions
);
418 QVBoxLayout
* moreLayout
= new QVBoxLayout(mMoreOptions
);
419 moreLayout
->setMargin(0);
420 moreLayout
->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
423 mReminder
= createReminder(mMoreOptions
);
426 mReminder
->setFixedSize(mReminder
->sizeHint());
427 connect(mReminder
, &Reminder::changed
, this, &EditAlarmDlg::contentsChanged
);
428 moreLayout
->addWidget(mReminder
, 0, Qt::AlignLeft
);
430 connect(mTimeWidget
, &AlarmTimeWidget::changed
, mReminder
, &Reminder::setDefaultUnits
);
433 // Late cancel selector - default = allow late display
434 mLateCancel
= new LateCancelSelector(true, mMoreOptions
);
435 connect(mLateCancel
, &LateCancelSelector::changed
, this, &EditAlarmDlg::contentsChanged
);
436 moreLayout
->addWidget(mLateCancel
, 0, Qt::AlignLeft
);
438 PackedLayout
* playout
= new PackedLayout(Qt::AlignJustify
);
439 playout
->setSpacing(2 * style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
));
440 moreLayout
->addLayout(playout
);
442 // Acknowledgement confirmation required - default = no confirmation
443 CheckBox
* confirmAck
= type_createConfirmAckCheckbox(mMoreOptions
);
446 confirmAck
->setFixedSize(confirmAck
->sizeHint());
447 connect(confirmAck
, &CheckBox::toggled
, this, &EditAlarmDlg::contentsChanged
);
448 playout
->addWidget(confirmAck
);
451 if (theApp()->korganizerEnabled())
453 // Show in KOrganizer checkbox
454 mShowInKorganizer
= new CheckBox(i18n_chk_ShowInKOrganizer(), mMoreOptions
);
455 mShowInKorganizer
->setFixedSize(mShowInKorganizer
->sizeHint());
456 connect(mShowInKorganizer
, &CheckBox::toggled
, this, &EditAlarmDlg::contentsChanged
);
457 mShowInKorganizer
->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar"));
458 playout
->addWidget(mShowInKorganizer
);
461 mainLayout
->addWidget(mButtonBox
);
463 // Hide optional controls
464 KConfigGroup
config(KSharedConfig::openConfig(), EDIT_MORE_GROUP
);
465 showOptions(config
.readEntry(EDIT_MORE_KEY
, false));
467 // Initialise the state of all controls according to the specified event, if any
470 mTemplateName
->setFocus();
474 // Save the initial state of all controls so that we can later tell if they have changed
475 saveState((event
&& (mTemplate
|| !event
->isTemplate())) ? event
: Q_NULLPTR
);
476 contentsChanged(); // enable/disable OK button
479 // Note the current desktop so that the dialog can be shown on it.
480 // If a main window is visible, the dialog will by KDE default always appear on its
481 // desktop. If the user invokes the dialog via the system tray on a different desktop,
482 // that can cause confusion.
483 mDesktop
= KWindowSystem::currentDesktop();
485 if (theApp()->windowFocusBroken())
487 const QList
<QWidget
*> children
= findChildren
<QWidget
*>();
488 foreach (QWidget
* w
, children
)
489 w
->installEventFilter(this);
493 EditAlarmDlg::~EditAlarmDlg()
496 mWindowList
.removeAll(this);
499 /******************************************************************************
500 * Return the number of instances.
502 int EditAlarmDlg::instanceCount()
504 return mWindowList
.count();
507 /******************************************************************************
508 * Initialise the dialog controls from the specified event.
510 void EditAlarmDlg::initValues(const KAEvent
* event
)
512 setReadOnly(mDesiredReadOnly
);
515 mOnlyDeferred
= false;
516 mExpiredRecurrence
= false;
517 mLateCancel
->showAutoClose(false);
518 bool deferGroupVisible
= false;
521 // Set the values to those for the specified event
523 mTemplateName
->setText(event
->templateName());
524 bool recurs
= event
->recurs();
525 if ((recurs
|| event
->repetition()) && !mTemplate
&& event
->deferred())
527 deferGroupVisible
= true;
528 mDeferDateTime
= event
->deferDateTime();
529 mDeferTimeLabel
->setText(mDeferDateTime
.formatLocale());
534 // Editing a template
535 int afterTime
= event
->isTemplate() ? event
->templateAfterTime() : -1;
536 bool noTime
= !afterTime
;
537 bool useTime
= !event
->mainDateTime().isDateOnly();
538 RadioButton
* button
= noTime
? mTemplateDefaultTime
:
539 (afterTime
> 0) ? mTemplateUseTimeAfter
:
540 useTime
? mTemplateUseTime
: mTemplateAnyTime
;
541 button
->setChecked(true);
542 mTemplateTimeAfter
->setValue(afterTime
> 0 ? afterTime
: 1);
543 if (!noTime
&& useTime
)
544 mTemplateTime
->setValue(event
->mainDateTime().kDateTime().time());
546 mTemplateTime
->setValue(0);
550 if (event
->isTemplate())
552 // Initialising from an alarm template: use current date
553 KDateTime now
= KDateTime::currentDateTime(Preferences::timeZone());
554 int afterTime
= event
->templateAfterTime();
557 mTimeWidget
->setDateTime(now
.addSecs(afterTime
* 60));
558 mTimeWidget
->selectTimeFromNow();
562 KDateTime dt
= event
->startDateTime().kDateTime();
563 dt
.setTimeSpec(Preferences::timeZone());
564 QDate d
= now
.date();
565 if (!dt
.isDateOnly() && now
.time() >= dt
.time())
566 d
= d
.addDays(1); // alarm time has already passed, so use tomorrow
568 mTimeWidget
->setDateTime(dt
);
573 mExpiredRecurrence
= recurs
&& event
->mainExpired();
574 mTimeWidget
->setDateTime(recurs
|| event
->category() == CalEvent::ARCHIVED
? event
->startDateTime()
575 : event
->mainExpired() ? event
->deferDateTime() : event
->mainDateTime());
579 KAEvent::SubAction action
= event
->actionSubType();
581 if (event
->commandScript())
582 altext
.setScript(event
->cleanText());
584 altext
.setText(event
->cleanText());
585 setAction(action
, altext
);
587 mLateCancel
->setMinutes(event
->lateCancel(), event
->startDateTime().isDateOnly(),
588 TimePeriod::HoursMinutes
);
589 if (mShowInKorganizer
)
590 mShowInKorganizer
->setChecked(event
->copyToKOrganizer());
591 type_initValues(event
);
592 mRecurrenceEdit
->set(*event
); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
593 mTabs
->setTabText(mRecurPageIndex
, recurText(*event
));
597 // Set the values to their defaults
598 KDateTime defaultTime
= KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
601 mTemplateDefaultTime
->setChecked(true);
602 mTemplateTime
->setValue(0);
603 mTemplateTimeAfter
->setValue(1);
606 mTimeWidget
->setDateTime(defaultTime
);
607 mLateCancel
->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes
);
608 if (mShowInKorganizer
)
609 mShowInKorganizer
->setChecked(Preferences::defaultCopyToKOrganizer());
610 type_initValues(Q_NULLPTR
);
611 mRecurrenceEdit
->setDefaults(defaultTime
); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
612 slotRecurFrequencyChange(); // update the Recurrence text
614 if (mReminder
&& mTimeWidget
)
615 mReminder
->setDefaultUnits(mTimeWidget
->getDateTime(Q_NULLPTR
, false, false));
617 if (!deferGroupVisible
&& mDeferGroup
)
620 bool empty
= AlarmCalendar::resources()->events(CalEvent::TEMPLATE
).isEmpty();
621 if (mLoadTemplateButton
) {
622 mLoadTemplateButton
->setEnabled(!empty
);
626 /******************************************************************************
627 * Initialise various values in the New Alarm dialogue.
629 void EditAlarmDlg::setTime(const DateTime
& start
)
631 mTimeWidget
->setDateTime(start
);
633 void EditAlarmDlg::setRecurrence(const KARecurrence
& recur
, const KCalCore::Duration
& subRepeatInterval
, int subRepeatCount
)
636 event
.setTime(mTimeWidget
->getDateTime(Q_NULLPTR
, false, false));
637 event
.setRecurrence(recur
);
638 event
.setRepetition(Repetition(subRepeatInterval
, subRepeatCount
- 1));
639 mRecurrenceEdit
->set(event
);
641 void EditAlarmDlg::setRepeatAtLogin()
643 mRecurrenceEdit
->setRepeatAtLogin();
645 void EditAlarmDlg::setLateCancel(int minutes
)
647 mLateCancel
->setMinutes(minutes
, mTimeWidget
->getDateTime(Q_NULLPTR
, false, false).isDateOnly(),
648 TimePeriod::HoursMinutes
);
650 void EditAlarmDlg::setShowInKOrganizer(bool show
)
652 mShowInKorganizer
->setChecked(show
);
655 /******************************************************************************
656 * Set the read-only status of all non-template controls.
658 void EditAlarmDlg::setReadOnly(bool readOnly
)
660 mReadOnly
= readOnly
;
663 mTimeWidget
->setReadOnly(readOnly
);
664 mLateCancel
->setReadOnly(readOnly
);
665 if (mDeferChangeButton
)
668 mDeferChangeButton
->hide();
670 mDeferChangeButton
->show();
672 if (mShowInKorganizer
)
673 mShowInKorganizer
->setReadOnly(readOnly
);
676 /******************************************************************************
677 * Save the state of all controls.
679 void EditAlarmDlg::saveState(const KAEvent
* event
)
682 mSavedEvent
= Q_NULLPTR
;
684 mSavedEvent
= new KAEvent(*event
);
687 mSavedTemplateName
= mTemplateName
->text();
688 mSavedTemplateTimeType
= mTemplateTimeGroup
->checkedButton();
689 mSavedTemplateTime
= mTemplateTime
->time();
690 mSavedTemplateAfterTime
= mTemplateTimeAfter
->value();
692 checkText(mSavedTextFileCommandMessage
, false);
694 mSavedDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
695 mSavedLateCancel
= mLateCancel
->minutes();
696 if (mShowInKorganizer
)
697 mSavedShowInKorganizer
= mShowInKorganizer
->isChecked();
698 mSavedRecurrenceType
= mRecurrenceEdit
->repeatType();
699 mSavedDeferTime
= mDeferDateTime
.kDateTime();
702 /******************************************************************************
703 * Check whether any of the controls has changed state since the dialog was
705 * Reply = true if any non-deferral controls have changed, or if it's a new event.
706 * = false if no non-deferral controls have changed. In this case,
707 * mOnlyDeferred indicates whether deferral controls may have changed.
709 bool EditAlarmDlg::stateChanged() const
712 mOnlyDeferred
= false;
715 QString textFileCommandMessage
;
716 checkText(textFileCommandMessage
, false);
719 if (mSavedTemplateName
!= mTemplateName
->text()
720 || mSavedTemplateTimeType
!= mTemplateTimeGroup
->checkedButton()
721 || (mTemplateUseTime
->isChecked() && mSavedTemplateTime
!= mTemplateTime
->time())
722 || (mTemplateUseTimeAfter
->isChecked() && mSavedTemplateAfterTime
!= mTemplateTimeAfter
->value()))
727 KDateTime dt
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
728 if (mSavedDateTime
.timeSpec() != dt
.timeSpec() || mSavedDateTime
!= dt
)
731 if (mSavedLateCancel
!= mLateCancel
->minutes()
732 || (mShowInKorganizer
&& mSavedShowInKorganizer
!= mShowInKorganizer
->isChecked())
733 || textFileCommandMessage
!= mSavedTextFileCommandMessage
734 || mSavedRecurrenceType
!= mRecurrenceEdit
->repeatType())
736 if (type_stateChanged())
738 if (mRecurrenceEdit
->stateChanged())
740 if (mSavedEvent
&& mSavedEvent
->deferred())
741 mOnlyDeferred
= true;
746 /******************************************************************************
747 * Called whenever any of the controls changes state.
748 * Enable or disable the OK button depending on whether any controls have a
749 * different state from their initial state.
751 void EditAlarmDlg::contentsChanged()
753 // Don't do anything if it's a new alarm or we're still initialising
754 // (i.e. mSavedEvent null).
755 if (mSavedEvent
&& mButtonBox
->button(QDialogButtonBox::Ok
))
756 mButtonBox
->button(QDialogButtonBox::Ok
)->setEnabled(stateChanged() || mDeferDateTime
.kDateTime() != mSavedDeferTime
);
759 /******************************************************************************
760 * Get the currently entered dialog data.
761 * The data is returned in the supplied KAEvent instance.
762 * Reply = false if the only change has been to an existing deferral.
764 bool EditAlarmDlg::getEvent(KAEvent
& event
, Akonadi::Collection
& collection
)
766 collection
= mCollection
;
769 // It's a new event, or the edit controls have changed
770 setEvent(event
, mAlarmMessage
, false);
774 // Only the deferral time may have changed
775 event
= *mSavedEvent
;
778 // Just modify the original event, to avoid expired recurring events
779 // being returned as rubbish.
780 if (mDeferDateTime
.isValid())
781 event
.defer(mDeferDateTime
, event
.reminderDeferral(), false);
788 /******************************************************************************
789 * Extract the data in the dialog and set up a KAEvent from it.
790 * If 'trial' is true, the event is set up for a simple one-off test, ignoring
791 * recurrence, reminder, template etc. data.
793 void EditAlarmDlg::setEvent(KAEvent
& event
, const QString
& text
, bool trial
)
799 dt
= mAlarmDateTime
.effectiveKDateTime();
800 else if (mTemplateUseTime
->isChecked())
801 dt
= KDateTime(QDate(2000,1,1), mTemplateTime
->time());
804 int lateCancel
= (trial
|| !mLateCancel
->isEnabled()) ? 0 : mLateCancel
->minutes();
805 type_setEvent(event
, dt
, text
, lateCancel
, trial
);
809 if (mRecurrenceEdit
->repeatType() != RecurrenceEdit::NO_RECUR
)
811 mRecurrenceEdit
->updateEvent(event
, !mTemplate
);
812 KDateTime now
= KDateTime::currentDateTime(mAlarmDateTime
.timeSpec());
813 bool dateOnly
= mAlarmDateTime
.isDateOnly();
814 if ((dateOnly
&& mAlarmDateTime
.date() < now
.date())
815 || (!dateOnly
&& mAlarmDateTime
.kDateTime() < now
))
817 // A timed recurrence has an entered start date which has
818 // already expired, so we must adjust the next repetition.
819 event
.setNextOccurrence(now
);
821 mAlarmDateTime
= event
.startDateTime();
822 if (mDeferDateTime
.isValid() && mDeferDateTime
< mAlarmDateTime
)
824 bool deferral
= true;
825 bool deferReminder
= false;
826 int reminder
= mReminder
? mReminder
->minutes() : 0;
829 DateTime remindTime
= mAlarmDateTime
.addMins(-reminder
);
830 if (mDeferDateTime
>= remindTime
)
832 if (remindTime
> KDateTime::currentUtcDateTime())
833 deferral
= false; // ignore deferral if it's after next reminder
834 else if (mDeferDateTime
> remindTime
)
835 deferReminder
= true; // it's the reminder which is being deferred
839 event
.defer(mDeferDateTime
, deferReminder
, false);
844 int afterTime
= mTemplateDefaultTime
->isChecked() ? 0
845 : mTemplateUseTimeAfter
->isChecked() ? mTemplateTimeAfter
->value() : -1;
846 event
.setTemplate(mTemplateName
->text(), afterTime
);
851 /******************************************************************************
852 * Get the currently specified alarm flag bits.
854 KAEvent::Flags
EditAlarmDlg::getAlarmFlags() const
856 KAEvent::Flags
flags(0);
857 if (mShowInKorganizer
&& mShowInKorganizer
->isEnabled() && mShowInKorganizer
->isChecked())
858 flags
|= KAEvent::COPY_KORGANIZER
;
859 if (mRecurrenceEdit
->repeatType() == RecurrenceEdit::AT_LOGIN
)
860 flags
|= KAEvent::REPEAT_AT_LOGIN
;
861 if (mTemplate
? mTemplateAnyTime
->isChecked() : mAlarmDateTime
.isDateOnly())
862 flags
|= KAEvent::ANY_TIME
;
866 /******************************************************************************
867 * Called when the dialog is displayed.
868 * The first time through, sets the size to the same as the last time it was
871 void EditAlarmDlg::showEvent(QShowEvent
* se
)
873 QDialog::showEvent(se
);
874 if (!mDeferGroupHeight
)
877 mDeferGroupHeight
= mDeferGroup
->height() + style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing
);
879 if (KAlarm::readConfigWindowSize(mTemplate
? TEMPLATE_DIALOG_NAME
: EDIT_DIALOG_NAME
, s
))
881 bool defer
= mDeferGroup
&& !mDeferGroup
->isHidden();
882 s
.setHeight(s
.height() + (defer
? mDeferGroupHeight
: 0));
884 mTabScrollGroup
->setSized();
889 KWindowSystem::setOnDesktop(winId(), mDesktop
); // ensure it displays on the desktop expected by the user
891 if (theApp()->needWindowFocusFix())
893 QApplication::setActiveWindow(this);
894 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer
);
898 /******************************************************************************
899 * Called when the window is first shown, to ensure that it initially becomes
901 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
902 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
905 void EditAlarmDlg::focusFixTimer()
907 if (theApp()->needWindowFocusFix()
908 && QApplication::focusWidget()->window() != this)
910 QApplication::setActiveWindow(this);
911 QTimer::singleShot(0, this, &EditAlarmDlg::focusFixTimer
);
915 /******************************************************************************
916 * Called to detect when the mouse is pressed anywhere inside the window.
917 * Activates this window if a MessageWin window is also active.
918 * This is only required on Ubuntu's Unity desktop, which doesn't transfer
919 * keyboard focus properly between Edit Alarm Dialog windows and MessageWin
922 bool EditAlarmDlg::eventFilter(QObject
*, QEvent
* e
)
924 if (theApp()->needWindowFocusFix())
926 if (e
->type() == QEvent::MouseButtonPress
)
927 QApplication::setActiveWindow(this);
932 /******************************************************************************
933 * Called when the dialog is closed.
935 void EditAlarmDlg::closeEvent(QCloseEvent
* ce
)
938 QDialog::closeEvent(ce
);
941 /******************************************************************************
942 * Update the tab sizes (again) and if the resized dialog height is greater
943 * than the minimum, resize it again. This is necessary because (a) resizing
944 * tabs doesn't always work properly the first time, and (b) resizing to the
945 * minimum size hint doesn't always work either.
947 void EditAlarmDlg::slotResize()
949 QSize s
= mTabScrollGroup
->adjustSize(true);
950 s
= minimumSizeHint();
951 if (height() > s
.height())
953 // Resize to slightly greater than the minimum height.
954 // This is for some unknown reason necessary, since
955 // sometimes resizing to the minimum height fails.
956 resize(s
.width(), s
.height() + 2);
960 /******************************************************************************
961 * Called when the dialog's size has changed.
962 * Records the new size (adjusted to ignore the optional height of the deferred
963 * time edit widget) in the config file.
965 void EditAlarmDlg::resizeEvent(QResizeEvent
* re
)
967 if (isVisible() && mDeferGroupHeight
)
969 QSize s
= re
->size();
970 s
.setHeight(s
.height() - (!mDeferGroup
|| mDeferGroup
->isHidden() ? 0 : mDeferGroupHeight
));
971 KAlarm::writeConfigWindowSize(mTemplate
? TEMPLATE_DIALOG_NAME
: EDIT_DIALOG_NAME
, s
);
973 QDialog::resizeEvent(re
);
976 /******************************************************************************
977 * Called when any button is clicked.
979 void EditAlarmDlg::slotButtonClicked(QAbstractButton
*button
)
981 if (button
== mTryButton
) {
983 } else if (button
== mLoadTemplateButton
) {
985 } else if (button
== mMoreLessButton
) {
987 } else if (button
== mButtonBox
->button(QDialogButtonBox::Ok
)) {
996 /******************************************************************************
997 * Called when the OK button is clicked.
998 * Validate the input data.
1000 bool EditAlarmDlg::validate()
1002 if (!stateChanged())
1004 // No changes have been made except possibly to an existing deferral
1007 return mOnlyDeferred
;
1009 RecurrenceEdit::RepeatType recurType
= mRecurrenceEdit
->repeatType();
1011 && mTabs
->currentIndex() == mRecurPageIndex
&& recurType
== RecurrenceEdit::AT_LOGIN
)
1012 mTimeWidget
->setDateTime(mRecurrenceEdit
->endDateTime());
1013 bool timedRecurrence
= mRecurrenceEdit
->isTimedRepeatType(); // does it recur other than at login?
1016 // Check that the template name is not blank and is unique
1018 QString name
= mTemplateName
->text();
1020 errmsg
= i18nc("@info", "You must enter a name for the alarm template");
1021 else if (name
!= mSavedTemplateName
)
1023 if (AlarmCalendar::resources()->templateEvent(name
))
1024 errmsg
= i18nc("@info", "Template name is already in use");
1026 if (!errmsg
.isEmpty())
1028 mTemplateName
->setFocus();
1029 KAMessageBox::sorry(this, errmsg
);
1033 else if (mTimeWidget
)
1036 mAlarmDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, !timedRecurrence
, false, &errWidget
);
1039 // It's more than just an existing deferral being changed, so the time matters
1040 mTabs
->setCurrentIndex(mMainPageIndex
);
1041 errWidget
->setFocus();
1042 mTimeWidget
->getDateTime(); // display the error message now
1046 if (!type_validate(false))
1051 if (mChanged
&& mRecurrenceEdit
->repeatType() != RecurrenceEdit::NO_RECUR
)
1053 // Check whether the start date/time must be adjusted
1054 // to match the recurrence specification.
1055 DateTime dt
= mAlarmDateTime
; // setEvent() changes mAlarmDateTime
1057 setEvent(event
, mAlarmMessage
, false);
1058 mAlarmDateTime
= dt
; // restore
1059 KDateTime pre
= dt
.effectiveKDateTime();
1060 bool dateOnly
= dt
.isDateOnly();
1062 pre
= pre
.addDays(-1);
1064 pre
= pre
.addSecs(-1);
1066 event
.nextOccurrence(pre
, next
, KAEvent::IGNORE_REPETITION
);
1069 QString prompt
= dateOnly
? i18nc("@info The parameter is a date value",
1070 "The start date does not match the alarm's recurrence pattern, "
1071 "so it will be adjusted to the date of the next recurrence (%1).",
1072 KLocale::global()->formatDate(next
.date(), KLocale::ShortDate
))
1073 : i18nc("@info The parameter is a date/time value",
1074 "The start date/time does not match the alarm's recurrence pattern, "
1075 "so it will be adjusted to the date/time of the next recurrence (%1).",
1076 KLocale::global()->formatDateTime(next
.kDateTime(), KLocale::ShortDate
));
1077 if (KAMessageBox::warningContinueCancel(this, prompt
) != KMessageBox::Continue
)
1082 if (timedRecurrence
)
1085 Akonadi::Collection c
;
1086 getEvent(event
, c
); // this may adjust mAlarmDateTime
1087 KDateTime now
= KDateTime::currentDateTime(mAlarmDateTime
.timeSpec());
1088 bool dateOnly
= mAlarmDateTime
.isDateOnly();
1089 if ((dateOnly
&& mAlarmDateTime
.date() < now
.date())
1090 || (!dateOnly
&& mAlarmDateTime
.kDateTime() < now
))
1092 // A timed recurrence has an entered start date which
1093 // has already expired, so we must adjust it.
1094 if (event
.nextOccurrence(now
, mAlarmDateTime
, KAEvent::ALLOW_FOR_REPETITION
) == KAEvent::NO_OCCURRENCE
)
1096 KAMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
1099 if (event
.workTimeOnly() && !event
.nextTrigger(KAEvent::DISPLAY_TRIGGER
).isValid())
1101 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
1102 != KMessageBox::Continue
)
1108 QWidget
* errWidget
= mRecurrenceEdit
->checkData(mAlarmDateTime
.effectiveKDateTime(), errmsg
);
1111 mTabs
->setCurrentIndex(mRecurPageIndex
);
1112 errWidget
->setFocus();
1113 KAMessageBox::sorry(this, errmsg
);
1117 if (recurType
!= RecurrenceEdit::NO_RECUR
)
1120 int longestRecurMinutes
= -1;
1121 int reminder
= mReminder
? mReminder
->minutes() : 0;
1122 if (reminder
&& !mReminder
->isOnceOnly())
1124 mRecurrenceEdit
->updateEvent(recurEvent
, false);
1125 longestRecurMinutes
= recurEvent
.longestRecurrenceInterval().asSeconds() / 60;
1126 if (longestRecurMinutes
&& reminder
>= longestRecurMinutes
)
1128 mTabs
->setCurrentIndex(mMainPageIndex
);
1129 mReminder
->setFocusOnCount();
1130 KAMessageBox::sorry(this, xi18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.",
1131 Reminder::i18n_chk_FirstRecurrenceOnly()));
1135 if (mRecurrenceEdit
->subRepetition())
1137 if (longestRecurMinutes
< 0)
1139 mRecurrenceEdit
->updateEvent(recurEvent
, false);
1140 longestRecurMinutes
= recurEvent
.longestRecurrenceInterval().asSeconds() / 60;
1142 if (longestRecurMinutes
> 0
1143 && recurEvent
.repetition().intervalMinutes() * recurEvent
.repetition().count() >= longestRecurMinutes
- reminder
)
1145 KAMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1146 mRecurrenceEdit
->activateSubRepetition(); // display the alarm repetition dialog again
1149 if (!recurEvent
.repetition().isDaily()
1150 && ((mTemplate
&& mTemplateAnyTime
->isChecked()) || (!mTemplate
&& mAlarmDateTime
.isDateOnly())))
1152 KAMessageBox::sorry(this, i18nc("@info", "For a repetition within the recurrence, its period must be in units of days or weeks for a date-only alarm"));
1153 mRecurrenceEdit
->activateSubRepetition(); // display the alarm repetition dialog again
1158 if (!checkText(mAlarmMessage
))
1161 mCollection
= Akonadi::Collection();
1162 // An item ID = -2 indicates that the caller already
1163 // knows which collection to use.
1164 if (mCollectionItemId
>= -1)
1166 if (mCollectionItemId
>= 0)
1168 mCollection
= AlarmCalendar::resources()->collectionForEvent(mCollectionItemId
);
1169 if (mCollection
.isValid())
1171 CalEvent::Type type
= mTemplate
? CalEvent::TEMPLATE
: CalEvent::ACTIVE
;
1172 if (!(AkonadiModel::instance()->types(mCollection
) & type
))
1173 mCollection
= Akonadi::Collection(); // event may have expired while dialog was open
1176 bool cancelled
= false;
1177 CalEvent::Type type
= mTemplate
? CalEvent::TEMPLATE
: CalEvent::ACTIVE
;
1178 if (CollectionControlModel::isWritableEnabled(mCollection
, type
) <= 0)
1179 mCollection
= CollectionControlModel::destination(type
, this, false, &cancelled
);
1180 if (!mCollection
.isValid())
1183 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1190 /******************************************************************************
1191 * Called when the Try button is clicked.
1192 * Display/execute the alarm immediately for the user to check its configuration.
1194 void EditAlarmDlg::slotTry()
1197 if (checkText(text
))
1199 if (!type_validate(true))
1202 setEvent(event
, text
, true);
1203 if (!mNewAlarm
&& !stateChanged())
1205 // It's an existing alarm which hasn't been changed yet:
1206 // enable KALARM_UID environment variable to be set.
1207 event
.setEventId(mEventId
);
1210 void* result
= theApp()->execAlarm(event
, event
.firstAlarm(), false, false);
1211 type_executedTry(text
, result
);
1215 /******************************************************************************
1216 * Called when the Load Template button is clicked.
1217 * Prompt to select a template and initialise the dialog with its contents.
1219 void EditAlarmDlg::slotHelp()
1221 KAEvent::Actions type
;
1225 case KAEvent::MESSAGE
: type
= KAEvent::ACT_DISPLAY
; break;
1226 case KAEvent::COMMAND
: type
= KAEvent::ACT_COMMAND
; break;
1227 case KAEvent::EMAIL
: type
= KAEvent::ACT_EMAIL
; break;
1228 case KAEvent::AUDIO
: type
= KAEvent::ACT_AUDIO
; break;
1232 // Use AutoQPointer to guard against crash on application exit while
1233 // the dialogue is still open. It prevents double deletion (both on
1234 // deletion of EditAlarmDlg, and on return from this function).
1235 AutoQPointer
<TemplatePickDlg
> dlg
= new TemplatePickDlg(type
, this);
1236 if (dlg
->exec() == QDialog::Accepted
)
1238 KAEvent event
= dlg
->selectedTemplate();
1243 /******************************************************************************
1244 * Called when the More Options or Less Options buttons are clicked.
1245 * Show/hide the optional options and swap the More/Less buttons, and save the
1246 * new setting as the default from now on.
1248 void EditAlarmDlg::slotDefault()
1250 showOptions(!mShowingMore
);
1251 KConfigGroup
config(KSharedConfig::openConfig(), EDIT_MORE_GROUP
);
1252 config
.writeEntry(EDIT_MORE_KEY
, mShowingMore
);
1255 /******************************************************************************
1256 * Show/hide the optional options and swap the More/Less buttons.
1258 void EditAlarmDlg::showOptions(bool more
)
1260 qCDebug(KALARM_LOG
) << (more
? "More" : "Less");
1263 mMoreOptions
->show();
1264 mMoreLessButton
->setText(i18nc("@action:Button", "Less Options <<"));
1268 mMoreOptions
->hide();
1269 mMoreLessButton
->setText(i18nc("@action:button", "More Options >>"));
1272 mTimeWidget
->showMoreOptions(more
);
1273 type_showOptions(more
);
1274 mRecurrenceEdit
->showMoreOptions(more
);
1275 mShowingMore
= more
;
1276 QTimer::singleShot(0, this, &EditAlarmDlg::slotResize
);
1279 /******************************************************************************
1280 * Called when the Change deferral button is clicked.
1282 void EditAlarmDlg::slotEditDeferral()
1287 Repetition repetition
= mRecurrenceEdit
->subRepetition();
1288 DateTime start
= mSavedEvent
->recurs() ? (mExpiredRecurrence
? DateTime() : mSavedEvent
->mainDateTime())
1289 : mTimeWidget
->getDateTime(Q_NULLPTR
, !repetition
, !mExpiredRecurrence
);
1290 if (!start
.isValid())
1292 if (!mExpiredRecurrence
)
1296 KDateTime now
= KDateTime::currentUtcDateTime();
1299 if (repetition
&& start
< now
)
1301 // Sub-repetition - find the time of the next one
1302 int repeatNum
= repetition
.isDaily()
1303 ? (start
.daysTo(now
) + repetition
.intervalDays() - 1) / repetition
.intervalDays()
1304 : (start
.secsTo(now
) + repetition
.intervalSeconds() - 1) / repetition
.intervalSeconds();
1305 if (repeatNum
> repetition
.count())
1307 mTimeWidget
->getDateTime(); // output the appropriate error message
1310 start
= repetition
.duration(repeatNum
).end(start
.kDateTime());
1314 bool deferred
= mDeferDateTime
.isValid();
1315 // Use AutoQPointer to guard against crash on application exit while
1316 // the dialogue is still open. It prevents double deletion (both on
1317 // deletion of EditAlarmDlg, and on return from this function).
1318 AutoQPointer
<DeferAlarmDlg
> deferDlg
= new DeferAlarmDlg((deferred
? mDeferDateTime
: DateTime(now
.addSecs(60).toTimeSpec(start
.timeSpec()))),
1319 start
.isDateOnly(), deferred
, this);
1320 deferDlg
->setObjectName(QStringLiteral("EditDeferDlg")); // used by LikeBack
1323 // Don't allow deferral past the next recurrence
1324 int reminder
= mReminder
? mReminder
->minutes() : 0;
1327 DateTime remindTime
= start
.addMins(-reminder
);
1328 if (KDateTime::currentUtcDateTime() < remindTime
)
1331 deferDlg
->setLimit(start
.addSecs(-60));
1333 if (deferDlg
->exec() == QDialog::Accepted
)
1335 mDeferDateTime
= deferDlg
->getDateTime();
1336 mDeferTimeLabel
->setText(mDeferDateTime
.isValid() ? mDeferDateTime
.formatLocale() : QString());
1341 /******************************************************************************
1342 * Called when the main page is shown.
1343 * Sets the focus widget to the first edit field.
1345 void EditAlarmDlg::slotShowMainPage()
1347 if (!mMainPageShown
)
1350 mTemplateName
->setFocus();
1351 mMainPageShown
= true;
1355 // Set scroll position to top, since it otherwise jumps randomly
1356 StackedScrollWidget
* main
= static_cast<StackedScrollWidget
*>(mTabs
->widget(0));
1357 main
->verticalScrollBar()->setValue(0);
1361 if (!mReadOnly
&& mRecurPageShown
&& mRecurrenceEdit
->repeatType() == RecurrenceEdit::AT_LOGIN
)
1362 mTimeWidget
->setDateTime(mRecurrenceEdit
->endDateTime());
1363 if (mReadOnly
|| mRecurrenceEdit
->isTimedRepeatType())
1364 mTimeWidget
->setMinDateTime(); // don't set a minimum date/time
1366 mTimeWidget
->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1370 /******************************************************************************
1371 * Called when the recurrence edit page is shown.
1372 * The recurrence defaults are set to correspond to the start date.
1373 * The first time, for a new alarm, the recurrence end date is set according to
1374 * the alarm start time.
1376 void EditAlarmDlg::slotShowRecurrenceEdit()
1378 mRecurPageIndex
= mTabs
->currentIndex();
1379 if (!mReadOnly
&& !mTemplate
)
1381 mAlarmDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
1382 KDateTime now
= KDateTime::currentDateTime(mAlarmDateTime
.timeSpec());
1383 bool expired
= (mAlarmDateTime
.effectiveKDateTime() < now
);
1384 if (mRecurSetDefaultEndDate
)
1386 mRecurrenceEdit
->setDefaultEndDate(expired
? now
.date() : mAlarmDateTime
.date());
1387 mRecurSetDefaultEndDate
= false;
1389 mRecurrenceEdit
->setStartDate(mAlarmDateTime
.date(), now
.date());
1390 if (mRecurrenceEdit
->repeatType() == RecurrenceEdit::AT_LOGIN
)
1391 mRecurrenceEdit
->setEndDateTime(expired
? now
: mAlarmDateTime
.kDateTime());
1393 mRecurPageShown
= true;
1396 /******************************************************************************
1397 * Called when the recurrence type selection changes.
1398 * Enables/disables date-only alarms as appropriate.
1399 * Enables/disables controls depending on at-login setting.
1401 void EditAlarmDlg::slotRecurTypeChange(int repeatType
)
1403 bool atLogin
= (mRecurrenceEdit
->repeatType() == RecurrenceEdit::AT_LOGIN
);
1406 bool recurs
= (mRecurrenceEdit
->repeatType() != RecurrenceEdit::NO_RECUR
);
1408 mDeferGroup
->setEnabled(recurs
);
1409 mTimeWidget
->enableAnyTime(!recurs
|| repeatType
!= RecurrenceEdit::SUBDAILY
);
1412 mAlarmDateTime
= mTimeWidget
->getDateTime(Q_NULLPTR
, false, false);
1413 mRecurrenceEdit
->setEndDateTime(mAlarmDateTime
.kDateTime());
1416 mReminder
->enableOnceOnly(recurs
&& !atLogin
);
1419 mReminder
->setAfterOnly(atLogin
);
1420 mLateCancel
->setEnabled(!atLogin
);
1421 if (mShowInKorganizer
)
1422 mShowInKorganizer
->setEnabled(!atLogin
);
1423 slotRecurFrequencyChange();
1426 /******************************************************************************
1427 * Called when the recurrence frequency selection changes, or the sub-
1428 * repetition interval changes.
1429 * Updates the recurrence frequency text.
1431 void EditAlarmDlg::slotRecurFrequencyChange()
1433 slotSetSubRepetition();
1435 mRecurrenceEdit
->updateEvent(event
, false);
1436 mTabs
->setTabText(mRecurPageIndex
, recurText(event
));
1439 /******************************************************************************
1440 * Called when the Repetition within Recurrence button has been pressed to
1441 * display the sub-repetition dialog.
1442 * Alarm repetition has the following restrictions:
1443 * 1) Not allowed for a repeat-at-login alarm
1444 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1445 * 3) The overall repeat duration must be less than the recurrence interval.
1447 void EditAlarmDlg::slotSetSubRepetition()
1449 bool dateOnly
= mTemplate
? mTemplateAnyTime
->isChecked() : mTimeWidget
->anyTime();
1450 mRecurrenceEdit
->setSubRepetition((mReminder
? mReminder
->minutes() : 0), dateOnly
);
1453 /******************************************************************************
1454 * Called when one of the template time radio buttons is clicked,
1455 * to enable or disable the template time entry spin boxes.
1457 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton
*)
1459 mTemplateTime
->setEnabled(mTemplateUseTime
->isChecked());
1460 mTemplateTimeAfter
->setEnabled(mTemplateUseTimeAfter
->isChecked());
1463 /******************************************************************************
1464 * Called when the "Any time" checkbox is toggled in the date/time widget.
1465 * Sets the advance reminder and late cancel units to days if any time is checked.
1467 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime
)
1469 if (mReminder
&& mReminder
->isReminder())
1470 mReminder
->setDateOnly(anyTime
);
1471 mLateCancel
->setDateOnly(anyTime
);
1474 bool EditAlarmDlg::dateOnly() const
1476 return mTimeWidget
? mTimeWidget
->anyTime() : mTemplateAnyTime
->isChecked();
1479 bool EditAlarmDlg::isTimedRecurrence() const
1481 return mRecurrenceEdit
->isTimedRepeatType();
1484 void EditAlarmDlg::showMainPage()
1486 mTabs
->setCurrentIndex(mMainPageIndex
);
1489 #include "moc_editdlg.cpp"
1490 #include "moc_editdlg_p.cpp"