alarmdialog.cpp 39.8 KB
Newer Older
Laurent Montel's avatar
Laurent Montel committed
1
2
3
/*
  This file is part of the KDE reminder agent.

4
5
6
7
8
  SPDX-FileCopyrightText: 2000, 2003 Cornelius Schumacher <schumacher@kde.org>
  SPDX-FileCopyrightText: 2008-2020 Allen Winter <winter@kde.org>
  SPDX-FileCopyrightText: 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>

  SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
Laurent Montel's avatar
Laurent Montel committed
9
10
11
*/

#include "alarmdialog.h"
Laurent Montel's avatar
Laurent Montel committed
12
#include "config-korganizer.h"
Laurent Montel's avatar
Laurent Montel committed
13
#include "koalarmclient_debug.h"
Laurent Montel's avatar
Laurent Montel committed
14
15
#include "korganizer_interface.h"
#include "mailclient.h"
Laurent Montel's avatar
Laurent Montel committed
16

17
#include "dbusproperties.h" // DBUS-generated
Laurent Montel's avatar
Laurent Montel committed
18
#include "notifications_interface.h" // DBUS-generated
19

Allen Winter's avatar
Allen Winter committed
20
#include <CalendarSupport/IdentityManager>
Laurent Montel's avatar
Laurent Montel committed
21
22
23
24
25
26
27
28
#include <CalendarSupport/IncidenceViewer>
#include <CalendarSupport/KCalPrefs>
#include <CalendarSupport/Utils>

#include <KCalUtils/IncidenceFormatter>

#include <KIdentityManagement/Identity>

Allen Winter's avatar
Allen Winter committed
29
30
#include <IncidenceEditor/IncidenceDialog>
#include <IncidenceEditor/IncidenceDialogFactory>
Laurent Montel's avatar
Laurent Montel committed
31
32
33

#include <MailTransport/TransportManager>

Allen Winter's avatar
Allen Winter committed
34
35
#include <KConfigGroup>
#include <KIconLoader>
Laurent Montel's avatar
Laurent Montel committed
36
37
38
39
40
#include <KLocalizedString>
#include <KMessageBox>
#include <KNotification>
#include <KSharedConfig>
#include <KWindowSystem>
Allen Winter's avatar
Allen Winter committed
41

Laurent Montel's avatar
Laurent Montel committed
42
#include <QComboBox>
Allen Winter's avatar
Allen Winter committed
43
44
#include <QDialogButtonBox>
#include <QHeaderView>
Laurent Montel's avatar
Laurent Montel committed
45
#include <QKeyEvent>
Allen Winter's avatar
Allen Winter committed
46
47
#include <QLabel>
#include <QPushButton>
Laurent Montel's avatar
Laurent Montel committed
48
49
50
51
#include <QSpinBox>
#include <QTreeWidget>
#include <QVBoxLayout>

Allen Winter's avatar
Allen Winter committed
52
53
#include <phonon/mediaobject.h>

54
using namespace KCalendarCore;
Laurent Montel's avatar
Laurent Montel committed
55

56
// fallback defaults
Laurent Montel's avatar
Laurent Montel committed
57
static int defSuspendVal = 5;
58
static int defSuspendUnit = AlarmDialog::SuspendInMinutes;
Laurent Montel's avatar
Laurent Montel committed
59

60
61
62
static const char s_fdo_notifications_service[] = "org.freedesktop.Notifications";
static const char s_fdo_notifications_path[] = "/org/freedesktop/Notifications";

Laurent Montel's avatar
Laurent Montel committed
63
64
65
66
class ReminderTreeItem : public QTreeWidgetItem
{
public:
    ReminderTreeItem(const Akonadi::Item &incidence, QTreeWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
67
68
        : QTreeWidgetItem(parent)
        , mIncidence(incidence)
Laurent Montel's avatar
Laurent Montel committed
69
70
    {
    }
Laurent Montel's avatar
Laurent Montel committed
71
72
73
74

    ~ReminderTreeItem() override
    {
    }
Laurent Montel's avatar
Laurent Montel committed
75

76
    bool operator<(const QTreeWidgetItem &other) const override;
Laurent Montel's avatar
Laurent Montel committed
77
78
79
80
81

    QString mDisplayText;

    const Akonadi::Item mIncidence;
    QDateTime mRemindAt;
82
    QDateTime mTrigger;
83
    QDateTime mHappening;
Laurent Montel's avatar
Laurent Montel committed
84
    bool mNotified = false;
Laurent Montel's avatar
Laurent Montel committed
85
86
87
88
89
90
91
92
93
94
95
};

struct ConfItem {
    QString uid;
    QUrl akonadiUrl;
    QDateTime remindAt;
};

bool ReminderTreeItem::operator<(const QTreeWidgetItem &other) const
{
    switch (treeWidget()->sortColumn()) {
Laurent Montel's avatar
Laurent Montel committed
96
    case 1: { // happening datetime
97
        const auto item = static_cast<const ReminderTreeItem *>(&other);
Laurent Montel's avatar
Laurent Montel committed
98
99
        return item->mHappening < mHappening;
    }
Laurent Montel's avatar
Laurent Montel committed
100
    case 2: { // trigger datetime
101
        const auto item = static_cast<const ReminderTreeItem *>(&other);
Laurent Montel's avatar
Laurent Montel committed
102
103
104
        return item->mTrigger < mTrigger;
    }
    default:
Laurent Montel's avatar
Laurent Montel committed
105
        return QTreeWidgetItem::operator<(other);
Laurent Montel's avatar
Laurent Montel committed
106
107
108
109
    }
}

AlarmDialog::AlarmDialog(const Akonadi::ETMCalendar::Ptr &calendar, QWidget *parent)
Laurent Montel's avatar
Laurent Montel committed
110
111
112
    : QDialog(parent, Qt::WindowStaysOnTopHint)
    , mCalendar(calendar)
    , mSuspendTimer(this)
Laurent Montel's avatar
Laurent Montel committed
113
114
115
116
117
118
{
    // User1 => Edit...
    // User2 => Dismiss All
    // User3 => Dismiss Selected
    //    Ok => Suspend

119
    if (calendar) {
Laurent Montel's avatar
Laurent Montel committed
120
        connect(calendar.data(), &Akonadi::ETMCalendar::calendarChanged, this, &AlarmDialog::slotCalendarChanged);
121
122
        Akonadi::IncidenceChanger *changer = calendar->incidenceChanger();
        changer->setShowDialogsOnError(false);
123
    }
Laurent Montel's avatar
Laurent Montel committed
124
125
126
127
128

    KIconLoader::global()->addAppDir(QStringLiteral("korgac"));

    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup generalConfig(config, "General");
Laurent Montel's avatar
Laurent Montel committed
129
130
131
132

    const QSize initialSize(424, 187);
    // split up position and size to be compatible with previous version
    // that only stored the position.
Laurent Montel's avatar
Laurent Montel committed
133
    const QPoint pos = generalConfig.readEntry("Position", QPoint());
Laurent Montel's avatar
Laurent Montel committed
134
    if (!pos.isNull()) {
Laurent Montel's avatar
Laurent Montel committed
135
        const QSize size = generalConfig.readEntry("Size", initialSize);
Laurent Montel's avatar
Laurent Montel committed
136
137
138
        mRect = QRect(pos, size);
        setGeometry(mRect);
    }
139

140
    defSuspendVal = generalConfig.readEntry("DefaultSuspendValue", defSuspendVal);
141
    defSuspendUnit = generalConfig.readEntry("DefaultSuspendUnit", defSuspendUnit);
142
143
144

    int suspendVal = defSuspendVal;
    int suspendUnit = defSuspendUnit;
Laurent Montel's avatar
Laurent Montel committed
145
146
147
148

    QWidget *topBox = new QWidget(this);
    setWindowTitle(i18nc("@title:window", "Reminders"));
    setWindowIcon(QIcon::fromTheme(QStringLiteral("korgac")));
149
150
    auto buttonBox = new QDialogButtonBox(this);
    auto mainLayout = new QVBoxLayout(this);
Laurent Montel's avatar
Laurent Montel committed
151
    mainLayout->addWidget(topBox);
152
    mOkButton = new QPushButton;
Laurent Montel's avatar
Laurent Montel committed
153
154
    mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
    mUser1Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
155
    mUser1Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
156
157
    buttonBox->addButton(mUser1Button, QDialogButtonBox::ActionRole);
    mUser2Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
158
    mUser2Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
159
160
    buttonBox->addButton(mUser2Button, QDialogButtonBox::ActionRole);
    mUser3Button = new QPushButton;
Allen Winter's avatar
Allen Winter committed
161
    mUser3Button->setDefault(false);
Laurent Montel's avatar
Laurent Montel committed
162
163
164
165
    buttonBox->addButton(mUser3Button, QDialogButtonBox::ActionRole);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &AlarmDialog::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &AlarmDialog::reject);
    mainLayout->addWidget(buttonBox);
166
    buttonBox->addButton(mOkButton, QDialogButtonBox::ActionRole);
Laurent Montel's avatar
Laurent Montel committed
167
168

    mUser3Button->setText(i18nc("@action:button", "Dismiss Reminder"));
Laurent Montel's avatar
Laurent Montel committed
169
    mUser3Button->setToolTip(i18nc("@info:tooltip", "Dismiss the reminders for the selected incidences"));
170
171
172
173
    mUser3Button->setWhatsThis(i18nc("@info:whatsthis",
                                     "Press this button to dismiss the currently selected incidence.  "
                                     "All non-selected non-incidences will be unaffected."));

Laurent Montel's avatar
Laurent Montel committed
174
    mUser2Button->setText(i18nc("@action:button", "Dismiss All"));
Laurent Montel's avatar
Laurent Montel committed
175
176
    mUser2Button->setToolTip(i18nc("@info:tooltip", "Dismiss the reminders for all listed incidences"));
    mUser2Button->setWhatsThis(i18nc("@info:whatsthis", "Press this button to dismiss all the listed incidences."));
Laurent Montel's avatar
Laurent Montel committed
177
    mUser1Button->setText(i18nc("@action:button", "Edit..."));
Laurent Montel's avatar
Laurent Montel committed
178
    mUser1Button->setToolTip(i18nc("@info:tooltip", "Edit the selected incidence"));
179
180
181
    mUser1Button->setWhatsThis(i18nc("@info::whatsthis",
                                     "Press this button if you want to edit the selected incidence.  "
                                     "A dialog will popup allowing you to edit the incidence."));
Laurent Montel's avatar
Laurent Montel committed
182
183
184
185
    mOkButton->setText(i18nc("@action:button", "Suspend"));
    mOkButton->setToolTip(i18nc("@info:tooltip",
                                "Suspend the reminders for the selected incidences "
                                "by the specified interval"));
186
187
188
    mOkButton->setWhatsThis(i18nc("@info:whatsthis",
                                  "Press this button to suspend the currently selected incidences.  "
                                  "The suspend interval is configurable by the Suspend duration settings."));
Laurent Montel's avatar
Laurent Montel committed
189

190
    auto topLayout = new QVBoxLayout(topBox);
Laurent Montel's avatar
Laurent Montel committed
191
192
193
194
195
    // Try to keep the dialog small and non-obtrusive.
    // the user can resize down to the minimum
    setMinimumSize(280, 160);
    // take out some padding which makes it larger
    topLayout->setSpacing(2);
Laurent Montel's avatar
Laurent Montel committed
196
197
    topLayout->setContentsMargins({});
    setContentsMargins({});
Laurent Montel's avatar
Laurent Montel committed
198

Laurent Montel's avatar
Laurent Montel committed
199
200
201
202
    auto label = new QLabel(i18nc("@label",
                                  "Reminders: "
                                  "Clicking on the title toggles details for item"),
                            topBox);
203
    topLayout->addWidget(label);
Laurent Montel's avatar
Laurent Montel committed
204

205
    mIncidenceTree = new QTreeWidget(topBox);
Laurent Montel's avatar
Laurent Montel committed
206
207
    mIncidenceTree->setColumnCount(3);
    mIncidenceTree->setSortingEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
208
209
    const QStringList headerLabels = (QStringList(i18nc("@title:column reminder title", "Title"))
                                      << i18nc("@title:column happens at date/time", "Date Time") << i18nc("@title:column trigger date/time", "Trigger Time"));
Laurent Montel's avatar
Laurent Montel committed
210
    mIncidenceTree->setHeaderLabels(headerLabels);
211
212
213

    QHeaderView *header = mIncidenceTree->header();
    header->setSectionResizeMode(0, QHeaderView::Stretch);
Laurent Montel's avatar
Laurent Montel committed
214
    mIncidenceTree->headerItem()->setToolTip(0, i18nc("@info:tooltip", "The event or to-do title"));
215
216

    header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
Laurent Montel's avatar
Laurent Montel committed
217
    mIncidenceTree->headerItem()->setToolTip(1, i18nc("@info:tooltip", "The reminder is set for this date/time"));
218
219
220

    header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
    header->setStretchLastSection(false);
Laurent Montel's avatar
Laurent Montel committed
221
    mIncidenceTree->headerItem()->setToolTip(2, i18nc("@info:tooltip", "The date/time the reminder was triggered"));
Laurent Montel's avatar
Laurent Montel committed
222
223
224
225
226
227

    mIncidenceTree->setWordWrap(true);
    mIncidenceTree->setAllColumnsShowFocus(true);
    mIncidenceTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
    mIncidenceTree->setRootIsDecorated(false);

228
    topLayout->addWidget(mIncidenceTree);
Laurent Montel's avatar
Laurent Montel committed
229
230
231

    connect(mIncidenceTree, &QTreeWidget::itemClicked, this, &AlarmDialog::update);
    connect(mIncidenceTree, &QTreeWidget::itemDoubleClicked, this, &AlarmDialog::edit);
232
    connect(mIncidenceTree, &QTreeWidget::itemSelectionChanged, this, &AlarmDialog::updateButtons);
Laurent Montel's avatar
Laurent Montel committed
233
234

    mDetailView = new CalendarSupport::IncidenceViewer(mCalendar.data(), topBox);
Laurent Montel's avatar
Laurent Montel committed
235
236
237
    const QString s = xi18nc("@info default incidence details string",
                             "<emphasis>Select an event or to-do from the list above "
                             "to view its details here.</emphasis>");
Laurent Montel's avatar
Laurent Montel committed
238
    mDetailView->setDefaultMessage(s);
239
    topLayout->addWidget(mDetailView);
Laurent Montel's avatar
Laurent Montel committed
240
    mDetailView->hide();
Laurent Montel's avatar
Laurent Montel committed
241
    mLastItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
242
243

    QWidget *suspendBox = new QWidget(topBox);
244
    auto suspendBoxHBoxLayout = new QHBoxLayout(suspendBox);
Laurent Montel's avatar
Laurent Montel committed
245
    suspendBoxHBoxLayout->setContentsMargins({});
246
    topLayout->addWidget(suspendBox);
Laurent Montel's avatar
Laurent Montel committed
247

248
    auto l = new QLabel(i18nc("@label:spinbox", "Suspend &duration:"), suspendBox);
Laurent Montel's avatar
Laurent Montel committed
249
250
251
252
253
    suspendBoxHBoxLayout->addWidget(l);

    mSuspendSpin = new QSpinBox(suspendBox);
    suspendBoxHBoxLayout->addWidget(mSuspendSpin);
    mSuspendSpin->setRange(1, 9999);
Laurent Montel's avatar
Laurent Montel committed
254
255
256
257
258
259
    mSuspendSpin->setValue(suspendVal); // default suspend duration
    mSuspendSpin->setToolTip(i18nc("@info:tooltip", "Suspend the reminders by this amount of time"));
    mSuspendSpin->setWhatsThis(i18nc("@info:whatsthis",
                                     "Each reminder for the selected incidences will be suspended "
                                     "by this number of time units. You can choose the time units "
                                     "(typically minutes) in the adjacent selector."));
Laurent Montel's avatar
Laurent Montel committed
260
261
262

    l->setBuddy(mSuspendSpin);

263
    mSuspendUnit = new QComboBox(suspendBox);
Laurent Montel's avatar
Laurent Montel committed
264
265
266
267
268
    suspendBoxHBoxLayout->addWidget(mSuspendUnit);
    mSuspendUnit->addItem(i18nc("@item:inlistbox suspend in terms of minutes", "minute(s)"));
    mSuspendUnit->addItem(i18nc("@item:inlistbox suspend in terms of hours", "hour(s)"));
    mSuspendUnit->addItem(i18nc("@item:inlistbox suspend in terms of days", "day(s)"));
    mSuspendUnit->addItem(i18nc("@item:inlistbox suspend in terms of weeks", "week(s)"));
Laurent Montel's avatar
Laurent Montel committed
269
270
271
272
273
    mSuspendUnit->setToolTip(i18nc("@info:tooltip", "Suspend the reminders using this time unit"));
    mSuspendUnit->setWhatsThis(i18nc("@info:whatsthis",
                                     "Each reminder for the selected incidences will be suspended "
                                     "using this time unit. You can set the number of time units "
                                     "in the adjacent number entry input."));
274
    mSuspendUnit->setCurrentIndex(suspendUnit);
275

Laurent Montel's avatar
Laurent Montel committed
276
277
278
279
280
281
282
283
    connect(&mSuspendTimer, &QTimer::timeout, this, &AlarmDialog::wakeUp);

    connect(mOkButton, &QPushButton::clicked, this, &AlarmDialog::slotOk);
    connect(mUser1Button, &QPushButton::clicked, this, &AlarmDialog::slotUser1);
    connect(mUser2Button, &QPushButton::clicked, this, &AlarmDialog::slotUser2);
    connect(mUser3Button, &QPushButton::clicked, this, &AlarmDialog::slotUser3);

    mIdentityManager = new CalendarSupport::IdentityManager;
284
285
286

    QDBusConnection dbusConn = QDBusConnection::sessionBus();
    if (dbusConn.interface()->isServiceRegistered(QString::fromLatin1(s_fdo_notifications_service))) {
Laurent Montel's avatar
Laurent Montel committed
287
288
289
290
291
        OrgFreedesktopDBusPropertiesInterface *propsIface = new OrgFreedesktopDBusPropertiesInterface(QString::fromLatin1(s_fdo_notifications_service),
                                                                                                      QString::fromLatin1(s_fdo_notifications_path),
                                                                                                      dbusConn,
                                                                                                      this);
        connect(propsIface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, this, &AlarmDialog::slotDBusNotificationsPropertiesChanged);
292
    }
Laurent Montel's avatar
Laurent Montel committed
293
294
295
296
297
298
299
300
301
302
}

AlarmDialog::~AlarmDialog()
{
    mIncidenceTree->clear();
    delete mIdentityManager;
}

ReminderTreeItem *AlarmDialog::searchByItem(const Akonadi::Item &incidence)
{
Laurent Montel's avatar
Laurent Montel committed
303
    ReminderTreeItem *found = nullptr;
Laurent Montel's avatar
Laurent Montel committed
304
305
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
306
        auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
307
308
309
310
311
312
313
314
315
316
317
318
        if (item->mIncidence == incidence) {
            found = item;
            break;
        }
        ++it;
    }
    return found;
}

static QString cleanSummary(const QString &summary)
{
    QString retStr = summary;
319
    return retStr.replace(QLatin1Char('\n'), QLatin1Char(' '));
Laurent Montel's avatar
Laurent Montel committed
320
321
}

Laurent Montel's avatar
Laurent Montel committed
322
void AlarmDialog::addIncidence(const Akonadi::Item &incidenceitem, const QDateTime &reminderAt, const QString &displayText)
Laurent Montel's avatar
Laurent Montel committed
323
324
325
326
327
328
329
{
    Incidence::Ptr incidence = CalendarSupport::incidence(incidenceitem);
    ReminderTreeItem *item = searchByItem(incidenceitem);
    if (!item) {
        item = new ReminderTreeItem(incidenceitem, mIncidenceTree);
    }
    item->mNotified = false;
330
    item->mHappening = QDateTime();
Laurent Montel's avatar
Laurent Montel committed
331
    item->mRemindAt = reminderAt;
332
    item->mTrigger = QDateTime::currentDateTime();
Laurent Montel's avatar
Laurent Montel committed
333
334
335
336
    item->mDisplayText = displayText;
    item->setText(0, cleanSummary(incidence->summary()));

    QString displayStr;
337
    const auto dateTime = triggerDateForIncidence(incidence, reminderAt, displayStr);
Laurent Montel's avatar
Laurent Montel committed
338
339
340
341
342
343
344
345
346
347

    if (incidence->type() == Incidence::TypeEvent) {
        item->setIcon(0, QIcon::fromTheme(QStringLiteral("view-calendar-day")));
    } else if (incidence->type() == Incidence::TypeTodo) {
        item->setIcon(0, QIcon::fromTheme(QStringLiteral("view-calendar-tasks")));
    }

    item->mHappening = dateTime;
    item->setText(1, displayStr);

348
    item->setText(2, QLocale().toString(item->mTrigger, QLocale::ShortFormat));
Laurent Montel's avatar
Laurent Montel committed
349
350
351
352
    QString tip = KCalUtils::IncidenceFormatter::toolTipStr(CalendarSupport::displayName(mCalendar.data(), incidenceitem.parentCollection()),
                                                            incidence,
                                                            item->mRemindAt.date(),
                                                            true);
Laurent Montel's avatar
Laurent Montel committed
353
354
355
356
357
358
359
360
361
362
363
364
365
    if (!item->mDisplayText.isEmpty()) {
        tip += QLatin1String("<br>") + item->mDisplayText;
    }
    item->setToolTip(0, tip);
    item->setToolTip(1, tip);
    item->setToolTip(2, tip);
    item->setData(0, QTreeWidgetItem::UserType, false);

    mIncidenceTree->setCurrentItem(item);
    showDetails(item);
    slotSave();
}

366
367
void AlarmDialog::resetSuspend()
{
Laurent Montel's avatar
Laurent Montel committed
368
    mSuspendSpin->setValue(defSuspendVal); // default suspend duration
369
370
371
372
373
374
375
376
377
    mSuspendUnit->setCurrentIndex(defSuspendUnit);
}

void AlarmDialog::setDefaultSuspend()
{
    defSuspendVal = mSuspendSpin->value();
    defSuspendUnit = mSuspendUnit->currentIndex();
}

Laurent Montel's avatar
Laurent Montel committed
378
379
380
381
382
383
384
385
386
void AlarmDialog::slotOk()
{
    suspend();
}

void AlarmDialog::slotUser1()
{
    const ReminderList selection = selectedItems();
    if (!selection.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
387
        ReminderTreeItem *item = selection.first();
Laurent Montel's avatar
Laurent Montel committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
        if (mCalendar->hasRight(item->mIncidence, Akonadi::Collection::CanChangeItem)) {
            edit();
        }
    }
}

void AlarmDialog::slotUser2()
{
    dismissAll();
}

void AlarmDialog::slotUser3()
{
    dismissCurrent();
}

void AlarmDialog::dismissCurrent()
{
    dismiss(selectedItems());

Laurent Montel's avatar
Minor    
Laurent Montel committed
408
409
    const int activeCountNumber = activeCount();
    if (activeCountNumber == 0) {
Laurent Montel's avatar
Laurent Montel committed
410
411
412
413
        accept();
    } else {
        update();
    }
Laurent Montel's avatar
Minor    
Laurent Montel committed
414
    Q_EMIT reminderCount(activeCountNumber);
Laurent Montel's avatar
Laurent Montel committed
415
416
417
418
419
420
421
422
}

void AlarmDialog::dismissAll()
{
    ReminderList selections;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
Laurent Montel's avatar
Laurent Montel committed
423
        if (!(*it)->isDisabled()) { // do not disable suspended reminders
Laurent Montel's avatar
Laurent Montel committed
424
425
426
427
428
429
430
431
432
433
434
            selections.append(static_cast<ReminderTreeItem *>(*it));
        }
        ++it;
    }
    dismiss(selections);

    setTimer();
    accept();
    Q_EMIT reminderCount(activeCount());
}

Laurent Montel's avatar
Laurent Montel committed
435
void AlarmDialog::dismiss(const ReminderList &selections)
Laurent Montel's avatar
Laurent Montel committed
436
437
438
{
    QList<Akonadi::Item::Id> ids;
    ids.reserve(selections.count());
Laurent Montel's avatar
Laurent Montel committed
439
440
    for (ReminderList::const_iterator it = selections.constBegin(); it != selections.constEnd(); ++it) {
        qCDebug(KOALARMCLIENT_LOG) << "removing " << CalendarSupport::incidence((*it)->mIncidence)->summary();
Laurent Montel's avatar
Laurent Montel committed
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
        if (mIncidenceTree->itemBelow(*it)) {
            mIncidenceTree->setCurrentItem(mIncidenceTree->itemBelow(*it));
        } else if (mIncidenceTree->itemAbove(*it)) {
            mIncidenceTree->setCurrentItem(mIncidenceTree->itemAbove(*it));
        }
        mIncidenceTree->removeItemWidget(*it, 0);
        ids.append((*it)->mIncidence.id());
        delete *it;
    }

    removeFromConfig(ids);
}

void AlarmDialog::edit()
{
Laurent Montel's avatar
Laurent Montel committed
456
    const ReminderList selection = selectedItems();
Laurent Montel's avatar
Minor    
Laurent Montel committed
457
458
    if (selection.count() == 1) {
        Incidence::Ptr incidence = CalendarSupport::incidence(selection.first()->mIncidence);
Laurent Montel's avatar
Laurent Montel committed
459
460
        if (!mCalendar->hasRight(selection.first()->mIncidence, Akonadi::Collection::CanChangeItem)) {
            KMessageBox::sorry(this, i18nc("@info", "\"%1\" is a read-only incidence so modifications are not possible.", cleanSummary(incidence->summary())));
Laurent Montel's avatar
Minor    
Laurent Montel committed
461
462
            return;
        }
Laurent Montel's avatar
Laurent Montel committed
463

464
        if (!openIncidenceEditorNG(selection.first()->mIncidence)) {
Laurent Montel's avatar
Laurent Montel committed
465
466
            KMessageBox::error(this,
                               i18nc("@info", "An internal error occurred attempting to modify \"%1\". Unsupported type.", cleanSummary(incidence->summary())));
467
468
            qCWarning(KOALARMCLIENT_LOG) << "Attempting to edit an unsupported incidence type.";
        }
Laurent Montel's avatar
Minor    
Laurent Montel committed
469
    }
Laurent Montel's avatar
Laurent Montel committed
470
471
472
473
}

void AlarmDialog::suspend()
{
Laurent Montel's avatar
Laurent Montel committed
474
    if (!isVisible()) { // do nothing if the dialog is hidden
Laurent Montel's avatar
Laurent Montel committed
475
476
477
478
479
        return;
    }

    int unit = 1;
    switch (mSuspendUnit->currentIndex()) {
480
    case SuspendInWeeks:
Laurent Montel's avatar
Laurent Montel committed
481
        unit *= 7;
Laurent Montel's avatar
Laurent Montel committed
482
        Q_FALLTHROUGH();
483
    case SuspendInDays:
Laurent Montel's avatar
Laurent Montel committed
484
        unit *= 24;
Laurent Montel's avatar
Laurent Montel committed
485
        Q_FALLTHROUGH();
486
    case SuspendInHours:
Laurent Montel's avatar
Laurent Montel committed
487
        unit *= 60;
Laurent Montel's avatar
Laurent Montel committed
488
        Q_FALLTHROUGH();
489
    case SuspendInMinutes:
Laurent Montel's avatar
Laurent Montel committed
490
        unit *= 60;
Laurent Montel's avatar
Laurent Montel committed
491
        Q_FALLTHROUGH();
Laurent Montel's avatar
Laurent Montel committed
492
493
494
495
    default:
        break;
    }

Laurent Montel's avatar
Laurent Montel committed
496
    ReminderTreeItem *selitem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
497
498
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
Laurent Montel's avatar
Laurent Montel committed
499
        if ((*it)->isSelected() && !(*it)->isDisabled()) { // suspend selected, non-suspended reminders
Laurent Montel's avatar
Laurent Montel committed
500
501
            (*it)->setSelected(false);
            (*it)->setDisabled(true);
502
            auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
503
            item->mRemindAt = QDateTime::currentDateTime().addSecs(unit * mSuspendSpin->value());
504
            item->mHappening = item->mRemindAt;
Laurent Montel's avatar
Laurent Montel committed
505
            item->mNotified = false;
506
            (*it)->setText(1, QLocale().toString(item->mHappening, QLocale::ShortFormat));
Laurent Montel's avatar
Laurent Montel committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
            selitem = item;
        }
        ++it;
    }

    if (selitem) {
        if (mIncidenceTree->itemBelow(selitem)) {
            mIncidenceTree->setCurrentItem(mIncidenceTree->itemBelow(selitem));
        } else if (mIncidenceTree->itemAbove(selitem)) {
            mIncidenceTree->setCurrentItem(mIncidenceTree->itemAbove(selitem));
        }
    }

    // save suspended alarms too so they can be restored on restart
    // kolab/issue4108
    slotSave();

    setTimer();
    if (activeCount() == 0) {
        accept();
    } else {
        update();
    }
    Q_EMIT reminderCount(activeCount());
}

void AlarmDialog::setTimer()
{
    int nextReminderAt = -1;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
539
        auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
        if (item->mRemindAt > QDateTime::currentDateTime()) {
            const int secs = QDateTime::currentDateTime().secsTo(item->mRemindAt);
            nextReminderAt = nextReminderAt <= 0 ? secs : qMin(nextReminderAt, secs);
        }
        ++it;
    }

    if (nextReminderAt >= 0) {
        mSuspendTimer.stop();
        mSuspendTimer.start(1000 * (nextReminderAt + 1));
        mSuspendTimer.setSingleShot(true);
    }
}

554
555
556
557
558
559
560
561
void AlarmDialog::showEvent(QShowEvent *event)
{
    QDialog::showEvent(event);
    // Moving to showEvent from show() because with QDialog and Qt 5.7
    // setOnAllDesktops seemed to work only if the dialog was already showing,
    // which happens when a notification appears and the dialog was already
    // up.
    KWindowSystem::setOnAllDesktops(winId(), true);
562
    KWindowSystem::unminimizeWindow(winId());
563
564
565
    KWindowSystem::setState(winId(), NET::KeepAbove | NET::DemandsAttention);
}

Laurent Montel's avatar
Laurent Montel committed
566
567
568
569
570
571
572
573
574
575
void AlarmDialog::show()
{
    mIncidenceTree->resizeColumnToContents(0);
    mIncidenceTree->resizeColumnToContents(1);
    mIncidenceTree->resizeColumnToContents(2);
    mIncidenceTree->sortItems(1, Qt::AscendingOrder);

    // select the first item that hasn't already been notified
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
576
        auto item = static_cast<ReminderTreeItem *>(*it);
577
        if (!item->mNotified && !item->isDisabled()) {
Laurent Montel's avatar
Laurent Montel committed
578
579
580
581
582
583
            (*it)->setSelected(true);
            break;
        }
        ++it;
    }

584
585
    mUser2Button->setVisible(mIncidenceTree->topLevelItemCount() > 1);

Laurent Montel's avatar
Laurent Montel committed
586
    // reset the default suspend time
Laurent Montel's avatar
Laurent Montel committed
587
588
589
    // Allen: commented-out the following lines on 17 Sept 2013
    //  mSuspendSpin->setValue( defSuspendVal );
    //  mSuspendUnit->setCurrentIndex( defSuspendUnit );
Laurent Montel's avatar
Laurent Montel committed
590

Laurent Montel's avatar
Laurent Montel committed
591
592
593
    // move then show, avoids a visible jump if it is show then move
    if (!mRect.isNull()) {
        setGeometry(mRect);
Laurent Montel's avatar
Laurent Montel committed
594
    }
Laurent Montel's avatar
Laurent Montel committed
595
    QDialog::show();
596
597
598
599
600
    if (grabFocus()) {
        KWindowSystem::activateWindow(winId());
    } else {
        KWindowSystem::raiseWindow(winId());
    }
Laurent Montel's avatar
Laurent Montel committed
601
602
603
604
605
606
607
608
609
610
611
612

    // Audio, Procedure, and EMail alarms
    eventNotification();
}

void AlarmDialog::suspendAll()
{
    mIncidenceTree->clearSelection();
    QTreeWidgetItemIterator it(mIncidenceTree);

    // first, select all non-suspended reminders
    while (*it) {
Laurent Montel's avatar
Laurent Montel committed
613
        if (!(*it)->isDisabled()) { // do not suspend suspended reminders
Laurent Montel's avatar
Laurent Montel committed
614
615
616
617
618
            (*it)->setSelected(true);
        }
        ++it;
    }

Laurent Montel's avatar
Laurent Montel committed
619
    // suspend all selected reminders
Laurent Montel's avatar
Laurent Montel committed
620
621
622
623
624
625
626
627
628
629
    suspend();
}

void AlarmDialog::eventNotification()
{
    bool beeped = false;
    bool found = false;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
630
        auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
631
632
        ++it;
        if (item->isDisabled() || item->mNotified) {
Laurent Montel's avatar
Laurent Montel committed
633
            // skip suspended reminders or reminders that have been notified
Laurent Montel's avatar
Laurent Montel committed
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
            continue;
        }
        found = true;
        item->mNotified = true;
        Incidence::Ptr incidence = CalendarSupport::incidence(item->mIncidence);
        Alarm::List alarms = incidence->alarms();
        Alarm::List::ConstIterator ait;
        for (ait = alarms.constBegin(); ait != alarms.constEnd(); ++ait) {
            Alarm::Ptr alarm = *ait;
            // FIXME: Check whether this should be done for all multiple alarms
            if (alarm->type() == Alarm::Procedure) {
                // FIXME: Add a message box asking whether the procedure should really be executed
                qCDebug(KOALARMCLIENT_LOG) << "Starting program: '" << alarm->programFile() << "'";

                QString program = alarm->programFile();

                // if the program name contains spaces escape it
Laurent Montel's avatar
Laurent Montel committed
651
                if (program.contains(QLatin1Char(' ')) && !(program.startsWith(QLatin1Char('\"')) && program.endsWith(QLatin1Char('\"')))) {
Laurent Montel's avatar
Laurent Montel committed
652
653
654
                    program = QLatin1Char('\"') + program + QLatin1Char('\"');
                }

Laurent Montel's avatar
Laurent Montel committed
655
                // TODO move alarm->programArguments() as argument of QProcess API ?
656
                QProcess::startDetached(program + QLatin1Char(' ') + alarm->programArguments(), QStringList());
Laurent Montel's avatar
Laurent Montel committed
657
658
            } else if (alarm->type() == Alarm::Audio) {
                beeped = true;
Laurent Montel's avatar
Laurent Montel committed
659
                Phonon::MediaObject *player = Phonon::createPlayer(Phonon::NotificationCategory, QUrl::fromLocalFile(alarm->audioFile()));
Laurent Montel's avatar
Laurent Montel committed
660
                player->setParent(this);
Laurent Montel's avatar
Laurent Montel committed
661
                connect(player, &Phonon::MediaObject::finished, player, &Phonon::MediaObject::deleteLater);
Laurent Montel's avatar
Laurent Montel committed
662
663
664
                player->play();
            } else if (alarm->type() == Alarm::Email) {
                QString from = CalendarSupport::KCalPrefs::instance()->email();
Allen Winter's avatar
Allen Winter committed
665
                KIdentityManagement::Identity id = mIdentityManager->identityForAddress(from);
Laurent Montel's avatar
Laurent Montel committed
666
667
668
669
670
671
672
                QString to;
                if (alarm->mailAddresses().isEmpty()) {
                    to = from;
                } else {
                    const Person::List addresses = alarm->mailAddresses();
                    QStringList add;
                    add.reserve(addresses.count());
Laurent Montel's avatar
Laurent Montel committed
673
                    Person::List::ConstIterator end(addresses.constEnd());
Laurent Montel's avatar
Laurent Montel committed
674
                    for (Person::List::ConstIterator it = addresses.constBegin(); it != end; ++it) {
675
                        add << (*it).fullName();
Laurent Montel's avatar
Laurent Montel committed
676
                    }
677
                    to = add.join(QLatin1String(", "));
Laurent Montel's avatar
Laurent Montel committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
                }

                QString subject;

                Akonadi::Item parentItem = mCalendar->item(alarm->parentUid());
                Incidence::Ptr parent = CalendarSupport::incidence(parentItem);

                if (alarm->mailSubject().isEmpty()) {
                    if (parent->summary().isEmpty()) {
                        subject = i18nc("@title", "Reminder");
                    } else {
                        subject = i18nc("@title", "Reminder: %1", cleanSummary(parent->summary()));
                    }
                } else {
                    subject = i18nc("@title", "Reminder: %1", alarm->mailSubject());
                }

Allen Winter's avatar
Allen Winter committed
695
                QString body = KCalUtils::IncidenceFormatter::mailBodyStr(parent.staticCast<IncidenceBase>());
Laurent Montel's avatar
Laurent Montel committed
696
697
698
                if (!alarm->mailText().isEmpty()) {
                    body += QLatin1Char('\n') + alarm->mailText();
                }
Laurent Montel's avatar
Laurent Montel committed
699
                // TODO: support attachments
Laurent Montel's avatar
Laurent Montel committed
700
                KOrg::MailClient mailer;
Laurent Montel's avatar
Laurent Montel committed
701
702
703
                const bool sendStatus =
                    mailer
                        .send(id, from, to, QString(), subject, body, true, false, QString(), MailTransport::TransportManager::self()->defaultTransportName());
704
                if (!sendStatus) {
705
                    KNotification::event(QStringLiteral("mailremindersent"),
Laurent Montel's avatar
Laurent Montel committed
706
707
708
                                         QString(),
                                         i18nc("@info email subject, error message",
                                               "<warning>Failed to send the Email reminder for %1. %2</warning>",
Laurent Montel's avatar
Laurent Montel committed
709
710
                                               subject,
                                               mailer.errorMsg()),
Laurent Montel's avatar
Laurent Montel committed
711
712
713
714
                                         QStringLiteral("korgac"),
                                         nullptr,
                                         KNotification::CloseOnTimeout,
                                         QStringLiteral("korgac"));
715
                }
Laurent Montel's avatar
Laurent Montel committed
716
717
718
719
720
721
722
723
724
725
726
            }
        }
    }

    if (!beeped && found) {
        KNotification::beep();
    }
}

void AlarmDialog::wakeUp()
{
727
728
729
730
    // Check if notifications are inhibited (e.x. plasma "do not disturb" mode.
    // In that case, we'll wait until they are allowed again (see slotDBusNotificationsPropertiesChanged)
    QDBusConnection dbusConn = QDBusConnection::sessionBus();
    if (dbusConn.interface()->isServiceRegistered(QString::fromLatin1(s_fdo_notifications_service))) {
Laurent Montel's avatar
Laurent Montel committed
731
        OrgFreedesktopNotificationsInterface iface(QString::fromLatin1(s_fdo_notifications_service), QString::fromLatin1(s_fdo_notifications_path), dbusConn);
732
733
734
735
736
        if (iface.inhibited()) {
            return;
        }
    }

Laurent Montel's avatar
Laurent Montel committed
737
738
    bool activeReminders = false;
    QTreeWidgetItemIterator it(mIncidenceTree);
Laurent Montel's avatar
Laurent Montel committed
739
    QTreeWidgetItem *firstItem = nullptr;
Laurent Montel's avatar
Laurent Montel committed
740
741
742
743
    while (*it) {
        if (!firstItem) {
            firstItem = *it;
        }
744
        auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
745
746
747
        Incidence::Ptr incidence = CalendarSupport::incidence(item->mIncidence);

        if (item->mRemindAt <= QDateTime::currentDateTime()) {
Laurent Montel's avatar
Laurent Montel committed
748
            if (item->isDisabled()) { // do not wakeup non-suspended reminders
Laurent Montel's avatar
Laurent Montel committed
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
                item->setDisabled(false);
                item->setSelected(false);
            }
            activeReminders = true;
        } else {
            item->setDisabled(true);
        }

        ++it;
    }

    if (activeReminders) {
        show();
    }
    setTimer();
    showDetails(firstItem);
    Q_EMIT reminderCount(activeCount());
}

Laurent Montel's avatar
Laurent Montel committed
768
769
770
void AlarmDialog::slotDBusNotificationsPropertiesChanged(const QString &interface,
                                                         const QVariantMap &changedProperties,
                                                         const QStringList &invalidatedProperties)
771
{
Laurent Montel's avatar
Laurent Montel committed
772
773
    Q_UNUSED(interface) // always "org.freedesktop.Notifications"
    Q_UNUSED(invalidatedProperties)
774
775
776
777
778
779
780
781
782
783
    const auto it = changedProperties.find(QStringLiteral("Inhibited"));
    if (it != changedProperties.end()) {
        const bool inhibited = it.value().toBool();
        qCDebug(KOALARMCLIENT_LOG) << "Notifications inhibited:" << inhibited;
        if (!inhibited) {
            wakeUp();
        }
    }
}

Laurent Montel's avatar
Laurent Montel committed
784
785
786
787
788
789
790
791
void AlarmDialog::slotSave()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup generalConfig(config, "General");
    int numReminders = 0;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
792
        auto item = static_cast<ReminderTreeItem *>(*it);
Laurent Montel's avatar
Laurent Montel committed
793
        KConfigGroup incidenceConfig(config, QStringLiteral("Incidence-%1").arg(numReminders + 1));
Laurent Montel's avatar
Laurent Montel committed
794
795
796
797
798
799
800
801
802

        Incidence::Ptr incidence = CalendarSupport::incidence(item->mIncidence);
        incidenceConfig.writeEntry("AkonadiUrl", item->mIncidence.url());
        incidenceConfig.writeEntry("RemindAt", item->mRemindAt);
        ++numReminders;
        ++it;
    }

    generalConfig.writeEntry("Reminders", numReminders);
Laurent Montel's avatar
Laurent Montel committed
803
804
805
    mRect = geometry();
    generalConfig.writeEntry("Position", mRect.topLeft());
    generalConfig.writeEntry("Size", mRect.size());
806
807
    generalConfig.writeEntry("DefaultSuspendValue", defSuspendVal);
    generalConfig.writeEntry("DefaultSuspendUnit", defSuspendUnit);
808

Laurent Montel's avatar
Laurent Montel committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
    config->sync();
}

AlarmDialog::ReminderList AlarmDialog::selectedItems() const
{
    ReminderList list;

    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
        if ((*it)->isSelected()) {
            list.append(static_cast<ReminderTreeItem *>(*it));
        }
        ++it;
    }
    return list;
}

int AlarmDialog::activeCount()
{
    int count = 0;
    QTreeWidgetItemIterator it(mIncidenceTree);
    while (*it) {
Laurent Montel's avatar
Laurent Montel committed
831
        if (!(*it)->isDisabled()) { // suspended reminders are non-active
Laurent Montel's avatar
Laurent Montel committed
832
833
834
835
836
837
838
839
840
841
            ++count;
        }
        ++it;
    }
    qCDebug(KOALARMCLIENT_LOG) << "computed " << count << " active reminders";
    return count;
}

void AlarmDialog::closeEvent(QCloseEvent *)
{
Laurent Montel's avatar
Laurent Montel committed
842
    // note, this is on application close (not window hide)
Laurent Montel's avatar
Laurent Montel committed
843
844
845
846
    slotSave();
    accept();
}

Laurent Montel's avatar
Laurent Montel committed
847
848
849
850
851
852
void AlarmDialog::reject()
{
    slotSave();
    QDialog::reject();
}

Laurent Montel's avatar
Laurent Montel committed
853
854
855
856
857
858
859
860
861
void AlarmDialog::updateButtons()
{
    const ReminderList selection = selectedItems();
    const int count = selection.count();
    const bool enabled = (count > 0);
    qCDebug(KOALARMCLIENT_LOG) << "selected items=" << count;
    mUser3Button->setEnabled(enabled);
    mOkButton->setEnabled(enabled);
    if (count == 1) {
Laurent Montel's avatar
Laurent Montel committed
862
        ReminderTreeItem *item = selection.first();
863
        if (mCalendar) {
Laurent Montel's avatar
Laurent Montel committed
864
            mUser1Button->setEnabled(mCalendar->hasRight(item->mIncidence, Akonadi::Collection::CanChangeItem));
865
        }
Laurent Montel's avatar
Laurent Montel committed
866
867
868
    } else {
        mUser1Button->setEnabled(false);
    }
Allen Winter's avatar
Allen Winter committed
869
    if (enabled) {
Laurent Montel's avatar
Laurent Montel committed
870
871
        mIncidenceTree->setFocus();
        mIncidenceTree->setCurrentItem(selection.first());
Allen Winter's avatar
Allen Winter committed
872
    }
Laurent Montel's avatar
Laurent Montel committed
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
}

void AlarmDialog::toggleDetails(QTreeWidgetItem *item)
{
    if (!item) {
        return;
    }

    if (!mDetailView->isHidden()) {
        if (mLastItem == item) {
            resize(size().width(), size().height() - mDetailView->height() - 50);
            mDetailView->hide();
        } else {
            showDetails(item);
        }
    } else {
        resize(size().width(), size().height() + mDetailView->height() + 50);
        showDetails(item);
        mDetailView->show();
    }
    mLastItem = item;
}

void AlarmDialog::showDetails(QTreeWidgetItem *item)
{
    if (!item) {
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
902
    auto *reminderItem = dynamic_cast<ReminderTreeItem *>(item);
Laurent Montel's avatar
Laurent Montel committed
903
904
905
906
907

    if (!reminderItem) {
        mDetailView->setIncidence(Akonadi::Item());
    } else {
        if (!reminderItem->mDisplayText.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
908
            const QString txt = QLatin1String("<qt><p><b>") + reminderItem->mDisplayText + QLatin1String("</b></p></qt>");
Laurent Montel's avatar
Laurent Montel committed
909
910
911
912
913
914
915
916
917
918
919
920
921
922
            mDetailView->setHeaderText(txt);
        } else {
            mDetailView->setHeaderText(QString());
        }
        mDetailView->setIncidence(reminderItem->mIncidence, reminderItem->mRemindAt.date());
    }
}

void AlarmDialog::update()
{
    updateButtons();

    const ReminderList selection = selectedItems();
    if (!selection.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
923
        ReminderTreeItem *item = selection.first();
Laurent Montel's avatar
Laurent Montel committed
924
        mUser1Button->setEnabled((mCalendar->hasRight(item->mIncidence, Akonadi::Collection::CanChangeItem)) && (selection.count() == 1));
Laurent Montel's avatar
Laurent Montel committed
925
926
927
928
929
930
931
        toggleDetails(item);
    }
}

void AlarmDialog::accept()
{
    if (activeCount() == 0) {
Laurent Montel's avatar
Laurent Montel committed
932
        slotSave();
Laurent Montel's avatar
Laurent Montel committed
933
934
935
936
937
        hide();
    }
}

/** static */
Laurent Montel's avatar
Laurent Montel committed
938
QDateTime AlarmDialog::triggerDateForIncidence(const Incidence::Ptr &incidence, const QDateTime &reminderAt, QString &displayStr)
Laurent Montel's avatar
Laurent Montel committed
939
{
940
    QDateTime result;
Laurent Montel's avatar
Laurent Montel committed
941
942
943
944
945
946

    if (incidence->alarms().isEmpty()) {
        return result;
    }

    if (incidence->recurs()) {
947
        result = incidence->recurrence()->getNextDateTime(reminderAt).toLocalTime();
Laurent Montel's avatar
Laurent Montel committed
948
949
950
    }

    if (!result.isValid()) {
951
        result = incidence->dateTime(Incidence::RoleAlarm).toLocalTime();
Laurent Montel's avatar
Laurent Montel committed
952
953
    }

954
    if (result.isValid()) {
955
        displayStr = QLocale().toString(result, QLocale::ShortFormat);
956
    }
Laurent Montel's avatar
Laurent Montel committed
957
958
959
960
961
    return result;
}

void AlarmDialog::slotCalendarChanged()
{
962
    KCalendarCore::Incidence::List incidences = mCalendar->incidences();
Laurent Montel's avatar
Laurent Montel committed
963
964
    const Akonadi::Item::List items = mCalendar->itemList(incidences);
    Akonadi::Item::List::ConstIterator end(items.constEnd());
Laurent Montel's avatar
Laurent Montel committed
965
    for (Akonadi::Item::List::ConstIterator it = items.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
966
967
968
969
970
971
972
973
        ReminderTreeItem *item = searchByItem(*it);

        if (item) {
            Incidence::Ptr incidence = CalendarSupport::incidence(*it);
            QString displayStr;

            // Yes, alarms can be empty, if someone edited the incidence and removed all alarms
            if (!incidence->alarms().isEmpty()) {
974
                const auto dateTime = triggerDateForIncidence(incidence, item->mRemindAt, displayStr);
Laurent Montel's avatar
Laurent Montel committed
975
976
                const QString summary = cleanSummary(incidence->summary());

977
                if (displayStr != item->text(1) || summary != item->text(0) || item->mHappening != dateTime) {
Laurent Montel's avatar
Laurent Montel committed
978
979
                    item->setText(1, displayStr);
                    item->setText(0, summary);
Laurent Montel's avatar
Laurent Montel committed
980
                    item->mHappening = dateTime;
Laurent Montel's avatar
Laurent Montel committed
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
                }
            }
        }
    }
}

void AlarmDialog::keyPressEvent(QKeyEvent *e)
{
    const int key = e->key() | e->modifiers();

    if (key == Qt::Key_Enter || key == Qt::Key_Return) {
        e->ignore();
        return;
    }

    QDialog::keyPressEvent(e);
}

bool AlarmDialog::openIncidenceEditorThroughKOrganizer(const Incidence::Ptr &incidence)
{
Laurent Montel's avatar
Laurent Montel committed
1001
    if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.korganizer"))) {
1002
        if (!QDBusConnection::sessionBus().interface()->startService(QStringLiteral("org.kde.korganizer")).isValid()) {
Laurent Montel's avatar
Laurent Montel committed
1003
            KMessageBox::error(this, i18nc("@info", "Could not start KOrganizer so editing is not possible."));
Laurent Montel's avatar
Laurent Montel committed
1004
1005
1006
            return false;
        }
    }
Laurent Montel's avatar
Laurent Montel committed
1007
    org::kde::korganizer::Korganizer korganizer(QStringLiteral("org.kde.korganizer"), QStringLiteral("/Korganizer"), QDBusConnection::sessionBus());
Laurent Montel's avatar
Laurent Montel committed
1008
1009
1010

    qCDebug(KOALARMCLIENT_LOG) << "editing incidence " << incidence->summary();
    if (!korganizer.editIncidence(incidence->uid())) {
Laurent Montel's avatar
Laurent Montel committed
1011
        KMessageBox::error(this, i18nc("@info", "An internal KOrganizer error occurred attempting to modify \"%1\"", cleanSummary(incidence->summary())));
Laurent Montel's avatar
Laurent Montel committed
1012
1013
1014
    }

    // get desktop # where korganizer (or kontact) runs
Laurent Montel's avatar
Laurent Montel committed
1015
1016
1017
    QString object = QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kontact"))
        ? QStringLiteral("kontact/MainWindow_1")
        : QStringLiteral("korganizer/MainWindow_1");
Laurent Montel's avatar
Laurent Montel committed
1018
1019
1020
1021
1022
1023
1024
1025
    QDBusInterface korganizerObj(QStringLiteral("org.kde.korganizer"), QLatin1Char('/') + object);
#if KDEPIM_HAVE_X11
    QDBusReply<int> reply = korganizerObj.call(QStringLiteral("winId"));
    if (reply.isValid()) {
        int window = reply;
        auto winInfo = KWindowInfo(window, NET::WMDesktop);
        int desktop = winInfo.desktop();
        if (KWindowSystem::currentDesktop() == desktop) {
1026
            KWindowSystem::minimizeWindow(winId());
Laurent Montel's avatar
Laurent Montel committed
1027
1028
1029
1030
1031
1032
1033
        } else {
            KWindowSystem::setCurrentDesktop(desktop);
        }
        KWindowSystem::activateWindow(winInfo.transientFor());
    }
#elif defined(Q_OS_WIN)
    // WId is a typedef to a void* on windows
Laurent Montel's avatar
Laurent Montel committed
1034
    QDBusReply<int> reply = korganizerObj.call(QStringLiteral("winId"));
Laurent Montel's avatar
Laurent Montel committed
1035
    if (reply.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
1036
        int window = reply;
1037
        KWindowSystem::minimizeWindow(winId());
Laurent Montel's avatar
Laurent Montel committed
1038
        KWindowSystem::allowExternalProcessWindowActivation();
1039
        KWindowSystem::activateWindow(static_cast<WId>(window));
Laurent Montel's avatar
Laurent Montel committed
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
    }
#else
    // TODO (mac)
#endif
    return true;
}

bool AlarmDialog::openIncidenceEditorNG(const Akonadi::Item &item)
{
    Incidence::Ptr incidence = CalendarSupport::incidence(item);
Laurent Montel's avatar
Laurent Montel committed
1050
1051
1052
1053
    IncidenceEditorNG::IncidenceDialog *dialog = IncidenceEditorNG::IncidenceDialogFactory::create(false, /*doesn't need initial saving*/
                                                                                                   incidence->type(),
                                                                                                   nullptr,
                                                                                                   this);
1054
1055
1056
1057
1058
1059
    if (!dialog) {
        return false;
    } else {
        dialog->load(item);
        return true;
    }
Laurent Montel's avatar
Laurent Montel committed
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
}

void AlarmDialog::removeFromConfig(const QList<Akonadi::Item::Id> &ids)
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup genGroup(config, "General");

    const int oldNumReminders = genGroup.readEntry("Reminders", 0);

    QVector<ConfItem> newReminders;
    // Delete everything
    for (int i = 1; i <= oldNumReminders; ++i) {
        const QString group(QStringLiteral("Incidence-%1").arg(i));
        KConfigGroup incGroup(config, group);
        const QString uid = incGroup.readEntry("UID");
        const QDateTime remindAtDate = incGroup.readEntry("RemindAt", QDateTime());
1076
        const QUrl akonadiUrl(incGroup.readEntry("AkonadiUrl"));
Laurent Montel's avatar
Laurent Montel committed
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
        const Akonadi::Item::Id id = Akonadi::Item::fromUrl(akonadiUrl).id();
        if (!ids.contains(id)) {
            ConfItem ci;
            ci.akonadiUrl = akonadiUrl;
            ci.remindAt = remindAtDate;
            ci.uid = uid;
            newReminders.append(ci);
        }
        config->deleteGroup(group);
    }

    const int newRemindersCount(newReminders.count());
    genGroup.writeEntry("Reminders", newRemindersCount);

Laurent Montel's avatar
Laurent Montel committed
1091
    // Write everything except those which have an uid we don't want
Laurent Montel's avatar
Laurent Montel committed
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    for (int i = 0; i < newRemindersCount; ++i) {
        const QString group(QStringLiteral("Incidence-%1").arg(i + 1));
        KConfigGroup incGroup(config, group);
        const ConfItem conf = newReminders.at(i);
        incGroup.writeEntry("UID", conf.uid);
        incGroup.writeEntry("RemindAt", conf.remindAt);
        incGroup.writeEntry("AkonadiUrl", conf.akonadiUrl);
        incGroup.sync();
    }
    genGroup.sync();
}
1103
1104
1105
1106
1107
1108
1109

bool AlarmDialog::grabFocus()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup generalConfig(config, "General");
    return generalConfig.readEntry("GrabFocus", false);
}