Updated the Tools menu to reflect current build.
[kdepim.git] / kalarm / editdlg.cpp
blobd33c7f684119625aef998923a85a909a38a6f86d
1 /*
2 * editdlg.cpp - dialog to create or modify an alarm or alarm template
3 * Program: kalarm
4 * Copyright © 2001-2012 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "kalarm.h"
22 #include "editdlg.moc"
23 #include "editdlg_p.moc"
24 #include "editdlgtypes.h"
26 #include "alarmcalendar.h"
27 #ifdef USE_AKONADI
28 #include "collectionmodel.h"
29 #else
30 #include "alarmresources.h"
31 #endif
32 #include "alarmtimewidget.h"
33 #include "autoqpointer.h"
34 #include "buttongroup.h"
35 #include "checkbox.h"
36 #include "deferdlg.h"
37 #include "functions.h"
38 #include "kalarmapp.h"
39 #include "latecancel.h"
40 #include "lineedit.h"
41 #include "mainwindow.h"
42 #include "messagebox.h"
43 #include "packedlayout.h"
44 #include "preferences.h"
45 #include "radiobutton.h"
46 #include "recurrenceedit.h"
47 #include "reminder.h"
48 #include "shellprocess.h"
49 #include "spinbox.h"
50 #include "stackedwidgets.h"
51 #include "templatepickdlg.h"
52 #include "timeedit.h"
53 #include "timespinbox.h"
55 #include <libkdepim/misc/maillistdrag.h>
57 #include <kglobal.h>
58 #include <klocale.h>
59 #include <kconfig.h>
60 #include <kfiledialog.h>
61 #include <kpushbutton.h>
62 #include <khbox.h>
63 #include <kvbox.h>
64 #include <kwindowsystem.h>
65 #include <kdebug.h>
67 #include <QLabel>
68 #include <QDir>
69 #include <QStyle>
70 #include <QGroupBox>
71 #include <QPushButton>
72 #include <QGridLayout>
73 #include <QHBoxLayout>
74 #include <QVBoxLayout>
75 #include <QDragEnterEvent>
76 #include <QResizeEvent>
77 #include <QShowEvent>
78 #include <QStackedWidget>
79 #include <QScrollBar>
80 #include <QTimer>
82 using namespace KCal;
83 using namespace KAlarmCal;
85 static const char EDIT_DIALOG_NAME[] = "EditDialog";
86 static const char TEMPLATE_DIALOG_NAME[] = "EditTemplateDialog";
87 static const char EDIT_MORE_GROUP[] = "ShowOpts";
88 static const char EDIT_MORE_KEY[] = "EditMore";
89 static const int maxDelayTime = 99*60 + 59; // < 100 hours
91 inline QString recurText(const KAEvent& event)
93 QString r;
94 if (event.repetition())
95 r = QString::fromLatin1("%1 / %2").arg(event.recurrenceText()).arg(event.repetitionText());
96 else
97 r = event.recurrenceText();
98 return i18nc("@title:tab", "Recurrence - [%1]", r);
101 // Collect these widget labels together to ensure consistent wording and
102 // translations across different modules.
103 QString EditAlarmDlg::i18n_chk_ShowInKOrganizer() { return i18nc("@option:check", "Show in KOrganizer"); }
106 EditAlarmDlg* EditAlarmDlg::create(bool Template, Type type, QWidget* parent, GetResourceType getResource)
108 kDebug();
109 switch (type)
111 case DISPLAY: return new EditDisplayAlarmDlg(Template, parent, getResource);
112 case COMMAND: return new EditCommandAlarmDlg(Template, parent, getResource);
113 case EMAIL: return new EditEmailAlarmDlg(Template, parent, getResource);
114 case AUDIO: return new EditAudioAlarmDlg(Template, parent, getResource);
115 default: break;
117 return 0;
120 EditAlarmDlg* EditAlarmDlg::create(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
121 GetResourceType getResource, bool readOnly)
123 switch (event->actionTypes())
125 case KAEvent::ACT_COMMAND: return new EditCommandAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
126 case KAEvent::ACT_DISPLAY_COMMAND:
127 case KAEvent::ACT_DISPLAY: return new EditDisplayAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
128 case KAEvent::ACT_EMAIL: return new EditEmailAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
129 case KAEvent::ACT_AUDIO: return new EditAudioAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
130 default:
131 break;
133 return 0;
137 /******************************************************************************
138 * Constructor.
139 * Parameters:
140 * Template = true to edit/create an alarm template
141 * = false to edit/create an alarm.
142 * event != to initialise the dialog to show the specified event's data.
144 EditAlarmDlg::EditAlarmDlg(bool Template, KAEvent::SubAction action, QWidget* parent, GetResourceType getResource)
145 : KDialog(parent),
146 mAlarmType(action),
147 mMainPageShown(false),
148 mRecurPageShown(false),
149 mRecurSetDefaultEndDate(true),
150 mTemplateName(0),
151 mDeferGroup(0),
152 mDeferChangeButton(0),
153 mTimeWidget(0),
154 mShowInKorganizer(0),
155 #ifndef USE_AKONADI
156 mResource(0),
157 #endif
158 mDeferGroupHeight(0),
159 mTemplate(Template),
160 mNewAlarm(true),
161 mDesiredReadOnly(false),
162 mReadOnly(false),
163 mShowingMore(true),
164 mSavedEvent(0)
166 init(0, getResource);
169 EditAlarmDlg::EditAlarmDlg(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
170 GetResourceType getResource, bool readOnly)
171 : KDialog(parent),
172 mAlarmType(event->actionSubType()),
173 mMainPageShown(false),
174 mRecurPageShown(false),
175 mRecurSetDefaultEndDate(true),
176 mTemplateName(0),
177 mDeferGroup(0),
178 mDeferChangeButton(0),
179 mTimeWidget(0),
180 mShowInKorganizer(0),
181 #ifndef USE_AKONADI
182 mResource(0),
183 #endif
184 mDeferGroupHeight(0),
185 mEventId(newAlarm ? QString() : event->id()),
186 mTemplate(Template),
187 mNewAlarm(newAlarm),
188 mDesiredReadOnly(readOnly),
189 mReadOnly(readOnly),
190 mShowingMore(true),
191 mSavedEvent(0)
193 init(event, getResource);
196 void EditAlarmDlg::init(const KAEvent* event, GetResourceType getResource)
198 switch (getResource)
200 #ifdef USE_AKONADI
201 case RES_USE_EVENT_ID:
202 if (event)
204 mCollectionItemId = event->itemId();
205 break;
207 // fall through to RES_PROMPT
208 case RES_PROMPT:
209 mCollectionItemId = -1;
210 break;
211 case RES_IGNORE:
212 default:
213 mCollectionItemId = -2;
214 break;
215 #else
216 case RES_USE_EVENT_ID:
217 if (event)
219 mResourceEventId = event->id();
220 break;
222 // fall through to RES_PROMPT
223 case RES_PROMPT:
224 mResourceEventId = QString(""); // empty but non-null
225 break;
226 case RES_IGNORE:
227 default:
228 mResourceEventId.clear(); // null
229 break;
230 #endif
234 void EditAlarmDlg::init(const KAEvent* event)
236 setObjectName(mTemplate ? "TemplEditDlg" : "EditDlg"); // used by LikeBack
237 QString caption;
238 if (mReadOnly)
239 caption = mTemplate ? i18nc("@title:window", "Alarm Template [read-only]")
240 : event->expired() ? i18nc("@title:window", "Archived Alarm [read-only]")
241 : i18nc("@title:window", "Alarm [read-only]");
242 else
243 caption = type_caption();
244 setCaption(caption);
245 setButtons((mReadOnly ? Cancel|Try|Default : mTemplate ? Ok|Cancel|Try|Default : Ok|Cancel|Try|Help|Default));
246 setDefaultButton(mReadOnly ? Cancel : Ok);
247 setButtonText(Help, i18nc("@action:button", "Load Template..."));
248 setButtonIcon(Help, KIcon());
249 setButtonIcon(Default, KIcon());
250 connect(this, SIGNAL(tryClicked()), SLOT(slotTry()));
251 connect(this, SIGNAL(defaultClicked()), SLOT(slotDefault())); // More/Less Options button
252 connect(this, SIGNAL(helpClicked()), SLOT(slotHelp())); // Load Template button
253 KVBox* mainWidget = new KVBox(this);
254 mainWidget->setMargin(0);
255 setMainWidget(mainWidget);
256 if (mTemplate)
258 KHBox* box = new KHBox(mainWidget);
259 box->setMargin(0);
260 box->setSpacing(spacingHint());
261 QLabel* label = new QLabel(i18nc("@label:textbox", "Template name:"), box);
262 label->setFixedSize(label->sizeHint());
263 mTemplateName = new KLineEdit(box);
264 mTemplateName->setReadOnly(mReadOnly);
265 connect(mTemplateName, SIGNAL(userTextChanged(QString)), SLOT(contentsChanged()));
266 label->setBuddy(mTemplateName);
267 box->setWhatsThis(i18nc("@info:whatsthis", "Enter the name of the alarm template"));
268 box->setFixedHeight(box->sizeHint().height());
270 mTabs = new KTabWidget(mainWidget);
271 mTabScrollGroup = new StackedScrollGroup(this, mTabs);
273 StackedScrollWidget* mainScroll = new StackedScrollWidget(mTabScrollGroup);
274 mTabs->addTab(mainScroll, i18nc("@title:tab", "Alarm"));
275 mMainPageIndex = 0;
276 PageFrame* mainPage = new PageFrame(mainScroll);
277 mainScroll->setWidget(mainPage); // mainPage becomes the child of mainScroll
278 connect(mainPage, SIGNAL(shown()), SLOT(slotShowMainPage()));
279 QVBoxLayout* topLayout = new QVBoxLayout(mainPage);
280 topLayout->setMargin(marginHint());
281 topLayout->setSpacing(spacingHint());
283 // Recurrence tab
284 StackedScrollWidget* recurScroll = new StackedScrollWidget(mTabScrollGroup);
285 mTabs->addTab(recurScroll, QString());
286 mRecurPageIndex = 1;
287 KVBox* recurTab = new KVBox();
288 recurTab->setMargin(marginHint());
289 recurScroll->setWidget(recurTab); // recurTab becomes the child of recurScroll
290 mRecurrenceEdit = new RecurrenceEdit(mReadOnly, recurTab);
291 connect(mRecurrenceEdit, SIGNAL(shown()), SLOT(slotShowRecurrenceEdit()));
292 connect(mRecurrenceEdit, SIGNAL(typeChanged(int)), SLOT(slotRecurTypeChange(int)));
293 connect(mRecurrenceEdit, SIGNAL(frequencyChanged()), SLOT(slotRecurFrequencyChange()));
294 connect(mRecurrenceEdit, SIGNAL(repeatNeedsInitialisation()), SLOT(slotSetSubRepetition()));
295 connect(mRecurrenceEdit, SIGNAL(contentsChanged()), SLOT(contentsChanged()));
297 // Controls specific to the alarm type
298 QGroupBox* actionBox = new QGroupBox(i18nc("@title:group", "Action"), mainPage);
299 topLayout->addWidget(actionBox, 1);
300 QVBoxLayout* layout = new QVBoxLayout(actionBox);
301 layout->setMargin(marginHint());
302 layout->setSpacing(spacingHint());
304 type_init(actionBox, layout);
306 if (!mTemplate)
308 // Deferred date/time: visible only for a deferred recurring event.
309 mDeferGroup = new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage);
310 topLayout->addWidget(mDeferGroup);
311 QHBoxLayout* hlayout = new QHBoxLayout(mDeferGroup);
312 hlayout->setMargin(marginHint());
313 hlayout->setSpacing(spacingHint());
314 QLabel* label = new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup);
315 label->setFixedSize(label->sizeHint());
316 hlayout->addWidget(label);
317 mDeferTimeLabel = new QLabel(mDeferGroup);
318 hlayout->addWidget(mDeferTimeLabel);
320 mDeferChangeButton = new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup);
321 mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint());
322 connect(mDeferChangeButton, SIGNAL(clicked()), SLOT(slotEditDeferral()));
323 mDeferChangeButton->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral"));
324 hlayout->addWidget(mDeferChangeButton);
325 //?? mDeferGroup->addSpace(0);
328 QHBoxLayout* hlayout = new QHBoxLayout();
329 hlayout->setMargin(0);
330 topLayout->addLayout(hlayout);
332 // Date and time entry
333 if (mTemplate)
335 QGroupBox* templateTimeBox = new QGroupBox(i18nc("@title:group", "Time"), mainPage);
336 hlayout->addWidget(templateTimeBox);
337 QGridLayout* grid = new QGridLayout(templateTimeBox);
338 grid->setMargin(marginHint());
339 grid->setSpacing(spacingHint());
340 mTemplateTimeGroup = new ButtonGroup(templateTimeBox);
341 connect(mTemplateTimeGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(slotTemplateTimeType(QAbstractButton*)));
342 connect(mTemplateTimeGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(contentsChanged()));
344 mTemplateDefaultTime = new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox);
345 mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint());
346 mTemplateDefaultTime->setReadOnly(mReadOnly);
347 mTemplateDefaultTime->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. "
348 "The normal default start time will be used."));
349 mTemplateTimeGroup->addButton(mTemplateDefaultTime);
350 grid->addWidget(mTemplateDefaultTime, 0, 0, Qt::AlignLeft);
352 KHBox* box = new KHBox(templateTimeBox);
353 box->setMargin(0);
354 box->setSpacing(spacingHint());
355 mTemplateUseTime = new RadioButton(i18nc("@option:radio", "Time:"), box);
356 mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint());
357 mTemplateUseTime->setReadOnly(mReadOnly);
358 mTemplateUseTime->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template."));
359 mTemplateTimeGroup->addButton(mTemplateUseTime);
360 mTemplateTime = new TimeEdit(box);
361 mTemplateTime->setFixedSize(mTemplateTime->sizeHint());
362 mTemplateTime->setReadOnly(mReadOnly);
363 mTemplateTime->setWhatsThis(i18nc("@info:whatsthis",
364 "<para>Enter the start time for alarms based on this template.</para><para>%1</para>",
365 TimeSpinBox::shiftWhatsThis()));
366 connect(mTemplateTime, SIGNAL(valueChanged(int)), SLOT(contentsChanged()));
367 box->setStretchFactor(new QWidget(box), 1); // left adjust the controls
368 box->setFixedHeight(box->sizeHint().height());
369 grid->addWidget(box, 0, 1, Qt::AlignLeft);
371 mTemplateAnyTime = new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox);
372 mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint());
373 mTemplateAnyTime->setReadOnly(mReadOnly);
374 mTemplateAnyTime->setWhatsThis(i18nc("@info:whatsthis", "Set the <interface>Any time</interface> option for alarms based on this template."));
375 mTemplateTimeGroup->addButton(mTemplateAnyTime);
376 grid->addWidget(mTemplateAnyTime, 1, 0, Qt::AlignLeft);
378 box = new KHBox(templateTimeBox);
379 box->setMargin(0);
380 box->setSpacing(spacingHint());
381 mTemplateUseTimeAfter = new RadioButton(i18nc("@option:radio", "Time from now:"), box);
382 mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint());
383 mTemplateUseTimeAfter->setReadOnly(mReadOnly);
384 mTemplateUseTimeAfter->setWhatsThis(i18nc("@info:whatsthis",
385 "Set alarms based on this template to start after the specified time "
386 "interval from when the alarm is created."));
387 mTemplateTimeGroup->addButton(mTemplateUseTimeAfter);
388 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime, box);
389 mTemplateTimeAfter->setValue(1439);
390 mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint());
391 mTemplateTimeAfter->setReadOnly(mReadOnly);
392 connect(mTemplateTimeAfter, SIGNAL(valueChanged(int)), SLOT(contentsChanged()));
393 mTemplateTimeAfter->setWhatsThis(i18nc("@info:whatsthis", "<para>%1</para><para>%2</para>",
394 AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis()));
395 box->setFixedHeight(box->sizeHint().height());
396 grid->addWidget(box, 1, 1, Qt::AlignLeft);
398 hlayout->addStretch();
400 else
402 mTimeWidget = new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME, mainPage);
403 connect(mTimeWidget, SIGNAL(dateOnlyToggled(bool)), SLOT(slotAnyTimeToggled(bool)));
404 connect(mTimeWidget, SIGNAL(changed(KDateTime)), SLOT(contentsChanged()));
405 topLayout->addWidget(mTimeWidget);
408 // Optional controls depending on More/Less Options button
409 mMoreOptions = new QFrame(mainPage);
410 mMoreOptions->setFrameStyle(QFrame::NoFrame);
411 topLayout->addWidget(mMoreOptions);
412 QVBoxLayout* moreLayout = new QVBoxLayout(mMoreOptions);
413 moreLayout->setMargin(0);
414 moreLayout->setSpacing(spacingHint());
416 // Reminder
417 mReminder = createReminder(mMoreOptions);
418 if (mReminder)
420 mReminder->setFixedSize(mReminder->sizeHint());
421 connect(mReminder, SIGNAL(changed()), SLOT(contentsChanged()));
422 moreLayout->addWidget(mReminder, 0, Qt::AlignLeft);
423 if (mTimeWidget)
424 connect(mTimeWidget, SIGNAL(changed(KDateTime)), mReminder, SLOT(setDefaultUnits(KDateTime)));
427 // Late cancel selector - default = allow late display
428 mLateCancel = new LateCancelSelector(true, mMoreOptions);
429 connect(mLateCancel, SIGNAL(changed()), SLOT(contentsChanged()));
430 moreLayout->addWidget(mLateCancel, 0, Qt::AlignLeft);
432 PackedLayout* playout = new PackedLayout(Qt::AlignJustify);
433 playout->setSpacing(2*spacingHint());
434 moreLayout->addLayout(playout);
436 // Acknowledgement confirmation required - default = no confirmation
437 CheckBox* confirmAck = type_createConfirmAckCheckbox(mMoreOptions);
438 if (confirmAck)
440 confirmAck->setFixedSize(confirmAck->sizeHint());
441 connect(confirmAck, SIGNAL(toggled(bool)), SLOT(contentsChanged()));
442 playout->addWidget(confirmAck);
445 if (theApp()->korganizerEnabled())
447 // Show in KOrganizer checkbox
448 mShowInKorganizer = new CheckBox(i18n_chk_ShowInKOrganizer(), mMoreOptions);
449 mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint());
450 connect(mShowInKorganizer, SIGNAL(toggled(bool)), SLOT(contentsChanged()));
451 mShowInKorganizer->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar"));
452 playout->addWidget(mShowInKorganizer);
455 setButtonWhatsThis(Ok, i18nc("@info:whatsthis", "Schedule the alarm at the specified time."));
457 // Hide optional controls
458 KConfigGroup config(KGlobal::config(), EDIT_MORE_GROUP);
459 showOptions(config.readEntry(EDIT_MORE_KEY, false));
461 // Initialise the state of all controls according to the specified event, if any
462 initValues(event);
463 if (mTemplateName)
464 mTemplateName->setFocus();
466 if (!mNewAlarm)
468 // Save the initial state of all controls so that we can later tell if they have changed
469 saveState((event && (mTemplate || !event->isTemplate())) ? event : 0);
470 contentsChanged(); // enable/disable OK button
473 // Note the current desktop so that the dialog can be shown on it.
474 // If a main window is visible, the dialog will by KDE default always appear on its
475 // desktop. If the user invokes the dialog via the system tray on a different desktop,
476 // that can cause confusion.
477 mDesktop = KWindowSystem::currentDesktop();
480 EditAlarmDlg::~EditAlarmDlg()
482 delete mSavedEvent;
485 /******************************************************************************
486 * Initialise the dialog controls from the specified event.
488 void EditAlarmDlg::initValues(const KAEvent* event)
490 setReadOnly(mDesiredReadOnly);
492 mChanged = false;
493 mOnlyDeferred = false;
494 mExpiredRecurrence = false;
495 mLateCancel->showAutoClose(false);
496 bool deferGroupVisible = false;
497 if (event)
499 // Set the values to those for the specified event
500 if (mTemplate)
501 mTemplateName->setText(event->templateName());
502 bool recurs = event->recurs();
503 if ((recurs || event->repetition()) && !mTemplate && event->deferred())
505 deferGroupVisible = true;
506 mDeferDateTime = event->deferDateTime();
507 mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
508 mDeferGroup->show();
510 if (mTemplate)
512 // Editing a template
513 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
514 bool noTime = !afterTime;
515 bool useTime = !event->mainDateTime().isDateOnly();
516 RadioButton* button = noTime ? mTemplateDefaultTime :
517 (afterTime > 0) ? mTemplateUseTimeAfter :
518 useTime ? mTemplateUseTime : mTemplateAnyTime;
519 button->setChecked(true);
520 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
521 if (!noTime && useTime)
522 mTemplateTime->setValue(event->mainDateTime().kDateTime().time());
523 else
524 mTemplateTime->setValue(0);
526 else
528 if (event->isTemplate())
530 // Initialising from an alarm template: use current date
531 KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
532 int afterTime = event->templateAfterTime();
533 if (afterTime >= 0)
535 mTimeWidget->setDateTime(now.addSecs(afterTime * 60));
536 mTimeWidget->selectTimeFromNow();
538 else
540 KDateTime dt = event->startDateTime().kDateTime();
541 dt.setTimeSpec(Preferences::timeZone());
542 QDate d = now.date();
543 if (!dt.isDateOnly() && now.time() >= dt.time())
544 d = d.addDays(1); // alarm time has already passed, so use tomorrow
545 dt.setDate(d);
546 mTimeWidget->setDateTime(dt);
549 else
551 mExpiredRecurrence = recurs && event->mainExpired();
552 mTimeWidget->setDateTime(recurs || event->category() == CalEvent::ARCHIVED ? event->startDateTime()
553 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
557 KAEvent::SubAction action = event->actionSubType();
558 AlarmText altext;
559 if (event->commandScript())
560 altext.setScript(event->cleanText());
561 else
562 altext.setText(event->cleanText());
563 setAction(action, altext);
565 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
566 TimePeriod::HoursMinutes);
567 if (mShowInKorganizer)
568 mShowInKorganizer->setChecked(event->copyToKOrganizer());
569 type_initValues(event);
570 mRecurrenceEdit->set(*event); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
571 mTabs->setTabText(mRecurPageIndex, recurText(*event));
573 else
575 // Set the values to their defaults
576 KDateTime defaultTime = KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
577 if (mTemplate)
579 mTemplateDefaultTime->setChecked(true);
580 mTemplateTime->setValue(0);
581 mTemplateTimeAfter->setValue(1);
583 else
584 mTimeWidget->setDateTime(defaultTime);
585 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes);
586 if (mShowInKorganizer)
587 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
588 type_initValues(0);
589 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
590 slotRecurFrequencyChange(); // update the Recurrence text
592 if (mReminder && mTimeWidget)
593 mReminder->setDefaultUnits(mTimeWidget->getDateTime(0, false, false));
595 if (!deferGroupVisible && mDeferGroup)
596 mDeferGroup->hide();
598 bool empty = AlarmCalendar::resources()->events(CalEvent::TEMPLATE).isEmpty();
599 enableButton(Help, !empty); // Load Templates button
602 /******************************************************************************
603 * Initialise various values in the New Alarm dialogue.
605 void EditAlarmDlg::setTime(const DateTime& start)
607 mTimeWidget->setDateTime(start);
609 void EditAlarmDlg::setRecurrence(const KARecurrence& recur, int subRepeatInterval, int subRepeatCount)
611 KAEvent event;
612 event.setTime(mTimeWidget->getDateTime(0, false, false));
613 event.setRecurrence(recur);
614 event.setRepetition(Repetition(subRepeatInterval, subRepeatCount - 1));
615 mRecurrenceEdit->set(event);
617 void EditAlarmDlg::setRepeatAtLogin()
619 mRecurrenceEdit->setRepeatAtLogin();
621 void EditAlarmDlg::setLateCancel(int minutes)
623 mLateCancel->setMinutes(minutes, mTimeWidget->getDateTime(0, false, false).isDateOnly(),
624 TimePeriod::HoursMinutes);
626 void EditAlarmDlg::setShowInKOrganizer(bool show)
628 mShowInKorganizer->setChecked(show);
631 /******************************************************************************
632 * Set the read-only status of all non-template controls.
634 void EditAlarmDlg::setReadOnly(bool readOnly)
636 mReadOnly = readOnly;
638 if (mTimeWidget)
639 mTimeWidget->setReadOnly(readOnly);
640 mLateCancel->setReadOnly(readOnly);
641 if (mDeferChangeButton)
643 if (readOnly)
644 mDeferChangeButton->hide();
645 else
646 mDeferChangeButton->show();
648 if (mShowInKorganizer)
649 mShowInKorganizer->setReadOnly(readOnly);
652 /******************************************************************************
653 * Save the state of all controls.
655 void EditAlarmDlg::saveState(const KAEvent* event)
657 delete mSavedEvent;
658 mSavedEvent = 0;
659 if (event)
660 mSavedEvent = new KAEvent(*event);
661 if (mTemplate)
663 mSavedTemplateName = mTemplateName->text();
664 mSavedTemplateTimeType = mTemplateTimeGroup->checkedButton();
665 mSavedTemplateTime = mTemplateTime->time();
666 mSavedTemplateAfterTime = mTemplateTimeAfter->value();
668 checkText(mSavedTextFileCommandMessage, false);
669 if (mTimeWidget)
670 mSavedDateTime = mTimeWidget->getDateTime(0, false, false);
671 mSavedLateCancel = mLateCancel->minutes();
672 if (mShowInKorganizer)
673 mSavedShowInKorganizer = mShowInKorganizer->isChecked();
674 mSavedRecurrenceType = mRecurrenceEdit->repeatType();
675 mSavedDeferTime = mDeferDateTime.kDateTime();
678 /******************************************************************************
679 * Check whether any of the controls has changed state since the dialog was
680 * first displayed.
681 * Reply = true if any non-deferral controls have changed, or if it's a new event.
682 * = false if no non-deferral controls have changed. In this case,
683 * mOnlyDeferred indicates whether deferral controls may have changed.
685 bool EditAlarmDlg::stateChanged() const
687 mChanged = true;
688 mOnlyDeferred = false;
689 if (!mSavedEvent)
690 return true;
691 QString textFileCommandMessage;
692 checkText(textFileCommandMessage, false);
693 if (mTemplate)
695 if (mSavedTemplateName != mTemplateName->text()
696 || mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton()
697 || (mTemplateUseTime->isChecked() && mSavedTemplateTime != mTemplateTime->time())
698 || (mTemplateUseTimeAfter->isChecked() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
699 return true;
701 else
703 KDateTime dt = mTimeWidget->getDateTime(0, false, false);
704 if (mSavedDateTime.timeSpec() != dt.timeSpec() || mSavedDateTime != dt)
705 return true;
707 if (mSavedLateCancel != mLateCancel->minutes()
708 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
709 || textFileCommandMessage != mSavedTextFileCommandMessage
710 || mSavedRecurrenceType != mRecurrenceEdit->repeatType())
711 return true;
712 if (type_stateChanged())
713 return true;
714 if (mRecurrenceEdit->stateChanged())
715 return true;
716 if (mSavedEvent && mSavedEvent->deferred())
717 mOnlyDeferred = true;
718 mChanged = false;
719 return false;
722 /******************************************************************************
723 * Called whenever any of the controls changes state.
724 * Enable or disable the OK button depending on whether any controls have a
725 * different state from their initial state.
727 void EditAlarmDlg::contentsChanged()
729 // Don't do anything if it's a new alarm or we're still initialising
730 // (i.e. mSavedEvent null).
731 if (mSavedEvent && button(Ok))
732 button(Ok)->setEnabled(stateChanged() || mDeferDateTime.kDateTime() != mSavedDeferTime);
735 /******************************************************************************
736 * Get the currently entered dialog data.
737 * The data is returned in the supplied KAEvent instance.
738 * Reply = false if the only change has been to an existing deferral.
740 #ifdef USE_AKONADI
741 bool EditAlarmDlg::getEvent(KAEvent& event, Akonadi::Collection& collection)
742 #else
743 bool EditAlarmDlg::getEvent(KAEvent& event, AlarmResource*& resource)
744 #endif
746 #ifdef USE_AKONADI
747 collection = mCollection;
748 #else
749 resource = mResource;
750 #endif
751 if (mChanged)
753 // It's a new event, or the edit controls have changed
754 setEvent(event, mAlarmMessage, false);
755 return true;
758 // Only the deferral time may have changed
759 event = *mSavedEvent;
760 if (mOnlyDeferred)
762 // Just modify the original event, to avoid expired recurring events
763 // being returned as rubbish.
764 if (mDeferDateTime.isValid())
765 event.defer(mDeferDateTime, event.reminderDeferral(), false);
766 else
767 event.cancelDefer();
769 return false;
772 /******************************************************************************
773 * Extract the data in the dialog and set up a KAEvent from it.
774 * If 'trial' is true, the event is set up for a simple one-off test, ignoring
775 * recurrence, reminder, template etc. data.
777 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial)
779 KDateTime dt;
780 if (!trial)
782 if (!mTemplate)
783 dt = mAlarmDateTime.effectiveKDateTime();
784 else if (mTemplateUseTime->isChecked())
785 dt = KDateTime(QDate(2000,1,1), mTemplateTime->time());
788 int lateCancel = (trial || !mLateCancel->isEnabled()) ? 0 : mLateCancel->minutes();
789 type_setEvent(event, dt, text, lateCancel, trial);
791 if (!trial)
793 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
795 mRecurrenceEdit->updateEvent(event, !mTemplate);
796 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
797 bool dateOnly = mAlarmDateTime.isDateOnly();
798 if ((dateOnly && mAlarmDateTime.date() < now.date())
799 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
801 // A timed recurrence has an entered start date which has
802 // already expired, so we must adjust the next repetition.
803 event.setNextOccurrence(now);
805 mAlarmDateTime = event.startDateTime();
806 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime)
808 bool deferral = true;
809 bool deferReminder = false;
810 int reminder = mReminder ? mReminder->minutes() : 0;
811 if (reminder)
813 DateTime remindTime = mAlarmDateTime.addMins(-reminder);
814 if (mDeferDateTime >= remindTime)
816 if (remindTime > KDateTime::currentUtcDateTime())
817 deferral = false; // ignore deferral if it's after next reminder
818 else if (mDeferDateTime > remindTime)
819 deferReminder = true; // it's the reminder which is being deferred
822 if (deferral)
823 event.defer(mDeferDateTime, deferReminder, false);
826 if (mTemplate)
828 int afterTime = mTemplateDefaultTime->isChecked() ? 0
829 : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1;
830 event.setTemplate(mTemplateName->text(), afterTime);
835 /******************************************************************************
836 * Get the currently specified alarm flag bits.
838 KAEvent::Flags EditAlarmDlg::getAlarmFlags() const
840 KAEvent::Flags flags(0);
841 if (mShowInKorganizer && mShowInKorganizer->isEnabled() && mShowInKorganizer->isChecked())
842 flags |= KAEvent::COPY_KORGANIZER;
843 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
844 flags |= KAEvent::REPEAT_AT_LOGIN;
845 if (mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly())
846 flags |= KAEvent::ANY_TIME;
847 return flags;
850 /******************************************************************************
851 * Called when the dialog is displayed.
852 * The first time through, sets the size to the same as the last time it was
853 * displayed.
855 void EditAlarmDlg::showEvent(QShowEvent* se)
857 KDialog::showEvent(se);
858 if (!mDeferGroupHeight)
860 if (mDeferGroup)
861 mDeferGroupHeight = mDeferGroup->height() + spacingHint();
862 QSize s;
863 if (KAlarm::readConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s))
865 bool defer = mDeferGroup && !mDeferGroup->isHidden();
866 s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0));
867 if (!defer)
868 mTabScrollGroup->setSized();
869 resize(s);
872 slotResize();
873 KWindowSystem::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user
876 /******************************************************************************
877 * Called when the dialog is closed.
879 void EditAlarmDlg::closeEvent(QCloseEvent* ce)
881 emit rejected();
882 KDialog::closeEvent(ce);
885 /******************************************************************************
886 * Update the tab sizes (again) and if the resized dialog height is greater
887 * than the minimum, resize it again. This is necessary because (a) resizing
888 * tabs doesn't always work properly the first time, and (b) resizing to the
889 * minimum size hint doesn't always work either.
891 void EditAlarmDlg::slotResize()
893 QSize s = mTabScrollGroup->adjustSize(true);
894 s = minimumSizeHint();
895 if (height() > s.height())
897 // Resize to slightly greater than the minimum height.
898 // This is for some unknown reason necessary, since
899 // sometimes resizing to the minimum height fails.
900 resize(s.width(), s.height() + 2);
904 /******************************************************************************
905 * Called when the dialog's size has changed.
906 * Records the new size (adjusted to ignore the optional height of the deferred
907 * time edit widget) in the config file.
909 void EditAlarmDlg::resizeEvent(QResizeEvent* re)
911 if (isVisible() && mDeferGroupHeight)
913 QSize s = re->size();
914 s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
915 KAlarm::writeConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s);
917 KDialog::resizeEvent(re);
920 /******************************************************************************
921 * Called when any button is clicked.
923 void EditAlarmDlg::slotButtonClicked(int button)
925 if (button == Ok)
927 if (validate())
928 accept();
930 else
931 KDialog::slotButtonClicked(button);
934 /******************************************************************************
935 * Called when the OK button is clicked.
936 * Validate the input data.
938 bool EditAlarmDlg::validate()
940 if (!stateChanged())
942 // No changes have been made except possibly to an existing deferral
943 if (!mOnlyDeferred)
944 reject();
945 return mOnlyDeferred;
947 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
948 if (mTimeWidget
949 && mTabs->currentIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN)
950 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
951 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login?
952 if (mTemplate)
954 // Check that the template name is not blank and is unique
955 QString errmsg;
956 QString name = mTemplateName->text();
957 if (name.isEmpty())
958 errmsg = i18nc("@info", "You must enter a name for the alarm template");
959 else if (name != mSavedTemplateName)
961 if (AlarmCalendar::resources()->templateEvent(name))
962 errmsg = i18nc("@info", "Template name is already in use");
964 if (!errmsg.isEmpty())
966 mTemplateName->setFocus();
967 KAMessageBox::sorry(this, errmsg);
968 return false;
971 else if (mTimeWidget)
973 QWidget* errWidget;
974 mAlarmDateTime = mTimeWidget->getDateTime(0, !timedRecurrence, false, &errWidget);
975 if (errWidget)
977 // It's more than just an existing deferral being changed, so the time matters
978 mTabs->setCurrentIndex(mMainPageIndex);
979 errWidget->setFocus();
980 mTimeWidget->getDateTime(); // display the error message now
981 return false;
984 if (!type_validate(false))
985 return false;
987 if (!mTemplate)
989 if (mChanged && mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
991 // Check whether the start date/time must be adjusted
992 // to match the recurrence specification.
993 DateTime dt = mAlarmDateTime; // setEvent() changes mAlarmDateTime
994 KAEvent event;
995 setEvent(event, mAlarmMessage, false);
996 mAlarmDateTime = dt; // restore
997 KDateTime pre = dt.effectiveKDateTime();
998 bool dateOnly = dt.isDateOnly();
999 if (dateOnly)
1000 pre = pre.addDays(-1);
1001 else
1002 pre = pre.addSecs(-1);
1003 DateTime next;
1004 event.nextOccurrence(pre, next, KAEvent::IGNORE_REPETITION);
1005 if (next != dt)
1007 QString prompt = dateOnly ? i18nc("@info The parameter is a date value",
1008 "The start date does not match the alarm's recurrence pattern, "
1009 "so it will be adjusted to the date of the next recurrence (%1).",
1010 KGlobal::locale()->formatDate(next.date(), KLocale::ShortDate))
1011 : i18nc("@info The parameter is a date/time value",
1012 "The start date/time does not match the alarm's recurrence pattern, "
1013 "so it will be adjusted to the date/time of the next recurrence (%1).",
1014 KGlobal::locale()->formatDateTime(next.kDateTime(), KLocale::ShortDate));
1015 if (KAMessageBox::warningContinueCancel(this, prompt) != KMessageBox::Continue)
1016 return false;
1020 if (timedRecurrence)
1022 KAEvent event;
1023 #ifdef USE_AKONADI
1024 Akonadi::Collection c;
1025 getEvent(event, c); // this may adjust mAlarmDateTime
1026 #else
1027 AlarmResource* r;
1028 getEvent(event, r); // this may adjust mAlarmDateTime
1029 #endif
1030 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1031 bool dateOnly = mAlarmDateTime.isDateOnly();
1032 if ((dateOnly && mAlarmDateTime.date() < now.date())
1033 || (!dateOnly && mAlarmDateTime.kDateTime() < now))
1035 // A timed recurrence has an entered start date which
1036 // has already expired, so we must adjust it.
1037 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1039 KAMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
1040 return false;
1042 if (event.workTimeOnly() && !event.nextTrigger(KAEvent::DISPLAY_TRIGGER).isValid())
1044 if (KAMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
1045 != KMessageBox::Continue)
1046 return false;
1050 QString errmsg;
1051 QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg);
1052 if (errWidget)
1054 mTabs->setCurrentIndex(mRecurPageIndex);
1055 errWidget->setFocus();
1056 KAMessageBox::sorry(this, errmsg);
1057 return false;
1060 if (recurType != RecurrenceEdit::NO_RECUR)
1062 KAEvent recurEvent;
1063 int longestRecurMinutes = -1;
1064 int reminder = mReminder ? mReminder->minutes() : 0;
1065 if (reminder && !mReminder->isOnceOnly())
1067 mRecurrenceEdit->updateEvent(recurEvent, false);
1068 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1069 if (longestRecurMinutes && reminder >= longestRecurMinutes)
1071 mTabs->setCurrentIndex(mMainPageIndex);
1072 mReminder->setFocusOnCount();
1073 KAMessageBox::sorry(this, i18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked.",
1074 Reminder::i18n_chk_FirstRecurrenceOnly()));
1075 return false;
1078 if (mRecurrenceEdit->subRepetition())
1080 if (longestRecurMinutes < 0)
1082 mRecurrenceEdit->updateEvent(recurEvent, false);
1083 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
1085 if (longestRecurMinutes > 0
1086 && recurEvent.repetition().intervalMinutes() * recurEvent.repetition().count() >= longestRecurMinutes - reminder)
1088 KAMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1089 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1090 return false;
1092 if (!recurEvent.repetition().isDaily()
1093 && ((mTemplate && mTemplateAnyTime->isChecked()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1095 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"));
1096 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1097 return false;
1101 if (!checkText(mAlarmMessage))
1102 return false;
1104 #ifdef USE_AKONADI
1105 mCollection = Akonadi::Collection();
1106 // An item ID = -2 indicates that the caller already
1107 // knows which collection to use.
1108 if (mCollectionItemId >= -1)
1110 if (mCollectionItemId >= 0)
1112 mCollection = AlarmCalendar::resources()->collectionForEvent(mCollectionItemId);
1113 if (mCollection.isValid())
1115 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1116 if (!(AkonadiModel::instance()->types(mCollection) & type))
1117 mCollection = Akonadi::Collection(); // event may have expired while dialog was open
1120 bool cancelled = false;
1121 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1122 if (CollectionControlModel::isWritableEnabled(mCollection, type) <= 0)
1123 mCollection = CollectionControlModel::destination(type, this, false, &cancelled);
1124 if (!mCollection.isValid())
1126 if (!cancelled)
1127 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1128 return false;
1131 #else
1132 mResource = 0;
1133 // A null resource event ID indicates that the caller already
1134 // knows which resource to use.
1135 if (!mResourceEventId.isNull())
1137 if (!mResourceEventId.isEmpty())
1139 mResource = AlarmCalendar::resources()->resourceForEvent(mResourceEventId);
1140 if (mResource)
1142 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1143 if (mResource->alarmType() != type)
1144 mResource = 0; // event may have expired while dialog was open
1147 bool cancelled = false;
1148 if (!mResource || !mResource->writable())
1150 CalEvent::Type type = mTemplate ? CalEvent::TEMPLATE : CalEvent::ACTIVE;
1151 mResource = AlarmResources::instance()->destination(type, this, false, &cancelled);
1153 if (!mResource)
1155 if (!cancelled)
1156 KAMessageBox::sorry(this, i18nc("@info", "You must select a calendar to save the alarm in"));
1157 return false;
1160 #endif
1161 return true;
1164 /******************************************************************************
1165 * Called when the Try button is clicked.
1166 * Display/execute the alarm immediately for the user to check its configuration.
1168 void EditAlarmDlg::slotTry()
1170 QString text;
1171 if (checkText(text))
1173 if (!type_validate(true))
1174 return;
1175 KAEvent event;
1176 setEvent(event, text, true);
1177 if (!mNewAlarm && !stateChanged())
1179 // It's an existing alarm which hasn't been changed yet:
1180 // enable KALARM_UID environment variable to be set.
1181 event.setEventId(mEventId);
1183 type_aboutToTry();
1184 void* result = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1185 type_executedTry(text, result);
1189 /******************************************************************************
1190 * Called when the Load Template button is clicked.
1191 * Prompt to select a template and initialise the dialog with its contents.
1193 void EditAlarmDlg::slotHelp()
1195 KAEvent::Actions type;
1196 switch (mAlarmType)
1198 case KAEvent::FILE:
1199 case KAEvent::MESSAGE: type = KAEvent::ACT_DISPLAY; break;
1200 case KAEvent::COMMAND: type = KAEvent::ACT_COMMAND; break;
1201 case KAEvent::EMAIL: type = KAEvent::ACT_EMAIL; break;
1202 case KAEvent::AUDIO: type = KAEvent::ACT_AUDIO; break;
1203 default:
1204 return;
1206 // Use AutoQPointer to guard against crash on application exit while
1207 // the dialogue is still open. It prevents double deletion (both on
1208 // deletion of EditAlarmDlg, and on return from this function).
1209 AutoQPointer<TemplatePickDlg> dlg = new TemplatePickDlg(type, this);
1210 if (dlg->exec() == QDialog::Accepted)
1211 #ifdef USE_AKONADI
1213 KAEvent event = dlg->selectedTemplate();
1214 initValues(&event);
1216 #else
1217 initValues(dlg->selectedTemplate());
1218 #endif
1221 /******************************************************************************
1222 * Called when the More Options or Less Options buttons are clicked.
1223 * Show/hide the optional options and swap the More/Less buttons, and save the
1224 * new setting as the default from now on.
1226 void EditAlarmDlg::slotDefault()
1228 showOptions(!mShowingMore);
1229 KConfigGroup config(KGlobal::config(), EDIT_MORE_GROUP);
1230 config.writeEntry(EDIT_MORE_KEY, mShowingMore);
1233 /******************************************************************************
1234 * Show/hide the optional options and swap the More/Less buttons.
1236 void EditAlarmDlg::showOptions(bool more)
1238 kDebug() << (more ? "More" : "Less");
1239 if (more)
1241 mMoreOptions->show();
1242 setButtonText(Default, i18nc("@action:button", "Less Options <<"));
1244 else
1246 mMoreOptions->hide();
1247 setButtonText(Default, i18nc("@action:button", "More Options >>"));
1249 if (mTimeWidget)
1250 mTimeWidget->showMoreOptions(more);
1251 type_showOptions(more);
1252 mRecurrenceEdit->showMoreOptions(more);
1253 mShowingMore = more;
1254 QTimer::singleShot(0, this, SLOT(slotResize()));
1257 /******************************************************************************
1258 * Called when the Change deferral button is clicked.
1260 void EditAlarmDlg::slotEditDeferral()
1262 if (!mTimeWidget)
1263 return;
1264 bool limit = true;
1265 Repetition repetition = mRecurrenceEdit->subRepetition();
1266 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1267 : mTimeWidget->getDateTime(0, !repetition, !mExpiredRecurrence);
1268 if (!start.isValid())
1270 if (!mExpiredRecurrence)
1271 return;
1272 limit = false;
1274 KDateTime now = KDateTime::currentUtcDateTime();
1275 if (limit)
1277 if (repetition && start < now)
1279 // Sub-repetition - find the time of the next one
1280 int repeatNum = repetition.isDaily()
1281 ? (start.daysTo(now) + repetition.intervalDays() - 1) / repetition.intervalDays()
1282 : (start.secsTo(now) + repetition.intervalSeconds() - 1) / repetition.intervalSeconds();
1283 if (repeatNum > repetition.count())
1285 mTimeWidget->getDateTime(); // output the appropriate error message
1286 return;
1288 start = repetition.duration(repeatNum).end(start.kDateTime());
1292 bool deferred = mDeferDateTime.isValid();
1293 // Use AutoQPointer to guard against crash on application exit while
1294 // the dialogue is still open. It prevents double deletion (both on
1295 // deletion of EditAlarmDlg, and on return from this function).
1296 AutoQPointer<DeferAlarmDlg> deferDlg = new DeferAlarmDlg((deferred ? mDeferDateTime : DateTime(now.addSecs(60).toTimeSpec(start.timeSpec()))),
1297 start.isDateOnly(), deferred, this);
1298 deferDlg->setObjectName("EditDeferDlg"); // used by LikeBack
1299 if (limit)
1301 // Don't allow deferral past the next recurrence
1302 int reminder = mReminder ? mReminder->minutes() : 0;
1303 if (reminder)
1305 DateTime remindTime = start.addMins(-reminder);
1306 if (KDateTime::currentUtcDateTime() < remindTime)
1307 start = remindTime;
1309 deferDlg->setLimit(start.addSecs(-60));
1311 if (deferDlg->exec() == QDialog::Accepted)
1313 mDeferDateTime = deferDlg->getDateTime();
1314 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString());
1315 contentsChanged();
1319 /******************************************************************************
1320 * Called when the main page is shown.
1321 * Sets the focus widget to the first edit field.
1323 void EditAlarmDlg::slotShowMainPage()
1325 if (!mMainPageShown)
1327 if (mTemplateName)
1328 mTemplateName->setFocus();
1329 mMainPageShown = true;
1331 else
1333 // Set scroll position to top, since it otherwise jumps randomly
1334 StackedScrollWidget* main = static_cast<StackedScrollWidget*>(mTabs->widget(0));
1335 main->verticalScrollBar()->setValue(0);
1337 if (mTimeWidget)
1339 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1340 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1341 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1342 mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1343 else
1344 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1348 /******************************************************************************
1349 * Called when the recurrence edit page is shown.
1350 * The recurrence defaults are set to correspond to the start date.
1351 * The first time, for a new alarm, the recurrence end date is set according to
1352 * the alarm start time.
1354 void EditAlarmDlg::slotShowRecurrenceEdit()
1356 mRecurPageIndex = mTabs->currentIndex();
1357 if (!mReadOnly && !mTemplate)
1359 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1360 KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
1361 bool expired = (mAlarmDateTime.effectiveKDateTime() < now);
1362 if (mRecurSetDefaultEndDate)
1364 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1365 mRecurSetDefaultEndDate = false;
1367 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1368 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1369 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime.kDateTime());
1371 mRecurPageShown = true;
1374 /******************************************************************************
1375 * Called when the recurrence type selection changes.
1376 * Enables/disables date-only alarms as appropriate.
1377 * Enables/disables controls depending on at-login setting.
1379 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1381 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1382 if (!mTemplate)
1384 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1385 if (mDeferGroup)
1386 mDeferGroup->setEnabled(recurs);
1387 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1388 if (atLogin)
1390 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1391 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.kDateTime());
1393 if (mReminder)
1394 mReminder->enableOnceOnly(recurs && !atLogin);
1396 if (mReminder)
1397 mReminder->setAfterOnly(atLogin);
1398 mLateCancel->setEnabled(!atLogin);
1399 if (mShowInKorganizer)
1400 mShowInKorganizer->setEnabled(!atLogin);
1401 slotRecurFrequencyChange();
1404 /******************************************************************************
1405 * Called when the recurrence frequency selection changes, or the sub-
1406 * repetition interval changes.
1407 * Updates the recurrence frequency text.
1409 void EditAlarmDlg::slotRecurFrequencyChange()
1411 slotSetSubRepetition();
1412 KAEvent event;
1413 mRecurrenceEdit->updateEvent(event, false);
1414 mTabs->setTabText(mRecurPageIndex, recurText(event));
1417 /******************************************************************************
1418 * Called when the Repetition within Recurrence button has been pressed to
1419 * display the sub-repetition dialog.
1420 * Alarm repetition has the following restrictions:
1421 * 1) Not allowed for a repeat-at-login alarm
1422 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1423 * 3) The overall repeat duration must be less than the recurrence interval.
1425 void EditAlarmDlg::slotSetSubRepetition()
1427 bool dateOnly = mTemplate ? mTemplateAnyTime->isChecked() : mTimeWidget->anyTime();
1428 mRecurrenceEdit->setSubRepetition((mReminder ? mReminder->minutes() : 0), dateOnly);
1431 /******************************************************************************
1432 * Called when one of the template time radio buttons is clicked,
1433 * to enable or disable the template time entry spin boxes.
1435 void EditAlarmDlg::slotTemplateTimeType(QAbstractButton*)
1437 mTemplateTime->setEnabled(mTemplateUseTime->isChecked());
1438 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isChecked());
1441 /******************************************************************************
1442 * Called when the "Any time" checkbox is toggled in the date/time widget.
1443 * Sets the advance reminder and late cancel units to days if any time is checked.
1445 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1447 if (mReminder && mReminder->isReminder())
1448 mReminder->setDateOnly(anyTime);
1449 mLateCancel->setDateOnly(anyTime);
1452 bool EditAlarmDlg::dateOnly() const
1454 return mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isChecked();
1457 bool EditAlarmDlg::isTimedRecurrence() const
1459 return mRecurrenceEdit->isTimedRepeatType();
1462 void EditAlarmDlg::showMainPage()
1464 mTabs->setCurrentIndex(mMainPageIndex);
1467 // vim: et sw=4: