Incidenceeditor

incidencealarm.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Bertjan Broeksema <broeksema@kde.org>
3 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "incidencealarm.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include "alarmdialog.h"
12#include "alarmpresets.h"
13#include "incidencedatetime.h"
14#include "ui_dialogdesktop.h"
15
16#include <CalendarSupport/KCalPrefs>
17
18using namespace IncidenceEditorNG;
19using namespace CalendarSupport;
20
21IncidenceAlarm::IncidenceAlarm(IncidenceDateTime *dateTime, Ui::EventOrTodoDesktop *ui)
22 : mUi(ui)
23 , mDateTime(dateTime)
24{
25 setObjectName("IncidenceAlarm"_L1);
26
27 mUi->mAlarmPresetCombo->insertItems(0, AlarmPresets::availablePresets());
28 mUi->mAlarmPresetCombo->setCurrentIndex(AlarmPresets::defaultPresetIndex());
29 updateButtons();
30
31 connect(mDateTime, &IncidenceDateTime::startDateTimeToggled, this, &IncidenceAlarm::handleDateTimeToggle);
32 connect(mDateTime, &IncidenceDateTime::endDateTimeToggled, this, &IncidenceAlarm::handleDateTimeToggle);
33 connect(mUi->mAlarmAddPresetButton, &QPushButton::clicked, this, &IncidenceAlarm::newAlarmFromPreset);
34 connect(mUi->mAlarmList, &QListWidget::itemSelectionChanged, this, &IncidenceAlarm::updateButtons);
35 connect(mUi->mAlarmList, &QListWidget::itemDoubleClicked, this, &IncidenceAlarm::editCurrentAlarm);
36 connect(mUi->mAlarmNewButton, &QPushButton::clicked, this, &IncidenceAlarm::newAlarm);
37 connect(mUi->mAlarmConfigureButton, &QPushButton::clicked, this, &IncidenceAlarm::editCurrentAlarm);
38 connect(mUi->mAlarmToggleButton, &QPushButton::clicked, this, &IncidenceAlarm::toggleCurrentAlarm);
39 connect(mUi->mAlarmRemoveButton, &QPushButton::clicked, this, &IncidenceAlarm::removeCurrentAlarm);
40}
41
42void IncidenceAlarm::load(const KCalendarCore::Incidence::Ptr &incidence)
43{
44 mLoadedIncidence = incidence;
45 // We must be sure that the date/time in mDateTime is the correct date time.
46 // So don't depend on CombinedIncidenceEditor or whatever external factor to
47 // load the date/time before loading the recurrence
48 mDateTime->load(incidence);
49
50 mAlarms.clear();
51 const auto lstAlarms = incidence->alarms();
54 }
55
57 if (mIsTodo) {
58 mUi->mAlarmPresetCombo->clear();
59 mUi->mAlarmPresetCombo->addItems(AlarmPresets::availablePresets(AlarmPresets::BeforeEnd));
60 } else {
61 mUi->mAlarmPresetCombo->clear();
62 mUi->mAlarmPresetCombo->addItems(AlarmPresets::availablePresets(AlarmPresets::BeforeStart));
63 }
64 mUi->mAlarmPresetCombo->setCurrentIndex(AlarmPresets::defaultPresetIndex());
65
66 handleDateTimeToggle();
67 mWasDirty = false;
68
69 updateAlarmList();
70}
71
72void IncidenceAlarm::save(const KCalendarCore::Incidence::Ptr &incidence)
73{
74 incidence->clearAlarms();
78 al->setParent(incidence.data());
79 // We need to make sure that both lists are the same in the end for isDirty.
80 Q_ASSERT(*al == *(*it));
81 incidence->addAlarm(al);
82 }
83}
84
85bool IncidenceAlarm::isDirty() const
86{
87 if (mLoadedIncidence->alarms().count() != mAlarms.count()) {
88 return true;
89 }
90
91 if (!mLoadedIncidence->alarms().isEmpty()) {
92 const KCalendarCore::Alarm::List initialAlarms = mLoadedIncidence->alarms();
93
94 if (initialAlarms.count() != mAlarms.count()) {
95 return true; // The number of alarms has changed
96 }
97
98 // Note: Not the most efficient algorithm but I'm assuming that we're only
99 // dealing with a couple, at most tens of alarms. The idea is we check
100 // if all currently enabled alarms are also in the incidence. The
101 // disabled alarms are not changed by our code at all, so we assume that
102 // they're still there.
103 for (const KCalendarCore::Alarm::Ptr &alarm : std::as_const(mAlarms)) {
104 bool found = false;
106 if (*alarm == *initialAlarm) {
107 found = true;
108 break;
109 }
110 }
111
112 if (!found) {
113 // There was an alarm in the mLoadedIncidence->alarms() that wasn't found
114 // in mLastAlarms. This means that one of the alarms was modified.
115 return true;
116 }
117 }
118 }
119
120 return false;
121}
122
123void IncidenceAlarm::editCurrentAlarm()
124{
125 KCalendarCore::Alarm::Ptr currentAlarm = mAlarms.at(mUi->mAlarmList->currentRow());
126
127 QPointer<AlarmDialog> dialog(new AlarmDialog(mLoadedIncidence->type(), mUi->mTabWidget));
128 dialog->load(currentAlarm);
129
130 dialog->setAllowBeginReminders(mDateTime->startDateTimeEnabled());
131 dialog->setAllowEndReminders(mDateTime->endDateTimeEnabled());
132
133 if (dialog->exec() == QDialog::Accepted) {
134 dialog->save(currentAlarm);
135 updateAlarmList();
137 }
138 delete dialog;
139}
140
141void IncidenceAlarm::handleDateTimeToggle()
142{
143 QWidget *parent = mUi->mAlarmPresetCombo->parentWidget(); // the parent of a toplevel widget
144 if (parent) {
145 parent->setEnabled(mDateTime->startDateTimeEnabled() || mDateTime->endDateTimeEnabled());
146 }
147
148 mUi->mAlarmPresetCombo->setEnabled(mDateTime->endDateTimeEnabled());
149 mUi->mAlarmAddPresetButton->setEnabled(mDateTime->endDateTimeEnabled());
150
151 mUi->mQuickAddReminderLabel->setEnabled(mDateTime->endDateTimeEnabled());
152}
153
154void IncidenceAlarm::newAlarm()
155{
156 QPointer<AlarmDialog> dialog(new AlarmDialog(mLoadedIncidence->type(), mUi->mTabWidget));
157 const int reminderOffset = KCalPrefs::instance()->reminderTime();
158
159 if (reminderOffset >= 0) {
160 dialog->setOffset(reminderOffset);
161 } else {
162 dialog->setOffset(DEFAULT_REMINDER_OFFSET);
163 }
164 dialog->setUnit(AlarmDialog::Minutes);
165 if (mIsTodo && mDateTime->endDateTimeEnabled()) {
166 dialog->setWhen(AlarmDialog::BeforeEnd);
167 } else {
168 dialog->setWhen(AlarmDialog::BeforeStart);
169 }
170
171 dialog->setAllowBeginReminders(mDateTime->startDateTimeEnabled());
172 dialog->setAllowEndReminders(mDateTime->endDateTimeEnabled());
173
174 if (dialog->exec() == QDialog::Accepted) {
175 KCalendarCore::Alarm::Ptr newAlarm(new KCalendarCore::Alarm(nullptr));
176 dialog->save(newAlarm);
177 newAlarm->setEnabled(true);
178 mAlarms.append(newAlarm);
179 updateAlarmList();
181 }
182 delete dialog;
183}
184
185void IncidenceAlarm::newAlarmFromPreset()
186{
187 if (mIsTodo) {
188 mAlarms.append(AlarmPresets::preset(AlarmPresets::BeforeEnd, mUi->mAlarmPresetCombo->currentText()));
189 } else {
190 mAlarms.append(AlarmPresets::preset(AlarmPresets::BeforeStart, mUi->mAlarmPresetCombo->currentText()));
191 }
192
193 updateAlarmList();
195}
196
197void IncidenceAlarm::removeCurrentAlarm()
198{
199 Q_ASSERT(mUi->mAlarmList->selectedItems().size() == 1);
200 const int curAlarmIndex = mUi->mAlarmList->currentRow();
201 delete mUi->mAlarmList->takeItem(curAlarmIndex);
202 mAlarms.remove(curAlarmIndex);
203
204 updateAlarmList();
205 updateButtons();
207}
208
209void IncidenceAlarm::toggleCurrentAlarm()
210{
211 Q_ASSERT(mUi->mAlarmList->selectedItems().size() == 1);
212 const int curAlarmIndex = mUi->mAlarmList->currentRow();
214 alarm->setEnabled(!alarm->enabled());
215
216 updateButtons();
217 updateAlarmList();
219}
220
221void IncidenceAlarm::updateAlarmList()
222{
223 const int prevEnabledAlarmCount = mEnabledAlarmCount;
224 mEnabledAlarmCount = 0;
225
226 const QModelIndex currentIndex = mUi->mAlarmList->currentIndex();
227 mUi->mAlarmList->clear();
228 for (const KCalendarCore::Alarm::Ptr &alarm : std::as_const(mAlarms)) {
229 mUi->mAlarmList->addItem(stringForAlarm(alarm));
230 if (alarm->enabled()) {
231 ++mEnabledAlarmCount;
232 }
233 }
234
235 mUi->mAlarmList->setCurrentIndex(currentIndex);
236 if (prevEnabledAlarmCount != mEnabledAlarmCount) {
237 Q_EMIT alarmCountChanged(mEnabledAlarmCount);
238 }
239}
240
241void IncidenceAlarm::updateButtons()
242{
243 if (mUi->mAlarmList->count() > 0 && !mUi->mAlarmList->selectedItems().isEmpty()) {
244 mUi->mAlarmConfigureButton->setEnabled(true);
245 mUi->mAlarmRemoveButton->setEnabled(true);
246 mUi->mAlarmToggleButton->setEnabled(true);
248 if (mUi->mAlarmList->currentIndex().isValid()) {
249 selAlarm = mAlarms.at(mUi->mAlarmList->currentIndex().row());
250 }
251 if (selAlarm && selAlarm->enabled()) {
252 mUi->mAlarmToggleButton->setText(i18nc("Disable currently selected reminder", "Disable"));
253 } else {
254 mUi->mAlarmToggleButton->setText(i18nc("Enable currently selected reminder", "Enable"));
255 }
256 } else {
257 mUi->mAlarmConfigureButton->setEnabled(false);
258 mUi->mAlarmRemoveButton->setEnabled(false);
259 mUi->mAlarmToggleButton->setEnabled(false);
260 }
261}
262
263QString IncidenceAlarm::stringForAlarm(const KCalendarCore::Alarm::Ptr &alarm)
264{
266
267 QString action;
268 switch (alarm->type()) {
272 action = i18nc("Alarm action", "Display a dialog");
273 break;
275 action = i18nc("Alarm action", "Play an audio file");
276 break;
277 default:
278 action = i18nc("Alarm action", "Invalid Reminder.");
279 return action;
280 }
281
282 const int offset = alarm->hasStartOffset() ? alarm->startOffset().asSeconds() / 60 : alarm->endOffset().asSeconds() / 60; // make minutes
283
284 QString offsetUnitTranslated = i18ncp("The reminder is set to X minutes before/after the event", "1 minute", "%1 minutes", qAbs(offset));
285
286 int useoffset = offset;
287 if (offset % (24 * 60) == 0 && offset != 0) { // divides evenly into days?
288 useoffset = offset / 60 / 24;
289 offsetUnitTranslated = i18ncp("The reminder is set to X days before/after the event", "1 day", "%1 days", qAbs(useoffset));
290 } else if (offset % 60 == 0 && offset != 0) { // divides evenly into hours?
291 useoffset = offset / 60;
292 offsetUnitTranslated = i18ncp("The reminder is set to X hours before/after the event", "1 hour", "%1 hours", qAbs(useoffset));
293 }
294
296 if (alarm->repeatCount() > 0) {
297 repeatStr = i18nc("The reminder is configured to repeat after snooze", "(Repeats)");
298 }
299
300 if (alarm->enabled()) {
301 if (useoffset > 0 && alarm->hasStartOffset()) {
302 if (mIsTodo) {
303 // i18n: These series of strings are used to show the user a description of
304 // the alarm. %1 is replaced by one of the actions above, %2 is replaced by
305 // one of the time units above, %3 is the (Repeats) part that will be used
306 // in case of repetition of the alarm.
307 return i18n("%1 %2 after the to-do started %3", action, offsetUnitTranslated, repeatStr);
308 } else {
309 return i18n("%1 %2 after the event started %3", action, offsetUnitTranslated, repeatStr);
310 }
311 } else if (useoffset < 0 && alarm->hasStartOffset()) {
312 if (mIsTodo) {
313 return i18n("%1 %2 before the to-do starts %3", action, offsetUnitTranslated, repeatStr);
314 } else {
315 return i18n("%1 %2 before the event starts %3", action, offsetUnitTranslated, repeatStr);
316 }
317 } else if (useoffset > 0 && alarm->hasEndOffset()) {
318 if (mIsTodo) {
319 return i18n("%1 %2 after the to-do is due %3", action, offsetUnitTranslated, repeatStr);
320 } else {
321 return i18n("%1 %2 after the event ends %3", action, offsetUnitTranslated, repeatStr);
322 }
323 } else if (useoffset < 0 && alarm->hasEndOffset()) {
324 if (mIsTodo) {
325 return i18n("%1 %2 before the to-do is due %3", action, offsetUnitTranslated, repeatStr);
326 } else {
327 return i18n("%1 %2 before the event ends %3", action, offsetUnitTranslated, repeatStr);
328 }
329 }
330 } else {
331 if (useoffset > 0 && alarm->hasStartOffset()) {
332 if (mIsTodo) {
333 return i18n("%1 %2 after the to-do started %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
334 } else {
335 return i18n("%1 %2 after the event started %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
336 }
337 } else if (useoffset < 0 && alarm->hasStartOffset()) {
338 if (mIsTodo) {
339 return i18n("%1 %2 before the to-do starts %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
340 } else {
341 return i18n("%1 %2 before the event starts %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
342 }
343 } else if (useoffset > 0 && alarm->hasEndOffset()) {
344 if (mIsTodo) {
345 return i18n("%1 %2 after the to-do is due %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
346 } else {
347 return i18n("%1 %2 after the event ends %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
348 }
349 } else if (useoffset < 0 && alarm->hasEndOffset()) {
350 if (mIsTodo) {
351 return i18n("%1 %2 before the to-do is due %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
352 } else {
353 return i18n("%1 %2 before the event ends %3 (Disabled)", action, offsetUnitTranslated, repeatStr);
354 }
355 }
356 }
357
358 // useoffset == 0
359 if (alarm->enabled()) {
360 if (mIsTodo && alarm->hasStartOffset()) {
361 return i18n("%1 when the to-do starts", action);
362 } else if (alarm->hasStartOffset()) {
363 return i18n("%1 when the event starts", action);
364 } else if (mIsTodo && alarm->hasEndOffset()) {
365 return i18n("%1 when the to-do is due", action);
366 } else {
367 return i18n("%1 when the event ends", action);
368 }
369 } else {
370 if (mIsTodo && alarm->hasStartOffset()) {
371 return i18n("%1 when the to-do starts (Disabled)", action);
372 } else if (alarm->hasStartOffset()) {
373 return i18n("%1 when the event starts (Disabled)", action);
374 } else if (mIsTodo && alarm->hasEndOffset()) {
375 return i18n("%1 when the to-do is due (Disabled)", action);
376 } else {
377 return i18n("%1 when the event ends (Disabled)", action);
378 }
379 }
380}
381
382#include "moc_incidencealarm.cpp"
void checkDirtyStatus()
Checks if the dirty status has changed until last check and emits the dirtyStatusChanged signal if ne...
QSharedPointer< IncidenceT > incidence() const
Convenience method to get a pointer for a specific const Incidence Type.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
const QList< QKeySequence > & end()
void clicked(bool checked)
typedef ConstIterator
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
void remove(qsizetype i, qsizetype n)
void itemDoubleClicked(QListWidgetItem *item)
void itemSelectionChanged()
Q_EMITQ_EMIT
QObject * parent() const const
T qobject_cast(QObject *object)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:50:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.